class ParameterWindow(QMainWindow): def __init__(self, dplugin, api, pl_win_name = '',parent = None): QMainWindow.__init__(self, parent) # Build the tree for the parameters self.parameterTree = QTreeView(self) self.parameterTree.setObjectName("parameterTree") # Add it as the central widget self.setCentralWidget(self.parameterTree) # Add the DParameterTreeModel to the parameter tree self.dparameterModel = DParameterTreeModel() self.dparameterModel.setHorizontalHeaderLabels(['Name','']) self.parameterTree.setModel(self.dparameterModel) self.parameterTree.setUniformRowHeights(True) # connect the callback function for value changes self.dparameterModel.dataChanged.connect(self.data_changed_parameter_model) self.dpluign_object = dplugin self.api = api self.setWindowTitle(pl_win_name+' Parameter') def show_paramters(self, para_list): """ Shows the list of parameters and values in the parameter window :param para_list: :return: """ for dparameter_name in sorted(para_list): dparameter = para_list[dparameter_name] dparameter_item = DParameterTreeItem(dparameter) self.dparameterModel.appendRow(dparameter_item) self.parameterTree.resizeColumnToContents(0) self.parameterTree.resizeColumnToContents(1) self.parameterTree.expandAll() fh = self.parameterTree.fontMetrics().height() if len(para_list.keys()) > 8: self.setFixedHeight(fh*9+fh+25) else: self.setFixedHeight(fh*len(para_list.keys())+fh+fh+25) def data_changed_parameter_model(self, index, n): """ This function is called when a dparameter value is changed by editing the 'value'-column. :param index: Index of current changed dparameter :param n: None :return: """ dparameter = self.parameterTree.model().data(index, Qt.UserRole) self.api.do_set_parameter(self.dpluign_object.id, dparameter.name, dparameter.value)
class BinaryDetailsDialog(QDialog): def __init__(self, data, *args, title=None, **kwargs): super().__init__(*args, **kwargs) self.setWindowFlag(Qt.WindowContextHelpButtonHint, False) self._data = data if not title: self.setWindowTitle(f"{self._data.binaries[0].binary_name} ({self._data.release_name})") self.setup_interface() def setup_interface(self): self.resize(600, 380) self.layout = QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.details_tree_model = BinaryDetailsTreeModel(self._data) self.details_tree_view = QTreeView() self.details_tree_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.details_tree_view.setAnimated(True) self.details_tree_view.setModel(self.details_tree_model) self.details_tree_view.expandAll() self.details_tree_view.resizeColumnToContents(0) self.details_tree_view.doubleClicked.connect( lambda index: QApplication.clipboard().setText( self.details_tree_model.data(index, Qt.DisplayRole) ) ) self.layout.addWidget(self.details_tree_view) self.setLayout(self.layout)
def __init__(self, model): super().__init__() self.setWindowTitle("Treeview for nested dict/list") self.setGeometry(300, 300, 600, 800) tree_view = QTreeView() tree_view.setModel(model) tree_view.expandAll() tree_view.resizeColumnToContents(0) self.setCentralWidget(tree_view)
class ItemsView(QDockWidget): def __init__(self, title, parent, items): super().__init__(title, parent) self.setFloating(False) self.set_items(items) def set_items(self, items): self.items = items self.itemsTree = QTreeView() self.itemsTree.setHeaderHidden(True) treeModel = QStandardItemModel() rootNode = treeModel.invisibleRootItem() groups = {} for item in items: category = item['data']['category'] node = self.__get_node(item['name']) if (category not in groups): groups[category] = self.__get_node(category) groups[category].appendRow(node) for key in groups: rootNode.appendRow(groups[key]) self.itemsTree.setModel(treeModel) self.itemsTree.expandAll() self.setWidget(self.itemsTree) def set_on_item_selected(self, method): self.itemsTree.selectionModel().selectionChanged.connect( lambda: method(self.__get_selected_item())) def __get_selected_item(self): current = self.itemsTree.currentIndex() found_item = None if (current.parent() != None): for item in self.items: if (item['name'] == current.data()): found_item = item return found_item def __get_node(self, title): node = QStandardItem() node.setEditable(False) node.setText(title) return node
class ReqTreeView(QWidget): def __init__(self): QWidget.__init__(self) self.setLayout(QVBoxLayout()) self.layout().setSpacing(0) self.layout().setContentsMargins(0, 0, 0, 0) self.nodes = {} self.tree_view = QTreeView() self.tree_view.header().close() self.root = QStandardItemModel() self.tree_view.setModel(self.root) self.layout().addWidget(self.tree_view) @pyqtSlot(HTTPRequest) def add_request_item(self, req): path_parts = req.url.geturl(False).split("/") path_parts = path_parts[1:] path_parts = ["/" + p for p in path_parts] path_parts = [req.dest_host] + path_parts if path_parts[0] not in self.nodes: item = PathNodeItem(path_parts[0], path_parts[0]) item.setFlags(item.flags() ^ Qt.ItemIsEditable) self.nodes[path_parts[0]] = item self.root.appendRow(item) else: item = self.nodes[path_parts[0]] item.add_child_path(path_parts[1:]) @pyqtSlot(list) def set_requests(self, reqs): self.clear() for req in reqs: if _include_req(req): self.add_request_item(req) self.tree_view.expandAll() def clear(self): self.nodes = {} self.root = QStandardItemModel() self.tree_view.setModel(self.root)
def showVideoInfoDialog(self, outjson): """ Show Video Information Dialog """ view = QTreeView() model = QJsonModel() view.setModel(model) model.loadJsonFromConsole(outjson) self.VideoInfoDialog = QDialog(self) self.VideoInfoDialog.setWindowTitle("Video Information : " + self.fileName) self.VideoInfoDialog.setWindowIcon( QIcon(":/imgFMV/images/video_information.png")) self.verticalLayout = QVBoxLayout(self.VideoInfoDialog) self.verticalLayout.addWidget(view) view.expandAll() view.header().setSectionResizeMode(QHeaderView.ResizeToContents) self.VideoInfoDialog.setWindowFlags(Qt.Window | Qt.WindowCloseButtonHint) self.VideoInfoDialog.setObjectName("VideoInfoDialog") self.VideoInfoDialog.resize(500, 400) self.VideoInfoDialog.show()
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self._app = QApplication.instance() self._lineEdit = None self.create_menu() self._completer = TreeModelCompleter(None, self) self._completer.setModel(self.model_from_file("./treemodel.txt")) self._completer.set_separator(".") self._completer.highlighted[QModelIndex].connect(self.highlight) central_widget = QWidget() model_label = QLabel() model_label.setText("Tree Model<br>(Double click items to edit)") mode_label = QLabel() mode_label.setText("Completion Mode") self._mode_combo = QComboBox() self._mode_combo.addItem(tr("Inline")) self._mode_combo.addItem(tr("Filtered Popup")) self._mode_combo.addItem(tr("Unfiltered Popup")) self._mode_combo.setCurrentIndex(1) case_label = QLabel() case_label.setText(tr("Case Sensitivity")) self._case_combo = QComboBox() self._case_combo.addItem(tr("Case Insensitive")) self._case_combo.addItem(tr("Case Sensitive")) self._case_combo.setCurrentIndex(0) separator_label = QLabel() separator_label.setText(tr("Tree Separator")) separator_line_edit = QLineEdit() separator_line_edit.setText(self._completer.separator()) separator_line_edit.textChanged.connect(self._completer.set_separator) wrap_check_box = QCheckBox() wrap_check_box.setText(tr("Wrap around completions")) wrap_check_box.setChecked(self._completer.wrapAround()) wrap_check_box.clicked.connect(self._completer.setWrapAround) self._contents_label = QLabel() self._contents_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) separator_line_edit.textChanged.connect(self.update_contents_label) self._tree_view = QTreeView() self._tree_view.setModel(self._completer.model()) self._tree_view.header().hide() self._tree_view.expandAll() self._mode_combo.activated.connect(self.change_mode) self._case_combo.activated.connect(self.change_case) self._line_edit = QLineEdit() self._line_edit.setCompleter(self._completer) layout = QGridLayout() layout.addWidget(model_label, 0, 0), layout.addWidget(self._tree_view, 0, 1) layout.addWidget(mode_label, 1, 0), layout.addWidget(self._mode_combo, 1, 1) layout.addWidget(case_label, 2, 0), layout.addWidget(self._case_combo, 2, 1) layout.addWidget(separator_label, 3, 0), layout.addWidget(separator_line_edit, 3, 1) layout.addWidget(wrap_check_box, 4, 0) layout.addWidget(self._contents_label, 5, 0, 1, 2) layout.addWidget(self._line_edit, 6, 0, 1, 2) central_widget.setLayout(layout) self.setCentralWidget(central_widget) self.change_case(self._case_combo.currentIndex()) self.change_mode(self._mode_combo.currentIndex()) self.setWindowTitle(tr("Tree Model Completer")) self._line_edit.setFocus() def create_menu(self): exit_action = QAction(tr("Exit"), self) about_act = QAction(tr("About"), self) about_qt_act = QAction(tr("About Qt"), self) exit_action.triggered.connect(QApplication.instance().quit) about_act.triggered.connect(self.about) about_qt_act.triggered.connect(QApplication.instance().aboutQt) file_menu = self.menuBar().addMenu(tr("File")) file_menu.addAction(exit_action) help_menu = self.menuBar().addMenu(tr("About")) help_menu.addAction(about_act) help_menu.addAction(about_qt_act) def change_mode(self, index): modes = (QCompleter.InlineCompletion, QCompleter.PopupCompletion, QCompleter.UnfilteredPopupCompletion) self._completer.setCompletionMode(modes[index]) def model_from_file(self, file_name): # file = QFile(file_name) # if not file.open(QFile.ReadOnly): # return QStringListModel(self._completer) QApplication.instance().setOverrideCursor(QCursor(Qt.WaitCursor)) model = QStandardItemModel(self._completer) parents = [model.invisibleRootItem()] with open(file_name) as file: pat = re.compile("^\\s+") for line in file: if not line: continue trimmed_line = line.strip() if not trimmed_line: continue match = pat.match(line) if not match: level = 0 else: length = match.end() - match.start() if line.startswith("\t"): level = length else: level = length // 4 while len(parents) < level + 2: parents.append(None) item = QStandardItem() item.setText(trimmed_line) parents[level].appendRow(item) parents[level + 1] = item QApplication.instance().restoreOverrideCursor() return model @pyqtSlot(QModelIndex) def highlight(self, index): proxy = self._completer.completionModel() source_index = proxy.mapToSource(index) self._tree_view.selectionModel().select( source_index, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) self._tree_view.scrollTo(source_index) def about(self): QMessageBox.about( self, tr("About"), tr("This example demonstrates how to use a QCompleter with a custom tree datamodel." )) def change_case(self, cs): self._completer.setCaseSensitivity( Qt.CaseSensitive if cs else Qt.CaseInsensitive) def update_contents_label(self, sep): self._contents_label.setText( "Type path from datamodel above with items at each level separated by a '%s'" % sep)
class FinderBox(QComboBox): running = False to_finish = 0 search_started = pyqtSignal() search_finished = pyqtSignal() def __init__(self, finders, iface, parent=None): self.iface = iface self.mapCanvas = iface.mapCanvas() self.rubber = QgsRubberBand(self.mapCanvas) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) QComboBox.__init__(self, parent) self.setEditable(True) self.setInsertPolicy(QComboBox.InsertAtTop) self.setMinimumHeight(27) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.insertSeparator(0) self.lineEdit().returnPressed.connect(self.search) self.result_view = QTreeView() self.result_view.setHeaderHidden(True) self.result_view.setMinimumHeight(300) self.result_view.activated.connect(self.itemActivated) self.result_view.pressed.connect(self.itemPressed) self.setView(self.result_view) self.result_model = ResultModel(self) self.setModel(self.result_model) self.finders = finders for finder in self.finders.values(): finder.result_found.connect(self.result_found) finder.limit_reached.connect(self.limit_reached) finder.finished.connect(self.finished) self.clearButton = QPushButton(self) self.clearButton.setIcon(QIcon(":/plugins/quickfinder/icons/draft.svg")) self.clearButton.setText('') self.clearButton.setFlat(True) self.clearButton.setCursor(QCursor(Qt.ArrowCursor)) self.clearButton.setStyleSheet('border: 0px; padding: 0px;') self.clearButton.clicked.connect(self.clear) layout = QHBoxLayout(self) self.setLayout(layout) layout.addStretch() layout.addWidget(self.clearButton) layout.addSpacing(20) button_size = self.clearButton.sizeHint() # frameWidth = self.lineEdit().style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth) padding = button_size.width() # + frameWidth + 1 self.lineEdit().setStyleSheet('QLineEdit {padding-right: %dpx; }' % padding) def __del__(self): if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber def clearSelection(self): self.result_model.setSelected(None, self.result_view.palette()) self.rubber.reset() def clear(self): self.clearSelection() self.result_model.clearResults() self.lineEdit().setText('') def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.clearSelection() QComboBox.keyPressEvent(self, event) def search(self): if self.running: return to_find = self.lineEdit().text() if not to_find or to_find == '': return self.running = True self.search_started.emit() self.clearSelection() self.result_model.clearResults() self.result_model.truncateHistory(MySettings().value("historyLength")) self.result_model.setLoading(True) self.showPopup() QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) self.finders_to_start = [] for finder in self.finders.values(): if finder.activated(): self.finders_to_start.append(finder) bbox = self.mapCanvas.fullExtent() while len(self.finders_to_start) > 0: finder = self.finders_to_start[0] self.finders_to_start.remove(finder) self.result_model.addResult(finder.name) finder.start(to_find, bbox=bbox) # For case there is no finder activated self.finished(None) def stop(self): self.finders_to_start = [] for finder in self.finders.values(): if finder.is_running(): finder.stop() self.finished(None) def result_found(self, finder, layername, value, geometry, srid): self.result_model.addResult(finder.name, layername, value, geometry, srid) self.result_view.expandAll() def limit_reached(self, finder, layername): self.result_model.addEllipsys(finder.name, layername) def finished(self, finder): if len(self.finders_to_start) > 0: return for finder in self.finders.values(): if finder.is_running(): return self.running = False self.search_finished.emit() self.result_model.setLoading(False) QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) def itemActivated(self, index): item = self.result_model.itemFromIndex(index) self.showItem(item) def itemPressed(self, index): item = self.result_model.itemFromIndex(index) if QApplication.mouseButtons() == Qt.LeftButton: self.showItem(item) def showItem(self, item): if isinstance(item, ResultItem): self.result_model.setSelected(item, self.result_view.palette()) geometry = self.transform_geom(item) self.rubber.reset(geometry.type()) self.rubber.setToGeometry(geometry, None) self.zoom_to_rubberband() return if isinstance(item, GroupItem): child = item.child(0) if isinstance(child, ResultItem): self.result_model.setSelected(item, self.result_view.palette()) self.rubber.reset(child.geometry.type()) for i in range(0, item.rowCount()): geometry = self.transform_geom(item.child(i)) self.rubber.addGeometry(geometry, None) self.zoom_to_rubberband() return if item.__class__.__name__ == 'QStandardItem': self.clearSelection() def transform_geom(self, item): src_crs = QgsCoordinateReferenceSystem() src_crs.createFromSrid(item.srid) dest_crs = self.mapCanvas.mapRenderer().destinationCrs() geom = QgsGeometry(item.geometry) geom.transform(QgsCoordinateTransform(src_crs, dest_crs)) return geom def zoom_to_rubberband(self): geom = self.rubber.asGeometry() if geom: rect = geom.boundingBox() rect.scale(1.5) self.mapCanvas.setExtent(rect) self.mapCanvas.refresh()
class LeftSideBar(QWidget): treeViewSelectionChanged = pyqtSignal(QModelIndex, QModelIndex) treeViewDoubleClicked = pyqtSignal(QModelIndex) addPlaylistRequested = pyqtSignal() removePlaylistRequested = pyqtSignal(UUID) addToPlaylistRequested = pyqtSignal(UUID) playlistAdded = pyqtSignal(UUID) playlistRenamed = pyqtSignal(UUID, str) def __init__(self, tree_items, parent=None): super(LeftSideBar, self).__init__(parent) self._tree_items = tree_items self._restoreSettings() self._setTreeView() self._setAlbumCoverBox() self._renderUI() self.setMinimumWidth(FRONT_COVER_MIN_WIDTH) self.setMaximumWidth(FRONT_COVER_MAX_WIDTH) def _restoreSettings(self): pass def _setTreeView(self): self.treeModel = TreeModel() self.treeModel.addTopLevelItems(self._tree_items.keys()) self.leftBarView = QTreeView() self.leftBarView.setModel(self.treeModel) self.leftBarView.setHeaderHidden(True) self.leftBarView.setRootIsDecorated(False) self.leftBarView.setItemsExpandable(False) self.leftBarView.setMouseTracking(True) self.leftBarView.expandAll() self.leftBarView.setFocusPolicy(Qt.NoFocus) self.leftBarView.setSelectionBehavior(QAbstractItemView.SelectRows) self.leftBarView.setSelectionMode(QAbstractItemView.SingleSelection) self.leftBarView.setEditTriggers(QAbstractItemView.SelectedClicked) self.leftBarView.selectionModel().currentRowChanged.connect( lambda c, p: self.treeViewSelectionChanged.emit(c, p)) self.leftBarView.selectionModel().setCurrentIndex( self.leftBarView.model().index( 0, 0, self.leftBarView.model().index(0, 0)), QItemSelectionModel.Select) self.leftBarView.doubleClicked.connect( lambda i: self.treeViewDoubleClicked.emit(i)) delegate = LeftSideBarDelegate(self.leftBarView) self.leftBarView.setItemDelegate(delegate) delegate.addPlaylistRequested.connect( lambda: self.addPlaylistRequested.emit()) delegate.removePlaylistRequested.connect( lambda i: self.removePlaylistRequested.emit( self.__getUuidFromIndex(i))) delegate.addToPlaylistRequested.connect( lambda i: self.addToPlaylistRequested.emit(self.__getUuidFromIndex(i))) delegate.editingFinished.connect(self._onRenamed) def _onRenamed(self, index, text): self.playlistRenamed.emit( self.__getUuidFromIndex(index), text) @property def model(self): return self.treeModel def _setAlbumCoverBox(self): self.albumCoverBox = CoverArtBox() def _renderUI(self): self.layout = QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) self.layout.addWidget(self.leftBarView) self.layout.addWidget(self.albumCoverBox) self.setLayout(self.layout) @QtCore.pyqtSlot(str, str, bytes) def changeCoverArtBoxInformation(self, title, artist, cover): self.albumCoverBox.setCoverArtBox(title, artist, cover) @QtCore.pyqtSlot(UUID, str, int, int) def addPlaylistEntry(self, uuid, name, row=0, column=0): model = self.model parent = model.getTopLevelIndex('PLAYLISTS') if model.insertPlaylistEntry(row, name, uuid, parent): self.playlistAdded.emit(uuid) child = model.index(row, column, parent) self.leftBarView.selectionModel().setCurrentIndex( child, QItemSelectionModel.SelectCurrent) self.leftBarView.edit(child) @QtCore.pyqtSlot(UUID, int, int) def createDefaults(self, uuid, row=0, column=0): model = self.model parent = model.getTopLevelIndex('LIBRARY') model.insertPlaylistEntry(row, 'Songs', uuid, parent) def __getUuidFromIndex(self, index): model = self.model uuid = model.getItemUuid(index) return uuid def __getIndexFromUuid(self, uuid): model = self.model row = model.getIndexFromUuid(uuid) return row @QtCore.pyqtSlot(UUID) def removePlaylistEntry(self, uuid): model = self.model row = self.__getIndexFromUuid(uuid) parent = model.getTopLevelIndex('PLAYLISTS') if not model.removeRow(row, parent): return None
class FileTreeView(QWidget): on_menu_select = pyqtSignal(str, dict) def __init__(self): super().__init__() self.list_file = [] self.get_data_file() v_box = QVBoxLayout() v_box.addLayout(self.finder()) v_box.addWidget(self.tree_view()) self.setLayout(v_box) def finder(self): w_find = QLineEdit() w_find.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)) w_find_button = QPushButton() w_find_button.setText("Find") w_find_button.setFixedWidth(50) h_box = QHBoxLayout() h_box.addWidget(w_find) h_box.addWidget(w_find_button, 1) return h_box def get_data_file(self): self.list_file = self.create_list(get_data_folder()) def create_list(self, dir): lst = os.listdir(path=dir) list_file = [] for f in lst: path = dir + "\\" + f file = {} if os.path.isdir(path): file["dir"] = dir file["name"] = f file["isDir"] = True file["child"] = self.create_list(path) else: file["dir"] = dir file["name"] = f file["isDir"] = False list_file.append(file) return list_file def tree_view(self): self.tree = QTreeView() self.tree.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.tree.setContextMenuPolicy(Qt.CustomContextMenu) self.tree.customContextMenuRequested.connect(self.openMenu) self.model = QtGui.QStandardItemModel() self.model.setHorizontalHeaderLabels(['List API']) self.tree.header().setDefaultSectionSize(180) self.tree.setModel(self.model) self.import_data(self.model.invisibleRootItem(), self.list_file) self.tree.expandAll() return self.tree def import_data(self, parent_item, list): for i in list: if i["isDir"]: item = QStandardItem(i["name"]) item.setEditable(False) item.setIcon(QIcon(get_icon_link("folder_yellow.svg"))) item.setData(i) parent_item.appendRow(item) self.import_data(item, i["child"]) else: item = QStandardItem(i["name"]) item.setEditable(False) item.setIcon(QIcon(get_icon_link("text_snippet.svg"))) item.setData(i) parent_item.appendRow(item) def openMenu(self, position): indexes = self.tree.selectedIndexes() level = 0 data = {} if len(indexes) > 0: index = indexes[0] item = self.model.itemFromIndex(index) data = item.data() if data['isDir']: level = 1 else: level = 2 menu = QMenu() menu.setStyleSheet(open('stylesheet/default.qss').read()) # Create preference action rename_action = QAction(QIcon(get_icon_link('edit.svg')), '&Rename', self) rename_action.setStatusTip('Rename') # exitAction.triggered.connect(self.exitCall) # Create preference action new_action = QAction(QIcon(get_icon_link('create_new_folder.svg')), '&New Folder', self) new_action.setStatusTip('New Folder') # exitAction.triggered.connect(self.exitCall) # Create preference action refresh_action = QAction(QIcon(get_icon_link('refresh.svg')), '&Refresh', self) refresh_action.setStatusTip('Refresh') # exitAction.triggered.connect(self.exitCall) # Create preference action delete_action = QAction(QIcon(get_icon_link('delete_forever.svg')), '&Delete', self) delete_action.setStatusTip('Delete') # exitAction.triggered.connect(self.exitCall) # Create preference action open_action = QAction(QIcon(get_icon_link('open_in_new.svg')), '&Open', self) open_action.setStatusTip('Open file') # open_action.triggered.connect(self.open_file) # Create preference action expand_action = QAction(QIcon(), '&Expand', self) expand_action.setStatusTip('Expand') # exitAction.triggered.connect(self.exitCall) # Create preference action collapse_action = QAction(QIcon(), '&Collapse', self) collapse_action.setStatusTip('Collapse') # exitAction.triggered.connect(self.exitCall) # Create preference action copy_action = QAction(QIcon(get_icon_link('content_copy.svg')), '&Copy', self) copy_action.setStatusTip('Copy') # exitAction.triggered.connect(self.exitCall) # Create preference action move_action = QAction(QIcon(get_icon_link('zoom_out_map.svg')), '&Move', self) move_action.setStatusTip('Move') # exitAction.triggered.connect(self.exitCall) if level == 1: menu.addAction(rename_action) menu.addAction(new_action) menu.addSeparator() menu.addAction(refresh_action) menu.addAction(expand_action) menu.addAction(collapse_action) menu.addSeparator() menu.addAction(delete_action) elif level == 2: menu.addAction(open_action) menu.addAction(refresh_action) menu.addSeparator() menu.addAction(rename_action) menu.addAction(copy_action) menu.addAction(move_action) menu.addSeparator() menu.addAction(delete_action) else: menu.addAction(new_action) menu.addAction(refresh_action) action = menu.exec_(self.tree.viewport().mapToGlobal(position)) if action == open_action: self.on_menu_select.emit("open", data)
class LootWizard(QMainWindow): def __init__(self): super (LootWizard, self).__init__() self.hide() self._upToDateMessage = 'is up-to-date' self.initUI() def kill_on_init(self): QTimer.singleShot(0, app.quit) def initUI(self): data_found = False try: self.__getFileinfo(sys.argv[1]) except (IndexError, FileNotFoundError): directory = QStandardPaths.standardLocations(QStandardPaths.DocumentsLocation)[0] directory += "/My Games/Path of Exile" fname = QFileDialog.getOpenFileName(None, 'Open file', directory) if fname[0]: try: self.__getFileinfo(fname[0]) except FileNotFoundError: pass else: data_found = True else: data_found = True if not data_found: self.kill_on_init() else: self.initWindowUI() def initWindowUI(self): self.resetButton = QPushButton("Reset") self.resetButton.setEnabled(False) self.resetButton.clicked.connect(self.reset) self.orphanButton = SquareButton("+") self.orphanButton.hide() self.overwriteButton = QPushButton("Overwrite") self.overwriteButton.setStyleSheet("background-color: #EFC795") self.overwriteButton.setEnabled(False) self.overwriteButton.clicked.connect(self.overwrite) self.forceCheckBox = QCheckBox("&Always inherit") self.forceCheckBox.setToolTip('Smartblocks inherit from virtual blockgroups.<br/>Check me to change from a <b>smartblock</b> parent.') self.forceCheckBox.hide() hbox = QHBoxLayout() hbox2 = QHBoxLayout() hbox2.addWidget(self.forceCheckBox) hbox.addLayout(hbox2) hbox.addWidget(self.orphanButton) hbox.addStretch(1) hbox.addWidget(self.overwriteButton) hbox.addWidget(self.resetButton) vbox = QVBoxLayout() self.setWindowTitle('Loot Wizard') self.view = QTreeView() self.setCentralWidget(QWidget()) self.scrollLayout = QFormLayout() self.scrollWidget = QWidget() self.scrollWidget.setLayout(self.scrollLayout) self.orphanDialog = OrphanDialog(self) self.orphanButton.clicked.connect(self.openOrphans) vbox.addWidget(self.view) vbox.addLayout(hbox) self.scrollLayout.addRow(self.view) self.centralWidget().setLayout(vbox) self.model = SmartblockModel(self.file_content, self) self.statusBar().showMessage(self.file_info.fileName() + ' ' + self._upToDateMessage) self.forceCheckBox.stateChanged.connect(self.switchforce) self.view.setModel(self.model) self.view.expandAll() self.view.setAlternatingRowColors(True) self.resize(300, 600) self.show() def inform(self, flag, external_call = False, end_process = False): reset_flag = False # hidden first if flag: if external_call: message = 'has non-updated changes' else: message = 'has changed' reset_flag = True else: if end_process: message = 'is saved' else: message = self._upToDateMessage self.statusBar().showMessage(self.file_info.fileName() + ' ' + message) self.resetButton.setEnabled(reset_flag) self.overwriteButton.setEnabled(flag) def displayOrphan(self, display = True): # self.displayButton(self.orphanButton, display) pass def displayInheritance(self, display = True): self.displayButton(self.forceCheckBox, display) def displayButton(self, btn, display = True): if display: btn.show() else: btn.hide() def switchforce(self, state): self.model.forceInheritance = state == Qt.Checked def overwrite(self): self.overwriteButton.setEnabled(False) self.resetButton.setEnabled(False) # TODO: disable from elsewhere ?! if self.model.save(self.file_info.absoluteFilePath()): self.inform(False, False, True) def reset(self): self.model.resetStatus() def __getFileinfo(self, filename): self.file_content = ''.join(fileinput.input(filename)) self.file_info = QFileInfo(filename) def openOrphans(self): self.orphanDialog.show()
class FinderBox(QComboBox): running = False to_finish = 0 search_started = pyqtSignal() search_finished = pyqtSignal() def __init__(self, finders, iface, parent=None): self.iface = iface self.mapCanvas = iface.mapCanvas() self.rubber = QgsRubberBand(self.mapCanvas) self.rubber.setColor(QColor(255, 255, 50, 200)) self.rubber.setIcon(self.rubber.ICON_CIRCLE) self.rubber.setIconSize(15) self.rubber.setWidth(4) self.rubber.setBrushStyle(Qt.NoBrush) QComboBox.__init__(self, parent) self.setEditable(True) self.setInsertPolicy(QComboBox.InsertAtTop) self.setMinimumHeight(27) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.insertSeparator(0) self.lineEdit().returnPressed.connect(self.search) self.result_view = QTreeView() self.result_view.setHeaderHidden(True) self.result_view.setMinimumHeight(300) self.result_view.activated.connect(self.itemActivated) self.result_view.pressed.connect(self.itemPressed) self.setView(self.result_view) self.result_model = ResultModel(self) self.setModel(self.result_model) self.finders = finders for finder in self.finders.values(): finder.result_found.connect(self.result_found) finder.limit_reached.connect(self.limit_reached) finder.finished.connect(self.finished) self.clearButton = QPushButton(self) self.clearButton.setIcon( QIcon(":/plugins/quickfinder/icons/draft.svg")) self.clearButton.setText('') self.clearButton.setFlat(True) self.clearButton.setCursor(QCursor(Qt.ArrowCursor)) self.clearButton.setStyleSheet('border: 0px; padding: 0px;') self.clearButton.clicked.connect(self.clear) layout = QHBoxLayout(self) self.setLayout(layout) layout.addStretch() layout.addWidget(self.clearButton) layout.addSpacing(20) button_size = self.clearButton.sizeHint() # frameWidth = self.lineEdit().style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth) padding = button_size.width() # + frameWidth + 1 self.lineEdit().setStyleSheet('QLineEdit {padding-right: %dpx; }' % padding) def __del__(self): if self.rubber: self.iface.mapCanvas().scene().removeItem(self.rubber) del self.rubber def clearSelection(self): self.result_model.setSelected(None, self.result_view.palette()) self.rubber.reset() def clear(self): self.clearSelection() self.result_model.clearResults() self.lineEdit().setText('') def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: self.clearSelection() QComboBox.keyPressEvent(self, event) def search(self): if self.running: return to_find = self.lineEdit().text() if not to_find or to_find == '': return self.running = True self.search_started.emit() self.clearSelection() self.result_model.clearResults() self.result_model.truncateHistory(MySettings().value("historyLength")) self.result_model.setLoading(True) self.showPopup() QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) self.finders_to_start = [] for finder in self.finders.values(): if finder.activated(): self.finders_to_start.append(finder) bbox = self.mapCanvas.fullExtent() while len(self.finders_to_start) > 0: finder = self.finders_to_start[0] self.finders_to_start.remove(finder) self.result_model.addResult(finder.name) finder.start(to_find, bbox=bbox) # For case there is no finder activated self.finished(None) def stop(self): self.finders_to_start = [] for finder in self.finders.values(): if finder.is_running(): finder.stop() self.finished(None) def result_found(self, finder, layername, value, geometry, srid): self.result_model.addResult(finder.name, layername, value, geometry, srid) self.result_view.expandAll() def limit_reached(self, finder, layername): self.result_model.addEllipsys(finder.name, layername) def finished(self, finder): if len(self.finders_to_start) > 0: return for finder in self.finders.values(): if finder.is_running(): return self.running = False self.search_finished.emit() self.result_model.setLoading(False) QCoreApplication.processEvents(QEventLoop.ExcludeUserInputEvents) def itemActivated(self, index): item = self.result_model.itemFromIndex(index) self.showItem(item) def itemPressed(self, index): item = self.result_model.itemFromIndex(index) if QApplication.mouseButtons() == Qt.LeftButton: self.showItem(item) def showItem(self, item): if isinstance(item, ResultItem): self.result_model.setSelected(item, self.result_view.palette()) geometry = self.transform_geom(item) self.rubber.reset(geometry.type()) self.rubber.setToGeometry(geometry, None) self.zoom_to_rubberband() return if isinstance(item, GroupItem): child = item.child(0) if isinstance(child, ResultItem): self.result_model.setSelected(item, self.result_view.palette()) self.rubber.reset(child.geometry.type()) for i in range(0, item.rowCount()): geometry = self.transform_geom(item.child(i)) self.rubber.addGeometry(geometry, None) self.zoom_to_rubberband() return if item.__class__.__name__ == 'QStandardItem': self.clearSelection() def transform_geom(self, item): src_crs = QgsCoordinateReferenceSystem() src_crs.createFromSrid(item.srid) dest_crs = self.mapCanvas.mapRenderer().destinationCrs() geom = QgsGeometry(item.geometry) geom.transform(QgsCoordinateTransform(src_crs, dest_crs)) return geom def zoom_to_rubberband(self): geom = self.rubber.asGeometry() if geom: rect = geom.boundingBox() rect.scale(1.5) self.mapCanvas.setExtent(rect) self.mapCanvas.refresh()
class EncoderSelector(QComboBox): __PATH_SEPARATOR__ = " → " _PATH_ROLE = 1 _DETAILS_ROLE = 2 class EncoderModel(QStandardItemModel): def __init__(self, json_file, disable_selections=False): super().__init__() self.disable_selections = disable_selections self.json_file = json_file self.json = None self.reload() def reload(self): with open(self.json_file) as config: self.json = json.load(config) self.beginResetModel() self.clear() self._build_descendents(self.invisibleRootItem(), self.json, self.disable_selections) self.endResetModel() def get_item(self, model_index): item = self.itemFromIndex(model_index) if item: return item.text() return None def del_item(self, media_type, encoder_group, name): del self.json[media_type][encoder_group][name] # Clean up ancestors if they dont have any children if len(self.json[media_type][encoder_group].keys()) == 0: del self.json[media_type][encoder_group] if len(self.json[media_type].keys()) == 0: del self.json[media_type] def add_item(self, media_type, group, name, extension, executable, command): if media_type not in self.json: self.json[media_type] = {} if group not in self.json[media_type]: self.json[media_type][group] = {} self.json[media_type][group][name] = { "extension": extension, "executable": executable, "command": command } def backup_and_save(self): # Backup current config backup_config_file() # Save new config with open(self.json_file, "w") as config: json.dump(self.json, config, indent=4) self.reload() @staticmethod def _build_descendents(parent, _json, disable_selections): encoder_keys = {"extension", "executable", "command"} intersection = set(_json.keys()) & encoder_keys if not intersection: # add node and build further for key in _json: section_root = QStandardItem(key) parent.appendRow(section_root) if disable_selections: parent.setSelectable(False) EncoderSelector.EncoderModel._build_descendents( section_root, _json[key], disable_selections) else: if len(intersection) < 3: raise SyntaxError( f"Missing value(s) {encoder_keys - intersection} in node {parent.text()}" ) else: path = "" item = parent while item.parent() is not None: path = f"{EncoderSelector.__PATH_SEPARATOR__}{item.text()}" + path item = item.parent() path = f"{item.text()}" + path parent.setData(path, Qt.UserRole + EncoderSelector._PATH_ROLE) parent.setData(_json, Qt.UserRole + EncoderSelector._DETAILS_ROLE) encoder_changed = pyqtSignal('PyQt_PyObject', 'PyQt_PyObject') def __init__(self): super().__init__() self.tree_view = QTreeView(self) self.tree_view.setEditTriggers(QTreeView.NoEditTriggers) self.tree_view.setSelectionBehavior(QTreeView.SelectRows) self.tree_view.setWordWrap(True) self.tree_view.setHeaderHidden(True) self.setView(self.tree_view) self.encoder_model = EncoderSelector.EncoderModel( get_config_file(), disable_selections=True) self.setModel(self.encoder_model) self.refresh_encoders() self._selected_encoder = "" def paintEvent(self, event): style = QApplication.style() opt = QStyleOptionComboBox() opt.rect = self.rect() self.initStyleOption(opt) painter = QPainter(self) painter.save() style.drawComplexControl(QStyle.CC_ComboBox, opt, painter) opt.currentText = self._encoder_path() style.drawControl(QStyle.CE_ComboBoxLabel, opt, painter) painter.restore() def hidePopup(self): super(EncoderSelector, self).hidePopup() selected_index = self.tree_view.selectionModel().currentIndex() if selected_index: encoder = self.tree_view.model().get_item(selected_index) if encoder: self._selected_encoder = encoder self.encoder_changed.emit(self._encoder_path(), self._encoder_details()) def get_encoder(self): return self._encoder_path(), self._encoder_details() def add_encoder(self, media_type, encoder_group, name, command, executable, extension): self.encoder_model.add_item(media_type, encoder_group, name, extension, executable, command) self.encoder_model.backup_and_save() self.refresh_encoders() def del_encoder(self, media_type, encoder_group, name): self.encoder_model.del_item(media_type, encoder_group, name) self.encoder_model.backup_and_save() self.refresh_encoders() def update_encoder(self, media_type, encoder_group, name, command, executable, extension): self.encoder_model.del_item(media_type, encoder_group, name) self.encoder_model.add_item(media_type, encoder_group, name, extension, executable, command) self.encoder_model.backup_and_save() self.refresh_encoders() def refresh_encoders(self): self.encoder_model.reload() self.tree_view.expandAll() def select_encoder(self, encoder_name): match = self._find_encoder(encoder_name) if match: self._selected_encoder = encoder_name self.encoder_changed.emit(self._encoder_path(), self._encoder_details()) def _encoder_path(self): if self._selected_encoder: match = self._find_encoder(self._selected_encoder) if match: return match[0].data(Qt.UserRole + self._PATH_ROLE) return None def _encoder_details(self): if self._selected_encoder: match = self._find_encoder(self._selected_encoder) if match: return match[0].data(Qt.UserRole + self._DETAILS_ROLE) return None def _find_encoder(self, encoder_name): return self.model().match(self.model().index(0, 0), Qt.UserRole + self._PATH_ROLE, encoder_name, 1, Qt.MatchEndsWith | Qt.MatchRecursive)
class NavigatorDock(DockWidget): def __init__(self): DockWidget.__init__(self, core.mainWindow(), '&Navigator', QIcon(':/enkiicons/goto.png'), "Alt+N") self._tags = [] self._tree = QTreeView(self) self._tree.installEventFilter(self) self._tree.setHeaderHidden(True) self.setFocusProxy(self._tree) self._filterEdit = LineEdit(self) self._filterEdit.setClearButtonVisible(True) self._filterEdit.textEdited.connect(self._applyFilter) self._filterEdit.clearButtonClicked.connect(self._applyFilter) self._filterEdit.clearButtonClicked.connect(self._tree.setFocus) self._filterEdit.clearButtonClicked.connect(self._hideFilter) self._filterEdit.installEventFilter(self) self._displayWidget = QWidget(self) layout = QVBoxLayout(self._displayWidget) layout.addWidget(self._tree) layout.addWidget(self._filterEdit) layout.setContentsMargins(0, 0, 0, 0) self.setWidget(self._displayWidget) self._tagModel = _TagModel(self._tree) self._tagModel.jumpToTagDone.connect(self._hideFilter) self._tree.setModel(self._tagModel) self._tree.activated.connect(self._tagModel.onActivated) self._tree.clicked.connect(self._tagModel.onActivated) self._tagModel.modelAboutToBeReset.connect(self._onModelAboutToBeReset) self._tagModel.modelReset.connect(self._onModelReset) self._currentTagPath = None self._errorLabel = None self._installed = False def term(self): self._tagModel.term() def install(self): if not self._installed: core.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self) core.actionManager().addAction("mView/aNavigator", self.showAction()) self._installed = True def remove(self): if self._installed: core.mainWindow().removeDockWidget(self) core.actionManager().removeAction("mView/aNavigator") self.hide() self._installed = False def setTags(self, tags): self._tags = tags self._setFilteredTags(tags) self._hideFilter() if self.widget() is not self._displayWidget: self.setWidget(self._displayWidget) self._displayWidget.show() if self._errorLabel is not None: self._errorLabel.hide() def _setFilteredTags(self, tags): self._tagModel.setTags(tags) def onError(self, error): self._displayWidget.hide() if self._errorLabel is None: self._errorLabel = QLabel(self) self._errorLabel.setWordWrap(True) self._errorLabel.setText(error) if not self.widget() is self._errorLabel: self.setWidget(self._errorLabel) self._errorLabel.show() self._displayWidget.hide() def _onModelAboutToBeReset(self): currIndex = self._tree.currentIndex() self._currentTagPath = self._tagModel.tagPathForIndex(currIndex) if currIndex.isValid() else None def _onModelReset(self): self._tree.expandAll() # restore current item if self._currentTagPath is not None: index = self._tagModel.indexForTagPath(self._currentTagPath) if index.isValid(): self._tree.setCurrentIndex(index) def eventFilter(self, object_, event): if object_ is self._tree: if event.type() == QEvent.KeyPress: if event.key() == Qt.Key_Backspace: if event.modifiers() == Qt.ControlModifier: self._onTreeCtrlBackspace() else: self._onTreeBackspace() return True elif event.text() and \ (event.text().isalnum() or event.text() == '_'): self._onTreeTextTyped(event.text()) return True elif object_ is self._filterEdit: if event.type() == QEvent.KeyPress: if event.key() in (Qt.Key_Up, Qt.Key_Down): self._tree.setFocus() self._tree.event(event) return True elif event.key() in (Qt.Key_Enter, Qt.Key_Return): currIndex = self._tree.currentIndex() if currIndex.isValid(): self._tagModel.onActivated(currIndex) return DockWidget.eventFilter(self, object_, event) def _hideFilter(self): hadText = self._filterEdit.text() != '' self._filterEdit.clear() self._filterEdit.hide() if hadText: self._applyFilter() def _applyFilter(self): text = self._filterEdit.text() if text: if not text.startswith('*'): text = '*' + text if not text.endswith('*'): text = text + '*' wildcard = text.lower() filteredTags = _filterTags(wildcard, self._tags) self._setFilteredTags(filteredTags) self._tree.expandAll() if filteredTags: firstMatchingTag = _findFirstMatching(wildcard, filteredTags) path = _tagPath(firstMatchingTag) index = self._tagModel.indexForTagPath(path) self._tree.setCurrentIndex(index) else: self._setFilteredTags(self._tags) if text: self._filterEdit.show() elif not self._filterEdit.hasFocus(): self._hideFilter() def _onTreeTextTyped(self, text): self._filterEdit.setText(self._filterEdit.text() + text) self._applyFilter() def _onTreeBackspace(self): text = self._filterEdit.text() if text: self._filterEdit.setText(text[:-1]) self._applyFilter() def _onTreeCtrlBackspace(self): self._hideFilter() self._applyFilter()
class StatsGui(QWidget): ''' This class accepts a glue data collection object, and builds an interactive window to display basic statistics (e.g. mean, median, mode) about each dataset ''' released = QtCore.pyqtSignal(object) def __init__(self, dc): # Initialize the object as a QWidget QWidget.__init__(self) #Save the datacollection object as an attribute of class StatsGui self.dc = dc #Set the title of the main GUI window self.setWindowTitle('Statistics') # Set up dicts for row indices self.subset_dict = dict() self.component_dict = dict() self.selected_dict = dict() self.selected_indices = [] #Set up tree view and fix it to the top half of the window self.treeview = QTreeView(self) # Set the default clicking behavior to be row selection self.treeview.setSelectionBehavior(QAbstractItemView.SelectRows) # Set up expand all, collapse all, select all and deselect all buttons # Layout for expand/collapse/select/deselect layout_left_options = QHBoxLayout() self.expand_data = QToolButton(self) self.expand_data.setText("Expand all data and subsets") self.expand_data.clicked.connect(self.expandClicked) layout_left_options.addWidget(self.expand_data) self.all = QToolButton(self) self.all.setText('Select all') self.all.clicked.connect(self.allClicked) layout_left_options.addWidget(self.all) self.none = QToolButton(self) self.none.setText('Deselect all') self.none.clicked.connect(self.noneClicked) layout_left_options.addWidget(self.none) # Set default significant figures to 5 getcontext().prec = 5 # Set up past selected items self.past_selected = [] # Sort by subsets as a default self.sortBySubsets() # Set up the combo box for users to choose the number of significant figures in the table # Set up bottom options layout layout_bottom_options = QHBoxLayout() self.siglabel = QLabel(self) self.siglabel.setText('Number of significant figures:') layout_bottom_options.addWidget(self.siglabel) self.sigfig = QSpinBox(self) self.sigfig.setRange(1, 10) self.sigfig.setValue(5) self.sigfig.valueChanged.connect(self.sigchange) layout_bottom_options.addWidget(self.sigfig) # Export to file button self.export = QPushButton(self) self.export.setText('Export to file') self.export.clicked.connect(self.exportToFile) layout_bottom_options.addWidget(self.export) # Set up the toggle button to switch tree sorting modes self.switch_mode = QToolButton(self) self.switch_mode.setText('Sort tree by components') self.switch_mode.clicked.connect(self.switchMode) layout_left_options.addWidget(self.switch_mode) # Add instructions to sort the table self.how = QLabel(self) self.how.setText('Click each header to sort table') layout_left_options.addWidget(self.how) #################Set up the QTableView Widget############################# self.table = QTableView(self) self.table.setSortingEnabled(True) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.table.verticalHeader().setVisible(False) #Set the table headings self.headings = ('Subset', 'Dataset', 'Component', 'Mean', 'Median', 'Minimum', 'Maximum', 'Sum') self.data_frame = pd.DataFrame(columns=self.headings) self.data_accurate = pd.DataFrame(columns=self.headings) self.model = pandasModel(self.data_frame, self.dc) self.table.setModel(self.model) layout_table = QHBoxLayout() layout_table.addWidget(self.table) layout_table.stretch(10) # Finish nesting all the layouts main_layout = QVBoxLayout() main_layout.addWidget(self.treeview) main_layout.addLayout(layout_left_options) main_layout.addLayout(layout_table) main_layout.addLayout(layout_bottom_options) self.setLayout(main_layout) # Set up dict for caching self.cache_stash = dict() def myPressedEvent(self, currentQModelIndex): ''' Every time a row (or rows) in the tree view is clicked: if it is selected, add it to the table if it is deselected, remove it from the table ''' # Get the indexes of all the selected components self.selected_indices = self.treeview.selectionModel().selectedRows() newly_selected = np.setdiff1d(self.selected_indices, self.past_selected) for index in range(0, len(newly_selected)): # Check which view mode the tree is in to get the correct indices if self.switch_mode.text() == 'Sort tree by components': data_i = newly_selected[index].parent().row() comp_i = newly_selected[index].row() subset_i = newly_selected[index].parent().parent().row() else: data_i = newly_selected[index].parent().parent().row() comp_i = newly_selected[index].parent().row() subset_i = newly_selected[index].row() - 1 is_subset = newly_selected[index].parent().parent().parent().row( ) == 1 or (self.switch_mode.text() == 'Sort tree by subsets' and subset_i != -1) # Check if its a subset and if so run subset stats if is_subset: self.runSubsetStats(subset_i, data_i, comp_i) else: # Run standard data stats self.runDataStats(data_i, comp_i) newly_dropped = np.setdiff1d(self.past_selected, self.selected_indices) for index in range(0, len(newly_dropped)): # Check which view mode the tree is in to get the correct indices if self.switch_mode.text() == 'Sort tree by components': data_i = newly_dropped[index].parent().row() comp_i = newly_dropped[index].row() subset_i = newly_dropped[index].parent().parent().row() else: data_i = newly_dropped[index].parent().parent().row() comp_i = newly_dropped[index].parent().row() subset_i = newly_dropped[index].row() - 1 is_subset = newly_dropped[index].parent().parent().parent().row( ) == 1 or (self.switch_mode.text() == 'Sort tree by subsets' and subset_i != -1) if is_subset: try: # Get the indices that match the component, dataset, and subset requirements idx_c = np.where(self.data_frame['Component'] == self.dc[data_i].components[comp_i].label) idx_d = np.where( self.data_frame['Dataset'] == self.dc[data_i].label) idx_s = np.where(self.data_frame['Subset'] == self.dc[data_i].subsets[subset_i].label) idx1 = np.intersect1d(idx_c, idx_d) idx2 = np.intersect1d(idx1, idx_s) self.data_frame = self.data_frame.drop(idx2) except: pass else: try: # Find the index in the table of the unchecked element, if it's in the table # Find the matching component and dataset indices and intersect them to get the unique index idx_c = np.where(self.data_frame['Component'] == self.dc[data_i].components[comp_i].label) idx_d = np.where( self.data_frame['Dataset'] == self.dc[data_i].label) idx_s = np.where(self.data_frame['Subset'] == '--') idx1 = np.intersect1d(idx_c, idx_d) idx2 = np.intersect1d(idx1, idx_s) self.data_frame = self.data_frame.drop(idx2) except: pass # Update the past selected indices self.past_selected = self.selected_indices model = pandasModel(self.data_frame, self.dc) self.table.setModel(model) self.table.setSortingEnabled(True) self.table.setShowGrid(False) def runDataStats(self, data_i, comp_i): ''' Runs statistics for the component comp_i of data set data_i ''' subset_label = "--" data_label = dc[data_i].label comp_label = self.dc[data_i].components[ comp_i].label # add to the name array to build the table # Build the cache key cache_key = subset_label + data_label + comp_label # See if the values have already been cached try: column_data = self.cache_stash[cache_key] except: # Find the stat values # Save the data in the cache mean_val = self.dc[data_i].compute_statistic( 'mean', self.dc[data_i].components[comp_i]) median_val = self.dc[data_i].compute_statistic( 'median', self.dc[data_i].components[comp_i]) min_val = self.dc[data_i].compute_statistic( 'minimum', self.dc[data_i].components[comp_i]) max_val = self.dc[data_i].compute_statistic( 'maximum', self.dc[data_i].components[comp_i]) sum_val = self.dc[data_i].compute_statistic( 'sum', self.dc[data_i].components[comp_i]) column_data = np.asarray([[subset_label], [data_label], [comp_label], [mean_val], [median_val], [min_val], [max_val], [sum_val]]).transpose() self.cache_stash[cache_key] = column_data # Save the accurate data in self.data_accurate column_df = pd.DataFrame(column_data, columns=self.headings) self.data_accurate = self.data_accurate.append(column_df, ignore_index=True) # Round the values according to the number of significant figures set by the user mean_val = Decimal(float(column_data[0][3])) * Decimal(1) median_val = Decimal(float(column_data[0][4])) * Decimal(1) min_val = Decimal(float(column_data[0][5])) * Decimal(1) max_val = Decimal(float(column_data[0][6])) * Decimal(1) sum_val = Decimal(float(column_data[0][7])) * Decimal(1) # Create the column data array and append it to the data frame column_data = np.asarray([[subset_label], [data_label], [comp_label], [mean_val], [median_val], [min_val], [max_val], [sum_val]]).transpose() column_df = pd.DataFrame(column_data, columns=self.headings) self.data_frame = self.data_frame.append(column_df, ignore_index=True) def runSubsetStats(self, subset_i, data_i, comp_i): ''' Runs statistics for the subset subset_i with respect to the component comp_i of data set data_i ''' subset_label = dc[data_i].subsets[subset_i].label data_label = dc[data_i].label comp_label = self.dc[data_i].components[ comp_i].label # add to the name array to build the table # Build the cache key cache_key = subset_label + data_label + comp_label # See if the statistics are already in the cache try: column_data = self.cache_stash[cache_key] # Find the stats if not in the cache # Save in the cache except: mean_val = self.dc[data_i].compute_statistic( 'mean', self.dc[data_i].subsets[subset_i].components[comp_i], subset_state=self.dc[data_i].subsets[subset_i].subset_state) median_val = self.dc[data_i].compute_statistic( 'median', self.dc[data_i].subsets[subset_i].components[comp_i], subset_state=self.dc.subset_groups[subset_i].subset_state) min_val = self.dc[data_i].compute_statistic( 'minimum', self.dc[data_i].subsets[subset_i].components[comp_i], subset_state=self.dc.subset_groups[subset_i].subset_state) max_val = self.dc[data_i].compute_statistic( 'maximum', self.dc[data_i].subsets[subset_i].components[comp_i], subset_state=self.dc.subset_groups[subset_i].subset_state) sum_val = self.dc[data_i].compute_statistic( 'sum', self.dc[data_i].subsets[subset_i].components[comp_i], subset_state=self.dc.subset_groups[subset_i].subset_state) column_data = np.asarray([[subset_label], [data_label], [comp_label], [mean_val], [median_val], [min_val], [max_val], [sum_val]]).transpose() self.cache_stash[cache_key] = column_data # Save the data in self.data_accurate column_df = pd.DataFrame(column_data, columns=self.headings) self.data_accurate = self.data_accurate.append(column_df, ignore_index=True) # Round the values according to the number of significant figures set by the user mean_val = Decimal(float(column_data[0][3])) * Decimal(1) median_val = Decimal(float(column_data[0][4])) * Decimal(1) min_val = Decimal(float(column_data[0][5])) * Decimal(1) max_val = Decimal(float(column_data[0][6])) * Decimal(1) sum_val = Decimal(float(column_data[0][7])) * Decimal(1) # Create the column data array and append it to the data frame column_data = np.asarray([[subset_label], [data_label], [comp_label], [mean_val], [median_val], [min_val], [max_val], [sum_val]]).transpose() column_df = pd.DataFrame(column_data, columns=self.headings) self.data_frame = self.data_frame.append(column_df, ignore_index=True) def sigchange(self, i): # Set the number of significant figures according to what the user selects getcontext().prec = i # Retrospectively change the number of significant figures in the table data_labels = self.data_frame['Dataset'] comp_labels = self.data_frame['Component'] subset_labels = self.data_frame['Subset'] mean_vals = [] median_vals = [] min_vals = [] max_vals = [] sum_vals = [] # Get the values from the self.data_accurate array and append them for i in range(0, len(self.data_frame)): mean_vals.append( Decimal(self.data_accurate['Mean'][i]) * Decimal(1)) median_vals.append( Decimal(self.data_accurate['Median'][i]) * Decimal(1)) min_vals.append( Decimal(self.data_accurate['Minimum'][i]) * Decimal(1)) max_vals.append( Decimal(self.data_accurate['Maximum'][i]) * Decimal(1)) sum_vals.append(Decimal(self.data_accurate['Sum'][i]) * Decimal(1)) column_data = np.asarray([ subset_labels, data_labels, comp_labels, mean_vals, median_vals, min_vals, max_vals, sum_vals ]).transpose() self.data_frame = pd.DataFrame(column_data, columns=self.headings) model = pandasModel(self.data_frame, self.dc) self.table.setModel(model) self.table.setSortingEnabled(True) self.table.setShowGrid(False) def expandClicked(self): if self.expand_data.text() == "Expand all data and subsets": self.treeview.expandAll() self.expand_data.setText("Collapse all data and subsets") else: self.treeview.collapseAll() self.expand_data.setText("Expand all data and subsets") def allClicked(self): # Select all components of the treeview if checked and fill the table with newly checked items # Does not deselect if user unclicks it original_idx = self.treeview.selectionModel().selectedRows() self.treeview.selectAll() end_idx = self.treeview.selectionModel().selectedRows() for index in end_idx: if index not in original_idx: # Check to see if the clicked item is a subset component or a data component if index.parent().parent().parent().row() != 1: self.runDataStats(index.parent().row(), index.row()) else: self.runSubsetStats(index.parent().parent().row(), index.parent().row(), index.row()) # Set the table to display the correct data frame model = pandasModel(self.data_frame, self.dc) self.table.setModel(model) self.table.setSortingEnabled(True) self.table.setShowGrid(False) def noneClicked(self): self.treeview.clearSelection() self.data_frame = pd.DataFrame(columns=self.headings) model = pandasModel(self.data_frame, self.dc) self.table.setModel(model) self.table.setSortingEnabled(True) self.table.setShowGrid(False) def exportToFile(self): file_name, fltr = compat.getsavefilename( caption="Choose an output filename") try: self.data_frame.to_csv(str(file_name), index=False) except: print("passed") pass def switchMode(self): # if the user clicks to sort by components, change the text to "sort by subsets" and sort tree by components if self.switch_mode.text() == 'Sort tree by components': self.sortByComponents() self.switch_mode.setText('Sort tree by subsets') # otherwise the user wants to sort by subsets, change text to "sort by components" and sort tree by subsets else: self.sortBySubsets() self.switch_mode.setText('Sort tree by components') def sizeHint(self): return QSize(600, 800) def sortBySubsets(self): ''' Sorts the treeview by subsets- Dataset then subset then component. What we originally had as the default ''' # Save the selected rows from the component view try: selected = dict() for i in range(0, len(self.selected_indices)): item = self.model_components.itemFromIndex( self.selected_indices[i]) if item.row() != 0: key = item.text() + " (" + item.parent().parent().text( ) + ")" + item.parent().text() selected[key] = item.index() else: key = item.text() + item.parent().text() selected[key] = item.index() except: pass # Set Expand/collapse button to "expand all" self.expand_data.setText("Expand all data and subsets") #Allow the user to select multiple rows at a time self.selection_model = QAbstractItemView.MultiSelection self.treeview.setSelectionMode(self.selection_model) # See if the model already exists instead of regenerating try: self.treeview.setModel(self.model_subsets) except: self.model_subsets = QStandardItemModel() self.model_subsets.setHorizontalHeaderLabels(['']) self.treeview.setModel(self.model_subsets) self.treeview.setUniformRowHeights(True) # populate the tree # Make all the datasets be parents, and make it so they are not selectable parent_data = QStandardItem('{}'.format('Data')) parent_data.setEditable(False) parent_data.setSelectable(False) for i in range(0, len(self.dc)): parent = QStandardItem('{}'.format(self.dc.labels[i])) parent.setIcon(helpers.layer_icon(self.dc[i])) parent.setEditable(False) parent.setSelectable(False) # Make all the data components be children, nested under their parent for j in range(0, len(self.dc[i].components)): child = QStandardItem('{}'.format( str(self.dc[i].components[j]))) child.setEditable(False) # Add to the subset_dict key = self.dc[i].label + self.dc[i].components[ j].label + "All data-" + self.dc[i].label self.subset_dict[key] = child.index() parent.appendRow(child) parent_data.appendRow(parent) #Add the parents with their children to the QStandardItemModel self.model_subsets.appendRow(parent_data) parent_subset = QStandardItem('{}'.format('Subsets')) parent_subset.setEditable(False) parent_subset.setSelectable(False) # Set up the subsets as Subsets > choose subset > choose data set > choose component for j in range(0, len(self.dc.subset_groups)): grandparent = QStandardItem('{}'.format( self.dc.subset_groups[j].label)) grandparent.setIcon( helpers.layer_icon(self.dc.subset_groups[j])) grandparent.setEditable(False) grandparent.setSelectable(False) for i in range(0, len(self.dc)): parent = QStandardItem( '{}'.format(self.dc.subset_groups[j].label) + ' (' + '{}'.format(self.dc[i].label) + ')') # Set up the circles parent.setIcon(helpers.layer_icon( self.dc.subset_groups[j])) parent.setEditable(False) parent.setSelectable(False) try: self.dc[i].compute_statistic( 'mean', self.dc[i].subsets[j].components[0], subset_state=self.dc[i].subsets[j].subset_state) except: parent.setForeground(QtGui.QBrush(Qt.gray)) for k in range(0, len(self.dc[i].components)): child = QStandardItem('{}'.format( str(self.dc[i].components[k]))) child.setEditable(False) # Update the dict to keep track of row indices key = self.dc[i].label + self.dc[i].components[ k].label + self.dc[i].subsets[j].label self.subset_dict[key] = child.index() parent.appendRow(child) # Make gray and unselectable components that aren't defined for a subset try: self.dc[i].compute_statistic( 'mean', self.dc[i].subsets[j].components[k], subset_state=self.dc[i].subsets[j].subset_state ) except: # print("Glue has raised an Incompatible Attribute error on this component. Let's do this instead.") child.setEditable(False) child.setSelectable(False) child.setForeground(QtGui.QBrush(Qt.gray)) grandparent.appendRow(parent) parent_subset.appendRow(grandparent) self.model_subsets.appendRow(parent_subset) # Fill out the dict now that the indices are connected to the QStandardItemModel # Full datasets for i in range(0, parent_data.rowCount()): for j in range(0, parent_data.child(i).rowCount()): key = "All data (" + parent_data.child( i).text() + ")" + parent_data.child(i).child(j).text() self.subset_dict[key] = parent_data.child(i).child( j).index() # Subsets for i in range(0, parent_subset.rowCount()): for j in range(0, parent_subset.child(i).rowCount()): for k in range(0, parent_subset.child(i).child(j).rowCount()): key = parent_subset.child(i).child(j).text( ) + parent_subset.child(i).child(j).child(k).text() self.subset_dict[key] = parent_subset.child(i).child( j).child(k).index() self.treeview.setUniformRowHeights(True) selection_model = QItemSelectionModel(self.model_subsets) self.treeview.setSelectionModel(selection_model) selection_model.selectionChanged.connect(self.myPressedEvent) # Select rows that should be selected sel_mod = self.treeview.selectionModel() for i in range(0, len(selected)): # key = list(self.selected_dict.keys())[list(self.selected_dict.values()).index(self.selected_dict[i])] key = list(selected.keys())[i] index = self.subset_dict[key] print(index.parent().row(), index.row()) # print(index, type(index)) # print(type(self.treeview.selectionModel().select(index, QItemSelectionModel.Select))) # sel_mod.select(index, QItemSelectionModel.Select|QItemSelectionModel.Rows) self.treeview.setCurrentIndex(index) self.treeview.setSelectionModel(sel_mod) def sortByComponents(self): ''' Sorts the treeview by components- Dataset then component then subsets ''' # Save the selected rows from the subset view if applicable try: selected = dict() for i in range(0, len(self.selected_indices)): item = self.model_subsets.itemFromIndex( self.selected_indices[i]) if item.parent().parent().text() == "Data": key = "All data (" + item.parent().text( ) + ")" + item.text() selected[key] = item.index() else: key = item.parent().text() + item.text() selected[key] = item.index() except: pass # Set Expand/collapse button to "expand all" self.expand_data.setText("Expand all data and subsets") self.selection_model = QAbstractItemView.MultiSelection self.treeview.setSelectionMode(self.selection_model) # See if the model already exists try: self.treeview.setModel(self.model_components) except: self.model_components = QStandardItemModel() self.model_components.setHorizontalHeaderLabels(['']) self.treeview.setModel(self.model_components) self.treeview.setUniformRowHeights(True) # populate the tree # Make all the datasets be parents, and make it so they are not selectable for i in range(0, len(dc)): grandparent = QStandardItem('{}'.format(self.dc.labels[i])) grandparent.setIcon(helpers.layer_icon(self.dc[i])) grandparent.setEditable(False) grandparent.setSelectable(False) # Make all the data components be children, nested under their parent for k in range(0, len(self.dc[i].components)): parent = QStandardItem('{}'.format( str(self.dc[i].components[k]))) parent.setEditable(False) parent.setSelectable(False) child = QStandardItem('{}'.format('All data (' + self.dc.labels[i] + ')')) child.setIcon(helpers.layer_icon(self.dc[i])) child.setEditable(False) parent.appendRow(child) for j in range(0, len(self.dc.subset_groups)): child = QStandardItem('{}'.format( self.dc.subset_groups[j].label)) child.setEditable(False) child.setIcon( helpers.layer_icon(self.dc.subset_groups[j])) try: self.dc[i].compute_statistic( 'mean', self.dc[i].subsets[j].components[k], subset_state=self.dc[i].subsets[j].subset_state ) except: # print("Glue has raised an Incompatible Attribute error on this component. Let's do this instead.") child.setEditable(False) child.setSelectable(False) child.setForeground(QtGui.QBrush(Qt.gray)) parent.appendRow(child) grandparent.appendRow(parent) self.model_components.appendRow(grandparent) # Fill out the dict now that the indices are connected to the QStandardItemModel for i in range(0, grandparent.rowCount()): for j in range(0, grandparent.child(i).rowCount()): if grandparent.child(i).child(j).row() == 0: key = grandparent.child(i).child( j).text() + grandparent.child(i).text() self.component_dict[key] = grandparent.child( i).child(j).index() else: key = grandparent.child(i).child( j).text() + " (" + grandparent.text( ) + ")" + grandparent.child(i).text() self.component_dict[key] = grandparent.child( i).child(j).index() self.treeview.setUniformRowHeights(True) selection_model = QItemSelectionModel(self.model_components) self.treeview.setSelectionModel(selection_model) selection_model.selectionChanged.connect(self.myPressedEvent) # Select the rows that should be selected sel_mod = self.treeview.selectionModel() for i in range(0, len(selected)): key = list(selected.keys())[i] index = self.component_dict[key] # self.treeview.selectionModel().select(index, QItemSelectionModel.Select) print(index.parent().row(), index.row()) # This causes an error when it runs # sel_mod.select(index, QItemSelectionModel.Select|QItemSelectionModel.Rows) self.treeview.setCurrentIndex(index) self.treeview.setSelectionModel(sel_mod)
def init_ui(self, window: QMainWindow, system_info): v_box = QVBoxLayout() button_style = "background-color: #006699; padding-left:20px; padding-right:20px;" \ "padding-top:5px; padding-bottom:5px;" # proceed to the json - button json_button = QPushButton("Proceed to JSON") json_button.setStyleSheet(button_style) json_button.clicked.connect( lambda: self.display_plaintext_data(window, system_info)) v_box.addSpacing(20) # if system_info is empty if not system_info: nothing_found = QLabel("Nothing was found.") v_box.addWidget(nothing_found, alignment=Qt.AlignCenter) layout_grid = QGridLayout() for i, component in enumerate(system_info): if i == 0: prev_type = component["type"] else: prev_type = system_info[i - 1]["type"] if component["type"] != prev_type or i == 0: if component['type'] == 'I': title = QLabel('ITEMS') layout_grid.addWidget(title, 0, 0) else: title = QLabel('PRODUCTS') layout_grid.addWidget(title, 0, 1) # noinspection PyArgumentList title.setFont(QFont("futura", pointSize=16, italic=False)) if i != 0: v_box.addSpacing(10) tree = QTreeView() root_model = QStandardItemModel() root_model.setHorizontalHeaderLabels(['Name', 'Value']) tree.setModel(root_model) parent = root_model.invisibleRootItem() if component['type'] == 'I': self.list_element(component, parent) index = 0 else: index = 1 name = component['features'].get('type', 'Unknown component').upper() parent.appendRow([QStandardItem(name), QStandardItem('')]) new_parent = parent.child(parent.rowCount() - 1) for feature in component.items(): if feature[0] != "type": if feature[0] == 'features': self.list_features(str(feature[1]), new_parent) elif feature[0] == 'contents': self.list_contents(str(feature[1]), new_parent) else: self.list_data(feature[0], feature[1], new_parent) tree.expandAll() layout_grid.addWidget(tree, 1, index) tree.resizeColumnToContents(0) v_box.addLayout(layout_grid) v_box.addWidget(json_button, alignment=Qt.AlignCenter) v_box.addSpacing(20) self.setLayout(v_box)
class MainWindow(QWidget): def __init__(self): super().__init__() self.currentDict = {} self.initUI() def updateTree(self, data): print(data) # self.contents.show() def initUI(self): hLayout = QHBoxLayout() self.verticalGroupBox = QGroupBox() vLayout = QVBoxLayout() self.dropArea = DropArea("<drop here>", self) vLayout.addWidget(self.dropArea) self.dataList = QListWidget() vLayout.addWidget(self.dataList) self.verticalGroupBox.setLayout(vLayout) hLayout.addWidget(self.verticalGroupBox) self.contents = QTreeView() hLayout.addWidget(self.contents) self.setLayout(hLayout) self.setWindowTitle('Simplify3d to CSV') self.setGeometry(300, 300, 300, 150) self.dropArea.dropped.connect(self.onFileDropped) def onFileDropped(self, filename): filename = parse.unquote(filename) if filename.startswith('file:/'): filename = filename[6:] print(filename) if os.path.isfile(filename) is not True: return tree = ET.parse(filename) root = tree.getroot() self.currentDict = XmlDictConfig(root) ''' QList<QStandardItem *> preparedRow =prepareRow("first", "second", "third"); QStandardItem *item = standardModel->invisibleRootItem(); // adding a row to the invisible root item produces a root element item->appendRow(preparedRow); QList<QStandardItem *> secondRow =prepareRow("111", "222", "333"); // adding a row to an item starts a subtree preparedRow.first()->appendRow(secondRow); treeView->setModel(standardModel); treeView->expandAll(); ''' standardModel = QStandardItemModel() preparedRow = (QStandardItem("Title"), QStandardItem("Description")) item = standardModel.invisibleRootItem() item.appendRow(preparedRow) self.addDictTree(self.currentDict, item) self.contents.setModel(standardModel) self.contents.expandAll() print("dict reading finished") def addDictTree(self, data, item): for k,v in data.items(): if isinstance(v, dict): childItem = QStandardItem(k) item.appendRow(childItem) self.addDictTree(v, childItem) else: try: print(k+":"+v) item.appendRow((QStandardItem(k), QStandardItem(v))) except: print('Item adding failed')
class PyCommonist(QWidget): tool = None def __init__(self): super(PyCommonist, self).__init__() self.initUI() self.threads = [] self.workers = [] def initUI(self): self.currentDirectoryPath = "" self.generateSplitter() self.generateLeftTopFrame() self.generateLeftBottomFrame() self.showMaximized() self.setWindowTitle("PyCommonist - Wikimedia Commons") self.show() """ onSelectFolder """ def onSelectFolder(self, selected, deselected): try: currentIndex = selected.indexes()[0] self.currentDirectoryPath = self.modelTree.filePath(currentIndex) print(self.currentDirectoryPath) self.statusBar.setText("") self.exifImageCollection = [] list_dir = os.listdir(self.currentDirectoryPath) files = [ f for f in sorted(list_dir) if isfile(join(self.currentDirectoryPath, f)) ] for file in files: fullFilePath = os.path.join(self.currentDirectoryPath, file) if fullFilePath.endswith(".jpeg") or fullFilePath.endswith( ".jpg") or fullFilePath.endswith(".png"): currentExifImage = EXIFImage() currentExifImage.fullFilePath = fullFilePath currentExifImage.filename = file tags = None try: """ EXIF """ f_exif = open(fullFilePath, "rb") tags = exifread.process_file(f_exif) # print(tags) except: print("A problem with EXIF data reading") """ Location""" # 'GPS GPSLatitude', 'GPS GPSLongitude'] # [45, 49, 339/25] [4, 55, 716/25] # 'GPS GPSImgDirection' 'GPS GPSLatitudeRef' lat = "" long = "" heading = "" try: currentExifImage.lat, currentExifImage.long, currentExifImage.heading = get_exif_location( tags) except: print("A problem with EXIF data reading") dt = None try: """ Date Time """ dt = tags[ "EXIF DateTimeOriginal"] # 2021:01:13 14:48:44 except: print("A problem with EXIF data reading") print(dt) dt = str(dt) indexSpace = dt.find(" ") currentExifImage.date = dt[0:indexSpace].replace(":", "-") currentExifImage.time = dt[indexSpace + 1:] self.exifImageCollection.append(currentExifImage) print(currentExifImage) self.generateRightFrame() except: print("Something bad happened inside onSelectFolder function") traceback.print_exc() """ cbImportNoneStateChanged """ def cbImportNoneStateChanged(self): print(self.cbImportNone.isChecked()) print(len(self._currentUpload)) if self.cbImportNone.isChecked() and len(self._currentUpload) > 0: for element in self._currentUpload: element.cbImport.setCheckState(False) """ cbImportAllStateChanged """ def cbImportAllStateChanged(self): print(self.cbImportAll.isChecked()) print(len(self._currentUpload)) if self.cbImportAll.isChecked() and len(self._currentUpload) > 0: for element in self._currentUpload: element.cbImport.setCheckState(True) """ onClickImport """ def onClickImport(self): if self.tool == None: self.tool = UploadTool() ret = self.tool.uploadImages(self) """ cleanThreads """ def cleanThreads(self): try: print("Clean properly threads") for thread in self.threads: print("Current thread proper deletion") thread.quit() thread.wait() except: print("A problem with cleanThreads") """ generateSplitter """ def generateSplitter(self): vbox = QVBoxLayout() hbox = QHBoxLayout() self.leftTopFrame = QFrame() self.leftTopFrame.setFrameShape(QFrame.StyledPanel) self.rightWidget = QWidget() self.rightWidget.resize(300, 300) self.layoutRight = QVBoxLayout() self.rightWidget.setLayout(self.layoutRight) self.scroll = QScrollArea() self.layoutRight.addWidget(self.scroll) self.scroll.setWidgetResizable(True) self.scrollContent = QWidget(self.scroll) self.scrollLayout = QVBoxLayout(self.scrollContent) self.scrollContent.setLayout(self.scrollLayout) self.scroll.setWidget(self.scrollContent) self.splitterLeft = QSplitter(Qt.Vertical) self.leftBottonFrame = QFrame() self.leftBottonFrame.setFrameShape(QFrame.StyledPanel) self.splitterLeft.addWidget(self.leftTopFrame) self.splitterLeft.addWidget(self.leftBottonFrame) self.splitterLeft.setSizes([VERTICAL_TOP_SIZE, VERTICAL_BOTTOM_SIZE]) """ Horizontal Splitter """ self.splitterCentral = QSplitter(Qt.Horizontal) self.splitterCentral.addWidget(self.splitterLeft) self.splitterCentral.addWidget(self.rightWidget) self.splitterCentral.setSizes( [HORIZONTAL_LEFT_SIZE, HORIZONTAL_RIGHT_SIZE]) hbox.addWidget(self.splitterCentral) vbox.addLayout(hbox) """ Status Bar """ self.statusBar = QLabel() self.statusBar.setStyleSheet(STYLE_STATUSBAR) vbox.addWidget(self.statusBar) self.setLayout(vbox) """ generateLeftTopFrame """ def generateLeftTopFrame(self): self.layoutLeftTop = QFormLayout() self.layoutLeftTop.setFormAlignment(Qt.AlignTop) self.lblUserName = QLabel("Username: "******"Password: "******"Source: ") self.lblSource.setAlignment(Qt.AlignLeft) self.lineEditSource = QLineEdit() self.lineEditSource.setFixedWidth(WIDTH_WIDGET) self.lineEditSource.setText(LeftFrameConfig.source) self.lineEditSource.setAlignment(Qt.AlignLeft) self.layoutLeftTop.addRow(self.lblSource, self.lineEditSource) self.lblAuthor = QLabel("Author: ") self.lblAuthor.setAlignment(Qt.AlignLeft) self.lineEditAuthor = QLineEdit() self.lineEditAuthor.setFixedWidth(WIDTH_WIDGET) self.lineEditAuthor.setText(LeftFrameConfig.author) self.lineEditAuthor.setAlignment(Qt.AlignLeft) self.layoutLeftTop.addRow(self.lblAuthor, self.lineEditAuthor) self.lblCategories = QLabel("Categories: ") self.lblCategories.setAlignment(Qt.AlignLeft) self.lineEditCategories = QLineEdit() self.lineEditCategories.setText(LeftFrameConfig.categories) self.lineEditCategories.setFixedWidth(WIDTH_WIDGET) self.lineEditCategories.setAlignment(Qt.AlignLeft) self.layoutLeftTop.addRow(self.lblCategories, self.lineEditCategories) self.lblLicense = QLabel("License: ") self.lblLicense.setAlignment(Qt.AlignLeft) self.lineEditLicense = QLineEdit() self.lineEditLicense.setFixedWidth(WIDTH_WIDGET) self.lineEditLicense.setText(LeftFrameConfig.licence) self.lineEditLicense.setAlignment(Qt.AlignLeft) self.layoutLeftTop.addRow(self.lblLicense, self.lineEditLicense) self.lblDescription = QLabel("Description: ") self.lblDescription.setAlignment(Qt.AlignLeft) self.lineEditDescription = QPlainTextEdit() self.lineEditDescription.setFixedWidth(WIDTH_WIDGET) self.layoutLeftTop.addRow(self.lblDescription, self.lineEditDescription) separationLeftTopFrame = QLabel() self.layoutLeftTop.addWidget(separationLeftTopFrame) """ Button Import & None/All checkboxes""" importWidget = QWidget() importLayout = QHBoxLayout() importWidget.setLayout(importLayout) self.cbImportNone = QCheckBox("None") self.cbImportNone.stateChanged.connect(self.cbImportNoneStateChanged) self.cbImportAll = QCheckBox("All") self.cbImportAll.stateChanged.connect(self.cbImportAllStateChanged) self.btnImport = QPushButton("Import!") self.btnImport.clicked.connect(self.onClickImport) importLayout.addWidget(self.cbImportNone) importLayout.addWidget(self.cbImportAll) importLayout.addWidget(self.btnImport) self.layoutLeftTop.addWidget(importWidget) importWidget.setStyleSheet("border:1px solid #808080;") self.cbImportNone.setStyleSheet("border:0px;") self.cbImportAll.setStyleSheet("border:0px;") self.btnImport.setStyleSheet(STYLE_IMPORT_BUTTON) """ Layout Association of the Left Top Frame""" self.leftTopFrame.setLayout(self.layoutLeftTop) """ generateLeftBottomFrame """ def generateLeftBottomFrame(self): self.layoutLeftBottom = QVBoxLayout() """Model for QTreeView""" self.modelTree = QFileSystemModel() self.modelTree.setRootPath(QDir.currentPath()) self.modelTree.setFilter(QDir.Dirs) # Only directories """ QTreeView """ self.treeLeftBottom = QTreeView() self.treeLeftBottom.setModel(self.modelTree) self.treeLeftBottom.setAnimated(False) self.treeLeftBottom.setIndentation(10) self.treeLeftBottom.setColumnWidth(0, 300) self.treeLeftBottom.expandAll() self.treeLeftBottom.selectionModel().selectionChanged.connect( self.onSelectFolder) self.layoutLeftBottom.addWidget(self.treeLeftBottom) self.leftBottonFrame.setLayout(self.layoutLeftBottom) """ generateRightFrame """ def generateRightFrame(self): self._currentUpload = [] layout = self.scrollLayout print(layout) print(layout.count()) while layout.count(): print("destroy") child = layout.takeAt(0) if child.widget(): child.widget().deleteLater() for currentExifImage in self.exifImageCollection: """ Current image """ localWidget = ImageUpload() localLayout = QHBoxLayout() localLayout.setAlignment(Qt.AlignRight) localWidget.setLayout(localLayout) self.scrollLayout.addWidget(localWidget) self._currentUpload.append(localWidget) """Local Left Widget""" localLeftWidget = QWidget() localLeftLayout = QFormLayout() localLeftLayout.setAlignment(Qt.AlignRight) localLeftWidget.setLayout(localLeftLayout) localLayout.addWidget(localLeftWidget) """ import? + Import Status """ cbImport = QCheckBox("Import") lblUploadResult = QLabel() lblUploadResult.setStyleSheet(STYLE_IMPORT_STATUS) localLeftLayout.addRow(cbImport, lblUploadResult) localWidget.cbImport = cbImport localWidget.lblUploadResult = lblUploadResult """ File Name of picture """ lblFileName = QLabel("Name: ") lblFileName.setAlignment(Qt.AlignLeft) lineEditFileName = QLineEdit() lineEditFileName.setFixedWidth(WIDTH_WIDGET_RIGHT) lineEditFileName.setText(currentExifImage.filename) lineEditFileName.setAlignment(Qt.AlignLeft) localLeftLayout.addRow(lblFileName, lineEditFileName) localWidget.lineEditFileName = lineEditFileName """ Shadow Real FileName """ lblRealFileName = QLineEdit() lblRealFileName.setText(currentExifImage.filename) localWidget.lblRealFileName = lblRealFileName localWidget.lblRealFileName.isVisible = False """ Description """ lblDescription = QLabel("Description: ") lblDescription.setAlignment(Qt.AlignLeft) lineEditDescription = QPlainTextEdit() lineEditDescription.setFixedWidth(WIDTH_WIDGET_RIGHT) localLeftLayout.addRow(lblDescription, lineEditDescription) localWidget.lineEditDescription = lineEditDescription """ Categories """ lblCategories = QLabel("Categories: ") lblCategories.setAlignment(Qt.AlignLeft) lineEditCategories = QLineEdit() lineEditCategories.setFixedWidth(WIDTH_WIDGET_RIGHT) lineEditCategories.setAlignment(Qt.AlignLeft) localLeftLayout.addRow(lblCategories, lineEditCategories) localWidget.lineEditCategories = lineEditCategories lblLocation = QLabel("Location: ") lblLocation.setAlignment(Qt.AlignLeft) lineEditLocation = QLineEdit() lineEditLocation.setFixedWidth(WIDTH_WIDGET_RIGHT) if currentExifImage.lat == None or currentExifImage.long == None: lineEditLocation.setText("") else: lineEditLocation.setText( str(currentExifImage.lat) + "|" + str(currentExifImage.long) + "|heading:" + str(currentExifImage.heading)) lineEditLocation.setAlignment(Qt.AlignLeft) localLeftLayout.addRow(lblLocation, lineEditLocation) localWidget.lineEditLocation = lineEditLocation lblDateTime = QLabel("Date Time: ") lblDateTime.setAlignment(Qt.AlignLeft) lineEditDateTime = QLineEdit() lineEditDateTime.setFixedWidth(WIDTH_WIDGET_RIGHT) lineEditDateTime.setText(currentExifImage.date + " " + currentExifImage.time) lineEditDateTime.setAlignment(Qt.AlignLeft) localLeftLayout.addRow(lblDateTime, lineEditDateTime) localWidget.lineEditDateTime = lineEditDateTime """ Image itself """ label = QLabel() pixmap = QPixmap(currentExifImage.fullFilePath) pixmapResize = pixmap.scaledToWidth(IMAGE_DIMENSION, Qt.FastTransformation) label.setPixmap(pixmapResize) localLayout.addWidget(label) localWidget.fullFilePath = currentExifImage.fullFilePath self.update()
# Defining a couple of items americaItem = QStandardItem("America") canadaItem = QStandardItem("Canada") europeItem = QStandardItem("Europe") franceItem = QStandardItem("France") brittanyItem = QStandardItem("Brittany") # Building up the hierarchy rootItem.appendRow(americaItem) rootItem.appendRow(europeItem) americaItem.appendRow(canadaItem) europeItem.appendRow(franceItem) franceItem.appendRow(brittanyItem) tree_view.setModel(model) # Bind selection to the print_selection function (must be after the model setup) selection_model = tree_view.selectionModel() selection_model.selectionChanged.connect(print_selection) tree_view.expandAll() # expand all (this is not the case by default) tree_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. # The environment will be informed, how the application ended. sys.exit(exit_code)
class AttributeController(QMainWindow): _visibility_dict = { 'Beginner': EVisibility.Beginner, 'Expert': EVisibility.Expert, 'Guru': EVisibility.Guru, 'All': EVisibility.Invisible, } def __init__(self, node_map, parent=None): # super().__init__(parent=parent) # self.setWindowTitle('Attribute Controller') # self._view = QTreeView() self._view.setFont(get_system_font()) # self._node_map = node_map self._model = FeatureTreeModel(node_map=self._node_map, ) # self._proxy = FilterProxyModel() self._proxy.setSourceModel(self._model) self._proxy.setDynamicSortFilter(False) # self._delegate = FeatureEditDelegate(proxy=self._proxy) self._view.setModel(self._proxy) self._view.setItemDelegate(self._delegate) self._view.setUniformRowHeights(True) # unit = 260 for i in range(2): self._view.setColumnWidth(i, unit) w, h = 700, 600 self._view.setGeometry(100, 100, w, h) self.setCentralWidget(self._view) self.setGeometry(100, 100, unit * 2, 640) self._combo_box_visibility = None self._line_edit_search_box = None # self._setup_toolbars() def _setup_toolbars(self): # group_filter = self.addToolBar('Node Visibility') group_manipulation = self.addToolBar('Node Tree Manipulation') # label_visibility = QLabel() label_visibility.setText('Visibility') label_visibility.setFont(get_system_font()) # self._combo_box_visibility = QComboBox() self._combo_box_visibility.setSizeAdjustPolicy( QComboBox.AdjustToContents) # items = ('Beginner', 'Expert', 'Guru', 'All') for item in items: self._combo_box_visibility.addItem(item) shortcut_key = 'Ctrl+v' shortcut = QShortcut(QKeySequence(shortcut_key), self) def show_popup(): self._combo_box_visibility.showPopup() shortcut.activated.connect(show_popup) self._combo_box_visibility.setToolTip( compose_tooltip('Filter the nodes to show', shortcut_key)) self._combo_box_visibility.setFont(get_system_font()) self._combo_box_visibility.currentIndexChanged.connect( self._invalidate_feature_tree_by_visibility) # button_expand_all = ActionExpandAll(icon='expand.png', title='Expand All', parent=self, action=self.action_on_expand_all) shortcut_key = 'Ctrl+e' button_expand_all.setToolTip( compose_tooltip('Expand the node tree', shortcut_key)) button_expand_all.setShortcut(shortcut_key) button_expand_all.toggle() # button_collapse_all = ActionCollapseAll( icon='collapse.png', title='Collapse All', parent=self, action=self.action_on_collapse_all) shortcut_key = 'Ctrl+c' button_collapse_all.setToolTip( compose_tooltip('Collapse the node tree', shortcut_key)) button_collapse_all.setShortcut(shortcut_key) button_collapse_all.toggle() # label_search = QLabel() label_search.setText('RegEx Search') label_search.setFont(get_system_font()) # self._line_edit_search_box = QLineEdit() self._line_edit_search_box.setFont(get_system_font()) self._line_edit_search_box.textEdited.connect( self._invalidate_feature_tree_by_keyword) # group_filter.addWidget(label_visibility) group_filter.addWidget(self._combo_box_visibility) group_filter.addWidget(label_search) group_filter.addWidget(self._line_edit_search_box) group_filter.setStyleSheet('QToolBar{spacing:6px;}') # group_manipulation.addAction(button_expand_all) group_manipulation.addAction(button_collapse_all) # group_manipulation.actionTriggered[QAction].connect( self.on_button_clicked_action) # self._combo_box_visibility.setCurrentIndex( self._visibility_dict['Expert']) def _invalidate_feature_tree_by_visibility(self): visibility = self._visibility_dict[ self._combo_box_visibility.currentText()] self._proxy.setVisibility(visibility) self._view.expandAll() @pyqtSlot('QString') def _invalidate_feature_tree_by_keyword(self, keyword): self._proxy.setKeyword(keyword) self._view.expandAll() @staticmethod def on_button_clicked_action(action): action.execute() def expand_all(self): self._view.expandAll() def collapse_all(self): self._view.collapseAll() def resize_column_width(self): for i in range(self._model.columnCount()): self._view.resizeColumnToContents(i) def action_on_expand_all(self): self.expand_all() def action_on_collapse_all(self): self.collapse_all()
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 FileTreeView(QWidget): on_menu_select = pyqtSignal(str, str) on_dir_change = pyqtSignal() def __init__(self, parent: Application): super().__init__() self.stopped = threading.Event() self.tree = QTreeView() self.handle = self.Handler(self) self.get_data_file() self.model = QtGui.QStandardItemModel() self.item_construct = {} v_box = QVBoxLayout() v_box.addWidget(self.finder()) v_box.addWidget(self.tree_view()) self.setLayout(v_box) self.watch_dog() parent.app_close.connect(self.exit_push) self.tree.setAlternatingRowColors(True) def exit_push(self): self.stopped.set() def finder(self): w_find = QLineEdit() w_find.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)) w_find.textChanged.connect(self.sort_list) w_find.setPlaceholderText("Search file..") return w_find def sort_list(self, text): list_sort = [] if text == "": self.show_tree(self.list_file) else: for data in self.list_file: if text.lower() in data.name().lower(): list_sort.append(data) self.show_tree(list_sort) def watch_dog(self): watch = ProcessRunnable(target=watch_winform, args=(get_data_folder(), self.handle, self.stopped)) watch.start() class Handler(watchdog.events.PatternMatchingEventHandler): def __init__(self, parent): super().__init__() self.parent = parent def on_created(self, event): print("Watchdog received created event", event.src_path, sep=" : ") asyncio.run( self.parent.file_change('create', event.src_path, event.is_directory)) def on_modified(self, event): print("Watchdog received modified event", event.src_path, sep=" : ") asyncio.run( self.parent.file_change('modify', event.src_path, event.is_directory)) def on_moved(self, event): print("Watchdog received move event", event.src_path, event.dest_path, sep=" : ") asyncio.run( self.parent.file_change('move', event.src_path, event.is_directory, event.dest_path)) def on_deleted(self, event): print("Watchdog received delete event", event.src_path, sep=" : ") asyncio.run( self.parent.file_change('delete', event.src_path, event.is_directory)) async def file_change(self, tpe, old, is_directory, new=""): if tpe == "move": self.import_single(self.model.invisibleRootItem(), new) self.remove_single(old) elif tpe == "delete": self.remove_single(old) elif tpe == "create": self.import_single(self.model.invisibleRootItem(), old) if is_directory: self.on_dir_change.emit() def get_data_file(self): self.list_file = [] self.create_list(get_data_folder()) def create_list(self, dir): lst = os.listdir(path=dir) for f in lst: path = os.path.join(dir, f) file = MyFile() if os.path.isdir(path): file.setParentName(os.path.basename(dir)) file.setParent(dir) file.setName(f) file.setDir(True) self.list_file.append(file) self.create_list(path) else: file.setParentName(os.path.basename(dir)) file.setParent(dir) file.setName(f) file.setDir(False) self.list_file.append(file) def tree_view(self): self.tree.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.tree.setContextMenuPolicy(Qt.CustomContextMenu) self.tree.customContextMenuRequested.connect(self.open_menu) self.tree.doubleClicked.connect(self.open_event) # self.model.itemChanged.connect(self.data_change) self.tree.setModel(self.model) self.show_tree(self.list_file) return self.tree def show_tree(self, list_file): self.model.clear() self.model.setHorizontalHeaderLabels(['List API']) self.tree.header().setDefaultSectionSize(180) parent = self.model.invisibleRootItem() self.item_construct = {} self.import_data(parent, list_file) self.tree.expandAll() def import_data_by_path(self, parent, path: list, index): if index < len(path): full = os.sep.join(path[:index + 1]) if full in self.item_construct: item = self.item_construct[full] else: item = QStandardItem(path[index]) if os.path.isfile(os.path.join(get_data_folder(), full)): item.setToolTip( self.read_description( os.path.join(get_data_folder(), full))) item.setEditable(False) if not os.path.isdir(os.path.join(get_data_folder(), full)): item.setIcon(QIcon(get_icon_link("text_snippet.svg"))) else: item.setIcon(QIcon(get_icon_link("folder_yellow.svg"))) item.setData(full) parent.appendRow(item) self.item_construct[full] = item self.import_data_by_path(item, path, index + 1) def read_description(self, path): try: data = json.loads(open(path, encoding='utf-8').read()) json_data = APIData() json_data.construct(data) x = json_data.parseSave().description() if x.isspace() or x == "": return ".." else: return x except Exception as ex: print(ex) return ".." def import_data(self, parent, list_data): for i in list_data: self.import_single(parent, i) def import_single(self, parent, file_path): path = self.path_extract(file_path) self.import_data_by_path(parent, path, 0) def remove_single(self, file_path): path = self.path_extract(file_path) full = os.sep.join(path[:len(path)]) if full in self.item_construct: item = self.item_construct[full] (item.parent() or self.model.invisibleRootItem()).removeRow(item.row()) del self.item_construct[full] def path_extract(self, file_path): if isinstance(file_path, MyFile): path = os.path.join(file_path.parent(), file_path.name()) else: path = file_path path = path.replace(get_data_folder(), "") if path.startswith(os.sep): path = path.replace(os.sep, "", 1) path = path.split(os.sep) return path def open_menu(self, position): indexes = self.tree.selectedIndexes() level = 0 data = "" item = None if len(indexes) > 0: index = indexes[0] item = self.model.itemFromIndex(index) data = item.data() data = os.path.join(get_data_folder(), data) if os.path.isdir(data): level = 1 else: level = 2 menu = QMenu() menu.setStyleSheet(open(get_stylesheet()).read()) rename_action = QAction(QIcon(get_icon_link('edit.svg')), '&Rename', self) rename_action.setStatusTip('Rename') new_action = QAction(QIcon(get_icon_link('create_new_folder.svg')), '&New Folder', self) new_action.setStatusTip('New Folder') refresh_action = QAction(QIcon(get_icon_link('refresh.svg')), '&Refresh', self) refresh_action.setStatusTip('Refresh') delete_action = QAction(QIcon(get_icon_link('delete_forever.svg')), '&Delete', self) delete_action.setStatusTip('Delete') open_action = QAction(QIcon(get_icon_link('open_in_new.svg')), '&Open', self) open_action.setStatusTip('Open file') expand_action = QAction(QIcon(), '&Expand', self) expand_action.setStatusTip('Expand') collapse_action = QAction(QIcon(), '&Collapse', self) collapse_action.setStatusTip('Collapse') duplicate_action = QAction(QIcon(get_icon_link('content_copy.svg')), '&Duplicate', self) duplicate_action.setStatusTip('Duplicate') copy_action = QAction(QIcon(get_icon_link('content_copy.svg')), '&Copy', self) copy_action.setStatusTip('Copy') move_action = QAction(QIcon(get_icon_link('zoom_out_map.svg')), '&Move', self) move_action.setStatusTip('Move') if level == 1: menu.addAction(rename_action) menu.addAction(new_action) menu.addSeparator() menu.addAction(refresh_action) menu.addAction(expand_action) menu.addAction(collapse_action) menu.addSeparator() menu.addAction(delete_action) elif level == 2: menu.addAction(open_action) menu.addAction(new_action) menu.addAction(refresh_action) menu.addSeparator() menu.addAction(rename_action) menu.addAction(duplicate_action) menu.addAction(copy_action) menu.addAction(move_action) menu.addSeparator() menu.addAction(delete_action) else: menu.addAction(new_action) menu.addAction(refresh_action) action = menu.exec_(self.tree.viewport().mapToGlobal(position)) if action == open_action: if data != "": self.on_menu_select.emit("open", data) elif action == refresh_action: self.get_data_file() self.show_tree(self.list_file) elif action == expand_action: if item is not None: self.tree.expand(item.index()) elif action == collapse_action: if item is not None: self.tree.collapse(item.index()) elif action == delete_action: if data != "": msg = QMessageBox() msg.setStyleSheet(open(get_stylesheet()).read()) msg.setIcon(QMessageBox.Warning) msg.setBaseSize(QSize(500, 300)) msg.setText("Delete file.") msg.setInformativeText("Are you sure to detele " + os.path.basename(data) + "?") msg.setWindowTitle("Delete Warning!!!") msg.addButton('Delete', QMessageBox.YesRole) msg.addButton('Move to Trash', QMessageBox.YesRole) msg.addButton('Cancel', QMessageBox.NoRole) rs = msg.exec_() if rs == 0: if os.path.isdir(data): shutil.rmtree(data) else: os.remove(data) elif rs == 1: send2trash(data) elif action == new_action: if data == "": data = get_data_folder() # input_name = QInputDialog() # input_name.setStyleSheet(open(get_stylesheet()).read()) # text, ok = input_name.getText(self, 'New Folder', 'Folder name:') inp = QComboDialog('New Folder', 'Folder name:', QComboDialog.Text) ok = inp.exec_() if ok and inp.select: if os.path.isdir(data): try: os.mkdir(os.path.join(data, inp.select)) except Exception as ex: alert = Alert("Error", "Create folder error", str(ex)) alert.exec_() print(ex) else: new = os.path.join(os.path.dirname(data), inp.select) try: os.mkdir(new) except Exception as ex: alert = Alert("Error", "Create folder error", str(ex)) alert.exec_() print(ex) elif action == rename_action: if data != "": # input_name = QInputDialog() # input_name.setStyleSheet(open(get_stylesheet()).read()) # text, ok = input_name.getText(self, 'Rename file', 'New name:') inp = QComboDialog('Rename file', 'New name:', QComboDialog.Text) ok = inp.exec_() if ok and inp.select: if os.path.isdir(data): new = os.path.join(os.path.dirname(data), inp.select) try: os.rename(data, new) except Exception as ex: alert = Alert("Error", "Rename folder error", str(ex)) alert.exec_() print(ex) else: filename, file_extension = os.path.splitext(data) new = os.path.join(os.path.dirname(data), inp.select + file_extension) try: os.rename(data, new) except Exception as ex: alert = Alert("Error", "Rename file error", str(ex)) alert.exec_() print(ex) elif action == move_action: if data != "": items = get_list_folder(get_data_folder(), get_data_folder()) # # item, ok = QInputDialog.getItem(self, "Select folder dialog", # "Select the destination folder", items, 0, False) inp = QComboDialog("Move", "Select the destination folder", QComboDialog.ComboBox, items) ok = inp.exec_() if ok and inp.select: folder = inp.select if inp.select.startswith(os.sep): folder = inp.select.replace(os.sep, "", 1) new = os.path.join(get_data_folder(), folder, os.path.basename(data)) try: os.rename(data, new) except Exception as ex: alert = Alert("Error", "Move file error", str(ex)) alert.exec_() print(ex) elif action == duplicate_action: if data != "": # input_name = QInputDialog() # input_name.setStyleSheet(open(get_stylesheet()).read()) # text, ok = input_name.getText(self, 'Duplicate file', 'New name:') inp = QComboDialog('Duplicate file', 'New name:', QComboDialog.Text) filename, file_extension = os.path.splitext(data) inp.set_init_text(filename) ok = inp.exec_() if ok and inp.select: new = os.path.join(os.path.dirname(data), inp.select + file_extension) try: copyfile(data, new) except Exception as ex: alert = Alert("Error", "Duplicate file error", str(ex)) alert.exec_() print(ex) elif action == copy_action: if data != "": items = get_list_folder(get_data_folder(), get_data_folder()) inp = QComboDialog("Copy", "Select the destination folder", QComboDialog.ComboBox, items) ok = inp.exec_() # item, ok = QInputDialog.getItem(self, "Select folder dialog", # "Select the destination folder", items, 0, False) if ok and inp.select: folder = inp.select if inp.select.startswith(os.sep): folder = inp.select.replace(os.sep, "", 1) new = os.path.join(get_data_folder(), folder, os.path.basename(data)) try: copyfile(data, new) except Exception as ex: alert = Alert("Error", "Copy file error", str(ex)) alert.exec_() print(ex) def open_event(self, index): item = self.model.itemFromIndex(index) if item is not None: data = item.data() data = os.path.join(get_data_folder(), data) if os.path.isdir(data): level = 1 else: level = 2 if data != "": if level == 2: self.on_menu_select.emit("open", data) elif level == 1: if not self.tree.isExpanded(item.index()): self.tree.collapse(item.index()) else: self.tree.expand(item.index())
class TestDialog(QDialog): def __init__(self, data): super(TestDialog, self).__init__() self.data = copy.deepcopy(data) # Layout btOk = QPushButton("OK") btCancel = QPushButton("Cancel") self.tree = QTreeView() hbox = QHBoxLayout() hbox.addStretch(1) hbox.addWidget(btOk) hbox.addWidget(btCancel) vbox = QVBoxLayout() vbox.addLayout(hbox) vbox.addWidget(self.tree) self.setLayout(vbox) self.setGeometry(300, 300, 600, 400) # Button signals btCancel.clicked.connect(self.reject) btOk.clicked.connect(self.accept) # Tree view self.tree.setModel(QStandardItemModel()) self.tree.setAlternatingRowColors(True) self.tree.setSortingEnabled(True) self.tree.setHeaderHidden(False) self.tree.setSelectionBehavior(QAbstractItemView.SelectItems) self.tree.model().setHorizontalHeaderLabels(['Parameter', 'Value']) for x in self.data: if not self.data[x]: continue parent = QStandardItem(x) parent.setFlags(QtCore.Qt.NoItemFlags) for y in self.data[x]: value = self.data[x][y] child0 = QStandardItem(y) child0.setFlags(QtCore.Qt.NoItemFlags | QtCore.Qt.ItemIsEnabled) child1 = QStandardItem(repr(value)) child1.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable | ~QtCore.Qt.ItemIsSelectable) parent.appendRow([child0, child1]) self.tree.model().appendRow(parent) self.tree.expandAll() self.tree.model().itemChanged.connect(self.handleItemChanged) def get_data(self): return self.data def handleItemChanged(self, item): parent = self.data[item.parent().text()] key = item.parent().child(item.row(), 0).text() parent[key] = eval(item.text())
class AddBookmarkDialog(QDialog, Ui_AddBookmarkDialog): """ Class implementing a dialog to add a bookmark or a bookmark folder. """ def __init__(self, parent=None, bookmarksManager=None): """ Constructor @param parent reference to the parent widget (QWidget) @param bookmarksManager reference to the bookmarks manager object (BookmarksManager) """ super(AddBookmarkDialog, self).__init__(parent) self.setupUi(self) self.__bookmarksManager = bookmarksManager self.__addedNode = None self.__addFolder = False if self.__bookmarksManager is None: import Helpviewer.HelpWindow self.__bookmarksManager = \ Helpviewer.HelpWindow.HelpWindow.bookmarksManager() self.__proxyModel = AddBookmarkProxyModel(self) model = self.__bookmarksManager.bookmarksModel() self.__proxyModel.setSourceModel(model) self.__treeView = QTreeView(self) self.__treeView.setModel(self.__proxyModel) self.__treeView.expandAll() self.__treeView.header().setStretchLastSection(True) self.__treeView.header().hide() self.__treeView.setItemsExpandable(False) self.__treeView.setRootIsDecorated(False) self.__treeView.setIndentation(10) self.__treeView.show() self.locationCombo.setModel(self.__proxyModel) self.locationCombo.setView(self.__treeView) self.addressEdit.setInactiveText(self.tr("Url")) self.nameEdit.setInactiveText(self.tr("Title")) self.resize(self.sizeHint()) def setUrl(self, url): """ Public slot to set the URL of the new bookmark. @param url URL of the bookmark (string) """ self.addressEdit.setText(url) self.resize(self.sizeHint()) def url(self): """ Public method to get the URL of the bookmark. @return URL of the bookmark (string) """ return self.addressEdit.text() def setTitle(self, title): """ Public method to set the title of the new bookmark. @param title title of the bookmark (string) """ self.nameEdit.setText(title) def title(self): """ Public method to get the title of the bookmark. @return title of the bookmark (string) """ return self.nameEdit.text() def setDescription(self, description): """ Public method to set the description of the new bookmark. @param description description of the bookamrk (string) """ self.descriptionEdit.setPlainText(description) def description(self): """ Public method to get the description of the bookmark. @return description of the bookamrk (string) """ return self.descriptionEdit.toPlainText() def setCurrentIndex(self, idx): """ Public method to set the current index. @param idx current index to be set (QModelIndex) """ proxyIndex = self.__proxyModel.mapFromSource(idx) self.__treeView.setCurrentIndex(proxyIndex) self.locationCombo.setCurrentIndex(proxyIndex.row()) def currentIndex(self): """ Public method to get the current index. @return current index (QModelIndex) """ idx = self.locationCombo.view().currentIndex() idx = self.__proxyModel.mapToSource(idx) return idx def setFolder(self, folder): """ Public method to set the dialog to "Add Folder" mode. @param folder flag indicating "Add Folder" mode (boolean) """ self.__addFolder = folder if folder: self.setWindowTitle(self.tr("Add Folder")) self.addressEdit.setVisible(False) else: self.setWindowTitle(self.tr("Add Bookmark")) self.addressEdit.setVisible(True) self.resize(self.sizeHint()) def isFolder(self): """ Public method to test, if the dialog is in "Add Folder" mode. @return flag indicating "Add Folder" mode (boolean) """ return self.__addFolder def addedNode(self): """ Public method to get a reference to the added bookmark node. @return reference to the added bookmark node (BookmarkNode) """ return self.__addedNode def accept(self): """ Public slot handling the acceptance of the dialog. """ if (not self.__addFolder and not self.addressEdit.text()) or \ not self.nameEdit.text(): super(AddBookmarkDialog, self).accept() return from .BookmarkNode import BookmarkNode idx = self.currentIndex() if not idx.isValid(): idx = self.__bookmarksManager.bookmarksModel().index(0, 0) parent = self.__bookmarksManager.bookmarksModel().node(idx) if self.__addFolder: type_ = BookmarkNode.Folder else: type_ = BookmarkNode.Bookmark bookmark = BookmarkNode(type_) bookmark.title = self.nameEdit.text() if not self.__addFolder: bookmark.url = self.addressEdit.text() bookmark.desc = self.descriptionEdit.toPlainText() self.__bookmarksManager.addBookmark(parent, bookmark) self.__addedNode = bookmark super(AddBookmarkDialog, self).accept()
def __init__(self): super().__init__() self.setWindowTitle('World Country Diagram') self.resize(500, 700) treeView = QTreeView() treeView.setHeaderHidden(False) treeModel = QStandardItemModel() self.most_used_cat_header = [ 'Nom', "Prénom", "Sexe", "Date Inscription", "Courriel Client", "Mot de passe", "Numéro de carte", "Expiration", "Code" ] treeModel.setHorizontalHeaderLabels(self.most_used_cat_header) rootNode = treeModel.invisibleRootItem() # America america = QStandardItem('America') california = QStandardItem('California') america.appendRow(california) oakland = QStandardItem('Oakland') sanfrancisco = QStandardItem('San Francisco') sanjose = QStandardItem('San Jose') california.appendRow(oakland) california.appendRow(sanfrancisco) california.appendRow(sanjose) texas = QStandardItem('Texas') america.appendRow(texas) austin = QStandardItem('Austin') houston = QStandardItem('Houston') dallas = QStandardItem('dallas') testo = QStandardItem("testo") testo2 = QStandardItem("testo2") testo3 = QStandardItem("testo3") vide1 = QStandardItem("") vide2 = QStandardItem("") vide3 = QStandardItem("") item = (austin, houston, dallas) america.appendRow(item) item2 = (vide1, vide2, vide3, testo, testo2, testo3) #texas.appendRow(austin) #texas.appendRow(houston) #texas.appendRow(dallas) austin.appendRow(item2) # Canada canada = QStandardItem('Canada') alberta = QStandardItem('Alberta') bc = QStandardItem('British Columbia') ontario = QStandardItem('Ontario') canada.appendRows([alberta, bc, ontario]) rootNode.appendRow(america) rootNode.appendRow(canada) treeView.setModel(treeModel) treeView.expandAll() treeView.doubleClicked.connect(self.getValue) self.setCentralWidget(treeView)
class KerningWindow(QWidget): def __init__(self, font, parent=None): super().__init__(parent, Qt.Window) self._font = font self._font.kerning.addObserver( self, "_kerningChanged", "Kerning.Changed") self._font.info.addObserver(self, "_fontInfoChanged", "Info.Changed") self.kerningView = QTreeView(self) self.kerningView.setModel( KerningDictModel(font.kerning, self.kerningView)) self.kerningView.expandAll() metrics = self.kerningView.fontMetrics() self.kerningView.setColumnWidth(1, 8 * metrics.width("0")) hdr = self.kerningView.header() hdr.setStretchLastSection(False) hdr.setSectionResizeMode(0, hdr.Stretch) hdr.hide() layout = QVBoxLayout(self) layout.addWidget(self.kerningView) layout.setContentsMargins(0, 0, 0, 0) self.updateWindowTitle(font=font) self.readSettings() def readSettings(self): geometry = settings.kerningWindowGeometry() if geometry: self.restoreGeometry(geometry) def writeSettings(self): settings.setKerningWindowGeometry(self.saveGeometry()) def updateWindowTitle(self, title=None, font=None): if title is None: title = self.tr("Kerning") if font is not None: title = "%s – %s %s" % ( title, font.info.familyName, font.info.styleName) self.setWindowTitle(title) # ------------- # Notifications # ------------- def _kerningChanged(self, notification): model = self.kerningView.model() model.setupModelData(self._font.kerning) def _fontInfoChanged(self, notification): self.updateWindowTitle(font=self._font) # ---------- # Qt methods # ---------- def sizeHint(self): return QSize(280, 460) def moveEvent(self, event): self.writeSettings() resizeEvent = moveEvent def closeEvent(self, event): super().closeEvent(event) if event.isAccepted(): self._font.kerning.removeObserver(self, "Kerning.Changed") self._font.info.removeObserver(self, "Info.Changed")
### model = QStandardItemModel(None) row1 = [QStandardItem("a"), QStandardItem("b")] row2 = [QStandardItem("c"), QStandardItem("d")] row3 = [QStandardItem("e"), QStandardItem("f")] row4 = [QStandardItem("g"), QStandardItem("h")] row5 = [QStandardItem("i"), QStandardItem("j")] root_item = model.invisibleRootItem() root_item.appendRow(row1) row1[0].appendRow(row2) row1[0].appendRow(row3) #row1[1].appendRow(row4) # ignored root_item.appendRow(row5) ### tree_view.setModel(model) tree_view.expandAll() # expand all (this is not the case by default) tree_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. # The environment will be informed, how the application ended. sys.exit(exit_code)
class EditScreen(QWidget): def __init__(self, rester: Rester): super(EditScreen, self).__init__() self.last_item_type = '' self.rester = rester self.tree = QTreeView() layout = QVBoxLayout() layout.addWidget(self.tree) self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels(['Name']) self.tree.header().setDefaultSectionSize(200) self.tree.setModel(self.model) self.data = self.rester.list_request(list(APIEnum)) self.import_data(self.data) self.tree.setSortingEnabled(False) self.tree.expandAll() self.tree.setSelectionMode(QAbstractItemView.SingleSelection) self.tree.selectionModel().selectionChanged.connect(self.item_selected) self.init_ui() def init_ui(self): hbox = QHBoxLayout() vbox = QVBoxLayout() left_layout = QFormLayout() tree_group_box = QGroupBox() self.search_field = QLineEdit() # self.search_field.editingFinished.connect(self.search_field_changed) self.search_field.textEdited.connect(self.search_field_changed) left_layout.addRow(QLabel('Search:'), self.search_field) tree_group_box.setLayout(left_layout) vbox.addWidget(tree_group_box) vbox.addWidget(self.tree) gb = QGroupBox() gb.setLayout(vbox) self.splitter = QSplitter() self.splitter.addWidget(gb) self.ew = EquipmentEdit() self.splitter.addWidget(self.ew) hbox.addWidget(self.splitter) self.setLayout(hbox) def import_data(self, data, root=None): self.model.setRowCount(0) if root is None: root = self.model.invisibleRootItem() for key, value_list in data.items(): parent = TreeItem({'name': key}, '', parent=True) parent.setEditable(False) root.appendRow([parent]) for value in value_list: parent.appendRow([TreeItem(value, key)]) def search_field_changed(self): term = self.search_field.text() data = {} for key, value_list in self.data.items(): li = [] for value in value_list: for k in value.keys(): if term in k: li.append(value) data[key] = li self.import_data(data) def item_selected(self): indexes = self.tree.selectedIndexes() selected = indexes[0] item = self.model.itemFromIndex(selected) if item.is_parent: return #if item.typ != self.last_item_type: self.ew.hide() self.ew.destroy() self.ew = get_edit_widget(item.typ, self.data) self.splitter.addWidget(self.ew) self.last_item_type = item.typ data_dict = item.get_data_dict() self.ew.load_data(data_dict)
class ProjectStatusDialog(QDialog): icons = { 'added': 'images/FA_icons/plus.svg', 'removed': 'images/FA_icons/trash.svg', 'updated': 'images/FA_icons/edit.svg', 'renamed': 'images/FA_icons/edit.svg', 'table': 'images/FA_icons/table.svg' } def __init__(self, pull_changes, push_changes, push_changes_summary, has_write_permissions, parent=None): super(ProjectStatusDialog, self).__init__(parent) self.setWindowTitle("Project status") self.table = QTreeView() self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels(["Status"]) self.table.setModel(self.model) self.add_content(pull_changes, 'Server changes', True) self.add_content(push_changes, 'Local changes', False, push_changes_summary) self.table.expandAll() box = QDialogButtonBox( QDialogButtonBox.Ok, centerButtons=True, ) box.accepted.connect(self.accept) box.rejected.connect(self.reject) lay = QVBoxLayout(self) lay.addWidget(self.table) has_files_to_replace = any([ "diff" not in file and is_versioned_file(file['path']) for file in push_changes["updated"] ]) info_text = self._get_info_text(has_files_to_replace, has_write_permissions) if info_text: text_box = QLabel() text_box.setWordWrap(True) text_box.setText(info_text) lay.addWidget(text_box) lay.addWidget(box, Qt.AlignCenter) self.resize(640, 640) def _get_info_text(self, has_files_to_replace, has_write_permissions): msg = "" if not has_write_permissions: msg += f"WARNING: You don't have writing permissions to this project. Changes won't be synced!\n" if has_files_to_replace: msg += f"\nWARNING: Unable to compare some of the modified files with their server version - " \ f"their history will be lost if uploaded." return msg def add_content(self, changes, root_text, is_server, changes_summary={}): """ Adds rows with changes info :param changes: Dict of added/removed/updated/renamed changes :param root_text: Text for the root item :param is_server: True if changes are related to server file changes :param changes_summary: If given and non empty, extra rows are added from geodiff summary. :return: """ if all(not changes[k] for k in changes): return root_item = QStandardItem(root_text) self.model.appendRow(root_item) for category in changes: for file in changes[category]: path = file['path'] item = self._get_icon_item(category, path) if is_versioned_file(path): if path in changes_summary: for sub_item in self._versioned_file_summary_items( changes_summary[path]['geodiff_summary']): item.appendRow(sub_item) elif not is_server: item.appendRow( QStandardItem("Unable to detect changes")) root_item.appendRow(item) def _versioned_file_summary_items(self, geodiff_summary): items = [] for s in geodiff_summary: table_name_item = self._get_icon_item('table', s['table']) for row in self._table_summary_items(s): table_name_item.appendRow(row) items.append(table_name_item) return items def _table_summary_items(self, summary): return [ QStandardItem("{}: {}".format(k, summary[k])) for k in summary if k != 'table' ] def _get_icon_item(self, key, text): path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.icons[key]) item = QStandardItem(text) item.setIcon(QIcon(path)) return item
class ShellTableEditor(QWidget): def __init__(self, parent=None): super().__init__(parent) self.model = None self.shell_model = ShellTableTreeModel() self.tree_view = QTreeView(self) self.tree_view.setModel(self.shell_model) layout = QVBoxLayout() self.setLayout(layout) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.init_navigation(), 0) layout.addWidget(self.tree_view, 1) layout.addWidget(self.init_docs(), 0) def get_icon(self, name): return self.style().standardIcon(name) def init_navigation(self): self.prev_button = QPushButton(self.get_icon(QStyle.SP_ArrowLeft), "") self.prev_button.setFlat(True) self.next_button = QPushButton(self.get_icon(QStyle.SP_ArrowRight), "") self.next_button.setFlat(True) self.prev_button.clicked.connect(self.handle_prev_button_clicked) self.next_button.clicked.connect(self.handle_next_button_clicked) self.current_index = QSpinBox() self.current_index.setMinimumWidth(60) self.current_index.valueChanged.connect(self.handle_current_index_changed) box = QWidget(self) box_layout = QHBoxLayout(box) box_layout.setContentsMargins(0, 0, 0, 0) box.setLayout(box_layout) box_layout.addWidget(self.prev_button, 0) box_layout.addWidget(self.next_button, 0) box_layout.addWidget(QLabel("Index:"), 0) box_layout.addWidget(self.current_index, 0) box_layout.addStretch(1) return box def init_docs(self): box = QWidget(self) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) box.setLayout(layout) layout.addWidget(QLabel(DOC_CAPACITY), 0, Qt.AlignTop) layout.addWidget(QLabel(DOC_RECOIL), 0, Qt.AlignTop) layout.addWidget(QLabel(DOC_RELOAD), 1, Qt.AlignTop) return box def handle_current_index_changed(self, value): parent_index = QModelIndex() qindex = self.shell_model.index(self.current_index.value(), 0, parent_index) self.tree_view.setRootIndex(qindex) self.tree_view.expandAll() def handle_prev_button_clicked(self): self.current_index.setValue(self.current_index.value() - 1) def handle_next_button_clicked(self): self.current_index.setValue(self.current_index.value() + 1) def set_model(self, model): self.model = model if model is None: self.shell_model.update([]) self.current_index.setMaximum(0) else: self.shell_model.update(self.model.data.entries) self.current_index.setMaximum(len(self.model.data.entries) - 1) self.handle_current_index_changed(0) self.tree_view.header().resizeSection(0, 120)
class Example(QWidget): def __init__(self): super().__init__() # make a text window for terminal output self.txt = QTextEdit() self.txt.setReadOnly(True) # do something with a push button self.btn = QPushButton('scan') self.btn.clicked.connect(self.run_scan) # A sort of Gadget widget - here using pyqt's model/view concept self.model = GadgetModel() self.tree = QTreeView() self.tree.setModel(self.model) self.tree.expandAll() self.monitor = GadgetMonitor() self.monitor.update.connect(self.model.motor_update) self.monitor.start() # A progress bar for scans self.progress = QProgressBar() self.progress.setMinimum(0) self._last_dt = 0 self.plot = pg.PlotWidget(parent=self, background='white') # layout containing these things hbox = QHBoxLayout() col1 = QVBoxLayout() col2 = QVBoxLayout() col1.addWidget(self.tree) col1.addWidget(self.txt) col1.addWidget(self.btn) col1.addWidget(self.progress) col2.addWidget(self.plot) hbox.addLayout(col1) hbox.addLayout(col2) self.setLayout(hbox) self.resize(1200, 700) # redirect stdout queue = Queue() sys.stdout = StreamWriter(queue) self.reader = StreamReader(queue) self.reader.signal.connect(self.new_stdout) self.reader.start() # a special recorder which generates qt signals queue = MP_Queue() self.qtrec = PyQtRecorder(queue, name='qtrec') self.qtrec.start() self.emitter = RecorderEmitter(queue) self.emitter.new.connect(self.new_data) self.emitter.start() # a scan runner object we can refer to, self.runner = None # quote contrast.wisdom() @pyqtSlot(dict) def new_data(self, dct): self.txt.setTextColor(QColor('black')) # for example if 'status' in dct.keys(): # header/footer if dct['status'] == 'started': self.progress.setValue(0) else: # update progress bar m = self.progress.maximum() n = self.progress.value() + 1 eta = str( datetime.timedelta( seconds=((m - n) * (dct['dt'] - self._last_dt)))).split('.')[0] self.progress.setValue(n) self.progress.setFormat('%u/%u (done in %s)' % (n, m, eta)) self._last_dt = dct['dt'] # plot something self.plot.plot([dct['sx']], [dct['det1']], symbol='o', color='k') @pyqtSlot(str) def new_stdout(self, msg): """ Intercepts the stdout signal and adds it to a text box or so. """ if len(msg) < 2: # exclude empty strings return if '\r' in msg: # exclude overwritten text return self.print(msg, 'gray') def print(self, msg, color='black'): self.txt.setTextColor(QColor(color)) self.txt.append(str(msg)) def run_scan(self): """ Helper method which puts together the scan to run, and launches a ScanRunner object. """ if self.runner and self.runner.isRunning(): return N = np.random.randint(5, 40) self.progress.setMaximum(N + 1) self.runner = ScanRunner(AScan, *[bl.sx, 0, 1, N, .1]) self.runner.start()
class MainWindow(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.setMinimumSize(QSize(480, 80)) self.setWindowTitle("PyQtSample") central_widget = QWidget(self) self.setCentralWidget(central_widget) hbox_layout = QHBoxLayout(self) central_widget.setLayout(hbox_layout) buttons_background = QWidget(self) buttons_layout = QHBoxLayout() buttons_background.setLayout(buttons_layout) self.add_button = QPushButton("Add") buttons_layout.addWidget(self.add_button) self.remove_button = QPushButton("Remove") buttons_layout.addWidget(self.remove_button) tree_layout = QVBoxLayout(self) self.tree_view = QTreeView() self.tree_view.header().hide() self.tree_view.setMaximumWidth(300) self.tree_model = ObjectsModel.TreeModel() self.tree_view.setModel(self.tree_model) tree_layout.addWidget(buttons_background) tree_layout.addWidget(self.tree_view) hbox_layout.addLayout(tree_layout) self.graphics_view = QGraphicsView() self.scene = QGraphicsScene() self.graphics_view.setScene(self.scene) hbox_layout.addWidget(self.graphics_view) self.properties_view = QTableView() self.properties_view.setMaximumWidth(300) self.properties_model = PropertiesModel.TableModel() self.properties_view.setModel(self.properties_model) hbox_layout.addWidget(self.properties_view) self.init_menu() self.test() self.connectSignals() def connectSignals(self): self.add_button.clicked.connect(self.onAddClicked) self.remove_button.clicked.connect(self.onRemoveClicked) self.tree_view.clicked.connect(self.onClicked) self.properties_model.dataChanged.connect(self.onPropertyChanged) def onPropertyChanged(self): self.rebuildModel() def rebuildModel(self): #save index hierarhy indexes = [] tmp_index = self.tree_view.currentIndex() indexes.append(tmp_index) while tmp_index.parent().isValid(): indexes.append(tmp_index.parent()) tmp_index = tmp_index.parent() self.tree_model.initRoot(self.items) self.tree_view.expandAll() if len(indexes) == 0: self.onClicked(self.tree_model.index(0, 0)) else: last_index = indexes.pop(-1) index = self.tree_model.index(last_index.row(), last_index.column()) #restore index hierarchy while len(indexes) > 0: last_index = indexes.pop(-1) index = self.tree_model.index(last_index.row(), last_index.column(), index) if index.isValid(): self.onClicked(index) else: self.onClicked(self.tree_model.index(0, 0)) def init_menu(self): exit_action = QAction("&Exit", self) exit_action.setShortcut('Ctrl+Q') exit_action.triggered.connect(qApp.quit) file_menu = self.menuBar().addMenu("&File") file_menu.addAction(QAction("Open", self)) file_menu.addAction(QAction("Save", self)) file_menu.addAction(QAction("SaveAs", self)) file_menu.addSeparator() file_menu.addAction(exit_action) def appendObjectOnScene(self, object=DataStructures.Object): if object.rect.isValid(): self.scene.addRect(object.rect, object.color) for child in object.childrens: self.appendObjectOnScene(child) def onClicked(self, index): object = index.data(Qt.UserRole + 1) self.properties_model.initProperties(object) self.scene.clear() self.appendObjectOnScene(object) self.tree_view.setCurrentIndex(index) def onAddClicked(self): index = self.tree_view.currentIndex() print(index) object = index.data(Qt.UserRole + 1) print(object.description()) object.add_children(DataStructures.Object("New item")) self.rebuildModel() def onRemoveClicked(self): index = self.tree_view.currentIndex() object = index.data(Qt.UserRole + 1) if not object.parent: self.items.remove(object) else: object.parent.childrens.remove(object) self.rebuildModel() def test(self): self.items = [] static = DataStructures.Object( "Static", DataStructures.createRect(0, 0, 800, 200)) static.add_children(DataStructures.Object("child_1")) static.add_children(DataStructures.Object("child_2")) static.add_children(DataStructures.Object("child_3")) static.color = QColor(200, 0, 0).name() static.childrens[0].add_children( DataStructures.Object("child_1.1", DataStructures.createRect(40, 40, 80, 40))) self.items.append(static) dynamic = DataStructures.Object( "Dynamic", DataStructures.createRect(0, 0, 200, 800)) dynamic.add_children(DataStructures.Object("child_1")) dynamic.add_children(DataStructures.Object("child_2")) dynamic.add_children(DataStructures.Object("child_3")) dynamic.childrens[2].add_children(DataStructures.Object("child_2.1")) dynamic.color = QColor(0, 0, 200).name() self.items.append(dynamic) self.rebuildModel()
class ImageViewer(QWidget): def __init__(self): super(ImageViewer, self).__init__() pal = QPalette() pal.setColor(QPalette.Background, Qt.lightGray) self.factor = 3.0 self.config = Config() self.currentRep = "" self.createActions() self.createToolbarMenus() #self.createMenus() self.browserFile() self.imgqLabel() self.boxSliders() self.verticalLayout = QVBoxLayout(self) self.horizontalLayout = QHBoxLayout(self) self.textInfo = QTextEdit() self.textInfoTop = QTextEdit() self.textInfoTop.setEnabled(True) self.textInfoTop.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Ignored) self.textInfoTop.setFontPointSize(11) self.textInfoTop.setStyleSheet("background-color: lightgray") #self.textInfoTop.adjustSize() self.textInfoTop.setText('Welcome to IRMaGe') self.tableJson = QTableWidget() self.tableJson.setColumnCount(2) self.tableJson.setColumnWidth(0, 150) self.tableJson.setColumnWidth(1, 400) self.tableJson.setSizeAdjustPolicy( QAbstractScrollArea.AdjustToContents) self.tableJson.setHorizontalHeaderLabels(['Keys', 'Values']) #self.tableJson.setBackgroundRole(QPalette.Light) self.scrollText = QScrollArea() self.scrollText.setBackgroundRole(QPalette.Dark) self.scrollText.setWidget(self.textInfoTop) self.scrollText.setWidgetResizable(True) #======================================================================= # self.adjustScrollBar(self.scrollText.horizontalScrollBar(), 1.0) # self.adjustScrollBar(self.scrollText.verticalScrollBar(), 2.0) #======================================================================= self.scrollTable = QScrollArea() self.scrollTable.setBackgroundRole(QPalette.Dark) self.scrollTable.setWidget(self.tableJson) self.scrollTable.setWidgetResizable(True) #======================================================================= # self.adjustScrollBar(self.scrollTable.horizontalScrollBar(), 2.0) # self.adjustScrollBar(self.scrollTable.verticalScrollBar(), 2.0) #======================================================================= self.headerTabData = [ 'Data', 'PatientName', 'StudyName', 'DateCreation', 'PatientSex', 'PatientWeight', 'ProtocolName', 'SequenceName' ] self.tableData = TableDataBrower(self) self.tableData.setColumnCount(8) self.tableData.setRowCount(10) self.tableData.setColumnWidth(0, 200) self.tableData.setHorizontalHeaderLabels(self.headerTabData) self.tableData.setBackgroundRole(QPalette.Light) self.tableData.setSizeAdjustPolicy( QAbstractScrollArea.AdjustToContents) self.tableData.verticalHeader().hide() self.scrollBrowser = QScrollArea() self.scrollBrowser.setBackgroundRole(QPalette.Dark) self.scrollBrowser.setWidget(self.tableData) self.scrollBrowser.setWidgetResizable(True) self.splitter0 = QSplitter(Qt.Vertical) self.splitter0.addWidget(self.scrollText) self.splitter0.addWidget(self.scrollTable) self.scrollArea = QScrollArea() self.scrollArea.setBackgroundRole(QPalette.Dark) self.scrollArea.setWidget(self.imageLabel) self.scrollArea.setWidgetResizable(False) self.scrollArea.setAlignment(Qt.AlignCenter) self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), 0.8) self.adjustScrollBar(self.scrollArea.verticalScrollBar(), 1.0) self.splitter1 = QSplitter(Qt.Horizontal) self.splitter1.addWidget(self.splitter0) self.splitter1.addWidget(self.scrollArea) self.splitter1.addWidget(self.layoutSlide) self.splitter3 = QSplitter(Qt.Horizontal) self.splitter3.addWidget(self.browser) self.splitter3.addWidget(self.scrollBrowser) self.splitter2 = QSplitter(Qt.Vertical) self.splitter2.addWidget(self.splitter1) self.splitter2.addWidget(self.splitter3) self.splitter2.setHandleWidth(15) #======================================================================= # self.splitter2. #======================================================================= self.verticalLayout.addWidget(self.menuToolBar) self.verticalLayout.addWidget(self.splitter2) self.setWindowTitle("MRImage Viewer (IRMaGe)") self.resize(800, 600) self.setAutoFillBackground(True) self.setPalette(pal) def changeSel(self): print('Tab changed') def adjustScrollBar(self, scrollBar, factor): scrollBar.setValue( int(factor * scrollBar.value() + ((factor - 1) * scrollBar.pageStep() / 2))) def imgqLabel(self): QLabel.__init__(self) image = QImage('sources_images/LogoIRMaGe.png') self.scaleFactor = 1.0 self.imageLabel = QLabel() self.imageLabel.setBackgroundRole(QPalette.Base) self.imageLabel.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.imageLabel.setScaledContents(True) self.imageLabel.setPixmap(QPixmap.fromImage(image)) self.scaleFactor *= self.factor self.imageLabel.adjustSize() self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size()) def open(self, filePath): self.img = nib.load(filePath) self.textInfoTop.setText('File : ' + filePath + '\n') self.textInfoTop.append('Dim : ' + str(self.img.shape) + '\n') self.enableSliders() self.a1.setValue(0) self.a2.setValue(0) self.a3.setValue(0) self.c2.setMaximum(self.img.shape[0]) self.c2.setMinimum(-self.img.shape[0]) self.c3.setMaximum(self.img.shape[1]) self.c3.setMinimum(-self.img.shape[1]) self.navigImage() self.fitToWindowAct.setEnabled(True) self.fitToWindow() def openJson(self, pathJson, fileName): with open(pathJson, 'r') as stream: try: json_object = json.load(stream) data = json.dumps(json_object, indent=0, sort_keys=True) data = json.loads(data) rowPosition = 0 self.tableJson.setRowCount(0) i = 0 for keyd in self.headerTabData: try: val = str(data[keyd]) val = val.replace('[', '') val = val.replace(']', '') except: val = '' #=========================================================== # self.tableData.insertRow(i) # self.tableData.setItem(0,i,QTableWidgetItem(val)) # i+=1 #=========================================================== #=============================================================== # self.tableData.setItem(0,0,QTableWidgetItem(fileName)) # self.tableData.selectRow(0) #=============================================================== for keys in data: stringValue = str(data[keys]) stringValue = stringValue.replace('[', '') stringValue = stringValue.replace(']', '') self.tableJson.insertRow(rowPosition) self.tableJson.setItem(rowPosition, 0, QTableWidgetItem(keys)) self.tableJson.setItem(rowPosition, 1, QTableWidgetItem(stringValue)) rowPosition += 1 self.tableJson.resizeColumnsToContents() except json.JSONDecodeError as exc: itemError = 'Error Json format' self.tableJson.setRowCount(0) self.tableJson.insertRow(0) self.tableJson.setItem(0, 0, QTableWidgetItem(itemError)) print(exc) def jsonParser(self, pathJson): with open(pathJson, 'r') as stream: try: json_object = json.load(stream) listTag = json.dumps(json_object, indent=0, sort_keys=True) listTag = json.loads(listTag) except json.JSONDecodeError as exc: itemError = 'Error Json format' return listTag def tableDataFill(self, pathRepertory): files = [f for f in fnmatch.filter(os.listdir(pathRepertory), '*.nii')] self.tableData.setRowCount(0) j = 0 for f in files: base = os.path.splitext(f)[0] g = os.path.join(pathRepertory, base + ".json") self.tableData.insertRow(j) if os.path.isfile(g): data = self.jsonParser(g) i = 0 for keyw in self.headerTabData: try: val = str(data[keyw]) val = val.replace('[', '') val = val.replace(']', '') except: val = '' self.tableData.setItem(j, i, QTableWidgetItem(val)) i += 1 else: self.tableData.setItem(j, 1, QTableWidgetItem('No json file found')) self.tableData.setItem(j, 0, QTableWidgetItem(f)) self.tableData.resizeColumnsToContents() j += 1 def indexImage(self): sl1 = self.a1.value() sl2 = self.a2.value() sl3 = self.a3.value() if len(self.img.shape) == 3: x = self.img.get_data()[:, :, sl1].copy() self.a1.setMaximum(self.img.shape[2] - 1) self.a2.setMaximum(0) self.a3.setMaximum(0) if len(self.img.shape) == 4: x = self.img.get_data()[:, :, sl1, sl2].copy() self.a1.setMaximum(self.img.shape[2] - 1) self.a2.setMaximum(self.img.shape[3] - 1) self.a3.setMaximum(0) if len(self.img.shape) == 5: x = self.img.get_data()[:, :, sl1, sl2, sl3].copy() self.a1.setMaximum(self.img.shape[2] - 1) self.a2.setMaximum(self.img.shape[3] - 1) self.a3.setMaximum(self.img.shape[4] - 1) x = rotate(x, -90, reshape=False) x = np.uint8((x - x.min()) / x.ptp() * 255.0) self.x = x ############################ Slice controls ######################################### def boxSliders(self): self.k1 = QLabel('Slider 1 ') self.k2 = QLabel('Slider 2') self.k3 = QLabel('Slider 3') self.a1 = self.createSlider(0, 0, 0) self.a2 = self.createSlider(0, 0, 0) self.a3 = self.createSlider(0, 0, 0) self.a1.valueChanged.connect(self.changePosValue) self.a2.valueChanged.connect(self.changePosValue) self.a3.valueChanged.connect(self.changePosValue) self.txta1 = self.createFieldValue() self.txta2 = self.createFieldValue() self.txta3 = self.createFieldValue() self.controlsGroup = QGroupBox('Slice Controls') gridCtrl = QGridLayout() gridCtrl.addWidget(self.k1, 0, 0) gridCtrl.addWidget(self.a1, 0, 1) gridCtrl.addWidget(self.txta1, 0, 2) gridCtrl.addWidget(self.k2, 1, 0) gridCtrl.addWidget(self.a2, 1, 1) gridCtrl.addWidget(self.txta2, 1, 2) gridCtrl.addWidget(self.k3, 2, 0) gridCtrl.addWidget(self.a3, 2, 1) gridCtrl.addWidget(self.txta3, 2, 2) self.controlsGroup.setLayout(gridCtrl) ############################ brightness and contrast ################################ self.txtb1 = self.createFieldValue() self.txtb2 = self.createFieldValue() self.txtb3 = self.createFieldValue() self.txtb4 = self.createFieldValue() self.l1 = QLabel('Brightness ') self.b1 = self.createSlider(101, 0, 50) self.l2 = QLabel('Contrast') self.b2 = self.createSlider(101, 0, 50) self.l3 = QLabel('Sharpness') self.b3 = self.createSlider(101, 0, 50) self.l4 = QLabel('Color') self.b4 = self.createSlider(101, 0, 50) self.b1.valueChanged.connect(self.changeContValue) self.b2.valueChanged.connect(self.changeContValue) self.b3.valueChanged.connect(self.changeContValue) self.b4.valueChanged.connect(self.changeContValue) self.txtb1.setText(str(0)) self.txtb2.setText(str(0)) self.txtb3.setText(str(0)) self.txtb4.setText(str(0)) self.buttonResetContrast = QPushButton('reset', self) self.buttonResetContrast.setToolTip('Reset all values') self.buttonResetContrast.setEnabled(False) self.buttonResetContrast.clicked.connect(self.resetValuesContrast) self.contrastGroup = QGroupBox('Brightness and Contrast') gridCont = QGridLayout() gridCont.addWidget(self.l1, 0, 0) gridCont.addWidget(self.b1, 0, 1) gridCont.addWidget(self.txtb1, 0, 2) gridCont.addWidget(self.l2, 1, 0) gridCont.addWidget(self.b2, 1, 1) gridCont.addWidget(self.txtb2, 1, 2) gridCont.addWidget(self.l3, 2, 0) gridCont.addWidget(self.b3, 2, 1) gridCont.addWidget(self.txtb3, 2, 2) gridCont.addWidget(self.l4, 3, 0) gridCont.addWidget(self.b4, 3, 1) gridCont.addWidget(self.txtb4, 3, 2) gridCont.addWidget(self.buttonResetContrast, 4, 2) self.contrastGroup.setLayout(gridCont) ############################ Transformation ######################################### self.txtc1 = self.createFieldValue() self.txtc2 = self.createFieldValue() self.txtc3 = self.createFieldValue() self.txtc4 = self.createFieldValue() self.m1 = QLabel('Rotation') self.c1 = self.createSlider(180, -180, 0) self.m2 = QLabel('Translate X ') self.c2 = self.createSlider(1, -1, 0) self.m3 = QLabel('Translate Y ') self.c3 = self.createSlider(1, -1, 0) self.m4 = QLabel('Resize') self.c4 = self.createSlider(10, 0, 0) self.c1.valueChanged.connect(self.changeTransValue) self.c2.valueChanged.connect(self.changeTransValue) self.c3.valueChanged.connect(self.changeTransValue) self.c4.valueChanged.connect(self.changeTransValue) self.txtc1.setText(str(0)) self.txtc2.setText(str(0)) self.txtc3.setText(str(0)) self.txtc4.setText(str(0)) self.buttonResetTransform = QPushButton('reset', self) self.buttonResetTransform.setToolTip('Reset all values') self.buttonResetTransform.setEnabled(False) self.buttonResetTransform.clicked.connect(self.resetValuesTransform) self.transformationGroup = QGroupBox('Transformations') gridTransf = QGridLayout() gridTransf.addWidget(self.m1, 0, 0) gridTransf.addWidget(self.c1, 0, 1) gridTransf.addWidget(self.txtc1, 0, 2) gridTransf.addWidget(self.m2, 1, 0) gridTransf.addWidget(self.c2, 1, 1) gridTransf.addWidget(self.txtc2, 1, 2) gridTransf.addWidget(self.m3, 2, 0) gridTransf.addWidget(self.c3, 2, 1) gridTransf.addWidget(self.txtc3, 2, 2) gridTransf.addWidget(self.m4, 3, 0) gridTransf.addWidget(self.c4, 3, 1) gridTransf.addWidget(self.txtc4, 3, 2) gridTransf.addWidget(self.buttonResetTransform, 4, 2) self.transformationGroup.setLayout(gridTransf) #################################################################################### self.layoutSliders = QVBoxLayout() self.layoutSliders.addWidget(self.controlsGroup) self.layoutSliders.addWidget(self.contrastGroup) self.layoutSliders.addWidget(self.transformationGroup) self.layoutSlide = QWidget() self.layoutSlide.setLayout(self.layoutSliders) def createSlider(self, maxm=0, minm=0, pos=0): slider = QSlider(Qt.Horizontal) slider.setFocusPolicy(Qt.StrongFocus) #slider.setTickPosition(QSlider.TicksBothSides) slider.setTickInterval(1) #slider.setSingleStep(1) slider.setMaximum(maxm) slider.setMinimum(minm) slider.setValue(pos) slider.setEnabled(False) return slider def createFieldValue(self): fieldValue = QLineEdit() fieldValue.setEnabled(False) fieldValue.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) return fieldValue def displayPosValue(self): self.txta1.setText( str(self.a1.value() + 1) + ' / ' + str(self.a1.maximum() + 1)) self.txta2.setText( str(self.a2.value() + 1) + ' / ' + str(self.a2.maximum() + 1)) self.txta3.setText( str(self.a3.value() + 1) + ' / ' + str(self.a3.maximum() + 1)) def changePosValue(self): self.navigImage() def navigImage(self): self.indexImage() self.displayPosValue() w, h = self.x.shape image = QImage(self.x.data, w, h, QImage.Format_Indexed8) self.pixm = QPixmap.fromImage(image) self.imageLabel.setPixmap(self.pixm) self.imageLabel.adjustSize() self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size()) self.filter() def changeContValue(self): self.txtb1.setText(str(self.b1.value() - 50)) self.txtb2.setText(str(self.b2.value() - 50)) self.txtb3.setText(str(self.b3.value() - 50)) self.txtb4.setText(str(self.b4.value() - 50)) self.filter() def changeTransValue(self): self.txtc1.setText(str(self.c1.value())) self.txtc2.setText(str(self.c2.value())) self.txtc3.setText(str(self.c3.value())) self.txtc4.setText(str(self.c4.value())) self.filter() def filter(self): img = Image.fromarray(self.x, 'L') brightness = ImageEnhance.Brightness(img) newImg = brightness.enhance(1.2 * (self.b1.value() + 1) / 50.0) contrast = ImageEnhance.Contrast(newImg) newImg = contrast.enhance((self.b2.value() + 1) / 50.0) sharpness = ImageEnhance.Sharpness(newImg) newImg = sharpness.enhance(2.0 * (self.b3.value() + 1) / 50.0) color = ImageEnhance.Color(newImg) newImg = color.enhance((self.b4.value() + 1) / 50.0) newImg = newImg.rotate(self.c1.value()) newImg = newImg.transform( img.size, Image.AFFINE, (1, 0, self.c2.value(), 0, 1, self.c3.value())) size1 = int(img.size[0] * (self.c4.value() + 1)) size2 = int(img.size[1] * (self.c4.value() + 1)) newImg = newImg.resize((size1, size2), Image.ANTIALIAS) self.pixm = QPixmap.fromImage(newImg.toqimage()) self.imageLabel.setPixmap(self.pixm) self.imageLabel.adjustSize() self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size()) def resetValuesContrast(self): self.b1.setSliderPosition(50) self.b2.setSliderPosition(50) self.b3.setSliderPosition(50) self.b4.setSliderPosition(50) self.changeContValue() def resetValuesTransform(self): self.c1.setSliderPosition(0) self.c2.setSliderPosition(0) self.c3.setSliderPosition(0) self.c4.setSliderPosition(0) self.changeTransValue() def enableSliders(self): self.a1.setEnabled(True) self.a2.setEnabled(True) self.a3.setEnabled(True) self.b1.setEnabled(True) self.b2.setEnabled(True) self.b3.setEnabled(True) self.b4.setEnabled(True) self.c1.setEnabled(True) self.c2.setEnabled(True) self.c3.setEnabled(True) self.c4.setEnabled(True) self.buttonResetContrast.setEnabled(True) self.buttonResetTransform.setEnabled(True) #################################################################################### def browserFile(self): global Browser, Model self.browser = QTreeView() model = QFileSystemModel() model.setNameFilters(['*.nii']) model.setNameFilterDisables(False) model.setReadOnly(True) self.browser.setModel(model) self.browser.expandAll() self.browser.setColumnWidth(0, 400) self.browser.selectionModel().selectionChanged.connect(self.select) Browser = self.browser Model = model #======================================================================= # self.browser.doubleClicked.connect(self.selection) #self.browser.clicked.connect(self.selection) #======================================================================= def select(self, signal): file_path = self.browser.model().filePath(signal.indexes()[0]) shortName, fileExt = os.path.splitext(file_path) filePath, fileName = os.path.split(file_path) self.textInfo.setText(filePath) blackColor = QColor(0, 0, 0) if os.path.isfile(file_path): if fileExt == ".nii": if self.currentRep != filePath: self.tableDataFill(filePath) self.currentRep = filePath self.open(file_path) self.tableData.selectRow( self.tableData.findItems(fileName, Qt.MatchExactly)[0].row()) if os.path.isfile(shortName + '.json'): greenColor = QColor(50, 150, 100) self.textInfoTop.setTextColor(greenColor) self.textInfoTop.append('Json file exists ' + '\n') self.openJson(shortName + '.json', fileName) else: redColor = QColor(255, 0, 0) self.textInfoTop.setTextColor(redColor) self.textInfoTop.append('Json file doesn\'t exist' + '\n') self.tableJson.setRowCount(0) else: self.tableData.setRowCount(0) self.currentRep = filePath self.textInfoTop.setTextColor(blackColor) self.scrollText.setWidgetResizable(True) #################################################################################### def createMenus(self): self.fileMenu = QMenu("&File", self) self.fileMenu.addAction(self.exitAct) self.viewMenu = QMenu("&View", self) self.viewMenu.addAction(self.zoomInAct) self.viewMenu.addAction(self.zoomOutAct) self.viewMenu.addAction(self.normalSizeAct) self.viewMenu.addSeparator() self.viewMenu.addAction(self.fitToWindowAct) self.viewMenu.addSeparator() self.helpMenu = QMenu("&Help", self) self.helpMenu.addAction(self.aboutAct) self.menuBar = QMenuBar() self.menuBar.addMenu(self.fileMenu) self.menuBar.addMenu(self.viewMenu) self.menuBar.addMenu(self.helpMenu) def createToolbarMenus(self): self.menuToolBar = QToolBar() viewMenu = QToolButton() viewMenu.setText('View') viewMenu.setPopupMode(QToolButton.MenuButtonPopup) aMenu = QMenu() aMenu.addAction(self.zoomInAct) aMenu.addAction(self.zoomOutAct) aMenu.addAction(self.normalSizeAct) aMenu.addSeparator() aMenu.addAction(self.fitToWindowAct) viewMenu.setMenu(aMenu) helpMenu = QToolButton() helpMenu.setText('Help') helpMenu.setPopupMode(QToolButton.MenuButtonPopup) bMenu = QMenu() helpMenu.setMenu(bMenu) self.menuToolBar.addWidget(viewMenu) self.menuToolBar.addWidget(helpMenu) def createActions(self): self.exitAct = QAction("Exit", self, shortcut="Ctrl+Q", triggered=self.close) self.zoomInAct = QAction("Zoom In (25%)", self, shortcut="Ctrl++", enabled=False, triggered=self.zoomIn) self.zoomOutAct = QAction("Zoom Out (25%)", self, shortcut="Ctrl+-", enabled=False, triggered=self.zoomOut) self.normalSizeAct = QAction("Normal Size", self, shortcut="Ctrl+S", enabled=False, triggered=self.normalSize) self.fitToWindowAct = QAction("Fit to Window", self, enabled=False, checkable=True, shortcut="Ctrl+F", triggered=self.fitToWindow) def zoomIn(self): self.factor = 1.25 self.scaleImage(self.factor) def zoomOut(self): self.factor = 0.8 self.scaleImage(self.factor) def normalSize(self): self.imageLabel.adjustSize() self.scaleFactor = 1.0 def fitToWindow(self): fitToWindow = self.fitToWindowAct.isChecked() self.scrollArea.setWidgetResizable(fitToWindow) self.scrollText.setWidgetResizable(fitToWindow) if not fitToWindow: self.normalSize() self.updateActions() def updateActions(self): self.zoomInAct.setEnabled(not self.fitToWindowAct.isChecked()) self.zoomOutAct.setEnabled(not self.fitToWindowAct.isChecked()) self.normalSizeAct.setEnabled(not self.fitToWindowAct.isChecked()) def scaleImage(self, factor): self.scaleFactor *= factor self.imageLabel.resize(self.scaleFactor * self.imageLabel.pixmap().size()) self.adjustScrollBar(self.scrollArea.horizontalScrollBar(), factor) self.adjustScrollBar(self.scrollArea.verticalScrollBar(), factor) self.zoomInAct.setEnabled(self.scaleFactor < 5.0) self.zoomOutAct.setEnabled(self.scaleFactor > 0.333) def close(self): self.close()
class Broswer_Img(QMainWindow): """ 选择图片文件并且预览,自动匹配多图的情况和背景图片 """ Close_Signal = pyqtSignal(list) def __init__(self, *args, **kwargs): QMainWindow.__init__(self, *args, **kwargs) self.Current_Dir = QDir.home().absolutePath() #self.Current_Dir = Wk_Dir self.setWindowTitle("Select Imags") self.setWindowModality(Qt.ApplicationModal) self.Left_Dock_Code() self.Central_Frame_Code() self.Right_Dock_Code() self.connect_Signals() self.wb_nav_left.setEnabled(False) self.wb_nav_right.setEnabled(False) self.bkgd_nav_left.setEnabled(False) self.bkgd_nav_right.setEnabled(False) #self.setGeometry(200, 200, 1000, 600) #self.setMaximumSize(QSize(1000, 600)) def Left_Dock_Code(self): self.Left_Frame = QFrame(self) self.Model = QFileSystemModel() self.Model.setNameFilterDisables(False) self.Model.setRootPath(self.Current_Dir) #self.Model.setSorting(QDir.DirsFirst | QDir.IgnoreCase | QDir.Name) self.Model.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs | QDir.AllEntries) self.Model.setNameFilters(['*.tif']) self.Tree = QTreeView(self.Left_Frame) self.Tree.setModel(self.Model) self.Tree.setRootIndex(self.Model.index(self.Current_Dir)) self.Tree.expandAll() self.Dir_Select = QPushButton("Select a Folder", self.Left_Frame) layout = QVBoxLayout() layout.addWidget(self.Tree) layout.addWidget(self.Dir_Select) self.Left_Frame.setLayout(layout) self.Left_Dock = QDockWidget('Broswer Images', self) self.Left_Dock.setWidget(self.Left_Frame) self.Left_Dock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.Dir_Select.clicked.connect(self.dir_selection) self.addDockWidget(Qt.LeftDockWidgetArea, self.Left_Dock) def Central_Frame_Code(self): self.Central_Frame = QFrame(self) layout = QGridLayout() self.wb_label = QLabel(self.Central_Frame) self.bkgd_label = QLabel(self.Central_Frame) #self.wb_label.setMaximumHeight(30) self.wb_label.setWordWrap(True) self.wb = QLabel(self.Central_Frame) self.wb.setScaledContents(True) self.bkgd = QLabel(self.Central_Frame) self.bkgd.setScaledContents(True) self.wb.setMaximumSize(QSize(300, 300)) self.bkgd.setMaximumSize(QSize(300, 300)) self.wb_navigator = QFrame(self.Central_Frame) self.wb_nav_left = QPushButton('<--', self.wb_navigator) self.wb_nav_right = QPushButton('-->', self.wb_navigator) nav_layout = QHBoxLayout() nav_layout.addWidget(self.wb_nav_left) nav_layout.addWidget(self.wb_nav_right) self.wb_navigator.setLayout(nav_layout) self.wb_navigator.setMaximumHeight(60) self.bkgd_navigator = QFrame(self.Central_Frame) self.bkgd_nav_left = QPushButton('<--', self.bkgd_navigator) self.bkgd_nav_right = QPushButton('-->', self.bkgd_navigator) nav_layout2 = QHBoxLayout() nav_layout2.addWidget(self.bkgd_nav_left) nav_layout2.addWidget(self.bkgd_nav_right) self.bkgd_navigator.setLayout(nav_layout2) self.bkgd_navigator.setMaximumHeight(60) self.btns = QFrame(self.Central_Frame) self.btns.setMaximumHeight(60) self.Btn_Add = QPushButton('Add', self.btns) self.Btn_Close = QPushButton('Close', self.btns) btn_layout = QHBoxLayout() btn_layout.addWidget(self.Btn_Add) btn_layout.addWidget(self.Btn_Close) self.btns.setLayout(btn_layout) # 根据具体的传入参数构建不同的视图 layout.addWidget(self.wb_label, 0, 0) layout.addWidget(self.bkgd_label, 0, 1) layout.addWidget(self.wb, 1, 0) layout.addWidget(self.bkgd, 1, 1) layout.addWidget(self.wb_navigator, 2, 0) layout.addWidget(self.bkgd_navigator, 2, 1) layout.addWidget(self.btns, 3, 0, 2, 0) layouts = QVBoxLayout() layouts.addLayout(layout) self.Central_Frame.setLayout(layouts) self.setCentralWidget(self.Central_Frame) #self.setStyleSheet('border:1px solid red') def Right_Dock_Code(self): self.Added_Img_tree = Img_Tree([], self) self.Right_Dock = QDockWidget('Selected Images', self) self.Right_Dock.setWidget(self.Added_Img_tree) self.Right_Dock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.addDockWidget(Qt.RightDockWidgetArea, self.Right_Dock) def connect_Signals(self): self.Tree.clicked.connect(self.Load_Img_to_Central_Frame) self.wb_nav_left.clicked.connect(self.change_img_index) self.wb_nav_right.clicked.connect(self.change_img_index) self.bkgd_nav_left.clicked.connect(self.change_img_index) self.bkgd_nav_right.clicked.connect(self.change_img_index) self.Btn_Add.clicked.connect(self.Add_Btn_Action) self.Btn_Close.clicked.connect(self.Close_Btn_Action) def Load_Img_to_Central_Frame(self, Index): select_file = self.Tree.model().filePath(Index) _, ext = os.path.splitext(select_file) if ext in ['.tif', '.jpeg', '.png', '.jpg']: self.Related_Imgs = BioRad_Imgs(select_file) self.set_Central_Frame() def set_Central_Frame(self): self.wb_nav_left.setEnabled(False) self.wb_nav_right.setEnabled(False) self.bkgd_nav_left.setEnabled(False) self.bkgd_nav_right.setEnabled(False) self.current_wb = self.Related_Imgs.WB_list[self.Related_Imgs.wb_index] self.current_bkgd = self.Related_Imgs.BKGD_list[ self.Related_Imgs.bkgd_index] self.wb_label.setText( self.current_wb.replace(self.Related_Imgs.Dir, '.')) self.bkgd_label.setText( self.current_bkgd.replace(self.Related_Imgs.Dir, '.')) wb, _ = CV_Img_to_QImage(cv2.imread(self.current_wb)) bkgd, _ = CV_Img_to_QImage(cv2.imread(self.current_bkgd)) self.wb.setPixmap(wb) self.wb.setScaledContents(True) self.bkgd.setPixmap(bkgd) wb_len = len(self.Related_Imgs.WB_list) bkgd_len = len(self.Related_Imgs.BKGD_list) if self.Related_Imgs.wb_index > 0: self.wb_nav_left.setEnabled(True) if wb_len - self.Related_Imgs.wb_index > 1: self.wb_nav_right.setEnabled(True) if self.Related_Imgs.bkgd_index > 0: self.bkgd_nav_left.setEnabled(True) if bkgd_len - self.Related_Imgs.bkgd_index > 1: self.bkgd_nav_right.setEnabled(True) def change_img_index(self): sender = self.sender() if sender == self.wb_nav_left: self.Related_Imgs.wb_index = self.Related_Imgs.wb_index - 1 if sender == self.wb_nav_right: self.Related_Imgs.wb_index = self.Related_Imgs.wb_index + 1 if sender == self.bkgd_nav_left: self.Related_Imgs.bkgd_index = self.Related_Imgs.bkgd_index - 1 if sender == self.bkgd_nav_right: self.Related_Imgs.bkgd_index = self.Related_Imgs.bkgd_index + 1 self.set_Central_Frame() def Add_Btn_Action(self): list = [{'wb': self.current_wb, 'bkgd': self.current_bkgd}] self.Added_Img_tree.Add_top_Level_Item(list) print(self.Added_Img_tree.imgs) def Close_Btn_Action(self): self.close() def closeEvent(self, event): self.Close_Signal.emit(self.Added_Img_tree.imgs) def dir_selection(self): global Wk_Dir dir = QFileDialog.getExistingDirectory(self, "Choose a Directory", Wk_Dir) self.Current_Dir = dir Wk_Dir = dir self.Tree.setRootIndex(self.Model.index(self.Current_Dir)) self.Left_Dock.setWindowTitle(dir)
class ProjectStatusDialog(QDialog): icons = { "added": "images/FA_icons/plus.svg", "removed": "images/FA_icons/trash.svg", "updated": "images/FA_icons/edit.svg", "renamed": "images/FA_icons/edit.svg", "table": "images/FA_icons/table.svg", } def __init__( self, pull_changes, push_changes, push_changes_summary, has_write_permissions, validation_results, mergin_project=None, parent=None ): super(ProjectStatusDialog, self).__init__(parent) self.validation_results = validation_results self.setWindowTitle("Project status") self.table = QTreeView() self.table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels(["Status"]) self.table.setModel(self.model) self.mp = mergin_project self.check_any_changes(pull_changes, push_changes) self.add_content(pull_changes, "Server changes", True) self.add_content(push_changes, "Local changes", False, push_changes_summary) self.table.expandAll() main_lout = QVBoxLayout(self) self.tabs = QTabWidget() main_lout.addWidget(self.tabs) self.status_tab = QWidget() self.valid_tab = QWidget() self.tabs.addTab(self.status_tab, "Status") self.tabs.addTab(self.valid_tab, "Validation results") status_lay = QVBoxLayout(self.status_tab) status_lay.addWidget(self.table) has_files_to_replace = any( ["diff" not in file and is_versioned_file(file["path"]) for file in push_changes["updated"]] ) info_text = self._get_info_text(has_files_to_replace, has_write_permissions) if info_text: text_box = QLabel() text_box.setWordWrap(True) text_box.setText(info_text) status_lay.addWidget(text_box) box = QDialogButtonBox(QDialogButtonBox.Ok, centerButtons=True,) box.accepted.connect(self.accept) box.rejected.connect(self.reject) main_lout.addWidget(box, Qt.AlignCenter) self.valid_view = QTreeView() self.valid_view.setStyleSheet("QTreeView::item { padding: 5px }") self.valid_model = QStandardItemModel() self.show_validation_results() self.resize(640, 640) def _get_info_text(self, has_files_to_replace, has_write_permissions): msg = "" if not has_write_permissions: msg += f"WARNING: You don't have writing permissions to this project. Changes won't be synced!\n" if has_files_to_replace: msg += ( f"\nWARNING: Unable to compare some of the modified files with their server version - " f"their history will be lost if uploaded." ) return msg def check_any_changes(self, pull_changes, push_changes): if not sum(len(v) for v in list(pull_changes.values()) + list(push_changes.values())): root_item = QStandardItem("No changes") self.model.appendRow(root_item) def add_content(self, changes, root_text, is_server, changes_summary={}): """ Adds rows with changes info :param changes: Dict of added/removed/updated/renamed changes :param root_text: Text for the root item :param is_server: True if changes are related to server file changes :param changes_summary: If given and non empty, extra rows are added from geodiff summary. :return: """ if all(not changes[k] for k in changes): return root_item = QStandardItem(root_text) self.model.appendRow(root_item) for category in changes: for file in changes[category]: path = file["path"] item = self._get_icon_item(category, path) if is_versioned_file(path): if path in changes_summary: for sub_item in self._versioned_file_summary_items(changes_summary[path]["geodiff_summary"]): item.appendRow(sub_item) elif not is_server and category != "added": item.appendRow(QStandardItem("Unable to detect changes")) msg = f"Mergin plugin: Unable to detect changes for {path}" QgsApplication.messageLog().logMessage(msg) if self.mp is not None: self.mp.log.warning(msg) root_item.appendRow(item) def _versioned_file_summary_items(self, geodiff_summary): items = [] for s in geodiff_summary: table_name_item = self._get_icon_item("table", s["table"]) for row in self._table_summary_items(s): table_name_item.appendRow(row) items.append(table_name_item) return items def _table_summary_items(self, summary): return [QStandardItem("{}: {}".format(k, summary[k])) for k in summary if k != "table"] def _get_icon_item(self, key, text): path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.icons[key]) item = QStandardItem(text) item.setIcon(QIcon(path)) return item def show_validation_results(self): lout = QVBoxLayout(self.valid_tab) lout.addWidget(self.valid_view) self.valid_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.valid_model.setHorizontalHeaderLabels(["Validation results"]) self.valid_view.setModel(self.valid_model) map_layers = QgsProject.instance().mapLayers() for issues_data in sorted(self.validation_results): level, issue = issues_data layer_ids = self.validation_results[issues_data] issue_item = QStandardItem(issue) for lid in sorted(layer_ids, key=lambda x: map_layers[x].name()): layer = map_layers[lid] lyr_item = QStandardItem(f"- {layer.name()}") lyr_item.setToolTip(layer.publicSource()) issue_item.appendRow(lyr_item) self.valid_model.appendRow(issue_item) self.valid_view.expandAll()