diff --git a/.gitignore b/.gitignore
index 6e193ba..f7b4df5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ citra_util.spec
troppical.spec
.old
*.spec
+/ui/__pycache__
diff --git a/imports.py b/imports.py
new file mode 100644
index 0000000..3126f3d
--- /dev/null
+++ b/imports.py
@@ -0,0 +1,15 @@
+from PyQt6.QtWidgets import QMainWindow, QApplication, QLabel, QPushButton, QVBoxLayout, QWidget, QCheckBox, QStackedLayout, QHBoxLayout, QGroupBox, QComboBox, QProgressBar, QLineEdit, QMessageBox, QFileDialog, QInputDialog, QTreeWidget, QTreeWidgetItem
+from PyQt6.QtGui import QPixmap, QIcon, QImage
+from PyQt6.QtCore import Qt, QThread, pyqtSignal, QByteArray, QFile, pyqtSlot
+import requests
+import os
+import subprocess
+import sys
+import json
+from zipfile import ZipFile
+import shutil
+import tempfile
+from icons import styledark_rc
+import win32com.client
+import winreg
+from pathlib import Path
diff --git a/main.py b/main.py
index bd0ce08..49fd8a5 100644
--- a/main.py
+++ b/main.py
@@ -1,21 +1,21 @@
-import os
-import subprocess
-import sys
-import requests
-import json
-from PyQt6.QtWidgets import QMainWindow, QApplication, QLabel, QPushButton, QVBoxLayout, QWidget, QCheckBox, QStackedLayout, QHBoxLayout, QGroupBox, QComboBox, QProgressBar, QLineEdit, QMessageBox, QFileDialog, QInputDialog, QTreeWidget, QTreeWidgetItem
-from PyQt6.QtGui import QPixmap, QIcon, QImage
-from PyQt6.QtCore import Qt, QThread, pyqtSignal, QByteArray, QFile, pyqtSlot
-from zipfile import ZipFile
-import shutil
-import tempfile
-from icons import styledark_rc
-import win32com.client
-import winreg
-from stylesheet import Style
-from pathlib import Path
-
-def get_latest_git_tag():
+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:
@@ -32,264 +32,19 @@ def get_latest_git_tag():
print(f"Failed to get latest GitHub release tag: {e}")
return tag
-class QtUi(QMainWindow, Style):
- def __init__(self):
- super().__init__()
- self.logic = Logic()
- self.logic.fetch_google_sheet_data()
- # Show a message box indicating that Troppical is starting
- self.loading_message_box = QMessageBox(self)
- self.loading_message_box.setIcon(QMessageBox.Icon.Information)
- self.loading_message_box.setText("Troppical is starting.... If you get a notifcation that Troppical is not responding, ignore it.")
- self.loading_message_box.setWindowModality(Qt.WindowModality.ApplicationModal)
- close_button = self.loading_message_box.addButton(QMessageBox.StandardButton.Close)
- close_button.hide()
- self.loading_message_box.show()
- QApplication.processEvents()
- self.ui() # Init UI
- self.load_stylesheet()
-
- # Logo Header
- def Header(self):
- # Header Layout
- self.headerLayout = QVBoxLayout()
- self.headerLayout.setContentsMargins(0, 20, 0, 0)
- # Icon Widget
- iconLabel = QLabel()
- icon_path = os.path.join(sys._MEIPASS, 'icon.ico')
- icon = QIcon(icon_path)
- pixmap = icon.pixmap(180, 180) # Specify the size directly
- iconLabel.setPixmap(pixmap)
- # Text Widget
- label = QLabel("Troppical")
- # Set Widgets
- self.headerLayout.addWidget(iconLabel, alignment=Qt.AlignmentFlag.AlignCenter)
- self.headerLayout.addWidget(label, alignment=Qt.AlignmentFlag.AlignCenter)
- return self.headerLayout
-
- # Widgets
- def ui(self):
- # Init Window
- 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.selection_page()
-
-
- # Emulator Select Page
- def selection_page(self):
- self.emulatorSelectPage = QWidget()
- emulatorSelectLayout = QVBoxLayout()
- emulatorSelectGroup = QGroupBox("Select your emulator from the list")
- emulatorSelectGroupLayout = QVBoxLayout()
-
- # Create a QTreeWidget
- self.emulatorTreeWidget = QTreeWidget()
- self.emulatorTreeWidget.setHeaderHidden(True)
- self.emulatorTreeWidget.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
-
- emulatorSelectGroupLayout.addWidget(self.emulatorTreeWidget)
-
- # Keep track of emulator systems
- system_items = {}
-
- for troppical_api_data in self.logic.troppical_api:
- 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()
-
- # Check if the emulator system already has a tree item, if not create one
- if emulator_system not in system_items:
- 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]
-
- # Add the emulator to the appropriate tree item
- emulator_item = QTreeWidgetItem(system_item)
- emulator_item.setText(0, emulator_name)
- emulator_item.setIcon(0, icon)
- emulator_item.setToolTip(0, emulator_desc)
-
- # 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
- self.layout.addWidget(self.emulatorSelectPage)
-
- # Next button to confirm selection
- self.nextButton = QPushButton("Next")
- self.nextButton.clicked.connect(self.logic.set_emulator)
- emulatorSelectLayout.addWidget(self.nextButton)
- # Welcome page
- self.welcomePage = QWidget()
- ## Layout and gorup
- welcomerLayout = QVBoxLayout()
- welcomerGroup = QGroupBox("")
- welcomerGroupLayout = QVBoxLayout()
- buttonsLayout = QHBoxLayout()
- welcomerGroup.setLayout(welcomerGroupLayout)
- self.welcomePage.setLayout(welcomerLayout) # Set the welcomepage layout
- ## Widgets
- self.welcomerLabel = QLabel()
- self.welcomerLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.installButton = QPushButton('Install')
- self.installButton.clicked.connect(self.logic.qt_button_click)
- self.updateButton = QPushButton('Check for Updates')
- self.updateButton.clicked.connect(self.logic.qt_button_click)
- self.uninstallButton = QPushButton('Uninstall')
- self.uninstallButton.clicked.connect(self.logic.qt_button_click)
- self.backButton = QPushButton("Back")
- self.backButton.setFixedSize(80, 30)
- self.backButton.clicked.connect(self.logic.qt_button_click)
- ## Add widgets / layouts
- welcomerLayout.addLayout(self.Header())
- welcomerLayout.addWidget(welcomerGroup)
- welcomerGroupLayout.addWidget(self.welcomerLabel)
- buttonsLayout.addWidget(self.installButton)
- buttonsLayout.addWidget(self.updateButton)
- buttonsLayout.addWidget(self.uninstallButton)
- welcomerLayout.addLayout(buttonsLayout)
- welcomerLayout.insertWidget(0, self.backButton)
- ## Add the welcome page to the layout
- self.layout.addWidget(self.welcomePage)
-
- # Install page
- self.installPage = QWidget()
- ## Layout and gorup
- installLayout = QVBoxLayout()
- checkboxLayout = QVBoxLayout()
- checkboxGroup = QGroupBox("Do you want to create shortcuts?") # Checkboxes
- checkboxGroup.setLayout(checkboxLayout) # Set the layout of Checkboxes
- pathSelectorLayout = QHBoxLayout() # Browse widget layout
- self.installPage.setLayout(installLayout) # Set the installpage layout
- ## Widgets
- InstalOpt = QLabel('Installation Options')
- InstalOpt.setAlignment(Qt.AlignmentFlag.AlignCenter)
- self.installationSourceComboBox = QComboBox() # Dropdown menu
- self.desktopShortcutCheckbox = QCheckBox("Create a desktop shortcut") # Checkboxes
- self.startMenuShortcutCheckbox = QCheckBox("Create a start menu shortcut")
- self.installationPathLineEdit = QLineEdit() # Browse for installation path widget
- self.browseButton = QPushButton("Browse")
- self.browseButton.clicked.connect(self.logic.InstallPath)
- self.install_emu_button = QPushButton('Install') # Install button
- self.install_emu_button.clicked.connect(self.logic.qt_button_click)
- ## Add widgets / layouts
- installLayout.addLayout(self.Header()) ### Icon self.header
- installLayout.addWidget(InstalOpt) ### Instalation Option Label
- installLayout.addWidget(self.installationSourceComboBox) ## Install Sorce Widget
- pathSelectorLayout.addWidget(self.installationPathLineEdit) ## Browse Widget
- pathSelectorLayout.addWidget(self.browseButton)
- installLayout.addLayout(pathSelectorLayout)
- checkboxLayout.addWidget(self.desktopShortcutCheckbox) # Checkboxes
- checkboxLayout.addWidget(self.startMenuShortcutCheckbox)
- installLayout.addWidget(checkboxGroup)
- installLayout.addWidget(self.install_emu_button)
- ## Add the install page to the layout
- self.layout.addWidget(self.installPage)
-
- # Progress bar page
- self.progressBarPage = QWidget()
- ## Layout and groups
- progressBarLayout = QVBoxLayout()
- self.progressBarPage.setLayout(progressBarLayout)
- ## Widgets
- self.downloadProgressBar = QProgressBar()
- self.downloadProgressBar.setRange(0, 100) # Progress bar Widgets
- self.extractionProgressBar = QProgressBar()
- self.extractionProgressBar.setRange(0, 100)
- self.labeldown = QLabel("Downloading: ")
- self.labelext = QLabel("Extracting: ")
- ## Add widgets / layouts
- progressBarLayout.addLayout(self.Header()) # Add the icon self.header
- progressBarLayout.addWidget(self.labeldown)
- progressBarLayout.addWidget(self.downloadProgressBar)
- progressBarLayout.addWidget(self.labelext)
- progressBarLayout.addWidget(self.extractionProgressBar)
- # Add the progress bar page to the layout
- self.layout.addWidget(self.progressBarPage)
-
- # Finish page
- self.finishPage = QWidget()
- ## Layout and groups
- finishLayout = QVBoxLayout()
- self.finishPage.setLayout(finishLayout)
- ## Widgets
- finishLabel = QLabel("Installation Complete!")
- finishLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) # Center the text horizontally
- finishButton = QPushButton("Finish")
- finishButton.clicked.connect(self.close)
- installAnotherButton = QPushButton("Install another emulator")
- installAnotherButton.clicked.connect(lambda: (
- self.layout.setCurrentIndex(0),
- self.downloadProgressBar.setValue(0),
- self.extractionProgressBar.setValue(0),
- setattr(self.logic, 'install_mode', None),
- setattr(self.logic, 'selection', None)
- ))
- ## Add widgets / layouts
- finishLayout.addLayout(self.Header()) # Add the icon self.header
- finishLayout.addWidget(finishLabel)
- # Create a horizontal layout for the buttons
- buttonLayout = QHBoxLayout()
- buttonLayout.addWidget(finishButton)
- buttonLayout.addWidget(installAnotherButton)
- finishLayout.addLayout(buttonLayout)
- # Add the progress bar page to the layout
- self.layout.addWidget(self.finishPage)
-
- self.loading_message_box.close()
-
- def load_stylesheet(app):
- app.setStyleSheet(Style.dark_stylesheet)
-
class Logic:
def __init__(self):
- self.regvalue = None
+ self.regvalue = None
self.install_mode = None
self.emulator = None
-
- def fetch_google_sheet_data(self):
- url = "https://script.googleusercontent.com/macros/echo?user_content_key=Hw-G9S_OHELhOUAsT-oQr8ux2HPMIpva3U1w0Su7P1ZYrr1ngXyqlN6LBhfev1taFoRtJ07w_KDhWVbMaBaeJ3c86H4e0k8Xm5_BxDlH2jW0nuo2oDemN9CCS2h10ox_1xSncGQajx_ryfhECjZEnL69XsVZDOhipZMwrhs3JioNozSVnp4Chm6SveAF_nlUSMgTaOh-zk0bQ5F9LtyaiRZKic-heYuYVV866SySaVfv-0TkTPKcCtz9Jw9Md8uu&lib=MmjrdpKGbUxdyxLDAqWkoFhoZjK-0W8qS"
- response = requests.get(url)
- if response.status_code == 200:
- all_data = response.json()
- self.troppical_api = [item for item in all_data if item.get('emulator_platform') != 'android']
- return self.troppical_api
- else:
- print("Failed to fetch data:", response.status_code)
- # Set which emulator to use for the installer depeanding on the selected emulator
+ # Set which emulator to use for the installer depeanding on the selected emulator
def set_emulator(self):
- selected_item = qtui.emulatorTreeWidget.currentItem()
+ selected_item = MainWindow.instances().selection_page.emulatorTreeWidget.currentItem()
+ print (selected_item)
if not selected_item or not selected_item.parent():
- QMessageBox.warning(qtui, "Selection Error", "Please select an emulator.")
+ QMessageBox.warning(SelectionPage.window, "Selection Error", "Please select an emulator.")
+ print ("Please select an emulator.")
return
emulator_name = selected_item.text(0)
@@ -321,15 +76,15 @@ def set_emulator(self):
# Disable buttons depanding on if it the program is already installed
def disable_qt_buttons_if_installed(self):
- regvalue = self.checkreg()
+ regvalue = self.checkreg()
if regvalue is None:
- qtui.installButton.setEnabled(True)
+ qtui.installButton.setEnabled(True)
qtui.updateButton.setEnabled(False)
- qtui.uninstallButton.setEnabled(False)
+ qtui.uninstallButton.setEnabled(False)
else:
- qtui.installButton.setEnabled(False)
+ qtui.installButton.setEnabled(False)
qtui.updateButton.setEnabled(True)
- qtui.uninstallButton.setEnabled(True)
+ qtui.uninstallButton.setEnabled(True)
# Button clinking function
def qt_button_click(self):
@@ -351,7 +106,7 @@ def qt_button_click(self):
if current_index > 0:
qtui.layout.setCurrentIndex(current_index - 1)
return self.install_mode
-
+
# Select installation path function
def InstallPath(self):
self.Install_Dir = qtui.installationPathLineEdit.text()
@@ -361,23 +116,23 @@ def InstallPath(self):
# Set Emulator name to the selected directory path
self.Install_Dir = os.path.join(selectedDirectory, self.emulator)
qtui.installationPathLineEdit.setText(self.Install_Dir)
- return self.Install_Dir
+ return self.Install_Dir
- # Add the various version to the selection combobox
+ # Add the various version to the selection combobox
def Add_releases_to_combobox(self):
print (self.releases_url)
response = requests.get(self.releases_url)
- self.releases = response.json()[:5]
+ self.releases = response.json()[:5]
# Clear the combo box before adding new items
qtui.installationSourceComboBox.clear()
# Remove or add items based on the emulator
- for release in self.releases:
+ for release in self.releases:
qtui.installationSourceComboBox.addItem(release['tag_name'])
-
- # Update button function
- def emulator_updates(self):
+
+ # Update button function
+ def emulator_updates(self):
self.checkreg() # Initialise the reg value function
installed_emulator = self.updatevalue
response = requests.get(self.releases_url + "/latest")
@@ -405,7 +160,7 @@ def emulator_updates(self):
pass
else:
QMessageBox.information(qtui, "No Update Found", f"You are up to date with the latest version of {self.emulator}", QMessageBox.StandardButton.Ok)
-
+
# Preparing which file to downlaod
def Prepare_Download(self):
print (self.install_mode)
@@ -413,7 +168,7 @@ def Prepare_Download(self):
if reg_key is not None:
UpdateChannelValue = reg_key[1]
else:
- UpdateChannelValue = None
+ UpdateChannelValue = None
self.selection = qtui.installationSourceComboBox.currentText()
@@ -469,7 +224,7 @@ def Prepare_Download(self):
self.createreg()
else:
QMessageBox.critical(qtui, "Error", f"No suitable Windows download found for {self.emulator} in the latest release. Please try another release or check for updates.")
- # Download function
+ # Download function
def Download_Emulator(self):
temp_file = tempfile.NamedTemporaryFile(delete=False).name
if self.install_mode == "Install":
@@ -491,7 +246,7 @@ def Download_Emulator(self):
def on_download_finished(self):
self.extract_and_install(self.download_worker.dest, self.installationPath)
- # Extract and install function
+ # Extract and install function
def extract_and_install(self, temp_file, extract_to):
extract_to = self.installationPath
# Rename the temporary file to have a .zip extension and create a temporary extraction folder
@@ -530,24 +285,24 @@ def move_files(self, extract_to):
shutil.copytree(src_item_path, dest_item_path, dirs_exist_ok=True)
else:
shutil.copy2(src_item_path, dest_item_path)
-
+
# Mark the installation as complete
self.installation_complete()
# Install is complete
def installation_complete(self):
qtui.extractionProgressBar.setValue(100)
-
+
# Fetch the executable path from the troppical_api data
for troppical_api_data in self.troppical_api:
if troppical_api_data['emulator_name'] == self.emulator:
exe_name = troppical_api_data.get('exe_path', '')
- executable_path = os.path.normpath(os.path.join(qtui.installationPathLineEdit.text(), exe_name))
+ executable_path = os.path.normpath(os.path.join(qtui.installationPathLineEdit.text(), exe_name))
print (executable_path)
if executable_path is None:
QMessageBox.critical(qtui, "Error", f"Executable path not found for emulator: {self.emulator}")
return
-
+
if qtui.desktopShortcutCheckbox.isChecked():
self.define_shortcut(executable_path, 'desktop')
if qtui.startMenuShortcutCheckbox.isChecked():
@@ -564,12 +319,12 @@ def checkreg(self):
self.asset_version, regtype = winreg.QueryValueEx(self.registry_key, 'Asset_version')
winreg.CloseKey(self.registry_key)
return self.regvalue, self.updatevalue, self.asset_version
- except FileNotFoundError:
+ except FileNotFoundError:
pass
- # Function to create the reg values
- def createreg (self):
+ # Function to create the reg values
+ def createreg (self):
winreg.CreateKey(winreg.HKEY_CURRENT_USER, f"Software\\{self.emulator}")
- self.registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, f"Software\\{self.emulator}", 0,
+ self.registry_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, f"Software\\{self.emulator}", 0,
winreg.KEY_WRITE)
if self.install_mode == "Install":
winreg.SetValueEx(self.registry_key, 'Install_Dir', 0, winreg.REG_SZ, qtui.installationPathLineEdit.text())
@@ -577,13 +332,13 @@ def createreg (self):
winreg.SetValueEx(self.registry_key, 'Version', 0, winreg.REG_SZ, self.selection)
winreg.CloseKey(self.registry_key)
- # Function to create the shortcuts
+ # Function to create the shortcuts
def define_shortcut(self, target, location_type):
if location_type == 'desktop':
path = os.path.join(os.environ['USERPROFILE'], 'Desktop')
elif location_type == 'start_menu':
path = os.path.join(os.environ['APPDATA'], 'Microsoft', 'Windows', 'Start Menu', 'Programs')
-
+
filename = f'{self.emulator}.lnk'
shortcut_path = os.path.join(path, filename)
@@ -604,7 +359,7 @@ def define_shortcut(self, target, location_type):
except Exception as e:
QMessageBox.critical(qtui, "Error", f"Failed to create shortcut: {e}")
- # Uninstall function
+ # Uninstall function
def uninstall(self):
self.checkreg()
print (self.regvalue)
@@ -628,16 +383,16 @@ def uninstall(self):
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, f"Software\\{self.emulator}")
QMessageBox.information(qtui, "Uninstall", f"{self.emulator} has been successfully uninstalled.")
self.emulator = None
- qtui.layout.setCurrentIndex(0)
+ qtui.layout.setCurrentIndex(0)
else:
QMessageBox.critical(qtui, "Error", "The directory might have been moved or deleted. Please reinstall the program.")
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, f"Software\\{self.emulator}")
- qtui.updateButton.setEnabled(False)
- qtui.uninstallButton.setEnabled(False)
- qtui.installButton.setEnabled(True)
+ qtui.updateButton.setEnabled(False)
+ qtui.uninstallButton.setEnabled(False)
+ qtui.installButton.setEnabled(True)
else:
QMessageBox.critical(qtui, "Error",("Failed to read the registry key. Try and reinstall again!"))
- qtui.layout.setCurrentIndex(1)
+ qtui.layout.setCurrentIndex(1)
# Download Worker class to download the files
class DownloadWorker(QThread):
@@ -671,9 +426,8 @@ def do_download(self):
self.finished.emit()
if __name__ == "__main__":
- version = get_latest_git_tag()
+ version = Online.get_latest_git_tag()
app = QApplication(sys.argv)
- qtui = QtUi()
- qtui.show()
- self = Logic()
+ ui_window = MainWindow()
+ ui_window.show()
sys.exit(app.exec())
diff --git a/ui/finish_page.py b/ui/finish_page.py
new file mode 100644
index 0000000..6409296
--- /dev/null
+++ b/ui/finish_page.py
@@ -0,0 +1,35 @@
+
+from imports import *
+from main import Logic as main
+from header import Header
+
+# Finish page
+class FinishPage(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.Header = Header.header(self)
+ self.finishPage = QWidget()
+ ## Layout and groups
+ finishLayout = QVBoxLayout()
+ self.finishPage.setLayout(finishLayout)
+ ## Widgets
+ finishLabel = QLabel("Installation Complete!")
+ finishLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) # Center the text horizontally
+ finishButton = QPushButton("Finish")
+ finishButton.clicked.connect(self.close)
+ installAnotherButton = QPushButton("Install another emulator")
+ installAnotherButton.clicked.connect(lambda: (
+ self.layout.setCurrentIndex(0),
+ self.downloadProgressBar.setValue(0),
+ self.extractionProgressBar.setValue(0),
+ setattr(main, 'install_mode', None),
+ setattr(main, 'selection', None)
+ ))
+ ## Add widgets / layouts
+ finishLayout.addLayout(self.Header) # Add the icon self.header
+ finishLayout.addWidget(finishLabel)
+ # Create a horizontal layout for the buttons
+ buttonLayout = QHBoxLayout()
+ buttonLayout.addWidget(finishButton)
+ buttonLayout.addWidget(installAnotherButton)
+ finishLayout.addLayout(buttonLayout)
diff --git a/ui/header.py b/ui/header.py
new file mode 100644
index 0000000..a70d65f
--- /dev/null
+++ b/ui/header.py
@@ -0,0 +1,19 @@
+from imports import *
+
+class Header():
+ def header(self):
+ # Header Layout
+ self.headerLayout = QVBoxLayout()
+ self.headerLayout.setContentsMargins(0, 20, 0, 0)
+ # Icon Widget
+ iconLabel = QLabel()
+ # icon_path = os.path.join(sys._MEIPASS, 'icon.ico')
+ # icon = QIcon(icon_path)
+ # pixmap = icon.pixmap(180, 180) # Specify the size directly
+ # iconLabel.setPixmap(pixmap)
+ # Text Widget
+ label = QLabel("Troppical")
+ # Set Widgets
+ self.headerLayout.addWidget(iconLabel, alignment=Qt.AlignmentFlag.AlignCenter)
+ self.headerLayout.addWidget(label, alignment=Qt.AlignmentFlag.AlignCenter)
+ return self.headerLayout
diff --git a/ui/install_page.py b/ui/install_page.py
new file mode 100644
index 0000000..b61e8be
--- /dev/null
+++ b/ui/install_page.py
@@ -0,0 +1,39 @@
+ # Install page
+from imports import *
+from main import Logic as main
+from header import Header
+
+class InstallPage(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.header = Header.header(self)
+ self.installPage = QWidget()
+ ## Layout and gorup
+ installLayout = QVBoxLayout()
+ checkboxLayout = QVBoxLayout()
+ checkboxGroup = QGroupBox("Do you want to create shortcuts?") # Checkboxes
+ checkboxGroup.setLayout(checkboxLayout) # Set the layout of Checkboxes
+ pathSelectorLayout = QHBoxLayout() # Browse widget layout
+ self.installPage.setLayout(installLayout) # Set the installpage layout
+ ## Widgets
+ InstalOpt = QLabel('Installation Options')
+ InstalOpt.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ self.installationSourceComboBox = QComboBox() # Dropdown menu
+ self.desktopShortcutCheckbox = QCheckBox("Create a desktop shortcut") # Checkboxes
+ 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
+ installLayout.addLayout(self.header) ### Icon self.header
+ installLayout.addWidget(InstalOpt) ### Instalation Option Label
+ installLayout.addWidget(self.installationSourceComboBox) ## Install Sorce Widget
+ pathSelectorLayout.addWidget(self.installationPathLineEdit) ## Browse Widget
+ pathSelectorLayout.addWidget(self.browseButton)
+ installLayout.addLayout(pathSelectorLayout)
+ checkboxLayout.addWidget(self.desktopShortcutCheckbox) # Checkboxes
+ checkboxLayout.addWidget(self.startMenuShortcutCheckbox)
+ installLayout.addWidget(checkboxGroup)
+ installLayout.addWidget(self.install_emu_button)
diff --git a/ui/progress_bar.py b/ui/progress_bar.py
new file mode 100644
index 0000000..44575ba
--- /dev/null
+++ b/ui/progress_bar.py
@@ -0,0 +1,25 @@
+from imports import *
+from header import Header
+
+ # Progress bar page
+class ProgressBarPage(QWidget):
+ def __init__(self):
+ super().__init__()
+ self.Header = Header.header(self)
+ self.progressBarPage = QWidget()
+ ## Layout and groups
+ progressBarLayout = QVBoxLayout()
+ self.progressBarPage.setLayout(progressBarLayout)
+ ## Widgets
+ self.downloadProgressBar = QProgressBar()
+ self.downloadProgressBar.setRange(0, 100) # Progress bar Widgets
+ self.extractionProgressBar = QProgressBar()
+ self.extractionProgressBar.setRange(0, 100)
+ self.labeldown = QLabel("Downloading: ")
+ self.labelext = QLabel("Extracting: ")
+ ## Add widgets / layouts
+ progressBarLayout.addLayout(self.Header) # Add the icon self.header
+ progressBarLayout.addWidget(self.labeldown)
+ progressBarLayout.addWidget(self.downloadProgressBar)
+ progressBarLayout.addWidget(self.labelext)
+ progressBarLayout.addWidget(self.extractionProgressBar)
diff --git a/ui/selection_page.py b/ui/selection_page.py
new file mode 100644
index 0000000..a628767
--- /dev/null
+++ b/ui/selection_page.py
@@ -0,0 +1,82 @@
+from imports import *
+from main import Online
+from main import Logic
+
+ # 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")
+ emulatorSelectGroupLayout = QVBoxLayout()
+
+ # Create a QTreeWidget
+ self.emulatorTreeWidget = QTreeWidget()
+ self.emulatorTreeWidget.setHeaderHidden(True)
+ self.emulatorTreeWidget.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOn)
+
+ 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()
+
+ # Check if the emulator system already has a tree item, if not create one
+ if emulator_system not in system_items:
+ 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]
+
+ # Add the emulator to the appropriate tree item
+ emulator_item = QTreeWidgetItem(system_item)
+ emulator_item.setText(0, emulator_name)
+ emulator_item.setIcon(0, icon)
+ emulator_item.setToolTip(0, emulator_desc)
+
+ # 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()
+ print(selected_item)
+ if not selected_item or not selected_item.parent():
+ QMessageBox.warning(self, "Selection Error", "Please select an emulator.")
+ print("Please select an emulator.")
+ return None
+ return selected_item
diff --git a/ui/welcome_page.py b/ui/welcome_page.py
new file mode 100644
index 0000000..c7d1d5e
--- /dev/null
+++ b/ui/welcome_page.py
@@ -0,0 +1,38 @@
+from imports import *
+from main import Logic as main
+from header import Header
+
+class WelcomePage(QWidget,):
+ def __init__(self):
+ super().__init__()
+ self.header = Header.header(self)
+ # Welcome page
+ self.welcomePage = QWidget()
+ ## Layout and gorup
+ welcomerLayout = QVBoxLayout()
+ welcomerGroup = QGroupBox("")
+ welcomerGroupLayout = QVBoxLayout()
+ buttonsLayout = QHBoxLayout()
+ welcomerGroup.setLayout(welcomerGroupLayout)
+ self.welcomePage.setLayout(welcomerLayout) # Set the welcomepage layout
+ ## Widgets
+ self.welcomerLabel = QLabel()
+ self.welcomerLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ self.installButton = QPushButton('Install')
+ self.installButton.clicked.connect(main.qt_button_click)
+ self.updateButton = QPushButton('Check for Updates')
+ self.updateButton.clicked.connect(main.qt_button_click)
+ self.uninstallButton = QPushButton('Uninstall')
+ self.uninstallButton.clicked.connect(main.qt_button_click)
+ self.backButton = QPushButton("Back")
+ self.backButton.setFixedSize(80, 30)
+ self.backButton.clicked.connect(main.qt_button_click)
+ ## Add widgets / layouts
+ welcomerLayout.addLayout(self.header)
+ welcomerLayout.addWidget(welcomerGroup)
+ welcomerGroupLayout.addWidget(self.welcomerLabel)
+ buttonsLayout.addWidget(self.installButton)
+ buttonsLayout.addWidget(self.updateButton)
+ buttonsLayout.addWidget(self.uninstallButton)
+ welcomerLayout.addLayout(buttonsLayout)
+ welcomerLayout.insertWidget(0, self.backButton)
diff --git a/ui_init.py b/ui_init.py
new file mode 100644
index 0000000..250a422
--- /dev/null
+++ b/ui_init.py
@@ -0,0 +1,41 @@
+# Page imports
+from imports import *
+sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "ui"))
+from selection_page import SelectionPage
+from welcome_page import WelcomePage
+from install_page import InstallPage
+from progress_bar import ProgressBarPage
+from finish_page import FinishPage
+from stylesheet import Style
+
+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.selection_page = SelectionPage()
+ self.welcome_page = WelcomePage()
+ self.install_page = InstallPage()
+ self.progress_bar_page = ProgressBarPage()
+ self.finish_page = FinishPage()
+ self.widget_2_layout()
+ self.load_stylesheet()
+
+ def widget_2_layout(self):
+ self.layout.addWidget(self.selection_page.emulatorSelectPage)
+ self.layout.addWidget(self.welcome_page.welcomePage)
+ self.layout.addWidget(self.install_page.installPage)
+ self.layout.addWidget(self.progress_bar_page.progressBarPage)
+ self.layout.addWidget(self.finish_page.finishPage)
+
+ def load_stylesheet(app):
+ app.setStyleSheet(Style.dark_stylesheet)
+
+
+