├── .gitignore ├── src ├── 86BoxManager.py ├── addvm.py ├── addvm_ui.py ├── edit.py ├── edit_ui.py ├── main_ui.py ├── mainw.py ├── settings.py ├── settings_ui.py └── util.py └── ui ├── Edit.ui ├── Settings.ui ├── addvm.ui └── main.ui /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 105 | __pypackages__/ 106 | 107 | # Celery stuff 108 | celerybeat-schedule 109 | celerybeat.pid 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | .env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | 141 | # pytype static type analyzer 142 | .pytype/ 143 | 144 | # Cython debug symbols 145 | cython_debug/ 146 | 147 | # PyCharm 148 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 149 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 150 | # and can be added to the global gitignore or merged into this file. For a more nuclear 151 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 152 | #.idea/ 153 | 154 | -------------------------------------------------------------------------------- /src/86BoxManager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from mainw import MainWin 4 | from PyQt5 import QtCore, QtGui, QtWidgets 5 | from util import loadOrNew 6 | import sys 7 | # Windows home + "AppData\Local\86BoxManPy\" 8 | # Linux home + ".config/86BoxManPy/" 9 | # Mac home + "Library/Application Support/86BoxManPy/86BoxManPy/" 10 | 11 | if __name__ == "__main__": 12 | datadict = loadOrNew() 13 | app = QtWidgets.QApplication(sys.argv) 14 | MainWindow = QtWidgets.QMainWindow() 15 | MainWindow.ui = MainWin() 16 | MainWindow.ui.setupWin(MainWindow,datadict) 17 | MainWindow.show() 18 | sys.exit(app.exec_()) 19 | -------------------------------------------------------------------------------- /src/addvm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from PyQt5 import QtCore, QtGui, QtWidgets 3 | import os 4 | from addvm_ui import Ui_addVM 5 | 6 | class addVMC(Ui_addVM): 7 | def setupWin(self, addVM, datadict): 8 | self.setupUi(addVM) 9 | self.datadict = datadict 10 | self.lineEdit_2.textChanged.connect(self.vmNameChange) 11 | self.importCheckbox.clicked.connect(self.importCheckboxClick) 12 | self.browseImport.clicked.connect(self.browseImportClick) 13 | self.cancelButton.clicked.connect(lambda: addVM.close()) 14 | self.addButton.clicked.connect(lambda: self.addButtonClick(addVM)) 15 | 16 | def importCheckboxClick(self): 17 | self.lineEdit.setEnabled(self.importCheckbox.isChecked()) 18 | self.browseImport.setEnabled(self.importCheckbox.isChecked()) 19 | 20 | def browseImportClick(self): 21 | import platform 22 | browse = QtWidgets.QFileDialog() 23 | browse.setFileMode(QtWidgets.QFileDialog.Directory) 24 | browse.setOption(QtWidgets.QFileDialog.ShowDirsOnly) 25 | if browse.exec_() == QtWidgets.QDialog.Accepted: 26 | filename = browse.selectedFiles() 27 | if filename: 28 | if os.path.exists(filename[0]): 29 | config_file = os.path.join(filename[0], '86box.cfg') 30 | if os.path.exists(config_file): 31 | self.lineEdit.setText(filename[0]) 32 | self.vmPathLabel.setText(filename[0]) 33 | else: 34 | dlg = QtWidgets.QMessageBox(window) 35 | dlg.setWindowTitle("86box.cfg not found") 36 | dlg.setText("No VM config detected please confirm path and try again") 37 | button = dlg.exec() 38 | 39 | def vmNameChange(self): 40 | if not(self.importCheckbox.checkState()): 41 | if 'VMPath' in self.datadict.keys(): 42 | vmpath = self.datadict['VMPath'] 43 | self.vmPathLabel.setText(os.path.join(vmpath,self.lineEdit_2.text())) 44 | 45 | 46 | def addButtonClick(self,window): 47 | name=self.lineEdit_2.text().strip() 48 | if len(name) > 0: 49 | name=self.lineEdit_2.text() 50 | description = self.lineEdit_3.text() 51 | path = self.vmPathLabel.text() 52 | if not(self.importCheckbox.isChecked()): 53 | if not(os.path.exists(path)): 54 | os.mkdir(path) 55 | if not('VMList' in self.datadict.keys()): 56 | self.datadict['VMList'] = {} 57 | self.datadict['VMList'][name] = (description,path) 58 | from util import saveConfig 59 | saveConfig(self.datadict) 60 | if self.configureCheck.isChecked(): 61 | if '86BoxPath' in self.datadict.keys(): 62 | import subprocess 63 | ops = [] 64 | ops.append(self.datadict['86BoxPath']) 65 | if 'RomOverride' in self.datadict.keys(): 66 | if self.datadict['RomOverride']: 67 | ops.append('-R') 68 | ops.append(self.datadict['RomPath']) 69 | ops.append('-P') 70 | ops.append(path) 71 | ops.append('-V') 72 | ops.append(name) 73 | ops.append('-S') 74 | p = subprocess.Popen(ops) 75 | p.wait() 76 | if self.startVMcheck.checkState(): 77 | self.datadict['RunVM'] = name 78 | window.close() 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/addvm_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'ui/addvm.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.6 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_addVM(object): 15 | def setupUi(self, addVM): 16 | addVM.setObjectName("addVM") 17 | addVM.setWindowModality(QtCore.Qt.ApplicationModal) 18 | addVM.resize(580, 250) 19 | self.verticalLayout = QtWidgets.QVBoxLayout(addVM) 20 | self.verticalLayout.setObjectName("verticalLayout") 21 | self.importCheckbox = QtWidgets.QCheckBox(addVM) 22 | self.importCheckbox.setObjectName("importCheckbox") 23 | self.verticalLayout.addWidget(self.importCheckbox) 24 | self.horizontalLayout = QtWidgets.QHBoxLayout() 25 | self.horizontalLayout.setObjectName("horizontalLayout") 26 | self.lineEdit = QtWidgets.QLineEdit(addVM) 27 | self.lineEdit.setEnabled(False) 28 | self.lineEdit.setObjectName("lineEdit") 29 | self.horizontalLayout.addWidget(self.lineEdit) 30 | self.browseImport = QtWidgets.QPushButton(addVM) 31 | self.browseImport.setEnabled(False) 32 | self.browseImport.setObjectName("browseImport") 33 | self.horizontalLayout.addWidget(self.browseImport) 34 | self.verticalLayout.addLayout(self.horizontalLayout) 35 | self.formLayout = QtWidgets.QFormLayout() 36 | self.formLayout.setObjectName("formLayout") 37 | self.nameLabel = QtWidgets.QLabel(addVM) 38 | self.nameLabel.setObjectName("nameLabel") 39 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.nameLabel) 40 | self.lineEdit_2 = QtWidgets.QLineEdit(addVM) 41 | self.lineEdit_2.setObjectName("lineEdit_2") 42 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.lineEdit_2) 43 | self.descriptionLabel = QtWidgets.QLabel(addVM) 44 | self.descriptionLabel.setObjectName("descriptionLabel") 45 | self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.descriptionLabel) 46 | self.lineEdit_3 = QtWidgets.QLineEdit(addVM) 47 | self.lineEdit_3.setObjectName("lineEdit_3") 48 | self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.lineEdit_3) 49 | self.label_3 = QtWidgets.QLabel(addVM) 50 | self.label_3.setObjectName("label_3") 51 | self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_3) 52 | self.vmPathLabel = QtWidgets.QLabel(addVM) 53 | self.vmPathLabel.setObjectName("vmPathLabel") 54 | self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.vmPathLabel) 55 | self.verticalLayout.addLayout(self.formLayout) 56 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 57 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 58 | self.startVMcheck = QtWidgets.QCheckBox(addVM) 59 | self.startVMcheck.setObjectName("startVMcheck") 60 | self.horizontalLayout_2.addWidget(self.startVMcheck) 61 | self.configureCheck = QtWidgets.QCheckBox(addVM) 62 | self.configureCheck.setObjectName("configureCheck") 63 | self.horizontalLayout_2.addWidget(self.configureCheck) 64 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 65 | self.horizontalLayout_2.addItem(spacerItem) 66 | self.verticalLayout.addLayout(self.horizontalLayout_2) 67 | spacerItem1 = QtWidgets.QSpacerItem(20, 4, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 68 | self.verticalLayout.addItem(spacerItem1) 69 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout() 70 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 71 | spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 72 | self.horizontalLayout_3.addItem(spacerItem2) 73 | self.addButton = QtWidgets.QPushButton(addVM) 74 | self.addButton.setDefault(True) 75 | self.addButton.setObjectName("addButton") 76 | self.horizontalLayout_3.addWidget(self.addButton) 77 | self.cancelButton = QtWidgets.QPushButton(addVM) 78 | self.cancelButton.setObjectName("cancelButton") 79 | self.horizontalLayout_3.addWidget(self.cancelButton) 80 | self.verticalLayout.addLayout(self.horizontalLayout_3) 81 | 82 | self.retranslateUi(addVM) 83 | QtCore.QMetaObject.connectSlotsByName(addVM) 84 | 85 | def retranslateUi(self, addVM): 86 | _translate = QtCore.QCoreApplication.translate 87 | addVM.setWindowTitle(_translate("addVM", "Add VM")) 88 | self.importCheckbox.setText(_translate("addVM", "Import VM")) 89 | self.browseImport.setText(_translate("addVM", "Browse")) 90 | self.nameLabel.setText(_translate("addVM", "Name:")) 91 | self.descriptionLabel.setText(_translate("addVM", "Description:")) 92 | self.label_3.setText(_translate("addVM", "Path:")) 93 | self.vmPathLabel.setText(_translate("addVM", "C:\\I like VMs\\And Potatoes\\")) 94 | self.startVMcheck.setText(_translate("addVM", "Start this VM now")) 95 | self.configureCheck.setText(_translate("addVM", "Configure this VM now")) 96 | self.addButton.setText(_translate("addVM", "Add")) 97 | self.cancelButton.setText(_translate("addVM", "Cancel")) 98 | -------------------------------------------------------------------------------- /src/edit.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from PyQt5 import QtCore, QtGui, QtWidgets 4 | from edit_ui import Ui_editVM 5 | import sys 6 | from util import errorBox, saveConfig 7 | 8 | 9 | class editVMiW(Ui_editVM): 10 | def setupWin(self, editVM, datadict, name, runningVM): 11 | self.setupUi(editVM) 12 | self.runningVM = runningVM 13 | self.datadict = datadict 14 | self.changed = False 15 | self.window = editVM 16 | self.vmName.setText(name) 17 | (description, path) = self.datadict['VMList'][name] 18 | self.vmname = name 19 | self.vmDescription.setText(description) 20 | self.vmPath.setText(path) 21 | self.vmName.textChanged.connect(self.setChanged) 22 | self.vmPath.textChanged.connect(self.setChanged) 23 | self.vmDescription.textChanged.connect(self.setChanged) 24 | self.editButton.clicked.connect(self.editButtonClicked) 25 | self.cancelButton.clicked.connect(self.cancelButtonClicked) 26 | 27 | def setChanged(self): 28 | self.changed = True 29 | 30 | def editButtonClicked(self): 31 | import os 32 | if self.changed: 33 | newName = self.vmName.text() 34 | newDesc = self.vmDescription.text() 35 | newPath = self.vmPath.text() 36 | if not(self.vmname in self.runningVM.keys()): 37 | if self.vmname in self.datadict['VMList'].keys(): 38 | nope,nope1 = self.datadict['VMList'].pop(self.vmname) 39 | if os.path.exists(newPath): 40 | self.datadict['VMList'][newName] = (newDesc, newPath) 41 | saveConfig(self.datadict) 42 | if self.configureCheck.isChecked(): 43 | if '86BoxPath' in self.datadict.keys(): 44 | import subprocess 45 | ops = [] 46 | ops.append(self.datadict['86BoxPath']) 47 | if 'RomOverride' in self.datadict.keys(): 48 | if self.datadict['RomOverride']: 49 | ops.append('-R') 50 | ops.append(self.datadict['RomPath']) 51 | ops.append('-P') 52 | ops.append(path) 53 | ops.append('-V') 54 | ops.append(newName) 55 | ops.append('-S') 56 | p = subprocess.Popen(ops) 57 | p.wait() 58 | if self.startVMcheck.checkState(): 59 | self.datadict['RunVM'] = newName 60 | else: 61 | errorBox(self, self.window, "Error", "Path doesn't exist.") 62 | else: 63 | errorBox(self, self.window, "Error", "VM Still running. Cannot Edit") 64 | 65 | self.window.close() 66 | 67 | def cancelButtonClicked(self): 68 | self.window.close() 69 | 70 | def pathBrowseClicked(self): 71 | browse = QtWidgets.QFileDialog() 72 | browse.setFileMode(QtWidgets.QFileDialog.Directory) 73 | browse.setOption(QtWidgets.QFileDialog.ShowDirsOnly) 74 | if browse.exec_() == QtWidgets.QDialog.Accepted: 75 | filename = browse.selectedFiles() 76 | if filename: 77 | if os.path.exists(filename[0]): 78 | self.vmPath.setText(filename[0]) 79 | -------------------------------------------------------------------------------- /src/edit_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'ui/Edit.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.6 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_editVM(object): 15 | def setupUi(self, editVM): 16 | editVM.setObjectName("editVM") 17 | editVM.setWindowModality(QtCore.Qt.ApplicationModal) 18 | editVM.resize(580, 180) 19 | self.verticalLayout = QtWidgets.QVBoxLayout(editVM) 20 | self.verticalLayout.setObjectName("verticalLayout") 21 | self.formLayout = QtWidgets.QFormLayout() 22 | self.formLayout.setObjectName("formLayout") 23 | self.nameLabel = QtWidgets.QLabel(editVM) 24 | self.nameLabel.setObjectName("nameLabel") 25 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.nameLabel) 26 | self.vmName = QtWidgets.QLineEdit(editVM) 27 | self.vmName.setObjectName("vmName") 28 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.vmName) 29 | self.descriptionLabel = QtWidgets.QLabel(editVM) 30 | self.descriptionLabel.setObjectName("descriptionLabel") 31 | self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.descriptionLabel) 32 | self.vmDescription = QtWidgets.QLineEdit(editVM) 33 | self.vmDescription.setObjectName("vmDescription") 34 | self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.vmDescription) 35 | self.label_3 = QtWidgets.QLabel(editVM) 36 | self.label_3.setObjectName("label_3") 37 | self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_3) 38 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout() 39 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 40 | self.vmPath = QtWidgets.QLineEdit(editVM) 41 | self.vmPath.setObjectName("vmPath") 42 | self.horizontalLayout_3.addWidget(self.vmPath) 43 | self.pathBrowse = QtWidgets.QPushButton(editVM) 44 | self.pathBrowse.setObjectName("pathBrowse") 45 | self.horizontalLayout_3.addWidget(self.pathBrowse) 46 | self.formLayout.setLayout(2, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_3) 47 | self.verticalLayout.addLayout(self.formLayout) 48 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 49 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 50 | self.startVMcheck = QtWidgets.QCheckBox(editVM) 51 | self.startVMcheck.setObjectName("startVMcheck") 52 | self.horizontalLayout_2.addWidget(self.startVMcheck) 53 | self.configureCheck = QtWidgets.QCheckBox(editVM) 54 | self.configureCheck.setObjectName("configureCheck") 55 | self.horizontalLayout_2.addWidget(self.configureCheck) 56 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 57 | self.horizontalLayout_2.addItem(spacerItem) 58 | self.verticalLayout.addLayout(self.horizontalLayout_2) 59 | spacerItem1 = QtWidgets.QSpacerItem(20, 2, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 60 | self.verticalLayout.addItem(spacerItem1) 61 | self.horizontalLayout = QtWidgets.QHBoxLayout() 62 | self.horizontalLayout.setObjectName("horizontalLayout") 63 | spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 64 | self.horizontalLayout.addItem(spacerItem2) 65 | self.editButton = QtWidgets.QPushButton(editVM) 66 | self.editButton.setDefault(True) 67 | self.editButton.setObjectName("editButton") 68 | self.horizontalLayout.addWidget(self.editButton) 69 | self.cancelButton = QtWidgets.QPushButton(editVM) 70 | self.cancelButton.setObjectName("cancelButton") 71 | self.horizontalLayout.addWidget(self.cancelButton) 72 | self.verticalLayout.addLayout(self.horizontalLayout) 73 | 74 | self.retranslateUi(editVM) 75 | QtCore.QMetaObject.connectSlotsByName(editVM) 76 | 77 | def retranslateUi(self, editVM): 78 | _translate = QtCore.QCoreApplication.translate 79 | editVM.setWindowTitle(_translate("editVM", "Edit VM")) 80 | self.nameLabel.setText(_translate("editVM", "Name:")) 81 | self.descriptionLabel.setText(_translate("editVM", "Description:")) 82 | self.label_3.setText(_translate("editVM", "Path:")) 83 | self.pathBrowse.setText(_translate("editVM", "Browse")) 84 | self.startVMcheck.setText(_translate("editVM", "Start this VM now")) 85 | self.configureCheck.setText(_translate("editVM", "Configure this VM now")) 86 | self.editButton.setText(_translate("editVM", "Edit")) 87 | self.cancelButton.setText(_translate("editVM", "Cancel")) 88 | -------------------------------------------------------------------------------- /src/main_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'ui/main.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.6 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_MainWindow(object): 15 | def setupUi(self, MainWindow): 16 | MainWindow.setObjectName("MainWindow") 17 | MainWindow.resize(680, 510) 18 | self.centralwidget = QtWidgets.QWidget(MainWindow) 19 | self.centralwidget.setObjectName("centralwidget") 20 | self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) 21 | self.verticalLayout.setObjectName("verticalLayout") 22 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout() 23 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 24 | self.horizontalLayout = QtWidgets.QHBoxLayout() 25 | self.horizontalLayout.setContentsMargins(-1, -1, 17, -1) 26 | self.horizontalLayout.setSpacing(0) 27 | self.horizontalLayout.setObjectName("horizontalLayout") 28 | self.addButton = QtWidgets.QPushButton(self.centralwidget) 29 | self.addButton.setObjectName("addButton") 30 | self.horizontalLayout.addWidget(self.addButton) 31 | self.editButton = QtWidgets.QPushButton(self.centralwidget) 32 | self.editButton.setObjectName("editButton") 33 | self.horizontalLayout.addWidget(self.editButton) 34 | self.removeButton = QtWidgets.QPushButton(self.centralwidget) 35 | self.removeButton.setObjectName("removeButton") 36 | self.horizontalLayout.addWidget(self.removeButton) 37 | self.horizontalLayout_3.addLayout(self.horizontalLayout) 38 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 39 | self.horizontalLayout_3.addItem(spacerItem) 40 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 41 | self.horizontalLayout_2.setContentsMargins(-1, -1, 17, -1) 42 | self.horizontalLayout_2.setSpacing(0) 43 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 44 | self.startButton = QtWidgets.QPushButton(self.centralwidget) 45 | self.startButton.setObjectName("startButton") 46 | self.horizontalLayout_2.addWidget(self.startButton) 47 | self.configButton = QtWidgets.QPushButton(self.centralwidget) 48 | self.configButton.setObjectName("configButton") 49 | self.horizontalLayout_2.addWidget(self.configButton) 50 | self.horizontalLayout_3.addLayout(self.horizontalLayout_2) 51 | spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 52 | self.horizontalLayout_3.addItem(spacerItem1) 53 | self.settingsButton = QtWidgets.QPushButton(self.centralwidget) 54 | self.settingsButton.setObjectName("settingsButton") 55 | self.horizontalLayout_3.addWidget(self.settingsButton) 56 | self.verticalLayout.addLayout(self.horizontalLayout_3) 57 | self.vmTable = QtWidgets.QTableWidget(self.centralwidget) 58 | self.vmTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) 59 | self.vmTable.setRowCount(0) 60 | self.vmTable.setColumnCount(4) 61 | self.vmTable.setObjectName("vmTable") 62 | item = QtWidgets.QTableWidgetItem() 63 | self.vmTable.setHorizontalHeaderItem(0, item) 64 | item = QtWidgets.QTableWidgetItem() 65 | self.vmTable.setHorizontalHeaderItem(1, item) 66 | item = QtWidgets.QTableWidgetItem() 67 | self.vmTable.setHorizontalHeaderItem(2, item) 68 | item = QtWidgets.QTableWidgetItem() 69 | self.vmTable.setHorizontalHeaderItem(3, item) 70 | self.vmTable.horizontalHeader().setCascadingSectionResizes(False) 71 | self.vmTable.horizontalHeader().setStretchLastSection(True) 72 | self.vmTable.verticalHeader().setVisible(False) 73 | self.verticalLayout.addWidget(self.vmTable) 74 | MainWindow.setCentralWidget(self.centralwidget) 75 | self.statusbar = QtWidgets.QStatusBar(MainWindow) 76 | self.statusbar.setObjectName("statusbar") 77 | MainWindow.setStatusBar(self.statusbar) 78 | 79 | self.retranslateUi(MainWindow) 80 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 81 | 82 | def retranslateUi(self, MainWindow): 83 | _translate = QtCore.QCoreApplication.translate 84 | MainWindow.setWindowTitle(_translate("MainWindow", "86Box Manager Lite")) 85 | self.addButton.setText(_translate("MainWindow", "Add")) 86 | self.editButton.setText(_translate("MainWindow", "Edit")) 87 | self.removeButton.setText(_translate("MainWindow", "Remove")) 88 | self.startButton.setText(_translate("MainWindow", "Start")) 89 | self.configButton.setText(_translate("MainWindow", "Configure")) 90 | self.settingsButton.setText(_translate("MainWindow", "Settings")) 91 | item = self.vmTable.horizontalHeaderItem(0) 92 | item.setText(_translate("MainWindow", "Name")) 93 | item = self.vmTable.horizontalHeaderItem(1) 94 | item.setText(_translate("MainWindow", "Status")) 95 | item = self.vmTable.horizontalHeaderItem(2) 96 | item.setText(_translate("MainWindow", "Description")) 97 | item = self.vmTable.horizontalHeaderItem(3) 98 | item.setText(_translate("MainWindow", "Path")) 99 | -------------------------------------------------------------------------------- /src/mainw.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork, Qt 4 | from addvm import addVMC 5 | from settings import settingsWindow 6 | import subprocess 7 | from main_ui import Ui_MainWindow 8 | from edit import editVMiW 9 | 10 | def really_delete_vm(): 11 | msg = QtWidgets.QMessageBox() 12 | msg.setIcon(QtWidgets.QMessageBox.Warning) 13 | msg.setText("Warning: You are about to delete a VM. Are you sure you want to continue?") 14 | msg.setWindowTitle("Delete VM?") 15 | msg.setStandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.Cancel) 16 | 17 | # start the app 18 | retval = msg.exec_() 19 | 20 | return QtWidgets.QMessageBox.Yes == retval 21 | 22 | class MainWin(Ui_MainWindow): 23 | def setupWin(self, MainWindow,datadict): 24 | self.setupUi(MainWindow) 25 | self.window = MainWindow 26 | self.datadict = datadict 27 | self.runningVM = {} 28 | self.window = MainWindow 29 | self.timer = QtCore.QTimer() 30 | self.timer.timeout.connect(self.timerFire) 31 | self.timer.setSingleShot(False) 32 | self.timer.setInterval(100) 33 | self.timer.start() 34 | self.addButton.clicked.connect(lambda: self.addButtonfunc(self.datadict)) 35 | self.settingsButton.clicked.connect(lambda: self.settingsButtonClicked(self.datadict)) 36 | self.configButton.clicked.connect(self.configButtonClicked) 37 | self.startButton.clicked.connect(self.startButtonClicked) 38 | self.removeButton.clicked.connect(self.removeButtonClicked) 39 | self.editButton.clicked.connect(self.editButtonClicked) 40 | 41 | def sendMessage(self,name,message): 42 | if name in self.runningVM.keys(): 43 | block = QtCore.QByteArray() 44 | out = QtCore.QTextStream(block, QtCore.QIODevice.WriteOnly) 45 | out << message + "\n" 46 | out.flush() 47 | print(block) 48 | if 'client' in self.runningVM[name].keys(): 49 | self.runningVM[name]['client'].write(block) 50 | self.runningVM[name]['client'].flush() 51 | 52 | def errorBox(self,window,title,message): 53 | dlg = QtWidgets.QMessageBox(window) 54 | dlg.setWindowTitle(title) 55 | dlg.setText(message) 56 | results = dlg.exec() 57 | 58 | def editButtonClicked(self): 59 | items = self.vmTable.selectedItems() 60 | if len(items) >0: 61 | name = items[0].text() 62 | if not(name in self.runningVM.keys()): 63 | editDialog = QtWidgets.QDialog() 64 | edit = editVMiW() 65 | edit.setupWin(editDialog, self.datadict, name, self.runningVM) 66 | editDialog.setAttribute(QtCore.Qt.WA_DeleteOnClose) 67 | editDialog.exec_() 68 | if 'RunVM' in self.datadict.keys(): 69 | name = self.datadict.pop('RunVM') 70 | desc,path = self.datadict['VMList'][name] 71 | if '86BoxPath' in self.datadict.keys(): 72 | ops=[] 73 | ops.append(self.datadict['86BoxPath']) 74 | if 'RomOverride' in self.datadict.keys(): 75 | if self.datadict['RomOverride']: 76 | ops.append('-R') 77 | ops.append(self.datadict['RomPath']) 78 | if 'LogEnable' in self.datadict.keys(): 79 | if self.datadict['LogEnable']: 80 | ops.append('-L') 81 | log_path = os.path.join(self.datadict['LogPath'],name+'.log') 82 | ops.append(log_path) 83 | ops.append('-P') 84 | ops.append(path) 85 | ops.append('-V') 86 | ops.append(name) 87 | server = QtNetwork.QLocalServer() 88 | socketName = name+str(os.getpid()) 89 | server.listen(socketName) 90 | server.newConnection.connect(lambda: self.socketConnect(name)) 91 | new_env = os.environ 92 | new_env["86BOX_MANAGER_SOCKET"] = socketName 93 | self.runningVM[name] = {'process':subprocess.Popen(ops, env=new_env),'server': server} 94 | 95 | 96 | def socketConnect(self,name): 97 | import time 98 | print("Socket connected:"+name) 99 | self.runningVM[name]['client'] = self.runningVM[name]['server'].nextPendingConnection() 100 | print(self.runningVM[name]['client']) 101 | self.runningVM[name]['client'].disconnected.connect(self.runningVM[name]['client'].deleteLater) 102 | 103 | def addButtonfunc(self, datadict): 104 | import os 105 | if not(os.path.exists(datadict['86BoxPath'])): 106 | self.errorBox(self.window,'Invalid 86Box path','Please review the settings and define a 86Box path') 107 | return 108 | elif not(os.path.exists(datadict['VMPath'])): 109 | self.errorBox(self.window,'Invalid VM storage path','Please review the settings and define a path to VM storage') 110 | return 111 | elif datadict['RomOverride']: 112 | if not(os.path.exists(datadict['RomPath'])): 113 | self.errorBox(self.window,'Invalid Rom storage path','Please review the settings and define a path to Rom Storage') 114 | return 115 | AddDialog = QtWidgets.QDialog() 116 | ui = addVMC() 117 | ui.setupWin(AddDialog, datadict) 118 | AddDialog.setAttribute(QtCore.Qt.WA_DeleteOnClose) 119 | AddDialog.exec_() 120 | if 'RunVM' in self.datadict.keys(): 121 | name = self.datadict.pop('RunVM') 122 | desc,path = self.datadict['VMList'][name] 123 | if '86BoxPath' in self.datadict.keys(): 124 | ops=[] 125 | ops.append(self.datadict['86BoxPath']) 126 | if 'RomOverride' in self.datadict.keys(): 127 | if self.datadict['RomOverride']: 128 | ops.append('-R') 129 | ops.append(self.datadict['RomPath']) 130 | if 'LogEnable' in self.datadict.keys(): 131 | if self.datadict['LogEnable']: 132 | ops.append('-L') 133 | log_path = os.path.join(self.datadict['LogPath'],name+'.log') 134 | ops.append(log_path) 135 | ops.append('-P') 136 | ops.append(path) 137 | ops.append('-V') 138 | ops.append(name) 139 | server = QtNetwork.QLocalServer() 140 | socketName = name+str(os.getpid()) 141 | server.listen(socketName) 142 | server.newConnection.connect(lambda: self.socketConnect(name)) 143 | new_env = os.environ 144 | new_env["86BOX_MANAGER_SOCKET"] = socketName 145 | self.runningVM[name] = {'process':subprocess.Popen(ops, env=new_env),'server': server} 146 | 147 | def removeButtonClicked(self): 148 | # show message box asking if VM should be removed 149 | # if yes, remove VM from VMList and delete VM directory 150 | if not really_delete_vm(): 151 | return # abort deletion 152 | 153 | items = self.vmTable.selectedItems() 154 | if len(items) > 0: 155 | name = items[0].text() 156 | if name not in self.runningVM.keys(): 157 | desc,path = self.datadict['VMList'][name] 158 | import shutil 159 | import os 160 | ignore = self.datadict['VMList'].pop(name) 161 | if os.path.exists(path): 162 | shutil.rmtree(path) 163 | from util import saveConfig 164 | saveConfig(self.datadict) 165 | 166 | def settingsButtonClicked(self, datadict): 167 | SettingsDialog = QtWidgets.QDialog() 168 | ui = settingsWindow() 169 | ui.setupWin(SettingsDialog, datadict) 170 | SettingsDialog.setAttribute(QtCore.Qt.WA_DeleteOnClose) 171 | SettingsDialog.exec_() 172 | 173 | def configButtonClicked(self): 174 | items = self.vmTable.selectedItems() 175 | if len(items) > 0: 176 | name = items[0].text() 177 | if name not in self.runningVM.keys(): 178 | desc,path = self.datadict['VMList'][name] 179 | if '86BoxPath' in self.datadict.keys(): 180 | import subprocess 181 | ops = [] 182 | ops.append(self.datadict['86BoxPath']) 183 | if 'RomOverride' in self.datadict.keys(): 184 | if self.datadict['RomOverride']: 185 | ops.append('-R') 186 | ops.append(self.datadict['RomPath']) 187 | ops.append('-P') 188 | ops.append(path) 189 | ops.append('-V') 190 | ops.append(name) 191 | ops.append('-S') 192 | p = subprocess.Popen(ops) 193 | p.wait() 194 | 195 | def startButtonClicked(self): 196 | import os 197 | items = self.vmTable.selectedItems() 198 | if len(items) > 0: 199 | name = items[0].text() 200 | if name not in self.runningVM.keys(): 201 | desc,path = self.datadict['VMList'][name] 202 | if '86BoxPath' in self.datadict.keys(): 203 | import subprocess 204 | ops = [] 205 | ops.append(self.datadict['86BoxPath']) 206 | if 'RomOverride' in self.datadict.keys(): 207 | if self.datadict['RomOverride']: 208 | ops.append('-R') 209 | ops.append(self.datadict['RomPath']) 210 | if 'LogEnable' in self.datadict.keys(): 211 | if self.datadict['LogEnable']: 212 | ops.append('-L') 213 | log_path = os.path.join(self.datadict['LogPath'],name+'.log') 214 | ops.append(log_path) 215 | ops.append('-P') 216 | ops.append(path) 217 | ops.append('-V') 218 | ops.append(name) 219 | server = QtNetwork.QLocalServer() 220 | socketName = name+str(os.getpid()) 221 | if server.listen(socketName): 222 | server.newConnection.connect(lambda: self.socketConnect(name)) 223 | new_env = os.environ 224 | new_env["86BOX_MANAGER_SOCKET"] = socketName 225 | self.runningVM[name] = {'process':subprocess.Popen(ops, env=new_env),'server': server} 226 | 227 | 228 | def timerFire(self): 229 | stopped = [] 230 | if len(self.runningVM) > 0 : 231 | for name in self.runningVM: 232 | process = self.runningVM[name]['process'] 233 | if not(process.poll() == None): 234 | stopped.append(name) 235 | if len(stopped) > 0: 236 | for vm in stopped: 237 | ignore = self.runningVM.pop(vm) 238 | if 'VMList' in self.datadict.keys(): 239 | if len(self.datadict['VMList']) > 0: 240 | self.vmTable.setRowCount(len(self.datadict['VMList'])) 241 | rowc=0 242 | for name in self.datadict['VMList']: 243 | desc,path = self.datadict['VMList'][name] 244 | if name in self.runningVM.keys(): 245 | stat = "Running" 246 | else: 247 | stat = "Stopped" 248 | self.vmTable.setItem(rowc, 0, QtWidgets.QTableWidgetItem(name)) 249 | self.vmTable.setItem(rowc, 2, QtWidgets.QTableWidgetItem(desc)) 250 | self.vmTable.setItem(rowc, 1, QtWidgets.QTableWidgetItem(stat)) 251 | self.vmTable.setItem(rowc, 3, QtWidgets.QTableWidgetItem(path)) 252 | rowc +=1 253 | if self.vmTable.rowCount() > len(self.datadict['VMList']): 254 | self.vmTable.setRowCount(len(self.datadict['VMList'])) 255 | 256 | 257 | 258 | 259 | -------------------------------------------------------------------------------- /src/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from PyQt5 import QtCore, QtGui, QtWidgets 4 | import os 5 | from util import saveConfig 6 | from settings_ui import Ui_settingsWindow 7 | 8 | 9 | class settingsWindow(Ui_settingsWindow): 10 | def setupWin(self, settingsWindow,datadict): 11 | self.setupUi(settingsWindow) 12 | self.datadict = datadict 13 | if '86BoxPath' in datadict.keys(): 14 | self.lineEdit.setText(datadict['86BoxPath']) 15 | if 'VMPath' in datadict.keys(): 16 | self.lineEdit_2.setText(datadict['VMPath']) 17 | if 'RomOverride' in datadict.keys(): 18 | state = datadict['RomOverride'] 19 | self.romCheck.setChecked(state) 20 | self.lineEdit_3.setEnabled(state) 21 | self.browseRoms.setEnabled(state) 22 | if state: 23 | if 'RomPath' in datadict.keys(): 24 | self.lineEdit_3.setText(datadict['RomPath']) 25 | if 'LogEnable' in datadict.keys(): 26 | state = datadict['LogEnable'] 27 | self.checkBox.setChecked(state) 28 | self.lineEdit_4.setEnabled(state) 29 | self.logBrowse.setEnabled(state) 30 | 31 | self.romCheck.clicked.connect(self.romCheckClick) 32 | self.checkBox.clicked.connect(self.logCheckClick) 33 | self.browse86box.clicked.connect(self.browse86boxClick) 34 | self.browseVMstorage.clicked.connect(lambda: self.selectButtonDir(self.lineEdit_2)) 35 | self.browseRoms.clicked.connect(lambda: self.selectButtonDir(self.lineEdit_3)) 36 | self.logBrowse.clicked.connect(lambda: self.selectButtonDir(self.lineEdit_4)) 37 | self.settingsCancel.clicked.connect(lambda: settingsWindow.close()) 38 | self.settingsOK.clicked.connect(lambda: self.validateConfig(settingsWindow)) 39 | 40 | def logCheckClick(self): 41 | self.lineEdit_4.setEnabled(self.checkBox.isChecked()) 42 | self.logBrowse.setEnabled(self.checkBox.isChecked()) 43 | 44 | def romCheckClick(self): 45 | self.lineEdit_3.setEnabled(self.romCheck.isChecked()) 46 | self.browseRoms.setEnabled(self.romCheck.isChecked()) 47 | 48 | def browse86boxClick(self): 49 | import platform 50 | browse = QtWidgets.QFileDialog() 51 | browse.setFileMode(QtWidgets.QFileDialog.ExistingFile) 52 | if platform.system() == "Windows": 53 | browse.setNameFilters(["EXE files (*.exe)"]) 54 | browse.selectNameFilter("EXE files (*.exe)") 55 | if browse.exec_() == QtWidgets.QDialog.Accepted: 56 | filename = browse.selectedFiles() 57 | if filename: 58 | if os.path.exists(filename[0]): 59 | self.lineEdit.setText(filename[0]) 60 | 61 | def selectButtonDir(self,textbox): 62 | import platform 63 | browse = QtWidgets.QFileDialog() 64 | browse.setFileMode(QtWidgets.QFileDialog.Directory) 65 | browse.setOption(QtWidgets.QFileDialog.ShowDirsOnly) 66 | if browse.exec_() == QtWidgets.QDialog.Accepted: 67 | filename = browse.selectedFiles() 68 | if filename: 69 | if os.path.exists(filename[0]): 70 | textbox.setText(filename[0]) 71 | 72 | def validateConfig(self,window): 73 | import pickle 74 | error = False 75 | if not os.path.exists(self.lineEdit.text()): 76 | error = True 77 | self.lineEdit.setStyleSheet("background-color: rgb(255, 0, 0)") 78 | if not os.path.exists(self.lineEdit_2.text()): 79 | error = True 80 | self.lineEdit_2.setStyleSheet("background-color: rgb(255, 0, 0)") 81 | if self.romCheck.isChecked(): 82 | if not(os.path.exists(self.lineEdit_3.text())): 83 | error = True 84 | self.lineEdit_3.setStyleSheet("background-color: rgb(255, 0, 0)") 85 | if self.checkBox.isChecked(): 86 | if not(os.path.exists(self.lineEdit_4.text())): 87 | error = True 88 | self.lineEdit_4.setStyleSheet("background-color: rgb(255, 0, 0)") 89 | if not(error): 90 | self.datadict["86BoxPath"] = self.lineEdit.text() 91 | self.datadict["VMPath"] = self.lineEdit_2.text() 92 | self.datadict['RomOverride'] = self.romCheck.isChecked() 93 | if self.romCheck.isChecked(): 94 | self.datadict["RomPath"] = self.lineEdit_3.text() 95 | self.datadict['LogEnable'] = self.checkBox.isChecked() 96 | if self.checkBox.isChecked(): 97 | self.datadict["LogPath"] = self.lineEdit_4.text() 98 | saveConfig(self.datadict) 99 | window.close() 100 | else: 101 | dlg = QtWidgets.QMessageBox(window) 102 | dlg.setWindowTitle("Invalid path/File") 103 | dlg.setText("Please review the settings") 104 | button = dlg.exec() 105 | 106 | -------------------------------------------------------------------------------- /src/settings_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'ui/Settings.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.15.6 6 | # 7 | # WARNING: Any manual changes made to this file will be lost when pyuic5 is 8 | # run again. Do not edit this file unless you know what you are doing. 9 | 10 | 11 | from PyQt5 import QtCore, QtGui, QtWidgets 12 | 13 | 14 | class Ui_settingsWindow(object): 15 | def setupUi(self, settingsWindow): 16 | settingsWindow.setObjectName("settingsWindow") 17 | settingsWindow.setWindowModality(QtCore.Qt.ApplicationModal) 18 | settingsWindow.resize(680, 300) 19 | self.verticalLayout = QtWidgets.QVBoxLayout(settingsWindow) 20 | self.verticalLayout.setObjectName("verticalLayout") 21 | self.tabWidget = QtWidgets.QTabWidget(settingsWindow) 22 | self.tabWidget.setObjectName("tabWidget") 23 | self.generalTab = QtWidgets.QWidget() 24 | self.generalTab.setObjectName("generalTab") 25 | self.formLayout = QtWidgets.QFormLayout(self.generalTab) 26 | self.formLayout.setObjectName("formLayout") 27 | self.label_3 = QtWidgets.QLabel(self.generalTab) 28 | self.label_3.setObjectName("label_3") 29 | self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_3) 30 | self.horizontalLayout = QtWidgets.QHBoxLayout() 31 | self.horizontalLayout.setObjectName("horizontalLayout") 32 | self.lineEdit = QtWidgets.QLineEdit(self.generalTab) 33 | self.lineEdit.setObjectName("lineEdit") 34 | self.horizontalLayout.addWidget(self.lineEdit) 35 | self.browse86box = QtWidgets.QPushButton(self.generalTab) 36 | self.browse86box.setObjectName("browse86box") 37 | self.horizontalLayout.addWidget(self.browse86box) 38 | self.formLayout.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout) 39 | self.label_4 = QtWidgets.QLabel(self.generalTab) 40 | self.label_4.setObjectName("label_4") 41 | self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_4) 42 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout() 43 | self.horizontalLayout_2.setObjectName("horizontalLayout_2") 44 | self.lineEdit_2 = QtWidgets.QLineEdit(self.generalTab) 45 | self.lineEdit_2.setObjectName("lineEdit_2") 46 | self.horizontalLayout_2.addWidget(self.lineEdit_2) 47 | self.browseVMstorage = QtWidgets.QPushButton(self.generalTab) 48 | self.browseVMstorage.setObjectName("browseVMstorage") 49 | self.horizontalLayout_2.addWidget(self.browseVMstorage) 50 | self.formLayout.setLayout(1, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_2) 51 | self.romCheck = QtWidgets.QCheckBox(self.generalTab) 52 | self.romCheck.setObjectName("romCheck") 53 | self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.romCheck) 54 | self.label_5 = QtWidgets.QLabel(self.generalTab) 55 | self.label_5.setObjectName("label_5") 56 | self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_5) 57 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout() 58 | self.horizontalLayout_3.setObjectName("horizontalLayout_3") 59 | self.lineEdit_3 = QtWidgets.QLineEdit(self.generalTab) 60 | self.lineEdit_3.setEnabled(False) 61 | self.lineEdit_3.setObjectName("lineEdit_3") 62 | self.horizontalLayout_3.addWidget(self.lineEdit_3) 63 | self.browseRoms = QtWidgets.QPushButton(self.generalTab) 64 | self.browseRoms.setEnabled(False) 65 | self.browseRoms.setObjectName("browseRoms") 66 | self.horizontalLayout_3.addWidget(self.browseRoms) 67 | self.formLayout.setLayout(3, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_3) 68 | spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 69 | self.formLayout.setItem(4, QtWidgets.QFormLayout.FieldRole, spacerItem) 70 | self.tabWidget.addTab(self.generalTab, "") 71 | self.advancedTab = QtWidgets.QWidget() 72 | self.advancedTab.setObjectName("advancedTab") 73 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.advancedTab) 74 | self.verticalLayout_2.setObjectName("verticalLayout_2") 75 | self.checkBox = QtWidgets.QCheckBox(self.advancedTab) 76 | self.checkBox.setObjectName("checkBox") 77 | self.verticalLayout_2.addWidget(self.checkBox) 78 | self.horizontalLayout_5 = QtWidgets.QHBoxLayout() 79 | self.horizontalLayout_5.setObjectName("horizontalLayout_5") 80 | self.lineEdit_4 = QtWidgets.QLineEdit(self.advancedTab) 81 | self.lineEdit_4.setEnabled(False) 82 | self.lineEdit_4.setObjectName("lineEdit_4") 83 | self.horizontalLayout_5.addWidget(self.lineEdit_4) 84 | self.logBrowse = QtWidgets.QPushButton(self.advancedTab) 85 | self.logBrowse.setEnabled(False) 86 | self.logBrowse.setObjectName("logBrowse") 87 | self.horizontalLayout_5.addWidget(self.logBrowse) 88 | self.verticalLayout_2.addLayout(self.horizontalLayout_5) 89 | spacerItem1 = QtWidgets.QSpacerItem(20, 86, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 90 | self.verticalLayout_2.addItem(spacerItem1) 91 | self.tabWidget.addTab(self.advancedTab, "") 92 | self.aboutTab = QtWidgets.QWidget() 93 | self.aboutTab.setObjectName("aboutTab") 94 | self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.aboutTab) 95 | self.verticalLayout_3.setObjectName("verticalLayout_3") 96 | self.label = QtWidgets.QLabel(self.aboutTab) 97 | font = QtGui.QFont() 98 | font.setPointSize(19) 99 | self.label.setFont(font) 100 | self.label.setObjectName("label") 101 | self.verticalLayout_3.addWidget(self.label) 102 | self.label_2 = QtWidgets.QLabel(self.aboutTab) 103 | self.label_2.setObjectName("label_2") 104 | self.verticalLayout_3.addWidget(self.label_2) 105 | self.tabWidget.addTab(self.aboutTab, "") 106 | self.verticalLayout.addWidget(self.tabWidget) 107 | spacerItem2 = QtWidgets.QSpacerItem(20, 4, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) 108 | self.verticalLayout.addItem(spacerItem2) 109 | self.horizontalLayout_4 = QtWidgets.QHBoxLayout() 110 | self.horizontalLayout_4.setObjectName("horizontalLayout_4") 111 | spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) 112 | self.horizontalLayout_4.addItem(spacerItem3) 113 | self.settingsOK = QtWidgets.QPushButton(settingsWindow) 114 | self.settingsOK.setDefault(True) 115 | self.settingsOK.setObjectName("settingsOK") 116 | self.horizontalLayout_4.addWidget(self.settingsOK) 117 | self.settingsCancel = QtWidgets.QPushButton(settingsWindow) 118 | self.settingsCancel.setObjectName("settingsCancel") 119 | self.horizontalLayout_4.addWidget(self.settingsCancel) 120 | self.verticalLayout.addLayout(self.horizontalLayout_4) 121 | 122 | self.retranslateUi(settingsWindow) 123 | self.tabWidget.setCurrentIndex(0) 124 | QtCore.QMetaObject.connectSlotsByName(settingsWindow) 125 | 126 | def retranslateUi(self, settingsWindow): 127 | _translate = QtCore.QCoreApplication.translate 128 | settingsWindow.setWindowTitle(_translate("settingsWindow", "Settings")) 129 | self.label_3.setText(_translate("settingsWindow", "86Box Path:")) 130 | self.browse86box.setText(_translate("settingsWindow", "Browse")) 131 | self.label_4.setText(_translate("settingsWindow", "Path to VMs:")) 132 | self.browseVMstorage.setText(_translate("settingsWindow", "Browse")) 133 | self.romCheck.setText(_translate("settingsWindow", "Override default rom path")) 134 | self.label_5.setText(_translate("settingsWindow", "Rom Path:")) 135 | self.browseRoms.setText(_translate("settingsWindow", "Browse")) 136 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.generalTab), _translate("settingsWindow", "General")) 137 | self.checkBox.setText(_translate("settingsWindow", "Enable Logging to file")) 138 | self.logBrowse.setText(_translate("settingsWindow", "Browse")) 139 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.advancedTab), _translate("settingsWindow", "Advanced")) 140 | self.label.setText(_translate("settingsWindow", "86Box Manager Lite")) 141 | self.label_2.setText(_translate("settingsWindow", "

