def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) font = QFont('monospace', 8) font.setStyleHint(QFont.Monospace) self.setFont(font) self.setShowGrid(False) self.horizontalHeader().hide() self.verticalHeader().setSectionResizeMode( QtWidgets.QHeaderView.Fixed) self.verticalHeader().setHighlightSections(False) self.horizontalHeader().setHighlightSections(False) self.verticalHeader().setSectionsClickable(False) # Don't let the user edit the table cells. self.setEditTriggers(self.NoEditTriggers) self.setSelectionBehavior(QAbstractItemView.SelectItems) self.setSelectionMode(QAbstractItemView.ContiguousSelection) self.setModel(QStandardItemModel(1, 33)) # This will store the raw data that is displayed in the hex view. self.hex_data = None # Determine how wide ASCII columns should be. self.ascii_width = self.fontMetrics().horizontalAdvance('m') # HACK: Get how much space a hex item needs by asking temporarily creating one, and then asking Qt, # because self.fontMetrics().width('mm') isn't enough, apparently, unlike above. self.model().setItem(0, 0, QStandardItem('mm')) self.resizeColumnToContents(0) self.hex_width = self.visualRect(self.model().createIndex( 0, 0)).width() # Default to 16 hex columns, with 16 ASCII columns, and one separator column, for a total of 33. self._set_bytes_per_row(16) # HACK: Get how much space is needed for 16 bytes per row by # getting the left and right bound of the left-most and right-most items, respectively. start = self.visualRect(self.model().createIndex(0, 0)).left() end = self.visualRect(self.model().createIndex(0, 32)).right() self.full_width = end - start # Record the default background color for items, since apparently that's platform dependent. # Note: Normally we can only get the default background color if there's actually an item there, # but we made one earlier to determine the value for self.hex_width, so we don't need to do it again. self.default_background_color = self.model().item(0, 0).background() self.model().setRowCount(0) self.selectionModel().selectionChanged.connect( self._selection_changed)
def __init__(self, parent, history): super().__init__(parent=parent) self.setWindowTitle("History") layout = QVBoxLayout() text = QPlainTextEdit() font = QFont() font.setFamily("monospace") font.setStyleHint(QFont.Monospace) text.setFont(font) highlighter = PythonHighlighter(text.document()) # noqa: F841 text.setReadOnly(True) text.setPlainText(history) layout.addWidget(text) buttonbox = QDialogButtonBox(QDialogButtonBox.Ok) clipboardbutton = QPushButton("Copy to clipboard") buttonbox.addButton(clipboardbutton, QDialogButtonBox.ActionRole) clipboard = QGuiApplication.clipboard() clipboardbutton.clicked.connect( lambda: clipboard.setText(history + "\n")) layout.addWidget(buttonbox) self.setLayout(layout) buttonbox.accepted.connect(self.accept) self.resize(700, 500)
def __init__(self, parent, chunks, fname): super().__init__(parent) self.setWindowTitle(f"XDF Chunks ({fname})") self.chunks = chunks TAGS = { 1: "FileHeader", 2: "StreamHeader", 3: "Samples", 4: "ClockOffset", 5: "Boundary", 6: "StreamFooter" } self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels( ["#", "Bytes", "Tag", "Stream ID"]) for i, chunk in enumerate(chunks, start=1): row = [] row.append(_add_item(i)) row.append(_add_item(chunk["nbytes"])) row.append(_add_item(f"{chunk['tag']} ({TAGS[chunk['tag']]})")) row.append(_add_item(chunk.get("stream_id", ""))) self.model.appendRow(row) self.view = QTableView() self.view.setModel(self.model) self.view.verticalHeader().setVisible(False) self.view.horizontalHeader().setStretchLastSection(True) self.view.setShowGrid(False) self.view.setSelectionMode(QAbstractItemView.SingleSelection) self.view.setSelectionBehavior(QAbstractItemView.SelectRows) self.view.setSortingEnabled(True) self.view.sortByColumn(0, Qt.AscendingOrder) self.view.setEditTriggers(QTableView.NoEditTriggers) self.view.setFixedWidth(450) self.details = QPlainTextEdit("") self.details.setFixedWidth(450) self.details.setReadOnly(True) self.details.setTabStopDistance(30) font = QFont() font.setFamily("monospace") font.setStyleHint(QFont.Monospace) self.details.setFont(font) self.details.setLineWrapMode(QPlainTextEdit.NoWrap) hbox = QHBoxLayout() hbox.addWidget(self.view) hbox.addWidget(self.details) vbox = QVBoxLayout(self) vbox.addLayout(hbox) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok) vbox.addWidget(self.buttonbox) self.buttonbox.accepted.connect(self.accept) self.view.clicked.connect(self._update_details) self.setFixedSize(980, 650) self.view.setColumnWidth(0, 70) self.view.setColumnWidth(1, 80) self.view.setColumnWidth(2, 150) self.view.setColumnWidth(3, 70)
def main(gamePath: Optional[str] = None, configPath: Optional[str] = None, startupMode: StartupMode = StartupMode.Main) -> NoReturn: from w3modmanager.util.util import getRuntimePath from w3modmanager.core.model import Model from w3modmanager.core.errors import OtherInstanceError, InvalidGamePath, InvalidConfigPath from w3modmanager.ui.graphical.mainwindow import MainWindow from w3modmanager.domain.web.nexus import closeSession from w3modmanager.domain.system.permissions import \ getWritePermissions, setWritePermissions from PySide6.QtCore import Qt, QSettings from PySide6.QtWidgets import QApplication, QMessageBox from PySide6.QtGui import QIcon, QPalette, QFont from qasync import QEventLoop QApplication.setOrganizationName(w3modmanager.ORG_NAME) QApplication.setOrganizationDomain(w3modmanager.ORG_URL) QApplication.setApplicationName(w3modmanager.TITLE) QApplication.setApplicationVersion(w3modmanager.VERSION) QApplication.setApplicationDisplayName('') QApplication.setAttribute(Qt.AA_NativeWindows) app = QApplication(sys.argv) app.setStyleSheet(''' Link { text-decoration: none; } ''') eventloop = QEventLoop(app) asyncio.set_event_loop(eventloop) palette = QPalette(QApplication.palette()) palette.setColor(QPalette.Link, Qt.red) palette.setColor(QPalette.LinkVisited, Qt.red) palette.setColor(QPalette.PlaceholderText, Qt.gray) app.setPalette(palette) font = QFont('Segoe UI') font.setStyleHint(QFont.System) font.setWeight(QFont.Normal) font.setStyleStrategy(QFont.PreferDevice) font.setPointSize(9) app.setFont(font) icon = QIcon() icon.addFile(str(getRuntimePath('resources/icons/w3b.ico'))) app.setWindowIcon(icon) pool = ThreadPoolExecutor() asyncio.get_running_loop().set_default_executor(pool) # configure startup overrides settings = QSettings() if gamePath: settings.setValue('gamePath', gamePath) if configPath: settings.setValue('configPath', configPath) if startupMode == StartupMode.About: MainWindow.showAboutDialog(None).exec_() sys.exit() if startupMode == StartupMode.Settings: MainWindow.showSettingsDialog(None).exec_() sys.exit() exception_hook_set = False def createModel(ignorelock: bool = False) -> Model: nonlocal settings return Model( Path(str(settings.value('gamePath'))), Path(str(settings.value('configPath'))), Path( appdirs.user_data_dir(w3modmanager.NAME, w3modmanager.ORG_NAME)), ignorelock) try: # try to initialize the mod management model try: model = createModel() # if another instance is already open, inform and ask to open anyway except OtherInstanceError as e: if MainWindow.showOtherInstanceDialog( None).exec_() == QMessageBox.Yes: model = createModel(True) else: raise e # if game path or config path is invalid or not set, # show a special settings dialog and retry except (InvalidGamePath, InvalidConfigPath): MainWindow.showSettingsDialog(None, True).exec_() model = createModel() # check for write access to the game and config directories for path in ( model.gamepath, model.configpath, model.cachepath, ): if not getWritePermissions(path): if MainWindow.showInvalidPermissionsDialog(None, path).exec_() != QMessageBox.Yes \ or not setWritePermissions(path): raise PermissionError(f'Not enough permissions for {path}') window = MainWindow(model) app.setActiveWindow(window) def show_exception_hook(exctype, value, tb) -> None: # noqa nonlocal window MainWindow.showCritcalErrorDialog( window, value, ''.join(traceback.format_exception(exctype, value, tb))).exec_() exception_hook(exctype, value, tb) sys.excepthook = show_exception_hook exception_hook_set = True with eventloop: status = eventloop.run_forever() eventloop.run_until_complete(closeSession()) sys.exit(status) except OtherInstanceError as e: sys.exit(f'error: {str(e)}') except (InvalidGamePath, InvalidConfigPath) as e: MainWindow.showInvalidConfigErrorDialog(None).exec_() sys.exit(f'error: {str(e)}') except PermissionError as e: MainWindow.showInvalidPermissionsErrorDialog(None).exec_() sys.exit(f'error: {str(e)}') except Exception as e: if not exception_hook_set: MainWindow.showCritcalErrorDialog(None, str(e)).exec_() raise e sys.exit()