From 84d020cc6bb9dec79da345895080b3510c3354be Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 11 Jan 2026 02:57:49 +0000 Subject: [PATCH] Add QML files for Email Parser and Key Generator, and implement main application structure --- qml/EmailParserTab.qml | 260 +++++++++++++++++++++++++++++++++++++ qml/KeyGeneratorTab.qml | 249 +++++++++++++++++++++++++++++++++++ qml/Main.qml | 99 ++++++++++++++ qml/README.md | 180 +++++++++++++++++++++++++ qml/RegistrationDialog.qml | 205 +++++++++++++++++++++++++++++ qml/main.py | 175 +++++++++++++++++++++++++ 6 files changed, 1168 insertions(+) create mode 100644 qml/EmailParserTab.qml create mode 100644 qml/KeyGeneratorTab.qml create mode 100644 qml/Main.qml create mode 100644 qml/README.md create mode 100644 qml/RegistrationDialog.qml create mode 100644 qml/main.py diff --git a/qml/EmailParserTab.qml b/qml/EmailParserTab.qml new file mode 100644 index 00000000..1e6a166c --- /dev/null +++ b/qml/EmailParserTab.qml @@ -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") + } +} diff --git a/qml/KeyGeneratorTab.qml b/qml/KeyGeneratorTab.qml new file mode 100644 index 00000000..5714e2ca --- /dev/null +++ b/qml/KeyGeneratorTab.qml @@ -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") + } +} diff --git a/qml/Main.qml b/qml/Main.qml new file mode 100644 index 00000000..19d2f344 --- /dev/null +++ b/qml/Main.qml @@ -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 { + } + } +} diff --git a/qml/README.md b/qml/README.md new file mode 100644 index 00000000..957898e8 --- /dev/null +++ b/qml/README.md @@ -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. diff --git a/qml/RegistrationDialog.qml b/qml/RegistrationDialog.qml new file mode 100644 index 00000000..e61ed336 --- /dev/null +++ b/qml/RegistrationDialog.qml @@ -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() + } + } + } + } +} diff --git a/qml/main.py b/qml/main.py new file mode 100644 index 00000000..de2c4fd2 --- /dev/null +++ b/qml/main.py @@ -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())