A config manager for 86Box

Version 0.0.1

Copyright 2022 Malcolm Haak

Licenced under the <insert licence here>

")) 142 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.aboutTab), _translate("settingsWindow", "About")) 143 | self.settingsOK.setText(_translate("settingsWindow", "Ok")) 144 | self.settingsCancel.setText(_translate("settingsWindow", "Cancel")) 145 | -------------------------------------------------------------------------------- /src/util.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | def createV1Config(): 5 | v1 = {} 6 | v1["ConfigVersion"] = 1 7 | v1["ConfigPath"] = "" 8 | v1["VMList"] = {} 9 | v1["86BoxPath"] = "" 10 | v1["VMPath"] = "" 11 | v1["RomOverride"] = False 12 | v1["RomPath"] = "" 13 | v1["LogEnable"] = False 14 | v1["LogPath"] = "" 15 | return v1 16 | 17 | def createConfig(): 18 | return createV1Config() 19 | 20 | 21 | def genConfPath(): 22 | from pathlib import Path 23 | from os import path 24 | import platform 25 | home = str(Path.home()) 26 | if platform.system() == "Linux": 27 | return path.join(home,'.config/86BoxManPy/') 28 | elif platform.system() == "Windows": 29 | return path.join(home, 'AppData\\Local\\86BoxManPy\\') 30 | elif platform.system() == "Darwin": 31 | return path.join(home, 'Library/Application Support/86BoxManPy/') 32 | 33 | def saveConfig(config): 34 | with open(config['ConfigPath'], 'w') as handle: 35 | json.dump(config, handle) 36 | 37 | def loadOrNew(): 38 | from os import path 39 | import sys 40 | from os import mkdir 41 | config_path = genConfPath() 42 | config_file = path.join(config_path, 'config.json') 43 | if not path.exists(config_path): 44 | mkdir(config_path) 45 | if path.exists(config_file): 46 | try: 47 | with open(config_file) as handle: 48 | datadict = json.load(handle) 49 | datadict["ConfigPath"] = config_file 50 | # If later versions exist put convertion code here 51 | 52 | except EOFError as e: 53 | datadict = createConfig() 54 | datadict["ConfigPath"] = config_file 55 | saveConfig(datadict) 56 | else: 57 | datadict = createConfig() 58 | datadict["ConfigPath"] = config_file 59 | saveConfig(datadict) 60 | return datadict 61 | 62 | 63 | def errorBox(self,window,title,message): 64 | dlg = QtWidgets.QMessageBox(window) 65 | dlg.setWindowTitle(title) 66 | dlg.setText(message) 67 | results = dlg.exec() 68 | -------------------------------------------------------------------------------- /ui/Edit.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | editVM 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 580 13 | 180 14 | 15 | 16 | 17 | Edit VM 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Name: 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | Description: 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Path: 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | Browse 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | Start this VM now 71 | 72 | 73 | 74 | 75 | 76 | 77 | Configure this VM now 78 | 79 | 80 | 81 | 82 | 83 | 84 | Qt::Horizontal 85 | 86 | 87 | 88 | 40 89 | 20 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | Qt::Vertical 100 | 101 | 102 | 103 | 20 104 | 2 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | Qt::Horizontal 115 | 116 | 117 | 118 | 40 119 | 20 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | Edit 128 | 129 | 130 | true 131 | 132 | 133 | 134 | 135 | 136 | 137 | Cancel 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /ui/Settings.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | settingsWindow 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 680 13 | 300 14 | 15 | 16 | 17 | Settings 18 | 19 | 20 | 21 | 22 | 23 | 0 24 | 25 | 26 | 27 | General 28 | 29 | 30 | 31 | 32 | 33 | 86Box Path: 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Browse 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Path to VMs: 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | Browse 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Override default rom path 76 | 77 | 78 | 79 | 80 | 81 | 82 | Rom Path: 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | false 92 | 93 | 94 | 95 | 96 | 97 | 98 | false 99 | 100 | 101 | Browse 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | Qt::Vertical 111 | 112 | 113 | 114 | 20 115 | 40 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | Advanced 125 | 126 | 127 | 128 | 129 | 130 | Enable Logging to file 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | false 140 | 141 | 142 | 143 | 144 | 145 | 146 | false 147 | 148 | 149 | Browse 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | Qt::Vertical 159 | 160 | 161 | 162 | 20 163 | 86 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | About 173 | 174 | 175 | 176 | 177 | 178 | 179 | 19 180 | 181 | 182 | 183 | 86Box Manager Lite 184 | 185 | 186 | 187 | 188 | 189 | 190 | <html><head/><body><p>A config manager for 86Box<br/><br/>Version 0.0.1<br/><br/>Copyright 2022 Malcolm Haak</p><p>Licenced under the &lt;insert licence here&gt;</p></body></html> 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | Qt::Vertical 202 | 203 | 204 | 205 | 20 206 | 4 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | Qt::Horizontal 217 | 218 | 219 | 220 | 40 221 | 20 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | Ok 230 | 231 | 232 | true 233 | 234 | 235 | 236 | 237 | 238 | 239 | Cancel 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 10 252 | 253 | 254 | 10 255 | 256 | 257 | true 258 | 259 | 260 | true 261 | 262 | 263 | true 264 | 265 | 266 | 267 | -------------------------------------------------------------------------------- /ui/addvm.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | addVM 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 580 13 | 250 14 | 15 | 16 | 17 | Add VM 18 | 19 | 20 | 21 | 22 | 23 | Import VM 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | false 33 | 34 | 35 | 36 | 37 | 38 | 39 | false 40 | 41 | 42 | Browse 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Name: 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Description: 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Path: 74 | 75 | 76 | 77 | 78 | 79 | 80 | C:\I like VMs\And Potatoes\ 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | Start this VM now 92 | 93 | 94 | 95 | 96 | 97 | 98 | Configure this VM now 99 | 100 | 101 | 102 | 103 | 104 | 105 | Qt::Horizontal 106 | 107 | 108 | 109 | 40 110 | 20 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | Qt::Vertical 121 | 122 | 123 | 124 | 20 125 | 4 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | Qt::Horizontal 136 | 137 | 138 | 139 | 40 140 | 20 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | Add 149 | 150 | 151 | true 152 | 153 | 154 | 155 | 156 | 157 | 158 | Cancel 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /ui/main.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 680 10 | 510 11 | 12 | 13 | 14 | 86Box Manager Lite 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 0 24 | 25 | 26 | 17 27 | 28 | 29 | 30 | 31 | Add 32 | 33 | 34 | 35 | 36 | 37 | 38 | Edit 39 | 40 | 41 | 42 | 43 | 44 | 45 | Remove 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | Qt::Horizontal 55 | 56 | 57 | 58 | 40 59 | 20 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 0 68 | 69 | 70 | 17 71 | 72 | 73 | 74 | 75 | Start 76 | 77 | 78 | 79 | 80 | 81 | 82 | Configure 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | Qt::Horizontal 92 | 93 | 94 | 95 | 40 96 | 20 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | Settings 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | QAbstractItemView::SelectRows 114 | 115 | 116 | 0 117 | 118 | 119 | 4 120 | 121 | 122 | false 123 | 124 | 125 | true 126 | 127 | 128 | false 129 | 130 | 131 | 132 | Name 133 | 134 | 135 | 136 | 137 | Status 138 | 139 | 140 | 141 | 142 | Description 143 | 144 | 145 | 146 | 147 | Path 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 10 161 | 162 | 163 | 10 164 | 165 | 166 | true 167 | 168 | 169 | true 170 | 171 | 172 | true 173 | 174 | 175 | 176 | --------------------------------------------------------------------------------