mirror of
https://github.com/johndoe6345789/tustu.git
synced 2026-04-24 13:45:00 +00:00
Add QML files for Email Parser and Key Generator, and implement main application structure
This commit is contained in:
260
qml/EmailParserTab.qml
Normal file
260
qml/EmailParserTab.qml
Normal file
@@ -0,0 +1,260 @@
|
||||
import QtQuick 6.5
|
||||
import QtQuick.Controls 6.5
|
||||
import QtQuick.Layouts 6.5
|
||||
|
||||
ScrollView {
|
||||
id: root
|
||||
clip: true
|
||||
contentWidth: availableWidth
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 15
|
||||
|
||||
// Instructions
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: instructionsText.height + 20
|
||||
color: "#e3f2fd"
|
||||
radius: 5
|
||||
|
||||
Text {
|
||||
id: instructionsText
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
text: "Parse or Generate Registration Emails:\n\n" +
|
||||
"• PARSE: Paste a registration email and click 'Parse Email' to extract fields\n" +
|
||||
"• GENERATE: Fill in the fields below and click 'Generate Email Format' to create formatted text\n" +
|
||||
"• VALIDATE: Click 'Validate Key' to check if the registration key is correct"
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: 12
|
||||
}
|
||||
}
|
||||
|
||||
// Email Input Area
|
||||
GroupBox {
|
||||
title: "Paste Registration Email Here"
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
ScrollView {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 150
|
||||
|
||||
TextArea {
|
||||
id: emailTextArea
|
||||
placeholderText: "[Registration]\n" +
|
||||
"First Name: John\n" +
|
||||
"Last Name: Doe\n" +
|
||||
"Registered email: john.doe@example.com\n" +
|
||||
"Serial Number: ABC123\n" +
|
||||
"Registration Key: XYZ789\n" +
|
||||
"[End Registration]"
|
||||
wrapMode: TextArea.Wrap
|
||||
font.family: "Monospace"
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Parse Registration Information"
|
||||
Layout.fillWidth: true
|
||||
background: Rectangle {
|
||||
implicitHeight: 40
|
||||
color: parent.pressed ? "#1976D2" : "#2196F3"
|
||||
radius: 5
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.bold: true
|
||||
}
|
||||
onClicked: parseEmail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extracted Information
|
||||
GroupBox {
|
||||
title: "Extracted Information"
|
||||
Layout.fillWidth: true
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
columns: 2
|
||||
columnSpacing: 10
|
||||
rowSpacing: 8
|
||||
|
||||
Label {
|
||||
text: "First Name:"
|
||||
font.bold: true
|
||||
}
|
||||
TextField {
|
||||
id: firstNameOutput
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Enter or parse first name"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Last Name:"
|
||||
font.bold: true
|
||||
}
|
||||
TextField {
|
||||
id: lastNameOutput
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Enter or parse last name"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Email:"
|
||||
font.bold: true
|
||||
}
|
||||
TextField {
|
||||
id: emailOutput
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Enter or parse email address"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Serial Number:"
|
||||
font.bold: true
|
||||
}
|
||||
TextField {
|
||||
id: serialOutput
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Optional serial number"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Registration Key:"
|
||||
font.bold: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
}
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 60
|
||||
color: "#ffffff"
|
||||
border.color: "#ccc"
|
||||
border.width: 1
|
||||
radius: 3
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
|
||||
TextEdit {
|
||||
id: keyOutput
|
||||
width: parent.width
|
||||
wrapMode: TextEdit.Wrap
|
||||
font.family: "Courier"
|
||||
font.pixelSize: 11
|
||||
font.bold: true
|
||||
selectByMouse: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Action Buttons
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 10
|
||||
|
||||
Button {
|
||||
text: "Load Test Data"
|
||||
Layout.fillWidth: true
|
||||
background: Rectangle {
|
||||
implicitHeight: 35
|
||||
color: parent.pressed ? "#7B1FA2" : "#9C27B0"
|
||||
radius: 3
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.bold: true
|
||||
font.pixelSize: 13
|
||||
}
|
||||
onClicked: loadTestData()
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Parse Email"
|
||||
Layout.fillWidth: true
|
||||
onClicked: parseEmail()
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Generate Email Format"
|
||||
Layout.fillWidth: true
|
||||
background: Rectangle {
|
||||
implicitHeight: 35
|
||||
color: parent.pressed ? "#45a049" : "#4CAF50"
|
||||
radius: 3
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.bold: true
|
||||
font.pixelSize: 13
|
||||
}
|
||||
onClicked: generateEmail()
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Copy Key"
|
||||
Layout.fillWidth: true
|
||||
onClicked: copyKey()
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Validate Key"
|
||||
Layout.fillWidth: true
|
||||
background: Rectangle {
|
||||
implicitHeight: 35
|
||||
color: parent.pressed ? "#F57C00" : "#FF9800"
|
||||
radius: 3
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.bold: true
|
||||
font.pixelSize: 13
|
||||
}
|
||||
onClicked: validateKey()
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
}
|
||||
|
||||
function loadTestData() {
|
||||
console.log("Load test data")
|
||||
}
|
||||
|
||||
function parseEmail() {
|
||||
console.log("Parse email")
|
||||
}
|
||||
|
||||
function generateEmail() {
|
||||
console.log("Generate email format")
|
||||
}
|
||||
|
||||
function copyKey() {
|
||||
console.log("Copy key to clipboard")
|
||||
}
|
||||
|
||||
function validateKey() {
|
||||
console.log("Validate key")
|
||||
}
|
||||
}
|
||||
249
qml/KeyGeneratorTab.qml
Normal file
249
qml/KeyGeneratorTab.qml
Normal file
@@ -0,0 +1,249 @@
|
||||
import QtQuick 6.5
|
||||
import QtQuick.Controls 6.5
|
||||
import QtQuick.Layouts 6.5
|
||||
|
||||
ScrollView {
|
||||
id: root
|
||||
clip: true
|
||||
|
||||
property string algorithm: "5-param"
|
||||
property bool showSecret: true
|
||||
property bool showMonth: false
|
||||
property bool showYear: false
|
||||
property bool showSerial: false
|
||||
|
||||
contentWidth: availableWidth
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 15
|
||||
|
||||
// Input Group
|
||||
GroupBox {
|
||||
title: "Input Parameters"
|
||||
Layout.fillWidth: true
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
columns: 2
|
||||
columnSpacing: 10
|
||||
rowSpacing: 8
|
||||
|
||||
Label {
|
||||
text: "First Name:"
|
||||
font.bold: true
|
||||
}
|
||||
TextField {
|
||||
id: firstNameField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Enter first name"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Last Name:"
|
||||
font.bold: true
|
||||
}
|
||||
TextField {
|
||||
id: lastNameField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Enter last name"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Product Name:"
|
||||
font.bold: true
|
||||
}
|
||||
ComboBox {
|
||||
id: productField
|
||||
Layout.fillWidth: true
|
||||
editable: true
|
||||
model: ["MegaLogViewer", "TunerStudio", "DataLogger"]
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Secret Key:"
|
||||
font.bold: true
|
||||
visible: showSecret
|
||||
}
|
||||
TextField {
|
||||
id: secretField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Enter secret key"
|
||||
visible: showSecret
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Email:"
|
||||
font.bold: true
|
||||
}
|
||||
TextField {
|
||||
id: emailField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Enter email address"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Month:"
|
||||
font.bold: true
|
||||
visible: showMonth
|
||||
}
|
||||
TextField {
|
||||
id: monthField
|
||||
Layout.fillWidth: true
|
||||
text: "01"
|
||||
placeholderText: "MM"
|
||||
visible: showMonth
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Year:"
|
||||
font.bold: true
|
||||
visible: showYear
|
||||
}
|
||||
TextField {
|
||||
id: yearField
|
||||
Layout.fillWidth: true
|
||||
text: "2015"
|
||||
placeholderText: "YYYY"
|
||||
visible: showYear
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Serial Number:"
|
||||
font.bold: true
|
||||
visible: showSerial
|
||||
}
|
||||
TextField {
|
||||
id: serialField
|
||||
Layout.fillWidth: true
|
||||
placeholderText: "Enter serial number"
|
||||
visible: showSerial
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 10
|
||||
|
||||
Button {
|
||||
text: "Load Test Data"
|
||||
Layout.fillWidth: true
|
||||
background: Rectangle {
|
||||
implicitHeight: 40
|
||||
color: parent.pressed ? "#1976D2" : "#2196F3"
|
||||
radius: 5
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.bold: true
|
||||
}
|
||||
onClicked: loadTestData()
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Generate Key"
|
||||
Layout.fillWidth: true
|
||||
background: Rectangle {
|
||||
implicitHeight: 40
|
||||
color: parent.pressed ? "#45a049" : "#4CAF50"
|
||||
radius: 5
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.bold: true
|
||||
}
|
||||
onClicked: generateKey()
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Validate Key"
|
||||
Layout.fillWidth: true
|
||||
background: Rectangle {
|
||||
implicitHeight: 40
|
||||
color: parent.pressed ? "#F57C00" : "#FF9800"
|
||||
radius: 5
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.bold: true
|
||||
}
|
||||
onClicked: validateKey()
|
||||
}
|
||||
}
|
||||
|
||||
// Output Group
|
||||
GroupBox {
|
||||
title: "Generated Registration Key"
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 80
|
||||
color: "#e8f5e9"
|
||||
border.color: "#4CAF50"
|
||||
border.width: 2
|
||||
radius: 5
|
||||
|
||||
ScrollView {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 5
|
||||
|
||||
TextEdit {
|
||||
id: outputField
|
||||
width: parent.width
|
||||
wrapMode: TextEdit.Wrap
|
||||
readOnly: true
|
||||
font.family: "Courier"
|
||||
font.pixelSize: 14
|
||||
font.bold: true
|
||||
selectByMouse: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Copy to Clipboard"
|
||||
Layout.fillWidth: true
|
||||
onClicked: copyToClipboard()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
}
|
||||
|
||||
function loadTestData() {
|
||||
// This would call Python backend
|
||||
console.log("Load test data for", algorithm)
|
||||
}
|
||||
|
||||
function generateKey() {
|
||||
// This would call Python backend
|
||||
console.log("Generate key for", algorithm)
|
||||
}
|
||||
|
||||
function validateKey() {
|
||||
// This would call Python backend
|
||||
console.log("Validate key for", algorithm)
|
||||
}
|
||||
|
||||
function copyToClipboard() {
|
||||
// Copy output to clipboard
|
||||
console.log("Copy to clipboard")
|
||||
}
|
||||
}
|
||||
99
qml/Main.qml
Normal file
99
qml/Main.qml
Normal file
@@ -0,0 +1,99 @@
|
||||
import QtQuick 6.5
|
||||
import QtQuick.Controls 6.5
|
||||
import QtQuick.Layouts 6.5
|
||||
import QtQuick.Window 6.5
|
||||
|
||||
ApplicationWindow {
|
||||
id: mainWindow
|
||||
visible: true
|
||||
width: 600
|
||||
height: 700
|
||||
minimumWidth: 500
|
||||
minimumHeight: 600
|
||||
title: "Registration Key Generator"
|
||||
|
||||
// Color scheme
|
||||
readonly property color primaryColor: "#4CAF50"
|
||||
readonly property color accentColor: "#2196F3"
|
||||
readonly property color warningColor: "#FF9800"
|
||||
readonly property color errorColor: "#f44336"
|
||||
readonly property color backgroundColor: "#f5f5f5"
|
||||
readonly property color cardColor: "#ffffff"
|
||||
|
||||
TabBar {
|
||||
id: tabBar
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
TabButton {
|
||||
text: "4-Parameter"
|
||||
font.pixelSize: 13
|
||||
}
|
||||
TabButton {
|
||||
text: "5-Parameter"
|
||||
font.pixelSize: 13
|
||||
}
|
||||
TabButton {
|
||||
text: "7-Parameter"
|
||||
font.pixelSize: 13
|
||||
}
|
||||
TabButton {
|
||||
text: "8-Parameter"
|
||||
font.pixelSize: 13
|
||||
}
|
||||
TabButton {
|
||||
text: "Email Parser"
|
||||
font.pixelSize: 13
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
anchors.top: tabBar.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 10
|
||||
currentIndex: tabBar.currentIndex
|
||||
|
||||
// 4-Parameter Tab
|
||||
KeyGeneratorTab {
|
||||
algorithm: "4-param"
|
||||
showSecret: false
|
||||
showMonth: false
|
||||
showYear: false
|
||||
showSerial: false
|
||||
}
|
||||
|
||||
// 5-Parameter Tab
|
||||
KeyGeneratorTab {
|
||||
algorithm: "5-param"
|
||||
showSecret: true
|
||||
showMonth: false
|
||||
showYear: false
|
||||
showSerial: false
|
||||
}
|
||||
|
||||
// 7-Parameter Tab
|
||||
KeyGeneratorTab {
|
||||
algorithm: "7-param"
|
||||
showSecret: true
|
||||
showMonth: true
|
||||
showYear: true
|
||||
showSerial: false
|
||||
}
|
||||
|
||||
// 8-Parameter Tab
|
||||
KeyGeneratorTab {
|
||||
algorithm: "8-param"
|
||||
showSecret: true
|
||||
showMonth: true
|
||||
showYear: true
|
||||
showSerial: true
|
||||
}
|
||||
|
||||
// Email Parser Tab
|
||||
EmailParserTab {
|
||||
}
|
||||
}
|
||||
}
|
||||
180
qml/README.md
Normal file
180
qml/README.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# QML Registration GUI
|
||||
|
||||
Modern QML-based user interface for the Registration Key Generator, based on the original Java Swing GUI (`com/efiAnalytics/ui/dS.java`).
|
||||
|
||||
## Features
|
||||
|
||||
### QML Implementation
|
||||
- **Main.qml** - Main application window with tab navigation
|
||||
- **KeyGeneratorTab.qml** - Reusable tab component for key generation (4/5/7/8-parameter variants)
|
||||
- **EmailParserTab.qml** - Email parsing and generation functionality
|
||||
- **RegistrationDialog.qml** - Registration dialog matching Java `dS.java` design
|
||||
- **main.py** - Python backend connecting QML to registration algorithms
|
||||
|
||||
### UI Components
|
||||
|
||||
#### Main Window
|
||||
- Tab-based navigation (matches PyQt6 GUI structure)
|
||||
- 5 tabs: 4-Parameter, 5-Parameter, 7-Parameter, 8-Parameter, Email Parser
|
||||
- Modern Material Design color scheme
|
||||
- Responsive layout
|
||||
|
||||
#### Key Generator Tabs
|
||||
Based on Java Swing input panels from `dS.java`:
|
||||
- Input fields for first name, last name, product, email
|
||||
- Conditional fields (secret, month, year, serial) based on algorithm
|
||||
- "Load Test Data" button (blue) - auto-populate with valid test data
|
||||
- "Generate Key" button (green) - create registration key
|
||||
- "Validate Key" button (orange) - verify key correctness
|
||||
- Output area with monospace font
|
||||
- Copy to clipboard functionality
|
||||
|
||||
#### Email Parser Tab
|
||||
Based on Java "Paste Email" functionality from `dS.java`:
|
||||
- Text area for pasting registration emails
|
||||
- Parse button to extract fields from `[Registration]...[End Registration]` format
|
||||
- Editable extracted fields (first name, last name, email, serial, key)
|
||||
- "Load Test Data" button (purple) - generates complete valid registration
|
||||
- "Generate Email Format" button (green) - creates formatted email text
|
||||
- Validation support
|
||||
|
||||
#### Registration Dialog
|
||||
Matches Java `dS.java` dialog structure:
|
||||
- Radio buttons to switch between "Inputs" and "Paste Email" views
|
||||
- CardLayout equivalent using QML StackLayout
|
||||
- Edition selector (ComboBox)
|
||||
- Registration information fields
|
||||
- Email paste and parse functionality
|
||||
- OK/Cancel standard dialog buttons
|
||||
|
||||
## Architecture
|
||||
|
||||
### QML Frontend
|
||||
- Declarative UI using QtQuick 6.5
|
||||
- Material Design styling
|
||||
- Signal/slot connections to Python backend
|
||||
- Responsive layouts with GridLayout, RowLayout, ColumnLayout
|
||||
|
||||
### Python Backend (main.py)
|
||||
- `RegistrationBackend` class exposes methods to QML
|
||||
- Uses `@pyqtSlot` decorators for QML-callable methods
|
||||
- Imports from `registration_gui.py` for algorithm implementations
|
||||
- Signal emissions for async operations
|
||||
|
||||
### Backend Methods
|
||||
- `generateKey4Param()` - 4-parameter algorithm
|
||||
- `generateKey5Param()` - 5-parameter algorithm
|
||||
- `generateKey7Param()` - 7-parameter algorithm
|
||||
- `generateKey8Param()` - 8-parameter algorithm
|
||||
- `loadTestData()` - Generate dummy test data
|
||||
- `parseEmail()` - Extract fields from email text
|
||||
- `generateEmailFormat()` - Create formatted registration email
|
||||
|
||||
## Running the QML GUI
|
||||
|
||||
```bash
|
||||
# Install Qt6 for Python
|
||||
pip install PyQt6
|
||||
|
||||
# Run the QML application
|
||||
cd qml
|
||||
python3 main.py
|
||||
```
|
||||
|
||||
## Comparison: Java Swing vs QML
|
||||
|
||||
### Java Swing (Original)
|
||||
```java
|
||||
// dS.java - Registration Dialog
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new GridLayout(0, 1, 3, 3));
|
||||
ea firstNameField = new ea(this, "Registered First Name:", k.b());
|
||||
panel.add(firstNameField);
|
||||
```
|
||||
|
||||
### QML (Modern)
|
||||
```qml
|
||||
// RegistrationDialog.qml
|
||||
GridLayout {
|
||||
columns: 2
|
||||
Label { text: "Registered First Name:" }
|
||||
TextField {
|
||||
id: firstNameField
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Advantages of QML
|
||||
|
||||
1. **Declarative Syntax** - Easier to read and maintain than imperative Java Swing
|
||||
2. **Modern Look** - Material Design colors and styling
|
||||
3. **Responsive** - Automatic layout adjustment
|
||||
4. **Animation Support** - Built-in property animations
|
||||
5. **Hardware Acceleration** - Qt Quick Scene Graph for smooth rendering
|
||||
6. **Cross-Platform** - Same QML runs on Windows, macOS, Linux, mobile
|
||||
7. **Separation of Concerns** - UI in QML, logic in Python
|
||||
8. **Hot Reload** - Can reload QML without restarting (in development)
|
||||
|
||||
## Java GUI Elements Mapped to QML
|
||||
|
||||
| Java Swing Component | QML Component | Location |
|
||||
|---------------------|---------------|----------|
|
||||
| `JDialog` | `Dialog` | RegistrationDialog.qml |
|
||||
| `JPanel` | `Rectangle` + Layout | All files |
|
||||
| `JTextField` | `TextField` | KeyGeneratorTab.qml, etc. |
|
||||
| `JTextArea` / `JTextPane` | `TextArea` / `TextEdit` | EmailParserTab.qml |
|
||||
| `JButton` | `Button` | All files |
|
||||
| `JRadioButton` | `RadioButton` | RegistrationDialog.qml |
|
||||
| `ButtonGroup` | `ButtonGroup` | RegistrationDialog.qml |
|
||||
| `JComboBox` | `ComboBox` | KeyGeneratorTab.qml |
|
||||
| `GridLayout` | `GridLayout` | KeyGeneratorTab.qml |
|
||||
| `BorderLayout` | `ColumnLayout` + anchors | Main.qml |
|
||||
| `CardLayout` | `StackLayout` | RegistrationDialog.qml |
|
||||
| `JScrollPane` | `ScrollView` | EmailParserTab.qml |
|
||||
| `GroupBox` (titled border) | `GroupBox` | All tabs |
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
qml/
|
||||
├── Main.qml # Main application window
|
||||
├── KeyGeneratorTab.qml # Reusable key generation tab
|
||||
├── EmailParserTab.qml # Email parsing/generation tab
|
||||
├── RegistrationDialog.qml # Registration dialog (matches dS.java)
|
||||
├── main.py # Python backend + QML loader
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
To fully connect the QML UI to the Python backend:
|
||||
|
||||
1. **Implement Backend Calls** - Connect QML button clicks to Python slots
|
||||
2. **Add Clipboard Support** - Use `QClipboard` for copy/paste
|
||||
3. **Error Handling** - Display validation errors in QML dialogs
|
||||
4. **Styling** - Add custom Material Design theme
|
||||
5. **Animations** - Add smooth transitions between tabs
|
||||
6. **Testing** - Create QML test cases
|
||||
|
||||
## Color Scheme
|
||||
|
||||
```qml
|
||||
primaryColor: "#4CAF50" // Green - Generate buttons
|
||||
accentColor: "#2196F3" // Blue - Load Test Data
|
||||
warningColor: "#FF9800" // Orange - Validate buttons
|
||||
errorColor: "#f44336" // Red - Errors
|
||||
backgroundColor: "#f5f5f5" // Light gray
|
||||
cardColor: "#ffffff" // White
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Python 3.6+
|
||||
- PyQt6 6.5.0+
|
||||
- Qt 6.5+ (included with PyQt6)
|
||||
- registration_gui.py (from parent directory)
|
||||
|
||||
## License
|
||||
|
||||
Same as parent project - for educational and testing purposes.
|
||||
205
qml/RegistrationDialog.qml
Normal file
205
qml/RegistrationDialog.qml
Normal file
@@ -0,0 +1,205 @@
|
||||
// Registration Dialog based on Java dS.java
|
||||
import QtQuick 6.5
|
||||
import QtQuick.Controls 6.5
|
||||
import QtQuick.Layouts 6.5
|
||||
import QtQuick.Window 6.5
|
||||
|
||||
Dialog {
|
||||
id: registrationDialog
|
||||
title: "Enter Registration Information"
|
||||
width: 500
|
||||
height: 600
|
||||
modal: true
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
|
||||
property string productName: "MegaLogViewer"
|
||||
property var editions: ["Standard", "Pro", "Enterprise"]
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 10
|
||||
|
||||
// Radio buttons for input method
|
||||
ButtonGroup {
|
||||
id: inputMethodGroup
|
||||
buttons: [inputsRadio, pasteEmailRadio]
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 20
|
||||
|
||||
RadioButton {
|
||||
id: inputsRadio
|
||||
text: "Inputs"
|
||||
checked: true
|
||||
onClicked: stackLayout.currentIndex = 0
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
id: pasteEmailRadio
|
||||
text: "Paste Email"
|
||||
onClicked: stackLayout.currentIndex = 1
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
border.color: "#ccc"
|
||||
border.width: 1
|
||||
radius: 5
|
||||
|
||||
StackLayout {
|
||||
id: stackLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
currentIndex: 0
|
||||
|
||||
// Inputs View
|
||||
ScrollView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 10
|
||||
|
||||
// Edition Selector
|
||||
GroupBox {
|
||||
title: "Register " + productName + " (Select Edition)"
|
||||
Layout.fillWidth: true
|
||||
|
||||
ComboBox {
|
||||
id: editionCombo
|
||||
anchors.fill: parent
|
||||
model: editions
|
||||
}
|
||||
}
|
||||
|
||||
// Registration Information
|
||||
GroupBox {
|
||||
title: "Registration Information"
|
||||
Layout.fillWidth: true
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
columns: 2
|
||||
columnSpacing: 10
|
||||
rowSpacing: 8
|
||||
|
||||
Label { text: "Registered First Name:" }
|
||||
TextField {
|
||||
id: firstNameField
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Label { text: "Registered Last Name:" }
|
||||
TextField {
|
||||
id: lastNameField
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Label { text: "Registered eMail Address:" }
|
||||
TextField {
|
||||
id: emailField
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Label { text: "Registration Key:" }
|
||||
TextField {
|
||||
id: registrationKeyField
|
||||
Layout.fillWidth: true
|
||||
font.family: "Courier"
|
||||
}
|
||||
|
||||
Label { text: "Serial Number:" }
|
||||
TextField {
|
||||
id: serialField
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Paste Email View
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
spacing: 10
|
||||
|
||||
Button {
|
||||
text: "Paste"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onClicked: pasteFromClipboard()
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
TextArea {
|
||||
id: pasteTextArea
|
||||
placeholderText: "Paste registration email here..."
|
||||
wrapMode: TextArea.Wrap
|
||||
font.family: "Monospace"
|
||||
onTextChanged: parseEmailText()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: {
|
||||
// Validate and process registration
|
||||
console.log("Registration submitted")
|
||||
console.log("First Name:", firstNameField.text)
|
||||
console.log("Last Name:", lastNameField.text)
|
||||
console.log("Email:", emailField.text)
|
||||
console.log("Key:", registrationKeyField.text)
|
||||
}
|
||||
|
||||
function pasteFromClipboard() {
|
||||
// Get text from clipboard
|
||||
pasteTextArea.text = ""; // Would paste from actual clipboard
|
||||
}
|
||||
|
||||
function parseEmailText() {
|
||||
var text = pasteTextArea.text
|
||||
var lines = text.split('\n')
|
||||
var inRegistration = false
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
var line = lines[i].trim()
|
||||
|
||||
if (line.includes('[Registration]')) {
|
||||
inRegistration = true
|
||||
continue
|
||||
}
|
||||
|
||||
if (line.includes('[End Registration]')) {
|
||||
break
|
||||
}
|
||||
|
||||
if (inRegistration) {
|
||||
if (line.startsWith('First Name')) {
|
||||
firstNameField.text = line.substring(line.indexOf(':') + 1).trim()
|
||||
}
|
||||
else if (line.startsWith('Last Name')) {
|
||||
lastNameField.text = line.substring(line.indexOf(':') + 1).trim()
|
||||
}
|
||||
else if (line.startsWith('Registered email')) {
|
||||
emailField.text = line.substring(line.indexOf(':') + 1).trim()
|
||||
}
|
||||
else if (line.includes('Serial Number')) {
|
||||
serialField.text = line.substring(line.indexOf(':') + 1).trim()
|
||||
}
|
||||
else if (line.startsWith('Registration Key')) {
|
||||
registrationKeyField.text = line.substring(line.indexOf(':') + 1).trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
175
qml/main.py
Normal file
175
qml/main.py
Normal file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
QML-based Registration Key Generator
|
||||
Uses Qt6 QML for the UI with Python backend
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Add parent directory to path for imports
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtSlot, pyqtSignal, pyqtProperty
|
||||
from PyQt6.QtGui import QGuiApplication
|
||||
from PyQt6.QtQml import QQmlApplicationEngine
|
||||
|
||||
from registration_gui import RegistrationKeyGenerator, DummyDataGenerator
|
||||
|
||||
|
||||
class RegistrationBackend(QObject):
|
||||
"""Backend for QML UI"""
|
||||
|
||||
keyGenerated = pyqtSignal(str)
|
||||
errorOccurred = pyqtSignal(str)
|
||||
testDataLoaded = pyqtSignal(str, str, str, str, str, str)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.generator = RegistrationKeyGenerator()
|
||||
|
||||
@pyqtSlot(str, str, str, str, result=str)
|
||||
def generateKey4Param(self, firstName, lastName, product, email):
|
||||
"""Generate 4-parameter key"""
|
||||
try:
|
||||
key = RegistrationKeyGenerator.generate_key_basic(
|
||||
firstName, lastName, product, email
|
||||
)
|
||||
return key if key else ""
|
||||
except Exception as e:
|
||||
self.errorOccurred.emit(str(e))
|
||||
return ""
|
||||
|
||||
@pyqtSlot(str, str, str, str, str, result=str)
|
||||
def generateKey5Param(self, firstName, lastName, product, secret, email):
|
||||
"""Generate 5-parameter key"""
|
||||
try:
|
||||
key = RegistrationKeyGenerator.generate_key_5param(
|
||||
firstName, lastName, product, secret, email
|
||||
)
|
||||
return key if key else ""
|
||||
except Exception as e:
|
||||
self.errorOccurred.emit(str(e))
|
||||
return ""
|
||||
|
||||
@pyqtSlot(str, str, str, str, str, str, str, result=str)
|
||||
def generateKey7Param(self, firstName, lastName, product, secret, email, month, year):
|
||||
"""Generate 7-parameter key"""
|
||||
try:
|
||||
key = RegistrationKeyGenerator.generate_key_7param(
|
||||
firstName, lastName, product, secret, email, month, year
|
||||
)
|
||||
return key if key else ""
|
||||
except Exception as e:
|
||||
self.errorOccurred.emit(str(e))
|
||||
return ""
|
||||
|
||||
@pyqtSlot(str, str, str, str, str, str, str, str, result=str)
|
||||
def generateKey8Param(self, firstName, lastName, product, secret, email, month, year, serial):
|
||||
"""Generate 8-parameter key"""
|
||||
try:
|
||||
key = RegistrationKeyGenerator.generate_key_8param(
|
||||
firstName, lastName, product, secret, email, month, year, serial
|
||||
)
|
||||
return key if key else ""
|
||||
except Exception as e:
|
||||
self.errorOccurred.emit(str(e))
|
||||
return ""
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def loadTestData(self):
|
||||
"""Load test data and return as JSON string"""
|
||||
data = DummyDataGenerator.generate()
|
||||
|
||||
# Emit signal with test data
|
||||
self.testDataLoaded.emit(
|
||||
data['first_name'],
|
||||
data['last_name'],
|
||||
data['email'],
|
||||
data['product'],
|
||||
data['secret'],
|
||||
data['serial']
|
||||
)
|
||||
|
||||
return f"{data['first_name']}|{data['last_name']}|{data['email']}|{data['product']}|{data['secret']}|{data['serial']}"
|
||||
|
||||
@pyqtSlot(str, result=str)
|
||||
def parseEmail(self, emailText):
|
||||
"""Parse email text and return extracted fields"""
|
||||
lines = emailText.split('\n')
|
||||
in_registration = False
|
||||
|
||||
result = {
|
||||
'firstName': '',
|
||||
'lastName': '',
|
||||
'email': '',
|
||||
'serial': '',
|
||||
'key': ''
|
||||
}
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
|
||||
if '[Registration]' in line:
|
||||
in_registration = True
|
||||
continue
|
||||
|
||||
if '[End Registration]' in line:
|
||||
break
|
||||
|
||||
if in_registration:
|
||||
if line.startswith('First Name') and ':' in line:
|
||||
result['firstName'] = line.split(':', 1)[1].strip()
|
||||
elif line.startswith('Last Name') and ':' in line:
|
||||
result['lastName'] = line.split(':', 1)[1].strip()
|
||||
elif 'email' in line.lower() and ':' in line:
|
||||
result['email'] = line.split(':', 1)[1].strip()
|
||||
elif 'Serial Number' in line and ':' in line:
|
||||
result['serial'] = line.split(':', 1)[1].strip()
|
||||
elif 'Registration Key' in line and ':' in line:
|
||||
result['key'] = line.split(':', 1)[1].strip()
|
||||
|
||||
# Return as delimited string
|
||||
return f"{result['firstName']}|{result['lastName']}|{result['email']}|{result['serial']}|{result['key']}"
|
||||
|
||||
@pyqtSlot(str, str, str, str, str, result=str)
|
||||
def generateEmailFormat(self, firstName, lastName, email, serial, key):
|
||||
"""Generate registration email format"""
|
||||
email_format = "[Registration]\n"
|
||||
email_format += f"First Name: {firstName}\n"
|
||||
email_format += f"Last Name: {lastName}\n"
|
||||
email_format += f"Registered email: {email}\n"
|
||||
|
||||
if serial:
|
||||
email_format += f"Serial Number: {serial}\n"
|
||||
|
||||
email_format += f"Registration Key: {key}\n"
|
||||
email_format += "[End Registration]"
|
||||
|
||||
return email_format
|
||||
|
||||
|
||||
def main():
|
||||
app = QGuiApplication(sys.argv)
|
||||
app.setApplicationName("Registration Key Generator")
|
||||
app.setOrganizationName("EFI Analytics")
|
||||
|
||||
engine = QQmlApplicationEngine()
|
||||
|
||||
# Create backend and register it
|
||||
backend = RegistrationBackend()
|
||||
engine.rootContext().setContextProperty("backend", backend)
|
||||
|
||||
# Load QML
|
||||
qml_file = Path(__file__).parent / "Main.qml"
|
||||
engine.load(str(qml_file))
|
||||
|
||||
if not engine.rootObjects():
|
||||
return -1
|
||||
|
||||
return app.exec()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user