diff --git a/main.py b/main.py
index 49fd8a5..46cb2ed 100644
--- a/main.py
+++ b/main.py
@@ -1,58 +1,46 @@
-from imports import *
-from ui_init import *
-
-class Online:
- def init (self):
- self.troppical_database = self.fetch_data()
-
- def fetch_data(self):
- url = "https://raw.githubusercontent.com/kleidis/test/main/troppical-data.json"
- response = requests.get(url)
- if response.status_code == 200:
- all_data = response.json()
- self.troppical_database = [item for item in all_data if item.get('emulator_platform') != 'android']
- return self.troppical_database
- else:
- print("Failed to fetch data:", response.status_code)
-
- def get_latest_git_tag():
- tag = "1.0"
- github_token = os.getenv("GITHUB_TOKEN", "")
- try:
- command = f"GH_TOKEN={github_token} gh release list --limit 1 --json tagName --jq '.[0].tagName'"
- process = subprocess.Popen(['bash', '-c', command], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- out, err = process.communicate()
- if process.returncode == 0:
- tag = out.decode('utf-8').strip()
- if tag.startswith("v"):
- tag = tag[1:]
- else:
- print(f"Failed to get latest GitHub release tag: {err.decode('utf-8')}")
- except Exception as e:
- print(f"Failed to get latest GitHub release tag: {e}")
- return tag
-
-class Logic:
+from PyQt6.QtWidgets import QApplication, QMessageBox, QFileDialog, QInputDialog
+from PyQt6.QtCore import QThread, pyqtSignal, pyqtSlot
+import requests
+import os
+import subprocess
+import sys
+from zipfile import ZipFile
+import shutil
+import tempfile
+from icons import styledark_rc
+import win32com.client
+import winreg
+from pathlib import Path
+from init_instances import inst
+
+class Main():
def __init__(self):
self.regvalue = None
self.install_mode = None
self.emulator = None
+ def initialize_app(self):
+ version = inst.online.get_latest_git_tag()
+ app = QApplication(sys.argv)
+ ui_main = inst.ui
+ ui_main.show()
+ sys.exit(app.exec())
+
# Set which emulator to use for the installer depeanding on the selected emulator
- def set_emulator(self):
- selected_item = MainWindow.instances().selection_page.emulatorTreeWidget.currentItem()
- print (selected_item)
+ def set_emulator(self, selection_page):
+ selected_item = selection_page.emulatorTreeWidget.currentItem()
+ print(selected_item)
if not selected_item or not selected_item.parent():
- QMessageBox.warning(SelectionPage.window, "Selection Error", "Please select an emulator.")
- print ("Please select an emulator.")
+ QMessageBox.warning(selection_page.window, "Selection Error", "Please select an emulator.")
+ print("Please select an emulator.")
return
emulator_name = selected_item.text(0)
if self.emulator != emulator_name:
# Clear previous emulator settings
- qtui.labeldown.setText("Downloading: ")
- qtui.labelext.setText("Extracting: ")
- qtui.welcomerLabel.setText("")
+ inst.progress_bar_page_instance.labeldown.setText("Downloading: ")
+ inst.ui.labelext.setText("Extracting: ")
+ inst.ui.welcomerLabel.setText("")
# Set new emulator
self.emulator = emulator_name
@@ -64,48 +52,27 @@ def set_emulator(self):
# Update UI components with new emulator settings
reg_result = self.checkreg()
installed_emulator = "Not Installed" if reg_result is None else reg_result[1]
- qtui.installationPathLineEdit.setText(os.path.join(os.environ['LOCALAPPDATA'], self.emulator))
- qtui.labeldown.setText("Downloading: " + self.emulator)
- qtui.labelext.setText("Extracting: " + self.emulator)
- qtui.welcomerLabel.setText(f'Your currently selected emulator is {self.emulator} and current version is {installed_emulator}.')
+ inst.ui.installationPathLineEdit.setText(os.path.join(os.environ['LOCALAPPDATA'], self.emulator))
+ inst.ui.labeldown.setText("Downloading: " + self.emulator)
+ inst.ui.labelext.setText("Extracting: " + self.emulator)
+ inst.ui.welcomerLabel.setText(f'Your currently selected emulator is {self.emulator} and current version is {installed_emulator}.')
- print (self.emulator)
+ print(self.emulator)
self.checkreg()
self.disable_qt_buttons_if_installed()
- qtui.layout.setCurrentIndex(1)
+ inst.ui.layout.setCurrentIndex(1)
# Disable buttons depanding on if it the program is already installed
def disable_qt_buttons_if_installed(self):
regvalue = self.checkreg()
if regvalue is None:
- qtui.installButton.setEnabled(True)
- qtui.updateButton.setEnabled(False)
- qtui.uninstallButton.setEnabled(False)
+ inst.ui.installButton.setEnabled(True)
+ inst.ui.updateButton.setEnabled(False)
+ inst.ui.uninstallButton.setEnabled(False)
else:
- qtui.installButton.setEnabled(False)
- qtui.updateButton.setEnabled(True)
- qtui.uninstallButton.setEnabled(True)
-
- # Button clinking function
- def qt_button_click(self):
- button = qtui.sender()
- if button is qtui.installButton:
- self.install_mode = "Install"
- qtui.layout.setCurrentIndex(2)
- self.Add_releases_to_combobox()
- elif button is qtui.updateButton:
- self.emulator_updates()
- elif button is qtui.uninstallButton:
- self.install_mode = "Uninstall" # Unused for now
- self.uninstall()
- if button is qtui.install_emu_button:
- qtui.layout.setCurrentIndex(3)
- self.Prepare_Download()
- if button is qtui.backButton:
- current_index = qtui.layout.currentIndex()
- if current_index > 0:
- qtui.layout.setCurrentIndex(current_index - 1)
- return self.install_mode
+ inst.ui.installButton.setEnabled(False)
+ inst.ui.updateButton.setEnabled(True)
+ inst.ui.uninstallButton.setEnabled(True)
# Select installation path function
def InstallPath(self):
@@ -394,40 +361,6 @@ def uninstall(self):
QMessageBox.critical(qtui, "Error",("Failed to read the registry key. Try and reinstall again!"))
qtui.layout.setCurrentIndex(1)
-# Download Worker class to download the files
-class DownloadWorker(QThread):
- progress = pyqtSignal(int)
-
- def __init__(self, url, dest):
- super().__init__()
- self.url = url
- self.dest = dest
-
-
- @pyqtSlot()
- def do_download(self):
- try:
- response = requests.get(self.url, stream=True)
- total_size = int(response.headers.get('content-length', 0))
- if total_size == 0:
- print("The content-length of the response is zero.")
- return
-
- downloaded_size = 0
- with open(self.dest, 'wb') as file:
- for data in response.iter_content(1024):
- downloaded_size += len(data)
- file.write(data)
- progress_percentage = (downloaded_size / total_size) * 100
- self.progress.emit(int(progress_percentage))
- self.finished.emit()
- except Exception as e:
- QMessageBox.critical(QtUi, "Error",("Error doing download."))
- self.finished.emit()
-
if __name__ == "__main__":
- version = Online.get_latest_git_tag()
- app = QApplication(sys.argv)
- ui_window = MainWindow()
- ui_window.show()
- sys.exit(app.exec())
+ main = Main()
+ main.initialize_app()
diff --git a/ui/install_page.py b/ui/install_page.py
index b61e8be..cdbfb66 100644
--- a/ui/install_page.py
+++ b/ui/install_page.py
@@ -1,7 +1,7 @@
- # Install page
-from imports import *
-from main import Logic as main
-from header import Header
+from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QHBoxLayout, QGroupBox, QComboBox, QCheckBox, QLineEdit
+from PyQt6.QtCore import Qt
+from init_instances import inst
+from ui.header import Header
class InstallPage(QWidget):
def __init__(self):
@@ -23,10 +23,8 @@ def __init__(self):
self.startMenuShortcutCheckbox = QCheckBox("Create a start menu shortcut")
self.installationPathLineEdit = QLineEdit() # Browse for installation path widget
self.browseButton = QPushButton("Browse")
- self.browseButton.clicked.connect(main.InstallPath)
- self.install_emu_button = QPushButton('Install') # Install button
- self.install_emu_button.clicked.connect(main.qt_button_click)
- ## Add widgets / layouts
+ self.browseButton.clicked.connect(inst.main.InstallPath)
+ self.install_emu_button = QPushButton('Install') # Install button ## Add widgets / layouts
installLayout.addLayout(self.header) ### Icon self.header
installLayout.addWidget(InstalOpt) ### Instalation Option Label
installLayout.addWidget(self.installationSourceComboBox) ## Install Sorce Widget
diff --git a/ui/progress_bar.py b/ui/progress_bar.py
index 44575ba..e2b1349 100644
--- a/ui/progress_bar.py
+++ b/ui/progress_bar.py
@@ -1,5 +1,5 @@
-from imports import *
-from header import Header
+from PyQt6.QtWidgets import QWidget, QVBoxLayout, QProgressBar, QLabel
+from ui.header import Header
# Progress bar page
class ProgressBarPage(QWidget):
diff --git a/ui/selection_page.py b/ui/selection_page.py
index a628767..7aa2162 100644
--- a/ui/selection_page.py
+++ b/ui/selection_page.py
@@ -1,13 +1,13 @@
-from imports import *
-from main import Online
-from main import Logic
+from PyQt6.QtWidgets import QWidget, QVBoxLayout, QGroupBox, QTreeWidget, QTreeWidgetItem, QPushButton, QMessageBox
+from PyQt6.QtGui import QIcon
+from PyQt6.QtCore import Qt
+from init_instances import inst
- # Emulator Select Page
+# Emulator Select Page
class SelectionPage(QWidget):
def __init__(self):
super().__init__()
- self.troppical_database = Online.fetch_data(self)
self.emulatorSelectPage = QWidget()
emulatorSelectLayout = QVBoxLayout()
emulatorSelectGroup = QGroupBox("Select your emulator from the list")
@@ -20,35 +20,48 @@ def __init__(self):
emulatorSelectGroupLayout.addWidget(self.emulatorTreeWidget)
- # Keep track of emulator systems
- system_items = {}
-
- for troppical_api_data in self.troppical_database:
- emulator_system = troppical_api_data['emulator_system']
- emulator_name = troppical_api_data['emulator_name']
- emulator_desc = troppical_api_data.get('emulator_desc', '')
-
- # Fetch and decode the logo
- logo_url = troppical_api_data['emulator_logo']
- response = requests.get(logo_url)
- if response.status_code == 200:
- image_bytes = response.content
- qimage = QImage.fromData(QByteArray(image_bytes))
- pixmap = QPixmap.fromImage(qimage).scaled(32, 32, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation)
- icon = QIcon(pixmap)
- print(logo_url)
- else:
- QMessageBox.critical(self, "Failed to fetch logo", f"Failed to fetch logo for {emulator_name}. Status code: {response.status_code}")
- icon = QIcon()
+ # Set layout for the group and add to the main layout
+ emulatorSelectGroup.setLayout(emulatorSelectGroupLayout)
+ emulatorSelectLayout.addWidget(emulatorSelectGroup)
+ self.emulatorSelectPage.setLayout(emulatorSelectLayout) # Set the layout for the emulator selection page
+
+ # Next button to confirm selection
+ self.nextButton = QPushButton("Next")
+ self.nextButton.clicked.connect(lambda: inst.main_instance.set_emulator(self))
+ emulatorSelectLayout.addWidget(self.nextButton)
+
+ # Show initializing message
+ self.initializing_msg = QMessageBox(self)
+ self.initializing_msg.setWindowTitle("Troppical API")
+ self.initializing_msg.setText("Getting emulator data...")
+ self.initializing_msg.setStandardButtons(QMessageBox.StandardButton.NoButton)
+ self.initializing_msg.show()
+
+ # Start the secondary thread with the task and callback
+ inst.ui.start_secondary_thread(inst.online.filter_emulator_data, self.populate_emulator_tree)
+
+ def populate_emulator_tree(self, emulator_data):
+ # Iterate over each emulator item and add it to the tree
+ for emulator_name, data in emulator_data.items():
+ emulator_system = data['system']
+ emulator_desc = data['description']
+ icon = data['icon']
+ print(f"Fetching data for {emulator_name}")
# Check if the emulator system already has a tree item, if not create one
- if emulator_system not in system_items:
+ system_item = None
+ for i in range(self.emulatorTreeWidget.topLevelItemCount()):
+ item = self.emulatorTreeWidget.topLevelItem(i)
+ if item.text(0) == emulator_system:
+ system_item = item
+ break
+
+ if not system_item:
system_item = QTreeWidgetItem(self.emulatorTreeWidget)
system_item.setText(0, emulator_system)
- system_item.setExpanded(True) # Uncollapse the category by default
- system_items[emulator_system] = system_item
- else:
- system_item = system_items[emulator_system]
+ system_item.setExpanded(True)
+ # Make the system item unselectable
+ system_item.setFlags(system_item.flags() & ~Qt.ItemFlag.ItemIsSelectable)
# Add the emulator to the appropriate tree item
emulator_item = QTreeWidgetItem(system_item)
@@ -56,21 +69,16 @@ def __init__(self):
emulator_item.setIcon(0, icon)
emulator_item.setToolTip(0, emulator_desc)
+ # Close the initializing message
+ self.initializing_msg.hide()
+
# Sort the systems and emulators alphabetically
self.emulatorTreeWidget.sortItems(0, Qt.SortOrder.AscendingOrder)
for i in range(self.emulatorTreeWidget.topLevelItemCount()):
system_item = self.emulatorTreeWidget.topLevelItem(i)
system_item.sortChildren(0, Qt.SortOrder.AscendingOrder)
- # Set layout for the group and add to the main layout
- emulatorSelectGroup.setLayout(emulatorSelectGroupLayout)
- emulatorSelectLayout.addWidget(emulatorSelectGroup)
- self.emulatorSelectPage.setLayout(emulatorSelectLayout) # Set the layout for the emulator selection page
- # Next button to confirm selection
- self.nextButton = QPushButton("Next")
- self.nextButton.clicked.connect(Logic.set_emulator)
- emulatorSelectLayout.addWidget(self.nextButton)
def get_selected_emulator(self):
selected_item = self.emulatorTreeWidget.currentItem()
diff --git a/ui/ui_main.py b/ui/ui_main.py
new file mode 100644
index 0000000..a54ec27
--- /dev/null
+++ b/ui/ui_main.py
@@ -0,0 +1,137 @@
+# Page imports
+from PyQt6.QtWidgets import QApplication, QMainWindow, QWidget, QStackedLayout, QMessageBox, QLabel
+from stylesheet import Style
+import requests
+from PyQt6.QtGui import QIcon, QImage, QPixmap
+from PyQt6.QtCore import Qt, QByteArray, QObject, QThread, pyqtSignal
+from init_instances import inst
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ super().__init__()
+ self.setWindowTitle(f'Troppical - {"version"}') # Window name with version
+ self.setCentralWidget(QWidget(self)) # Set a central widget
+ self.layout = QStackedLayout(self.centralWidget()) # Set the layout on the central widget
+ self.setMaximumSize(1000, 720) # Set the maximum window size to 1280x720
+ self.setMinimumSize(1000, 720) # Set the minimum window size to 800x600
+ # Set the window icon
+ # icon_path = os.path.join(sys._MEIPASS, 'icon.ico')
+ # self.setWindowIcon(QIcon(icon_path))
+ self.load_stylesheet()
+ # Initialize the first page
+ self.layout.addWidget(inst.wel.welcomePage)
+
+ # Connect buttons after initialization
+ self.connect_buttons()
+
+ self.shared_thread = None # Initialize shared_thread to None
+
+ def connect_buttons(self):
+ # Connect buttons to the qt_button_click method
+ inst.wel.manageButton.clicked.connect(self.qt_button_click)
+ inst.act.installButton.clicked.connect(self.qt_button_click)
+ inst.act.updateButton.clicked.connect(self.qt_button_click)
+ inst.act.uninstallButton.clicked.connect(self.qt_button_click)
+ inst.install.install_emu_button.clicked.connect(self.qt_button_click)
+ inst.act.backButton.clicked.connect(self.qt_button_click)
+
+ def widget_2_layout(self):
+ # Add actual pages instead of placeholders
+ self.layout.addWidget(inst.sel.emulatorSelectPage)
+ self.layout.addWidget(inst.wel.welcomePage)
+ self.layout.addWidget(inst.install.installPage)
+ self.layout.addWidget(inst.bar.progressBarPage)
+ self.layout.addWidget(inst.finish.finishPage)
+
+ def initialize_page(self, index):
+ # Mapping of index to page attributes and instances
+ page_map = {
+ 1: ('selection_page', inst.sel, 'emulatorSelectPage'),
+ 2: ('welcome_page', inst.wel, 'welcomePage'),
+ 3: ('install_page', inst.install, 'installPage'),
+ 4: ('progress_bar_page', inst.bar, 'progressBarPage'),
+ 5: ('finish_page', inst.finish, 'finishPage')
+ }
+
+ # Initialize and replace the widget for the given index
+ if index in page_map:
+ attr_name, instance, widget_name = page_map[index]
+ setattr(self, attr_name, instance)
+ widget = getattr(instance, widget_name)
+ self.layout.replaceWidget(self.layout.widget(index), widget)
+ self.layout.setCurrentIndex(index)
+ print(f"Page {index} initialized: {attr_name}")
+
+ def load_stylesheet(self):
+ self.setStyleSheet(Style.dark_stylesheet)
+
+ def qt_button_click(self):
+ self.widget_2_layout()
+ button = self.sender()
+
+ # Define a mapping of buttons to their actions
+ button_actions = {
+ inst.wel.manageButton: 1, # Assuming index 1 is the manage page
+ inst.act.installButton: 2,
+ inst.act.updateButton: inst.main.emulator_updates,
+ inst.act.uninstallButton: self.handle_uninstall,
+ inst.install.install_emu_button: 3,
+ inst.act.backButton: self.handle_back
+ }
+
+ # Execute the corresponding action if the button is in the mapping
+ action = button_actions.get(button)
+ if isinstance(action, int):
+ self.initialize_page(action)
+ elif callable(action):
+ action()
+
+ def handle_manage(self):
+ self.layout.setCurrentIndex(1) # Assuming index 1 is the desired page
+ def handle_install(self):
+ inst.main.install_mode = "Install"
+ self.layout.setCurrentIndex(2)
+ inst.main.Add_releases_to_combobox()
+ def handle_uninstall(self):
+ inst.main.install_mode = "Uninstall" # Unused for now
+ inst.main.uninstall()
+ def handle_install_emu(self):
+ self.layout.setCurrentIndex(3)
+ inst.main.Prepare_Download()
+ def handle_back(self):
+ current_index = self.layout.currentIndex()
+ if current_index > 0:
+ self.layout.setCurrentIndex(current_index - 1)
+
+ def start_secondary_thread(self, task, callback, *args, **kwargs):
+ if self.shared_thread is not None:
+ self.shared_thread.quit()
+ self.shared_thread.wait()
+
+ self.shared_thread = QThread()
+ worker = inst.secondary_thread
+ worker.set_task(task, *args, **kwargs)
+ worker.moveToThread(self.shared_thread)
+ worker.finished.connect(callback)
+ self.shared_thread.started.connect(worker.run)
+ self.shared_thread.start()
+
+class Worker(QObject):
+ finished = pyqtSignal(dict)
+
+ def __init__(self):
+ super().__init__()
+ self.task = None
+ self.args = ()
+ self.kwargs = {}
+
+ def set_task(self, task, *args, **kwargs):
+ self.task = task
+ self.args = args
+ self.kwargs = kwargs
+
+ def run(self):
+ if self.task is not None:
+ result = self.task(*self.args, **self.kwargs)
+ self.finished.emit(result)
diff --git a/ui/welcome.py b/ui/welcome.py
new file mode 100644
index 0000000..c412742
--- /dev/null
+++ b/ui/welcome.py
@@ -0,0 +1,43 @@
+from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QHBoxLayout
+from PyQt6.QtCore import Qt
+from ui.header import Header
+from init_instances import inst
+
+# Welcome page
+class InitPage(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.welcomePage = QWidget()
+
+ # Layout and groups
+ welcomeLayout = QVBoxLayout()
+ self.welcomePage.setLayout(welcomeLayout)
+
+ # Add header
+ # self.Header = Header().header() # Initialize the header
+ # welcomeLayout.addLayout(self.Header)
+
+ # Welcome label
+ welcomeLabel = QLabel("Welcome to Troppical Installer!")
+ welcomeLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) # Center the text horizontally
+ welcomeLayout.addWidget(welcomeLabel)
+
+ # Create a horizontal layout for the buttons
+ buttonLayout = QHBoxLayout()
+
+ # Manage button
+ self.manageButton = QPushButton("Manage")
+ self.manageButton.setFixedSize(200, 100) # Set size for large rectangular shape
+
+ # Configure button
+ self.configureButton = QPushButton("Configure")
+ self.configureButton.setFixedSize(200, 100) # Set size for large rectangular shape
+ self.configureButton.setToolTip("WIP: Coming soon!")
+ self.configureButton.setEnabled(False) # Set the button to be grayed out
+
+ # Add buttons to the layout
+ buttonLayout.addWidget(self.manageButton)
+ buttonLayout.addWidget(self.configureButton)
+
+ # Add button layout to the main layout
+ welcomeLayout.addLayout(buttonLayout)
\ No newline at end of file