diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b09a2eb --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Current File", + "type": "python", + "request": "launch", + "program": "./src/main.py", + "console": "integratedTerminal", + "justMyCode": true + } + ] +} \ No newline at end of file diff --git a/src/colorpalette.py b/src/colorpalette.py index 6a4929c..77738d3 100644 --- a/src/colorpalette.py +++ b/src/colorpalette.py @@ -121,7 +121,7 @@ def setSecondaryIndex(self, index): @pyqtSlot(bool) def setColorSwitchEnabled(self, enabled): - """Sets the switch color aciton/button to be enabled/disabled + """Sets the switch color action/button to be enabled/disabled :param enabled: Whether color switch is to be enabled or disabled :type enabled: bool @@ -151,6 +151,7 @@ def __init__(self, index, source, parent=None): self.selected = False self.setPixmap(QPixmap(75, 75)) self.fill(QColor(211, 211, 211)) + self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) def paintEvent(self, event): """Color paint event to draw grid and transaprency indications @@ -164,15 +165,18 @@ def paintEvent(self, event): pen = QPen(Qt.red) pen.setWidth(5) painter.setPen(pen) - painter.drawLine(QLineF(0, 0, 75, 75)) + painter.drawLine(QLineF(0, 0, 72, 72)) if self.selected: pen = QPen(Qt.red) - pen.setWidth(10) + pen.setWidth(5) + pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin) + painter.setPen(pen) + painter.drawRect(2, 2, 70, 70) else: pen = QPen(Qt.black) pen.setWidth(1) - painter.setPen(pen) - painter.drawRect(0, 0, 74, 74) + painter.setPen(pen) + painter.drawRect(0, 0, 74, 74) def fill(self, color): """Fills color @@ -245,11 +249,12 @@ def __init__(self, source, parent=None): swatch.edit.connect(self.openPicker) self.grid.addWidget(swatch, *position) self.enabled = False + self.main_layout = QHBoxLayout() self.main_layout.addLayout(self.grid) self.main_layout.addWidget(self.color_preview) self.main_layout.setContentsMargins(0, 0, 5, 0) - self.main_layout.setSpacing(19) + self.main_layout.setSpacing(20) self.setLayout(self.main_layout) def setup(self, data): @@ -407,17 +412,11 @@ def __init__(self, source, parent=None): self.docked_widget.setSizePolicy( QSizePolicy.Maximum, QSizePolicy.Maximum ) + self.color_palette = ColorPalette(self.source, self) self.color_palette_list = QComboBox() - self.color_palette_list.setSizePolicy( - QSizePolicy.Minimum, QSizePolicy.Maximum - ) + self.palette_picker = QHBoxLayout() - self.palette_label = QLabel("Palette:") - self.palette_label.setSizePolicy( - QSizePolicy.Maximum, QSizePolicy.Maximum - ) - self.palette_picker.addWidget(self.palette_label) self.palette_picker.addWidget(self.color_palette_list) self.add_palette = QToolButton(self) self.add_palette.mousePressEvent = self.addPaletteReq @@ -443,6 +442,7 @@ def __init__(self, source, parent=None): self.palette_picker.addWidget(self.add_palette) self.palette_picker.addWidget(self.remove_palette) self.palette_picker.addWidget(self.rename_palette) + self.docked_widget.layout().addLayout(self.palette_picker) self.docked_widget.layout().addWidget(self.color_palette) self.color_palette_list.setEnabled(False) diff --git a/src/jide.py b/src/jide.py index f09a3e3..35d2443 100644 --- a/src/jide.py +++ b/src/jide.py @@ -1,3 +1,4 @@ +import os from pathlib import Path import shutil import subprocess @@ -44,6 +45,10 @@ def __init__(self): self.setupStatusBar() self.setupPrefs() + QCoreApplication.processEvents() + + self.loadProject("./data/demo.jrf") + def setupWindow(self): """Entry point to set up primary window attributes """ @@ -228,7 +233,10 @@ def selectFile(self): """Open file action to hand file handle to GameData """ file_name, _ = QFileDialog.getOpenFileName( - self, "Open file", "", "JCAP Resource File (*.jrf)" + self, + "Open file", + str(Path(__file__)), + "JCAP Resource File (*.jrf)", ) self.loadProject(file_name) @@ -490,18 +498,51 @@ def loadJCAP(self): """ self.statusBar.showMessage("Loading JCAP...") self.genDATFiles() + + self.prefs.beginGroup("paths") dat_path = Path(__file__).parents[1] / "data" / "DAT Files" - jcap_path = Path(__file__).parents[2] / "jcap" / "dev" / "software" - sysload_path = jcap_path / "sysload.sh" + if not self.prefs.contains("jcap_path"): + QMessageBox( + QMessageBox.Critical, "Error", "Path to JCAP not set." + ).exec() + self.openPrefs() + return + jcap_path = self.prefs.value("jcap_path") + if not os.path.exists(jcap_path): + QMessageBox( + QMessageBox.Critical, "Error", "Invalid JCAP path." + ).exec() + self.openPrefs() + return + # jcap_path = Path(__file__).parents[2] / "jcap" / "dev" / "software" + self.prefs.endGroup() + + sysload_path = Path(jcap_path) / "sysload.sh" + + if not os.path.exists(sysload_path): + QMessageBox( + QMessageBox.Critical, + "Error", + ( + "sysload.sh not found in JCAP path; Verify that the path " + "is correct, or try re-downloading the JCAP files" + ), + ).exec() + self.openPrefs() + return for dat_file in dat_path.glob("**/*"): - shutil.copy(str(dat_file), str(jcap_path)) + shutil.copy(str(dat_file), jcap_path) self.prefs.beginGroup("ports") if not self.prefs.contains("cpu_port") or not self.prefs.contains( "gpu_port" ): - # Popup error + QMessageBox( + QMessageBox.Critical, + "Error", + "CPU and/or GPU COM ports not set.", + ).exec() self.openPrefs() return cpu_port = self.prefs.value("cpu_port") diff --git a/src/preferences.py b/src/preferences.py index 1d237b6..a8b64b6 100644 --- a/src/preferences.py +++ b/src/preferences.py @@ -6,10 +6,14 @@ from PyQt5.QtWidgets import ( QDialog, QFormLayout, + QHBoxLayout, QDialogButtonBox, QLabel, QComboBox, QPushButton, + QLineEdit, + QFileDialog, + QMessageBox, ) from PyQt5.QtCore import QSettings @@ -25,8 +29,6 @@ def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("Preferences") self.prefs = QSettings() - self.cpu_port = None - self.gpu_port = None self.actions = QDialogButtonBox( QDialogButtonBox.Ok @@ -45,14 +47,14 @@ def __init__(self, parent=None): self.gpu_port_combo_box.addItems(com_ports) self.prefs.beginGroup("ports") if self.prefs.contains("cpu_port"): - self.cpu_port = self.prefs.value("cpu_port") - self.cpu_port_combo_box.setCurrentText(self.cpu_port) + self.cpu_port_combo_box.setCurrentText( + self.prefs.value("cpu_port") + ) if self.prefs.contains("gpu_port"): - self.gpu_port = self.prefs.value("gpu_port") - self.gpu_port_combo_box.setCurrentText(self.gpu_port) + self.gpu_port_combo_box.setCurrentText( + self.prefs.value("gpu_port") + ) self.prefs.endGroup() - self.cpu_port_combo_box.currentTextChanged.connect(self.setCpuPort) - self.gpu_port_combo_box.currentTextChanged.connect(self.setGpuPort) auto_ident = QPushButton("Auto-Identify CPU/GPU") auto_ident.clicked.connect(self.idUcs) @@ -60,9 +62,34 @@ def __init__(self, parent=None): settings_form.addRow("CPU Port:", self.cpu_port_combo_box) settings_form.addRow("GPU Port:", self.gpu_port_combo_box) settings_form.addRow(auto_ident) - settings_form.addWidget(self.actions) + + jcap_path_prefs = QHBoxLayout() + self.jcap_path = QLineEdit() + self.prefs.beginGroup("paths") + if self.prefs.contains("jcap_path"): + self.jcap_path.setText(self.prefs.value("jcap_path")) + self.prefs.endGroup() + self.jcap_browse = QPushButton("Browse") + self.jcap_browse.clicked.connect(self.browseToJcap) + jcap_path_prefs.addWidget(QLabel("JCAP Path:")) + jcap_path_prefs.addWidget(self.jcap_path) + jcap_path_prefs.addWidget(self.jcap_browse) + settings_form.addRow(jcap_path_prefs) + + settings_form.addRow(self.actions) self.setLayout(settings_form) + def browseToJcap(self): + dir = QFileDialog.getExistingDirectory( + self, + "Select JCAP Directory", + self.jcap_path.text(), + QFileDialog.ShowDirsOnly, + ) + + if dir: + self.jcap_path.setText(dir) + def idUcs(self): """Executes the serial routines necessary to identify which COM ports the GPU and CPU are on @@ -100,31 +127,30 @@ def idUcs(self): elif response == b"GPU": new_gpu_port = port - if not new_cpu_port or not new_gpu_port: - # Error dialog - return - - self.setCpuPort(new_cpu_port) - self.setGpuPort(new_gpu_port) - - self.cpu_port_combo_box.setCurrentText(self.cpu_port) - self.gpu_port_combo_box.setCurrentText(self.gpu_port) - - def setCpuPort(self, port): - """Sets the GPU COM port in preferences + message = "" + error = False + if not new_cpu_port: + message = ( + "CPU not detected on any COM port.\nPlease ensure that " + "the debug switch is enabled and that the CPU serial " + "plug is properly connected.\n\n" + ) + error = True - :param port: COM port hosting the CPU - :type port: str - """ - self.cpu_port = port + if not new_gpu_port: + message += ( + "GPU not detected on any COM port.\nPlease ensure " + "that the debug switch is enabled and that the GPU " + "serial plug is properly connected.\n\n" + ) + error = True - def setGpuPort(self, port): - """Sets the GPU COM port in preferences + if error: + QMessageBox(QMessageBox.Critical, "Error", message).exec() + return - :param port: COM port hosting the GPU - :type port: str - """ - self.gpu_port = port + self.cpu_port_combo_box.setCurrentText(new_cpu_port) + self.gpu_port_combo_box.setCurrentText(new_gpu_port) def handlePrefsBtns(self, button): """Handles Ok/Apply/Cancel preferences buttons @@ -134,9 +160,26 @@ def handlePrefsBtns(self, button): """ if button is self.actions.button(QDialogButtonBox.Cancel): self.reject() + return + + cpu_port = self.cpu_port_combo_box.currentText() + gpu_port = self.gpu_port_combo_box.currentText() + if cpu_port == gpu_port: + QMessageBox( + QMessageBox.Critical, + "Error", + "CPU and GPU COM ports cannot be the same", + ).exec() + return + self.prefs.beginGroup("ports") - self.prefs.setValue("cpu_port", self.cpu_port) - self.prefs.setValue("gpu_port", self.gpu_port) + self.prefs.setValue("cpu_port", cpu_port) + self.prefs.setValue("gpu_port", gpu_port) self.prefs.endGroup() + + self.prefs.beginGroup("paths") + self.prefs.setValue("jcap_path", self.jcap_path.text()) + self.prefs.endGroup() + if button is self.actions.button(QDialogButtonBox.Ok): self.accept()