class SqlRelationalTableModel(QtSql.QSqlRelationalTableModel): def __init__(self, model, parent, db): super().__init__(parent, db) self.model = model self._proxyModel = QSortFilterProxyModel(self) self._proxyModel.setSortLocaleAware(True) self._proxyModel.setSourceModel(self) def relationModel(self, _column): return self.model def data(self, index, role=Qt.DisplayRole): if role == Qt.DecorationRole: if index.row() < 0: return None iconIndex = self.index(index.row(), self.fieldIndex('icon')) if not self.data(iconIndex) or self.data(iconIndex).isNull(): return None icon = QPixmap() icon.loadFromData(self.data(iconIndex)) return icon return super().data(index, role) def proxyModel(self): return self._proxyModel def sort(self, sort=True): if sort: self._proxyModel.sort(self.fieldIndex('value')) else: self._proxyModel.sort(-1)
class UserAgentsDialog(QDialog, Ui_UserAgentsDialog): """ Class implementing a dialog to show all saved user agent settings. """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(UserAgentsDialog, self).__init__(parent) self.setupUi(self) self.removeButton.clicked.connect( self.userAgentsTable.removeSelected) self.removeAllButton.clicked.connect( self.userAgentsTable.removeAll) self.userAgentsTable.verticalHeader().hide() self.__userAgentModel = UserAgentModel( Helpviewer.HelpWindow.HelpWindow.userAgentsManager(), self) self.__proxyModel = QSortFilterProxyModel(self) self.__proxyModel.setSourceModel(self.__userAgentModel) self.searchEdit.textChanged.connect( self.__proxyModel.setFilterFixedString) self.userAgentsTable.setModel(self.__proxyModel) fm = QFontMetrics(QFont()) height = fm.height() + fm.height() // 3 self.userAgentsTable.verticalHeader().setDefaultSectionSize(height) self.userAgentsTable.verticalHeader().setMinimumSectionSize(-1) self.userAgentsTable.resizeColumnsToContents() self.userAgentsTable.horizontalHeader().setStretchLastSection(True)
def __init__(self, parent=None): super().__init__(parent) self._model = LogRecordModel() filter_model = QSortFilterProxyModel() filter_model.setSourceModel(self._model) filter_model.setFilterKeyColumn(3) self.msg_filter = QLineEdit() self.log_view = QTableView() self.log_view.setModel(filter_model) header = self.log_view.horizontalHeader() #header.setSectionResizeMode(header.Stretch) header.setStretchLastSection(True) self.status_label = QLabel() # Connect signals: self.msg_filter.textChanged.connect(filter_model.setFilterFixedString) # Make nice layout: layout = QVBoxLayout(self) layout.addWidget(self.msg_filter) layout.addWidget(self.log_view) layout.addWidget(self.status_label) # Attach udp server: self._udpServer = UdpHandler(self._model) self._model.stats_changed.connect(self.status_label.setText)
def setSourceModel(self, model): QSortFilterProxyModel.setSourceModel(self, model) self.sourceModel().dataChanged.connect(self.mapModelMaybe) self.sourceModel().rowsInserted.connect(self.mapModel) self.sourceModel().rowsRemoved.connect(self.mapModel) self.sourceModel().rowsMoved.connect(self.mapModel) self.mapModel()
def __init__(self, core, parent=None): super().__init__(parent) uic.loadUi(resource_path('data/ObjectsTab.ui'), self) self._core = core model = models.ObjectModel(self._core.inv) proxyModel = QSortFilterProxyModel() proxyModel.setSourceModel(model) self.tableView.setModel(proxyModel)
def __init__(self, settings, directory, check_id_fct, annotations_path, parent=None): super().__init__(parent) # FIXME Delayed refactoring of check_id_fct and annotations_path. # Variables section. library_id = settings["libraryID"] library_type = settings["libraryType"] api_key = settings["apiKey"] self._zotero = ZoteroWrap(library_id, library_type, api_key, directory) # Widgets section. model = ZoteroTableModel(self._zotero, check_id_fct, annotations_path) model.load() proxy_model = QSortFilterProxyModel() proxy_model.setSourceModel(model) proxy_model.setDynamicSortFilter(True) proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) proxy_model.setFilterKeyColumn(-1) # NB: All columns. self.view = QTableView(self) self.view.setModel(proxy_model) self.view.setCornerButtonEnabled(False) self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.view.setSelectionBehavior(QAbstractItemView.SelectRows) self.view.setSelectionMode(QAbstractItemView.SingleSelection) # NB: Triggers a call to sortByColumn() which sorts by the first column. self.view.setSortingEnabled(True) self.view.setWordWrap(False) self.view.verticalHeader().hide() self.filter_edit = FilterEdit(self.view) # NB: The thread does not begin executing until start() is called. self.refresh_thread = ZoteroRefreshThread(model, self) # Layouts section. header_layout = QFormLayout() header_layout.addRow("Filter:", self.filter_edit) header_layout.setFieldGrowthPolicy(QFormLayout.AllNonFixedFieldsGrow) utils.configure_form_layout(header_layout) main_layout = QVBoxLayout() main_layout.addLayout(header_layout) main_layout.addWidget(self.view) self.setLayout(main_layout) # Signals section. self.filter_edit.textChanged.connect(proxy_model.setFilterFixedString) self.refresh_thread.started.connect(self.refresh_started) self.refresh_thread.finished.connect(self.refresh_finished)
def createModel(parent,rows,cols,colNames): model=QStandardItemModel(rows, cols+1, parent) i=0 for c in colNames: model.setHeaderData(i, Qt.Horizontal, c) i+=1 proxyModel=QSortFilterProxyModel() proxyModel.setDynamicSortFilter(True) proxyModel.setSourceModel(model) proxyModel.setFilterKeyColumn(0) return proxyModel
def initUI(self, db): equipment = EquipmentModel(db) self.setModel(equipment) self.setStates() self.setSelectionMode(QAbstractItemView.MultiSelection) self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setSizes() proxy = QSortFilterProxyModel() self.setModel(proxy) proxy.setSourceModel(equipment)
class ZoomValuesDialog(QDialog, Ui_ZoomValuesDialog): """ Class implementing a dialog to show all saved zoom values. """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(ZoomValuesDialog, self).__init__(parent) self.setupUi(self) self.removeButton.clicked.connect( self.zoomValuesTable.removeSelected) self.removeAllButton.clicked.connect(self.zoomValuesTable.removeAll) import Helpviewer.HelpWindow from .ZoomValuesModel import ZoomValuesModel self.zoomValuesTable.verticalHeader().hide() self.__zoomValuesModel = ZoomValuesModel( Helpviewer.HelpWindow.HelpWindow.zoomManager(), self) self.__proxyModel = QSortFilterProxyModel(self) self.__proxyModel.setSourceModel(self.__zoomValuesModel) self.searchEdit.textChanged.connect( self.__proxyModel.setFilterFixedString) self.zoomValuesTable.setModel(self.__proxyModel) fm = QFontMetrics(QFont()) height = fm.height() + fm.height() // 3 self.zoomValuesTable.verticalHeader().setDefaultSectionSize(height) self.zoomValuesTable.verticalHeader().setMinimumSectionSize(-1) self.__calculateHeaderSizes() def __calculateHeaderSizes(self): """ Private method to calculate the section sizes of the horizontal header. """ fm = QFontMetrics(QFont()) for section in range(self.__zoomValuesModel.columnCount()): header = self.zoomValuesTable.horizontalHeader()\ .sectionSizeHint(section) if section == 0: header = fm.width("extraveryveryverylongsitename") elif section == 1: header = fm.width("averagelongzoomvalue") buffer = fm.width("mm") header += buffer self.zoomValuesTable.horizontalHeader()\ .resizeSection(section, header) self.zoomValuesTable.horizontalHeader().setStretchLastSection(True)
class ClickToFlashWhitelistDialog(QDialog, Ui_ClickToFlashWhitelistDialog): """ Class implementing a dialog to manage the ClickToFlash whitelist. """ def __init__(self, whitelist, parent=None): """ Constructor @param whitelist list of whitelisted hosts (list of string) @param parent reference to the parent widget (QWidget) """ super(ClickToFlashWhitelistDialog, self).__init__(parent) self.setupUi(self) self.iconLabel.setPixmap(UI.PixmapCache.getPixmap("flashBlock48.png")) self.__model = QStringListModel(whitelist[:], self) self.__model.sort(0) self.__proxyModel = QSortFilterProxyModel(self) self.__proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.__proxyModel.setSourceModel(self.__model) self.whitelist.setModel(self.__proxyModel) self.searchEdit.textChanged.connect( self.__proxyModel.setFilterFixedString) self.removeButton.clicked.connect(self.whitelist.removeSelected) self.removeAllButton.clicked.connect(self.whitelist.removeAll) @pyqtSlot() def on_addButton_clicked(self): """ Private slot to add an entry to the whitelist. """ host, ok = QInputDialog.getText( self, self.tr("ClickToFlash Whitelist"), self.tr("Enter host name to add to whitelist:"), QLineEdit.Normal) if ok and host != "" and host not in self.__model.stringList(): self.__model.insertRow(self.__model.rowCount()) self.__model.setData( self.__model.index(self.__model.rowCount() - 1), host) self.__model.sort(0) def getWhitelist(self): """ Public method to get the whitelisted hosts. @return list of whitelisted hosts (list of string) """ return self.__model.stringList()
class E5ErrorMessageFilterDialog(QDialog, Ui_E5ErrorMessageFilterDialog): """ Class implementing a dialog to manage the list of messages to be ignored. """ def __init__(self, messageFilters, parent=None): """ Constructor @param messageFilters list of message filters to be edited (list of strings) @param parent reference to the parent widget (QWidget) """ super(E5ErrorMessageFilterDialog, self).__init__(parent) self.setupUi(self) self.__model = QStringListModel(messageFilters, self) self.__model.sort(0) self.__proxyModel = QSortFilterProxyModel(self) self.__proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.__proxyModel.setSourceModel(self.__model) self.filterList.setModel(self.__proxyModel) self.searchEdit.textChanged.connect( self.__proxyModel.setFilterFixedString) self.removeButton.clicked.connect(self.filterList.removeSelected) self.removeAllButton.clicked.connect(self.filterList.removeAll) @pyqtSlot() def on_addButton_clicked(self): """ Private slot to add an entry to the list. """ filter, ok = QInputDialog.getText( self, self.tr("Error Messages Filter"), self.tr("Enter message filter to add to the list:"), QLineEdit.Normal) if ok and filter != "" and filter not in self.__model.stringList(): self.__model.insertRow(self.__model.rowCount()) self.__model.setData( self.__model.index(self.__model.rowCount() - 1), filter) self.__model.sort(0) def getFilters(self): """ Public method to get the list of message filters. @return error message filters (list of strings) """ return self.__model.stringList()[:]
class NoCacheHostsDialog(QDialog, Ui_NoCacheHostsDialog): """ Class implementing a dialog to manage the list of hosts not to be cached. """ def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(NoCacheHostsDialog, self).__init__(parent) self.setupUi(self) self.__model = QStringListModel( Preferences.getHelp("NoCacheHosts"), self) self.__model.sort(0) self.__proxyModel = QSortFilterProxyModel(self) self.__proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.__proxyModel.setSourceModel(self.__model) self.noCacheList.setModel(self.__proxyModel) self.searchEdit.textChanged.connect( self.__proxyModel.setFilterFixedString) self.removeButton.clicked.connect(self.noCacheList.removeSelected) self.removeAllButton.clicked.connect(self.noCacheList.removeAll) @pyqtSlot() def on_addButton_clicked(self): """ Private slot to add an entry to the list. """ host, ok = QInputDialog.getText( self, self.tr("Not Cached Hosts"), self.tr("Enter host name to add to the list:"), QLineEdit.Normal) if ok and host != "" and host not in self.__model.stringList(): self.__model.insertRow(self.__model.rowCount()) self.__model.setData( self.__model.index(self.__model.rowCount() - 1), host) self.__model.sort(0) def accept(self): """ Public method to accept the dialog data. """ Preferences.setHelp("NoCacheHosts", self.__model.stringList()) super(NoCacheHostsDialog, self).accept()
class SpellingDictionaryEditDialog(QDialog, Ui_SpellingDictionaryEditDialog): """ Class implementing a dialog to edit the various spell checking dictionaries. """ def __init__(self, data, info, parent=None): """ Constructor @param data contents to be edited (string) @param info info string to show at the header (string) @param parent reference to the parent widget (QWidget) """ super(SpellingDictionaryEditDialog, self).__init__(parent) self.setupUi(self) self.infoLabel.setText(info) self.__model = QStringListModel(data.splitlines(), self) self.__model.sort(0) self.__proxyModel = QSortFilterProxyModel(self) self.__proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.__proxyModel.setDynamicSortFilter(True) self.__proxyModel.setSourceModel(self.__model) self.wordList.setModel(self.__proxyModel) self.searchEdit.textChanged.connect( self.__proxyModel.setFilterFixedString) self.removeButton.clicked.connect(self.wordList.removeSelected) self.removeAllButton.clicked.connect(self.wordList.removeAll) @pyqtSlot() def on_addButton_clicked(self): """ Private slot to handle adding an entry. """ self.__model.insertRow(self.__model.rowCount()) self.wordList.edit( self.__proxyModel.index(self.__model.rowCount() - 1, 0)) def getData(self): """ Public method to get the data. @return data of the dialog (string) """ return os.linesep.join( [line for line in self.__model.stringList() if line])
def filterOnFocus(window, focused): loclist = getattr(window, 'build_loclist', None) if not loclist: return model = loclist.model() if not getattr(model, 'isFilterOnFocus', False): orig = model model = QSortFilterProxyModel() model.isFilterOnFocus = True loclist.setModel(model) model.setSourceModel(orig) model.setFilterRole(AbsolutePathRole) model.setFilterRegExp(QRegExp.escape(focused.path or ''))
class CompletingComboBox(QComboBox): """An editable combo box that filters and autocompletes.""" def __init__(self, parent=None): super().__init__(parent) self.setEditable(True) self.filter = QSortFilterProxyModel(self) self.filter.setFilterCaseSensitivity(Qt.CaseInsensitive) self.filter.setSourceModel(self.model()) self.completer = QCompleter(self.filter, self) self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) self.setCompleter(self.completer) self.lineEdit().textEdited.connect( self.filter.setFilterFixedString) self.currentIndexChanged.connect(self._index_changed) def _index_changed(self, index): self.lineEdit().selectAll()
class TagChooser(QListView): changed = Signal() def __init__(self, db): super(TagChooser,self).__init__() self.db = db self.filter = u'' self.data = QStandardItemModel() self.proxy = QSortFilterProxyModel() self.proxy.setSourceModel(self.data) self.setModel(self.proxy) self.data.itemChanged.connect(self.changed) for t in sorted(self.db.list_tags()): item = QStandardItem(t) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setCheckState(Qt.Unchecked) self.data.appendRow(item) def setTags(self, tags): for i in range(self.data.rowCount()): item = self.data.item(i) if item.text() in tags: item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) def selectedTags(self): tags = [] for i in range(self.data.rowCount()): item = self.data.item(i) if item.checkState() == Qt.Checked: tags.append(item.text()) return tags def matchingFiles(self): tags = self.selectedTags() if not tags: return [] res = list(self.db.find_files_by_tags(tags)) res.sort() return res
def __init__(self, parent=None): QWidget.__init__(self, parent) self.setupUi(self) self.toolBox.setStyleSheet(style.toolBoxSS()) self.mw = mainWindow() self.txtGeneralSplitScenes.setStyleSheet(style.lineEditSS()) # TreeView to select parent # We use a proxy to display only folders proxy = QSortFilterProxyModel() proxy.setFilterKeyColumn(Outline.type) proxy.setFilterFixedString("folder") proxy.setSourceModel(self.mw.mdlOutline) self.treeGeneralParent.setModel(proxy) for i in range(1, self.mw.mdlOutline.columnCount()): self.treeGeneralParent.hideColumn(i) self.treeGeneralParent.setCurrentIndex(self.getParentIndex()) self.chkGeneralParent.toggled.connect(self.treeGeneralParent.setVisible) self.treeGeneralParent.hide()
def main(): app = QGuiApplication(sys.argv) app.setApplicationName('InfiniteCopy') openDataBase() view = QQuickView() clipboardItemModel = ClipboardItemModel() clipboardItemModel.create() filterProxyModel = QSortFilterProxyModel() filterProxyModel.setSourceModel(clipboardItemModel) clipboard = Clipboard() clipboard.setFormats([ mimeText, mimeHtml, mimePng, mimeSvg ]) clipboard.changed.connect(clipboardItemModel.addItem) engine = view.engine() imageProvider = ClipboardItemModelImageProvider(clipboardItemModel) engine.addImageProvider("items", imageProvider) context = view.rootContext() context.setContextProperty('clipboardItemModel', clipboardItemModel) context.setContextProperty('clipboardItemModelFilterProxy', filterProxyModel) context.setContextProperty('clipboard', clipboard) view.setSource(QUrl.fromLocalFile('qml/MainWindow.qml')) view.setGeometry(100, 100, 400, 240) view.show() engine.quit.connect(QGuiApplication.quit) return app.exec_()
class CustomQCompleter(QCompleter): """ adapted from: http://stackoverflow.com/a/7767999/2156909 """ def __init__(self, parent=None): super().__init__(parent) self.local_completion_prefix = "" self.source_model = None self.filterProxyModel = QSortFilterProxyModel(self) self.usingOriginalModel = False def setModel(self, strList): self.source_model = QStringListModel(strList) self.filterProxyModel = QSortFilterProxyModel(self) self.filterProxyModel.setSourceModel(self.source_model) super().setModel(self.filterProxyModel) self.usingOriginalModel = True def updateModel(self): if not self.usingOriginalModel: self.filterProxyModel.setSourceModel(self.source_model) pattern = QRegExp(self.local_completion_prefix, Qt.CaseInsensitive, QRegExp.FixedString) self.filterProxyModel.setFilterRegExp(pattern) def splitPath(self, path): self.local_completion_prefix = path self.updateModel() if self.filterProxyModel.rowCount() == 0: self.usingOriginalModel = False self.filterProxyModel.setSourceModel(QStringListModel([path])) return [path] self.usingOriginalModel = True return []
class VistaListaClienti(QWidget): def __init__(self, parent=None): super(VistaListaClienti, self).__init__(parent) self.setWindowIcon(QtGui.QIcon('logos/logo.png')) self.controller = ControlloreListaClienti() main_layout = QHBoxLayout() v_layout = QVBoxLayout() self.list_view = QListView() self.update_ui() #Crea un elenco fittizio sopra l'elenco reale per poter usare la barra di ricerca self.filter_proxy_model = QSortFilterProxyModel() self.filter_proxy_model.setSourceModel(self.listview_model) self.filter_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.filter_proxy_model.setFilterKeyColumn(0) search_field = QLineEdit() search_field.setStyleSheet('font-size: 15px; height: 30px;') search_field.textChanged.connect( self.filter_proxy_model.setFilterRegExp) v_layout.addWidget(search_field) self.list_view.setModel(self.filter_proxy_model) v_layout.addWidget(self.list_view) main_layout.addLayout(v_layout) #Bottone per aprire un cliente buttons_layout = QVBoxLayout() open_button = QPushButton("Apri") open_button.clicked.connect(self.show_selected_info) buttons_layout.addWidget(open_button) #Bottone per creare un nuovo cliente new_button = QPushButton("Nuovo") new_button.clicked.connect(self.show_new_cliente) buttons_layout.addWidget(new_button) buttons_layout.addStretch() main_layout.addLayout(buttons_layout) self.setLayout(main_layout) self.resize(600, 300) self.setWindowTitle("Lista Clienti") #Metodo che mostra a schermo le informazioni del cliente selezionato def show_selected_info(self): try: sourceindex = self.list_view.selectedIndexes()[0].row() cliente_selezionato = self.controller.get_cliente_by_index( sourceindex) self.vista_cliente = VistaCliente( cliente_selezionato, self.controller.rimuovi_cliente_by_id, self.update_ui) self.vista_cliente.show() except IndexError: QMessageBox.critical(self, 'Errore', 'Per favore, seleziona un cliente', QMessageBox.Ok, QMessageBox.Ok) #Metodo aprire la vista di inserimento del nuovo cliente def show_new_cliente(self): self.vista_inserisci_cliente = VistaInserisciCliente( self.controller, self.update_ui) self.vista_inserisci_cliente.show() #Metodo che serve per generare e/o aggiornare la vista def update_ui(self): self.listview_model = QStandardItemModel(self.list_view) for cliente in self.controller.get_lista_dei_clienti(): item = QStandardItem() item.setText(cliente.nome + " " + cliente.cognome) item.setEditable(False) font = item.font() font.setPointSize(18) item.setFont(font) self.listview_model.appendRow(item) self.list_view.setModel(self.listview_model) # salva i dati sul file pickle alla chiusura della view def closeEvent(self, event): self.controller.save_data() #Metodo per collegare l'indice selezionato all'elenco fittizio all'indice dell'elenco reale def toSourceIndex(self, index): return self.filter_proxy_model.mapToSource(index).row()
def populate_table(self, table: QTableView, data=[]): model = TableModel(data) sortable_model = QSortFilterProxyModel(model) sortable_model.setSourceModel(model) table.setModel(sortable_model)
class DevicesListWidget(QWidget): def __init__(self, parent, *args, **kwargs): super(DevicesListWidget, self).__init__(*args, **kwargs) self.setWindowTitle("Devices list") self.setWindowState(Qt.WindowMaximized) self.setLayout(VLayout(margin=0, spacing=0)) self.mqtt = parent.mqtt self.mdi = parent.mdi self.idx = None self.settings = QSettings() self.hidden_columns = self.settings.value("hidden_columns", [1, 2]) self.tb = Toolbar(Qt.Horizontal, 16, Qt.ToolButtonTextBesideIcon) self.tb.addAction(QIcon("GUI/icons/add.png"), "Add", self.device_add) self.layout().addWidget(self.tb) self.device_list = TableView() self.model = parent.device_model self.telemetry_model = parent.telemetry_model self.sorted_device_model = QSortFilterProxyModel() self.sorted_device_model.setSourceModel(parent.device_model) self.device_list.setModel(self.sorted_device_model) self.device_list.setupColumns(columns, self.hidden_columns) self.device_list.setSortingEnabled(True) self.device_list.setWordWrap(True) self.device_list.setItemDelegate(DeviceDelegate()) self.device_list.sortByColumn(DevMdl.TOPIC, Qt.AscendingOrder) self.device_list.setContextMenuPolicy(Qt.CustomContextMenu) self.layout().addWidget(self.device_list) self.device_list.clicked.connect(self.select_device) self.device_list.doubleClicked.connect(self.device_config) self.device_list.customContextMenuRequested.connect(self.show_list_ctx_menu) self.device_list.horizontalHeader().setContextMenuPolicy(Qt.CustomContextMenu) self.device_list.horizontalHeader().customContextMenuRequested.connect(self.show_header_ctx_menu) self.ctx_menu = QMenu() self.ctx_menu_relays = None self.create_actions() self.build_header_ctx_menu() def create_actions(self): self.ctx_menu.addAction(QIcon("GUI/icons/configure.png"), "Configure", self.device_config) self.ctx_menu.addAction(QIcon("GUI/icons/delete.png"), "Remove", self.device_delete) self.ctx_menu.addSeparator() self.ctx_menu.addAction(QIcon("GUI/icons/refresh.png"), "Refresh", self.ctx_menu_refresh) self.ctx_menu.addSeparator() self.ctx_menu.addAction(QIcon("GUI/icons/on.png"), "Power ON", lambda: self.ctx_menu_power(state="ON")) self.ctx_menu.addAction(QIcon("GUI/icons/off.png"), "Power OFF", lambda: self.ctx_menu_power(state="OFF")) self.ctx_menu_relays = QMenu("Relays") self.ctx_menu_relays.setIcon(QIcon("GUI/icons/switch.png")) relays_btn = self.ctx_menu.addMenu(self.ctx_menu_relays) self.ctx_menu_relays.setEnabled(False) self.ctx_menu.addSeparator() self.ctx_menu.addAction(QIcon("GUI/icons/clear.png"), "Clear retained", self.ctx_menu_clean_retained) self.ctx_menu.addSeparator() self.ctx_menu_copy = QMenu("Copy") self.ctx_menu_copy.setIcon(QIcon("GUI/icons/copy.png")) copy_btn = self.ctx_menu.addMenu(self.ctx_menu_copy) self.ctx_menu.addSeparator() self.ctx_menu.addAction(QIcon("GUI/icons/restart.png"), "Restart", self.ctx_menu_restart) self.ctx_menu.addAction(QIcon("GUI/icons/web.png"), "Open WebUI", self.ctx_menu_webui) self.ctx_menu_copy.addAction("IP", lambda: self.ctx_menu_copy_value(DevMdl.IP)) self.ctx_menu_copy.addAction("MAC", lambda: self.ctx_menu_copy_value(DevMdl.MAC)) self.ctx_menu_copy.addAction("BSSID", lambda: self.ctx_menu_copy_value(DevMdl.BSSID)) self.ctx_menu_copy.addSeparator() self.ctx_menu_copy.addAction("Topic", lambda: self.ctx_menu_copy_value(DevMdl.TOPIC)) self.ctx_menu_copy.addAction("FullTopic", lambda: self.ctx_menu_copy_value(DevMdl.FULL_TOPIC)) self.ctx_menu_copy.addAction("STAT topic", lambda: self.ctx_menu_copy_prefix_topic("STAT")) self.ctx_menu_copy.addAction("CMND topic", lambda: self.ctx_menu_copy_prefix_topic("CMND")) self.ctx_menu_copy.addAction("TELE topic", lambda: self.ctx_menu_copy_prefix_topic("TELE")) self.tb.addActions(self.ctx_menu.actions()) self.tb.widgetForAction(relays_btn).setPopupMode(QToolButton.InstantPopup) self.tb.widgetForAction(copy_btn).setPopupMode(QToolButton.InstantPopup) def ctx_menu_copy_value(self, column): if self.idx: row = self.idx.row() value = self.model.data(self.model.index(row, column)) QApplication.clipboard().setText(value) def ctx_menu_copy_prefix_topic(self, prefix): if self.idx: if prefix == "STAT": topic = self.model.statTopic(self.idx) elif prefix == "CMND": topic = self.model.commandTopic(self.idx) elif prefix == "TELE": topic = self.model.teleTopic(self.idx) QApplication.clipboard().setText(topic) def ctx_menu_clean_retained(self): if self.idx: relays = self.model.data(self.model.index(self.idx.row(), DevMdl.POWER)) if relays and len(relays.keys()>1): cmnd_topic = self.model.cmndTopic(self.idx) for r in relays.keys(): self.mqtt.publish(cmnd_topic + r, retain=True) QMessageBox.information(self, "Clear retained", "Cleared reatined messages.") def ctx_menu_power(self, relay=None, state=None): if self.idx: relays = self.model.data(self.model.index(self.idx.row(), DevMdl.POWER)) cmnd_topic = self.model.commandTopic(self.idx) if relay: self.mqtt.publish(cmnd_topic+relay, payload=state) elif relays: for r in relays.keys(): self.mqtt.publish(cmnd_topic+r, payload=state) def ctx_menu_restart(self): if self.idx: self.mqtt.publish("{}/restart".format(self.model.commandTopic(self.idx)), payload="1") def ctx_menu_refresh(self): if self.idx: for q in initial_queries: self.mqtt.publish("{}/status".format(self.model.commandTopic(self.idx)), payload=q) def ctx_menu_telemetry(self): if self.idx: self.mqtt.publish("{}/status".format(self.model.commandTopic(self.idx)), payload=8) def ctx_menu_bssid(self): if self.idx: bssid = self.model.bssid(self.idx) current = self.settings.value("BSSID/{}".format(bssid), "") alias, ok = QInputDialog.getText(self, "BSSID alias", "Alias for {}. Clear to remove.".format(bssid), text=current) if ok: self.settings.setValue("BSSID/{}".format(bssid), alias) self.model.refreshBSSID() def ctx_menu_webui(self): if self.idx: QDesktopServices.openUrl(QUrl("http://{}".format(self.model.ip(self.idx)))) def show_list_ctx_menu(self, at): self.select_device(self.device_list.indexAt(at)) self.ctx_menu.popup(self.device_list.viewport().mapToGlobal(at)) def build_header_ctx_menu(self): self.hdr_ctx_menu = QMenu() for c in columns.keys(): a = self.hdr_ctx_menu.addAction(columns[c][0]) a.setData(c) a.setCheckable(True) a.setChecked(not self.device_list.isColumnHidden(c)) a.toggled.connect(self.header_ctx_menu_toggle_col) def show_header_ctx_menu(self, at): self.hdr_ctx_menu.popup(self.device_list.horizontalHeader().viewport().mapToGlobal(at)) def header_ctx_menu_toggle_col(self, state): self.device_list.setColumnHidden(self.sender().data(), not state) hidden_columns = [int(c) for c in columns.keys() if self.device_list.isColumnHidden(c)] self.settings.setValue("hidden_columns", hidden_columns) self.settings.sync() def select_device(self, idx): self.idx = self.sorted_device_model.mapToSource(idx) self.device = self.model.data(self.model.index(idx.row(), DevMdl.TOPIC)) relays = self.model.data(self.model.index(self.idx.row(), DevMdl.POWER)) if relays and len(relays.keys()) > 1: self.ctx_menu_relays.setEnabled(True) self.ctx_menu_relays.setEnabled(True) self.ctx_menu_relays.clear() for r in relays.keys(): actR = self.ctx_menu_relays.addAction("{} ON".format(r)) actR.triggered.connect(lambda st, x=r: self.ctx_menu_power(x, "ON")) actR = self.ctx_menu_relays.addAction("{} OFF".format(r)) actR.triggered.connect(lambda st, x=r: self.ctx_menu_power(x, "OFF")) self.ctx_menu_relays.addSeparator() else: self.ctx_menu_relays.setEnabled(False) self.ctx_menu_relays.clear() def device_config(self, idx=None): dev_cfg = DevicesConfigWidget(self, self.model.topic(self.idx)) self.mdi.addSubWindow(dev_cfg) dev_cfg.setWindowState(Qt.WindowMaximized) def device_add(self): rc = self.model.rowCount() self.model.insertRow(rc) dlg = DeviceEditDialog(self.model, rc) dlg.full_topic.setText("%prefix%/%topic%/") if dlg.exec_() == QDialog.Accepted: self.model.setData(self.model.index(rc, DevMdl.FRIENDLY_NAME), self.model.data(self.model.index(rc, DevMdl.TOPIC))) topic = dlg.topic.text() tele_dev = self.telemetry_model.addDevice(TasmotaDevice, topic) self.telemetry_model.devices[topic] = tele_dev else: self.model.removeRow(rc) def device_delete(self): if self.idx: topic = self.model.topic(self.idx) if QMessageBox.question(self, "Confirm", "Do you want to remove '{}' from devices list?".format(topic)) == QMessageBox.Yes: self.model.removeRows(self.idx.row(),1) tele_idx = self.telemetry_model.devices.get(topic) if tele_idx: self.telemetry_model.removeRows(tele_idx.row(),1) def closeEvent(self, event): event.ignore()
class UserQueryTab(SEToolsWidget, QScrollArea): """User browser and query tab.""" def __init__(self, parent, policy, perm_map): super(UserQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = UserQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.userquery").removeHandler(self.handler) def setupUi(self): self.load_ui("userquery.ui") # populate user list self.user_model = SEToolsListModel(self) self.user_model.item_list = sorted(self.policy.users()) self.users.setModel(self.user_model) # populate role list self.role_model = SEToolsListModel(self) self.role_model.item_list = sorted(r for r in self.policy.roles() if r != "object_r") self.roles.setModel(self.role_model) # set up results self.table_results_model = UserTableModel(self, self.policy.mls) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) # setup indications of errors on level/range self.orig_palette = self.name.palette() self.error_palette = self.name.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_name_error() if self.policy.mls: self.clear_level_error() self.clear_range_error() else: # hide level and range criteria self.level_criteria.setHidden(True) self.range_criteria.setHidden(True) # set up processing thread self.thread = QThread() self.worker = ResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.userquery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.users.doubleClicked.connect(self.get_detail) self.users.get_detail.triggered.connect(self.get_detail) self.name.textEdited.connect(self.clear_name_error) self.name.editingFinished.connect(self.set_name) self.name_regex.toggled.connect(self.set_name_regex) self.roles.selectionModel().selectionChanged.connect(self.set_roles) self.invert_roles.clicked.connect(self.invert_role_selection) self.level.textEdited.connect(self.clear_level_error) self.level.editingFinished.connect(self.set_level) self.range_.textEdited.connect(self.clear_range_error) self.range_.editingFinished.connect(self.set_range) self.buttonBox.clicked.connect(self.run) # # User browser # def get_detail(self): # .ui is set for single item selection. index = self.users.selectedIndexes()[0] item = self.user_model.data(index, Qt.UserRole) self.log.debug("Generating detail window for {0}".format(item)) user_detail(self, item) # # Name criteria # def clear_name_error(self): self.name.setToolTip("Match the user name.") self.name.setPalette(self.orig_palette) def set_name(self): try: self.query.name = self.name.text() except Exception as ex: self.log.error("User name error: {0}".format(ex)) self.name.setToolTip("Error: " + str(ex)) self.name.setPalette(self.error_palette) def set_name_regex(self, state): self.log.debug("Setting name_regex {0}".format(state)) self.query.name_regex = state self.clear_name_error() self.set_name() # # Role criteria # def set_roles(self): selected_roles = [] for index in self.roles.selectionModel().selectedIndexes(): selected_roles.append(self.role_model.data(index, Qt.UserRole)) self.query.roles = selected_roles def invert_role_selection(self): invert_list_selection(self.roles.selectionModel()) # # Default level criteria # def clear_level_error(self): self.level.setToolTip("Match the default level of the user.") self.level.setPalette(self.orig_palette) def set_level(self): try: self.query.level = self.level.text() except Exception as ex: self.log.info("Level criterion error: " + str(ex)) self.level.setToolTip("Error: " + str(ex)) self.level.setPalette(self.error_palette) # # Range criteria # def clear_range_error(self): self.range_.setToolTip("Match the default range of the user.") self.range_.setPalette(self.orig_palette) def set_range(self): try: self.query.range_ = self.range_.text() except Exception as ex: self.log.info("Range criterion error: " + str(ex)) self.range_.setToolTip("Error: " + str(ex)) self.range_.setPalette(self.error_palette) # # Results runner # def run(self, button): # right now there is only one button. self.query.roles_equal = self.roles_equal.isChecked() self.query.level_dom = self.level_dom.isChecked() self.query.level_domby = self.level_domby.isChecked() self.query.range_overlap = self.range_overlap.isChecked() self.query.range_subset = self.range_subset.isChecked() self.query.range_superset = self.range_superset.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self): # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class DevicesListWidget(QWidget): def __init__(self, parent, *args, **kwargs): super(DevicesListWidget, self).__init__(*args, **kwargs) self.setWindowTitle("Devices list") self.setWindowState(Qt.WindowMaximized) self.setLayout(VLayout(margin=0, spacing=0)) self.mqtt = parent.mqtt self.mdi = parent.mdi self.idx = None self.nam = QNetworkAccessManager() self.backup = bytes() self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat) self.hidden_columns = self.settings.value("hidden_columns", [1, 2]) self.tb = Toolbar(Qt.Horizontal, 16, Qt.ToolButtonTextBesideIcon) self.tb.addAction(QIcon("GUI/icons/add.png"), "Add", self.device_add) self.layout().addWidget(self.tb) self.device_list = TableView() self.model = parent.device_model self.telemetry_model = parent.telemetry_model self.sorted_device_model = QSortFilterProxyModel() self.sorted_device_model.setSourceModel(parent.device_model) self.device_list.setModel(self.sorted_device_model) self.device_list.setupColumns(columns, self.hidden_columns) self.device_list.setSortingEnabled(True) self.device_list.setWordWrap(True) self.device_list.setItemDelegate(DeviceDelegate()) self.device_list.sortByColumn(DevMdl.TOPIC, Qt.AscendingOrder) self.device_list.setContextMenuPolicy(Qt.CustomContextMenu) self.layout().addWidget(self.device_list) self.device_list.clicked.connect(self.select_device) self.device_list.doubleClicked.connect(self.device_config) self.device_list.customContextMenuRequested.connect( self.show_list_ctx_menu) self.device_list.horizontalHeader().setContextMenuPolicy( Qt.CustomContextMenu) self.device_list.horizontalHeader().customContextMenuRequested.connect( self.show_header_ctx_menu) self.ctx_menu = QMenu() self.ctx_menu_relays = None self.create_actions() self.build_header_ctx_menu() def create_actions(self): self.ctx_menu.addAction(QIcon("GUI/icons/configure.png"), "Configure", self.device_config) self.ctx_menu.addAction(QIcon("GUI/icons/delete.png"), "Remove", self.device_delete) self.ctx_menu.addSeparator() self.ctx_menu.addAction(QIcon("GUI/icons/refresh.png"), "Refresh", self.ctx_menu_refresh) self.ctx_menu.addSeparator() self.ctx_menu.addAction(QIcon("GUI/icons/on.png"), "Power ON", lambda: self.ctx_menu_power(state="ON")) self.ctx_menu.addAction(QIcon("GUI/icons/off.png"), "Power OFF", lambda: self.ctx_menu_power(state="OFF")) self.ctx_menu_relays = QMenu("Relays") self.ctx_menu_relays.setIcon(QIcon("GUI/icons/switch.png")) relays_btn = self.ctx_menu.addMenu(self.ctx_menu_relays) self.ctx_menu_relays.setEnabled(False) self.ctx_menu.addSeparator() self.ctx_menu.addAction(QIcon("GUI/icons/clear.png"), "Clear retained", self.ctx_menu_clean_retained) self.ctx_menu.addSeparator() self.ctx_menu_copy = QMenu("Copy") self.ctx_menu_copy.setIcon(QIcon("GUI/icons/copy.png")) copy_btn = self.ctx_menu.addMenu(self.ctx_menu_copy) self.ctx_menu.addSeparator() self.ctx_menu.addAction("Set teleperiod", self.ctx_menu_teleperiod) self.ctx_menu.addAction(QIcon("GUI/icons/restart.png"), "Restart", self.ctx_menu_restart) self.ctx_menu.addAction(QIcon("GUI/icons/web.png"), "Open WebUI", self.ctx_menu_webui) self.ctx_menu.addSeparator() self.ctx_menu_ota = QMenu("OTA upgrade") self.ctx_menu_ota.addAction("Set OTA URL", self.ctx_menu_ota_set_url) self.ctx_menu_ota.addAction("Upgrade", self.ctx_menu_ota_set_upgrade) ota_btn = self.ctx_menu.addMenu(self.ctx_menu_ota) self.ctx_menu.addAction("Config backup", self.ctx_menu_config_backup) self.ctx_menu_copy.addAction( "IP", lambda: self.ctx_menu_copy_value(DevMdl.IP)) self.ctx_menu_copy.addAction( "MAC", lambda: self.ctx_menu_copy_value(DevMdl.MAC)) self.ctx_menu_copy.addAction("BSSID", self.ctx_menu_copy_bssid) self.ctx_menu_copy.addSeparator() self.ctx_menu_copy.addAction( "Topic", lambda: self.ctx_menu_copy_value(DevMdl.TOPIC)) self.ctx_menu_copy.addAction( "FullTopic", lambda: self.ctx_menu_copy_value(DevMdl.FULL_TOPIC)) self.ctx_menu_copy.addAction( "STAT topic", lambda: self.ctx_menu_copy_prefix_topic("STAT")) self.ctx_menu_copy.addAction( "CMND topic", lambda: self.ctx_menu_copy_prefix_topic("CMND")) self.ctx_menu_copy.addAction( "TELE topic", lambda: self.ctx_menu_copy_prefix_topic("TELE")) self.tb.addActions(self.ctx_menu.actions()) self.tb.widgetForAction(ota_btn).setPopupMode(QToolButton.InstantPopup) self.tb.widgetForAction(relays_btn).setPopupMode( QToolButton.InstantPopup) self.tb.widgetForAction(copy_btn).setPopupMode( QToolButton.InstantPopup) def ctx_menu_copy_value(self, column): if self.idx: row = self.idx.row() value = self.model.data(self.model.index(row, column)) QApplication.clipboard().setText(value) def ctx_menu_copy_bssid(self): if self.idx: QApplication.clipboard().setText(self.model.bssid(self.idx)) def ctx_menu_copy_prefix_topic(self, prefix): if self.idx: if prefix == "STAT": topic = self.model.statTopic(self.idx) elif prefix == "CMND": topic = self.model.commandTopic(self.idx) elif prefix == "TELE": topic = self.model.teleTopic(self.idx) QApplication.clipboard().setText(topic) def ctx_menu_clean_retained(self): if self.idx: relays = self.model.data( self.model.index(self.idx.row(), DevMdl.POWER)) if relays and len(relays.keys()) > 0: cmnd_topic = self.model.commandTopic(self.idx) for r in relays.keys(): self.mqtt.publish(cmnd_topic + r, retain=True) QMessageBox.information(self, "Clear retained", "Cleared retained messages.") def ctx_menu_power(self, relay=None, state=None): if self.idx: relays = self.model.data( self.model.index(self.idx.row(), DevMdl.POWER)) cmnd_topic = self.model.commandTopic(self.idx) if relay: self.mqtt.publish(cmnd_topic + relay, payload=state) elif relays: for r in relays.keys(): self.mqtt.publish(cmnd_topic + r, payload=state) def ctx_menu_restart(self): if self.idx: self.mqtt.publish("{}/restart".format( self.model.commandTopic(self.idx)), payload="1") def ctx_menu_refresh(self): if self.idx: for q in initial_queries: self.mqtt.publish("{}/status".format( self.model.commandTopic(self.idx)), payload=q) def ctx_menu_teleperiod(self): if self.idx: teleperiod, ok = QInputDialog.getInt( self, "Set telemetry period", "Input 1 to reset to default\n[Min: 10, Max: 3600]", int( self.model.data( self.model.index(self.idx.row(), DevMdl.TELEPERIOD))), 1, 3600) if ok: if teleperiod != 1 and teleperiod < 10: teleperiod = 10 self.mqtt.publish("{}/teleperiod".format( self.model.commandTopic(self.idx)), payload=teleperiod) def ctx_menu_telemetry(self): if self.idx: self.mqtt.publish("{}/status".format( self.model.commandTopic(self.idx)), payload=8) def ctx_menu_webui(self): if self.idx: QDesktopServices.openUrl( QUrl("http://{}".format(self.model.ip(self.idx)))) def ctx_menu_config_backup(self): if self.idx: self.backup = bytes() ip = self.model.data(self.model.index(self.idx.row(), DevMdl.IP)) self.dl = self.nam.get( QNetworkRequest(QUrl("http://{}/dl".format(ip)))) self.dl.readyRead.connect(self.get_dump) self.dl.finished.connect(self.save_dump) def ctx_menu_ota_set_url(self): if self.idx: current_url = self.model.data( self.model.index(self.idx.row(), DevMdl.OTA_URL)) url, ok = QInputDialog.getText( self, "Set OTA URL", '100 chars max. Set to "1" to reset to default.', text=current_url) if ok: self.mqtt.publish("{}/otaurl".format( self.model.commandTopic(self.idx)), payload=url) def ctx_menu_ota_set_upgrade(self): if self.idx: current_url = self.model.data( self.model.index(self.idx.row(), DevMdl.OTA_URL)) if QMessageBox.question( self, "OTA Upgrade", "Are you sure to OTA upgrade from\n{}".format(current_url), QMessageBox.Yes | QMessageBox.No) == QMessageBox.Yes: self.model.setData( self.model.index(self.idx.row(), DevMdl.FIRMWARE), "Upgrade in progress") self.mqtt.publish("{}/upgrade".format( self.model.commandTopic(self.idx)), payload="1") def show_list_ctx_menu(self, at): self.select_device(self.device_list.indexAt(at)) self.ctx_menu.popup(self.device_list.viewport().mapToGlobal(at)) def build_header_ctx_menu(self): self.hdr_ctx_menu = QMenu() for c in columns.keys(): a = self.hdr_ctx_menu.addAction(columns[c][0]) a.setData(c) a.setCheckable(True) a.setChecked(not self.device_list.isColumnHidden(c)) a.toggled.connect(self.header_ctx_menu_toggle_col) def show_header_ctx_menu(self, at): self.hdr_ctx_menu.popup( self.device_list.horizontalHeader().viewport().mapToGlobal(at)) def header_ctx_menu_toggle_col(self, state): self.device_list.setColumnHidden(self.sender().data(), not state) hidden_columns = [ int(c) for c in columns.keys() if self.device_list.isColumnHidden(c) ] self.settings.setValue("hidden_columns", hidden_columns) self.settings.sync() def select_device(self, idx): self.idx = self.sorted_device_model.mapToSource(idx) self.device = self.model.data(self.model.index(idx.row(), DevMdl.TOPIC)) relays = self.model.data(self.model.index(self.idx.row(), DevMdl.POWER)) if relays and len(relays.keys()) > 1: self.ctx_menu_relays.setEnabled(True) self.ctx_menu_relays.setEnabled(True) self.ctx_menu_relays.clear() for r in relays.keys(): actR = self.ctx_menu_relays.addAction("{} ON".format(r)) actR.triggered.connect( lambda st, x=r: self.ctx_menu_power(x, "ON")) actR = self.ctx_menu_relays.addAction("{} OFF".format(r)) actR.triggered.connect( lambda st, x=r: self.ctx_menu_power(x, "OFF")) self.ctx_menu_relays.addSeparator() else: self.ctx_menu_relays.setEnabled(False) self.ctx_menu_relays.clear() def device_config(self, idx=None): if self.idx: dev_cfg = DevicesConfigWidget(self, self.model.topic(self.idx)) self.mdi.addSubWindow(dev_cfg) dev_cfg.setWindowState(Qt.WindowMaximized) def device_add(self): rc = self.model.rowCount() self.model.insertRow(rc) dlg = DeviceEditDialog(self.model, rc) dlg.full_topic.setText("%prefix%/%topic%/") if dlg.exec_() == QDialog.Accepted: self.model.setData( self.model.index(rc, DevMdl.FRIENDLY_NAME), self.model.data(self.model.index(rc, DevMdl.TOPIC))) topic = dlg.topic.text() tele_dev = self.telemetry_model.addDevice(TasmotaDevice, topic) self.telemetry_model.devices[topic] = tele_dev else: self.model.removeRow(rc) def device_delete(self): if self.idx: topic = self.model.topic(self.idx) if QMessageBox.question( self, "Confirm", "Do you want to remove '{}' from devices list?".format( topic)) == QMessageBox.Yes: self.model.removeRows(self.idx.row(), 1) tele_idx = self.telemetry_model.devices.get(topic) if tele_idx: self.telemetry_model.removeRows(tele_idx.row(), 1) def get_dump(self): self.backup += self.dl.readAll() def save_dump(self): fname = self.dl.header(QNetworkRequest.ContentDispositionHeader) if fname: fname = fname.split('=')[1] save_file = QFileDialog.getSaveFileName( self, "Save config backup", "{}/TDM/{}".format(QDir.homePath(), fname))[0] if save_file: with open(save_file, "wb") as f: f.write(self.backup) def closeEvent(self, event): event.ignore()
class TERuleQueryTab(AnalysisTab): """A Type Enforcement rule query.""" def __init__(self, parent, policy, perm_map): super(TERuleQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = TERuleQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.terulequery").removeHandler(self.handler) def setupUi(self): self.load_ui("apol/terulequery.ui") # set up source/target autocompletion typeattr_completion_list = [str(t) for t in self.policy.types()] typeattr_completion_list.extend( str(a) for a in self.policy.typeattributes()) typeattr_completer_model = QStringListModel(self) typeattr_completer_model.setStringList( sorted(typeattr_completion_list)) self.typeattr_completion = QCompleter() self.typeattr_completion.setModel(typeattr_completer_model) self.source.setCompleter(self.typeattr_completion) self.target.setCompleter(self.typeattr_completion) # set up default autocompletion type_completion_list = [str(t) for t in self.policy.types()] type_completer_model = QStringListModel(self) type_completer_model.setStringList(sorted(type_completion_list)) self.type_completion = QCompleter() self.type_completion.setModel(type_completer_model) self.default_type.setCompleter(self.type_completion) # setup indications of errors on source/target/default self.errors = set() self.orig_palette = self.source.palette() self.error_palette = self.source.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_source_error() self.clear_target_error() self.clear_default_error() self.clear_xperm_error() # populate class list self.class_model = SEToolsListModel(self) self.class_model.item_list = sorted(self.policy.classes()) self.tclass.setModel(self.class_model) # populate perm list self.perms_model = PermListModel(self, self.policy) self.perms.setModel(self.perms_model) # populate bool list self.bool_model = SEToolsListModel(self) self.bool_model.item_list = sorted(self.policy.bools()) self.bool_criteria.setModel(self.bool_model) # set up results self.table_results_model = TERuleTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(0, Qt.AscendingOrder) # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.terulequery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.set_source_regex(self.source_regex.isChecked()) self.set_target_regex(self.target_regex.isChecked()) self.set_default_regex(self.default_regex.isChecked()) self.toggle_xperm_criteria() self.criteria_frame.setHidden(not self.criteria_expander.isChecked()) self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.buttonBox.clicked.connect(self.run) self.allowxperm.toggled.connect(self.toggle_xperm_criteria) self.auditallowxperm.toggled.connect(self.toggle_xperm_criteria) self.neverallowxperm.toggled.connect(self.toggle_xperm_criteria) self.dontauditxperm.toggled.connect(self.toggle_xperm_criteria) self.clear_ruletypes.clicked.connect(self.clear_all_ruletypes) self.all_ruletypes.clicked.connect(self.set_all_ruletypes) self.source.textEdited.connect(self.clear_source_error) self.source.editingFinished.connect(self.set_source) self.source_regex.toggled.connect(self.set_source_regex) self.target.textEdited.connect(self.clear_target_error) self.target.editingFinished.connect(self.set_target) self.target_regex.toggled.connect(self.set_target_regex) self.tclass.selectionModel().selectionChanged.connect(self.set_tclass) self.invert_class.clicked.connect(self.invert_tclass_selection) self.perms.selectionModel().selectionChanged.connect(self.set_perms) self.invert_perms.clicked.connect(self.invert_perms_selection) self.xperms.textEdited.connect(self.clear_xperm_error) self.xperms.editingFinished.connect(self.set_xperm) self.default_type.textEdited.connect(self.clear_default_error) self.default_type.editingFinished.connect(self.set_default_type) self.default_regex.toggled.connect(self.set_default_regex) self.bool_criteria.selectionModel().selectionChanged.connect( self.set_bools) # # Ruletype criteria # def _set_ruletypes(self, value): self.allow.setChecked(value) self.allowxperm.setChecked(value) self.auditallow.setChecked(value) self.auditallowxperm.setChecked(value) self.neverallow.setChecked(value) self.neverallowxperm.setChecked(value) self.dontaudit.setChecked(value) self.dontauditxperm.setChecked(value) self.type_transition.setChecked(value) self.type_member.setChecked(value) self.type_change.setChecked(value) def set_all_ruletypes(self): self._set_ruletypes(True) def clear_all_ruletypes(self): self._set_ruletypes(False) # # Source criteria # def clear_source_error(self): self.clear_criteria_error( self.source, "Match the source type/attribute of the rule.") def set_source(self): try: self.query.source = self.source.text() except Exception as ex: self.log.error("Source type/attribute error: {0}".format(ex)) self.set_criteria_error(self.source, ex) def set_source_regex(self, state): self.log.debug("Setting source_regex {0}".format(state)) self.query.source_regex = state self.clear_source_error() self.set_source() # # Target criteria # def clear_target_error(self): self.clear_criteria_error( self.target, "Match the target type/attribute of the rule.") def set_target(self): try: self.query.target = self.target.text() except Exception as ex: self.log.error("Target type/attribute error: {0}".format(ex)) self.set_criteria_error(self.target, ex) def set_target_regex(self, state): self.log.debug("Setting target_regex {0}".format(state)) self.query.target_regex = state self.clear_target_error() self.set_target() # # Class criteria # def set_tclass(self): selected_classes = [] for index in self.tclass.selectionModel().selectedIndexes(): selected_classes.append(self.class_model.data(index, Qt.UserRole)) self.query.tclass = selected_classes self.perms_model.set_classes(selected_classes) def invert_tclass_selection(self): invert_list_selection(self.tclass.selectionModel()) # # Permissions criteria # def set_perms(self): selected_perms = [] for index in self.perms.selectionModel().selectedIndexes(): selected_perms.append(self.perms_model.data(index, Qt.UserRole)) self.query.perms = selected_perms def invert_perms_selection(self): invert_list_selection(self.perms.selectionModel()) # # Extended permission criteria # def toggle_xperm_criteria(self): mode = any( (self.allowxperm.isChecked(), self.auditallowxperm.isChecked(), self.neverallowxperm.isChecked(), self.dontauditxperm.isChecked())) self.xperms.setEnabled(mode) self.xperms_equal.setEnabled(mode) def clear_xperm_error(self): self.clear_criteria_error( self.xperms, "Match the extended permissions of the rule. " "Comma-separated permissions or ranges of permissions.") def set_xperm(self): xperms = [] try: text = self.xperms.text() if text: for item in self.xperms.text().split(","): rng = item.split("-") if len(rng) == 2: xperms.append((int(rng[0], base=16), int(rng[1], base=16))) elif len(rng) == 1: xperms.append((int(rng[0], base=16), int(rng[0], base=16))) else: raise ValueError( "Enter an extended permission or extended permission " "range, e.g. 0x5411 or 0x8800-0x88ff.") self.query.xperms = xperms else: self.query.xperms = None except Exception as ex: self.log.error("Extended permissions error: {0}".format(ex)) self.set_criteria_error(self.xperms, ex) # # Default criteria # def clear_default_error(self): self.clear_criteria_error(self.default_type, "Match the default type the rule.") def set_default_type(self): self.query.default_regex = self.default_regex.isChecked() try: self.query.default = self.default_type.text() except Exception as ex: self.log.error("Default type error: {0}".format(ex)) self.set_criteria_error(self.default_type, ex) def set_default_regex(self, state): self.log.debug("Setting default_regex {0}".format(state)) self.query.default_regex = state self.clear_default_error() self.set_default_type() # # Boolean criteria # def set_bools(self): selected_bools = [] for index in self.bool_criteria.selectionModel().selectedIndexes(): selected_bools.append(self.bool_model.data(index, Qt.UserRole)) self.query.boolean = selected_bools # # Save/Load tab # def save(self): """Return a dictionary of settings.""" if self.errors: raise TabFieldError("Field(s) are in error: {0}".format(" ".join( o.objectName() for o in self.errors))) settings = {} save_checkboxes(self, settings, [ "criteria_expander", "notes_expander", "allow", "allowxperm", "auditallow", "auditallowxperm", "neverallow", "neverallowxperm", "dontaudit", "dontauditxperm", "type_transition", "type_change", "type_member", "source_indirect", "source_regex", "target_indirect", "target_regex", "perms_subset", "xperms_equal", "default_regex", "bools_equal" ]) save_lineedits(self, settings, ["source", "target", "xperms", "default_type"]) save_listviews(self, settings, ["tclass", "perms", "bool_criteria"]) save_textedits(self, settings, ["notes"]) return settings def load(self, settings): load_checkboxes(self, settings, [ "allow", "allowxperm", "auditallow", "auditallowxperm", "neverallow", "neverallowxperm", "dontaudit", "dontauditxperm", "type_transition", "type_change", "type_member", "criteria_expander", "notes_expander", "source_indirect", "source_regex", "target_indirect", "target_regex", "perms_subset", "xperms_equal", "default_regex", "bools_equal" ]) load_lineedits(self, settings, ["source", "target", "xperms", "default_type"]) load_listviews(self, settings, ["tclass", "perms", "bool_criteria"]) load_textedits(self, settings, ["notes"]) # # Results runner # def run(self, button): # right now there is only one button. rule_types = [] max_results = 0 if self.allow.isChecked(): rule_types.append("allow") max_results += self.policy.allow_count if self.allowxperm.isChecked(): rule_types.append("allowxperm") max_results += self.policy.allowxperm_count if self.auditallow.isChecked(): rule_types.append("auditallow") max_results += self.policy.auditallow_count if self.auditallowxperm.isChecked(): rule_types.append("auditallowxperm") max_results += self.policy.auditallowxperm_count if self.neverallow.isChecked(): rule_types.append("neverallow") max_results += self.policy.neverallow_count if self.neverallowxperm.isChecked(): rule_types.append("neverallowxperm") max_results += self.policy.neverallowxperm_count if self.dontaudit.isChecked(): rule_types.append("dontaudit") max_results += self.policy.dontaudit_count if self.dontauditxperm.isChecked(): rule_types.append("dontauditxperm") max_results += self.policy.dontauditxperm_count if self.type_transition.isChecked(): rule_types.append("type_transition") max_results += self.policy.type_transition_count if self.type_member.isChecked(): rule_types.append("type_member") max_results += self.policy.type_member_count if self.type_change.isChecked(): rule_types.append("type_change") max_results += self.policy.type_change_count self.query.ruletype = rule_types self.query.source_indirect = self.source_indirect.isChecked() self.query.target_indirect = self.target_indirect.isChecked() self.query.perms_subset = self.perms_subset.isChecked() self.query.boolean_equal = self.bools_equal.isChecked() # if query is broad, show warning. if not any((self.query.source, self.query.target, self.query.tclass, self.query.perms, self.query.xperms, self.query.default, self.query.boolean)) \ and max_results > 1000: reply = QMessageBox.question( self, "Continue?", "This is a broad query, estimated to return {0} results. Continue?" .format(max_results), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.No: return # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} type enforcement rule(s) found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText( "Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() # If the permissions column width is too long, pull back # to a reasonable size header = self.table_results.horizontalHeader() if header.sectionSize(4) > 400: header.resizeSection(4, 400) if not self.busy.wasCanceled(): self.busy.setLabelText( "Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText( "Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class OperacaoLogistica(QDialog): #TODO: Acertar formatação na listagem de items por SIMAFIC def __init__(self, parent=None): super().__init__(parent) self.setWindowFlags(Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint) self.setWindowTitle('Operação Logística') self.setMinimumSize(QSize(h_size, v_size)) self.setWindowIcon(QIcon(main_icon)) verticalSpacer = QSpacerItem(40, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) #Pedido Input Field self.proxy_list_result_id = QSortFilterProxyModel() self.numero_pedido = QLineEdit(self) self.numero_pedido.setPlaceholderText("Insira o Número do Pedido") self.numero_pedido.textChanged.connect( lambda wildcard: self.proxy_list_result_id.setFilterWildcard( wildcard)) #Voltar Btn self.voltar_btn = QPushButton(self) #self.voltar_btn.setStyleSheet('background-color: rgb(0,0,255); color: #fff') self.voltar_btn.setText('Voltar') self.voltar_btn.clicked.connect(self.goMainWindow) self.close() #Adicionar Cores no StyleSheet colors = ['##393318', ' ##fff'] self.pedidos = services.get_all_pedidos() self.item_result = None self.item_escolhido = None self.id_pedido_list = QListView() self.simafics_do_id = QListWidget() #self.simafics_do_id.setHidden(True) self.createPedidoIdList() self.id_pedido_list.clicked.connect( lambda id_pedido: self.createListaSimafics(id_pedido)) self.simafics_do_id.itemDoubleClicked.connect( lambda pedido: self.simaficSelecionado(pedido)) self.pedidos_label = QLabel() self.pedidos_label.setBuddy(self.id_pedido_list) self.simafic_label = QLabel() self.simafic_label.setBuddy(self.simafics_do_id) self.itensTree = PedidoItensTree() self.treeItensTV = QTreeView() self.treeItensTV.setEditTriggers(QAbstractItemView.NoEditTriggers) self.treeItensTV.setAlternatingRowColors(True) self.treeItensTV.setRootIsDecorated(True) self.treeItensTV.doubleClicked.connect(self.simaficSelecionado) self.treeItensTV.setColumnHidden(8, True) if len(self.pedidos) <= 0: self.pedidos_label.setText( "É necessário adicionar um pedido na tela de cadastro.") self.pedidos_label.setStyleSheet("QLabel { color: red; }") else: self.pedidos_label.setText("Listagem de Pedidos:") self.pedidos_label.setStyleSheet("QLabel { color: black; }") self.simafic_label.setText( "Selecione um pedido para ver a listagem de Itens por SIMAFIC:" ) self.simafic_label.setStyleSheet("QLabel { color: red; }") layout = QGridLayout() layout.setColumnStretch(0, 1) layout.setColumnStretch(1, 4) layout.addWidget(self.numero_pedido, 0, 0) layout.addWidget(self.voltar_btn, 0, 1) layout.addWidget(self.pedidos_label, 1, 0) layout.addWidget(self.simafic_label, 1, 1) layout.addWidget(self.id_pedido_list, 2, 0) layout.addWidget(self.treeItensTV, 2, 1) #layout.addWidget(self.simafics_do_id, 2,1) self.setLayout(layout) def createPedidoIdList(self): print('def createPedidoIdList(self):') onlyids = set() pedidosbyid = [] pedidosCompletos = [] self.proxy_list_result_id = QSortFilterProxyModel() for obj in self.pedidos: if obj.id_pedido not in onlyids: pedidosbyid.append(obj) onlyids.add(obj.id_pedido) self.pedidoId_model = QStringListModel(onlyids, self) self.proxy_list_result_id.setSourceModel(self.pedidoId_model) self.id_pedido_list.setModel(self.proxy_list_result_id) self.id_pedido_list.setAlternatingRowColors(True) self.id_pedido_list.setEditTriggers(QAbstractItemView.NoEditTriggers) def createListaSimafics(self, id_pedido): pedido = id_pedido.data() self.pedidosModel = self.itensTree.createPedidosModel(self.itensTree) self.treeItensTV.setModel(self.pedidosModel) print('def listaSimafics(self, id_pedido): {id_pedido}'.format( id_pedido=pedido)) self.item_result = None self.item_result = [x for x in self.pedidos if x.id_pedido == pedido] self.simafics_do_id.clear() self.pedidosModel.beginResetModel self.pedidosModel.modelReset self.pedidosModel.endResetModel for idx, item in enumerate(self.item_result): print(item) self.itensTree.addItens( self.pedidosModel, item.cod_simafic, item.desc, item.qty_scanneada, item.qty_total, item.nome_responsavel, item.id_caixa, item.time_updated.strftime("%d/%m/%y %H:%M:%S"), item.id_pedido, item) self.simafic_label.setText( "Listagem de Itens do pedido {} por SIMAFIC:".format(pedido)) self.simafic_label.setStyleSheet("QLabel { color: black; }") #self.simafics_do_id.setHidden(False) def simaficSelecionado(self, item): print(item.column(), item.row()) simafic_escolhido = self.treeItensTV.model().index(item.row(), 0).data() id_pedido = self.treeItensTV.model().index(item.row(), 7).data() self.item_escolhido = [ x for x in self.item_result if x.cod_simafic == simafic_escolhido and x.id_pedido == id_pedido ] self.cams = ItemScanner(self.item_escolhido[0]) self.cams.show() self.close() def goMainWindow(self): self.cams = mainView self.cams.show() self.close() def goScan(self): self.cams = ItemScanner("Eu sou o Pedido", "Eu sou o Simafic", "Eu sou a Descrição", "300") self.cams.show() self.close()
class FSUseQueryTab(AnalysisTab): """A fs_use_* rule query.""" def __init__(self, parent, policy, perm_map): super(FSUseQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = FSUseQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.fsusequery").removeHandler(self.handler) def setupUi(self): self.load_ui("fsusequery.ui") # set up user autocompletion user_completion_list = [str(u) for u in self.policy.users()] user_completer_model = QStringListModel(self) user_completer_model.setStringList(sorted(user_completion_list)) self.user_completion = QCompleter() self.user_completion.setModel(user_completer_model) self.user.setCompleter(self.user_completion) # set up role autocompletion role_completion_list = [str(r) for r in self.policy.roles()] role_completer_model = QStringListModel(self) role_completer_model.setStringList(sorted(role_completion_list)) self.role_completion = QCompleter() self.role_completion.setModel(role_completer_model) self.role.setCompleter(self.role_completion) # set up type autocompletion type_completion_list = [str(t) for t in self.policy.types()] type_completer_model = QStringListModel(self) type_completer_model.setStringList(sorted(type_completion_list)) self.type_completion = QCompleter() self.type_completion.setModel(type_completer_model) self.type_.setCompleter(self.type_completion) # setup indications of errors on source/target/default self.errors = set() self.orig_palette = self.type_.palette() self.error_palette = self.type_.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_fs_error() self.clear_user_error() self.clear_type_error() self.clear_role_error() self.clear_range_error() # set up results self.table_results_model = FSUseTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(1, Qt.AscendingOrder) # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.fsusequery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.set_fs_regex(self.fs_regex.isChecked()) self.criteria_frame.setHidden(not self.criteria_expander.isChecked()) self.notes.setHidden(not self.notes_expander.isChecked()) # Range criteria is available only if policy is MLS if not self.policy.mls: self.range_criteria.setEnabled(False) self.range_criteria.setToolTip("MLS is disabled in this policy.") self.range_.setToolTip("MLS is disabled in this policy.") self.range_exact.setToolTip("MLS is disabled in this policy.") self.range_overlap.setToolTip("MLS is disabled in this policy.") self.range_subset.setToolTip("MLS is disabled in this policy.") self.range_superset.setToolTip("MLS is disabled in this policy.") # connect signals self.buttonBox.clicked.connect(self.run) self.clear_ruletypes.clicked.connect(self.clear_all_ruletypes) self.all_ruletypes.clicked.connect(self.set_all_ruletypes) self.fs.textEdited.connect(self.clear_fs_error) self.fs.editingFinished.connect(self.set_fs) self.fs_regex.toggled.connect(self.set_fs_regex) self.user.textEdited.connect(self.clear_user_error) self.user.editingFinished.connect(self.set_user) self.user_regex.toggled.connect(self.set_user_regex) self.role.textEdited.connect(self.clear_role_error) self.role.editingFinished.connect(self.set_role) self.role_regex.toggled.connect(self.set_role_regex) self.type_.textEdited.connect(self.clear_type_error) self.type_.editingFinished.connect(self.set_type) self.type_regex.toggled.connect(self.set_type_regex) self.range_.textEdited.connect(self.clear_range_error) self.range_.editingFinished.connect(self.set_range) # # Ruletype criteria # def _set_ruletypes(self, value): self.fs_use_xattr.setChecked(value) self.fs_use_trans.setChecked(value) self.fs_use_task.setChecked(value) def set_all_ruletypes(self): self._set_ruletypes(True) def clear_all_ruletypes(self): self._set_ruletypes(False) # # FS criteria # def clear_fs_error(self): self.clear_criteria_error(self.fs, "Match the filesystem type.") def set_fs(self): try: self.query.fs = self.fs.text() except Exception as ex: self.log.error("Filesystem type error: {0}".format(ex)) self.set_criteria_error(self.fs, ex) def set_fs_regex(self, state): self.log.debug("Setting fs_regex {0}".format(state)) self.query.fs_regex = state self.clear_fs_error() self.set_fs() # # User criteria # def clear_user_error(self): self.clear_criteria_error(self.user, "Match the user of the context.") def set_user(self): try: self.query.user = self.user.text() except Exception as ex: self.log.error("Context user error: {0}".format(ex)) self.set_criteria_error(self.user, ex) def set_user_regex(self, state): self.log.debug("Setting user_regex {0}".format(state)) self.query.user_regex = state self.clear_user_error() self.set_user() # # Role criteria # def clear_role_error(self): self.clear_criteria_error(self.role, "Match the role of the context.") def set_role(self): try: self.query.role = self.role.text() except Exception as ex: self.log.error("Context role error: {0}".format(ex)) self.set_criteria_error(self.role, ex) def set_role_regex(self, state): self.log.debug("Setting role_regex {0}".format(state)) self.query.role_regex = state self.clear_role_error() self.set_role() # # Type criteria # def clear_type_error(self): self.clear_criteria_error(self.type_, "Match the type of the context.") def set_type(self): try: self.query.type_ = self.type_.text() except Exception as ex: self.log.error("Context type error: {0}".format(ex)) self.set_criteria_error(self.type_, ex) def set_type_regex(self, state): self.log.debug("Setting type_regex {0}".format(state)) self.query.type_regex = state self.clear_type_error() self.set_type() # # Range criteria # def clear_range_error(self): self.clear_criteria_error(self.range_, "Match the range of the context.") def set_range(self): try: self.query.range_ = self.range_.text() except Exception as ex: self.log.info("Context range error: " + str(ex)) self.set_criteria_error(self.range_, ex) # # Save/Load tab # def save(self): """Return a dictionary of settings.""" if self.errors: raise TabFieldError("Field(s) are in error: {0}". format(" ".join(o.objectName() for o in self.errors))) settings = {} save_checkboxes(self, settings, ["criteria_expander", "notes_expander", "fs_regex", "fs_use_xattr", "fs_use_trans", "fs_use_task", "user_regex", "role_regex", "type_regex", "range_exact", "range_overlap", "range_subset", "range_superset"]) save_lineedits(self, settings, ["fs", "user", "role", "type_", "range_"]) save_textedits(self, settings, ["notes"]) return settings def load(self, settings): load_checkboxes(self, settings, ["criteria_expander", "notes_expander", "fs_regex", "fs_use_xattr", "fs_use_trans", "fs_use_task", "user_regex", "role_regex", "type_regex", "range_exact", "range_overlap", "range_subset", "range_superset"]) load_lineedits(self, settings, ["fs", "user", "role", "type_", "range_"]) load_textedits(self, settings, ["notes"]) # # Results runner # def run(self, button): # right now there is only one button. rule_types = [] for mode in [self.fs_use_xattr, self.fs_use_trans, self.fs_use_task]: if mode.isChecked(): rule_types.append(mode.objectName()) self.query.ruletype = rule_types self.query.range_overlap = self.range_overlap.isChecked() self.query.range_subset = self.range_subset.isChecked() self.query.range_superset = self.range_superset.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} fs_use_* statment(s) found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self._version = "0.1.20" self.setWindowIcon(QIcon("GUI/icons/logo.png")) self.setWindowTitle("Tasmota Device Manager {}".format(self._version)) self.main_splitter = QSplitter() self.devices_splitter = QSplitter(Qt.Vertical) self.mqtt_queue = [] self.devices = {} self.fulltopic_queue = [] old_settings = QSettings() self.settings = QSettings("{}/TDM/tdm.cfg".format(QDir.homePath()), QSettings.IniFormat) self.setMinimumSize(QSize(1280, 800)) for k in old_settings.allKeys(): self.settings.setValue(k, old_settings.value(k)) old_settings.remove(k) self.device_model = TasmotaDevicesModel() self.telemetry_model = TasmotaDevicesTree() self.console_model = ConsoleModel() self.sorted_console_model = QSortFilterProxyModel() self.sorted_console_model.setSourceModel(self.console_model) self.sorted_console_model.setFilterKeyColumn(CnsMdl.FRIENDLY_NAME) self.setup_mqtt() self.setup_telemetry_view() self.setup_main_layout() self.add_devices_tab() self.build_toolbars() self.setStatusBar(QStatusBar()) self.queue_timer = QTimer() self.queue_timer.timeout.connect(self.mqtt_publish_queue) self.queue_timer.start(500) self.auto_timer = QTimer() self.auto_timer.timeout.connect(self.autoupdate) self.load_window_state() if self.settings.value("connect_on_startup", False, bool): self.actToggleConnect.trigger() def setup_main_layout(self): self.mdi = QMdiArea() self.mdi.setActivationOrder(QMdiArea.ActivationHistoryOrder) self.mdi.setViewMode(QMdiArea.TabbedView) self.mdi.setDocumentMode(True) mdi_widget = QWidget() mdi_widget.setLayout(VLayout()) mdi_widget.layout().addWidget(self.mdi) self.devices_splitter.addWidget(mdi_widget) vl_console = VLayout() hl_filter = HLayout() self.cbFilter = QCheckBox("Console filtering") self.cbxFilterDevice = QComboBox() self.cbxFilterDevice.setEnabled(False) self.cbxFilterDevice.setFixedWidth(200) self.cbxFilterDevice.setModel(self.device_model) self.cbxFilterDevice.setModelColumn(DevMdl.FRIENDLY_NAME) hl_filter.addWidgets([self.cbFilter, self.cbxFilterDevice]) hl_filter.addStretch(0) vl_console.addLayout(hl_filter) self.console_view = TableView() self.console_view.setModel(self.console_model) self.console_view.setupColumns(columns_console) self.console_view.setAlternatingRowColors(True) self.console_view.verticalHeader().setDefaultSectionSize(20) self.console_view.setMinimumHeight(200) vl_console.addWidget(self.console_view) console_widget = QWidget() console_widget.setLayout(vl_console) self.devices_splitter.addWidget(console_widget) self.main_splitter.insertWidget(0, self.devices_splitter) self.setCentralWidget(self.main_splitter) self.console_view.clicked.connect(self.select_cons_entry) self.console_view.doubleClicked.connect(self.view_payload) self.cbFilter.toggled.connect(self.toggle_console_filter) self.cbxFilterDevice.currentTextChanged.connect( self.select_console_filter) def setup_telemetry_view(self): tele_widget = QWidget() vl_tele = VLayout() self.tview = QTreeView() self.tview.setMinimumWidth(300) self.tview.setModel(self.telemetry_model) self.tview.setAlternatingRowColors(True) self.tview.setUniformRowHeights(True) self.tview.setIndentation(15) self.tview.setSizePolicy( QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum)) self.tview.expandAll() self.tview.resizeColumnToContents(0) vl_tele.addWidget(self.tview) tele_widget.setLayout(vl_tele) self.main_splitter.addWidget(tele_widget) def setup_mqtt(self): self.mqtt = MqttClient() self.mqtt.connecting.connect(self.mqtt_connecting) self.mqtt.connected.connect(self.mqtt_connected) self.mqtt.disconnected.connect(self.mqtt_disconnected) self.mqtt.connectError.connect(self.mqtt_connectError) self.mqtt.messageSignal.connect(self.mqtt_message) def add_devices_tab(self): tabDevicesList = DevicesListWidget(self) self.mdi.addSubWindow(tabDevicesList) tabDevicesList.setWindowState(Qt.WindowMaximized) def load_window_state(self): wndGeometry = self.settings.value('window_geometry') if wndGeometry: self.restoreGeometry(wndGeometry) spltState = self.settings.value('splitter_state') if spltState: self.main_splitter.restoreState(spltState) def build_toolbars(self): main_toolbar = Toolbar(orientation=Qt.Horizontal, iconsize=16, label_position=Qt.ToolButtonTextBesideIcon) main_toolbar.setObjectName("main_toolbar") self.addToolBar(main_toolbar) main_toolbar.addAction(QIcon("./GUI/icons/connections.png"), "Broker", self.setup_broker) self.actToggleConnect = QAction(QIcon("./GUI/icons/disconnect.png"), "MQTT") self.actToggleConnect.setCheckable(True) self.actToggleConnect.toggled.connect(self.toggle_connect) main_toolbar.addAction(self.actToggleConnect) self.actToggleAutoUpdate = QAction(QIcon("./GUI/icons/automatic.png"), "Auto telemetry") self.actToggleAutoUpdate.setCheckable(True) self.actToggleAutoUpdate.toggled.connect(self.toggle_autoupdate) main_toolbar.addAction(self.actToggleAutoUpdate) main_toolbar.addSeparator() main_toolbar.addAction(QIcon("./GUI/icons/bssid.png"), "BSSId", self.bssid) main_toolbar.addAction(QIcon("./GUI/icons/export.png"), "Export list", self.export) def initial_query(self, idx, queued=False): for q in initial_queries: topic = "{}status".format(self.device_model.commandTopic(idx)) if queued: self.mqtt_queue.append([topic, q]) else: self.mqtt.publish(topic, q, 1) self.console_log(topic, "Asked for STATUS {}".format(q), q) def setup_broker(self): brokers_dlg = BrokerDialog() if brokers_dlg.exec_( ) == QDialog.Accepted and self.mqtt.state == self.mqtt.Connected: self.mqtt.disconnect() def toggle_autoupdate(self, state): if state: self.auto_timer.setInterval(5000) self.auto_timer.start() def toggle_connect(self, state): if state and self.mqtt.state == self.mqtt.Disconnected: self.broker_hostname = self.settings.value('hostname', 'localhost') self.broker_port = self.settings.value('port', 1883, int) self.broker_username = self.settings.value('username') self.broker_password = self.settings.value('password') self.mqtt.hostname = self.broker_hostname self.mqtt.port = self.broker_port if self.broker_username: self.mqtt.setAuth(self.broker_username, self.broker_password) self.mqtt.connectToHost() elif not state and self.mqtt.state == self.mqtt.Connected: self.mqtt_disconnect() def autoupdate(self): if self.mqtt.state == self.mqtt.Connected: for d in range(self.device_model.rowCount()): idx = self.device_model.index(d, 0) cmnd = self.device_model.commandTopic(idx) self.mqtt.publish(cmnd + "STATUS", payload=8) def mqtt_connect(self): self.broker_hostname = self.settings.value('hostname', 'localhost') self.broker_port = self.settings.value('port', 1883, int) self.broker_username = self.settings.value('username') self.broker_password = self.settings.value('password') self.mqtt.hostname = self.broker_hostname self.mqtt.port = self.broker_port if self.broker_username: self.mqtt.setAuth(self.broker_username, self.broker_password) if self.mqtt.state == self.mqtt.Disconnected: self.mqtt.connectToHost() def mqtt_disconnect(self): self.mqtt.disconnectFromHost() def mqtt_connecting(self): self.statusBar().showMessage("Connecting to broker") def mqtt_connected(self): self.actToggleConnect.setIcon(QIcon("./GUI/icons/connect.png")) self.statusBar().showMessage("Connected to {}:{} as {}".format( self.broker_hostname, self.broker_port, self.broker_username if self.broker_username else '[anonymous]')) self.mqtt_subscribe() for d in range(self.device_model.rowCount()): idx = self.device_model.index(d, 0) self.initial_query(idx) def mqtt_subscribe(self): main_topics = ["+/stat/+", "+/tele/+", "stat/#", "tele/#"] for d in range(self.device_model.rowCount()): idx = self.device_model.index(d, 0) if not self.device_model.isDefaultTemplate(idx): main_topics.append(self.device_model.commandTopic(idx)) main_topics.append(self.device_model.statTopic(idx)) for t in main_topics: self.mqtt.subscribe(t) def mqtt_publish_queue(self): for q in self.mqtt_queue: t, p = q self.mqtt.publish(t, p) self.mqtt_queue.pop(self.mqtt_queue.index(q)) def mqtt_disconnected(self): self.actToggleConnect.setIcon(QIcon("./GUI/icons/disconnect.png")) self.statusBar().showMessage("Disconnected") def mqtt_connectError(self, rc): reason = { 1: "Incorrect protocol version", 2: "Invalid client identifier", 3: "Server unavailable", 4: "Bad username or password", 5: "Not authorized", } self.statusBar().showMessage("Connection error: {}".format(reason[rc])) self.actToggleConnect.setChecked(False) def mqtt_message(self, topic, msg): found = self.device_model.findDevice(topic) if found.reply == 'LWT': if not msg: msg = "offline" if found.index.isValid(): self.console_log(topic, "LWT update: {}".format(msg), msg) self.device_model.updateValue(found.index, DevMdl.LWT, msg) self.initial_query(found.index, queued=True) elif msg == "Online": self.console_log( topic, "LWT for unknown device '{}'. Asking for FullTopic.". format(found.topic), msg, False) self.mqtt_queue.append( ["cmnd/{}/fulltopic".format(found.topic), ""]) self.mqtt_queue.append( ["{}/cmnd/fulltopic".format(found.topic), ""]) elif found.reply == 'RESULT': try: full_topic = loads(msg).get('FullTopic') new_topic = loads(msg).get('Topic') template_name = loads(msg).get('NAME') ota_url = loads(msg).get('OtaUrl') teleperiod = loads(msg).get('TelePeriod') if full_topic: # TODO: update FullTopic for existing device AFTER the FullTopic changes externally (the message will arrive from new FullTopic) if not found.index.isValid(): self.console_log( topic, "FullTopic for {}".format(found.topic), msg, False) new_idx = self.device_model.addDevice(found.topic, full_topic, lwt='online') tele_idx = self.telemetry_model.addDevice( TasmotaDevice, found.topic) self.telemetry_model.devices[found.topic] = tele_idx #TODO: add QSortFilterProxyModel to telemetry treeview and sort devices after adding self.initial_query(new_idx) self.console_log( topic, "Added {} with fulltopic {}, querying for STATE". format(found.topic, full_topic), msg) self.tview.expand(tele_idx) self.tview.resizeColumnToContents(0) elif new_topic: if found.index.isValid() and found.topic != new_topic: self.console_log( topic, "New topic for {}".format(found.topic), msg) self.device_model.updateValue(found.index, DevMdl.TOPIC, new_topic) tele_idx = self.telemetry_model.devices.get( found.topic) if tele_idx: self.telemetry_model.setDeviceName( tele_idx, new_topic) self.telemetry_model.devices[ new_topic] = self.telemetry_model.devices.pop( found.topic) elif template_name: self.device_model.updateValue( found.index, DevMdl.MODULE, "{} (0)".format(template_name)) elif ota_url: self.device_model.updateValue(found.index, DevMdl.OTA_URL, ota_url) elif teleperiod: self.device_model.updateValue(found.index, DevMdl.TELEPERIOD, teleperiod) except JSONDecodeError as e: self.console_log( topic, "JSON payload decode error. Check error.log for additional info." ) with open("{}/TDM/error.log".format(QDir.homePath()), "a+") as l: l.write("{}\t{}\t{}\t{}\n".format( QDateTime.currentDateTime().toString( "yyyy-MM-dd hh:mm:ss"), topic, msg, e.msg)) elif found.index.isValid(): ok = False try: if msg.startswith("{"): payload = loads(msg) else: payload = msg ok = True except JSONDecodeError as e: self.console_log( topic, "JSON payload decode error. Check error.log for additional info." ) with open("{}/TDM/error.log".format(QDir.homePath()), "a+") as l: l.write("{}\t{}\t{}\t{}\n".format( QDateTime.currentDateTime().toString( "yyyy-MM-dd hh:mm:ss"), topic, msg, e.msg)) if ok: try: if found.reply == 'STATUS': self.console_log(topic, "Received device status", msg) payload = payload['Status'] self.device_model.updateValue( found.index, DevMdl.FRIENDLY_NAME, payload['FriendlyName'][0]) self.telemetry_model.setDeviceFriendlyName( self.telemetry_model.devices[found.topic], payload['FriendlyName'][0]) module = payload['Module'] if module == 0: self.mqtt.publish( self.device_model.commandTopic(found.index) + "template") else: self.device_model.updateValue( found.index, DevMdl.MODULE, modules.get(module, 'Unknown')) self.device_model.updateValue(found.index, DevMdl.MODULE_ID, module) elif found.reply == 'STATUS1': self.console_log(topic, "Received program information", msg) payload = payload['StatusPRM'] self.device_model.updateValue( found.index, DevMdl.RESTART_REASON, payload.get('RestartReason')) self.device_model.updateValue(found.index, DevMdl.OTA_URL, payload.get('OtaUrl')) elif found.reply == 'STATUS2': self.console_log(topic, "Received firmware information", msg) payload = payload['StatusFWR'] self.device_model.updateValue(found.index, DevMdl.FIRMWARE, payload['Version']) self.device_model.updateValue(found.index, DevMdl.CORE, payload['Core']) elif found.reply == 'STATUS3': self.console_log(topic, "Received syslog information", msg) payload = payload['StatusLOG'] self.device_model.updateValue(found.index, DevMdl.TELEPERIOD, payload['TelePeriod']) elif found.reply == 'STATUS5': self.console_log(topic, "Received network status", msg) payload = payload['StatusNET'] self.device_model.updateValue(found.index, DevMdl.MAC, payload['Mac']) self.device_model.updateValue(found.index, DevMdl.IP, payload['IPAddress']) elif found.reply in ('STATE', 'STATUS11'): self.console_log(topic, "Received device state", msg) if found.reply == 'STATUS11': payload = payload['StatusSTS'] self.parse_state(found.index, payload) elif found.reply in ('SENSOR', 'STATUS8'): self.console_log(topic, "Received telemetry", msg) if found.reply == 'STATUS8': payload = payload['StatusSNS'] self.parse_telemetry(found.index, payload) elif found.reply.startswith('POWER'): self.console_log( topic, "Received {} state".format(found.reply), msg) payload = {found.reply: msg} self.parse_power(found.index, payload) except KeyError as k: self.console_log( topic, "JSON key error. Check error.log for additional info.") with open("{}/TDM/error.log".format(QDir.homePath()), "a+") as l: l.write("{}\t{}\t{}\tKeyError: {}\n".format( QDateTime.currentDateTime().toString( "yyyy-MM-dd hh:mm:ss"), topic, payload, k.args[0])) def parse_power(self, index, payload, from_state=False): old = self.device_model.power(index) power = { k: payload[k] for k in payload.keys() if k.startswith("POWER") } # TODO: fix so that number of relays get updated properly after module/no. of relays change needs_update = False if old: # if from_state and len(old) != len(power): # needs_update = True # # else: for k in old.keys(): needs_update |= old[k] != power.get(k, old[k]) if needs_update: break else: needs_update = True if needs_update: self.device_model.updateValue(index, DevMdl.POWER, power) def parse_state(self, index, payload): bssid = payload['Wifi'].get('BSSId') if not bssid: bssid = payload['Wifi'].get('APMac') self.device_model.updateValue(index, DevMdl.BSSID, bssid) self.device_model.updateValue(index, DevMdl.SSID, payload['Wifi']['SSId']) self.device_model.updateValue(index, DevMdl.CHANNEL, payload['Wifi'].get('Channel', "n/a")) self.device_model.updateValue(index, DevMdl.RSSI, payload['Wifi']['RSSI']) self.device_model.updateValue(index, DevMdl.UPTIME, payload['Uptime']) self.device_model.updateValue(index, DevMdl.LOADAVG, payload.get('LoadAvg')) self.device_model.updateValue(index, DevMdl.LINKCOUNT, payload['Wifi'].get('LinkCount', "n/a")) self.device_model.updateValue(index, DevMdl.DOWNTIME, payload['Wifi'].get('Downtime', "n/a")) self.parse_power(index, payload, True) tele_idx = self.telemetry_model.devices.get( self.device_model.topic(index)) if tele_idx: tele_device = self.telemetry_model.getNode(tele_idx) self.telemetry_model.setDeviceFriendlyName( tele_idx, self.device_model.friendly_name(index)) pr = tele_device.provides() for k in pr.keys(): self.telemetry_model.setData(pr[k], payload.get(k)) def parse_telemetry(self, index, payload): device = self.telemetry_model.devices.get( self.device_model.topic(index)) if device: node = self.telemetry_model.getNode(device) time = node.provides()['Time'] if 'Time' in payload: self.telemetry_model.setData(time, payload.pop('Time')) temp_unit = "C" pres_unit = "hPa" if 'TempUnit' in payload: temp_unit = payload.pop('TempUnit') if 'PressureUnit' in payload: pres_unit = payload.pop('PressureUnit') for sensor in sorted(payload.keys()): if sensor == 'DS18x20': for sns_name in payload[sensor].keys(): d = node.devices().get(sensor) if not d: d = self.telemetry_model.addDevice( DS18x20, payload[sensor][sns_name]['Type'], device) self.telemetry_model.getNode(d).setTempUnit(temp_unit) payload[sensor][sns_name]['Id'] = payload[sensor][ sns_name].pop('Address') pr = self.telemetry_model.getNode(d).provides() for pk in pr.keys(): self.telemetry_model.setData( pr[pk], payload[sensor][sns_name].get(pk)) self.tview.expand(d) elif sensor.startswith('DS18B20'): d = node.devices().get(sensor) if not d: d = self.telemetry_model.addDevice( DS18x20, sensor, device) self.telemetry_model.getNode(d).setTempUnit(temp_unit) pr = self.telemetry_model.getNode(d).provides() for pk in pr.keys(): self.telemetry_model.setData(pr[pk], payload[sensor].get(pk)) self.tview.expand(d) if sensor == 'COUNTER': d = node.devices().get(sensor) if not d: d = self.telemetry_model.addDevice( CounterSns, "Counter", device) pr = self.telemetry_model.getNode(d).provides() for pk in pr.keys(): self.telemetry_model.setData(pr[pk], payload[sensor].get(pk)) self.tview.expand(d) else: d = node.devices().get(sensor) if not d: d = self.telemetry_model.addDevice( sensor_map.get(sensor, Node), sensor, device) pr = self.telemetry_model.getNode(d).provides() if 'Temperature' in pr: self.telemetry_model.getNode(d).setTempUnit(temp_unit) if 'Pressure' in pr or 'SeaPressure' in pr: self.telemetry_model.getNode(d).setPresUnit(pres_unit) for pk in pr.keys(): self.telemetry_model.setData(pr[pk], payload[sensor].get(pk)) self.tview.expand(d) # self.tview.resizeColumnToContents(0) def console_log(self, topic, description, payload="", known=True): longest_tp = 0 longest_fn = 0 short_topic = "/".join(topic.split("/")[0:-1]) fname = self.devices.get(short_topic, "") if not fname: device = self.device_model.findDevice(topic) fname = self.device_model.friendly_name(device.index) self.devices.update({short_topic: fname}) self.console_model.addEntry(topic, fname, description, payload, known) if len(topic) > longest_tp: longest_tp = len(topic) self.console_view.resizeColumnToContents(1) if len(fname) > longest_fn: longest_fn = len(fname) self.console_view.resizeColumnToContents(1) def view_payload(self, idx): if self.cbFilter.isChecked(): idx = self.sorted_console_model.mapToSource(idx) row = idx.row() timestamp = self.console_model.data( self.console_model.index(row, CnsMdl.TIMESTAMP)) topic = self.console_model.data( self.console_model.index(row, CnsMdl.TOPIC)) payload = self.console_model.data( self.console_model.index(row, CnsMdl.PAYLOAD)) dlg = PayloadViewDialog(timestamp, topic, payload) dlg.exec_() def select_cons_entry(self, idx): self.cons_idx = idx def export(self): fname, _ = QFileDialog.getSaveFileName(self, "Export device list as...", directory=QDir.homePath(), filter="CSV files (*.csv)") if fname: if not fname.endswith(".csv"): fname += ".csv" with open(fname, "w", encoding='utf8') as f: column_titles = [ 'mac', 'topic', 'friendly_name', 'full_topic', 'cmnd_topic', 'stat_topic', 'tele_topic', 'module', 'module_id', 'firmware', 'core' ] c = csv.writer(f) c.writerow(column_titles) for r in range(self.device_model.rowCount()): d = self.device_model.index(r, 0) c.writerow([ self.device_model.mac(d), self.device_model.topic(d), self.device_model.friendly_name(d), self.device_model.fullTopic(d), self.device_model.commandTopic(d), self.device_model.statTopic(d), self.device_model.teleTopic(d), modules.get(self.device_model.module(d)), self.device_model.module(d), self.device_model.firmware(d), self.device_model.core(d) ]) def bssid(self): BSSIdDialog().exec_() # if dlg.exec_() == QDialog.Accepted: def toggle_console_filter(self, state): self.cbxFilterDevice.setEnabled(state) if state: self.console_view.setModel(self.sorted_console_model) else: self.console_view.setModel(self.console_model) def select_console_filter(self, fname): self.sorted_console_model.setFilterFixedString(fname) def closeEvent(self, e): self.settings.setValue("window_geometry", self.saveGeometry()) self.settings.setValue("splitter_state", self.main_splitter.saveState()) self.settings.sync() e.accept()
class CadastroPedidos(QDialog): def __init__(self, parent=None): super(CadastroPedidos, self).__init__(parent=None) self.setWindowFlags(Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint) self.setMinimumSize(QSize(h_size, v_size)) self.originalPalette = QApplication.palette() self.setWindowIcon(QIcon(main_icon)) self.setWindowTitle("Cadastro de Pedidos") self.setStyleSheet(style) self.pedidos_selecionados = [] #Sempre que for iniciado criará um objeto data self.data = dict() voltar_btn = QPushButton(self) voltar_btn.setText('Voltar') voltar_btn.clicked.connect(self.goMainWindow) voltar_btn.setFocusPolicy(Qt.NoFocus) self.dadosDoPedido() self.resumoGeral() self.resumoDosItens() '''disableWidgetsCheckBox.toggled.connect( self.bottomLeftGroupBox.setHidden ) ''' topLayout = QHBoxLayout() topLayout.addWidget(voltar_btn) leftLayout = QVBoxLayout() leftLayout.addWidget(self.topLeftGroupBox, 100) #leftLayout.addWidget(self.bottomLeftGroupBox, 50) mainLayout = QGridLayout() mainLayout.addLayout(topLayout, 0, 0, 1, 2) mainLayout.addLayout(leftLayout, 1, 0) mainLayout.addWidget(self.topRightGroupBox, 1, 1) #mainLayout.addWidget(self.submitButtons, 3, 0, 1, 2) mainLayout.setRowStretch(1, 1) mainLayout.setColumnStretch(0, 1) mainLayout.setColumnStretch(1, 3) self.setLayout(mainLayout) def dadosDoPedido(self): self.topLeftGroupBox = QGroupBox("Dados do Pedido") verticalSpacer = QSpacerItem(40, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) formLayout = QFormLayout() self.pedido = QLineEdit(self) self.pedido.setPlaceholderText("ex: 123.456") pedido_label = QLabel("Pedido:") self.n_simafic = QLineEdit(self) self.n_simafic.setPlaceholderText("ex: 08.04.02.507-6") n_simafic_label = QLabel("COD. SIMAFIC:") self.qtd_items = QLineEdit(self) self.qtd_items.setPlaceholderText("ex: 100") qtd_items_label = QLabel("Quantidade de Items:") '''ADD PEDIDO''' add_item = QPushButton('Adicionar Item') add_item.setObjectName('Add') add_item.setIcon(QIcon('assets/check_icon_blue2.png')) add_item.clicked.connect(self.add_items) '''CLEAR BUTTON CONFIG ''' clear_btn = QPushButton('Limpar Campos') clear_btn.setObjectName('Yellow') clear_btn.setIcon(QIcon('assets/eraser.png')) clear_btn.clicked.connect(self.limpar_pedidos) formLayout.addRow(pedido_label, self.pedido) formLayout.addRow(n_simafic_label, self.n_simafic) formLayout.addRow(qtd_items_label, self.qtd_items) formLayout.addItem(verticalSpacer) formLayout.addRow(add_item) formLayout.addRow(clear_btn) '''checkBox = QCheckBox("Tri-state check box") checkBox.setTristate(True) checkBox.setCheckState(Qt.PartiallyChecked)''' #layout.addWidget(checkBox) '''formLayout.addWidget(add_item) formLayout.addWidget(clear_btn)''' layout = QVBoxLayout() layout.addLayout(formLayout) layout.addStretch(2) self.topLeftGroupBox.setLayout(layout) def resumoGeral(self): self.topRightGroupBox = QTabWidget() self.topRightGroupBox.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Ignored) #[First Tab] Create first tab verticalSpacer = QSpacerItem(40, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) tab1ListaPedidos = QWidget() layout = QVBoxLayout(self) #[First Tab] - TextFields searchPedido = QLineEdit(self) searchPedido.setPlaceholderText("Filtrar por pedido: ") searchProduto = QLineEdit(self) searchProduto.setPlaceholderText("Filtrar por produto: ") #[First Tab] - Set TableView self.tabv_pedidos = QTableView() tab2hbox = QHBoxLayout() self.modelAllPedidos = services.get_all_pedidos_pandas() #[First Tab] - Set Filters self.proxyPedidoFilter = QSortFilterProxyModel() self.proxyPedidoFilter.setSourceModel(self.modelAllPedidos) self.proxyPedidoFilter.setFilterKeyColumn(0) self.proxyPedidoFilter.setSortCaseSensitivity(Qt.CaseSensitive) self.proxyPedidoFilterSecondLayer = QSortFilterProxyModel() self.proxyPedidoFilterSecondLayer.setSourceModel( self.proxyPedidoFilter) self.proxyPedidoFilterSecondLayer.setFilterKeyColumn(1) self.proxyPedidoFilterSecondLayer.setSortCaseSensitivity( Qt.CaseSensitive) self.tabv_pedidos.resizeColumnsToContents() self.tabv_pedidos.doubleClicked.connect(self.abrirItensDoPedido) self.tabv_pedidos.setSelectionMode(QAbstractItemView.SingleSelection) self.tabv_pedidos.setSelectionBehavior(QTableView.SelectRows) self.tabv_pedidos.setColumnWidth(2, 100) self.tabv_pedidos.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) tab2hbox.addWidget(self.tabv_pedidos) #[Connect Fields] searchProduto.textChanged.connect( lambda wildcard: self.proxyPedidoFilterSecondLayer. setFilterWildcard(wildcard)) searchPedido.textChanged.connect( lambda wildcard: self.proxyPedidoFilter.setFilterWildcard(wildcard )) self.tabv_pedidos.setModel(self.proxyPedidoFilterSecondLayer) #[First Tab] - Set Layout tabItensValidos = QWidget() tableItensValidos = QTableView() #tableItensValidos.horizontalHeader().sectionClicked.connect(your_callable) model = services.get_simafic_as_dataframe() tableItensValidos.setModel(model) tab1hbox = QHBoxLayout() #tab1hbox.setContentsMargins(5, 5, 5, 5) tableItensValidos.resizeColumnsToContents() tab1hbox.addWidget(tableItensValidos) tabItensValidos.setLayout(tab1hbox) layoutText = QHBoxLayout() layoutText.addWidget(searchPedido) layoutText.addWidget(searchProduto) layout.addLayout(layoutText) layout.addWidget( QLabel( "Para abrir mais opções sobre um pedido, clique duas vezes no item." )) self.tabv_pedidos.verticalHeader() self.tabv_pedidos.resizeColumnsToContents() layout.addWidget(self.tabv_pedidos) tab1ListaPedidos.setLayout(layout) #TODO: Agrupar items por pedido (Drop Down) | Auto-resize nas cells da TView #TODO: Adicionar self.topRightGroupBox.addTab(tab1ListaPedidos, "&Lista de Pedidos: ") self.topRightGroupBox.addTab(tabItensValidos, "&Lista de Itens:") def resumoDosItens(self): self.bottomLeftGroupBox = QGroupBox("Lista de Itens do Pedido nº") self.listDataItens = list() self.listaViewItens = QListWidget() verticalSpacer = QSpacerItem(40, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) self.resumoLayout = QGridLayout() self.resumoLayout.addItem(verticalSpacer) self.bottomLeftGroupBox.setLayout(self.resumoLayout) def abrirItensDoPedido(self, item): print( "[abrirIItensDoPedido] O Item foi selecionado através de um click na: {} x linha: {}" .format(item.column(), item.row())) pedido = self.tabv_pedidos.model().index(item.row(), 0).data() simafic = self.tabv_pedidos.model().index(item.row(), 1).data() print(pedido, simafic) self.pedidos_selecionados = pedido try: pedido_item = services.get_pedido_x_item(pedido, simafic) print(pedido_item) except (ValidationError, DBPedidosException) as error: error_dialog = QErrorMessage() error_dialog.setWindowTitle(error.errors) error_dialog.setWindowIcon(QIcon(main_icon)) error_dialog.showMessage(error.message) error_dialog.exec_() box = QMessageBox() box.setWindowIcon(QIcon(main_icon)) box.setWindowTitle("Pedido {} selecionado.".format( pedido_item.id_pedido)) box.setText("O que deseja fazer com o item {}?".format( pedido_item.cod_simafic)) box.setStandardButtons(QMessageBox.Open | QMessageBox.Discard | QMessageBox.Cancel) buttonOpen = box.button(QMessageBox.Open) buttonOpen.setText('Alterar') buttonDiscard = box.button(QMessageBox.Discard) buttonDiscard.setText('Excluir') buttonCancel = box.button(QMessageBox.Cancel) buttonCancel.setText('Cancelar') box.exec_() if box.clickedButton() == buttonOpen: print("Alterar...") self.cams = UpdateScreen(pedido_item, parent=self) self.cams.show() elif box.clickedButton() == buttonDiscard: print("Excluir ") self.confirmarExclusao(pedido_item) elif box.clickedButton() == buttonCancel: print("Cancelar ") def confirmarExclusao(self, pedido): box = QMessageBox() box.setWindowIcon(QIcon(main_icon)) box.setWindowTitle('Confirmação de Exclusão') box.setText( "Tem certeza que deseja excluir o item: {} do pedido {}?".format( pedido.cod_simafic, pedido.id_pedido)) box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) buttonYes = box.button(QMessageBox.Yes) buttonYes.setText("Excluir") buttonNo = box.button(QMessageBox.No) buttonNo.setText("Cancelar") box.exec_() if box.clickedButton() == buttonYes: self.excluirPedido(pedido) print("Pedido excluido") else: print("Exclusão cancelada") return def excluirPedido(self, pedido): try: services.excluirPedidoItem(pedido) self.update_model_tableview() except (ValidationError, DBPedidosException) as error: error_dialog = QErrorMessage() error_dialog.setWindowTitle(error.errors) error_dialog.setWindowIcon(QIcon(main_icon)) error_dialog.showMessage(error.message) error_dialog.exec_() def add_items(self): try: pedido, n_simafic, qtd_items = self.pedido.text( ), self.n_simafic.text(), self.qtd_items.text() print("Add Pedido: {} {} {}".format(pedido, n_simafic, qtd_items)) if services.validateCadastro(pedido, n_simafic, qtd_items): print("Add Pedido: {} {} {}".format(pedido, n_simafic, qtd_items)) mb = QMessageBox() mb.setIconPixmap(QPixmap('assets/check_icon_blue2')) mb.setWindowTitle("Sucesso") mb.setText( 'O pedido: {} foi criado com sucesso!'.format(pedido)) services.add_pedido(pedido, n_simafic, qtd_items) mb.exec_() self.update_model_tableview() self.limpar_pedidos() except (ValidationError, DBPedidosException) as error: error_dialog = QErrorMessage() error_dialog.setWindowTitle(error.errors) error_dialog.setWindowIcon(QIcon(main_icon)) error_dialog.showMessage(error.message) error_dialog.exec_() pass def update_model_tableview(self): self.modelAllPedidos.setDataFrame(services.get_all_pedidos_df()) self.topRightGroupBox.setCurrentIndex(0) def limpar_pedidos(self): self.n_simafic.clear() self.qtd_items.clear() self.pedido.clear() def goMainWindow(self): self.cams = mainView self.cams.show() self.close()
class Reservations(QWidget): """ Klasa odpowiedzialna za widget klienci """ def __init__(self, parent, db): super(QWidget, self).__init__(parent) self.parent = parent self.btn_mod = QPushButton('Zmiana statusu') self.btn_usun = QPushButton('Usuń') self.btn_dodaj = QPushButton('Dodaj') self.lbl_klient_ = QLabel('') self.lbl_usluga_ = QLabel('') self.lbl_termin_ = QDateTimeEdit() self.gb_layout = QVBoxLayout() self.kalendarz = QCalendarWidget() self.view_u = QTableView() self.view_k = QTableView() self.view_p = QTableView() self.view = QTableView() self.proxy_u = QSortFilterProxyModel(self) self.proxy_k = QSortFilterProxyModel(self) self.proxy_p = QSortFilterProxyModel(self) self.proxy = QSortFilterProxyModel(self) self.id_klient = -1 self.id_usluga = -1 self.id_pracownik = -1 self.id_rezerwacje = -1 self.data = None self.data_do = None self.dzien_tyg = 0 self.czas = [] # Lista składana przycisków godzin self.btn_godz = [QPushButton(str(i + 1)) for i in range(16)] # Parametry połączenia z bazą self.model_u = QSqlTableModel(self, db) self.model_k = QSqlTableModel(self, db) self.model_p = QSqlTableModel(self, db) self.model = QSqlTableModel(self, db) self.initUI() def initUI(self): """ Inicjuje UI """ self.view_u.setObjectName('Usługi') self.view_k.setObjectName('Klienci') self.view_p.setObjectName('Pracownicy') self.view.setObjectName('Rezerwacje') self.table_init_u() self.table_init_k() self.table_init_p() self.table_init() self.btn_mod.setDisabled(True) self.btn_usun.setDisabled(True) self.btn_dodaj.setDisabled(True) # Tworzenie kontrolek lbl_wysz_u = QLabel('Wyszukaj usługę:') lbl_wysz_k = QLabel('Wyszukaj klienta:') lbl_wysz_p = QLabel('Wyszukaj pracownika:') txt_wysz_u = QLineEdit(self) txt_wysz_k = QLineEdit(self) txt_wysz_p = QLineEdit(self) lbl_klient = QLabel('Klient:') lbl_usluga = QLabel('Usługa:') lbl_termin = QLabel('Termin:') sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.kalendarz.sizePolicy().hasHeightForWidth()) self.kalendarz.setSizePolicy(sizePolicy) # Tworzenie widoków centralbox = QHBoxLayout() hbox_wysz_u = QHBoxLayout() hbox_wysz_k = QHBoxLayout() hbox_wysz_p = QHBoxLayout() vbox_u = QVBoxLayout() vbox_k = QVBoxLayout() vbox_p = QVBoxLayout() vbox_right = QVBoxLayout() hbox_btn = QHBoxLayout() hbox_k = QHBoxLayout() hbox_u = QHBoxLayout() hbox_t = QHBoxLayout() vbox_cal = QVBoxLayout() groupbox = QGroupBox('Godziny:') groupbox.setLayout(self.gb_layout) hbox_left = QHBoxLayout() # Metody self.lbl_termin_.setCalendarWidget(self.kalendarz) self.lbl_termin_.setDate(self.kalendarz.selectedDate()) self.dzien_tyg = self.kalendarz.selectedDate().dayOfWeek() txt_wysz_u.textChanged.connect(self.searching_u) txt_wysz_k.textChanged.connect(self.searching_k) txt_wysz_p.textChanged.connect(self.searching_p) self.view_k.clicked.connect(lambda: self.clicked_table(self.view_k)) self.view_p.clicked.connect(lambda: self.clicked_table(self.view_p)) self.view_u.clicked.connect(lambda: self.clicked_table(self.view_u)) self.view.clicked.connect(lambda: self.clicked_table(self.view)) self.kalendarz.clicked.connect(self.show_data) self.btn_dodaj.clicked.connect(self.add) self.btn_mod.clicked.connect(self.modify) self.btn_usun.clicked.connect(self.remove) # Ustawianie widoków hbox_wysz_k.addWidget(lbl_wysz_k) hbox_wysz_k.addWidget(txt_wysz_k) hbox_wysz_p.addWidget(lbl_wysz_p) hbox_wysz_p.addWidget(txt_wysz_p) hbox_wysz_u.addWidget(lbl_wysz_u) hbox_wysz_u.addWidget(txt_wysz_u) vbox_u.addLayout(hbox_wysz_u) vbox_u.addWidget(self.view_u) vbox_k.addLayout(hbox_wysz_k) vbox_k.addWidget(self.view_k) vbox_p.addLayout(hbox_wysz_p) vbox_p.addWidget(self.view_p) vbox_right.addLayout(vbox_p) vbox_right.addLayout(vbox_u) vbox_right.addLayout(vbox_k) hbox_btn.addWidget(self.btn_usun) hbox_btn.addWidget(self.btn_mod) hbox_k.addWidget(lbl_klient) hbox_k.addWidget(self.lbl_klient_) hbox_u.addWidget(lbl_usluga) hbox_u.addWidget(self.lbl_usluga_) hbox_t.addWidget(lbl_termin) hbox_t.addWidget(self.lbl_termin_) vbox_cal.addWidget(self.kalendarz) vbox_cal.addLayout(hbox_u) vbox_cal.addLayout(hbox_t) vbox_cal.addLayout(hbox_k) self.view.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)) vbox_cal.addWidget(self.view) vbox_cal.addLayout(hbox_btn) vbox_cal.addWidget(self.btn_dodaj) hbox_left.addLayout(vbox_cal) hbox_left.addWidget(groupbox) centralbox.addLayout(hbox_left) centralbox.addLayout(vbox_right) self.setLayout(centralbox) self.show() def show_data(self): self.lbl_termin_.setDate(self.kalendarz.selectedDate()) self.dzien_tyg = self.kalendarz.selectedDate().dayOfWeek() self.clicked_table(self.view_p) def table_init_u(self): """ Inicjuje wygląd tabeli usługi """ # self.model_u.setTable('uslugi') query = QSqlQuery( 'SELECT uslugi.uslugi_id, uslugi.nazwa, uslugi.cena, date_format(uslugi.czas, "%H:%i") AS czas FROM uslugi NATURAL JOIN uzytkownik_usluga WHERE uzytkownik_usluga.uzytkownik_id = ' + str(self.id_pracownik) + ';') self.model_u.setQuery(query) # self.model_u.select() self.proxy_u.setSourceModel(self.model_u) naglowki = { 'uslugi_id': 'ID', 'nazwa': 'Nazwa', 'cena': 'Cena', "czas": 'Czas', } # Ustawianie nagłówków ilosc_kolumn = self.model_u.columnCount() for i in range(ilosc_kolumn): nazwa_kolumn = self.model_u.headerData(i, Qt.Horizontal) self.model_u.setHeaderData(i, Qt.Horizontal, naglowki[nazwa_kolumn]) self.view_u.setSizeAdjustPolicy( QAbstractScrollArea.AdjustToContentsOnFirstShow) self.view_u.setSortingEnabled(True) self.view_u.setAlternatingRowColors(True) # Wczytanie danych self.view_u.setModel(self.proxy_u) self.view_u.hideColumn(0) self.view_u.sortByColumn(1, Qt.AscendingOrder) self.view_u.setEditTriggers(QAbstractItemView.NoEditTriggers) def table_init_k(self): """ Inicjuje wygląd tabeli klienta """ self.model_k.setTable('klienci') query = QSqlQuery( 'SELECT klienci_id, imie, nazwisko, email, telefon, ulica, numer_mieszkania, miejscowosc, poczta FROM ' 'klienci;') self.model_k.setQuery(query) # self.model_k.select() self.proxy_k.setSourceModel(self.model_k) naglowki = { 'klienci_id': 'ID', 'imie': 'Imię', 'nazwisko': 'Nazwisko', "email": 'Email', 'telefon': 'Telefon', 'ulica': 'Ulica', 'numer_mieszkania': 'Numer mieszkania', 'miejscowosc': 'Miejscowosc', 'poczta': 'Kod pocztowy', } # Ustawianie nagłówków ilosc_kolumn = self.model_k.columnCount() for i in range(ilosc_kolumn): nazwa_kolumn = self.model_k.headerData(i, Qt.Horizontal) self.model_k.setHeaderData(i, Qt.Horizontal, naglowki[nazwa_kolumn]) self.view_k.setSizeAdjustPolicy( QAbstractScrollArea.AdjustToContentsOnFirstShow) self.view_k.setSortingEnabled(True) self.view_k.setAlternatingRowColors(True) # Wczytanie danych self.view_k.setModel(self.proxy_k) self.view_k.hideColumn(0) self.view_k.sortByColumn(1, Qt.AscendingOrder) self.view_k.setEditTriggers(QAbstractItemView.NoEditTriggers) def table_init_p(self): """ Inicjuje wygląd tabeli pracownik """ self.model_p.setTable('uzytkownik') query = QSqlQuery( 'SELECT uzytkownik_id, imie, nazwisko FROM uzytkownik WHERE ' 'pracownik = 1;') self.model_p.setQuery(query) # self.model_p.select() self.proxy_p.setSourceModel(self.model_p) naglowki = { 'uzytkownik_id': 'ID', 'imie': 'Imię', "nazwisko": 'Nazwisko', } # Ustawianie nagłówków ilosc_kolumn = self.model_p.columnCount() for i in range(ilosc_kolumn): nazwa_kolumn = self.model_p.headerData(i, Qt.Horizontal) self.model_p.setHeaderData(i, Qt.Horizontal, naglowki[nazwa_kolumn]) self.view_p.setSizeAdjustPolicy( QAbstractScrollArea.AdjustToContentsOnFirstShow) self.view_p.setSortingEnabled(True) self.view_p.setAlternatingRowColors(True) # Wczytanie danych self.view_p.setModel(self.proxy_p) self.view_p.hideColumn(0) self.view_p.sortByColumn(1, Qt.AscendingOrder) self.view_p.setEditTriggers(QAbstractItemView.NoEditTriggers) def table_init(self): """ Inicjuje wygląd tabeli """ query = QSqlQuery( 'SELECT wizyty.wizyty_id, CONCAT(klienci.imie, " ", klienci.nazwisko) AS klient, uslugi.nazwa, ' 'wizyty.rezerwacja_od, wizyty.rezerwacja_do, wizyty.status FROM klienci, uslugi NATURAL JOIN wizyty WHERE wizyty.rezerwacja_od > CURRENT_TIMESTAMP AND wizyty.uzytkownik_id = ' + str(self.id_pracownik) + ';') self.model.setQuery(query) self.proxy.setSourceModel(self.model) naglowki = { 'wizyty_id': 'ID', 'klient': 'Klient', 'nazwa': 'Usługa', 'rezerwacja_od': 'Rezerwacja od', "rezerwacja_do": 'Rezerwacja do', 'status': 'Status rezerwacji' } # Ustawianie nagłówków ilosc_kolumn = self.model.columnCount() for i in range(ilosc_kolumn): nazwa_kolumn = self.model.headerData(i, Qt.Horizontal) self.model.setHeaderData(i, Qt.Horizontal, naglowki[nazwa_kolumn]) self.view.setSizeAdjustPolicy( QAbstractScrollArea.AdjustToContentsOnFirstShow) self.view.setSortingEnabled(True) self.view.setAlternatingRowColors(True) # Wczytanie danych self.view.setModel(self.proxy) self.view.hideColumn(0) self.view.sortByColumn(4, Qt.AscendingOrder) self.view.setEditTriggers(QAbstractItemView.NoEditTriggers) def clicked_table(self, view): """ Metoda edytująca zaznaczone wiersze - Wstawia wartości z wierszy w odpowiednie pola """ index = (view.selectionModel().currentIndex()) if view.objectName() == 'Klienci': self.id_klient = index.sibling(index.row(), 0).data() self.lbl_klient_.setText('<b>' + index.sibling(index.row(), 1).data() + ' ' + index.sibling(index.row(), 2).data() + '</b>') elif view.objectName() == 'Usługi': self.id_usluga = index.sibling(index.row(), 0).data() self.lbl_usluga_.setText('<b>' + index.sibling(index.row(), 1).data() + '</b>') self.data_do = index.sibling(index.row(), 3).data() elif view.objectName() == 'Rezerwacje': self.id_rezerwacje = index.sibling(index.row(), 0).data() if self.id_rezerwacje > 0: self.btn_mod.setEnabled(True) self.btn_usun.setEnabled(True) elif view.objectName() == 'Pracownicy': self.id_pracownik = index.sibling(index.row(), 0).data() self.czas = [] for i in reversed(range(self.gb_layout.count())): self.gb_layout.itemAt(i).widget().setParent(None) czas = datetime.datetime(2000, 1, 1, 8, 0) dzien = { 1: ('pon_od', 'pon_do'), 2: ('wt_od', 'wt_do'), 3: ('sr_od', 'sr_do'), 4: ('czw_od', 'czw_do'), 5: ('pt_od', 'pt_do'), 6: ('sob_od', 'sob_do'), 7: ('', '') } query = 'SELECT date_format({}, "%H") AS g_start, date_format({}, "%H") AS g_stop FROM godziny WHERE ' \ 'uzytkownik_id = {};'.format(dzien[self.dzien_tyg][0], dzien[self.dzien_tyg][1], self.id_pracownik) wynik = query_to_db(query) if wynik: godzina_stop = (int(wynik[1]) - int(wynik[0])) * 2 godzina = int(wynik[0]) for btn in self.btn_godz: btn.setEnabled(True) else: godzina_stop = 0 godzina = 1 for btn in self.btn_godz: btn.setDisabled(True) minuta = 0 czas = datetime.time(godzina, minuta) for i in range(godzina_stop): self.btn_godz[i].setText(czas.strftime("%H:%M")) self.czas.append(czas) self.btn_godz[i].setObjectName(str(i)) self.gb_layout.addWidget(self.btn_godz[i]) if i % 2 != 0: godzina += 1 minuta = 0 else: minuta = 30 czas = datetime.time(godzina, minuta) QMetaObject.connectSlotsByName(self) # Czyszczenie, odświeżanie self.refresh() query = QSqlQuery( 'SELECT uslugi.uslugi_id, uslugi.nazwa, uslugi.cena, date_format(uslugi.czas, "%H:%i") AS czas FROM uslugi NATURAL JOIN uzytkownik_usluga WHERE uzytkownik_usluga.uzytkownik_id = ' + str(self.id_pracownik) + ';') self.model_u.setQuery(query) self.lbl_klient_.setText('') self.lbl_usluga_.setText('') if self.id_klient > 0 and self.id_pracownik > 0 and self.id_usluga > 0: self.btn_dodaj.setEnabled(True) def if_checked(self, txt, q, val=None): """ Sprawdza poprawność wprowadzonych damych. :param val: wartości do zapytania :param q: zapytanie query MySql :param txt: komunikat """ if self.id_klient < 0 and self.id_pracownik < 0 and self.id_usluga < 0 and self.data is None: msg = QMessageBox(self) msg.setIcon(QMessageBox.Warning) msg.setText(txt) msg.setWindowTitle("Popraw dane") msg.exec_() return False else: print('Trwa zmiana w bazie danych') if val: print('Połączenie') query_to_db(q, val) else: print('Transakcja') return transaction_to_db(q) return True def refresh(self): """ Odświeża widok tabeli rezerwacji """ # Odświeżanie widoku tabeli query = QSqlQuery( 'SELECT wizyty.wizyty_id, CONCAT(klienci.imie, " ", klienci.nazwisko) AS klient, uslugi.nazwa, ' 'wizyty.rezerwacja_od, wizyty.rezerwacja_do, wizyty.status FROM wizyty,klienci,uslugi WHERE wizyty.klienci_id= klienci.klienci_id AND wizyty.uslugi_id = uslugi.uslugi_id AND wizyty.rezerwacja_od > CURRENT_TIMESTAMP AND wizyty.uzytkownik_id = ' + str(self.id_pracownik) + ';') self.model.setQuery(query) self.view.reset() def add(self): """ Dodaje rezerwację do bazy danych i odświeża widok. """ tekst = 'Nie wprowadzono wszystkich danych' self.data = self.lbl_termin_.dateTime().toString(Qt.ISODate) self.data_do = datetime.datetime.fromisoformat( self.data) + datetime.timedelta( hours=datetime.time.fromisoformat(self.data_do).hour, minutes=datetime.time.fromisoformat(self.data_do).minute) # Dodanie nowego użytkownika query = 'INSERT INTO wizyty (klienci_id, uslugi_id, uzytkownik_id, rezerwacja_od, rezerwacja_do, ' \ 'status) VALUES (%s, %s, %s, %s, %s, %s);' val = (self.id_klient, self.id_usluga, self.id_pracownik, self.data, str(self.data_do.isoformat()), 'oczekuje') print(val) if self.if_checked(tekst, query, val): msg = QMessageBox(self) msg.setIcon(QMessageBox.Information) msg.setText('Rezerwacja została dodana do bazy danych') msg.setWindowTitle("Dodano nową rezerwację") msg.exec_() self.refresh() def modify(self): """ Zmienia status rezerwacji i odświeża widok. """ tekst = 'Nie wprowadzono wszystkich danych' items = ('Oczekuje', 'Wykonana', 'Rezygnacja') item, ok = QInputDialog.getItem(self, "Wybierz status", "Wybierz nowy status rezerwacji", items, 0, False) if ok and item: # Zmodyfikowanie statusu query = 'UPDATE wizyty SET status = %s WHERE wizyty_id = %s;' val = (item.lower(), self.id_rezerwacje) if self.if_checked(tekst, query, val): msg = QMessageBox(self) msg.setIcon(QMessageBox.Information) msg.setText('Status rezerwacji został zmodyfikowany') msg.setWindowTitle("Zmodyfikowano status") msg.exec_() self.refresh() def remove(self): """ Usuwa rezerwację z bazy danych """ test = 'Błąd! Nie można usunąć danej rezerwacji!' query = 'DELETE FROM wizyty WHERE wizyty_id = %s' val = (self.id_rezerwacje, ) ret = QMessageBox.question( self, 'Usuwanie rezerwacji', "Czy na pewno chcesz usunąć daną rezerwację klienta?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if ret == QMessageBox.Yes: if self.if_checked(test, query, val): msg = QMessageBox() msg.setIcon(QMessageBox.Information) msg.setText('Rezerwacja została usunięta') msg.setWindowTitle("Usunięto") msg.exec_() self.refresh() @pyqtSlot() def on_0_clicked(self): self.lbl_termin_.setTime(self.czas[0]) @pyqtSlot() def on_1_clicked(self): self.lbl_termin_.setTime(self.czas[1]) @pyqtSlot() def on_2_clicked(self): self.lbl_termin_.setTime(self.czas[2]) @pyqtSlot() def on_3_clicked(self): self.lbl_termin_.setTime(self.czas[3]) @pyqtSlot() def on_4_clicked(self): self.lbl_termin_.setTime(self.czas[4]) @pyqtSlot() def on_5_clicked(self): self.lbl_termin_.setTime(self.czas[5]) @pyqtSlot() def on_6_clicked(self): self.lbl_termin_.setTime(self.czas[6]) @pyqtSlot() def on_7_clicked(self): self.lbl_termin_.setTime(self.czas[7]) @pyqtSlot() def on_8_clicked(self): self.lbl_termin_.setTime(self.czas[8]) @pyqtSlot() def on_9_clicked(self): self.lbl_termin_.setTime(self.czas[9]) @pyqtSlot() def on_10_clicked(self): self.lbl_termin_.setTime(self.czas[10]) @pyqtSlot() def on_11_clicked(self): self.lbl_termin_.setTime(self.czas[11]) @pyqtSlot() def on_12_clicked(self): self.lbl_termin_.setTime(self.czas[12]) @pyqtSlot() def on_13_clicked(self): self.lbl_termin_.setTime(self.czas[13]) @pyqtSlot() def on_14_clicked(self): self.lbl_termin_.setTime(self.czas[14]) @pyqtSlot() def on_15_clicked(self): self.lbl_termin_.setTime(self.czas[15]) @pyqtSlot(str) def searching_u(self, text): """ Wyszukuje po wszystkich kolumnach tabeli :param text: """ search = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp) self.proxy_u.setFilterRegExp(search) # Odpowiedzialne za kolumnę, po której filtruje self.proxy_u.setFilterKeyColumn(-1) @pyqtSlot(str) def searching_k(self, text): """ Wyszukuje po wszystkich kolumnach tabeli :param text: """ search = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp) self.proxy_k.setFilterRegExp(search) # Odpowiedzialne za kolumnę, po której filtruje self.proxy_k.setFilterKeyColumn(-1) @pyqtSlot(str) def searching_p(self, text): """ Wyszukuje po wszystkich kolumnach tabeli :param text: """ search = QRegExp(text, Qt.CaseInsensitive, QRegExp.RegExp) self.proxy_p.setFilterRegExp(search) # Odpowiedzialne za kolumnę, po której filtruje self.proxy_p.setFilterKeyColumn(-1)
def insertRows(self, position, rows, parent=QModelIndex()): self.beginInsertRows(parent, position, position + rows - 1) self.endInsertRows() return True if __name__ == '__main__': app = QApplication(sys.argv) # How to apply sorting in a QTableView # Step 1: create the model for the QTableView tablemodel = SortingTableModel() tablemodel.insertRows(len(DATA), 1) # Step 2: create the sorter model sortermodel = QSortFilterProxyModel() sortermodel.setSourceModel(tablemodel) sortermodel.setFilterKeyColumn(3) # Step 3: setup the QTableView to enable sorting tableview = QTableView() tableview.setWindowTitle('Sorting QTableView') tableview.setModel(sortermodel) tableview.setSortingEnabled(True) tableview.sortByColumn(1, Qt.AscendingOrder) # sorting via 'Fruit' header tableview.show() sys.exit(app.exec())
class BreakPointViewer(QTreeView): """ Class implementing the Breakpoint viewer widget. Breakpoints will be shown with all their details. They can be modified through the context menu of this widget. @signal sourceFile(str, int) emitted to show the source of a breakpoint """ sourceFile = pyqtSignal(str, int) def __init__(self, parent=None): """ Constructor @param parent the parent (QWidget) """ super(BreakPointViewer, self).__init__(parent) self.setObjectName("BreakPointViewer") self.__model = None self.setItemsExpandable(False) self.setRootIsDecorated(False) self.setAlternatingRowColors(True) self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setWindowTitle(self.tr("Breakpoints")) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.__showContextMenu) self.doubleClicked.connect(self.__doubleClicked) self.__createPopupMenus() self.condHistory = [] self.fnHistory = [] self.fnHistory.append('') self.__loadRecent() def setModel(self, model): """ Public slot to set the breakpoint model. @param model reference to the breakpoint model (BreakPointModel) """ self.__model = model self.sortingModel = QSortFilterProxyModel() self.sortingModel.setDynamicSortFilter(True) self.sortingModel.setSourceModel(self.__model) super(BreakPointViewer, self).setModel(self.sortingModel) header = self.header() header.setSortIndicator(0, Qt.AscendingOrder) header.setSortIndicatorShown(True) header.setSectionsClickable(True) self.setSortingEnabled(True) self.__layoutDisplay() def __layoutDisplay(self): """ Private slot to perform a layout operation. """ self.__resizeColumns() self.__resort() def __resizeColumns(self): """ Private slot to resize the view when items get added, edited or deleted. """ self.header().resizeSections(QHeaderView.ResizeToContents) self.header().setStretchLastSection(True) def __resort(self): """ Private slot to resort the tree. """ self.model().sort(self.header().sortIndicatorSection(), self.header().sortIndicatorOrder()) def __toSourceIndex(self, index): """ Private slot to convert an index to a source index. @param index index to be converted (QModelIndex) @return mapped index (QModelIndex) """ return self.sortingModel.mapToSource(index) def __fromSourceIndex(self, sindex): """ Private slot to convert a source index to an index. @param sindex source index to be converted (QModelIndex) @return mapped index (QModelIndex) """ return self.sortingModel.mapFromSource(sindex) def __setRowSelected(self, index, selected=True): """ Private slot to select a complete row. @param index index determining the row to be selected (QModelIndex) @param selected flag indicating the action (bool) """ if not index.isValid(): return if selected: flags = QItemSelectionModel.SelectionFlags( QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) else: flags = QItemSelectionModel.SelectionFlags( QItemSelectionModel.Deselect | QItemSelectionModel.Rows) self.selectionModel().select(index, flags) def __createPopupMenus(self): """ Private method to generate the popup menus. """ self.menu = QMenu() self.menu.addAction(self.tr("Add"), self.__addBreak) self.menu.addAction(self.tr("Edit..."), self.__editBreak) self.menu.addSeparator() self.menu.addAction(self.tr("Enable"), self.__enableBreak) self.menu.addAction(self.tr("Enable all"), self.__enableAllBreaks) self.menu.addSeparator() self.menu.addAction(self.tr("Disable"), self.__disableBreak) self.menu.addAction(self.tr("Disable all"), self.__disableAllBreaks) self.menu.addSeparator() self.menu.addAction(self.tr("Delete"), self.__deleteBreak) self.menu.addAction(self.tr("Delete all"), self.__deleteAllBreaks) self.menu.addSeparator() self.menu.addAction(self.tr("Goto"), self.__showSource) self.menu.addSeparator() self.menu.addAction(self.tr("Configure..."), self.__configure) self.backMenuActions = {} self.backMenu = QMenu() self.backMenu.addAction(self.tr("Add"), self.__addBreak) self.backMenuActions["EnableAll"] = self.backMenu.addAction( self.tr("Enable all"), self.__enableAllBreaks) self.backMenuActions["DisableAll"] = self.backMenu.addAction( self.tr("Disable all"), self.__disableAllBreaks) self.backMenuActions["DeleteAll"] = self.backMenu.addAction( self.tr("Delete all"), self.__deleteAllBreaks) self.backMenu.aboutToShow.connect(self.__showBackMenu) self.backMenu.addSeparator() self.backMenu.addAction(self.tr("Configure..."), self.__configure) self.multiMenu = QMenu() self.multiMenu.addAction(self.tr("Add"), self.__addBreak) self.multiMenu.addSeparator() self.multiMenu.addAction(self.tr("Enable selected"), self.__enableSelectedBreaks) self.multiMenu.addAction(self.tr("Enable all"), self.__enableAllBreaks) self.multiMenu.addSeparator() self.multiMenu.addAction(self.tr("Disable selected"), self.__disableSelectedBreaks) self.multiMenu.addAction(self.tr("Disable all"), self.__disableAllBreaks) self.multiMenu.addSeparator() self.multiMenu.addAction(self.tr("Delete selected"), self.__deleteSelectedBreaks) self.multiMenu.addAction(self.tr("Delete all"), self.__deleteAllBreaks) self.multiMenu.addSeparator() self.multiMenu.addAction(self.tr("Configure..."), self.__configure) def __showContextMenu(self, coord): """ Private slot to show the context menu. @param coord the position of the mouse pointer (QPoint) """ cnt = self.__getSelectedItemsCount() if cnt <= 1: index = self.indexAt(coord) if index.isValid(): cnt = 1 self.__setRowSelected(index) coord = self.mapToGlobal(coord) if cnt > 1: self.multiMenu.popup(coord) elif cnt == 1: self.menu.popup(coord) else: self.backMenu.popup(coord) def __clearSelection(self): """ Private slot to clear the selection. """ for index in self.selectedIndexes(): self.__setRowSelected(index, False) def __addBreak(self): """ Private slot to handle the add breakpoint context menu entry. """ from .EditBreakpointDialog import EditBreakpointDialog dlg = EditBreakpointDialog((self.fnHistory[0], None), None, self.condHistory, self, modal=1, addMode=1, filenameHistory=self.fnHistory) if dlg.exec_() == QDialog.Accepted: fn, line, cond, temp, enabled, count = dlg.getAddData() if fn is not None: if fn in self.fnHistory: self.fnHistory.remove(fn) self.fnHistory.insert(0, fn) if cond: if cond in self.condHistory: self.condHistory.remove(cond) self.condHistory.insert(0, cond) self.__saveRecent() self.__model.addBreakPoint(fn, line, (cond, temp, enabled, count)) self.__resizeColumns() self.__resort() def __doubleClicked(self, index): """ Private slot to handle the double clicked signal. @param index index of the entry that was double clicked (QModelIndex) """ if index.isValid(): self.__editBreakpoint(index) def __editBreak(self): """ Private slot to handle the edit breakpoint context menu entry. """ index = self.currentIndex() if index.isValid(): self.__editBreakpoint(index) def __editBreakpoint(self, index): """ Private slot to edit a breakpoint. @param index index of breakpoint to be edited (QModelIndex) """ sindex = self.__toSourceIndex(index) if sindex.isValid(): bp = self.__model.getBreakPointByIndex(sindex) if not bp: return fn, line, cond, temp, enabled, count = bp[:6] from .EditBreakpointDialog import EditBreakpointDialog dlg = EditBreakpointDialog((fn, line), (cond, temp, enabled, count), self.condHistory, self, modal=True) if dlg.exec_() == QDialog.Accepted: cond, temp, enabled, count = dlg.getData() if cond: if cond in self.condHistory: self.condHistory.remove(cond) self.condHistory.insert(0, cond) self.__saveRecent() self.__model.setBreakPointByIndex(sindex, fn, line, (cond, temp, enabled, count)) self.__resizeColumns() self.__resort() def __setBpEnabled(self, index, enabled): """ Private method to set the enabled status of a breakpoint. @param index index of breakpoint to be enabled/disabled (QModelIndex) @param enabled flag indicating the enabled status to be set (boolean) """ sindex = self.__toSourceIndex(index) if sindex.isValid(): self.__model.setBreakPointEnabledByIndex(sindex, enabled) def __enableBreak(self): """ Private slot to handle the enable breakpoint context menu entry. """ index = self.currentIndex() self.__setBpEnabled(index, True) self.__resizeColumns() self.__resort() def __enableAllBreaks(self): """ Private slot to handle the enable all breakpoints context menu entry. """ index = self.model().index(0, 0) while index.isValid(): self.__setBpEnabled(index, True) index = self.indexBelow(index) self.__resizeColumns() self.__resort() def __enableSelectedBreaks(self): """ Private slot to handle the enable selected breakpoints context menu entry. """ for index in self.selectedIndexes(): if index.column() == 0: self.__setBpEnabled(index, True) self.__resizeColumns() self.__resort() def __disableBreak(self): """ Private slot to handle the disable breakpoint context menu entry. """ index = self.currentIndex() self.__setBpEnabled(index, False) self.__resizeColumns() self.__resort() def __disableAllBreaks(self): """ Private slot to handle the disable all breakpoints context menu entry. """ index = self.model().index(0, 0) while index.isValid(): self.__setBpEnabled(index, False) index = self.indexBelow(index) self.__resizeColumns() self.__resort() def __disableSelectedBreaks(self): """ Private slot to handle the disable selected breakpoints context menu entry. """ for index in self.selectedIndexes(): if index.column() == 0: self.__setBpEnabled(index, False) self.__resizeColumns() self.__resort() def __deleteBreak(self): """ Private slot to handle the delete breakpoint context menu entry. """ index = self.currentIndex() sindex = self.__toSourceIndex(index) if sindex.isValid(): self.__model.deleteBreakPointByIndex(sindex) def __deleteAllBreaks(self): """ Private slot to handle the delete all breakpoints context menu entry. """ self.__model.deleteAll() def __deleteSelectedBreaks(self): """ Private slot to handle the delete selected breakpoints context menu entry. """ idxList = [] for index in self.selectedIndexes(): sindex = self.__toSourceIndex(index) if sindex.isValid() and index.column() == 0: idxList.append(sindex) self.__model.deleteBreakPoints(idxList) def __showSource(self): """ Private slot to handle the goto context menu entry. """ index = self.currentIndex() sindex = self.__toSourceIndex(index) bp = self.__model.getBreakPointByIndex(sindex) if not bp: return fn, line = bp[:2] self.sourceFile.emit(fn, line) def highlightBreakpoint(self, fn, lineno): """ Public slot to handle the clientLine signal. @param fn filename of the breakpoint (string) @param lineno line number of the breakpoint (integer) """ sindex = self.__model.getBreakPointIndex(fn, lineno) if sindex.isValid(): return index = self.__fromSourceIndex(sindex) if index.isValid(): self.__clearSelection() self.__setRowSelected(index, True) def handleResetUI(self): """ Public slot to reset the breakpoint viewer. """ self.__clearSelection() def __showBackMenu(self): """ Private slot to handle the aboutToShow signal of the background menu. """ if self.model().rowCount() == 0: self.backMenuActions["EnableAll"].setEnabled(False) self.backMenuActions["DisableAll"].setEnabled(False) self.backMenuActions["DeleteAll"].setEnabled(False) else: self.backMenuActions["EnableAll"].setEnabled(True) self.backMenuActions["DisableAll"].setEnabled(True) self.backMenuActions["DeleteAll"].setEnabled(True) def __getSelectedItemsCount(self): """ Private method to get the count of items selected. @return count of items selected (integer) """ count = len(self.selectedIndexes()) // (self.__model.columnCount() - 1) # column count is 1 greater than selectable return count def __configure(self): """ Private method to open the configuration dialog. """ e5App().getObject("UserInterface").showPreferences( "debuggerGeneralPage") def __loadRecent(self): """ Private method to load the recently used file names. """ Preferences.Prefs.rsettings.sync() # load recently used file names self.fnHistory = [] self.fnHistory.append('') rs = Preferences.Prefs.rsettings.value(recentNameBreakpointFiles) if rs is not None: recent = [ f for f in Preferences.toList(rs) if QFileInfo(f).exists() ] self.fnHistory.extend( recent[:Preferences.getDebugger("RecentNumber")]) # load recently entered condition expressions self.condHistory = [] rs = Preferences.Prefs.rsettings.value(recentNameBreakpointConditions) if rs is not None: self.condHistory = Preferences.toList( rs)[:Preferences.getDebugger("RecentNumber")] def __saveRecent(self): """ Private method to save the list of recently used file names. """ recent = [f for f in self.fnHistory if f] Preferences.Prefs.rsettings.setValue(recentNameBreakpointFiles, recent) Preferences.Prefs.rsettings.setValue(recentNameBreakpointConditions, self.condHistory) Preferences.Prefs.rsettings.sync()
class OWGeneSets(OWWidget): name = "Gene Sets" description = "" icon = "icons/OWGeneSets.svg" priority = 9 want_main_area = True # settings selected_organism = Setting(0) auto_commit = Setting(True) auto_apply = Setting(True) gene_col_index = ContextSetting(0) use_attr_names = ContextSetting(False) class Inputs: genes = Input("Genes", Table) class Outputs: matched_genes = Output("Matched Genes", Table) class Information(OWWidget.Information): pass class Error(OWWidget.Error): cant_reach_host = Msg("Host orange.biolab.si is unreachable.") cant_load_organisms = Msg( "No available organisms, please check your connection.") def __init__(self): super().__init__() # commit self.commit_button = None # progress bar self.progress_bar = None self.progress_bar_iterations = None # data self.input_data = None self.tax_id = None self.input_genes = None self.mapped_genes = None self.organisms = list() self.input_info = None self.column_candidates = [] # filter self.lineEdit_filter = None self.search_pattern = '' self.organism_select_combobox = None # data model view self.data_view = None self.data_model = None # gene matcher NCBI self.gene_matcher = None # filter proxy model self.filter_proxy_model = None # hierarchy widget self.hierarchy_widget = None self.hierarchy_state = None # input options self.gene_columns = None self.gene_column_combobox = None self.attr_names_checkbox = None # threads self.threadpool = QThreadPool(self) self.workers = None # gui self.setup_gui() self._get_available_organisms() # self.handle_input(self.input_genes) # self.on_organism_change() def _progress_advance(self): # GUI should be updated in main thread. That's why we are calling advance method here if self.progress_bar: self.progress_bar.advance() def _get_selected_organism(self): return self.organisms[self.selected_organism] def _get_available_organisms(self): available_organism = sorted([(tax_id, taxonomy.name(tax_id)) for tax_id in taxonomy.common_taxids()], key=lambda x: x[1]) self.organisms = [tax_id[0] for tax_id in available_organism] self.organism_select_combobox.addItems( [tax_id[1] for tax_id in available_organism]) def _gene_names_from_table(self): """ Extract and return gene names from `Orange.data.Table`. """ self.input_genes = [] if self.input_data: if self.use_attr_names: self.input_genes = [ str(attr.name).strip() for attr in self.input_data.domain.attributes ] elif self.gene_columns: column = self.gene_columns[self.gene_col_index] self.input_genes = [ str(e[column]) for e in self.input_data if not np.isnan(e[column]) ] def _update_gene_matcher(self): self._gene_names_from_table() if self.gene_matcher: self.gene_matcher.genes = self.input_genes self.gene_matcher.organism = self._get_selected_organism() def on_input_option_change(self): self._update_gene_matcher() self.match_genes() @Inputs.genes def handle_input(self, data): if data: self.input_data = data self.gene_matcher = gene.GeneMatcher(self._get_selected_organism()) self.gene_column_combobox.clear() self.column_candidates = [ attr for attr in data.domain.variables + data.domain.metas if isinstance(attr, (StringVariable, DiscreteVariable)) ] for var in self.column_candidates: self.gene_column_combobox.addItem(*attributeItem(var)) self.tax_id = str(data_hints.get_hint(self.input_data, TAX_ID)) self.use_attr_names = data_hints.get_hint( self.input_data, GENE_NAME, default=self.use_attr_names) self.gene_col_index = min(self.gene_col_index, len(self.column_candidates) - 1) if self.tax_id in self.organisms: self.selected_organism = self.organisms.index(self.tax_id) self.on_input_option_change() def update_info_box(self): info_string = '' if self.input_genes: info_string += '{} unique gene names on input.\n'.format( len(self.input_genes)) mapped = self.gene_matcher.get_known_genes() if mapped: ratio = (len(mapped) / len(self.input_genes)) * 100 info_string += '{} ({:.2f}%) gene names matched.\n'.format( len(mapped), ratio) else: info_string += 'No genes on input.\n' self.input_info.setText(info_string) def match_genes(self): if self.gene_matcher: # init progress bar self.progress_bar = ProgressBar(self, iterations=len( self.gene_matcher.genes)) # status message self.setStatusMessage('gene matcher running') worker = Worker(self.gene_matcher.run_matcher, progress_callback=True) worker.signals.progress.connect(self._progress_advance) worker.signals.finished.connect(self.handle_matcher_results) # move download process to worker thread self.threadpool.start(worker) def handle_matcher_results(self): assert threading.current_thread() == threading.main_thread() if self.progress_bar: self.progress_bar.finish() self.setStatusMessage('') if self.gene_matcher.map_input_to_ncbi(): self.download_gene_sets() self.update_info_box() else: # reset gene sets self.init_item_model() self.update_info_box() def on_gene_sets_download(self, result): # make sure this happens in the main thread. # Qt insists that widgets be created within the GUI(main) thread. assert threading.current_thread() == threading.main_thread() self.progress_bar.finish() self.setStatusMessage('') tax_id, sets = result self.set_hierarchy_model(self.hierarchy_widget, *hierarchy_tree(tax_id, sets)) self.organism_select_combobox.setEnabled(True) # re-enable combobox self.update_info_box() self.mapped_genes = self.gene_matcher.map_input_to_ncbi() self.workers = defaultdict(list) self.progress_bar_iterations = dict() for selected_hierarchy in [*self.get_hierarchies()]: gene_sets = geneset.load_gene_sets(selected_hierarchy) worker = Worker(get_collections, gene_sets, self.mapped_genes, progress_callback=True, partial_result=True) worker.signals.error.connect(self.handle_error) worker.signals.finished.connect(self.handle_worker_finished) worker.signals.progress.connect(self._progress_advance) worker.signals.partial_result.connect(self.populate_data_model) worker.setAutoDelete(False) self.workers[selected_hierarchy] = worker self.progress_bar_iterations[selected_hierarchy] = len(gene_sets) def handle_worker_finished(self): # We check if all workers have completed. If not, continue # dirty hax, is this ok? if self.progress_bar and self.progress_bar.widget.progressBarValue == 100: self.progress_bar.finish() self.setStatusMessage('') self.hierarchy_widget.setDisabled(False) # adjust column width for i in range(len(DATA_HEADER_LABELS) - 1): self.data_view.resizeColumnToContents(i) self.filter_proxy_model.setSourceModel(self.data_model) def populate_data_model(self, partial_result): assert threading.current_thread() == threading.main_thread() if partial_result: self.data_model.appendRow(partial_result) def set_hierarchy_model(self, model, tax_id, sets): # TODO: maybe optimize this code? for key, value in sets.items(): item = QTreeWidgetItem(model, [key]) item.setFlags(item.flags() & (Qt.ItemIsUserCheckable | ~Qt.ItemIsSelectable | Qt.ItemIsEnabled)) # item.setDisabled(True) item.setData(0, Qt.CheckStateRole, Qt.Unchecked) item.setExpanded(True) item.tax_id = tax_id item.hierarchy = key if value: item.setFlags(item.flags() | Qt.ItemIsTristate) self.set_hierarchy_model(item, tax_id, value) else: if item.parent(): item.hierarchy = ((item.parent().hierarchy, key), tax_id) if not item.childCount() and not item.parent(): item.hierarchy = ((key, ), tax_id) def download_gene_sets(self): tax_id = self._get_selected_organism() self.Error.clear() # do not allow user to change organism when download task is running self.organism_select_combobox.setEnabled(False) # reset hierarchy widget state self.hierarchy_widget.clear() # clear data view self.init_item_model() # get all gene sets for selected organism gene_sets = geneset.list_all(organism=tax_id) # init progress bar self.progress_bar = ProgressBar(self, iterations=len(gene_sets) * 100) # status message self.setStatusMessage('downloading sets') worker = Worker(download_gene_sets, gene_sets, progress_callback=True) worker.signals.progress.connect(self._progress_advance) worker.signals.result.connect(self.on_gene_sets_download) worker.signals.error.connect(self.handle_error) # move download process to worker thread self.threadpool.start(worker) def display_gene_sets(self): self.init_item_model() self.hierarchy_widget.setDisabled(True) only_selected_hier = [*self.get_hierarchies(only_selected=True)] # init progress bar iterations = sum([ self.progress_bar_iterations[hier] for hier in only_selected_hier ]) self.progress_bar = ProgressBar(self, iterations=iterations) self.setStatusMessage('displaying gene sets') if not only_selected_hier: self.progress_bar.finish() self.setStatusMessage('') self.hierarchy_widget.setDisabled(False) return for selected_hierarchy in only_selected_hier: self.threadpool.start(self.workers[selected_hierarchy]) def handle_error(self, ex): self.progress_bar.finish() self.setStatusMessage('') if isinstance(ex, ConnectionError): self.organism_select_combobox.setEnabled( True) # re-enable combobox self.Error.cant_reach_host() print(ex) def get_hierarchies(self, **kwargs): """ return selected hierarchy """ only_selected = kwargs.get('only_selected', None) sets_to_display = list() if only_selected: iterator = QTreeWidgetItemIterator(self.hierarchy_widget, QTreeWidgetItemIterator.Checked) else: iterator = QTreeWidgetItemIterator(self.hierarchy_widget) while iterator.value(): # note: if hierarchy value is not a tuple, then this is just top level qTreeWidgetItem that # holds subcategories. We don't want to display all sets from category if type(iterator.value().hierarchy) is not str: if not only_selected: sets_to_display.append(iterator.value().hierarchy) else: if not iterator.value().isDisabled(): sets_to_display.append(iterator.value().hierarchy) iterator += 1 return sets_to_display def commit(self): selection_model = self.data_view.selectionModel() if selection_model: # genes_from_set = selection_model.selectedRows(GENES) matched_genes = selection_model.selectedRows(MATCHED) if matched_genes and self.input_genes: genes = [ model_index.data(Qt.UserRole) for model_index in matched_genes ] output_genes = [ gene_name for gene_name in list(set.union(*genes)) ] input_to_ncbi = self.gene_matcher.map_input_to_ncbi() ncbi_to_input = { ncbi_id: input_name for input_name, ncbi_id in self.gene_matcher.map_input_to_ncbi().items() } if self.use_attr_names: selected = [ self.input_data.domain[ncbi_to_input[output_gene]] for output_gene in output_genes ] domain = Domain(selected, self.input_data.domain.class_vars, self.input_data.domain.metas) new_data = self.input_data.from_table( domain, self.input_data) self.Outputs.matched_genes.send(new_data) elif self.column_candidates: column = self.column_candidates[self.gene_col_index] selected_rows = [] for row_index, row in enumerate(self.input_data): if str(row[column]) in input_to_ncbi.keys( ) and input_to_ncbi[str(row[column])] in output_genes: selected_rows.append(row_index) if selected_rows: selected = self.input_data[selected_rows] else: selected = None self.Outputs.matched_genes.send(selected) def setup_gui(self): # control area info_box = vBox(self.controlArea, 'Input info') self.input_info = widgetLabel(info_box) organism_box = vBox(self.controlArea, 'Organisms') self.organism_select_combobox = comboBox( organism_box, self, 'selected_organism', callback=self.on_input_option_change) # Selection of genes attribute box = widgetBox(self.controlArea, 'Gene attribute') self.gene_columns = itemmodels.VariableListModel(parent=self) self.gene_column_combobox = comboBox( box, self, 'gene_col_index', callback=self.on_input_option_change) self.gene_column_combobox.setModel(self.gene_columns) self.attr_names_checkbox = checkBox( box, self, 'use_attr_names', 'Use attribute names', disables=[(-1, self.gene_column_combobox)], callback=self.on_input_option_change) self.gene_column_combobox.setDisabled(bool(self.use_attr_names)) hierarchy_box = widgetBox(self.controlArea, "Entity Sets") self.hierarchy_widget = QTreeWidget(self) self.hierarchy_widget.setEditTriggers(QTreeView.NoEditTriggers) self.hierarchy_widget.setHeaderLabels(HIERARCHY_HEADER_LABELS) self.hierarchy_widget.itemClicked.connect(self.display_gene_sets) hierarchy_box.layout().addWidget(self.hierarchy_widget) self.commit_button = auto_commit(self.controlArea, self, "auto_commit", "&Commit", box=False) # rubber(self.controlArea) # main area self.filter_proxy_model = QSortFilterProxyModel(self.data_view) self.filter_proxy_model.setFilterKeyColumn(3) self.data_view = QTreeView() self.data_view.setModel(self.filter_proxy_model) self.data_view.setAlternatingRowColors(True) self.data_view.setSortingEnabled(True) self.data_view.setSelectionMode(QTreeView.ExtendedSelection) self.data_view.setEditTriggers(QTreeView.NoEditTriggers) self.data_view.viewport().setMouseTracking(True) self.data_view.setItemDelegateForColumn( TERM, LinkStyledItemDelegate(self.data_view)) self.data_view.selectionModel().selectionChanged.connect(self.commit) self.lineEdit_filter = lineEdit(self.mainArea, self, 'search_pattern', 'Filter gene sets:') self.lineEdit_filter.setPlaceholderText('search pattern ...') self.lineEdit_filter.textChanged.connect( self.filter_proxy_model.setFilterRegExp) self.mainArea.layout().addWidget(self.data_view) def init_item_model(self): if self.data_model: self.data_model.clear() self.filter_proxy_model.setSourceModel(None) else: self.data_model = QStandardItemModel() self.data_model.setSortRole(Qt.UserRole) self.data_model.setHorizontalHeaderLabels(DATA_HEADER_LABELS) def sizeHint(self): return QSize(1280, 960)
class TERuleQueryTab(SEToolsWidget, QScrollArea): """A Type Enforcement rule query.""" def __init__(self, parent, policy, perm_map): super(TERuleQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = TERuleQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.terulequery").removeHandler(self.handler) def setupUi(self): self.load_ui("terulequery.ui") # set up source/target autocompletion typeattr_completion_list = [str(t) for t in self.policy.types()] typeattr_completion_list.extend(str(a) for a in self.policy.typeattributes()) typeattr_completer_model = QStringListModel(self) typeattr_completer_model.setStringList(sorted(typeattr_completion_list)) self.typeattr_completion = QCompleter() self.typeattr_completion.setModel(typeattr_completer_model) self.source.setCompleter(self.typeattr_completion) self.target.setCompleter(self.typeattr_completion) # set up default autocompletion type_completion_list = [str(t) for t in self.policy.types()] type_completer_model = QStringListModel(self) type_completer_model.setStringList(sorted(type_completion_list)) self.type_completion = QCompleter() self.type_completion.setModel(type_completer_model) self.default_type.setCompleter(self.type_completion) # setup indications of errors on source/target/default self.orig_palette = self.source.palette() self.error_palette = self.source.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_source_error() self.clear_target_error() self.clear_default_error() self.clear_xperm_error() # populate class list self.class_model = SEToolsListModel(self) self.class_model.item_list = sorted(self.policy.classes()) self.tclass.setModel(self.class_model) # populate perm list self.perms_model = PermListModel(self, self.policy) self.perms.setModel(self.perms_model) # populate bool list self.bool_model = SEToolsListModel(self) self.bool_model.item_list = sorted(self.policy.bools()) self.bool_criteria.setModel(self.bool_model) # set up results self.table_results_model = TERuleTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(0, Qt.AscendingOrder) # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.terulequery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.set_source_regex(self.source_regex.isChecked()) self.set_target_regex(self.target_regex.isChecked()) self.set_default_regex(self.default_regex.isChecked()) self.toggle_xperm_criteria() self.criteria_frame.setHidden(not self.criteria_expander.isChecked()) self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.buttonBox.clicked.connect(self.run) self.allowxperm.toggled.connect(self.toggle_xperm_criteria) self.auditallowxperm.toggled.connect(self.toggle_xperm_criteria) self.neverallowxperm.toggled.connect(self.toggle_xperm_criteria) self.dontauditxperm.toggled.connect(self.toggle_xperm_criteria) self.clear_ruletypes.clicked.connect(self.clear_all_ruletypes) self.all_ruletypes.clicked.connect(self.set_all_ruletypes) self.source.textEdited.connect(self.clear_source_error) self.source.editingFinished.connect(self.set_source) self.source_regex.toggled.connect(self.set_source_regex) self.target.textEdited.connect(self.clear_target_error) self.target.editingFinished.connect(self.set_target) self.target_regex.toggled.connect(self.set_target_regex) self.tclass.selectionModel().selectionChanged.connect(self.set_tclass) self.invert_class.clicked.connect(self.invert_tclass_selection) self.perms.selectionModel().selectionChanged.connect(self.set_perms) self.invert_perms.clicked.connect(self.invert_perms_selection) self.xperms.textEdited.connect(self.clear_xperm_error) self.xperms.editingFinished.connect(self.set_xperm) self.default_type.textEdited.connect(self.clear_default_error) self.default_type.editingFinished.connect(self.set_default_type) self.default_regex.toggled.connect(self.set_default_regex) self.bool_criteria.selectionModel().selectionChanged.connect(self.set_bools) # # Ruletype criteria # def _set_ruletypes(self, value): self.allow.setChecked(value) self.allowxperm.setChecked(value) self.auditallow.setChecked(value) self.auditallowxperm.setChecked(value) self.neverallow.setChecked(value) self.neverallowxperm.setChecked(value) self.dontaudit.setChecked(value) self.dontauditxperm.setChecked(value) self.type_transition.setChecked(value) self.type_member.setChecked(value) self.type_change.setChecked(value) def set_all_ruletypes(self): self._set_ruletypes(True) def clear_all_ruletypes(self): self._set_ruletypes(False) # # Source criteria # def clear_source_error(self): self.source.setToolTip("Match the source type/attribute of the rule.") self.source.setPalette(self.orig_palette) def set_source(self): try: self.query.source = self.source.text() except Exception as ex: self.log.error("Source type/attribute error: {0}".format(ex)) self.source.setToolTip("Error: " + str(ex)) self.source.setPalette(self.error_palette) def set_source_regex(self, state): self.log.debug("Setting source_regex {0}".format(state)) self.query.source_regex = state self.clear_source_error() self.set_source() # # Target criteria # def clear_target_error(self): self.target.setToolTip("Match the target type/attribute of the rule.") self.target.setPalette(self.orig_palette) def set_target(self): try: self.query.target = self.target.text() except Exception as ex: self.log.error("Target type/attribute error: {0}".format(ex)) self.target.setToolTip("Error: " + str(ex)) self.target.setPalette(self.error_palette) def set_target_regex(self, state): self.log.debug("Setting target_regex {0}".format(state)) self.query.target_regex = state self.clear_target_error() self.set_target() # # Class criteria # def set_tclass(self): selected_classes = [] for index in self.tclass.selectionModel().selectedIndexes(): selected_classes.append(self.class_model.data(index, Qt.UserRole)) self.query.tclass = selected_classes self.perms_model.set_classes(selected_classes) def invert_tclass_selection(self): invert_list_selection(self.tclass.selectionModel()) # # Permissions criteria # def set_perms(self): selected_perms = [] for index in self.perms.selectionModel().selectedIndexes(): selected_perms.append(self.perms_model.data(index, Qt.UserRole)) self.query.perms = selected_perms def invert_perms_selection(self): invert_list_selection(self.perms.selectionModel()) # # Extended permission criteria # def toggle_xperm_criteria(self): mode = any((self.allowxperm.isChecked(), self.auditallowxperm.isChecked(), self.neverallowxperm.isChecked(), self.dontauditxperm.isChecked())) self.xperms.setEnabled(mode) self.xperms_equal.setEnabled(mode) def clear_xperm_error(self): self.xperms.setToolTip("Match the extended permissions of the rule. Comma-separated " "permissions or ranges of permissions.") self.xperms.setPalette(self.orig_palette) def set_xperm(self): xperms = [] try: text = self.xperms.text() if text: for item in self.xperms.text().split(","): rng = item.split("-") if len(rng) == 2: xperms.append((int(rng[0], base=16), int(rng[1], base=16))) elif len(rng) == 1: xperms.append((int(rng[0], base=16), int(rng[0], base=16))) else: raise ValueError("Enter an extended permission or extended permission " "range, e.g. 0x5411 or 0x8800-0x88ff.") self.query.xperms = xperms else: self.query.xperms = None except Exception as ex: self.log.error("Extended permissions error: {0}".format(ex)) self.xperms.setToolTip("Error: " + str(ex)) self.xperms.setPalette(self.error_palette) # # Default criteria # def clear_default_error(self): self.default_type.setToolTip("Match the default type the rule.") self.default_type.setPalette(self.orig_palette) def set_default_type(self): self.query.default_regex = self.default_regex.isChecked() try: self.query.default = self.default_type.text() except Exception as ex: self.log.error("Default type error: {0}".format(ex)) self.default_type.setToolTip("Error: " + str(ex)) self.default_type.setPalette(self.error_palette) def set_default_regex(self, state): self.log.debug("Setting default_regex {0}".format(state)) self.query.default_regex = state self.clear_default_error() self.set_default_type() # # Boolean criteria # def set_bools(self): selected_bools = [] for index in self.bool_criteria.selectionModel().selectedIndexes(): selected_bools.append(self.bool_model.data(index, Qt.UserRole)) self.query.boolean = selected_bools # # Save/Load tab # def save(self): """Return a dictionary of settings.""" settings = {} save_checkboxes(self, settings, ["criteria_expander", "notes_expander", "allow", "allowxperm", "auditallow", "auditallowxperm", "neverallow", "neverallowxperm", "dontaudit", "dontauditxperm", "type_transition", "type_change", "type_member", "source_indirect", "source_regex", "target_indirect", "target_regex", "perms_subset", "xperms_equal", "default_regex", "bools_equal"]) save_lineedits(self, settings, ["source", "target", "xperms", "default_type"]) save_listviews(self, settings, ["tclass", "perms", "bool_criteria"]) save_textedits(self, settings, ["notes"]) return settings def load(self, settings): load_checkboxes(self, settings, ["allow", "allowxperm", "auditallow", "auditallowxperm", "neverallow", "neverallowxperm", "dontaudit", "dontauditxperm", "type_transition", "type_change", "type_member", "criteria_expander", "notes_expander", "source_indirect", "source_regex", "target_indirect", "target_regex", "perms_subset", "xperms_equal", "default_regex", "bools_equal"]) load_lineedits(self, settings, ["source", "target", "xperms", "default_type"]) load_listviews(self, settings, ["tclass", "perms", "bool_criteria"]) load_textedits(self, settings, ["notes"]) # # Results runner # def run(self, button): # right now there is only one button. rule_types = [] max_results = 0 if self.allow.isChecked(): rule_types.append("allow") max_results += self.policy.allow_count if self.allowxperm.isChecked(): rule_types.append("allowxperm") max_results += self.policy.allowxperm_count if self.auditallow.isChecked(): rule_types.append("auditallow") max_results += self.policy.auditallow_count if self.auditallowxperm.isChecked(): rule_types.append("auditallowxperm") max_results += self.policy.auditallowxperm_count if self.neverallow.isChecked(): rule_types.append("neverallow") max_results += self.policy.neverallow_count if self.neverallowxperm.isChecked(): rule_types.append("neverallowxperm") max_results += self.policy.neverallowxperm_count if self.dontaudit.isChecked(): rule_types.append("dontaudit") max_results += self.policy.dontaudit_count if self.dontauditxperm.isChecked(): rule_types.append("dontauditxperm") max_results += self.policy.dontauditxperm_count if self.type_transition.isChecked(): rule_types.append("type_transition") max_results += self.policy.type_transition_count if self.type_member.isChecked(): rule_types.append("type_member") max_results += self.policy.type_member_count if self.type_change.isChecked(): rule_types.append("type_change") max_results += self.policy.type_change_count self.query.ruletype = rule_types self.query.source_indirect = self.source_indirect.isChecked() self.query.target_indirect = self.target_indirect.isChecked() self.query.perms_subset = self.perms_subset.isChecked() self.query.boolean_equal = self.bools_equal.isChecked() # if query is broad, show warning. if not any((self.query.source, self.query.target, self.query.tclass, self.query.perms, self.query.xperms, self.query.default, self.query.boolean)) \ and max_results > 1000: reply = QMessageBox.question( self, "Continue?", "This is a broad query, estimated to return {0} results. Continue?". format(max_results), QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.No: return # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} type enforcement rule(s) found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() # If the permissions column width is too long, pull back # to a reasonable size header = self.table_results.horizontalHeader() if header.sectionSize(4) > 400: header.resizeSection(4, 400) if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class MLSRuleQueryTab(AnalysisTab): """An MLS rule query.""" section = AnalysisSection.Rules tab_title = "MLS Rules" mlsonly = True def __init__(self, parent, policy, perm_map): super(MLSRuleQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = MLSRuleQuery(policy) self.setupUi() def __del__(self): with suppress(RuntimeError): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.mlsrulequery").removeHandler(self.handler) def setupUi(self): self.load_ui("apol/mlsrulequery.ui") # set up source/target autocompletion typeattr_completion_list = [str(t) for t in self.policy.types()] typeattr_completion_list.extend(str(a) for a in self.policy.typeattributes()) typeattr_completer_model = QStringListModel(self) typeattr_completer_model.setStringList(sorted(typeattr_completion_list)) self.typeattr_completion = QCompleter() self.typeattr_completion.setModel(typeattr_completer_model) self.source.setCompleter(self.typeattr_completion) self.target.setCompleter(self.typeattr_completion) # setup indications of errors on source/target/default self.errors = set() self.orig_palette = self.source.palette() self.error_palette = self.source.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_source_error() self.clear_target_error() self.clear_default_error() # populate class list self.class_model = SEToolsListModel(self) self.class_model.item_list = sorted(self.policy.classes()) self.tclass.setModel(self.class_model) # set up results self.table_results_model = MLSRuleTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(1, Qt.AscendingOrder) # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.mlsrulequery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.set_source_regex(self.source_regex.isChecked()) self.set_target_regex(self.target_regex.isChecked()) self.criteria_frame.setHidden(not self.criteria_expander.isChecked()) self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.buttonBox.clicked.connect(self.run) self.clear_ruletypes.clicked.connect(self.clear_all_ruletypes) self.all_ruletypes.clicked.connect(self.set_all_ruletypes) self.source.textEdited.connect(self.clear_source_error) self.source.editingFinished.connect(self.set_source) self.source_regex.toggled.connect(self.set_source_regex) self.target.textEdited.connect(self.clear_target_error) self.target.editingFinished.connect(self.set_target) self.target_regex.toggled.connect(self.set_target_regex) self.tclass.selectionModel().selectionChanged.connect(self.set_tclass) self.invert_class.clicked.connect(self.invert_tclass_selection) self.default_range.textEdited.connect(self.clear_default_error) self.default_range.editingFinished.connect(self.set_default_range) # # Ruletype criteria # def _set_ruletypes(self, value): self.range_transition.setChecked(value) def set_all_ruletypes(self): self._set_ruletypes(True) def clear_all_ruletypes(self): self._set_ruletypes(False) # # Source criteria # def clear_source_error(self): self.clear_criteria_error(self.source, "Match the source type/attribute of the rule.") def set_source(self): try: self.query.source = self.source.text() except Exception as ex: self.log.error("Source type/attribute error: {0}".format(ex)) self.set_criteria_error(self.source, ex) def set_source_regex(self, state): self.log.debug("Setting source_regex {0}".format(state)) self.query.source_regex = state self.clear_source_error() self.set_source() # # Target criteria # def clear_target_error(self): self.clear_criteria_error(self.target, "Match the target type/attribute of the rule.") def set_target(self): try: self.query.target = self.target.text() except Exception as ex: self.log.error("Target type/attribute error: {0}".format(ex)) self.set_criteria_error(self.target, ex) def set_target_regex(self, state): self.log.debug("Setting target_regex {0}".format(state)) self.query.target_regex = state self.clear_target_error() self.set_target() # # Class criteria # def set_tclass(self): selected_classes = [] for index in self.tclass.selectionModel().selectedIndexes(): selected_classes.append(self.class_model.data(index, Qt.UserRole)) self.query.tclass = selected_classes def invert_tclass_selection(self): invert_list_selection(self.tclass.selectionModel()) # # Default criteria # def clear_default_error(self): self.clear_criteria_error(self.default_range, "Match the default type the rule.") def set_default_range(self): try: self.query.default = self.default_range.text() except Exception as ex: self.log.error("Default range error: {0}".format(ex)) self.set_criteria_error(self.default_range, ex) # # Save/Load tab # def save(self): """Return a dictionary of settings.""" if self.errors: raise TabFieldError("Field(s) are in error: {0}". format(" ".join(o.objectName() for o in self.errors))) settings = {} save_checkboxes(self, settings, ["criteria_expander", "notes_expander", "range_transition", "source_indirect", "source_regex", "target_indirect", "target_regex"]) save_lineedits(self, settings, ["source", "target", "default_range"]) save_listviews(self, settings, ["tclass"]) save_textedits(self, settings, ["notes"]) return settings def load(self, settings): load_checkboxes(self, settings, ["criteria_expander", "notes_expander", "range_transition", "source_indirect", "source_regex", "target_indirect", "target_regex"]) load_lineedits(self, settings, ["source", "target", "default_range"]) load_listviews(self, settings, ["tclass"]) load_textedits(self, settings, ["notes"]) # # Results runner # def run(self, button): # right now there is only one button. self.query.ruletype = ['range_transition'] self.query.source_indirect = self.source_indirect.isChecked() self.query.target_indirect = self.target_indirect.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} MLS rule(s) found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class InitialSIDQueryTab(SEToolsWidget, QScrollArea): """An initial SID query.""" def __init__(self, parent, policy, perm_map): super(InitialSIDQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = InitialSIDQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.initsidquery").removeHandler(self.handler) def setupUi(self): self.load_ui("initsidquery.ui") # set up user autocompletion user_completion_list = [str(u) for u in self.policy.users()] user_completer_model = QStringListModel(self) user_completer_model.setStringList(sorted(user_completion_list)) self.user_completion = QCompleter() self.user_completion.setModel(user_completer_model) self.user.setCompleter(self.user_completion) # set up role autocompletion role_completion_list = [str(r) for r in self.policy.roles()] role_completer_model = QStringListModel(self) role_completer_model.setStringList(sorted(role_completion_list)) self.role_completion = QCompleter() self.role_completion.setModel(role_completer_model) self.role.setCompleter(self.role_completion) # set up type autocompletion type_completion_list = [str(t) for t in self.policy.types()] type_completer_model = QStringListModel(self) type_completer_model.setStringList(sorted(type_completion_list)) self.type_completion = QCompleter() self.type_completion.setModel(type_completer_model) self.type_.setCompleter(self.type_completion) # setup indications of errors on source/target/default self.orig_palette = self.type_.palette() self.error_palette = self.type_.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_name_error() self.clear_user_error() self.clear_type_error() self.clear_role_error() self.clear_range_error() # set up results self.table_results_model = InitialSIDTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(0, Qt.AscendingOrder) # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.initsidquery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.set_name_regex(self.name_regex.isChecked()) self.criteria_frame.setHidden(not self.criteria_expander.isChecked()) self.notes.setHidden(not self.notes_expander.isChecked()) # Range criteria is available only if policy is MLS if not self.policy.mls: self.range_criteria.setEnabled(False) self.range_criteria.setToolTip("MLS is disabled in this policy.") self.range_.setToolTip("MLS is disabled in this policy.") self.range_exact.setToolTip("MLS is disabled in this policy.") self.range_overlap.setToolTip("MLS is disabled in this policy.") self.range_subset.setToolTip("MLS is disabled in this policy.") self.range_superset.setToolTip("MLS is disabled in this policy.") # connect signals self.buttonBox.clicked.connect(self.run) self.name.textEdited.connect(self.clear_name_error) self.name.editingFinished.connect(self.set_name) self.name_regex.toggled.connect(self.set_name_regex) self.user.textEdited.connect(self.clear_user_error) self.user.editingFinished.connect(self.set_user) self.user_regex.toggled.connect(self.set_user_regex) self.role.textEdited.connect(self.clear_role_error) self.role.editingFinished.connect(self.set_role) self.role_regex.toggled.connect(self.set_role_regex) self.type_.textEdited.connect(self.clear_type_error) self.type_.editingFinished.connect(self.set_type) self.type_regex.toggled.connect(self.set_type_regex) self.range_.textEdited.connect(self.clear_range_error) self.range_.editingFinished.connect(self.set_range) # # Name criteria # def clear_name_error(self): self.name.setToolTip("Match the name.") self.name.setPalette(self.orig_palette) def set_name(self): try: self.query.name = self.name.text() except Exception as ex: self.log.error("Name error: {0}".format(ex)) self.name.setToolTip("Error: " + str(ex)) self.name.setPalette(self.error_palette) def set_name_regex(self, state): self.log.debug("Setting name_regex {0}".format(state)) self.query.name_regex = state self.clear_name_error() self.set_name() # # User criteria # def clear_user_error(self): self.user.setToolTip("Match the user of the context.") self.user.setPalette(self.orig_palette) def set_user(self): try: self.query.user = self.user.text() except Exception as ex: self.log.error("Context user error: {0}".format(ex)) self.user.setToolTip("Error: " + str(ex)) self.user.setPalette(self.error_palette) def set_user_regex(self, state): self.log.debug("Setting user_regex {0}".format(state)) self.query.user_regex = state self.clear_user_error() self.set_user() # # Role criteria # def clear_role_error(self): self.role.setToolTip("Match the role of the context.") self.role.setPalette(self.orig_palette) def set_role(self): try: self.query.role = self.role.text() except Exception as ex: self.log.error("Context role error: {0}".format(ex)) self.role.setToolTip("Error: " + str(ex)) self.role.setPalette(self.error_palette) def set_role_regex(self, state): self.log.debug("Setting role_regex {0}".format(state)) self.query.role_regex = state self.clear_role_error() self.set_role() # # Type criteria # def clear_type_error(self): self.type_.setToolTip("Match the type of the context.") self.type_.setPalette(self.orig_palette) def set_type(self): try: self.query.type_ = self.type_.text() except Exception as ex: self.log.error("Context type error: {0}".format(ex)) self.type_.setToolTip("Error: " + str(ex)) self.type_.setPalette(self.error_palette) def set_type_regex(self, state): self.log.debug("Setting type_regex {0}".format(state)) self.query.type_regex = state self.clear_type_error() self.set_type() # # Range criteria # def clear_range_error(self): self.range_.setToolTip("Match the range of the context.") self.range_.setPalette(self.orig_palette) def set_range(self): try: self.query.range_ = self.range_.text() except Exception as ex: self.log.info("Context range error: " + str(ex)) self.range_.setToolTip("Error: " + str(ex)) self.range_.setPalette(self.error_palette) # # Results runner # def run(self, button): # right now there is only one button. self.query.range_overlap = self.range_overlap.isChecked() self.query.range_subset = self.range_subset.isChecked() self.query.range_superset = self.range_superset.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} initial SID statment(s) found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class Mcar(QMainWindow, Ui_MainWindow): """ 汽车参数数据管理 """ sqlModel = QSqlQueryModel() def __init__(self, db, parent=None): """ 设置表格样式 """ super(Mcar, self).__init__(parent) self.setupUi(self) self.db = db self.initUi() def initUi(self): # 左右分栏布局五五开 self.splitter.setStretchFactor(0, 5) self.splitter.setStretchFactor(1, 5) query = QSqlQuery() # 系列复选框关键字(遍历) series_key = [ "", ] query.exec_("SELECT 系列 from CarData group by 系列;") while (query.next()): series_key.append(query.value(0)) self.comboBox.addItems(series_key) # 级别复选框关键字 level_key = [ "", ] query.exec_("SELECT 级别 from CarData group by 级别;") while (query.next()): level_key.append(query.value(0)) self.comboBox_3.addItems(level_key) # 价格复选框关键字 price_key = ["", "厂商指导价", "补贴售后价", "经销商参考价"] self.comboBox_2.addItems(price_key) # 能源类型关键字 energytype_key = [ "", ] query.exec_("SELECT 能源类型 from CarData group by 能源类型;") while (query.next()): energytype_key.append(query.value(0)) self.comboBox_4.addItems(energytype_key) # 厂商关键字 vendertype_key = [ "", ] query.exec_("SELECT 厂商 from CarData group by 厂商;") while (query.next()): vendertype_key.append(query.value(0)) self.comboBox_5.addItems(vendertype_key) # 环保标准关键字 envstandard_key = [ "", ] query.exec_("SELECT 环保标准 from CarData group by 环保标准;") while (query.next()): envstandard_key.append(query.value(0)) self.comboBox_6.addItems(envstandard_key) self.tableView.setIconSize(QSize(55, 25)) # 水平布局填充 self.tableView.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.setTableModel() def loading(self): while self.sqlModel.canFetchMore(): self.sqlModel.fetchMore() def setTableModel(self): """ 表格数据显示 """ # self.sqlModel = QSqlQueryModel() # self.sqlModel.setQuery("SELECT * FROM CarData;") self.sqlModel.setQuery( "SELECT id,系列,车型,厂商指导价,补贴后售价,经销商参考价,厂商,级别,能源类型,环保标准,上市时间,\"最大功率(kW)\",\"最大扭矩(N·m)\",发动机,变速箱,\"长*宽*高(mm)\",车身结构,\"最高车速(km/h)\",\"官方0-100km/h加速(s)\",\"实测0-100km/h加速(s)\",\"实测100-0km/h制动(m)\",\"工信部综合油耗(L/100km)\",\"实测油耗(L/100km)\",整车质保,\"长度(mm)\",\"宽度(mm)\",\"高度(mm)\",\"轴距(mm)\",\"前轮距(mm)\",\"后轮距(mm)\",\"最小离地间隙(mm)\",\"车门数(个)\",\"座位数(个)\",\"油箱容积(L)\",\"行李厢容积(L)\",\"整备质量(kg)\",发动机型号,\"排量(mL)\",\"排量(L)\",进气形式 FROM CarData;" ) print("数据库读取中.....") # while self.sqlModel.canFetchMore(): # self.sqlModel.fetchMore() self.proxyModel = QSortFilterProxyModel() # self.proxyModel.sort(0, Qt.AscendingOrder) 会有bug self.proxyModel.setSourceModel(self.sqlModel) # 大小写不敏感 self.proxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.tableView.setModel(self.proxyModel) # 开启排序 # self.tableView.setSortingEnabled(True) self.tableView.hideColumn(0) # hidden for i in range(8, 1371): self.tableView.hideColumn(i) # self.tableView.show() @pyqtSlot(QModelIndex) def on_tableView_clicked(self, index): """ 单击显示车辆详细信息 """ row = index.row() self.label_12.setText(self.sqlModel.record(row).value("系列")) self.label_14.setText(self.sqlModel.record(row).value("车型")) strp1 = str(self.sqlModel.record(row).value("厂商指导价")) if strp1 != "暂无报价": self.label_16.setText(strp1 + "万元") else: self.label_16.setText("暂无报价") strp2 = str(self.sqlModel.record(row).value("补贴后售价")) if strp2 != "暂无报价": self.label_18.setText(strp2 + "万元") else: self.label_18.setText("暂无报价") strp3 = str(self.sqlModel.record(row).value("经销商参考价")) if strp3 != "暂无报价": self.label_20.setText(strp3 + "万元") else: self.label_20.setText("暂无报价") self.label_22.setText(self.sqlModel.record(row).value("厂商")) self.label_24.setText(self.sqlModel.record(row).value("级别")) self.label_26.setText(self.sqlModel.record(row).value("能源类型")) self.label_28.setText(self.sqlModel.record(row).value("环保标准")) self.label_30.setText(str(self.sqlModel.record(row).value("上市时间"))) self.label_32.setText(str(self.sqlModel.record(row).value("最大功率(kW)"))) self.label_34.setText(str( self.sqlModel.record(row).value("最大扭矩(N·m)"))) self.label_36.setText(self.sqlModel.record(row).value("发动机")) self.label_38.setText(self.sqlModel.record(row).value("变速箱")) self.label_40.setText(self.sqlModel.record(row).value("长*宽*高(mm)")) self.label_42.setText(self.sqlModel.record(row).value("车身结构")) self.label_44.setText( str(self.sqlModel.record(row).value("最高车速(km/h)"))) self.label_46.setText( str(self.sqlModel.record(row).value("官方0-100km/h加速(s)"))) self.label_48.setText( str(self.sqlModel.record(row).value("实测0-100km/h加速(s)"))) self.label_50.setText( str(self.sqlModel.record(row).value("实测100-0km/h制动(m)"))) self.label_52.setText( str(self.sqlModel.record(row).value("工信部综合油耗(L/100km)"))) self.label_54.setText( str(self.sqlModel.record(row).value("实测油耗(L/100km)"))) self.label_56.setText(self.sqlModel.record(row).value("整车质保")) self.label_58.setText(self.sqlModel.record(row).value("进气形式")) self.label_60.setText(str(self.sqlModel.record(row).value("车门数(个)"))) self.label_70.setText(str(self.sqlModel.record(row).value("排量(L)"))) self.label_62.setText(str(self.sqlModel.record(row).value("座位数(个)"))) self.label_64.setText(str(self.sqlModel.record(row).value("油箱容积(L)"))) self.label_66.setText(str(self.sqlModel.record(row).value("行李厢容积(L)"))) self.label_68.setText(self.sqlModel.record(row).value("发动机型号")) @pyqtSlot() def on_pushButton_search_clicked(self): # 系列 self.seriesFilter = self.comboBox.currentText() # 级别 self.levelFilter = self.comboBox_3.currentText() # 价格 self.priceFilter = self.comboBox_2.currentText() # 能源类型 self.energyFilter = self.comboBox_4.currentText() # 厂商 self.vendorFilter = self.comboBox_5.currentText() # 环保标准 self.envFilter = self.comboBox_6.currentText() # 价格下限 self.bottomFilter = self.lineEdit_bottomPrice.text() # 价格上限 self.topFilter = self.lineEdit_topPrice.text() self.Filter(self.seriesFilter, self.levelFilter, self.priceFilter, self.energyFilter, self.vendorFilter, self.envFilter, self.bottomFilter, self.topFilter) def Filter(self, sf, lf, pf, enef, vf, envf, bf, tf): for i in range(8, 1371): self.tableView.hideColumn(i) if sf == lf == pf == enef == vf == envf == bf == tf == self.lineEdit.text( ) == self.lineEdit_2.text() == self.lineEdit_topPrice.text( ) == self.lineEdit_bottomPrice.text() == "": self.sqlModel.setQuery( "SELECT id,系列,车型,厂商指导价,补贴后售价,经销商参考价,厂商,级别,能源类型,环保标准,上市时间,\"最大功率(kW)\",\"最大扭矩(N·m)\",发动机,变速箱,\"长*宽*高(mm)\",车身结构,\"最高车速(km/h)\",\"官方0-100km/h加速(s)\",\"实测0-100km/h加速(s)\",\"实测100-0km/h制动(m)\",\"工信部综合油耗(L/100km)\",\"实测油耗(L/100km)\",整车质保,\"长度(mm)\",\"宽度(mm)\",\"高度(mm)\",\"轴距(mm)\",\"前轮距(mm)\",\"后轮距(mm)\",\"最小离地间隙(mm)\",\"车门数(个)\",\"座位数(个)\",\"油箱容积(L)\",\"行李厢容积(L)\",\"整备质量(kg)\",发动机型号,\"排量(mL)\",\"排量(L)\",进气形式 FROM CarData;" ) elif pf == "" and (self.lineEdit_topPrice.text() != "" or self.lineEdit_bottomPrice.text() != ""): msg_box = QMessageBox() msg_box.setWindowTitle("提示") msg_box.setText("请选择价格") if msg_box.exec_() == QMessageBox.Yes: return else: arr = {'系列': sf, '级别': lf, '能源类型': enef, '厂商': vf, '环保标准': envf} base_query = "SELECT id,系列,车型,厂商指导价,补贴后售价,经销商参考价,厂商,级别,能源类型,环保标准,上市时间,\"最大功率(kW)\",\"最大扭矩(N·m)\",发动机,变速箱,\"长*宽*高(mm)\",车身结构,\"最高车速(km/h)\",\"官方0-100km/h加速(s)\",\"实测0-100km/h加速(s)\",\"实测100-0km/h制动(m)\",\"工信部综合油耗(L/100km)\",\"实测油耗(L/100km)\",整车质保,\"长度(mm)\",\"宽度(mm)\",\"高度(mm)\",\"轴距(mm)\",\"前轮距(mm)\",\"后轮距(mm)\",\"最小离地间隙(mm)\",\"车门数(个)\",\"座位数(个)\",\"油箱容积(L)\",\"行李厢容积(L)\",\"整备质量(kg)\",发动机型号,\"排量(mL)\",\"排量(L)\",进气形式 FROM CarData WHERE " # base_query = "SELECT * FROM CarData WHERE " flag = True for key in arr.keys(): if arr[key] != "" and flag: flag = False base_query = base_query + key + "=" + '\"' + arr[key] + '\"' elif not flag and arr[key] != "": base_query = base_query + " AND " + key + "=" + '\"' + arr[ key] + '\"' # 上市时间筛选 if self.lineEdit.text() != "" and self.lineEdit_2.text( ) != "" and flag: flag = False base_query = base_query + "\"上市时间\" BETWEEN " + self.lineEdit.text( ) + " AND " + self.lineEdit_2.text() elif self.lineEdit.text() != "" and self.lineEdit_2.text( ) != "" and (not flag): base_query = base_query + " AND " + "\"上市时间\" BETWEEN " + self.lineEdit.text( ) + " AND " + self.lineEdit_2.text() # 价格筛选 if self.lineEdit_bottomPrice.text( ) != "" and self.lineEdit_topPrice.text( ) != "" and pf != "" and flag: flag = False base_query = base_query + "\"" + pf + "\"" + " BETWEEN " + self.lineEdit_bottomPrice.text( ) + " AND " + self.lineEdit_topPrice.text() elif self.lineEdit_bottomPrice.text( ) != "" and self.lineEdit_topPrice.text() != "" and pf != "" and ( not flag): base_query = base_query + " AND " + "\"" + pf + "\"" + " BETWEEN " + self.lineEdit_bottomPrice.text( ) + " AND " + self.lineEdit_topPrice.text() base_query = base_query + " ORDER BY " + "id" + ';' self.sqlModel.setQuery(base_query)
def setSourceModel(self, source_model): try: self.source_model = source_model QSortFilterProxyModel.setSourceModel(self, self.source_model) except Exception as e: logging.exception('exception occurred')
class MLSRuleQueryTab(SEToolsWidget, QScrollArea): """An MLS rule query.""" def __init__(self, parent, policy, perm_map): super(MLSRuleQueryTab, self).__init__(parent) self.log = logging.getLogger(self.__class__.__name__) self.policy = policy self.query = MLSRuleQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) def setupUi(self): self.load_ui("mlsrulequery.ui") # set up source/target autocompletion typeattr_completion_list = [str(t) for t in self.policy.types()] typeattr_completion_list.extend( str(a) for a in self.policy.typeattributes()) typeattr_completer_model = QStringListModel(self) typeattr_completer_model.setStringList( sorted(typeattr_completion_list)) self.typeattr_completion = QCompleter() self.typeattr_completion.setModel(typeattr_completer_model) self.source.setCompleter(self.typeattr_completion) self.target.setCompleter(self.typeattr_completion) # setup indications of errors on source/target/default self.orig_palette = self.source.palette() self.error_palette = self.source.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_source_error() self.clear_target_error() self.clear_default_error() # populate class list self.class_model = SEToolsListModel(self) self.class_model.item_list = sorted(self.policy.classes()) self.tclass.setModel(self.class_model) # set up results self.table_results_model = MLSRuleListModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) # set up processing thread self.thread = QThread() self.worker = ResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) # Ensure settings are consistent with the initial .ui state self.set_source_regex(self.source_regex.isChecked()) self.set_target_regex(self.target_regex.isChecked()) self.criteria_frame.setHidden(not self.criteria_expander.isChecked()) self.results_frame.setHidden(not self.results_expander.isChecked()) self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.buttonBox.clicked.connect(self.run) self.clear_ruletypes.clicked.connect(self.clear_all_ruletypes) self.all_ruletypes.clicked.connect(self.set_all_ruletypes) self.source.textEdited.connect(self.clear_source_error) self.source.editingFinished.connect(self.set_source) self.source_regex.toggled.connect(self.set_source_regex) self.target.textEdited.connect(self.clear_target_error) self.target.editingFinished.connect(self.set_target) self.target_regex.toggled.connect(self.set_target_regex) self.tclass.selectionModel().selectionChanged.connect(self.set_tclass) self.invert_class.clicked.connect(self.invert_tclass_selection) self.default_range.textEdited.connect(self.clear_default_error) self.default_range.editingFinished.connect(self.set_default_range) # # Ruletype criteria # def _set_ruletypes(self, value): self.range_transition.setChecked(value) def set_all_ruletypes(self): self._set_ruletypes(True) def clear_all_ruletypes(self): self._set_ruletypes(False) # # Source criteria # def clear_source_error(self): self.source.setToolTip("Match the source type/attribute of the rule.") self.source.setPalette(self.orig_palette) def set_source(self): try: self.query.source = self.source.text() except Exception as ex: self.source.setToolTip("Error: " + str(ex)) self.source.setPalette(self.error_palette) def set_source_regex(self, state): self.log.debug("Setting source_regex {0}".format(state)) self.query.source_regex = state self.clear_source_error() self.set_source() # # Target criteria # def clear_target_error(self): self.target.setToolTip("Match the target type/attribute of the rule.") self.target.setPalette(self.orig_palette) def set_target(self): try: self.query.target = self.target.text() except Exception as ex: self.target.setToolTip("Error: " + str(ex)) self.target.setPalette(self.error_palette) def set_target_regex(self, state): self.log.debug("Setting target_regex {0}".format(state)) self.query.target_regex = state self.clear_target_error() self.set_target() # # Class criteria # def set_tclass(self): selected_classes = [] for index in self.tclass.selectionModel().selectedIndexes(): selected_classes.append(self.class_model.data(index, Qt.UserRole)) self.query.tclass = selected_classes def invert_tclass_selection(self): invert_list_selection(self.tclass.selectionModel()) # # Default criteria # def clear_default_error(self): self.default_range.setToolTip("Match the default type the rule.") self.default_range.setPalette(self.orig_palette) def set_default_range(self): try: self.query.default = self.default_range.text() except Exception as ex: self.default_range.setToolTip("Error: " + str(ex)) self.default_range.setPalette(self.error_palette) # # Results runner # def run(self, button): # right now there is only one button. self.query.ruletype = ['range_transition'] self.query.source_indirect = self.source_indirect.isChecked() self.query.target_indirect = self.target_indirect.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self): # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText( "Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText( "Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText( "Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class TileStampsDock(QDockWidget): setStamp = pyqtSignal(TileStamp) def __init__(self, stampManager, parent=None): super().__init__(parent) self.mTileStampManager = stampManager self.mTileStampModel = stampManager.tileStampModel() self.mProxyModel = QSortFilterProxyModel(self.mTileStampModel) self.mFilterEdit = QLineEdit(self) self.mNewStamp = QAction(self) self.mAddVariation = QAction(self) self.mDuplicate = QAction(self) self.mDelete = QAction(self) self.mChooseFolder = QAction(self) self.setObjectName("TileStampsDock") self.mProxyModel.setSortLocaleAware(True) self.mProxyModel.setSortCaseSensitivity(Qt.CaseInsensitive) self.mProxyModel.setFilterCaseSensitivity(Qt.CaseInsensitive) self.mProxyModel.setSourceModel(self.mTileStampModel) self.mProxyModel.sort(0) self.mTileStampView = TileStampView(self) self.mTileStampView.setModel(self.mProxyModel) self.mTileStampView.setVerticalScrollMode( QAbstractItemView.ScrollPerPixel) self.mTileStampView.header().setStretchLastSection(False) self.mTileStampView.header().setSectionResizeMode( 0, QHeaderView.Stretch) self.mTileStampView.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.mTileStampView.setContextMenuPolicy(Qt.CustomContextMenu) self.mTileStampView.customContextMenuRequested.connect( self.showContextMenu) self.mNewStamp.setIcon(QIcon(":images/16x16/document-new.png")) self.mAddVariation.setIcon(QIcon(":/images/16x16/add.png")) self.mDuplicate.setIcon(QIcon(":/images/16x16/stock-duplicate-16.png")) self.mDelete.setIcon(QIcon(":images/16x16/edit-delete.png")) self.mChooseFolder.setIcon(QIcon(":images/16x16/document-open.png")) Utils.setThemeIcon(self.mNewStamp, "document-new") Utils.setThemeIcon(self.mAddVariation, "add") Utils.setThemeIcon(self.mDelete, "edit-delete") Utils.setThemeIcon(self.mChooseFolder, "document-open") self.mFilterEdit.setClearButtonEnabled(True) self.mFilterEdit.textChanged.connect( self.mProxyModel.setFilterFixedString) self.mTileStampModel.stampRenamed.connect(self.ensureStampVisible) self.mNewStamp.triggered.connect(self.newStamp) self.mAddVariation.triggered.connect(self.addVariation) self.mDuplicate.triggered.connect(self.duplicate) self.mDelete.triggered.connect(self.delete_) self.mChooseFolder.triggered.connect(self.chooseFolder) self.mDuplicate.setEnabled(False) self.mDelete.setEnabled(False) self.mAddVariation.setEnabled(False) widget = QWidget(self) layout = QVBoxLayout(widget) layout.setContentsMargins(5, 5, 5, 5) buttonContainer = QToolBar() buttonContainer.setFloatable(False) buttonContainer.setMovable(False) buttonContainer.setIconSize(QSize(16, 16)) buttonContainer.addAction(self.mNewStamp) buttonContainer.addAction(self.mAddVariation) buttonContainer.addAction(self.mDuplicate) buttonContainer.addAction(self.mDelete) stretch = QWidget() stretch.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) buttonContainer.addWidget(stretch) buttonContainer.addAction(self.mChooseFolder) listAndToolBar = QVBoxLayout() listAndToolBar.setSpacing(0) listAndToolBar.addWidget(self.mFilterEdit) listAndToolBar.addWidget(self.mTileStampView) listAndToolBar.addWidget(buttonContainer) layout.addLayout(listAndToolBar) selectionModel = self.mTileStampView.selectionModel() selectionModel.currentRowChanged.connect(self.currentRowChanged) self.setWidget(widget) self.retranslateUi() def changeEvent(self, e): super().changeEvent(e) x = e.type() if x == QEvent.LanguageChange: self.retranslateUi() else: pass def keyPressEvent(self, event): x = event.key() if x == Qt.Key_Delete or x == Qt.Key_Backspace: self.delete_() return super().keyPressEvent(event) def currentRowChanged(self, index): sourceIndex = self.mProxyModel.mapToSource(index) isStamp = self.mTileStampModel.isStamp(sourceIndex) self.mDuplicate.setEnabled(isStamp) self.mDelete.setEnabled(sourceIndex.isValid()) self.mAddVariation.setEnabled(isStamp) if (isStamp): self.setStamp.emit(self.mTileStampModel.stampAt(sourceIndex)) else: variation = self.mTileStampModel.variationAt(sourceIndex) if variation: # single variation clicked, use it specifically self.setStamp.emit(TileStamp(Map(variation.map))) def showContextMenu(self, pos): index = self.mTileStampView.indexAt(pos) if (not index.isValid()): return menu = QMenu() sourceIndex = self.mProxyModel.mapToSource(index) if (self.mTileStampModel.isStamp(sourceIndex)): addStampVariation = QAction(self.mAddVariation.icon(), self.mAddVariation.text(), menu) deleteStamp = QAction(self.mDelete.icon(), self.tr("Delete Stamp"), menu) deleteStamp.triggered.connect(self.delete_) addStampVariation.triggered.connect(self.addVariation) menu.addAction(addStampVariation) menu.addSeparator() menu.addAction(deleteStamp) else: removeVariation = QAction(QIcon(":/images/16x16/remove.png"), self.tr("Remove Variation"), menu) Utils.setThemeIcon(removeVariation, "remove") removeVariation.triggered.connect(self.delete_) menu.addAction(removeVariation) menu.exec(self.mTileStampView.viewport().mapToGlobal(pos)) def newStamp(self): stamp = self.mTileStampManager.createStamp() if (self.isVisible() and not stamp.isEmpty()): stampIndex = self.mTileStampModel.index(stamp) if (stampIndex.isValid()): viewIndex = self.mProxyModel.mapFromSource(stampIndex) self.mTileStampView.setCurrentIndex(viewIndex) self.mTileStampView.edit(viewIndex) def delete_(self): index = self.mTileStampView.currentIndex() if (not index.isValid()): return sourceIndex = self.mProxyModel.mapToSource(index) self.mTileStampModel.removeRow(sourceIndex.row(), sourceIndex.parent()) def duplicate(self): index = self.mTileStampView.currentIndex() if (not index.isValid()): return sourceIndex = self.mProxyModel.mapToSource(index) if (not self.mTileStampModel.isStamp(sourceIndex)): return stamp = self.mTileStampModel.stampAt = TileStamp(sourceIndex) self.mTileStampModel.addStamp(stamp.clone()) def addVariation(self): index = self.mTileStampView.currentIndex() if (not index.isValid()): return sourceIndex = self.mProxyModel.mapToSource(index) if (not self.mTileStampModel.isStamp(sourceIndex)): return stamp = self.mTileStampModel.stampAt(sourceIndex) self.mTileStampManager.addVariation(stamp) def chooseFolder(self): prefs = Preferences.instance() stampsDirectory = prefs.stampsDirectory() stampsDirectory = QFileDialog.getExistingDirectory( self.window(), self.tr("Choose the Stamps Folder"), stampsDirectory) if (not stampsDirectory.isEmpty()): prefs.setStampsDirectory(stampsDirectory) def ensureStampVisible(self, stamp): stampIndex = self.mTileStampModel.index(stamp) if (stampIndex.isValid()): self.mTileStampView.scrollTo( self.mProxyModel.mapFromSource(stampIndex)) def retranslateUi(self): self.setWindowTitle(self.tr("Tile Stamps")) self.mNewStamp.setText(self.tr("Add New Stamp")) self.mAddVariation.setText(self.tr("Add Variation")) self.mDuplicate.setText(self.tr("Duplicate Stamp")) self.mDelete.setText(self.tr("Delete Selected")) self.mChooseFolder.setText(self.tr("Set Stamps Folder")) self.mFilterEdit.setPlaceholderText(self.tr("Filter"))
class BackAnnotateMainWindow(QMainWindow): def __init__(self): super(BackAnnotateMainWindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.ui.loadBoardButton.clicked.connect(self.loadBoard) self.ui.prepareSchematicButton.clicked.connect(self.prepareSchematic) self.settings = QSettings("kicad_backannotate", "kicad_backannotate") self.ui.prepareSchematicButton.setEnabled(False) self.ui.writeSchematicButton.setEnabled(False) self.show() self.ui.schGroup.hide() self.ui.commitboardbutton.setEnabled(False) self.ui.writeSchematicButton.setEnabled(False) self.ui.commitboardbutton.pressed.connect(self.commitBoard) self.ui.writeSchematicButton.pressed.connect(self.commitSchematic) self.ui.inchRadio.toggled.connect(self.unitsChanged) self.ui.mmRadio.toggled.connect(self.unitsChanged) units = self.get_lastunits() if not units: self.ui.mmRadio.setChecked(True) else: self.ui.inchRadio.setChecked(True) def loadBoard(self, checked, filename=None): print(filename) lastdir = self.get_lastdir() if filename is None: filename, _ = QFileDialog.getOpenFileName(self, "select board file", lastdir, _KICAD_BOARD_FILTER) if len(filename) == 0: return self._model = RemapTable(filename, self.ui.sortOrder.currentIndex(), self.style()) self._filterproxy = QSortFilterProxyModel() self._filterproxy.setSourceModel(self._model) self.ui.remapView.setModel(self._filterproxy) self.ui.remapView.selectionModel().currentChanged.connect( self.componentSelected) self.ui.remapView.setSortingEnabled(True) self.settings.setValue("lastVisitedDir", os.path.dirname(filename)) self.ui.sortOrder.currentIndexChanged.connect(self._model.resortdata) self.ui.prepareSchematicButton.setEnabled(True) self.statusBar().showMessage("Board loaded OK") self.ui.remapView.resizeColumnsToContents() self.ui.commitboardbutton.setEnabled(True) def prepareSchematic(self, checked, filename=None): lastdir = self.get_lastdir() if filename is None: filename, _ = QFileDialog.getOpenFileName(self, "select schematic file", lastdir, _KICAD_SCHEM_FILTER) if len(filename) == 0: return self._updater = SchematicUpdater(filename) stremap = string_remapping(self._model.remap) done = self._updater.recursive_remap(stremap) if len(done) != len(stremap): self.statusBar().showMessage("WARNING: some symbols not found!") self._allsymsfound = False else: self.statusBar().showMessage( "Schematic loaded OK, all symbols found") self._allsymsfound = True self.ui.writeSchematicButton.setEnabled(True) self.ui.loadBoardButton.setEnabled(False) self.ui.sortOrder.setEnabled(False) self._model.showSchematicStatus(stremap, done) self.ui.remapView.resizeColumnsToContents() self.ui.schGroup.show() self.ui.commitboardbutton.setEnabled(True) def get_lastdir(self): lastdir_variant = self.settings.value("lastVisitedDir") if not lastdir_variant: lastdir = "" else: lastdir = str(lastdir_variant) return lastdir def get_lastunits(self): units_variant = self.settings.value("lastUnits") units = 0 if not units_variant else int(units_variant) return units def componentSelected(self, index, previous): if not hasattr(self, "_model"): return props = self._model.getProperties(index) #mm self.x_mm = props["x"] / 1E6 self.y_mm = props["y"] / 1E6 self.x_inch = self.x_mm * INCH_TO_MM self.y_inch = self.y_mm * INCH_TO_MM self.unitsChanged(None) self.ui.timestampvalue.setNum(props["tstamp"]) self.ui.footprintvalue.setText(str(props["libid"])) desig = self._model._oldvals[index.row()] if hasattr(self, "_updater"): if desig in self._updater.found_in_schematic_file: fname = os.path.basename( self._updater.found_in_schematic_file[desig]) self.ui.schfilevalue.setText(fname) else: self.ui.schfilevalue.setText("") def unitsChanged(self, toggleval): if not hasattr(self, "x_mm"): return if self.ui.mmRadio.isChecked(): self.ui.xvalue.setNum(self.x_mm) self.ui.yvalue.setNum(self.y_mm) self.settings.setValue("lastUnits", 0) else: self.ui.xvalue.setNum(self.x_inch) self.ui.yvalue.setNum(self.y_inch) self.settings.setValue("lastUnits", 1) def commitBoard(self): lastdir = self.get_lastdir() self._model._remapper.change_board_references(self._model.remap) savename, _ = QFileDialog.getSaveFileName(self, "select output board file", lastdir, _KICAD_BOARD_FILTER) self._model._remapper.save_board(savename) self.statusBar().showMessage("saved output board %s" % savename) def commitSchematic(self): lastdir = self.get_lastdir() ext, ok = QInputDialog.getText(self, "extension", "select updated schematic name append", 0, "_remapped") if not ok: return self._updater.save_changes(ext) self.statusBar().showMessage("saved output schematic")
class SensitivityQueryTab(SEToolsWidget, QScrollArea): """Sensitivity browser and query tab.""" def __init__(self, parent, policy, perm_map): super(SensitivityQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = SensitivityQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.sensitivityquery").removeHandler(self.handler) def setupUi(self): self.load_ui("sensitivityquery.ui") # populate sensitivity list self.sensitivity_model = SEToolsListModel(self) self.sensitivity_model.item_list = sorted(self.policy.sensitivities()) self.sens.setModel(self.sensitivity_model) # set up results self.table_results_model = MLSComponentTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(0, Qt.AscendingOrder) # setup indications of errors on level/range self.orig_palette = self.name.palette() self.error_palette = self.name.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_name_error() # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.sensitivityquery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.sens.doubleClicked.connect(self.get_detail) self.sens.get_detail.triggered.connect(self.get_detail) self.name.textEdited.connect(self.clear_name_error) self.name.editingFinished.connect(self.set_name) self.name_regex.toggled.connect(self.set_name_regex) self.buttonBox.clicked.connect(self.run) # # Sensitivity browser # def get_detail(self): # .ui is set for single item selection. index = self.sens.selectedIndexes()[0] item = self.sensitivity_model.data(index, Qt.UserRole) self.log.debug("Generating detail window for {0}".format(item)) sensitivity_detail(self, item) # # Name criteria # def clear_name_error(self): self.name.setToolTip("Match the sensitivity name.") self.name.setPalette(self.orig_palette) def set_name(self): try: self.query.name = self.name.text() except Exception as ex: self.log.error("Sensitivity name error: {0}".format(ex)) self.name.setToolTip("Error: " + str(ex)) self.name.setPalette(self.error_palette) def set_name_regex(self, state): self.log.debug("Setting name_regex {0}".format(state)) self.query.name_regex = state self.clear_name_error() self.set_name() # # Results runner # def run(self, button): # right now there is only one button. # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} categories found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() # If the attrs or alias column widths are too long, pull back # to a reasonable size header = self.table_results.horizontalHeader() if header.sectionSize(1) > 400: header.resizeSection(1, 400) if header.sectionSize(2) > 400: header.resizeSection(2, 400) if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class MainWindow(QMainWindow): """ The main GUI window. """ def __init__(self, application): super().__init__() self.application = application self.tabulator_dialog = None self.sound = QSound('laser.wav') self.window = Ui_MainWindow() self.window.setupUi(self) self.setWindowIcon(QIcon('aswan-icon.png')) # Data binding self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(self.application.REPORT) self.window.treeView.setModel(self.proxy_model) # Command binding self.window.actionQuit.triggered.connect(qApp.quit) self.window.actionColumns.triggered.connect(self.cmd_view_columns) self.window.actionClear.triggered.connect(self.cmd_view_clear) self.window.actionFit_Columns_To_Contents.triggered.connect( self.cmd_view_fit_columns_to_contents) self.window.actionDocumentation.triggered.connect( self.cmd_help_documentation) # Start the report updates self.update() def cmd_view_columns(self): """ Configure the columns produced by the tabulator. """ self.tabulator_dialog = TabulatorDialog(self.application) self.tabulator_dialog.show() def cmd_view_clear(self): """ Clear the report. """ self.application.REPORT.clear() def cmd_view_fit_columns_to_contents(self): """ Fit report columns to visible rows. """ self.window.treeView.header().resizeSections( QHeaderView.ResizeToContents) def cmd_help_documentation(self): """ Show documentation. """ webbrowser.open_new('https://www.intrepiduniverse.com/') def update(self): """ Timer method to update the report in real time. """ try: if self.window.actionRealtime.isChecked(): table = self.application.REPORT.update() if table['rows']: if self.window.actionAudible_Blink.isChecked(): self.sound.play() finally: QTimer.singleShot(100, self.update)
class RBACRuleQueryTab(SEToolsWidget, QScrollArea): """A RBAC rule query.""" def __init__(self, parent, policy, perm_map): super(RBACRuleQueryTab, self).__init__(parent) self.log = logging.getLogger(self.__class__.__name__) self.policy = policy self.query = RBACRuleQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) def setupUi(self): self.load_ui("rbacrulequery.ui") # set up role autocompletion (source, default) role_completion_list = [str(r) for r in self.policy.roles()] role_completer_model = QStringListModel(self) role_completer_model.setStringList(sorted(role_completion_list)) self.role_completion = QCompleter() self.role_completion.setModel(role_completer_model) self.source.setCompleter(self.role_completion) self.default_role.setCompleter(self.role_completion) # set up role/type autocompletion (target) roletype_completion_list = [str(r) for r in self.policy.roles()] # roletype_completion_list.extend(str(a) for a in self.policy.roleattributes()) roletype_completion_list.extend(str(t) for t in self.policy.types()) roletype_completion_list.extend(str(a) for a in self.policy.typeattributes()) roletype_completer_model = QStringListModel(self) roletype_completer_model.setStringList(sorted(roletype_completion_list)) self.roletype_completion = QCompleter() self.roletype_completion.setModel(roletype_completer_model) self.target.setCompleter(self.roletype_completion) # setup indications of errors on source/target/default self.orig_palette = self.source.palette() self.error_palette = self.source.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_source_error() self.clear_target_error() self.clear_default_error() # populate class list self.class_model = SEToolsListModel(self) self.class_model.item_list = sorted(self.policy.classes()) self.tclass.setModel(self.class_model) # set up results self.table_results_model = RBACRuleListModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) # set up processing thread self.thread = QThread() self.worker = ResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) # Ensure settings are consistent with the initial .ui state self.set_source_regex(self.source_regex.isChecked()) self.set_target_regex(self.target_regex.isChecked()) self.set_default_regex(self.default_regex.isChecked()) self.criteria_frame.setHidden(not self.criteria_expander.isChecked()) self.results_frame.setHidden(not self.results_expander.isChecked()) self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.buttonBox.clicked.connect(self.run) self.clear_ruletypes.clicked.connect(self.clear_all_ruletypes) self.all_ruletypes.clicked.connect(self.set_all_ruletypes) self.source.textEdited.connect(self.clear_source_error) self.source.editingFinished.connect(self.set_source) self.source_regex.toggled.connect(self.set_source_regex) self.target.textEdited.connect(self.clear_target_error) self.target.editingFinished.connect(self.set_target) self.target_regex.toggled.connect(self.set_target_regex) self.tclass.selectionModel().selectionChanged.connect(self.set_tclass) self.invert_class.clicked.connect(self.invert_tclass_selection) self.default_role.textEdited.connect(self.clear_default_error) self.default_role.editingFinished.connect(self.set_default_role) self.default_regex.toggled.connect(self.set_default_regex) # # Ruletype criteria # def _set_ruletypes(self, value): self.allow.setChecked(value) self.role_transition.setChecked(value) def set_all_ruletypes(self): self._set_ruletypes(True) def clear_all_ruletypes(self): self._set_ruletypes(False) # # Source criteria # def clear_source_error(self): self.source.setToolTip("Match the source role of the rule.") self.source.setPalette(self.orig_palette) def set_source(self): try: self.query.source = self.source.text() except Exception as ex: self.source.setToolTip("Error: " + str(ex)) self.source.setPalette(self.error_palette) def set_source_regex(self, state): self.log.debug("Setting source_regex {0}".format(state)) self.query.source_regex = state self.clear_source_error() self.set_source() # # Target criteria # def clear_target_error(self): self.target.setToolTip("Match the target role/type of the rule.") self.target.setPalette(self.orig_palette) def set_target(self): try: self.query.target = self.target.text() except Exception as ex: self.target.setToolTip("Error: " + str(ex)) self.target.setPalette(self.error_palette) def set_target_regex(self, state): self.log.debug("Setting target_regex {0}".format(state)) self.query.target_regex = state self.clear_target_error() self.set_target() # # Class criteria # def set_tclass(self): selected_classes = [] for index in self.tclass.selectionModel().selectedIndexes(): selected_classes.append(self.class_model.data(index, Qt.UserRole)) self.query.tclass = selected_classes def invert_tclass_selection(self): invert_list_selection(self.tclass.selectionModel()) # # Default criteria # def clear_default_error(self): self.default_role.setToolTip("Match the default role the rule.") self.default_role.setPalette(self.orig_palette) def set_default_role(self): self.query.default_regex = self.default_regex.isChecked() try: self.query.default = self.default_role.text() except Exception as ex: self.default_role.setToolTip("Error: " + str(ex)) self.default_role.setPalette(self.error_palette) def set_default_regex(self, state): self.log.debug("Setting default_regex {0}".format(state)) self.query.default_regex = state self.clear_default_error() self.set_default_role() # # Results runner # def run(self, button): # right now there is only one button. rule_types = [] for mode in [self.allow, self.role_transition]: if mode.isChecked(): rule_types.append(mode.objectName()) self.query.ruletype = rule_types self.query.source_indirect = self.source_indirect.isChecked() self.query.target_indirect = self.target_indirect.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self): # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class BoolQueryTab(AnalysisTab): """Bool browser and query tab.""" def __init__(self, parent, policy, perm_map): super(BoolQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = BoolQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.boolquery").removeHandler(self.handler) def setupUi(self): self.load_ui("apol/boolquery.ui") # populate bool list self.bool_model = SEToolsListModel(self) self.bool_model.item_list = sorted(r for r in self.policy.bools()) self.bools.setModel(self.bool_model) # set up results self.table_results_model = BooleanTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(0, Qt.AscendingOrder) # setup indications of errors on level/range self.errors = set() self.orig_palette = self.name.palette() self.error_palette = self.name.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_name_error() # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.boolquery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.bools.doubleClicked.connect(self.get_detail) self.bools.get_detail.triggered.connect(self.get_detail) self.name.textEdited.connect(self.clear_name_error) self.name.editingFinished.connect(self.set_name) self.name_regex.toggled.connect(self.set_name_regex) self.buttonBox.clicked.connect(self.run) # # Booleans browser # def get_detail(self): # .ui is set for single item selection. index = self.bools.selectedIndexes()[0] item = self.bool_model.data(index, Qt.UserRole) self.log.debug("Generating detail window for {0}".format(item)) boolean_detail(self, item) # # Name criteria # def clear_name_error(self): self.clear_criteria_error(self.name, "Match the Boolean name.") def set_name(self): try: self.query.name = self.name.text() except Exception as ex: self.log.error("Boolean name error: {0}".format(ex)) self.set_criteria_error(self.name, ex) def set_name_regex(self, state): self.log.debug("Setting name_regex {0}".format(state)) self.query.name_regex = state self.clear_name_error() self.set_name() # # Save/Load tab # def save(self): """Return a dictionary of settings.""" if self.errors: raise TabFieldError("Field(s) are in error: {0}".format(" ".join( o.objectName() for o in self.errors))) settings = {} save_checkboxes(self, settings, [ "criteria_expander", "notes_expander", "default_any", "default_true", "default_false", "name_regex" ]) save_lineedits(self, settings, ["name"]) save_textedits(self, settings, ["notes"]) return settings def load(self, settings): load_checkboxes(self, settings, [ "criteria_expander", "notes_expander", "default_any", "default_true", "default_false", "name_regex" ]) load_lineedits(self, settings, ["name"]) load_textedits(self, settings, ["notes"]) # # Results runner # def run(self, button): # right now there is only one button. if self.default_any.isChecked(): self.query.default = None else: self.query.default = self.default_true.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} Boolean(s) found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText( "Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText( "Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText( "Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class BoundsQueryTab(AnalysisTab): """Bounds browser and query tab.""" def __init__(self, parent, policy, perm_map): super(BoundsQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = BoundsQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.boundsquery").removeHandler(self.handler) def setupUi(self): self.load_ui("boundsquery.ui") # set up results self.table_results_model = BoundsTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(1, Qt.AscendingOrder) # setup indications of errors on level/range self.errors = set() self.orig_palette = self.parent.palette() self.error_palette = self.parent.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_parent_error() self.clear_child_error() # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.boundsquery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.parent.textEdited.connect(self.clear_parent_error) self.parent.editingFinished.connect(self.set_parent) self.parent_regex.toggled.connect(self.set_parent_regex) self.child.textEdited.connect(self.clear_child_error) self.child.editingFinished.connect(self.set_child) self.child_regex.toggled.connect(self.set_child_regex) self.buttonBox.clicked.connect(self.run) # # Parent criteria # def clear_parent_error(self): self.clear_criteria_error(self.parent, "Match the parent type.") def set_parent(self): try: self.query.parent = self.parent.text() except Exception as ex: self.log.error("Type parent error: {0}".format(ex)) self.set_criteria_error(self.parent, ex) def set_parent_regex(self, state): self.log.debug("Setting parent_regex {0}".format(state)) self.query.parent_regex = state self.clear_parent_error() self.set_parent() # # Child criteria # def clear_child_error(self): self.clear_criteria_error(self.child, "Match the child type.") def set_child(self): try: self.query.child = self.child.text() except Exception as ex: self.log.error("Type child error: {0}".format(ex)) self.set_criteria_error(self.child, ex) def set_child_regex(self, state): self.log.debug("Setting child_regex {0}".format(state)) self.query.child_regex = state self.clear_child_error() self.set_child() # # Save/Load tab # def save(self): """Return a dictionary of settings.""" if self.errors: raise TabFieldError("Field(s) are in error: {0}".format(" ".join( o.objectName() for o in self.errors))) settings = {} save_checkboxes(self, settings, [ "criteria_expander", "notes_expander", "typebounds", "parent_regex", "child_regex" ]) save_lineedits(self, settings, ["parent", "child"]) save_textedits(self, settings, ["notes"]) return settings def load(self, settings): load_checkboxes(self, settings, [ "criteria_expander", "notes_expander", "typebounds", "parent_regex", "child_regex" ]) load_lineedits(self, settings, ["parent", "child"]) load_textedits(self, settings, ["notes"]) # # Results runner # def run(self, button): # right now there is only one button. self.query.parent_regex = self.parent_regex.isChecked() self.query.child_regex = self.child_regex.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} bound(s) found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText( "Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText( "Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText( "Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class TypeQueryTab(SEToolsWidget, QScrollArea): """Type browser and query tab.""" def __init__(self, parent, policy, perm_map): super(TypeQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = TypeQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.typequery").removeHandler(self.handler) def setupUi(self): self.load_ui("typequery.ui") # populate type list self.type_model = SEToolsListModel(self) self.type_model.item_list = sorted(self.policy.types()) self.types.setModel(self.type_model) # populate attribute list self.attr_model = SEToolsListModel(self) self.attr_model.item_list = sorted(self.policy.typeattributes()) self.attrs.setModel(self.attr_model) # set up results self.table_results_model = TypeTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(0, Qt.AscendingOrder) # setup indications of errors on level/range self.orig_palette = self.name.palette() self.error_palette = self.name.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_name_error() # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.typequery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.types.doubleClicked.connect(self.get_detail) self.types.get_detail.triggered.connect(self.get_detail) self.name.textEdited.connect(self.clear_name_error) self.name.editingFinished.connect(self.set_name) self.name_regex.toggled.connect(self.set_name_regex) self.attrs.selectionModel().selectionChanged.connect(self.set_attrs) self.invert_attrs.clicked.connect(self.invert_attr_selection) self.buttonBox.clicked.connect(self.run) # # Type browser # def get_detail(self): # .ui is set for single item selection. index = self.types.selectedIndexes()[0] item = self.type_model.data(index, Qt.UserRole) self.log.debug("Generating detail window for {0}".format(item)) type_detail(self, item) # # Name criteria # def clear_name_error(self): self.name.setToolTip("Match the type name.") self.name.setPalette(self.orig_palette) def set_name(self): try: self.query.name = self.name.text() except Exception as ex: self.log.error("Type name error: {0}".format(ex)) self.name.setToolTip("Error: " + str(ex)) self.name.setPalette(self.error_palette) def set_name_regex(self, state): self.log.debug("Setting name_regex {0}".format(state)) self.query.name_regex = state self.clear_name_error() self.set_name() # # Attribute criteria # def set_attrs(self): selected_attrs = [] for index in self.attrs.selectionModel().selectedIndexes(): selected_attrs.append(self.attr_model.data(index, Qt.UserRole)) self.query.attrs = selected_attrs def invert_attr_selection(self): invert_list_selection(self.attrs.selectionModel()) # # Results runner # def run(self, button): # right now there is only one button. self.query.name_regex = self.name_regex.isChecked() self.query.attrs_equal = self.attrs_equal.isChecked() self.query.permissive = self.permissive.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} type(s) found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() # If the attrs or alias column widths are too long, pull back # to a reasonable size header = self.table_results.horizontalHeader() if header.sectionSize(1) > 400: header.resizeSection(1, 400) if header.sectionSize(2) > 400: header.resizeSection(2, 400) if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class IncidentView(QtWidgets.QWidget): def __init__(self, parent=None): super(IncidentView, self).__init__(parent) self.view = QtWidgets.QTreeView() self.view.setAllColumnsShowFocus(True) self.view.setUniformRowHeights(True) box = QtWidgets.QVBoxLayout() box.addWidget(self.view) self.load_trace = QtWidgets.QPushButton('&Trace') self.load_trace.setToolTip('Load into the Trace Window') self.load_trace.setEnabled(False) for activation_signal in [ self.view.activated, self.view.entered, self.view.pressed]: activation_signal.connect(lambda _: self.update_controls_state()) self.load_trace.clicked.connect(self.load_current_trace) self.view.doubleClicked.connect(self.jump_to_index) hbox = QtWidgets.QHBoxLayout() self.filter = QtWidgets.QLineEdit() self.filter.textChanged.connect(self.filter_model) filter_label = QtWidgets.QLabel('&Search') filter_label.setBuddy(self.filter) hbox.addWidget(filter_label) hbox.addWidget(self.filter) hbox.addWidget(self.load_trace) box.addLayout(hbox) self.setLayout(box) self.model = None self.proxy = None def display(self, incidents, locations): self.model = IncidentModel(incidents, locations, self) self.proxy = QSortFilterProxyModel(self) self.proxy.setSourceModel(self.model) self.proxy.setFilterRole(self.model.filter_role) self.proxy.setFilterRegExp(QRegExp(self.filter.text())) self.view.setModel(self.proxy) def filter_model(self, txt): if self.proxy: self.proxy.setFilterRegExp(QRegExp(txt)) def update_controls_state(self): curr = self.view.currentIndex() self.load_trace.setEnabled(curr.isValid() and curr.parent().isValid()) def load_current_trace(self): idx = self.proxy.mapToSource(self.view.currentIndex()) if not idx.isValid() or index_level(idx) not in (1, 2): raise ValueError('load_current_trace: invalid index') if index_level(idx) == 2: idx = idx.parent() incident = self.model.incidents[idx.parent().row()] location = incident.locations[idx.row()] backtrace = self.model.locations[location] for p in reversed(backtrace): self.load_trace_point(p) def jump_to_index(self, idx): idx = self.proxy.mapToSource(idx) if index_level(idx) != 2: # don't mess with parents, they are used to create children return grandpa = idx.parent().parent() incident = self.model.incidents[grandpa.row()] location = incident.locations[idx.parent().row()] trace = self.model.locations[location] point = trace[idx.row()] self.show_trace_point(point) def load_trace_point(self, p): add_insn_to_trace_view(p.addr) def show_trace_point(self, p): idaapi.jumpto(p.addr)
class ConstraintQueryTab(AnalysisTab): """A constraint query.""" def __init__(self, parent, policy, perm_map): super(ConstraintQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = ConstraintQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.constraintquery").removeHandler(self.handler) def setupUi(self): self.load_ui("apol/constraintquery.ui") # set up user autocompletion user_completion_list = [str(u) for u in self.policy.users()] user_completer_model = QStringListModel(self) user_completer_model.setStringList(sorted(user_completion_list)) self.user_completion = QCompleter() self.user_completion.setModel(user_completer_model) self.user.setCompleter(self.user_completion) # set up role autocompletion role_completion_list = [str(r) for r in self.policy.roles()] role_completer_model = QStringListModel(self) role_completer_model.setStringList(sorted(role_completion_list)) self.role_completion = QCompleter() self.role_completion.setModel(role_completer_model) self.role.setCompleter(self.role_completion) # set up type autocompletion type_completion_list = [str(t) for t in self.policy.types()] type_completer_model = QStringListModel(self) type_completer_model.setStringList(sorted(type_completion_list)) self.type_completion = QCompleter() self.type_completion.setModel(type_completer_model) self.type_.setCompleter(self.type_completion) # populate class list self.class_model = SEToolsListModel(self) self.class_model.item_list = sorted(self.policy.classes()) self.tclass.setModel(self.class_model) # populate perm list self.perms_model = PermListModel(self, self.policy) self.perms.setModel(self.perms_model) # setup indications of errors self.errors = set() self.orig_palette = self.type_.palette() self.error_palette = self.type_.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_user_error() self.clear_type_error() self.clear_role_error() # set up results self.table_results_model = ConstraintTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(0, Qt.AscendingOrder) # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.constraintquery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.set_user_regex(self.user_regex.isChecked()) self.set_role_regex(self.role_regex.isChecked()) self.set_type_regex(self.type_regex.isChecked()) self.criteria_frame.setHidden(not self.criteria_expander.isChecked()) self.notes.setHidden(not self.notes_expander.isChecked()) # MLS constraints available only if policy is MLS if not self.policy.mls: self.mlsconstrain.setEnabled(False) self.mlsvalidatetrans.setEnabled(False) self.mlsconstrain.setToolTip("MLS is disabled in this policy.") self.mlsvalidatetrans.setToolTip("MLS is disabled in this policy.") # connect signals self.buttonBox.clicked.connect(self.run) self.clear_ruletypes.clicked.connect(self.clear_all_ruletypes) self.all_ruletypes.clicked.connect(self.set_all_ruletypes) self.user.textEdited.connect(self.clear_user_error) self.user.editingFinished.connect(self.set_user) self.user_regex.toggled.connect(self.set_user_regex) self.role.textEdited.connect(self.clear_role_error) self.role.editingFinished.connect(self.set_role) self.role_regex.toggled.connect(self.set_role_regex) self.type_.textEdited.connect(self.clear_type_error) self.type_.editingFinished.connect(self.set_type) self.type_regex.toggled.connect(self.set_type_regex) self.tclass.selectionModel().selectionChanged.connect(self.set_tclass) self.invert_class.clicked.connect(self.invert_tclass_selection) self.perms.selectionModel().selectionChanged.connect(self.set_perms) self.invert_perms.clicked.connect(self.invert_perms_selection) # # Ruletype criteria # def _set_ruletypes(self, value): self.constrain.setChecked(value) self.validatetrans.setChecked(value) if self.policy.mls: self.mlsconstrain.setChecked(value) self.mlsvalidatetrans.setChecked(value) def set_all_ruletypes(self): self._set_ruletypes(True) def clear_all_ruletypes(self): self._set_ruletypes(False) # # Class criteria # def set_tclass(self): selected_classes = [] for index in self.tclass.selectionModel().selectedIndexes(): selected_classes.append(self.class_model.data(index, Qt.UserRole)) self.query.tclass = selected_classes self.perms_model.set_classes(selected_classes) def invert_tclass_selection(self): invert_list_selection(self.tclass.selectionModel()) # # Permissions criteria # def set_perms(self): selected_perms = [] for index in self.perms.selectionModel().selectedIndexes(): selected_perms.append(self.perms_model.data(index, Qt.UserRole)) self.query.perms = selected_perms def invert_perms_selection(self): invert_list_selection(self.perms.selectionModel()) # # User criteria # def clear_user_error(self): self.clear_criteria_error(self.user, "Match constraints that have a user in the expression.") def set_user(self): try: self.query.user = self.user.text() except Exception as ex: self.log.error("User error: {0}".format(ex)) self.set_criteria_error(self.user, ex) def set_user_regex(self, state): self.log.debug("Setting user_regex {0}".format(state)) self.query.user_regex = state self.clear_user_error() self.set_user() # # Role criteria # def clear_role_error(self): self.clear_criteria_error(self.role, "Match constraints that have a role in the expression.") def set_role(self): try: self.query.role = self.role.text() except Exception as ex: self.log.error("Role error: {0}".format(ex)) self.set_criteria_error(self.role, ex) def set_role_regex(self, state): self.log.debug("Setting role_regex {0}".format(state)) self.query.role_regex = state self.clear_role_error() self.set_role() # # Type criteria # def clear_type_error(self): self.clear_criteria_error(self.type_, "Match constraints that have a type in the expression.") def set_type(self): try: self.query.type_ = self.type_.text() except Exception as ex: self.log.error("Type error: {0}".format(ex)) self.set_criteria_error(self.type_, ex) def set_type_regex(self, state): self.log.debug("Setting type_regex {0}".format(state)) self.query.type_regex = state self.clear_type_error() self.set_type() # # Save/Load tab # def save(self): """Return a dictionary of settings.""" if self.errors: raise TabFieldError("Field(s) are in error: {0}". format(" ".join(o.objectName() for o in self.errors))) settings = {} save_checkboxes(self, settings, ["criteria_expander", "notes_expander", "constrain", "mlsconstrain", "validatetrans", "mlsvalidatetrans", "user_regex", "role_regex", "type_regex", "perms_subset"]) save_lineedits(self, settings, ["user", "role", "type_"]) save_listviews(self, settings, ["tclass", "perms"]) save_textedits(self, settings, ["notes"]) return settings def load(self, settings): load_checkboxes(self, settings, ["criteria_expander", "notes_expander", "constrain", "mlsconstrain", "validatetrans", "mlsvalidatetrans", "user_regex", "role_regex", "type_regex", "perms_subset"]) load_lineedits(self, settings, ["user", "role", "type_"]) load_listviews(self, settings, ["tclass", "perms"]) load_textedits(self, settings, ["notes"]) # # Results runner # def run(self, button): # right now there is only one button. rule_types = [] for mode in [self.constrain, self.mlsconstrain, self.validatetrans, self.mlsvalidatetrans]: if mode.isChecked(): rule_types.append(mode.objectName()) self.query.ruletype = rule_types self.query.perms_subset = self.perms_subset.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} constraint(s) found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() # If the permissions or expression column widths are too long, pull back # to a reasonable size header = self.table_results.horizontalHeader() if header.sectionSize(2) > 400: header.resizeSection(2, 400) if header.sectionSize(3) > 400: header.resizeSection(3, 400) if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
def _init_proxy_model(self): filter_proxy_model = QSortFilterProxyModel() filter_proxy_model.setSourceModel(self.model()) filter_proxy_model.setFilterKeyColumn(1) filter_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.setModel(filter_proxy_model)
class E5NetworkMonitor(QDialog, Ui_E5NetworkMonitor): """ Class implementing a network monitor dialog. """ _monitor = None @classmethod def instance(cls, networkAccessManager): """ Class method to get a reference to our singleton. @param networkAccessManager reference to the network access manager (QNetworkAccessManager) @return reference to the network monitor singleton (E5NetworkMonitor) """ if cls._monitor is None: cls._monitor = E5NetworkMonitor(networkAccessManager) return cls._monitor @classmethod def closeMonitor(cls): """ Class method to close the monitor dialog. """ if cls._monitor is not None: cls._monitor.close() def __init__(self, networkAccessManager, parent=None): """ Constructor @param networkAccessManager reference to the network access manager (QNetworkAccessManager) @param parent reference to the parent widget (QWidget) """ super(E5NetworkMonitor, self).__init__(parent) self.setupUi(self) self.__requestHeaders = QStandardItemModel(self) self.__requestHeaders.setHorizontalHeaderLabels( [self.tr("Name"), self.tr("Value")]) self.requestHeadersList.setModel(self.__requestHeaders) self.requestHeadersList.horizontalHeader().setStretchLastSection(True) self.requestHeadersList.doubleClicked.connect(self.__showHeaderDetails) self.__replyHeaders = QStandardItemModel(self) self.__replyHeaders.setHorizontalHeaderLabels( [self.tr("Name"), self.tr("Value")]) self.responseHeadersList.setModel(self.__replyHeaders) self.responseHeadersList.horizontalHeader().setStretchLastSection(True) self.responseHeadersList.doubleClicked.connect( self.__showHeaderDetails) self.requestsList.horizontalHeader().setStretchLastSection(True) self.requestsList.verticalHeader().setMinimumSectionSize(-1) self.__proxyModel = QSortFilterProxyModel(self) self.__proxyModel.setFilterKeyColumn(-1) self.searchEdit.textChanged.connect( self.__proxyModel.setFilterFixedString) self.removeButton.clicked.connect(self.requestsList.removeSelected) self.removeAllButton.clicked.connect(self.requestsList.removeAll) self.__model = E5RequestModel(networkAccessManager, self) self.__proxyModel.setSourceModel(self.__model) self.requestsList.setModel(self.__proxyModel) self.__proxyModel.rowsInserted.connect( self.requestsList.scrollToBottom) self.requestsList.selectionModel()\ .currentChanged[QModelIndex, QModelIndex]\ .connect(self.__currentChanged) fm = self.fontMetrics() em = fm.width("m") self.requestsList.horizontalHeader().resizeSection(0, em * 5) self.requestsList.horizontalHeader().resizeSection(1, em * 20) self.requestsList.horizontalHeader().resizeSection(3, em * 5) self.requestsList.horizontalHeader().resizeSection(4, em * 15) self.__headersDlg = None def closeEvent(self, evt): """ Protected method called upon closing the dialog. @param evt reference to the close event object (QCloseEvent) """ self.__class__._monitor = None super(E5NetworkMonitor, self).closeEvent(evt) def reject(self): """ Public slot to close the dialog with a Reject status. """ self.__class__._monitor = None super(E5NetworkMonitor, self).reject() def __currentChanged(self, current, previous): """ Private slot to handle a change of the current index. @param current new current index (QModelIndex) @param previous old current index (QModelIndex) """ self.__requestHeaders.setRowCount(0) self.__replyHeaders.setRowCount(0) if not current.isValid(): return row = self.__proxyModel.mapToSource(current).row() req = self.__model.requests[row].request for header in req.rawHeaderList(): self.__requestHeaders.insertRows(0, 1, QModelIndex()) self.__requestHeaders.setData( self.__requestHeaders.index(0, 0), str(header, "utf-8")) self.__requestHeaders.setData( self.__requestHeaders.index(0, 1), str(req.rawHeader(header), "utf-8")) self.__requestHeaders.item(0, 0).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.__requestHeaders.item(0, 1).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) for header in self.__model.requests[row].replyHeaders: self.__replyHeaders.insertRows(0, 1, QModelIndex()) self.__replyHeaders.setData( self.__replyHeaders.index(0, 0), header[0]) self.__replyHeaders.setData( self.__replyHeaders.index(0, 1), header[1]) self.__replyHeaders.item(0, 0).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) self.__replyHeaders.item(0, 1).setFlags( Qt.ItemIsSelectable | Qt.ItemIsEnabled) def __showHeaderDetails(self, index): """ Private slot to show a dialog with the header details. @param index index of the entry to show (QModelIndex) """ if not index.isValid(): return headerList = self.sender() if headerList is None: return row = index.row() name = headerList.model().data(headerList.model().index(row, 0)) value = headerList.model().data(headerList.model().index(row, 1)) if self.__headersDlg is None: from .E5NetworkHeaderDetailsDialog import \ E5NetworkHeaderDetailsDialog self.__headersDlg = E5NetworkHeaderDetailsDialog(self) self.__headersDlg.setData(name, value) self.__headersDlg.show()
class MinibufferInput(QLineEdit): completion_activated = Signal(QModelIndex) FuzzyMatch = Prompt.FuzzyMatch SimpleMatch = Prompt.SimpleMatch def __init__(self, parent, window): QLineEdit.__init__(self, parent) self._completer_model = None self._popup = Popup(window, self) self.textEdited.connect(self._show_completions) self._popup.installEventFilter(self) self.installEventFilter(self) self._eat_focusout = False self._proxy_model = QSortFilterProxyModel(self) self._proxy_model.setFilterKeyColumn(-1) self._popup.setModel(self._proxy_model) self._popup.activated.connect(self._on_completion_activated) self._popup.selectionModel().currentRowChanged.connect( self._on_row_changed) self._right_italic_text = "" self._mark = False self.configure_completer({}) def configure_completer(self, opts): self._popup._max_visible_items = opts.get("max-visible-items", 10) self._match = opts.get("match", self.SimpleMatch) self._autocomplete_single = opts.get("autocomplete-single", True) self._autocomplete = opts.get("autocomplete", False) if self._autocomplete: self._autocomplete_single = False self._complete_empty = opts.get("complete-empty", False) def keymap(self): prompt = self.parent()._prompt if prompt and prompt.keymap: return prompt.keymap return KEYMAP def eventFilter(self, obj, event): etype = event.type() if etype == QEvent.FocusOut and obj == self and self._eat_focusout \ and self._popup.isVisible(): # keep the focus on the line edit return True elif etype == QEvent.MouseButtonPress: # if we've clicked in the widget (or its descendant), let it handle # the click pos = obj.mapToGlobal(event.pos()) target = QApplication.widgetAt(pos) if target and (self.isAncestorOf(target) or target == self): if not self._popup.underMouse(): self._popup.hide() target.event(event) return True if not self._popup.underMouse(): self._popup.hide() return True elif etype in (QEvent.KeyPress, QEvent.KeyRelease): # send event to the line edit self._eat_focusout = True self.event(event) self._eat_focusout = False return True return QLineEdit.eventFilter(self, obj, event) def event(self, evt): t = evt.type() if t == QEvent.Show: LOCAL_KEYMAP_SETTER.minibuffer_input_focus_changed(self, True) elif t == QEvent.Hide: LOCAL_KEYMAP_SETTER.minibuffer_input_focus_changed(self, False) return QLineEdit.event(self, evt) def set_completer_model(self, completer_model): self._proxy_model.setSourceModel(completer_model) def completer_model(self): return self._proxy_model.sourceModel() def set_match(self, type): self._match = type if self._popup.isVisible(): self._show_completions(self.text()) def _on_row_changed(self, current, old): if self._autocomplete: self.complete(hide_popup=False) def _show_completions(self, txt, force=False): force = force or self._complete_empty if self._match is not None: if self._match == self.SimpleMatch: pattern = "^" + QRegExp.escape(txt) elif self._match == self.FuzzyMatch: pattern = ".*".join(QRegExp.escape(t) for t in txt.split()) self._proxy_model.setFilterRegExp( QRegExp(pattern, Qt.CaseInsensitive)) else: self._proxy_model.setFilterRegExp(None) if self._proxy_model.rowCount() == 0: self._popup.hide() elif not txt and not force: self._popup.hide() else: self._popup.popup() def show_completions(self, filter_text=None): self._show_completions( filter_text if filter_text is not None else self.text(), True) def _on_completion_activated(self, index, hide_popup=True): if hide_popup: self._popup.hide() model = index.model() if index.column() != 0: index = model.index(index.row(), 0) self.setText(model.data(index)) self.completion_activated.emit(model.mapToSource(index)) def popup(self): return self._popup def complete(self, hide_popup=True): if not self._popup.isVisible(): return index = self._popup.selectionModel().currentIndex() if index.isValid(): self._on_completion_activated(index, hide_popup=hide_popup) elif self._autocomplete_single and self._proxy_model.rowCount() == 1: self._on_completion_activated(self._proxy_model.index(0, 0), hide_popup=hide_popup) def select_next_completion(self, forward=True, steps=1): model = self._proxy_model entries = model.rowCount() if entries == 0: return selection = self._popup.selectionModel().currentIndex() if not selection.isValid(): row = 0 if forward else (entries - 1) else: row = selection.row() if forward: row = row + steps if row >= entries: row = 0 else: row = row - steps if row < 0: row = (entries - 1) self._popup.selectRow(row) def select_first_completion(self): self._popup.selectRow(0) def select_last_completion(self): entries = self._proxy_model.rowCount() self._popup.selectRow(entries - 1) def select_next_page_completion(self, forward=True): self.select_next_completion(forward=forward, steps=self._popup._max_visible_items - 1) def mark(self): return self._mark def set_mark(self, value=None): if value is None: value = not self._mark self._mark = value return self._mark def reinit(self): self.setText("") self.setEchoMode(self.Normal) self.setValidator(None) self._right_italic_text = "" def set_right_italic_text(self, text): self._right_italic_text = text self.update() def paintEvent(self, event): QLineEdit.paintEvent(self, event) if not self._right_italic_text: return painter = QPainter(self) font = painter.font() font.setItalic(True) painter.setFont(font) painter.setRenderHint(QPainter.Antialiasing, True) painter.drawText(self.rect().adjusted(0, 0, -10, 0), Qt.AlignRight, self._right_italic_text) @pyqtProperty("QColor") def background_color(self): return self.palette().color(self.backgroundRole()) @background_color.setter def background_color(self, color): palette = self.palette() palette.setColor(self.backgroundRole(), color) self.setPalette(palette)
return self._data.shape[1] def data(self, index, role): if role == Qt.DisplayRole: return "{}".format(self._data[index.row(), index.column()]) return QVariant() if __name__ == '__main__': app = QApplication(sys.argv) table_view = QTableView() my_model = MyModel(None) proxy_model = QSortFilterProxyModel() # <-- proxy_model.setSourceModel(my_model) # <-- table_view.setModel(proxy_model) # <-- #table_view.setModel(my_model) proxy_model.setFilterRegExp(QRegExp("1", Qt.CaseInsensitive, QRegExp.FixedString)) # <-- proxy_model.setFilterKeyColumn(0) # <-- #table_view.setSortingEnabled(True) table_view.show() # The mainloop of the application. The event handling starts from this point. # The exec_() method has an underscore. It is because the exec is a Python keyword. And thus, exec_() was used instead. exit_code = app.exec_() # The sys.exit() method ensures a clean exit.
class BreakPointViewer(QTreeView): """ Class implementing the Breakpoint viewer widget. Breakpoints will be shown with all their details. They can be modified through the context menu of this widget. @signal sourceFile(str, int) emitted to show the source of a breakpoint """ sourceFile = pyqtSignal(str, int) def __init__(self, parent=None): """ Constructor @param parent the parent (QWidget) """ super(BreakPointViewer, self).__init__(parent) self.setObjectName("BreakPointViewer") self.__model = None self.setItemsExpandable(False) self.setRootIsDecorated(False) self.setAlternatingRowColors(True) self.setSelectionMode(QAbstractItemView.ExtendedSelection) self.setSelectionBehavior(QAbstractItemView.SelectRows) self.setWindowTitle(self.tr("Breakpoints")) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.__showContextMenu) self.doubleClicked.connect(self.__doubleClicked) self.__createPopupMenus() self.condHistory = [] self.fnHistory = [] self.fnHistory.append('') def setModel(self, model): """ Public slot to set the breakpoint model. @param model reference to the breakpoint model (BreakPointModel) """ self.__model = model self.sortingModel = QSortFilterProxyModel() self.sortingModel.setDynamicSortFilter(True) self.sortingModel.setSourceModel(self.__model) super(BreakPointViewer, self).setModel(self.sortingModel) header = self.header() header.setSortIndicator(0, Qt.AscendingOrder) header.setSortIndicatorShown(True) if qVersion() >= "5.0.0": header.setSectionsClickable(True) else: header.setClickable(True) self.setSortingEnabled(True) self.__layoutDisplay() def __layoutDisplay(self): """ Private slot to perform a layout operation. """ self.__resizeColumns() self.__resort() def __resizeColumns(self): """ Private slot to resize the view when items get added, edited or deleted. """ self.header().resizeSections(QHeaderView.ResizeToContents) self.header().setStretchLastSection(True) def __resort(self): """ Private slot to resort the tree. """ self.model().sort(self.header().sortIndicatorSection(), self.header().sortIndicatorOrder()) def __toSourceIndex(self, index): """ Private slot to convert an index to a source index. @param index index to be converted (QModelIndex) @return mapped index (QModelIndex) """ return self.sortingModel.mapToSource(index) def __fromSourceIndex(self, sindex): """ Private slot to convert a source index to an index. @param sindex source index to be converted (QModelIndex) @return mapped index (QModelIndex) """ return self.sortingModel.mapFromSource(sindex) def __setRowSelected(self, index, selected=True): """ Private slot to select a complete row. @param index index determining the row to be selected (QModelIndex) @param selected flag indicating the action (bool) """ if not index.isValid(): return if selected: flags = QItemSelectionModel.SelectionFlags( QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) else: flags = QItemSelectionModel.SelectionFlags( QItemSelectionModel.Deselect | QItemSelectionModel.Rows) self.selectionModel().select(index, flags) def __createPopupMenus(self): """ Private method to generate the popup menus. """ self.menu = QMenu() self.menu.addAction(self.tr("Add"), self.__addBreak) self.menu.addAction(self.tr("Edit..."), self.__editBreak) self.menu.addSeparator() self.menu.addAction(self.tr("Enable"), self.__enableBreak) self.menu.addAction(self.tr("Enable all"), self.__enableAllBreaks) self.menu.addSeparator() self.menu.addAction(self.tr("Disable"), self.__disableBreak) self.menu.addAction(self.tr("Disable all"), self.__disableAllBreaks) self.menu.addSeparator() self.menu.addAction(self.tr("Delete"), self.__deleteBreak) self.menu.addAction(self.tr("Delete all"), self.__deleteAllBreaks) self.menu.addSeparator() self.menu.addAction(self.tr("Goto"), self.__showSource) self.menu.addSeparator() self.menu.addAction(self.tr("Configure..."), self.__configure) self.backMenuActions = {} self.backMenu = QMenu() self.backMenu.addAction(self.tr("Add"), self.__addBreak) self.backMenuActions["EnableAll"] = \ self.backMenu.addAction(self.tr("Enable all"), self.__enableAllBreaks) self.backMenuActions["DisableAll"] = \ self.backMenu.addAction(self.tr("Disable all"), self.__disableAllBreaks) self.backMenuActions["DeleteAll"] = \ self.backMenu.addAction(self.tr("Delete all"), self.__deleteAllBreaks) self.backMenu.aboutToShow.connect(self.__showBackMenu) self.backMenu.addSeparator() self.backMenu.addAction(self.tr("Configure..."), self.__configure) self.multiMenu = QMenu() self.multiMenu.addAction(self.tr("Add"), self.__addBreak) self.multiMenu.addSeparator() self.multiMenu.addAction(self.tr("Enable selected"), self.__enableSelectedBreaks) self.multiMenu.addAction(self.tr("Enable all"), self.__enableAllBreaks) self.multiMenu.addSeparator() self.multiMenu.addAction(self.tr("Disable selected"), self.__disableSelectedBreaks) self.multiMenu.addAction(self.tr("Disable all"), self.__disableAllBreaks) self.multiMenu.addSeparator() self.multiMenu.addAction(self.tr("Delete selected"), self.__deleteSelectedBreaks) self.multiMenu.addAction(self.tr("Delete all"), self.__deleteAllBreaks) self.multiMenu.addSeparator() self.multiMenu.addAction(self.tr("Configure..."), self.__configure) def __showContextMenu(self, coord): """ Private slot to show the context menu. @param coord the position of the mouse pointer (QPoint) """ cnt = self.__getSelectedItemsCount() if cnt <= 1: index = self.indexAt(coord) if index.isValid(): cnt = 1 self.__setRowSelected(index) coord = self.mapToGlobal(coord) if cnt > 1: self.multiMenu.popup(coord) elif cnt == 1: self.menu.popup(coord) else: self.backMenu.popup(coord) def __clearSelection(self): """ Private slot to clear the selection. """ for index in self.selectedIndexes(): self.__setRowSelected(index, False) def __addBreak(self): """ Private slot to handle the add breakpoint context menu entry. """ from .EditBreakpointDialog import EditBreakpointDialog dlg = EditBreakpointDialog((self.fnHistory[0], None), None, self.condHistory, self, modal=1, addMode=1, filenameHistory=self.fnHistory) if dlg.exec_() == QDialog.Accepted: fn, line, cond, temp, enabled, count = dlg.getAddData() if fn is not None: if fn in self.fnHistory: self.fnHistory.remove(fn) self.fnHistory.insert(0, fn) if cond: if cond in self.condHistory: self.condHistory.remove(cond) self.condHistory.insert(0, cond) self.__model.addBreakPoint(fn, line, (cond, temp, enabled, count)) self.__resizeColumns() self.__resort() def __doubleClicked(self, index): """ Private slot to handle the double clicked signal. @param index index of the entry that was double clicked (QModelIndex) """ if index.isValid(): self.__editBreakpoint(index) def __editBreak(self): """ Private slot to handle the edit breakpoint context menu entry. """ index = self.currentIndex() if index.isValid(): self.__editBreakpoint(index) def __editBreakpoint(self, index): """ Private slot to edit a breakpoint. @param index index of breakpoint to be edited (QModelIndex) """ sindex = self.__toSourceIndex(index) if sindex.isValid(): bp = self.__model.getBreakPointByIndex(sindex) if not bp: return fn, line, cond, temp, enabled, count = bp[:6] from .EditBreakpointDialog import EditBreakpointDialog dlg = EditBreakpointDialog( (fn, line), (cond, temp, enabled, count), self.condHistory, self, modal=True) if dlg.exec_() == QDialog.Accepted: cond, temp, enabled, count = dlg.getData() if cond: if cond in self.condHistory: self.condHistory.remove(cond) self.condHistory.insert(0, cond) self.__model.setBreakPointByIndex( sindex, fn, line, (cond, temp, enabled, count)) self.__resizeColumns() self.__resort() def __setBpEnabled(self, index, enabled): """ Private method to set the enabled status of a breakpoint. @param index index of breakpoint to be enabled/disabled (QModelIndex) @param enabled flag indicating the enabled status to be set (boolean) """ sindex = self.__toSourceIndex(index) if sindex.isValid(): self.__model.setBreakPointEnabledByIndex(sindex, enabled) def __enableBreak(self): """ Private slot to handle the enable breakpoint context menu entry. """ index = self.currentIndex() self.__setBpEnabled(index, True) self.__resizeColumns() self.__resort() def __enableAllBreaks(self): """ Private slot to handle the enable all breakpoints context menu entry. """ index = self.model().index(0, 0) while index.isValid(): self.__setBpEnabled(index, True) index = self.indexBelow(index) self.__resizeColumns() self.__resort() def __enableSelectedBreaks(self): """ Private slot to handle the enable selected breakpoints context menu entry. """ for index in self.selectedIndexes(): if index.column() == 0: self.__setBpEnabled(index, True) self.__resizeColumns() self.__resort() def __disableBreak(self): """ Private slot to handle the disable breakpoint context menu entry. """ index = self.currentIndex() self.__setBpEnabled(index, False) self.__resizeColumns() self.__resort() def __disableAllBreaks(self): """ Private slot to handle the disable all breakpoints context menu entry. """ index = self.model().index(0, 0) while index.isValid(): self.__setBpEnabled(index, False) index = self.indexBelow(index) self.__resizeColumns() self.__resort() def __disableSelectedBreaks(self): """ Private slot to handle the disable selected breakpoints context menu entry. """ for index in self.selectedIndexes(): if index.column() == 0: self.__setBpEnabled(index, False) self.__resizeColumns() self.__resort() def __deleteBreak(self): """ Private slot to handle the delete breakpoint context menu entry. """ index = self.currentIndex() sindex = self.__toSourceIndex(index) if sindex.isValid(): self.__model.deleteBreakPointByIndex(sindex) def __deleteAllBreaks(self): """ Private slot to handle the delete all breakpoints context menu entry. """ self.__model.deleteAll() def __deleteSelectedBreaks(self): """ Private slot to handle the delete selected breakpoints context menu entry. """ idxList = [] for index in self.selectedIndexes(): sindex = self.__toSourceIndex(index) if sindex.isValid() and index.column() == 0: idxList.append(sindex) self.__model.deleteBreakPoints(idxList) def __showSource(self): """ Private slot to handle the goto context menu entry. """ index = self.currentIndex() sindex = self.__toSourceIndex(index) bp = self.__model.getBreakPointByIndex(sindex) if not bp: return fn, line = bp[:2] self.sourceFile.emit(fn, line) def highlightBreakpoint(self, fn, lineno): """ Public slot to handle the clientLine signal. @param fn filename of the breakpoint (string) @param lineno line number of the breakpoint (integer) """ sindex = self.__model.getBreakPointIndex(fn, lineno) if sindex.isValid(): return index = self.__fromSourceIndex(sindex) if index.isValid(): self.__clearSelection() self.__setRowSelected(index, True) def handleResetUI(self): """ Public slot to reset the breakpoint viewer. """ self.__clearSelection() def __showBackMenu(self): """ Private slot to handle the aboutToShow signal of the background menu. """ if self.model().rowCount() == 0: self.backMenuActions["EnableAll"].setEnabled(False) self.backMenuActions["DisableAll"].setEnabled(False) self.backMenuActions["DeleteAll"].setEnabled(False) else: self.backMenuActions["EnableAll"].setEnabled(True) self.backMenuActions["DisableAll"].setEnabled(True) self.backMenuActions["DeleteAll"].setEnabled(True) def __getSelectedItemsCount(self): """ Private method to get the count of items selected. @return count of items selected (integer) """ count = len(self.selectedIndexes()) // (self.__model.columnCount() - 1) # column count is 1 greater than selectable return count def __configure(self): """ Private method to open the configuration dialog. """ e5App().getObject("UserInterface").showPreferences( "debuggerGeneralPage")
class MLSRuleQueryTab(AnalysisTab): """An MLS rule query.""" def __init__(self, parent, policy, perm_map): super(MLSRuleQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = MLSRuleQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.mlsrulequery").removeHandler(self.handler) def setupUi(self): self.load_ui("apol/mlsrulequery.ui") # set up source/target autocompletion typeattr_completion_list = [str(t) for t in self.policy.types()] typeattr_completion_list.extend(str(a) for a in self.policy.typeattributes()) typeattr_completer_model = QStringListModel(self) typeattr_completer_model.setStringList(sorted(typeattr_completion_list)) self.typeattr_completion = QCompleter() self.typeattr_completion.setModel(typeattr_completer_model) self.source.setCompleter(self.typeattr_completion) self.target.setCompleter(self.typeattr_completion) # setup indications of errors on source/target/default self.errors = set() self.orig_palette = self.source.palette() self.error_palette = self.source.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_source_error() self.clear_target_error() self.clear_default_error() # populate class list self.class_model = SEToolsListModel(self) self.class_model.item_list = sorted(self.policy.classes()) self.tclass.setModel(self.class_model) # set up results self.table_results_model = MLSRuleTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(1, Qt.AscendingOrder) # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.mlsrulequery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.set_source_regex(self.source_regex.isChecked()) self.set_target_regex(self.target_regex.isChecked()) self.criteria_frame.setHidden(not self.criteria_expander.isChecked()) self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.buttonBox.clicked.connect(self.run) self.clear_ruletypes.clicked.connect(self.clear_all_ruletypes) self.all_ruletypes.clicked.connect(self.set_all_ruletypes) self.source.textEdited.connect(self.clear_source_error) self.source.editingFinished.connect(self.set_source) self.source_regex.toggled.connect(self.set_source_regex) self.target.textEdited.connect(self.clear_target_error) self.target.editingFinished.connect(self.set_target) self.target_regex.toggled.connect(self.set_target_regex) self.tclass.selectionModel().selectionChanged.connect(self.set_tclass) self.invert_class.clicked.connect(self.invert_tclass_selection) self.default_range.textEdited.connect(self.clear_default_error) self.default_range.editingFinished.connect(self.set_default_range) # # Ruletype criteria # def _set_ruletypes(self, value): self.range_transition.setChecked(value) def set_all_ruletypes(self): self._set_ruletypes(True) def clear_all_ruletypes(self): self._set_ruletypes(False) # # Source criteria # def clear_source_error(self): self.clear_criteria_error(self.source, "Match the source type/attribute of the rule.") def set_source(self): try: self.query.source = self.source.text() except Exception as ex: self.log.error("Source type/attribute error: {0}".format(ex)) self.set_criteria_error(self.source, ex) def set_source_regex(self, state): self.log.debug("Setting source_regex {0}".format(state)) self.query.source_regex = state self.clear_source_error() self.set_source() # # Target criteria # def clear_target_error(self): self.clear_criteria_error(self.target, "Match the target type/attribute of the rule.") def set_target(self): try: self.query.target = self.target.text() except Exception as ex: self.log.error("Target type/attribute error: {0}".format(ex)) self.set_criteria_error(self.target, ex) def set_target_regex(self, state): self.log.debug("Setting target_regex {0}".format(state)) self.query.target_regex = state self.clear_target_error() self.set_target() # # Class criteria # def set_tclass(self): selected_classes = [] for index in self.tclass.selectionModel().selectedIndexes(): selected_classes.append(self.class_model.data(index, Qt.UserRole)) self.query.tclass = selected_classes def invert_tclass_selection(self): invert_list_selection(self.tclass.selectionModel()) # # Default criteria # def clear_default_error(self): self.clear_criteria_error(self.default_range, "Match the default type the rule.") def set_default_range(self): try: self.query.default = self.default_range.text() except Exception as ex: self.log.error("Default range error: {0}".format(ex)) self.set_criteria_error(self.default_range, ex) # # Save/Load tab # def save(self): """Return a dictionary of settings.""" if self.errors: raise TabFieldError("Field(s) are in error: {0}". format(" ".join(o.objectName() for o in self.errors))) settings = {} save_checkboxes(self, settings, ["criteria_expander", "notes_expander", "range_transition", "source_indirect", "source_regex", "target_indirect", "target_regex"]) save_lineedits(self, settings, ["source", "target", "default_range"]) save_listviews(self, settings, ["tclass"]) save_textedits(self, settings, ["notes"]) return settings def load(self, settings): load_checkboxes(self, settings, ["criteria_expander", "notes_expander", "range_transition", "source_indirect", "source_regex", "target_indirect", "target_regex"]) load_lineedits(self, settings, ["source", "target", "default_range"]) load_listviews(self, settings, ["tclass"]) load_textedits(self, settings, ["notes"]) # # Results runner # def run(self, button): # right now there is only one button. self.query.ruletype = ['range_transition'] self.query.source_indirect = self.source_indirect.isChecked() self.query.target_indirect = self.target_indirect.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} MLS rule(s) found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class MasternodesWidget(QWidget): """Widget that displays masternodes.""" def __init__(self, manager, parent=None): super(MasternodesWidget, self).__init__(parent) self.manager = manager self.model = MasternodesModel(self.manager) self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(self.model) self.view = QTableView() self.view.setModel(self.proxy_model) for header in [self.view.horizontalHeader(), self.view.verticalHeader()]: header.setHighlightSections(False) header = self.view.horizontalHeader() header.setSectionResizeMode(MasternodesModel.ALIAS, QHeaderView.ResizeToContents) header.setSectionResizeMode(MasternodesModel.VIN, QHeaderView.Stretch) header.setSectionResizeMode(MasternodesModel.COLLATERAL, QHeaderView.ResizeToContents) header.setSectionResizeMode(MasternodesModel.DELEGATE, QHeaderView.ResizeToContents) self.view.verticalHeader().setVisible(False) self.view.setSelectionMode(QAbstractItemView.SingleSelection) self.view.setSelectionBehavior(QAbstractItemView.SelectRows) self.view.setSortingEnabled(True) self.view.sortByColumn(self.model.ALIAS, Qt.AscendingOrder) vbox = QVBoxLayout() vbox.setContentsMargins(0, 0, 0, 0) vbox.addWidget(self.view) self.setLayout(vbox) def select_masternode(self, alias): """Select the row that represents alias.""" self.view.clearSelection() for i in range(self.proxy_model.rowCount()): idx = self.proxy_model.index(i, 0) mn_alias = str(self.proxy_model.data(idx)) if mn_alias == alias: self.view.selectRow(i) break def populate_collateral_key(self, row): """Fill in the collateral key for a masternode based on its collateral output. row refers to the desired row in the proxy model, not the actual model. """ mn = self.masternode_for_row(row) self.manager.populate_masternode_output(mn.alias) # Emit dataChanged for the collateral key. index = self.model.index(row, self.model.COLLATERAL) self.model.dataChanged.emit(index, index) def refresh_items(self): self.model.dataChanged.emit(QModelIndex(), QModelIndex()) def add_masternode(self, masternode, save = True): self.model.add_masternode(masternode, save=save) def remove_masternode(self, alias, save = True): self.model.remove_masternode(alias, save=save) def masternode_for_row(self, row): idx = self.proxy_model.mapToSource(self.proxy_model.index(row, 0)) return self.model.masternode_for_row(idx.row()) def import_masternode_conf_lines(self, conf_lines, pw): return self.model.import_masternode_conf_lines(conf_lines, pw)
class GuiPropertyDock(): ''' GuiPropertyDock ''' dock_name = "properties-dock" dock_displayed_name = _("Properties") def __init__(self): ''' Constructor ''' super(GuiPropertyDock, self).__init__() self.widget = None self.core_part = None # CorePropertyDock self._sheet_id = None self.tree_sheet = None @property def sheet_id(self): return self._sheet_id @sheet_id.setter def sheet_id(self, sheet_id): if self._sheet_id == sheet_id: pass self._sheet_id = sheet_id if self.sheet_id is not None: self.tree_sheet = gui_cfg.core.tree_sheet_manager.get_tree_sheet_from_sheet_id( self.sheet_id) self.core_part = self.tree_sheet.get_instance_of(self.dock_name) self.core_part.sheet_id = sheet_id def get_widget(self): if self.widget is None: self.widget = QWidget() self.ui = properties_dock_ui.Ui_PropertiesDock() self.ui.setupUi(self.widget) if self.tree_sheet is not None and self.core_part is not None: table_model = self.core_part.property_table_model # filter : self.filter = QSortFilterProxyModel(self.widget) self.filter.setFilterKeyColumn(-1) self.filter.setFilterCaseSensitivity(False) self.filter.setSourceModel(table_model) # model : self.ui.tableView.setModel(self.filter) # connect : self.ui.addPropButton.clicked.connect(self.add_property_row) self.ui.removePropButton.clicked.connect( self.remove_property_row) self.ui.filterLineEdit.textChanged.connect( self.filter.setFilterFixedString) self.ui.tableView.clicked.connect(self.set_current_row) self.widget.gui_part = self return self.widget @pyqtSlot() def add_property_row(self): index = self.filter.mapToSource(self.ui.tableView.currentIndex()) self.core_part.add_property_row(index) @pyqtSlot() def remove_property_row(self): index = self.filter.mapToSource(self.ui.tableView.currentIndex()) self.core_part.remove_property_row(index) @pyqtSlot('QModelIndex') def set_current_row(self, model_index): self.ui.tableView.setCurrentIndex(model_index)
def edit_contact_dialog(wallet_api, contact_key=None): editing = contact_key is not None if editing: title = _("Edit Contact") else: title = _("New Contact") d = WindowModalDialog(wallet_api.wallet_window, title) vbox = QVBoxLayout(d) vbox.addWidget(QLabel(title + ':')) def _contact_insert_completion(text): if text: index = combo1.findText(text) combo1.setCurrentIndex(index) identity_line = QLineEdit() name_line = QLineEdit() combo1 = QComboBox() combo1.setFixedWidth(280) combo1.setEditable(True) # add a filter model to filter matching items contact_filter_model = QSortFilterProxyModel(combo1) contact_filter_model.setFilterCaseSensitivity(Qt.CaseInsensitive) contact_filter_model.setSourceModel(combo1.model()) contact_completer = QCompleter(contact_filter_model, combo1) contact_completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) combo1.setCompleter(contact_completer) ok_button = OkButton(d) ok_button.setEnabled(False) def _validate_form() -> None: def _set_validation_state(element, is_valid) -> None: if not is_valid: element.setStyleSheet("border: 1px solid red") else: element.setStyleSheet("") can_submit = True system_name = combo1.currentText().lower().strip() is_valid = True try: system_id = get_system_id(system_name) except ContactDataError: system_id = None is_valid = False _set_validation_state(combo1, is_valid) can_submit = can_submit and is_valid identity_text = identity_line.text().strip() if system_id is None: identity_result = IdentityCheckResult.Invalid else: identity_result = wallet_api.check_identity_valid(system_id, identity_text, skip_exists=editing) is_valid = identity_result == IdentityCheckResult.Ok _set_validation_state(identity_line, is_valid) if is_valid: identity_line.setToolTip("") elif identity_result == IdentityCheckResult.Invalid: if system_id == IdentitySystem.OnChain: identity_line.setToolTip(_("Not a valid Bitcoin address")) else: identity_line.setToolTip(_("Incorrect format")) elif identity_result == IdentityCheckResult.InUse: identity_line.setToolTip(_("Already in use")) can_submit = can_submit and is_valid name_text = name_line.text().strip() name_result = wallet_api.check_label(name_text) is_valid = (name_result == IdentityCheckResult.Ok or editing and name_result == IdentityCheckResult.InUse) _set_validation_state(name_line, is_valid) if is_valid: name_line.setToolTip("") elif name_result == IdentityCheckResult.Invalid: name_line.setToolTip(_("Name too short")) elif name_result == IdentityCheckResult.InUse: name_line.setToolTip(_("Name already in use")) can_submit = can_submit and is_valid ok_button.setEnabled(can_submit) def _contact_text_changed(text: str) -> None: _validate_form() combo1.lineEdit().textEdited.connect(contact_filter_model.setFilterFixedString) combo1.editTextChanged.connect(_contact_text_changed) identity_line.textChanged.connect(_contact_text_changed) name_line.textChanged.connect(_contact_text_changed) contact_completer.activated.connect(_contact_insert_completion) combo1.addItems(list(IDENTITY_SYSTEM_NAMES.values())) grid = QGridLayout() identity_line.setFixedWidth(280) name_line.setFixedWidth(280) grid.addWidget(QLabel(_("Identity Type")), 1, 0) grid.addWidget(combo1, 1, 1) grid.addWidget(QLabel(_("Identity")), 2, 0) grid.addWidget(identity_line, 2, 1) grid.addWidget(QLabel(_("Name")), 3, 0) grid.addWidget(name_line, 3, 1) vbox.addLayout(grid) vbox.addLayout(Buttons(CancelButton(d), ok_button)) if contact_key is None: combo1.lineEdit().setText(IDENTITY_SYSTEM_NAMES[IdentitySystem.OnChain]) identity_line.setFocus() else: entry = wallet_api.get_contact(contact_key[0]) identity = [ ci for ci in entry.identities if ci.identity_id == contact_key[1] ][0] combo1.lineEdit().setText(IDENTITY_SYSTEM_NAMES[identity.system_id]) identity_line.setText(identity.system_data) name_line.setText(entry.label) name_line.setFocus() if d.exec_(): name_text = name_line.text().strip() identity_text = identity_line.text().strip() system_id = get_system_id(combo1.currentText()) if contact_key is not None: contact = wallet_api.get_contact(contact_key[0]) identity = [ ci for ci in contact.identities if ci.identity_id == contact_key[1] ][0] if contact_key[1] != identity.identity_id: wallet_api.remove_identity(contact_key[0], contact_key[1]) wallet_api.add_identity(contact_key[0], system_id, identity_text) if contact.label != name_text: wallet_api.set_label(contact_key[0], name_text) else: wallet_api.add_contact(system_id, name_text, identity_text)
class CommonQueryTab(AnalysisTab): """Common browser and query tab.""" def __init__(self, parent, policy, perm_map): super(CommonQueryTab, self).__init__(parent) self.log = logging.getLogger(__name__) self.policy = policy self.query = CommonQuery(policy) self.setupUi() def __del__(self): self.thread.quit() self.thread.wait(5000) logging.getLogger("setools.commonquery").removeHandler(self.handler) def setupUi(self): self.load_ui("commonquery.ui") # populate commons list self.common_model = SEToolsListModel(self) self.common_model.item_list = sorted(c for c in self.policy.commons()) self.commons.setModel(self.common_model) # populate perm list self.perms_model = SEToolsListModel(self) perms = set() for com in self.policy.commons(): perms.update(com.perms) self.perms_model.item_list = sorted(perms) self.perms.setModel(self.perms_model) # set up results self.table_results_model = CommonTableModel(self) self.sort_proxy = QSortFilterProxyModel(self) self.sort_proxy.setSourceModel(self.table_results_model) self.table_results.setModel(self.sort_proxy) self.table_results.sortByColumn(0, Qt.AscendingOrder) # setup indications of errors self.errors = set() self.orig_palette = self.name.palette() self.error_palette = self.name.palette() self.error_palette.setColor(QPalette.Base, Qt.red) self.clear_name_error() # set up processing thread self.thread = QThread() self.worker = QueryResultsUpdater(self.query, self.table_results_model) self.worker.moveToThread(self.thread) self.worker.raw_line.connect(self.raw_results.appendPlainText) self.worker.finished.connect(self.update_complete) self.worker.finished.connect(self.thread.quit) self.thread.started.connect(self.worker.update) # create a "busy, please wait" dialog self.busy = QProgressDialog(self) self.busy.setModal(True) self.busy.setRange(0, 0) self.busy.setMinimumDuration(0) self.busy.canceled.connect(self.thread.requestInterruption) self.busy.reset() # update busy dialog from query INFO logs self.handler = LogHandlerToSignal() self.handler.message.connect(self.busy.setLabelText) logging.getLogger("setools.commonquery").addHandler(self.handler) # Ensure settings are consistent with the initial .ui state self.set_name_regex(self.name_regex.isChecked()) self.notes.setHidden(not self.notes_expander.isChecked()) # connect signals self.commons.doubleClicked.connect(self.get_detail) self.commons.get_detail.triggered.connect(self.get_detail) self.name.textEdited.connect(self.clear_name_error) self.name.editingFinished.connect(self.set_name) self.name_regex.toggled.connect(self.set_name_regex) self.perms.selectionModel().selectionChanged.connect(self.set_perms) self.invert_perms.clicked.connect(self.invert_perms_selection) self.buttonBox.clicked.connect(self.run) # # Class browser # def get_detail(self): # .ui is set for single item selection. index = self.commons.selectedIndexes()[0] item = self.common_model.data(index, Qt.UserRole) self.log.debug("Generating detail window for {0}".format(item)) common_detail(self, item) # # Name criteria # def clear_name_error(self): self.clear_criteria_error(self.name, "Match the common name.") def set_name(self): try: self.query.name = self.name.text() except Exception as ex: self.log.error("Common name error: {0}".format(ex)) self.set_criteria_error(self.name, ex) def set_name_regex(self, state): self.log.debug("Setting name_regex {0}".format(state)) self.query.name_regex = state self.clear_name_error() self.set_name() # # Permissions criteria # def set_perms(self): selected_perms = [] for index in self.perms.selectionModel().selectedIndexes(): selected_perms.append(self.perms_model.data(index, Qt.UserRole)) self.query.perms = selected_perms def invert_perms_selection(self): invert_list_selection(self.perms.selectionModel()) # # Save/Load tab # def save(self): """Return a dictionary of settings.""" if self.errors: raise TabFieldError("Field(s) are in error: {0}". format(" ".join(o.objectName() for o in self.errors))) settings = {} save_checkboxes(self, settings, ["criteria_expander", "notes_expander", "name_regex", "perms_equal"]) save_lineedits(self, settings, ["name"]) save_listviews(self, settings, ["perms"]) save_textedits(self, settings, ["notes"]) return settings def load(self, settings): load_checkboxes(self, settings, ["criteria_expander", "notes_expander", "name_regex", "perms_equal"]) load_lineedits(self, settings, ["name"]) load_listviews(self, settings, ["perms"]) load_textedits(self, settings, ["notes"]) # # Results runner # def run(self, button): # right now there is only one button. self.query.perms_equal = self.perms_equal.isChecked() # start processing self.busy.setLabelText("Processing query...") self.busy.show() self.raw_results.clear() self.thread.start() def update_complete(self, count): self.log.info("{0} common(s) found.".format(count)) # update sizes/location of result displays if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's columns; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeColumnsToContents() # If the permissions column width is too long, pull back # to a reasonable size header = self.table_results.horizontalHeader() if header.sectionSize(1) > 400: header.resizeSection(1, 400) if not self.busy.wasCanceled(): self.busy.setLabelText("Resizing the result table's rows; GUI may be unresponsive") self.busy.repaint() self.table_results.resizeRowsToContents() if not self.busy.wasCanceled(): self.busy.setLabelText("Moving the raw result to top; GUI may be unresponsive") self.busy.repaint() self.raw_results.moveCursor(QTextCursor.Start) self.busy.reset()
class Example(QWidget): def __init__(self): super().__init__() self.setGeometry(300, 300, 400, 240) self.setWindowTitle("Filtering data") self.initData() self.initUI() def initData(self): words = ["radar", "robert", "Rome", "rodeo", "rust", "ready", "robot", "rampart", "RAM", "ROM"] self.model = QStringListModel(words) self.filterModel = QSortFilterProxyModel(self) self.filterModel.setSourceModel(self.model) self.filterModel.setDynamicSortFilter(True) def initUI(self): grid = QGridLayout() grid.setSpacing(10) self.lv = QListView(self) self.lv.setModel(self.filterModel) grid.addWidget(self.lv, 0, 0, 2, 2) self.filText = QLineEdit(self) grid.addWidget(self.filText, 0, 3, Qt.AlignTop) self.case = QCheckBox("Case sensitive", self) grid.addWidget(self.case, 1, 3, Qt.AlignTop) self.filterCombo = QComboBox(self) self.filterCombo.addItem("Regular expression", QVariant(QRegExp.RegExp)) self.filterCombo.addItem("Wildcard", QVariant(QRegExp.Wildcard)) self.filterCombo.addItem("Fixed string", QVariant(QRegExp.FixedString)) grid.addWidget(self.filterCombo, 2, 0) self.filterCombo.activated[str].connect(self.filterItems) self.filText.textChanged[str].connect(self.filterItems) self.case.toggled[bool].connect(self.filterItems) self.setLayout(grid) def filterItems(self, value): idx = self.filterCombo.currentIndex() syntaxType = self.filterCombo.itemData(idx) syntax = QRegExp.PatternSyntax(syntaxType) if self.case.isChecked(): case = Qt.CaseSensitive else: