def setup_help_menu(self): """Sets up the help menu.""" if should_create_widget(self.help_action): self.help_action = QtWidgets.QAction('Help...') self.help_action.setShortcut('F1') self.help_action.setToolTip('Opens the help manual') self.help_action.setMenuRole( QtWidgets.QAction.ApplicationSpecificRole) if should_create_widget(self.about_action): self.about_action = QtWidgets.QAction('About...') self.about_action.setShortcut('F3') self.about_action.setToolTip('Opens the about dialog.') self.about_action.setMenuRole(QtWidgets.QAction.AboutRole) if should_create_widget(self.update_action): self.update_action = QtWidgets.QAction('Check for Updates...') self.update_action.setShortcut('F2') self.update_action.setToolTip( 'Checks for client and extension updates') self.update_action.setMenuRole( QtWidgets.QAction.ApplicationSpecificRole) if self.help_action not in self.help_menu.actions(): self.help_menu.insertAction(self.about_action, self.help_action) self.help_menu.insertSeparator(self.about_action) if self.about_action not in self.help_menu.actions(): self.help_menu.insertAction(self.update_action, self.about_action) if self.update_action not in self.help_menu.actions(): self.help_menu.addAction(self.update_action)
def setup_ui(self): """Prepares the chatable's UI.""" if should_create_widget(self.central): self.central = QtWidgets.QWidget(flags=QtCore.Qt.Widget) self.layout = QtWidgets.QVBoxLayout(self.central) self.layout.setContentsMargins(0, 0, 0, 0) self.setWidget(self.central) if should_create_widget(self.display): self.display = QtWebEngineWidgets.QWebEngineView() self.display.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) if self.layout.indexOf(self.display) == -1: self.layout.addWidget(self.display) if should_create_widget(self.container): self.container = QtWidgets.QWidget(flags=QtCore.Qt.Widget) self.container.setLayout(QtWidgets.QHBoxLayout()) self.layout.addWidget(self.container) self.container.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) if should_create_widget(self.input): self.input = QtWidgets.QLineEdit() if self.container.layout().indexOf(self.input) == -1: self.container.layout().addWidget(self.input) if should_create_widget(self.send): self.send = QtWidgets.QPushButton("Send") if self.container.layout().indexOf(self.send) == -1: self.container.layout().addWidget(self.send)
def setup_stream_tab(self): """Sets up the stream tab.""" if should_create_widget(self.stream_chat): self.stream_chat = Chat() self.stream_tab.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.stream_chat)
def setup_file_menu(self): """Sets up the file menu.""" if should_create_widget(self.halt_action): self.halt_action = QtWidgets.QAction('Halt ShovelBot') self.halt_action.setToolTip('Halts ShovelBot') self.halt_action.setShortcut('CTRL+H') if should_create_widget(self.stop_action): self.stop_action = QtWidgets.QAction('Stop ShovelBot') self.stop_action.setToolTip('Stops ShovelBot') self.stop_action.setShortcut('ALT+S') if should_create_widget(self.start_action): self.start_action = QtWidgets.QAction('Start ShovelBot') self.start_action.setToolTip('Starts ShovelBot') self.start_action.setShortcut('CTRL+S') if should_create_widget(self.quit_action): self.quit_action = QtWidgets.QAction('Quit') self.quit_action.setShortcut('CTRL+Q') self.quit_action.setToolTip('Quits ShovelBot') self.quit_action.setMenuRole(QtWidgets.QAction.QuitRole) if should_create_widget(self.settings_action): self.settings_action = QtWidgets.QAction('Settings...') self.settings_action.setShortcut('CTRL+SHIFT+S') self.settings_action.setToolTip('Opens the settings dialog') self.settings_action.setMenuRole(QtWidgets.QAction.PreferencesRole) if self.start_action not in self.file_menu.actions(): self.file_menu.insertAction(self.halt_action, self.start_action) if self.halt_action not in self.file_menu.actions(): self.file_menu.insertAction(self.stop_action, self.halt_action) if self.stop_action not in self.file_menu.actions(): self.file_menu.insertAction(self.settings_action, self.stop_action) self.file_menu.insertSeparator(self.settings_action) if self.settings_action not in self.file_menu.actions(): self.file_menu.insertAction(self.quit_action, self.settings_action) self.file_menu.insertSeparator(self.quit_action) if self.quit_action not in self.file_menu.actions(): self.file_menu.addAction(self.quit_action)
def setup_menubar(self, parent: QtWidgets.QMainWindow): """Sets up the client's menubar.""" menubar: QtWidgets.QMenuBar = parent.menuBar() # Generate menubar elements if should_create_widget(self.file_menu): self.file_menu = QtWidgets.QMenu('File') if should_create_widget(self.help_menu): self.help_menu = QtWidgets.QMenu('Help') # Insert them if self.file_menu.menuAction() not in menubar.actions(): menubar.insertMenu(self.help_menu.menuAction(), self.file_menu) if self.help_menu.menuAction() not in menubar.actions(): menubar.addMenu(self.help_menu)
def setup_ui(self): """Sets up the ui for the about dialog.""" if should_create_widget(self.icon): self.icon = QtWidgets.QLabel() self.icon.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) if should_create_widget(self.caption): self.caption = QtWidgets.QLabel() self.caption.setWordWrap(True) self.caption.setOpenExternalLinks(True) self.caption.setAlignment(QtCore.Qt.AlignTop) if should_create_widget(self._scroll_area): self._scroll_area = QtWidgets.QScrollArea() self._scroll_area.setFrameShadow(QtWidgets.QFrame.Plain) self._scroll_area.setFrameShape(QtWidgets.QFrame.NoFrame) self._scroll_area.setContentsMargins(0, 0, 0, 0) self._scroll_area.setWidgetResizable(True) # Scroll area layout validation if self._scroll_area.layout() is not None: s_layout: QtWidgets.QHBoxLayout = self._scroll_area.layout() else: s_layout = QtWidgets.QHBoxLayout(self._scroll_area) s_layout.setContentsMargins(0, 0, 0, 0) # Scroll area layout insertion if self.caption.layout() != s_layout: s_layout.addWidget(self.caption) # Layout validation if self.layout() is not None: layout: QtWidgets.QHBoxLayout = self.layout() else: layout = QtWidgets.QHBoxLayout(self) # Layout insertion if self.icon.layout() != layout: layout.addWidget(self.icon) if self._scroll_area.layout() != layout: layout.addWidget(self._scroll_area)
def extension_context_requested(self, point: QtCore.QPoint): """Invoked when the user right-clicks the extension table.""" if should_create_widget(self.extension_menu): return logging.getLogger('core.ui').warning( "Extension menu doesn't exist; this shouldn't have happened!") # Declarations app: QtWidgets.QApplication = QtWidgets.QApplication.instance() partial: QtWidgets.QTableWidgetItem = self.extensions_table.itemAt( point) # Partial validation if partial is None: self.extension_load.setDisabled(True) self.extension_unload.setDisabled(True) self.extension_details.setDisabled(True) return self.extension_menu.popup(app.client.mapToGlobal(point)) # More declarations internal: QtWidgets.QTableWidgetItem = self.extensions_table.verticalHeaderItem( partial.row()) state = enums.ExtensionStates.UNLOADED if internal.text() not in app.client.extensions: self.LOGGER.warning( f"{internal.text()} doesn't exist in the internal extensions cache!" ) return self.LOGGER.warning( f'This should never happen unless someone modified the internals, or ShovelBot ' f'had a bug.') else: ext = app.client.extensions[internal.text()] # Re-enable the other actions self.extension_details.setEnabled(True) self.extension_load.setEnabled(True) # Instance check if isinstance(ext, dataclasses.Extension): state = ext.STATE # Update the state to the extension's true state # State check if state == enums.ExtensionStates.UNLOADED: self.extension_load.setText( 'Load') # You can only load an unloaded extension self.extension_unload.setDisabled( True) # You can't unload an unloaded extension else: self.extension_load.setText( 'Reload') # You can reload a loaded extension self.extension_unload.setEnabled( True) # You can unload a loaded extension return self.extension_menu.popup(app.client.mapToGlobal(point))
def setup_central_widget(self, parent: QtWidgets.QMainWindow): """Sets up the client's UI elements.""" if should_create_widget(self.central_tabs): self.central_tabs = QtWidgets.QTabWidget() self.central_tabs.setTabPosition(QtWidgets.QTabWidget.West) self.central_tabs.setDocumentMode(True) self.central_tabs.setMovable(True) # Insert them if parent.centralWidget() != self.central_tabs: parent.setCentralWidget(self.central_tabs)
def setup_extensions_tab(self): """Sets up the extensions tab.""" if should_create_widget(self.extensions_table): self.extensions_table = QTable() self.extensions_table.setEditTriggers( QtWidgets.QTableWidget.NoEditTriggers) self.extensions_table.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self.extensions_table.setAlternatingRowColors(True) self.extensions_table.setDropIndicatorShown(False) self.extensions_table.setAcceptDrops(False) self.extensions_table.setWordWrap(True) self.extensions_table.horizontalHeader().setStretchLastSection( True) self.extensions_table.horizontalHeader().setSortIndicatorShown( True) self.extensions_table.verticalHeader().setHidden(True) self.extensions_table.setColumnCount(3) self.extensions_table.set_horizontal_headers( 'Name', 'Version', 'State') if should_create_widget(self.extension_menu): self.extension_menu = QtWidgets.QMenu() if should_create_widget(self.extension_load): self.extension_load = QtWidgets.QAction('Load') if should_create_widget(self.extension_unload): self.extension_unload = QtWidgets.QAction('Unload') if should_create_widget(self.extension_details): self.extension_details = QtWidgets.QAction('Details') if should_create_widget(self.extension_visibility): self.extension_visibility = QtWidgets.QAction('Hide Unloaded') self.extension_visibility.setCheckable(True) if self.extension_details not in self.extension_menu.actions(): self.extension_menu.insertAction(self.extension_load, self.extension_details) self.extension_menu.insertSeparator(self.extension_load) if self.extension_load not in self.extension_menu.actions(): self.extension_menu.insertAction(self.extension_unload, self.extension_load) if self.extension_unload not in self.extension_menu.actions(): self.extension_menu.insertAction(self.extension_visibility, self.extension_unload) self.extension_menu.insertSeparator(self.extension_visibility) if self.extension_visibility not in self.extension_menu.actions(): self.extension_menu.addAction(self.extension_visibility) layout = QtWidgets.QGridLayout(self.extensions_tab) layout.addWidget(self.extensions_table)
def setup_central_tabs(self): """Sets up the central tabs.""" if should_create_widget(self.stream_tab): self.stream_tab = QtWidgets.QMainWindow(flags=QtCore.Qt.Widget) widget = QtWidgets.QWidget(flags=QtCore.Qt.Widget) widget.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored) self.stream_tab.setCentralWidget(widget) self.stream_tab.setDockNestingEnabled(True) if should_create_widget(self.extensions_tab): self.extensions_tab = QtWidgets.QWidget(flags=QtCore.Qt.Widget) widgets = [ self.central_tabs.widget(w) for w in range(self.central_tabs.count()) ] if self.stream_tab not in widgets: self.central_tabs.addTab(self.stream_tab, 'Stream') if self.extensions_tab not in widgets: self.central_tabs.addTab(self.extensions_tab, 'Extensions')
def setup_ui(self): """Sets up the dialog's ui.""" # Top-Level elements if should_create_widget(self.toolbar): self.toolbar = QtWidgets.QToolBar() self.toolbar.setAllowedAreas(QtCore.Qt.AllToolBarAreas) self.toolbar.setFloatable(False) self.toolbar.setOrientation(QtCore.Qt.Vertical) self.toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon) if should_create_widget(self.toolbar_container): self.toolbar_container = QtWidgets.QStackedWidget() self.toolbar_container.setHidden(True) if should_create_widget(self.display): self.display = QtWidgets.QTableWidget() self.display.setWordWrap(True) self.display.setEditTriggers(self.display.NoEditTriggers) self.display.setDropIndicatorShown(False) self.display.setDragDropOverwriteMode(False) self.display.setSortingEnabled(True) self.display.verticalHeader().setHidden(True) set_table_headers(self.display, ['Timestamp', 'Name', 'Level', 'Message']) append_table(self.display, Timestamp=datetime.datetime.now(tz=datetime.timezone.utc).isoformat(), Name='root', Level='INFO', Message='Testing') if should_create_widget(self.details): self.details = QtWidgets.QTableWidget() self.details.setWordWrap(True) self.details.setEditTriggers(self.details.NoEditTriggers) self.details.setDropIndicatorShown(False) self.details.setDragDropOverwriteMode(False) self.details.setSortingEnabled(False) self.details.verticalHeader().setHidden(True) self.details.setHidden(True) set_table_headers(self.details, ['Key', 'Value']) # Toolbar elements if should_create_widget(self.filter_action): self.filter_action = QtWidgets.QAction('⧩') self.filter_action.setToolTip('Opens the filter panel.') if should_create_widget(self.color_action): self.color_action = QtWidgets.QAction('🖌') self.color_action.setToolTip('Opens the color panel.') # Filter elements if should_create_widget(self.filter_container): self.filter_container = QtWidgets.QWidget() if should_create_widget(self.level_panel): self.level_panel = QtWidgets.QGroupBox('Level Filter') self.level_panel.setFlat(True) if should_create_widget(self.level_filter): self.level_filter = QtWidgets.QListWidget() self.level_filter.setEditTriggers(self.level_filter.NoEditTriggers) self.level_filter.setWordWrap(True) self.level_filter.setDropIndicatorShown(False) self.level_filter.setDragDropOverwriteMode(False) self.level_filter.setSortingEnabled(True) if should_create_widget(self.logger_panel): self.logger_panel = QtWidgets.QGroupBox('Logger Filter') self.logger_panel.setFlat(True) if should_create_widget(self.logger_filter): self.logger_filter = QtWidgets.QTreeWidget() self.logger_filter.setWordWrap(True) self.logger_filter.setEditTriggers(self.logger_filter.NoEditTriggers) self.logger_filter.setDropIndicatorShown(False) self.logger_filter.setDragEnabled(False) self.logger_filter.setSortingEnabled(True) self.logger_filter.setDragDropOverwriteMode(False) self.logger_filter.setHeaderHidden(True) # Misc if should_create_widget(self.deselect_action): self.deselect_action = QtWidgets.QAction('Deselect') self.deselect_action.setShortcut('ESC') layout = QtWidgets.QGridLayout(self) layout.addWidget(self.toolbar, 0, 0) layout.addWidget(self.toolbar_container, 0, 1) layout.addWidget(self.display, 0, 2) layout.addWidget(self.details, 1, 2) layout.setContentsMargins(5, 5, 5, 5) # Toolbar insertion self.toolbar.addAction(self.filter_action) self.toolbar.addAction(self.color_action) # Filter panel self.toolbar_container.addWidget(self.filter_container) f_layout = QtWidgets.QGridLayout(self.filter_container) f_layout.addWidget(self.logger_panel, 0, 0) f_layout.addWidget(self.level_panel, 1, 0) # Level filter f_le_layout = QtWidgets.QGridLayout(self.level_panel) f_le_layout.addWidget(self.level_filter) # Logger filter f_lo_layout = QtWidgets.QGridLayout(self.logger_panel) f_lo_layout.addWidget(self.logger_filter) # Misc self.display.addAction(self.deselect_action)
def setup_ui(self): """Sets up the UI for the help dialog.""" if should_create_widget(self.find_action): self.find_action = QtWidgets.QAction('Find') self.find_action.setShortcut('CTRL+F') if should_create_widget(self.display): self.display = QtWidgets.QTextBrowser() self.display.setReadOnly(True) if should_create_widget(self.search): self.search = QtWidgets.QLineEdit() if should_create_widget(self.search_button): self.search_button = QtWidgets.QPushButton('Search') if should_create_widget(self.search_container): self.search_container = QtWidgets.QWidget() if should_create_widget(self.search_box): self.search_box = QtWidgets.QWidget() if should_create_widget(self.search_augment_container): self.search_augment_container = QtWidgets.QWidget() if should_create_widget(self.search_insensitive): self.search_insensitive = QtWidgets.QCheckBox('Insensitive?') if should_create_widget(self.search_whole): self.search_whole = QtWidgets.QCheckBox('Whole Word?') if should_create_widget(self.search_backward): self.search_backward = QtWidgets.QCheckBox('Backwards?') if should_create_widget(self.search_auto): self.search_auto = QtWidgets.QCheckBox('Auto Search?') layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.search_box) layout.addWidget(self.display) box_layout = QtWidgets.QVBoxLayout(self.search_box) box_layout.addWidget(self.search_container) box_layout.addWidget(self.search_augment_container) box_layout.setContentsMargins(2, 2, 2, 2) search_layout = QtWidgets.QHBoxLayout(self.search_container) search_layout.addWidget(self.search) search_layout.addWidget(self.search_button) search_layout.setContentsMargins(0, 0, 0, 0) augment_layout = QtWidgets.QHBoxLayout(self.search_augment_container) augment_layout.addWidget(self.search_backward) augment_layout.addWidget(self.search_insensitive) augment_layout.addWidget(self.search_whole) augment_layout.addWidget(self.search_auto) augment_layout.setContentsMargins(0, 0, 0, 0) augment_layout.setSpacing(2) self.search_box.setHidden(True)
def setup_ui(self): """Sets up the generator's UI.""" # Widget validation # Top-level if should_create_widget(self.stacked): self.stacked = QtWidgets.QStackedWidget() # Stacks if should_create_widget(self.browser_container): self.browser_container = QtWidgets.QWidget() if should_create_widget(self.scope_container): self.scope_container = QtWidgets.QWidget() # Browser widgets if should_create_widget(self.browser): self.browser = QtWebEngineWidgets.QWebEngineView() self.browser.urlChanged.connect(self.url_watcher) if should_create_widget(self.label): self.label = QtWidgets.QLabel() self.label.setWordWrap(True) # Scope widgets if should_create_widget(self.scopes): self.scopes = QtWidgets.QListWidget() for scope in Scopes.__members__.values(): # type: Scopes i = QtWidgets.QListWidgetItem(scope.value) i.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled) i.setCheckState(QtCore.Qt.Unchecked) self.scopes.addItem(i) if should_create_widget(self.generate): self.generate = QtWidgets.QPushButton('Generate token') self.generate.clicked.connect(self.initiate) # Layout validation layout: QtWidgets.QGridLayout = self.layout() scope_layout: QtWidgets.QVBoxLayout = self.scope_container.layout() browser_layout: QtWidgets.QVBoxLayout = self.browser_container.layout() if layout is None: layout = QtWidgets.QGridLayout(self) if scope_layout is None: scope_layout = QtWidgets.QVBoxLayout(self.scope_container) scope_layout.setContentsMargins(0, 0, 0, 0) if browser_layout is None: browser_layout = QtWidgets.QVBoxLayout(self.browser_container) browser_layout.setContentsMargins(0, 0, 0, 0) # Generator layout if layout.indexOf(self.stacked) == -1: layout.addWidget(self.stacked) # Scope layout if scope_layout.indexOf(self.scopes) == -1: scope_layout.insertWidget(0, self.scopes) if scope_layout.indexOf(self.generate) == -1: scope_layout.addWidget(self.generate) # Browser layout if browser_layout.indexOf(self.label) == -1: browser_layout.insertWidget(0, self.label) if browser_layout.indexOf(self.browser) == -1: browser_layout.addWidget(self.browser) # Stacked insertion if self.stacked.indexOf(self.scope_container) == -1: self.stacked.addWidget(self.scope_container) if self.stacked.indexOf(self.browser_container) == -1: self.stacked.addWidget(self.browser_container)
def unload_extensions(self): """Unloads all extensions.""" self.LOGGER.warning(f'Unloading {len(self.extensions)} extensions...') for name, value in self.extensions.copy().items(): if isinstance(value, dataclasses.ExtensionStub): continue # Since we can't unload a stub try: stub = value.unload() except NotImplementedError: self.LOGGER.debug( f'{value.DISPLAY_NAME} does not implement an unload method!' ) else: self.LOGGER.debug( f'{value.DISPLAY_NAME} successfully unloaded!') self.extensions[name] = stub finally: self.LOGGER.info( f'Unregistering commands for {value.DISPLAY_NAME}...') before = len(self.command_manager.commands) for attr, inst in inspect.getmembers(value): if isinstance(inst, commands.Command): try: self.command_manager.commands.remove(inst) except ValueError: self.LOGGER.warning( f'Command {value.__class__.__name__}.{inst.name} was not previously ' f'registered!') else: self.LOGGER.debug( f'Unregistered command {value.__class__.__name__}.{inst.name}!' ) self.LOGGER.info( f'Unregistered {before - len(self.command_manager.commands)} commands!' ) try: row, *_ = self.ui.extensions_table.row_from_header( value.NAME) except LookupError: self.LOGGER.debug( f"Couldn't remove {value.DISPLAY_NAME} from the extensions table!" ) self.LOGGER.debug('Extensions table: {}'.format( 'DELETED' if should_create_widget() else 'EXISTS')) self.LOGGER.debug('Extension: {}'.format( self.extensions[value.NAME].STATE.value)) self.LOGGER.debug('Row: probably deleted by an extension') else: self.ui.extensions_table.set_row( row, State=value.STATE.name.replace('_', ' ').capitalize()) self.LOGGER.warning(f'Unloaded {len(self.extensions)} extensions!') self.extensions.clear()
def load_settings(self): """Loads ShovelBot's settings.""" if self._settings_file is None: app: QtWidgets.QApplication = QtWidgets.QApplication.instance() self._settings_file = QtCore.QFile( f'{app.applicationName()}.settings') if self._settings_file.exists(): if not self._settings_file.isOpen(): self._settings_file.open(self._settings_file.ReadOnly | self._settings_file.Text) if self._settings_file.isReadable(): raw_text: QtCore.QByteArray = self._settings_file.readAll() if not raw_text.isNull(): raw_text: str = raw_text.data().decode() try: decoded_text = json.loads(raw_text) except ValueError as e: self.LOGGER.warning( 'Could not decode file settings.json!') self.LOGGER.warning( f'Exception Type: {e.__class__.__name__}') self.LOGGER.warning(f'Exception Cause: {e.__cause__}') else: for raw_setting in decoded_text: try: setting = settings.Setting.from_data( raw_setting) except ValueError as e: self.LOGGER.warning( 'Could not decode file settings.json!') self.LOGGER.warning( f'Exception Type: {e.__class__.__name__}') self.LOGGER.warning( f'Exception Cause: {e.__cause__}') else: if should_create_widget(self.settings): self.settings = settings.Display() self.settings.register_setting(setting) finally: if self._settings_file.isOpen(): self._settings_file.close() self.validate_settings() else: self.LOGGER.warning('Settings file contains no data!') self.LOGGER.warning('Was this an error?') for s in self.generate_settings(): self.settings.register_setting(s) else: self.LOGGER.warning('Settings file does not exist!') self.LOGGER.info('Is this a first run?') for s in self.generate_settings(): self.settings.register_setting(s)
def setup_help_ui(self): """Prepares the help UI for display.""" if should_create_widget(self.help_dialog): self.help_dialog = Help(engine=self.help_engine)