def setupTabs(self): """ Setup the various tabs in the AddressWidget. """ groups = ["ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VW", "XYZ"] for group in groups: proxyModel = QSortFilterProxyModel(self) proxyModel.setSourceModel(self.tableModel) proxyModel.setDynamicSortFilter(True) tableView = QTableView() tableView.setModel(proxyModel) tableView.setSortingEnabled(True) tableView.setSelectionBehavior(QAbstractItemView.SelectRows) tableView.horizontalHeader().setStretchLastSection(True) tableView.verticalHeader().hide() tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) tableView.setSelectionMode(QAbstractItemView.SingleSelection) # This here be the magic: we use the group name (e.g. "ABC") to # build the regex for the QSortFilterProxyModel for the group's # tab. The regex will end up looking like "^[ABC].*", only # allowing this tab to display items where the name starts with # "A", "B", or "C". Notice that we set it to be case-insensitive. reFilter = "^[%s].*" % group proxyModel.setFilterRegExp(QRegExp(reFilter, Qt.CaseInsensitive)) proxyModel.setFilterKeyColumn(0) # Filter on the "name" column proxyModel.sort(0, Qt.AscendingOrder) # This prevents an application crash (see: http://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash) viewselectionmodel = tableView.selectionModel() tableView.selectionModel().selectionChanged.connect(self.selectionChanged) self.addTab(tableView, group)
def setupTabs(self): """ Setup the various tabs in the AddressWidget. """ groups = ["ABC", "DEF", "GHI", "JKL", "MNO", "PQR", "STU", "VW", "XYZ"] for group in groups: proxyModel = QSortFilterProxyModel(self) proxyModel.setSourceModel(self.tableModel) proxyModel.setDynamicSortFilter(True) tableView = QTableView() tableView.setModel(proxyModel) tableView.setSortingEnabled(True) tableView.setSelectionBehavior(QAbstractItemView.SelectRows) tableView.horizontalHeader().setStretchLastSection(True) tableView.verticalHeader().hide() tableView.setEditTriggers(QAbstractItemView.NoEditTriggers) tableView.setSelectionMode(QAbstractItemView.SingleSelection) # This here be the magic: we use the group name (e.g. "ABC") to # build the regex for the QSortFilterProxyModel for the group's # tab. The regex will end up looking like "^[ABC].*", only # allowing this tab to display items where the name starts with # "A", "B", or "C". Notice that we set it to be case-insensitive. reFilter = "^[%s].*" % group proxyModel.setFilterRegExp(QRegExp(reFilter, Qt.CaseInsensitive)) proxyModel.setFilterKeyColumn(0) # Filter on the "name" column proxyModel.sort(0, Qt.AscendingOrder) # This prevents an application crash (see: http://www.qtcentre.org/threads/58874-QListView-SelectionModel-selectionChanged-Crash) viewselectionmodel = tableView.selectionModel() tableView.selectionModel().selectionChanged.connect( self.selectionChanged) self.addTab(tableView, group)
class __HPasteCollectionWidget(CollectionWidget): def __init__(self, parent=None): super(HPasteCollectionWidget.__HPasteCollectionWidget, self).__init__(parent, metadataExposedKeys=('raw_url', 'nettype')) for x in xrange(1, 5): self.ui.mainView.horizontalHeader().hideSection(x) self.__nepane = None self.__netType = '' self.__nettypeFilter = QSortFilterProxyModel(self) self.__nettypeFilter.setFilterKeyColumn(4) self.__nettypeFilter.setFilterRegExp( QRegExp("*", Qt.CaseInsensitive, QRegExp.Wildcard)) self.appendFilter(self.__nettypeFilter) self.accepted.connect(self.doOnAccept) def setNetworkEditor(self, pane): if (not isinstance(pane, hou.NetworkEditor)): pane = None self.__nepane = pane #save to position pasted nodes in it if (pane is None): nettype = '*' self.__netType = '' #Used to create new snippet types else: nettype = hpaste.getChildContext(pane.pwd(), hou.applicationVersion()) self.__netType = nettype self.__nettypeFilter.setFilterRegExp( QRegExp(nettype, Qt.CaseInsensitive, QRegExp.Wildcard)) @Slot(object) def doOnAccept(self, item): if (item is None): return try: hou.clearAllSelected() hpaste.stringToNodes(item.content(), ne=self.__nepane) except Exception as e: hou.ui.displayMessage("could not paste: %s" % e.message, severity=hou.severityType.Warning) def _addItem(self, collection): #Please, dont throw from here! try: nodes = hou.selectedItems() except: nodes = hou.selectedNodes() if (len(nodes) == 0): QMessageBox.warning(self, 'not created', 'selection is empty, nothing to add') return while True: #btn,(name,desc) = (0,('1','2'))#hou.ui.readMultiInput('enter some information about new item',('name','description'),buttons=('Ok','Cancel')) name, desc, public, good = QDoubleInputDialog.getDoubleTextCheckbox( self, 'adding a new item to %s' % collection.name(), 'enter new item details', 'name', 'description', 'public', '', 'a snippet', False) if (not good): return if (len(name) > 0): break #validity check try: #print(name) #print(desc) #print(hpaste.nodesToString(nodes)) self.model().addItemToCollection( collection, name, desc, hpaste.nodesToString(nodes), public, metadata={'nettype': self.__netType}) except CollectionSyncError as e: QMessageBox.critical(self, 'something went wrong!', 'Server error occured: %s' % e.message) def _changeAccess(self, index): item = index.internalPointer() text, good = QInputDialog.getItem( None, 'modify item access', 'choose new access type:', ['private', 'public'], current=item.access() == CollectionItem.AccessType.public, editable=False) if (not good): return newaccess = CollectionItem.AccessType.public if text == 'public' else CollectionItem.AccessType.private if (newaccess == item.access()): return item.setAccess(newaccess) def _replaceContent(self, index): try: nodes = hou.selectedItems() except: nodes = hou.selectedNodes() if (len(nodes) == 0): QMessageBox.warning(self, 'cannot replace', 'selection is empty') return item = index.internalPointer() good = QMessageBox.warning( self, 'sure?', 'confirm that you want to replace the content of selected item "%s". This operation can not be undone.' % item.name(), QMessageBox.Ok | QMessageBox.Cancel) == QMessageBox.Ok if (not good): return try: item.setContent(hpaste.nodesToString(nodes)) except CollectionSyncError as e: QMessageBox.critical(self, 'something went wrong!', 'Server error occured: %s' % e.message) def _itemInfo(self, index): item = index.internalPointer() accesstext = 'public' if item.access( ) == CollectionItem.AccessType.public else 'private' readonlytext = 'readonly' if item.readonly() else 'editable' info = 'name: %s\n%s\naccess: %s\n%s\n\ncollection id: %s\n\nmetadata:\n' % ( item.name(), item.description(), accesstext, readonlytext, item.id()) info += '\n'.join(('%s : %s' % (key, item.metadata()[key]) for key in item.metadata())) QMessageBox.information(self, 'item information', info) def _renameItem(self, index): item = index.internalPointer() oldname = item.name() olddesc = item.description() newname, newdesc, good = QDoubleInputDialog.getDoubleText( self, 'modify item info', 'Enter new item name and description', 'name', 'description', oldname, olddesc) if (not good): return if (newname != oldname): item.setName(newname) if (newdesc != olddesc): item.setDescription(newdesc) #def _replaceContent(self, index): #pass def _confirmRemove(self, index): return QMessageBox.warning( self, 'sure?', 'confirm removing the item from collection. This operation can not be undone.', QMessageBox.Ok | QMessageBox.Cancel) == QMessageBox.Ok
class FE14CharacterEditor(Ui_FE14CharacterEditor): def __init__(self, is_person=False, parent=None): super().__init__(parent) self.is_person = is_person self.module: TableModule = locator.get_scoped( "ModuleService").get_module("Characters") self.proxy_model = QSortFilterProxyModel() self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) self.proxy_model.setSourceModel(self.module.entries_model) self.characters_list_view.setModel(self.proxy_model) self.selection: Optional[PropertyContainer] = None self.character_details_form_1 = PropertyForm( self.module.element_template, category="character_description_1") self.character_details_form_contents_1.setLayout( self.character_details_form_1) self.character_details_form_2 = PropertyForm( self.module.element_template, category="character_description_2") self.character_details_form_contents_2.setLayout( self.character_details_form_2) self.character_details_form_2.fix_editor_width(100) self.stats_editor = MergedStatsEditor( ["Bases", "Growths", "Modifiers", "Penalties", "Bonuses"]) self.stats_form = PropertyForm(self.module.element_template, category="stats") self.stats_layout.addWidget(self.stats_editor) self.stats_layout.addLayout(self.stats_form) self.skills_form = PropertyForm(self.module.element_template, category="skills", sort_editors=True) self.skills_contents.setLayout(self.skills_form) self.flags_editor = MergedFlagsEditor( ["Bitflags (1)", "Bitflags (2)", "Bitflags (3)", "Bitflags (4)"], self.module.element_template) self.flags_editor_2 = MergedFlagsEditor( ["Bitflags (5)", "Bitflags (6)", "Bitflags (7)", "Bitflags (8)"], self.module.element_template) self.misc_form = PropertyForm(self.module.element_template, category="misc") self.misc_layout.addWidget(self.flags_editor) self.misc_layout.addWidget(self.flags_editor_2) self.misc_layout.addLayout(self.misc_form) self.ids_form = PropertyForm(self.module.element_template, category="ids") self.ids_tab.setLayout(self.ids_form) self.classes_form = PropertyForm(self.module.element_template, category="classes", sort_editors=True) self.classes_tab.setLayout(self.classes_form) if not self.is_person: self.dialogue_tab = DialogueEditor() self.supports_tab = QWidget() self.supports_layout = QHBoxLayout() self.supports_widget = FE14SupportWidget() self.supports_scroll = QScrollArea() self.supports_scroll_contents = QWidget() self.supports_scroll.setWidget(self.supports_scroll_contents) self.supports_scroll.setWidgetResizable(True) self.supports_layout.addWidget(self.supports_widget) self.supports_layout.addWidget(self.supports_scroll) self.supports_tab.setLayout(self.supports_layout) self.supports_form = PropertyForm(self.module.element_template, category="supports") self.supports_scroll_contents.setLayout(self.supports_form) self.tab_widget.addTab(self.supports_tab, "Supports") self.tab_widget.addTab(self.dialogue_tab, "Dialogue") self.context_menu = QMenu(self) self.context_menu.addActions( [self.action_add, self.action_remove, self.action_copy_to]) self.clear_selection_shortcut = QShortcut(QKeySequence.Cancel, self) self._install_signals() self._clear() def set_module(self, module: TableModule): self.module = module if self.module: self.proxy_model.setSourceModel(self.module.entries_model) else: self.proxy_model.setSourceModel(None) self.setEnabled(self.module is not None) self._clear() def _on_context_menu_requested(self, point: QPoint): self.context_menu.exec_(self.characters_list_view.mapToGlobal(point)) def _install_signals(self): self.characters_list_view.selectionModel().currentRowChanged.connect( self._update_selection) self.characters_list_view.customContextMenuRequested.connect( self._on_context_menu_requested) self.search_bar.textChanged.connect(self._update_filter) self.action_add.triggered.connect(self._on_add_character_triggered) self.action_remove.triggered.connect( self._on_remove_character_triggered) self.action_copy_to.triggered.connect(self._on_copy_to_triggered) self.clear_selection_shortcut.activated.connect(self._clear) if self.character_details_form_1.editors['Name'] != None: self.character_details_form_1.editors[ 'Name'].value_editor.editingFinished.connect( self._update_conversation_widget) def _clear(self): self.characters_list_view.clearSelection() self.characters_list_view.selectionModel().clearCurrentIndex() def _update_selection(self, index: QModelIndex): self.selection = self.proxy_model.data(index, QtCore.Qt.UserRole) self.portraits_tab.update_target(self.selection) self.character_details_form_1.update_target(self.selection) self.character_details_form_2.update_target(self.selection) self.stats_editor.update_target(self.selection) self.ids_form.update_target(self.selection) self.classes_form.update_target(self.selection) self.stats_form.update_target(self.selection) self.skills_form.update_target(self.selection) self.flags_editor.update_target(self.selection) self.flags_editor_2.update_target(self.selection) self.misc_form.update_target(self.selection) if not self.is_person: self.dialogue_tab.update_target(self.selection) self.supports_widget.update_target(self.selection) self.supports_form.update_target(self.selection) if self.selection: locator.get_scoped("SpriteService").get_sprite_for_character( self.selection, 0) self.action_remove.setEnabled(self.selection is not None) self.action_copy_to.setEnabled(self.selection is not None) self._update_portrait_box() def _update_portrait_box(self): portrait_service = locator.get_scoped("PortraitService") mini_portraits = portrait_service.get_sorted_portraits_for_character( self.selection, "bu") if mini_portraits: _, texture = mini_portraits[0] scene = QGraphicsScene() scene.addPixmap(QPixmap.fromImage(texture.image())) self.portrait_display.setScene(scene) else: self.portrait_display.setScene(None) def _update_filter(self): self.proxy_model.setFilterRegExp(self.search_bar.text()) def _on_add_character_triggered(self): model = self.module.entries_model model.insertRow(model.rowCount()) source = self.module.entries[0] destination = self.module.entries[-1] source.copy_to(destination) # Update any present conversation widget with the new obj list self._update_conversation_widget() def _on_remove_character_triggered(self): if self.characters_list_view.currentIndex().isValid(): model = self.module.entries_model model.removeRow(self.characters_list_view.currentIndex().row()) model.beginResetModel() model.endResetModel() # Update any present conversation widget with the new obj list self._update_conversation_widget() def _update_conversation_widget(self): for editor in self.supports_widget.service._conversation_editors: editor: FE14ConversationEditor character_list = list() [ character_list.append(child[1]) for child in self.module.children() ] editor.text_area._character_list = character_list def _on_copy_to_triggered(self): if not self.selection: return logging.info("Beginning copy to for " + self.module.name) choices = [] for i in range(0, len(self.module.entries)): choices.append( str(i + 1) + ". " + self.module.entries[i].get_display_name()) choice = QInputDialog.getItem(self, "Select Destination", "Destination", choices) if choice[1]: for i in range(0, len(choices)): if choice[0] == choices[i]: self.selection.copy_to(self.module.entries[i]) # Update any present conversation widget with the new obj list self._update_conversation_widget() else: logging.info("No choice selected for " + self.module.name + " copy to. Aborting.")
class SearchBarEditor(QTableView): """A Google-like search bar, implemented as a QTableView with a _CustomLineEditDelegate in the first row. """ data_committed = Signal() def __init__(self, parent, tutor=None): """Initializes instance. Args: parent (QWidget): parent widget tutor (QWidget, NoneType): another widget used for positioning. """ super().__init__(parent) self._tutor = tutor self._base_size = QSize() self._base_offset = QPoint() self._original_text = None self._orig_pos = None self.first_index = QModelIndex() self.model = QStandardItemModel(self) self.proxy_model = QSortFilterProxyModel(self) self.proxy_model.setSourceModel(self.model) self.proxy_model.filterAcceptsRow = self._proxy_model_filter_accepts_row self.setModel(self.proxy_model) self.verticalHeader().hide() self.horizontalHeader().hide() self.setShowGrid(False) self.setMouseTracking(True) self.setTabKeyNavigation(False) delegate = _CustomLineEditDelegate(self) delegate.text_edited.connect(self._handle_delegate_text_edited) self.setItemDelegateForRow(0, delegate) def set_data(self, current, items): """Populates model. Args: current (str) items (Sequence(str)) """ item_list = [QStandardItem(current)] for item in items: qitem = QStandardItem(item) item_list.append(qitem) qitem.setFlags(~Qt.ItemIsEditable) self.model.invisibleRootItem().appendRows(item_list) self.first_index = self.proxy_model.mapFromSource( self.model.index(0, 0)) def set_base_size(self, size): self._base_size = size def set_base_offset(self, offset): self._base_offset = offset def update_geometry(self): """Updates geometry. """ self.horizontalHeader().setDefaultSectionSize(self._base_size.width()) self.verticalHeader().setDefaultSectionSize(self._base_size.height()) self._orig_pos = self.pos() + self._base_offset if self._tutor: self._orig_pos += self._tutor.mapTo(self.parent(), self._tutor.rect().topLeft()) self.refit() def refit(self): self.move(self._orig_pos) table_height = self.verticalHeader().length() size = QSize(self._base_size.width(), table_height + 2).boundedTo(self.parent().size()) self.resize(size) # Adjust position if widget is outside parent's limits bottom_right = self.mapToGlobal(self.rect().bottomRight()) parent_bottom_right = self.parent().mapToGlobal( self.parent().rect().bottomRight()) x_offset = max(0, bottom_right.x() - parent_bottom_right.x()) y_offset = max(0, bottom_right.y() - parent_bottom_right.y()) self.move(self.pos() - QPoint(x_offset, y_offset)) def data(self): return self.first_index.data(Qt.EditRole) @Slot("QString") def _handle_delegate_text_edited(self, text): """Filters model as the first row is being edited.""" self._original_text = text self.proxy_model.setFilterRegExp("^" + text) self.proxy_model.setData(self.first_index, text) self.refit() def _proxy_model_filter_accepts_row(self, source_row, source_parent): """Always accept first row. """ if source_row == 0: return True return QSortFilterProxyModel.filterAcceptsRow(self.proxy_model, source_row, source_parent) def keyPressEvent(self, event): """Sets data from current index into first index as the user navigates through the table using the up and down keys. """ super().keyPressEvent(event) event.accept( ) # Important to avoid unhandled behavior when trying to navigate outside view limits # Initialize original text. TODO: Is there a better place for this? if self._original_text is None: self.proxy_model.setData(self.first_index, event.text()) self._handle_delegate_text_edited(event.text()) # Set data from current index in model if event.key() in (Qt.Key_Up, Qt.Key_Down): current = self.currentIndex() if current.row() == 0: self.proxy_model.setData(self.first_index, self._original_text) else: self.proxy_model.setData(self.first_index, current.data()) def currentChanged(self, current, previous): super().currentChanged(current, previous) self.edit_first_index() def edit_first_index(self): """Edits first index if valid and not already being edited. """ if not self.first_index.isValid(): return if self.isPersistentEditorOpen(self.first_index): return self.edit(self.first_index) def mouseMoveEvent(self, event): """Sets the current index to the one hovered by the mouse.""" if not self.currentIndex().isValid(): return index = self.indexAt(event.pos()) if index.row() == 0: return self.setCurrentIndex(index) def mousePressEvent(self, event): """Commits data.""" index = self.indexAt(event.pos()) if index.row() == 0: return self.proxy_model.setData(self.first_index, index.data(Qt.EditRole)) self.data_committed.emit()
class __HPasteCollectionWidget(CollectionWidget): def __init__(self, parent=None): super(HPasteCollectionWidget.__HPasteCollectionWidget, self).__init__(parent, metadataExposedKeys=('raw_url', 'nettype')) for x in xrange(1, 5): self.ui.mainView.horizontalHeader().hideSection(x) self.__nepane = None self.__netType = '' self.__nettypeFilter = QSortFilterProxyModel(self) self.__nettypeFilter.setFilterKeyColumn(4) self.__nettypeFilter.setFilterRegExp( QRegExp("*", Qt.CaseInsensitive, QRegExp.Wildcard)) self.appendFilter(self.__nettypeFilter) self.accepted.connect(self.doOnAccept) self.__insideAuthCallback = False #self.setProperty("houdiniStyle", True) ss = "QTableView{border : 0px solid; gridline-color: rgb(48,48,48)}" ss += "QHeaderView::section{border-style: none; border-bottom: 0px; border-right: 0px;}" self.setStyleSheet(ss) self.__savedNetworkViewPos = None def setNetworkEditor(self, pane): if (not isinstance(pane, hou.NetworkEditor)): pane = None self.__nepane = pane #save to position pasted nodes in it self.__savedNetworkViewPos = pane.cursorPosition() if (pane is None): nettype = '*' self.__netType = '' #Used to create new snippet types else: nettype = hpaste.getChildContext(pane.pwd(), hou.applicationVersion()) self.__netType = nettype self.__nettypeFilter.setFilterRegExp( QRegExp(nettype, Qt.CaseInsensitive, QRegExp.Wildcard)) @Slot(object) def doOnAccept(self, item): if (item is None): return try: try: #>h16 hou.clearAllSelected() except: #<=h15.5 hou.node("/obj").setSelected(False, clear_all_selected=True) hpaste.stringToNodes( item.content(), ne=self.__nepane, override_network_position=self.__savedNetworkViewPos) except RuntimeWarning as e: log('Warnings encountered during load:\n%s' % e.message, 2) except Exception as e: hou.ui.displayMessage("could not paste: %s" % e.message, severity=hou.severityType.Warning) def _addItem(self, collection): #Please, dont throw from here! try: nodes = hou.selectedItems() except: nodes = hou.selectedNodes() if (len(nodes) == 0): QMessageBox.warning(self, 'not created', 'selection is empty, nothing to add') return while True: #btn,(name,desc) = (0,('1','2'))#hou.ui.readMultiInput('enter some information about new item',('name','description'),buttons=('Ok','Cancel')) name, desc, public, good = QDoubleInputDialog.getDoubleTextCheckbox( self, 'adding a new item to %s' % collection.name(), 'enter new item details', 'name', 'description', 'public', '', 'a snippet', False) if (not good): return if (len(name) > 0): break #validity check try: #print(name) #print(desc) #print(hpaste.nodesToString(nodes)) self.model().addItemToCollection( collection, name, desc, hpaste.nodesToString(nodes), public, metadata={'nettype': self.__netType}) except CollectionSyncError as e: QMessageBox.critical(self, 'something went wrong!', 'Server error occured: %s' % e.message) def _changeAccess(self, index): item = index.internalPointer() text, good = QInputDialog.getItem( None, 'modify item access', 'choose new access type:', ['private', 'public'], current=item.access() == CollectionItem.AccessType.public, editable=False) if (not good): return newaccess = CollectionItem.AccessType.public if text == 'public' else CollectionItem.AccessType.private if (newaccess == item.access()): return item.setAccess(newaccess) def _replaceContent(self, index): try: nodes = hou.selectedItems() except: nodes = hou.selectedNodes() if (len(nodes) == 0): QMessageBox.warning(self, 'cannot replace', 'selection is empty') return item = index.internalPointer() good = QMessageBox.warning( self, 'sure?', 'confirm that you want to replace the content of selected item "%s". This operation can not be undone.' % item.name(), QMessageBox.Ok | QMessageBox.Cancel) == QMessageBox.Ok if (not good): return try: item.setContent(hpaste.nodesToString(nodes)) except CollectionSyncError as e: QMessageBox.critical(self, 'something went wrong!', 'Server error occured: %s' % e.message) def _itemInfo(self, index): item = index.internalPointer() accesstext = 'public' if item.access( ) == CollectionItem.AccessType.public else 'private' readonlytext = 'readonly' if item.readonly() else 'editable' info = 'name: %s\n%s\naccess: %s\n%s\n\ncollection id: %s\n\nmetadata:\n' % ( item.name(), item.description(), accesstext, readonlytext, item.id()) info += '\n'.join(('%s : %s' % (key, item.metadata()[key]) for key in item.metadata())) QMessageBox.information(self, 'item information', info) def _renameItem(self, index): item = index.internalPointer() oldname = item.name() olddesc = item.description() newname, newdesc, good = QDoubleInputDialog.getDoubleText( self, 'modify item info', 'Enter new item name and description', 'name', 'description', oldname, olddesc) if (not good): return if (newname != oldname): item.setName(newname) if (newdesc != olddesc): item.setDescription(newdesc) def _removeIcon(self, index): ok = QMessageBox.warning( self, 'sure?', 'confirm removing Icon. This operation can not be undone.', QMessageBox.Ok | QMessageBox.Cancel) == QMessageBox.Ok if ok: super(HPasteCollectionWidget.__HPasteCollectionWidget, self)._removeIcon(index) def _confirmRemove(self, index): return QMessageBox.warning( self, 'sure?', 'confirm removing the item from collection. This operation can not be undone.', QMessageBox.Ok | QMessageBox.Cancel) == QMessageBox.Ok # a callback for authoriser def _authCallback(self, callbackinfo): auth, public, action = callbackinfo if self.__insideAuthCallback: return # prevent looping self.__insideAuthCallback = True try: if action == 0 or (action == 2 and not auth['enabled']): good = self.removeCollection(auth['user']) if not good: # means something went wrong during removal attempt - probably async collection syncing problem. Try later if public: GithubAuthorizator.setPublicCollsctionEnabled( auth['user'], True) else: GithubAuthorizator.setAuthorizationEnabled( auth['user'], True) elif action == 1 or (action == 2 and auth['enabled']): if public: self.addCollection( GithubCollection(auth['user'], public=True), async=True ) # TODO: reuse some token for public access else: self.addCollection(GithubCollection(auth['token']), async=True) except CollectionSyncError as e: QMessageBox.critical( self, 'something went wrong!', 'could not add/remove collection: %s' % e.message) finally: self.__insideAuthCallback = False
class FE14SupportEditor(QWidget, Ui_support_editor): def __init__(self): super().__init__() self.setupUi(self) self.pushButton_2.setEnabled(False) self.pushButton_3.setEnabled(False) self.comboBox.setEnabled(False) self.setWindowTitle("Support Editor") self.setWindowIcon(QIcon("paragon.ico")) self.error_dialog = None module_service = locator.get_scoped("ModuleService") self.service = None self.current_character = None self.current_supports = None self.current_support = None self.model = module_service.get_module("Characters").entries_model self.proxy_model = QSortFilterProxyModel(self) self.proxy_model.setSourceModel(self.model) self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) self.characters_list_view.setModel(self.proxy_model) self.characters_list_view.selectionModel().currentRowChanged.connect(self._update_selection) self.listWidget.selectionModel().currentRowChanged.connect(self._on_target_character_changed) self.listWidget_2.selectionModel().currentRowChanged.connect(self._update_support_selection) self.lineEdit.textChanged.connect(self._update_filter) self.pushButton_2.clicked.connect(self._on_add_support_pressed) self.pushButton_3.clicked.connect(self._on_remove_support_pressed) self.comboBox.currentIndexChanged.connect(self._on_support_type_changed) def show(self): super().show() self.service = locator.get_scoped("SupportsService") self.service.set_in_use() success = True try: self.service.check_support_id_validity() except: logging.exception("Support IDs are invalid.") self.error_dialog = ErrorDialog("Support IDs are invalid. This could mean an ID was out of bounds or not " "unique. See the log for details.") self.error_dialog.show() success = False self.setDisabled(not success) def _update_filter(self): self.proxy_model.setFilterRegExp(self.lineEdit.text()) def _update_selection(self, index: QtCore.QModelIndex): if index.isValid(): character = self.proxy_model.data(index, QtCore.Qt.UserRole) self._refresh_lists(character) self.current_character = character def _refresh_lists(self, character): self._update_supports_list(character) self._update_add_list(character) self.current_support = None self.comboBox.setEnabled(False) self.pushButton_2.setEnabled(False) self.pushButton_3.setEnabled(False) def _update_add_list(self, character): supported_characters = self._create_supported_characters_set(character) module_service = locator.get_scoped("ModuleService") self.listWidget.clear() characters = module_service.get_module("Characters").entries for target_character in characters: if target_character["PID"] not in supported_characters: model_index = self._get_model_index_of_character(target_character) display_name = self.model.data(model_index, QtCore.Qt.DisplayRole) item = QListWidgetItem(display_name) item.setData(QtCore.Qt.UserRole, target_character) self.listWidget.addItem(item) # Dict is not hashable. PIDs should be unique, so we'll use those instead. # Might be able to use IDs instead. def _create_supported_characters_set(self, character): supports = self.service.get_supports_for_character(character) result = set() for support in supports: result.add(support.character["PID"]) return result def _update_supports_list(self, character): supports = self.service.get_supports_for_character(character) self.listWidget_2.clear() for support in supports: model_index = self._get_model_index_of_character(support.character) display_name = self.model.data(model_index, QtCore.Qt.DisplayRole) item = QListWidgetItem(display_name) item.setData(QtCore.Qt.UserRole, support) self.listWidget_2.addItem(item) self.current_supports = supports def _get_model_index_of_character(self, character): module_service = locator.get_scoped("ModuleService") entries = module_service.get_module("Characters").entries for i in range(0, len(entries)): if entries[i] == character: return self.model.index(i) return QModelIndex() def _update_support_selection(self, index): if not index.isValid() or not self.current_supports: return self.current_support = self.current_supports[index.row()] index = SUPPORT_TYPE_TO_INDEX[self.current_support.support_type] self.comboBox.setCurrentIndex(index) self.comboBox.setEnabled(True) self.pushButton_3.setEnabled(True) def _on_support_type_changed(self, index): if not self.current_character or not self.current_support: return support_type = INDEX_TO_SUPPORT_TYPE[index] self.service.set_support_type(self.current_character, self.current_support, support_type) def _on_target_character_changed(self): self.pushButton_2.setEnabled(self.listWidget.currentIndex().isValid()) def _on_add_support_pressed(self): if not self.current_character or not self.listWidget.currentIndex().isValid(): return other_character = self.listWidget.currentItem().data(QtCore.Qt.UserRole) support_type = INDEX_TO_SUPPORT_TYPE[0] # Default to romantic. self.service.add_support_between_characters(self.current_character, other_character, support_type) self._refresh_lists(self.current_character) def _on_remove_support_pressed(self): if not self.current_character or not self.current_support: return self.service.remove_support(self.current_character, self.current_support) self._refresh_lists(self.current_character)
class LayersList(QWidget): ''' LayerList class which acts as collapsable list. ''' def __init__(self, name, layers, filter, expand=True): super().__init__() self.setWindowModality(QtCore.Qt.WindowModal) self.currently_expanded = True self.main_layout = QVBoxLayout() self.main_layout.setMargin(0) self.main_layout.setSpacing(0) self.main_layout.setContentsMargins(0, 0, 0, 0) self.expand_button = QPushButton(name) self.expand_button.setToolTip(f"List of {name} Layers") self.expand_button.setIcon( QIcon(os.path.join(PATH, 'LayersList_Down.png'))) self.layer_list = QListView() self.layer_list.setDragEnabled(True) self.layer_list.setEditTriggers(QAbstractItemView.NoEditTriggers) self.layer_list.setWrapping(False) self.layer_list.setViewMode(self.layer_list.ListMode) self.container_model = QStandardItemModel() self.model = QSortFilterProxyModel() self.model.setSourceModel(self.container_model) self.model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) #self.model.cas filter.textChanged.connect(self.filter_model) for l in layers: self.container_model.appendRow( QStandardItem( QIcon(os.path.join(PATH, 'LayersList_Layer_Icon.png')), l)) self.layer_list.setModel(self.model) self.main_layout.addWidget(self.expand_button, 0, Qt.AlignTop) self.main_layout.addWidget(self.layer_list, 0, Qt.AlignTop) self.expand_button.clicked.connect(self.expand) self.setLayout(self.main_layout) self.resized_size = len(layers) * (self.layer_list.sizeHintForRow(0) + self.layer_list.frameWidth()) self.layer_list.setMaximumHeight(self.resized_size) self.layer_list.setMinimumHeight(self.resized_size) self.setMinimumWidth(self.layer_list.frameWidth()) self.set_styling() if not expand: self.expand() @QtCore.Slot() def expand(self): if self.currently_expanded: self.layer_list.setMinimumHeight(0) self.currently_expanded = False self.expand_button.setIcon( QIcon(os.path.join(PATH, 'LayersList_Up2.png'))) self.layer_list.setMaximumHeight(0) else: self.layer_list.setMinimumHeight(self.resized_size) self.currently_expanded = True self.expand_button.setIcon( QIcon(os.path.join(PATH, 'LayersList_Down.png'))) self.layer_list.setMaximumHeight(self.resized_size) def set_styling(self): self.setStyleSheet(''' background-color:white; ''') self.expand_button.setStyleSheet(''' background-color:#d6d2d2; text-align:left; ''') @QtCore.Slot() def filter_model(self, text): self.show() self.model.setFilterRegExp(QRegExp(text, QtCore.Qt.CaseInsensitive)) if not self.currently_expanded: self.expand() if self.model.rowCount() == 0: self.hide()
class QStringTable(QTableView): def __init__(self, parent, selection_callback=None): super(QStringTable, self).__init__(parent) self._selected = selection_callback self._filter = None self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setShowGrid(False) self.verticalHeader().setVisible(False) self.verticalHeader().setDefaultSectionSize(24) self.setHorizontalScrollMode(self.ScrollPerPixel) self._model = QStringModel(None) self._proxy = QSortFilterProxyModel(self) self._proxy.setSourceModel(self._model) self._proxy.setFilterCaseSensitivity(Qt.CaseInsensitive) self.setModel(self._proxy) self.setSortingEnabled(True) self.setSelectionMode(QAbstractItemView.SingleSelection) # let the last column (string) fill table width self.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed) self.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch) self.doubleClicked.connect(self._on_string_selected) # # Properties # @property def cfg(self): return self._model.cfg @cfg.setter def cfg(self, v): self._model.cfg = v self.fast_resize() @property def xrefs(self): return self._model.xrefs @xrefs.setter def xrefs(self, v): self._model.xrefs = v @property def function(self): return self._model.function @function.setter def function(self, v): self._model.function = v self.fast_resize() @property def filter_string(self): return self._filter @filter_string.setter def filter_string(self, v): self._filter = v if isinstance(v, re.Pattern): self._proxy.setFilterRegExp(self._filter.pattern) else: self._proxy.setFilterWildcard(self._filter) self._proxy.setFilterKeyColumn(2) # # Public methods # def fast_resize(self): self.setVisible(False) self.resizeColumnsToContents() self.setVisible(True) # # Event handlers # def _on_string_selected(self, model_index): model_index = self._proxy.mapToSource(model_index) selected_index = model_index.row() if self._model is None: return if 0 <= selected_index < len(self._model.values): selected_item = self._model.values[selected_index] else: selected_item = None if self._selected is not None: self._selected(selected_item)
class FE14ChapterEditor(Ui_FE14ChapterEditor): def __init__(self): super().__init__() self.setWindowTitle("Chapter Editor") self.setWindowIcon(QIcon("paragon.ico")) self.error_dialog = None module_service = locator.get_scoped("ModuleService") self.chapter_module = module_service.get_module("Chapters") self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(self.chapter_module.entries_model) self.proxy_model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) self.chapter_list_view.setModel(self.proxy_model) self.chapter_search_bar.textChanged.connect(self._update_filter) self.chapter_list_view.selectionModel().currentRowChanged.connect( self._update_selection) self.add_chapter_action.triggered.connect( self._on_add_chapter_triggered) self.hide_selector_action.triggered.connect( self._on_toggle_selector_triggered) self._update_selection(QModelIndex()) def _update_filter(self): self.proxy_model.setFilterRegExp(self.search_field.text()) def _update_selection(self, index): service = locator.get_scoped("ChapterService") data = self.proxy_model.data(index, QtCore.Qt.UserRole) chapter_data: Optional[ ChapterData] = service.get_chapter_data_from_chapter( data) if data else None person_module = chapter_data.person if chapter_data else None message_archive = chapter_data.conversation_data if chapter_data else None self.config_tab.update_chapter_data(chapter_data) self.map_tab.update_chapter_data(chapter_data) self.characters_tab.set_module(person_module) self.conversation_tab.set_archive(message_archive) def _on_add_chapter_triggered(self): # Get the chapter to use as a base choices = self._create_chapter_choice_list() (choice, ok) = QInputDialog.getItem(self, "Select Base Chapter", "Base Chapter", choices) if not ok: return source_chapter = self._get_chapter_from_choice(choice, choices) # Get the desired CID. (desired_cid, ok) = QInputDialog.getText(self, "Enter a CID for the new chapter.", "CID") if not ok: return # Validate the CID. service = locator.get_scoped("ChapterService") if service.is_cid_in_use(desired_cid): self.error_dialog = ErrorDialog("The CID \"" + desired_cid + "\" is already in use.") self.error_dialog.show() return if not desired_cid.startswith("CID_"): self.error_dialog = ErrorDialog("CID must start with the \"CID_\"") self.error_dialog.show() return # Create the chapter service.create_chapter(source_chapter, desired_cid) def _create_chapter_choice_list(self): choices = [] for i in range(0, len(self.chapter_module.entries)): chapter = self.chapter_module.entries[i] cid = chapter["CID"].value choices.append(str(i) + ". " + cid) return choices def _get_chapter_from_choice(self, choice, choices_list): for i in range(0, len(choices_list)): if choice == choices_list[i]: return self.chapter_module.entries[i] raise ValueError def _on_toggle_selector_triggered(self): self.selector_widget.setVisible(not self.selector_widget.isVisible()) self.visual_splitter.setVisible(not self.visual_splitter.isVisible())
class InstallPluginDialog(QDialog): item_selected = Signal(str) def __init__(self, parent): """Initialize class""" super().__init__(parent) self.setWindowTitle('Install plugin') QVBoxLayout(self) self._line_edit = QLineEdit(self) self._line_edit.setPlaceholderText("Search registry...") self._list_view = QListView(self) self._model = QSortFilterProxyModel(self) self._source_model = _InstallPluginModel(self) self._model.setSourceModel(self._source_model) self._model.setFilterCaseSensitivity(Qt.CaseInsensitive) self._list_view.setModel(self._model) self._timer = QTimer(self) self._timer.setInterval(200) self._button_box = QDialogButtonBox(self) self._button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) self._button_box.button(QDialogButtonBox.Ok).setEnabled(False) self.layout().addWidget(self._line_edit) self.layout().addWidget(self._list_view) self.layout().addWidget(self._button_box) self.setAttribute(Qt.WA_DeleteOnClose) self.setMinimumWidth(400) self._button_box.button(QDialogButtonBox.Cancel).clicked.connect( self.close) self._button_box.button(QDialogButtonBox.Ok).clicked.connect( self._handle_ok_clicked) self._list_view.doubleClicked.connect(self._emit_item_selected) self._list_view.selectionModel().selectionChanged.connect( self._update_ok_button_enabled) self._line_edit.textEdited.connect(self._handle_search_text_changed) self._timer.timeout.connect(self._filter_model) def populate_list(self, names): for name in names: self._source_model.appendRow(QStandardItem(name)) @Slot(str) def _handle_search_text_changed(self, _text): self._timer.start() def _filter_model(self): self._model.setFilterRegExp(self._line_edit.text()) @Slot(bool) def _handle_ok_clicked(self, _=False): index = self._list_view.currentIndex() self._emit_item_selected(index) @Slot("QModelIndex") def _emit_item_selected(self, index): if not index.isValid(): return self.item_selected.emit(index.data(Qt.DisplayRole)) self.close() @Slot("QItemSelection", "QItemSelection") def _update_ok_button_enabled(self, _selected, _deselected): on = self._list_view.selectionModel().hasSelection() self._button_box.button(QDialogButtonBox.Ok).setEnabled(on)