class DatatypeSelector(QGroupBox): def __init__(self, title: str, datatype_to_widget, parent: "QWidget" = None): super().__init__(title, parent) # Maps a datatype with its respective widget. The widget is optional self._datatype_to_widget = datatype_to_widget self._datatype_combobox = QComboBox() self._stacked_widgets = QStackedWidget() for (i, (name, datatype_factory)) in enumerate( DataTypeContainer.providers.items()): datatype_instance = datatype_factory() self._datatype_combobox.addItem(name, datatype_instance) if datatype_factory in self._datatype_to_widget: self._stacked_widgets.insertWidget( i, self._datatype_to_widget[datatype_factory]( datatype_instance)) self._main_layout = QVBoxLayout() self._main_layout.addWidget(self._datatype_combobox) self._main_layout.addWidget(self._stacked_widgets) self.setLayout(self._main_layout) self._datatype_combobox.currentIndexChanged[int].connect( self._change_active_widget) @property def selected_datatype(self): return self._datatype_combobox.currentData() def change_current_datatype(self, new_datatype_dict: dict): index = self._datatype_combobox.findText(new_datatype_dict["class"]) if index != -1: self._datatype_combobox.setCurrentIndex(index) self._datatype_combobox.currentData().from_dict(new_datatype_dict) self._stacked_widgets.currentWidget().reload() def _change_active_widget(self, index): self._stacked_widgets.setCurrentIndex(index) # Hide the `stacked_widgets` when the current datatype doesn't needs to display # a widget if self._stacked_widgets.currentIndex() != index: self._stacked_widgets.setVisible(False) else: self._stacked_widgets.setVisible(True) def to_dict(self): return self.selected_datatype.to_dict()
class MainWidget(QWidget): def __init__(self, parent: QWidget, model: Model) -> None: super().__init__(parent) logger.add(self.log) self.mainlayout = QVBoxLayout() self.setLayout(self.mainlayout) self.splitter = QSplitter(Qt.Vertical) self.stack = QStackedWidget() self.splitter.addWidget(self.stack) # mod list widget self.modlistwidget = QWidget() self.modlistlayout = QVBoxLayout() self.modlistlayout.setContentsMargins(0, 0, 0, 0) self.modlistwidget.setLayout(self.modlistlayout) self.stack.addWidget(self.modlistwidget) # search bar self.searchbar = QLineEdit() self.searchbar.setPlaceholderText('Search...') self.modlistlayout.addWidget(self.searchbar) # mod list self.modlist = ModList(self, model) self.modlistlayout.addWidget(self.modlist) self.searchbar.textChanged.connect(lambda e: self.modlist.setFilter(e)) # welcome message welcomelayout = QVBoxLayout() welcomelayout.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) welcomewidget = QWidget() welcomewidget.setLayout(welcomelayout) welcomewidget.dragEnterEvent = self.modlist.dragEnterEvent # type: ignore welcomewidget.dragMoveEvent = self.modlist.dragMoveEvent # type: ignore welcomewidget.dragLeaveEvent = self.modlist.dragLeaveEvent # type: ignore welcomewidget.dropEvent = self.modlist.dropEvent # type: ignore welcomewidget.setAcceptDrops(True) icon = QIcon(str(getRuntimePath('resources/icons/open-folder.ico'))) iconpixmap = icon.pixmap(32, 32) icon = QLabel() icon.setPixmap(iconpixmap) icon.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) icon.setContentsMargins(4, 4, 4, 4) welcomelayout.addWidget(icon) welcome = QLabel('''<p><font> No mod installed yet. Drag a mod into this area to get started! </font></p>''') welcome.setAttribute(Qt.WA_TransparentForMouseEvents) welcome.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) welcomelayout.addWidget(welcome) self.stack.addWidget(welcomewidget) # output log self.output = QTextEdit(self) self.output.setTextInteractionFlags(Qt.NoTextInteraction) self.output.setReadOnly(True) self.output.setContextMenuPolicy(Qt.NoContextMenu) self.output.setPlaceholderText('Program output...') self.splitter.addWidget(self.output) # TODO: enhancement: add a launch game icon # TODO: enhancement: show indicator if scripts have to be merged self.splitter.setStretchFactor(0, 1) self.splitter.setStretchFactor(1, 0) self.mainlayout.addWidget(self.splitter) if len(model): self.stack.setCurrentIndex(0) self.splitter.setSizes([self.splitter.size().height(), 50]) else: self.stack.setCurrentIndex(1) self.splitter.setSizes([self.splitter.size().height(), 0]) model.updateCallbacks.append(self.modelUpdateEvent) def keyPressEvent(self, event: QKeyEvent) -> None: if event.key() == Qt.Key_Escape: self.modlist.setFocus() self.searchbar.setText('') elif event.matches(QKeySequence.Find): self.searchbar.setFocus() elif event.matches(QKeySequence.Paste): self.pasteEvent() super().keyPressEvent(event) def pasteEvent(self) -> None: clipboard = QApplication.clipboard().text().splitlines() if len(clipboard) == 1 and isValidNexusModsUrl(clipboard[0]): self.parentWidget().showDownloadModDialog() else: urls = [ url for url in QApplication.clipboard().text().splitlines() if len(str(url.strip())) ] if all( isValidModDownloadUrl(url) or isValidFileUrl(url) for url in urls): asyncio.create_task(self.modlist.checkInstallFromURLs(urls)) def modelUpdateEvent(self, model: Model) -> None: if len(model) > 0: if self.stack.currentIndex() != 0: self.stack.setCurrentIndex(0) self.repaint() else: if self.stack.currentIndex() != 1: self.stack.setCurrentIndex(1) self.repaint() def unhideOutput(self) -> None: if self.splitter.sizes()[1] < 10: self.splitter.setSizes([self.splitter.size().height(), 50]) def unhideModList(self) -> None: if self.splitter.sizes()[0] < 10: self.splitter.setSizes([50, self.splitter.size().height()]) def log(self, message: Any) -> None: # format log messages to user readable output settings = QSettings() record = message.record message = record['message'] extra = record['extra'] level = record['level'].name.lower() name = str(extra['name'] ) if 'name' in extra and extra['name'] is not None else '' path = str(extra['path'] ) if 'path' in extra and extra['path'] is not None else '' dots = bool( extra['dots'] ) if 'dots' in extra and extra['dots'] is not None else False newline = bool( extra['newline'] ) if 'newline' in extra and extra['newline'] is not None else False output = bool( extra['output'] ) if 'output' in extra and extra['output'] is not None else bool( message) modlist = bool( extra['modlist'] ) if 'modlist' in extra and extra['modlist'] is not None else False if level in ['debug' ] and settings.value('debugOutput', 'False') != 'True': if newline: self.output.append(f'') return n = '<br>' if newline else '' d = '...' if dots else '' if len(name) and len(path): path = f' ({path})' if output: message = html.escape(message, quote=True) if level in ['success', 'error', 'warning']: message = f'<strong>{message}</strong>' if level in ['success']: message = f'<font color="#04c45e">{message}</font>' if level in ['error', 'critical']: message = f'<font color="#ee3b3b">{message}</font>' if level in ['warning']: message = f'<font color="#ff6500">{message}</font>' if level in ['debug', 'trace']: message = f'<font color="#aaa">{message}</font>' path = f'<font color="#aaa">{path}</font>' if path else '' d = f'<font color="#aaa">{d}</font>' if d else '' time = record['time'].astimezone( tz=None).strftime('%Y-%m-%d %H:%M:%S') message = f'<font color="#aaa">{time}</font> {message}' self.output.append( f'{n}{message.strip()}{" " if name or path else ""}{name}{path}{d}' ) else: self.output.append(f'') self.output.verticalScrollBar().setValue( self.output.verticalScrollBar().maximum()) self.output.repaint() if modlist: self.unhideModList() if settings.value('unhideOutput', 'True') == 'True' and output: self.unhideOutput()
# --------------------------- # StackedWidgetを表示する # --------------------------- import sys from PySide2.QtWidgets import QApplication, QTextEdit, QStackedWidget app = QApplication(sys.argv) qw_text_edit_1 = QTextEdit() qw_text_edit_1.append('1') qw_text_edit_2 = QTextEdit() qw_text_edit_2.append('2') qw_stack = QStackedWidget() # QStackedWidgetにTextEditを2つ追加する qw_stack.addWidget(qw_text_edit_1) qw_stack.addWidget(qw_text_edit_2) print(qw_stack.currentIndex()) qw_stack.show() # 最初に追加したTextEditが表示される sys.exit(app.exec_())
def __init__(self, parent=None): super().__init__(parent) self.setMinimumSize(800, 600) self.setWindowTitle(self.tr("Preferences")) self._preferences = Preferences() # # Content self._generalPage = PreferencesGeneralPage() self._generalPage.setZeroMargins() self._generalPage.preferencesChanged.connect( self._onPreferencesChanged) self._documentsPage = PreferencesDocumentsPage() self._documentsPage.setZeroMargins() self._documentsPage.preferencesChanged.connect( self._onPreferencesChanged) self._documentPresetsPage = PreferencesDocumentPresetsPage() self._documentPresetsPage.setZeroMargins() self._documentPresetsPage.preferencesChanged.connect( self._onPreferencesChanged) stackedBox = QStackedWidget() stackedBox.addWidget(self._generalPage) stackedBox.addWidget(self._documentsPage) stackedBox.addWidget(self._documentPresetsPage) stackedBox.setCurrentIndex(0) listBox = QListWidget() listBox.addItem(self._generalPage.title()) listBox.addItem(self._documentsPage.title()) listBox.addItem(self._documentPresetsPage.title()) listBox.setCurrentRow(stackedBox.currentIndex()) listBox.currentRowChanged.connect(stackedBox.setCurrentIndex) preferencesBox = QHBoxLayout() preferencesBox.addWidget(listBox, 1) preferencesBox.addWidget(stackedBox, 3) # Button box buttonBox = QDialogButtonBox(QDialogButtonBox.RestoreDefaults | QDialogButtonBox.Ok | QDialogButtonBox.Apply | QDialogButtonBox.Cancel) self._buttonApply = buttonBox.button(QDialogButtonBox.Apply) buttonBox.button(QDialogButtonBox.RestoreDefaults).clicked.connect( self._onButtonDefaultsClicked) buttonBox.accepted.connect(self._onButtonOkClicked) buttonBox.button(QDialogButtonBox.Apply).clicked.connect( self._onButtonApplyClicked) buttonBox.rejected.connect(self.close) # Main layout layout = QVBoxLayout(self) layout.addLayout(preferencesBox) layout.addWidget(buttonBox) self._updatePreferences() self._buttonApply.setEnabled(False)
class UIStackedWidget(object): ''' By default: QWidget or QDockWidget +----------------------------------------------------------+ | QVBoxLayout | | +---------------------------------------------------+ | | | | | | | +-------------------------------------------+ | | | | | QList | | | | | | | | | | | | | | | | | +-------------------------------------------+ | | | | +-------------------------------------------+ | | | | | QStackedWidget | | | | | | | | | | | | | | | | | +-------------------------------------------+ | | | | | | | +---------------------------------------------------+ | | | +----------------------------------------------------------+ or if layout = horizontal: QWidget or QDockWidget +----------------------------------------------------------+ | QHBoxLayout | | +---------------------------------------------------+ | | | | | | | +--------------+ +------------------------+ | | | | | QList | | QStackedWidget | | | | | | | | | | | | | | | | | | | | | +--------------+ +------------------------+ | | | | | | | +---------------------------------------------------+ | | | +----------------------------------------------------------+ ''' def createStack(self, layout='vertical'): self.stack_list = QListWidget() self.list_margin_size = 11 self.Stack = QStackedWidget(self) self.layout_type = layout if self.layout_type == 'horizontal': box = QHBoxLayout(self) self.list_margin_size = 18 self.stack_list.setStyleSheet( "margin-top: {size}px".format(size=self.list_margin_size)) else: box = QVBoxLayout(self) self.stack_list.setStyleSheet( "margin-left : {size}px; margin-right : {size2}px".format( size=self.list_margin_size, size2=self.list_margin_size + 1)) box.addWidget(self.stack_list) box.addWidget(self.Stack) box.setAlignment(self.stack_list, Qt.AlignTop) self.setLayout(box) self.stack_list.currentRowChanged.connect(self.display) self.tabs = {} self.num_tabs = 0 self.widgets = {} def display(self, i): self.Stack.setCurrentIndex(i) def currentIndex(self): return self.Stack.currentIndex() def addTab(self, title, widget='form'): self.stack_list.addItem(title) if widget == 'form': widget = UIFormFactory.getQWidget(self) self.Stack.addWidget(widget) self.tabs[title] = widget self.num_tabs += 1 height_multiplier = self.num_tabs + 1 if self.layout_type != 'vertical': height_multiplier += 1 self.stack_list.setMaximumWidth( self.stack_list.sizeHintForColumn(0) * 1.2 + self.list_margin_size * 2) self.stack_list.setMaximumHeight( self.stack_list.sizeHintForRow(0) * height_multiplier + self.list_margin_size * 2) self.stack_list.setMaximumHeight( self.stack_list.sizeHintForRow(0) * height_multiplier) def addTabs(self, titles): for title in titles: self.addTab(title=title)