def test_replace_tape_widget_should_destroy_old_tape_and_put_a_new_one_on_the_panel(self): old_tape_widget = self.window.tape_widget assert isinstance(old_tape_widget, TapeWidget) assert self.window.centralWidget() == old_tape_widget old_tape_widget.add_note() old_tape_widget.add_note() old_note1 = item_to_note(old_tape_widget.model().item(0)) old_note2 = item_to_note(old_tape_widget.model().item(1)) new_note = Note( body = "Y", tags = ["Z"], created_at = datetime.utcnow() ) new_model = QStandardItemModel() new_item = QStandardItem() set_item_note(new_item, new_note) new_model.appendRow(new_item) self.window._replace_tape_widget(new_model) self.assertTrue(isinstance(self.window.tape_widget, TapeWidget)) self.assertNotEqual(self.window.tape_widget, old_tape_widget) self.assertEqual(len(list(self.window.tape_widget.notes())), 1) self.assertEqual(self.window.tape_widget.model(), new_model) self.assertEqual(item_to_note(self.window.tape_widget.model().item(0)), new_note) self.assertTrue(self.window.centralWidget() == self.window.tape_widget)
def on_add_group(self): """ Create a popup dialog that has a single list view of all the groups that are not yet added. """ # figure out who is added. groups_added = set() model = self.groupsListView.model() for i in range(model.rowCount()): groups_added.add(model.data(model.index(i, 0), Qt.UserRole)) # Add rows for each unadded group model = QStandardItemModel() for g in Groups: if g in groups_added: continue model.appendRow(GroupItem(g)) dialog = GetItemDialog( title="Choose a Group to Add", labelText="Groups", createText="Create a new Group", new_dialog_cls=CreateGroupWidget, item_cls=GroupItem, model=model ) if dialog.exec() == QDialog.Accepted: model = self.groupsListView.model() model.appendRow(GroupItem(dialog.selection))
class FileInspector(QTreeView): """docstring for FileInspector""" def __init__(self, parent=None): super(FileInspector, self).__init__(parent) self.model = QStandardItemModel(self) self.setModel(self.model) self.file_info = None header = QStandardItem('') self.model.setHorizontalHeaderItem(0, header) def populate(self, file_info=None, db_file=None): if file_info is not None: self.file_info = file_info db_file = FileInspectorHelper.get_or_insert_file(file_info) self.model.clear() if db_file: for classe in db_file.classes: parent = QStandardItem("{0}:".format(classe.name)) for method in classe.methods: parent.appendRow(QStandardItem("{0}()".\ format(method.name))) self.model.appendRow(parent) for function in db_file.functions: self.model.appendRow(QStandardItem("{0}()".\ format(function.name))) name = db_file.name if db_file else file_info.fileName() header = QStandardItem(name) self.model.setHorizontalHeaderItem(0, header) self.expandAll() @pyqtSlot(QFileInfo) def onFileItemActivated(self, file_info): self.populate(file_info)
class RefsUI(object): def __init__(self, window, uaclient): self.window = window self.uaclient = uaclient self.model = QStandardItemModel() self.window.ui.refView.setModel(self.model) self.window.ui.refView.horizontalHeader().setSectionResizeMode(1) self.window.ui.treeView.activated.connect(self.show_refs) self.window.ui.treeView.clicked.connect(self.show_refs) def clear(self): self.model.clear() def show_refs(self, idx): node = self.window.get_current_node(idx) self.model.clear() if node: self._show_refs(node) def _show_refs(self, node): self.model.setHorizontalHeaderLabels(['ReferenceType', 'NodeId', "BrowseName", "TypeDefinition"]) try: refs = self.uaclient.get_all_refs(node) except Exception as ex: self.window.show_error(ex) raise for ref in refs: self.model.appendRow([QStandardItem(str(ref.ReferenceTypeId)), QStandardItem(str(ref.NodeId)), QStandardItem(str(ref.BrowseName)), QStandardItem(str(ref.TypeDefinition))])
class ConfigTableViewWidget(QDialog, Ui_ConfigTableViewWidget): """ Class documentation goes here. """ def __init__(self, availableScalars, scalarsToShow, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super().__init__(parent) self.setupUi(self) self.ValueListModel = QStandardItemModel() for value in availableScalars: item = QStandardItem(value) check = Qt.Checked if value in scalarsToShow else Qt.Unchecked item.setCheckState(check) item.setCheckable(True) self.ValueListModel.appendRow(item) self.ValueListView.setModel(self.ValueListModel) def setVisibleValues(self): setVisible = [] for row in range(self.ValueListModel.rowCount()): item = self.ValueListModel.item(row) if item.checkState() == Qt.Checked: setVisible.append(item.text()) return setVisible
class FileListView(QListView): def __init__(self): super().__init__() self.setEditTriggers(QAbstractItemView.NoEditTriggers) """画像ファイル抽出&リスト追加処理""" def addFiles(self, files): itemCount = 0 imageFiles = [] self.model = QStandardItemModel() for file in files: if self.__isImage(file): imageFiles.append(file) item = QStandardItem(file) if itemCount % 2 == 0: item.setBackground(QBrush(QColor(221, 222, 211))) self.model.appendRow(QStandardItem(item)) itemCount += 1 if itemCount == 0: QMessageBox.information(None, "No Image Files!", "指定ディレクトリには画像がありません。") self.setModel(self.model) return imageFiles """画像ファイル選別処理(拡張子より選別)""" def __isImage(self, file): ext = os.path.splitext(file)[1].lower() return ext in [".jpg", ".jpeg", ".png", ".gif"]
def _get_config_model(): data = DataStorage() configurations_order = data.get_configurations_order() model = QStandardItemModel() for name in configurations_order: item = create_new_list_item(name) model.appendRow(item) return model
def set_data(self): group = self.obj self.setWindowTitle("{} (Group)".format(group.name)) self.nameValueLabel.setText(group.name) model = QStandardItemModel() self.charactersListView.setModel(model) for c in group.characters: item = CharacterItem(c) model.appendRow(item)
def fillUserList(newUser=None): model = QStandardItemModel() mylist = ['asd', 'wqe', 'wqra', 'tewt4'] if newUser: mylist += newUser print(mylist) for item in mylist: myitem = QStandardItem(item) myitem.setEditable(0) model.appendRow(myitem) myapp.listView.setModel(model) myapp.listView.doubleClicked.connect(openConversationWindow)
def set_table_data(self, data): table_model = QStandardItemModel() table_model.setHorizontalHeaderLabels(['Keyword', 'Count']) for row in data: qrow = [] for item in row: qitem = QStandardItem(str(item)) qrow.append(qitem) table_model.appendRow(qrow) self.table.setModel(table_model) self.table.resizeColumnsToContents() self.table.resizeRowsToContents()
class SettingWindow(QWidget): # on_addButtonClicked=pyqtSignal() # on_removeButtonClicked=pyqtSignal() # on_okButtonClicked=pyqtSignal() finished=pyqtSignal() def __init__(self): QWidget.__init__(self) self.listview=QListView(self) self.addButton=QPushButton(self) self.removeButton=QPushButton(self) self.resize(630,440) self.okButton=QPushButton(self) self.listview.setGeometry(30,30,410,351) self.addButton.setGeometry(490,40,80,22) self.addButton.setText("add") self.addButton.clicked.connect(self.click_add) self.removeButton.setGeometry(490,80,80,22) self.removeButton.setText("remove") self.removeButton.clicked.connect(self.click_remove) self.okButton.setGeometry(490,150,80,22) self.okButton.setText("ok") self.okButton.clicked.connect(self.click_ok) # self.aw=null self.fresh() def click_ok(self): self.finished.emit() self.close() def click_add(self): self.aw=AddWindow() self.aw.show() self.aw.okSig.connect(self.fresh) def click_remove(self): self.remove() def fresh(self): confFile=open("conf","r") self.listModel=QStandardItemModel() self.itemList=cPickle.load(confFile) confFile.close() for item in self.itemList: itemView=QStandardItem(QIcon(item.path),item.name) itemView.setEditable(False) self.listModel.appendRow(itemView) self.listview.setModel(self.listModel) def remove(self): index=self.listview.currentIndex().row() self.itemList.pop(index) self.listModel.removeRow(index) confFile=open("conf","w") cPickle.dump(self.itemList,confFile) confFile.close()
def test_set_model_replace_the_model_with_a_new_one(self): new_model = QStandardItemModel() item = QStandardItem() set_item_note(item, self.notes[0]) new_model.appendRow(item) assert self.tape_widget.model().rowCount() == 0 self.tape_widget.set_model(new_model) self.assertEqual(self.tape_widget.model(), new_model) self.assertEqual(self.tape_widget.proxy_model().sourceModel(), new_model) self.assertEqual(self.tape_widget.model().rowCount(), 1) self.assertEqual(self.tape_widget.model().item(0, 0), item)
class ListCategory(QSortFilterProxyModel): """Expose a list of items as a category for the CompletionModel.""" def __init__(self, name, items, delete_func=None, parent=None): super().__init__(parent) self.name = name self.srcmodel = QStandardItemModel(parent=self) self._pattern = '' # ListCategory filters all columns self.columns_to_filter = [0, 1, 2] self.setFilterKeyColumn(-1) for item in items: self.srcmodel.appendRow([QStandardItem(x) for x in item]) self.setSourceModel(self.srcmodel) self.delete_func = delete_func def set_pattern(self, val): """Setter for pattern. Args: val: The value to set. """ self._pattern = val val = re.sub(r' +', r' ', val) # See #1919 val = re.escape(val) val = val.replace(r'\ ', '.*') rx = QRegExp(val, Qt.CaseInsensitive) self.setFilterRegExp(rx) self.invalidate() sortcol = 0 self.sort(sortcol) def lessThan(self, _lindex, rindex): """Custom sorting implementation. Prefers all items which start with self._pattern. Other than that, keep items in their original order. Args: _lindex: The QModelIndex of the left item (*left* < right) rindex: The QModelIndex of the right item (left < *right*) Return: True if left < right, else False """ qtutils.ensure_valid(rindex) right = self.srcmodel.data(rindex) return not right.startswith(self._pattern)
class EventUI(object): def __init__(self, window, uaclient): self.window = window self.uaclient = uaclient self._handler = EventHandler() self._subscribed_nodes = [] # FIXME: not really needed self.model = QStandardItemModel() self.window.ui.evView.setModel(self.model) self.window.ui.actionSubscribeEvent.triggered.connect(self._subscribe) self.window.ui.actionUnsubscribeEvents.triggered.connect(self._unsubscribe) # context menu self.window.ui.treeView.addAction(self.window.ui.actionSubscribeEvent) self.window.ui.treeView.addAction(self.window.ui.actionUnsubscribeEvents) self._handler.event_fired.connect(self._update_event_model, type=Qt.QueuedConnection) def clear(self): self._subscribed_nodes = [] self.model.clear() def _subscribe(self): node = self.window.get_current_node() if node is None: return if node in self._subscribed_nodes: print("allready subscribed to event for node: ", node) return self.window.ui.evDockWidget.raise_() try: self.uaclient.subscribe_events(node, self._handler) except Exception as ex: self.window.show_error(ex) raise else: self._subscribed_nodes.append(node) def _unsubscribe(self): node = self.window.get_current_node() if node is None: return self._subscribed_nodes.remove(node) self.uaclient.unsubscribe_events(node) def _update_event_model(self, event): self.model.appendRow([QStandardItem(str(event))])
class TagChooser(QListView): changed = Signal() def __init__(self, db): super(TagChooser,self).__init__() self.db = db self.filter = u'' self.data = QStandardItemModel() self.proxy = QSortFilterProxyModel() self.proxy.setSourceModel(self.data) self.setModel(self.proxy) self.data.itemChanged.connect(self.changed) for t in sorted(self.db.list_tags()): item = QStandardItem(t) item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setCheckState(Qt.Unchecked) self.data.appendRow(item) def setTags(self, tags): for i in range(self.data.rowCount()): item = self.data.item(i) if item.text() in tags: item.setCheckState(Qt.Checked) else: item.setCheckState(Qt.Unchecked) def selectedTags(self): tags = [] for i in range(self.data.rowCount()): item = self.data.item(i) if item.checkState() == Qt.Checked: tags.append(item.text()) return tags def matchingFiles(self): tags = self.selectedTags() if not tags: return [] res = list(self.db.find_files_by_tags(tags)) res.sort() return res
class viewTable(QDialog): def __init__(self, parent=None): fileName = filedialog.askopenfilename() if fileName != '': k = fileName.rfind("/") self.parent = parent self.parent.statusbar.showMessage("Opening dataset...") QWidget.__init__(self,parent) self.setWindowFlags(Qt.Window) self.setObjectName("dialog1") self.setGeometry(100,100,400,400) self.setWindowTitle(fileName[k+1:]) self.setWindowIcon(QIcon('NSlogo.png')) self.model = QStandardItemModel(self) with open(fileName, "r") as fileInput: for row in csv.reader(fileInput, delimiter='\t'): items = [ QStandardItem(field) for field in row ] self.model.appendRow(items) self.tableView = QTableView(self) self.tableView.setModel(self.model) self.layoutVertical = QVBoxLayout(self) self.layoutVertical.addWidget(self.tableView) self.parent = parent self.showMaximized() self.show() self.parent.ui.logOutput.append("VIEWED:") print("VIEWED:") self.parent.ui.logOutput.append(" " + str(fileName.split('/')[-1])) print(" " + str(fileName.split('/')[-1])) def closeEvent(self, event): self.model.clear() self.parent.ui.logOutput.append("") self.parent.statusbar.showMessage("Welcome back!")
class RefsUI(object): def __init__(self, window, uaclient): self.window = window self.uaclient = uaclient self.model = QStandardItemModel() self.window.ui.refView.setModel(self.model) self.window.ui.refView.horizontalHeader().setSectionResizeMode(1) self.window.ui.treeView.activated.connect(self.show_refs) self.window.ui.treeView.clicked.connect(self.show_refs) def clear(self): self.model.clear() def show_refs(self, idx): node = self.window.get_current_node(idx) self.model.clear() if node: self._show_refs(node) def _show_refs(self, node): self.model.setHorizontalHeaderLabels(['ReferenceType', 'NodeId', "BrowseName", "TypeDefinition"]) try: refs = self.uaclient.get_all_refs(node) except Exception as ex: self.window.show_error(ex) raise for ref in refs: typename = ua.ObjectIdNames[ref.ReferenceTypeId.Identifier] if ref.NodeId.NamespaceIndex == 0 and ref.NodeId.Identifier in ua.ObjectIdNames: nodeid = ua.ObjectIdNames[ref.NodeId.Identifier] else: nodeid = ref.NodeId.to_string() if ref.TypeDefinition.Identifier in ua.ObjectIdNames: typedef = ua.ObjectIdNames[ref.TypeDefinition.Identifier] else: typedef = ref.TypeDefinition.to_string() self.model.appendRow([QStandardItem(typename), QStandardItem(nodeid), QStandardItem(ref.BrowseName.to_string()), QStandardItem(typedef) ])
def initUI(self): Model=QStandardItemModel() Model.setColumnCount(3) Model.setHorizontalHeaderLabels(["clmn1","clmn2","clmn3"]) Model.appendRow([QStandardItem(11),QStandardItem(12),QStandardItem(13)]) Model.appendRow([QStandardItem(21),QStandardItem(22),QStandardItem(23)]) Table=QTableView(self) Table.setModel(Model) ButDeleg=ButtonDelegate(Table) Table.setItemDelegateForColumn(1,ButDeleg) TXTDelegate=PlainTextEditDelegate() Table.setItemDelegateForColumn(0,TXTDelegate) ButDeleg.clicked.connect(self.Prnt) mainLayout = QVBoxLayout() mainLayout.setContentsMargins(0,0,0,0) mainLayout.addWidget(Table) self.setLayout(mainLayout)
class RefsWidget(QObject): error = pyqtSignal(str) def __init__(self, view): self.view = view QObject.__init__(self, view) self.model = QStandardItemModel() self.view.setModel(self.model) self.view.horizontalHeader().setSectionResizeMode(1) def clear(self): self.model.clear() def show_refs(self, node): self.model.clear() self._show_refs(node) def _show_refs(self, node): self.model.setHorizontalHeaderLabels(['ReferenceType', 'NodeId', "BrowseName", "TypeDefinition"]) try: refs = node.get_children_descriptions(refs=ua.ObjectIds.References) except Exception as ex: self.error.emit(ex) raise for ref in refs: typename = ua.ObjectIdNames[ref.ReferenceTypeId.Identifier] if ref.NodeId.NamespaceIndex == 0 and ref.NodeId.Identifier in ua.ObjectIdNames: nodeid = ua.ObjectIdNames[ref.NodeId.Identifier] else: nodeid = ref.NodeId.to_string() if ref.TypeDefinition.Identifier in ua.ObjectIdNames: typedef = ua.ObjectIdNames[ref.TypeDefinition.Identifier] else: typedef = ref.TypeDefinition.to_string() self.model.appendRow([QStandardItem(typename), QStandardItem(nodeid), QStandardItem(ref.BrowseName.to_string()), QStandardItem(typedef) ])
def analyzePhrase(self): root = self.mw.mdlOutline.rootItem nMin = self.spnPhraseMin.value() nMax = self.spnPhraseMax.value() def listPhrases(item, nMin, nMax): txt = item.text() # Split into words lst = re.findall(r"[\w']+", txt) # Ignores punctuation # lst = re.findall(r"[\w']+|[.,!?;]", txt) # Includes punctuation phrases = [] # Make tuples of n-length for n in range(nMin, nMax + 1): for l in range(len(lst) - n + 1): phrases.append(tuple(lst[l:l+n])) for c in item.children(): phrases += listPhrases(c, nMin, nMax) return phrases lst = listPhrases(root, nMin, nMax) # Count count = Counter(lst) # Showing mdl = QStandardItemModel() mdl.setHorizontalHeaderLabels([self.tr("Phrases"), self.tr("Frequency")]) for i in count.most_common(): word = QStandardItem(" ".join(i[0])) number = QStandardItem() number.setData(i[1], Qt.DisplayRole) if i[1] > 1: mdl.appendRow([word, number]) self.tblPhrase.setModel(mdl)
def reset(self): # Either way, we are going to replace the model. model = QStandardItemModel() self.groupsListView.setModel(model) if self.obj: # Reset to the original area area = self.obj # Set the window title self.setWindowTitle("Edit {} (Area)".format(area.name)) # Set the name line edit self.nameLineEdit.setText(area.name) for g in area.groups: model.appendRow(GroupItem(g)) else: # Set the defaults self.setWindowTitle("Create Area") self.nameLineEdit.setText("")
def analyzeWord(self): root = self.mw.mdlOutline.rootItem exclude = self.txtWordExclude.toPlainText().split(",") exclude = [e.strip().lower() for e in exclude] def listWords(item): txt = item.text() lst = re.findall(r"[\w']+", txt) for c in item.children(): lst += listWords(c) return lst lst = listWords(root) lst2 = [] # Cleaning for i in lst: if len(i) >= self.spnWordMin.value() and not i.lower() in exclude: lst2.append(i.lower()) # Count count = Counter(lst2) # Showing mdl = QStandardItemModel() mdl.setHorizontalHeaderLabels([self.tr("Word"), self.tr("Frequency")]) for i in count.most_common(): word = QStandardItem(i[0]) number = QStandardItem() number.setData(i[1], Qt.DisplayRole) mdl.appendRow([word, number]) self.tblWord.setModel(mdl)
class LinklistWidget(QListView): resultSelected = pyqtSignal(str) def __init__(self, parent): QListView.__init__(self, parent) self.resultListModel = QStandardItemModel(self) self.setModel(self.resultListModel) self.selectionModel().selectionChanged.connect(self.doItemSelected) def doItemSelected(self, selectedtItem, idx2): indexes = selectedtItem.indexes() if len(indexes) == 1: item = self.resultListModel.itemFromIndex(indexes[0]) pageId = item.text() self.resultSelected.emit(pageId) def setContents(self, linkList): self.resultListModel.clear() for item in linkList: resultItem = QStandardItem(item) resultItem.setEditable(False) self.resultListModel.appendRow(resultItem)
class DataChangeUI(object): def __init__(self, window, uaclient): self.window = window self.uaclient = uaclient self._subhandler = DataChangeHandler() self._subscribed_nodes = [] self.model = QStandardItemModel() self.window.ui.subView.setModel(self.model) self.window.ui.subView.horizontalHeader().setSectionResizeMode(1) self.window.ui.actionSubscribeDataChange.triggered.connect( self._subscribe) self.window.ui.actionUnsubscribeDataChange.triggered.connect( self._unsubscribe) # populate contextual menu self.window.addAction(self.window.ui.actionSubscribeDataChange) self.window.addAction(self.window.ui.actionUnsubscribeDataChange) # handle subscriptions self._subhandler.data_change_fired.connect( self._update_subscription_model, type=Qt.QueuedConnection) # accept drops self.model.canDropMimeData = self.canDropMimeData self.model.dropMimeData = self.dropMimeData def canDropMimeData(self, mdata, action, row, column, parent): return True def dropMimeData(self, mdata, action, row, column, parent): node = self.uaclient.client.get_node(mdata.text()) self._subscribe(node) return True def clear(self): self._subscribed_nodes = [] self.model.clear() def show_error(self, *args): self.window.show_error(*args) @trycatchslot def _subscribe(self, node=None): if not isinstance(node, SyncNode): node = self.window.get_current_node() if node is None: return if node in self._subscribed_nodes: logger.warning("allready subscribed to node: %s ", node) return self.model.setHorizontalHeaderLabels( ["DisplayName", "Value", "Timestamp"]) text = str(node.read_display_name().Text) row = [ QStandardItem(text), QStandardItem("No Data yet"), QStandardItem("") ] row[0].setData(node) self.model.appendRow(row) self._subscribed_nodes.append(node) self.window.ui.subDockWidget.raise_() try: self.uaclient.subscribe_datachange(node, self._subhandler) except Exception as ex: self.window.show_error(ex) idx = self.model.indexFromItem(row[0]) self.model.takeRow(idx.row()) raise @trycatchslot def _unsubscribe(self): node = self.window.get_current_node() if node is None: return self.uaclient.unsubscribe_datachange(node) self._subscribed_nodes.remove(node) i = 0 while self.model.item(i): item = self.model.item(i) if item.data() == node: self.model.removeRow(i) i += 1 def _update_subscription_model(self, node, value, timestamp): i = 0 while self.model.item(i): item = self.model.item(i) if item.data() == node: it = self.model.item(i, 1) it.setText(value) it_ts = self.model.item(i, 2) it_ts.setText(timestamp) i += 1
class QmyMainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.ui = Ui_MainWindow() self.ui.setupUi(self) self.setCentralWidget(self.ui.qSplitter) self.__ColCount = 6 self.itemModel = QStandardItemModel(5, self.__ColCount, self) self.selectionModel = QItemSelectionModel(self.itemModel) self.selectionModel.currentChanged.connect(self.do_curChanged) self.__lastColumnTitle = "测井取样" self.__lastColumnFlags = (Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) self.ui.qTableView.setModel(self.itemModel) self.ui.qTableView.setSelectionModel(self.selectionModel) oneOrMore = QAbstractItemView.ExtendedSelection self.ui.qTableView.setSelectionMode(oneOrMore) itemOrRow = QAbstractItemView.SelectItems self.ui.qTableView.setSelectionBehavior(itemOrRow) self.ui.qTableView.verticalHeader().setDefaultSectionSize(22) self.ui.qTableView.setAlternatingRowColors(True) self.ui.qTableView.setEnabled(False) self.qLabel1 = QLabel("当前单元格:", self) self.qLabel1.setMinimumWidth(180) self.qLabel2 = QLabel("单元格内容:", self) self.qLabel2.setMinimumWidth(150) self.qLabel3 = QLabel("当前文件:", self) self.ui.qStatusBar.addWidget(self.qLabel1) self.ui.qStatusBar.addWidget(self.qLabel2) self.ui.qStatusBar.addPermanentWidget(self.qLabel3) def __iniModelFromStringList(self, allLines): rowCnt = len(allLines) self.itemModel.setRowCount(rowCnt - 1) headerText = allLines[0].strip() headerList = headerText.split("\t") self.itemModel.setHorizontalHeaderLabels(headerList) self.__lastColumnTitle = headerList[len(headerList) - 1] lastColNo = self.__ColCount - 1 for i in range(rowCnt - 1): lineText = allLines[i + 1].strip() strList = lineText.split("\t") for j in range(self.__ColCount - 1): item = QStandardItem(strList[j]) self.itemModel.setItem(i, j, item) item = QStandardItem(self.__lastColumnTitle) item.setFlags(self.__lastColumnFlags) item.setCheckable(True) if (strList[lastColNo] == "0"): item.setCheckState(Qt.Unchecked) else: item.setCheckState(Qt.Checked) self.itemModel.setItem(i, lastColNo, item) def __setCellAlignment(self, align=Qt.AlignHCenter): if (not self.selectionModel.hasSelection()): return selectedIndex = self.selectionModel.selectedIndexes() count = len(selectedIndex) for i in range(count): index = selectedIndex[i] item = self.itemModel.itemFromIndex(index) item.setTextAlignment(align) @pyqtSlot() def on_qAction1_triggered(self): # 打开文件 curPath = os.getcwd() filename, flt = QFileDialog.getOpenFileName( self, "打开一个文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.qLabel3.setText("当前文件:" + filename) self.ui.qPlainTextEdit.clear() aFile = open(filename, 'r') allLines = aFile.readlines() aFile.close() for strLine in allLines: self.ui.qPlainTextEdit.appendPlainText(strLine.strip()) self.__iniModelFromStringList(allLines) self.ui.qTableView.setEnabled(True) self.ui.qAction2.setEnabled(True) self.ui.qAction3.setEnabled(True) self.ui.qAction4.setEnabled(True) self.ui.qAction5.setEnabled(True) self.ui.qAction6.setEnabled(True) @pyqtSlot() def on_qAction2_triggered(self): # 另存文件 curPath = os.getcwd() filename, flt = QFileDialog.getSaveFileName( self, "保存文件", curPath, "井斜数据文件(*.txt);;所有文件(*.*)") if (filename == ""): return self.on_qAction3_triggered() aFile = open(filename, "w") aFile.write(self.ui.qPlainTextEdit.toPlainText()) aFile.close() @pyqtSlot() def on_qAction4_triggered(self): # 添加行 itemList = [] for i in range(self.__ColCount - 1): item = QStandardItem("0") itemList.append(item) item = QStandardItem(self.__lastColumnTitle) item.setCheckable(True) item.setFlags(self.__lastColumnFlags) itemList.append(item) self.itemModel.appendRow(itemList) curIndex = self.itemModel.index(self.itemModel.rowCount() - 1, 0) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_qAction5_triggered(self): # 插入行 itemlist = [] for i in range(self.__ColCount - 1): item = QStandardItem("0") itemlist.append(item) item = QStandardItem(self.__lastColumnTitle) item.setFlags(self.__lastColumnFlags) item.setCheckable(True) item.setCheckState(Qt.Checked) itemlist.append(item) curIndex = self.selectionModel.currentIndex() self.itemModel.insertRow(curIndex.row(), itemlist) self.selectionModel.clearSelection() self.selectionModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) @pyqtSlot() def on_qAction6_triggered(self): # 删除行 curIndex = self.selectionModel.currentIndex() self.itemModel.removeRow(curIndex.row()) @pyqtSlot() def on_qAction7_triggered(self): # 居左 self.__setCellAlignment(Qt.AlignLeft | Qt.AlignVCenter) @pyqtSlot() def on_qAction8_triggered(self): # 居中 self.__setCellAlignment(Qt.AlignHCenter | Qt.AlignVCenter) @pyqtSlot() def on_qAction9_triggered(self): # 居右 self.__setCellAlignment(Qt.AlignRight | Qt.AlignVCenter) @pyqtSlot(bool) def on_qAction10_triggered(self, checked): # 粗体 if (not self.selectionModel.hasSelection()): return selectedIndex = self.selectionModel.selectedIndexes() count = len(selectedIndex) for i in range(count): index = selectedIndex[i] item = self.itemModel.itemFromIndex(index) font = item.font() font.setBold(checked) item.setFont(font) @pyqtSlot() def on_qAction3_triggered(self): # 模型数据 self.ui.qPlainTextEdit.clear() lineStr = "" for i in range(self.itemModel.columnCount() - 1): item = self.itemModel.horizontalHeaderItem(i) lineStr = lineStr + item.text() + "\t" item = self.itemModel.horizontalHeaderItem(self.__ColCount - 1) lineStr = lineStr + item.text() self.ui.qPlainTextEdit.appendPlainText(lineStr) for i in range(self.itemModel.rowCount()): lineStr = "" for j in range(self.itemModel.columnCount() - 1): item = self.itemModel.item(i, j) lineStr = lineStr + item.text() + "\t" item = self.itemModel.item(i, self.__ColCount - 1) if (item.checkState() == Qt.Checked): lineStr = lineStr + "1" else: lineStr = lineStr + "0" self.ui.qPlainTextEdit.appendPlainText(lineStr) def do_curChanged(self, current, previous): if (current != None): text = "当前单元格:%d行,%d列" % (current.row(), current.column()) self.qLabel1.setText(text) item = self.itemModel.itemFromIndex(current) self.qLabel2.setText("单元格内容:" + item.text()) font = item.font() self.ui.qAction10.setChecked(font.bold())
class VistaListaPrenotazioni(QWidget): def __init__(self, parent=None): super(VistaListaPrenotazioni, self).__init__(parent) self.controller = ControllerListaPrenotazioni() h_layout = QHBoxLayout() self.list_view = QListView() self.update_ui() h_layout.addWidget(self.list_view) buttons_layout = QVBoxLayout() open_button = QPushButton('Visualizza informazioni') open_button.setStyleSheet( 'QPushButton {background-color: #E9CFEC ; color: black; border-style: outset;border-width: 6px;' 'border-radius: 15px;border-color: #FA8072;padding: 6px}') open_button.clicked.connect(self.show_selected_info) buttons_layout.addWidget(open_button) new_button = QPushButton("Inserisci Prenotazione") new_button.setStyleSheet( 'QPushButton {background-color: #C3FDB8; color: black; border-style: outset;border-width: 6px;' 'border-radius: 15px;border-color: #3EB489;padding: 6px}') new_button.clicked.connect(self.visualizza_prenotazione) buttons_layout.addWidget(new_button) buttons_layout.addStretch() h_layout.addLayout(buttons_layout) self.setLayout(h_layout) self.resize(600, 300) self.setWindowTitle('Lista Prenotazioni') def update_ui(self): self.listview_model = QStandardItemModel(self.list_view) for prenotazione in self.controller.get_lista_delle_prenotazioni(): item = QStandardItem() item.setText(prenotazione.cliente.cognome + " " + prenotazione.cliente.nome) item.setEditable(False) font = item.font() font.setPointSize(18) item.setFont(font) self.listview_model.appendRow(item) self.list_view.setModel(self.listview_model) def show_selected_info(self): if len(self.list_view.selectedIndexes()) > 0: selected = self.list_view.selectedIndexes()[0].row() prenotazione_selezionata = self.controller.get_prenotazione_by_index( selected) self.vista_prenotazione = VistaPrenotazione( prenotazione_selezionata, self.controller.elimina_prenotazione_by_id, self.update_ui) self.vista_prenotazione.show() def visualizza_prenotazione(self): self.vista_inserisci_prenotazione = VistaInserisciPrenotazione( self.controller, self.update_ui) self.vista_inserisci_prenotazione.show() pass def closeEvent(self, event): self.controller.save_data()
class ClientMainWindow(QMainWindow): """ Class is the main user window. Contains all the main logic of the client module. The window configuration is created in QTDesigner and loaded from converted file main_window_conv.py """ def __init__(self, database, transport, keys): super().__init__() # основные переменные self.database = database self.transport = transport # объект - дешифорвщик сообщений с предзагруженным ключём self.decrypter = PKCS1_OAEP.new(keys) # Загружаем конфигурацию окна из дизайнера self.ui = Ui_MainClientWindow() self.ui.setupUi(self) # Кнопка "Выход" self.ui.menu_exit.triggered.connect(qApp.exit) # Кнопка отправить сообщение self.ui.btn_send.clicked.connect(self.send_message) # "добавить контакт" self.ui.btn_add_contact.clicked.connect(self.add_contact_window) self.ui.menu_add_contact.triggered.connect(self.add_contact_window) # Удалить контакт self.ui.btn_remove_contact.clicked.connect(self.delete_contact_window) self.ui.menu_del_contact.triggered.connect(self.delete_contact_window) # Дополнительные требующиеся атрибуты self.contacts_model = None self.history_model = None self.messages = QMessageBox() self.current_chat = None self.current_chat_key = None self.encryptor = None self.ui.list_messages.setHorizontalScrollBarPolicy( Qt.ScrollBarAlwaysOff) self.ui.list_messages.setWordWrap(True) # Даблклик по листу контактов отправляется в обработчик self.ui.list_contacts.doubleClicked.connect(self.select_active_user) self.clients_list_update() self.set_disabled_input() self.show() def set_disabled_input(self): """ Method making input fields inactive :return: """ # Надпись - получатель. self.ui.label_new_message.setText( 'Для выбора получателя дважды кликните на нем в окне контактов.') self.ui.text_message.clear() if self.history_model: self.history_model.clear() # Поле ввода и кнопка отправки неактивны до выбора получателя. self.ui.btn_clear.setDisabled(True) self.ui.btn_send.setDisabled(True) self.ui.text_message.setDisabled(True) self.encryptor = None self.current_chat = None self.current_chat_key = None def history_list_update(self): """ The filling method of the corresponding QListView history of correspondence with the current interlocutor. :return: """ # Получаем историю сортированную по дате list = sorted( self.database.get_history( self.current_chat), key=lambda item: item[3]) # Если модель не создана, создадим. if not self.history_model: self.history_model = QStandardItemModel() self.ui.list_messages.setModel(self.history_model) # Очистим от старых записей self.history_model.clear() # Берём не более 20 последних записей. length = len(list) start_index = 0 if length > 20: start_index = length - 20 # Заполнение модели записями, так-же стоит разделить входящие # и исходящие выравниванием и разным фоном. # отображает только последие 20 сообщений for i in range(start_index, length): item = list[i] if item[1] == 'in': mess = QStandardItem( f'Входящее от {item[3].replace(microsecond=0)}:\n {item[2]}') mess.setEditable(False) mess.setBackground(QBrush(QColor(255, 213, 213))) mess.setTextAlignment(Qt.AlignLeft) self.history_model.appendRow(mess) else: mess = QStandardItem( f'Исходящее от {item[3].replace(microsecond=0)}:\n {item[2]}') mess.setEditable(False) mess.setTextAlignment(Qt.AlignRight) mess.setBackground(QBrush(QColor(204, 255, 204))) self.history_model.appendRow(mess) self.ui.list_messages.scrollToBottom() def select_active_user(self): """ Double click event handler method on the contact list :return: """ # Выбранный пользователем (даблклик) находится в выделеном элементе в # QListView self.current_chat = self.ui.list_contacts.currentIndex().data() # вызываем основную функцию self.set_active_user() def set_active_user(self): """ A method for activating a chat with an interlocutor. :return: """ # Запрашиваем публичный ключ пользователя и создаём объект шифрования try: self.current_chat_key = self.transport.key_request( self.current_chat) client_log.debug(f'Загружен открытый ключ для {self.current_chat}') if self.current_chat_key: self.encryptor = PKCS1_OAEP.new( RSA.import_key(self.current_chat_key)) except (OSError, json.JSONDecodeError): self.current_chat_key = None self.encryptor = None client_log.debug(f'Не удалось получить ключ для {self.current_chat}') # Если ключа нет то ошибка, что не удалось начать чат с пользователем if not self.current_chat_key: self.messages.warning( self, 'Ошибка', 'Для выбранного пользователя нет ключа шифрования.') return # Ставим надпись и активируем кнопки self.ui.label_new_message.setText( f'Введите сообщенние для {self.current_chat}:') self.ui.btn_clear.setDisabled(False) self.ui.btn_send.setDisabled(False) self.ui.text_message.setDisabled(False) # Заполняем окно историю сообщений по требуемому пользователю. self.history_list_update() def clients_list_update(self): """ Method for updating the contact list. :return: """ contacts_list = self.database.get_contacts() self.contacts_model = QStandardItemModel() for i in sorted(contacts_list): item = QStandardItem(i) item.setEditable(False) self.contacts_model.appendRow(item) self.ui.list_contacts.setModel(self.contacts_model) def add_contact_window(self): """ Method creating a window - a dialog for adding a contact :return: """ global select_dialog select_dialog = AddContactDialog(self.transport, self.database) select_dialog.btn_ok.clicked.connect( lambda: self.add_contact_action(select_dialog)) select_dialog.show() def add_contact_action(self, item): """ Method to handle clicking the 'Add' button :param item: :return: """ new_contact = item.selector.currentText() self.add_contact(new_contact) item.close() def add_contact(self, new_contact): """ Method adding contact to server and client BD. After updating the databases, the contents of the window are also updated. :param new_contact: :return: """ try: self.transport.add_contact(new_contact) except ServerError as err: self.messages.critical(self, 'Ошибка сервера', err.text) except OSError as err: if err.errno: self.messages.critical( self, 'Ошибка', 'Потеряно соединение с сервером!') self.close() self.messages.critical(self, 'Ошибка', 'Таймаут соединения!') else: self.database.add_contact(new_contact) new_contact = QStandardItem(new_contact) new_contact.setEditable(False) self.contacts_model.appendRow(new_contact) client_log.info(f'Успешно добавлен контакт {new_contact}') self.messages.information( self, 'Успех', 'Контакт успешно добавлен.') def delete_contact_window(self): """ A method that creates a window for deleting a contact. :return: """ global remove_dialog remove_dialog = DelContactDialog(self.database) remove_dialog.btn_ok.clicked.connect( lambda: self.delete_contact(remove_dialog)) remove_dialog.show() def delete_contact(self, item): """ Method for removing contact from server and client BD. After updating the databases, the contents of the window are also updated. :param item: :return: """ selected = item.selector.currentText() try: self.transport.remove_contact(selected) except ServerError as err: self.messages.critical(self, 'Ошибка сервера', err.text) except OSError as err: if err.errno: self.messages.critical( self, 'Ошибка', 'Потеряно соединение с сервером!') self.close() self.messages.critical(self, 'Ошибка', 'Таймаут соединения!') else: self.database.del_contact(selected) self.clients_list_update() client_log.info(f'Успешно удалён контакт {selected}') self.messages.information(self, 'Успех', 'Контакт успешно удалён.') item.close() # Если удалён активный пользователь, то деактивируем поля ввода. if selected == self.current_chat: self.current_chat = None self.set_disabled_input() def send_message(self): """ The function of sending a message to the current interlocutor. Implements message encryption and sending. """ # Текст в поле, проверяем что поле не пустое затем забирается сообщение # и поле очищается message_text = self.ui.text_message.toPlainText() self.ui.text_message.clear() if not message_text: return # Шифруем сообщение ключом получателя и упаковываем в base64. message_text_encrypted = self.encryptor.encrypt( message_text.encode('utf8')) message_text_encrypted_base64 = base64.b64encode( message_text_encrypted) try: self.transport.send_message( self.current_chat, message_text_encrypted_base64.decode('ascii')) pass except ServerError as err: self.messages.critical(self, 'Ошибка', err.text) except OSError as err: if err.errno: self.messages.critical( self, 'Ошибка', 'Потеряно соединение с сервером!') self.close() self.messages.critical(self, 'Ошибка', 'Таймаут соединения!') except (ConnectionResetError, ConnectionAbortedError): self.messages.critical( self, 'Ошибка', 'Потеряно соединение с сервером!') self.close() else: self.database.save_message(self.current_chat, 'out', message_text) client_log.debug( f'Отправлено сообщение для {self.current_chat}: {message_text}') self.history_list_update() @pyqtSlot(dict) def message(self, message): """ Slot handler of incoming messages, performs decryption received messages and their saving in the message history. Asks the user if the message is not from the current one interlocutor. Changes the interlocutor if necessary. :param message: :return: """ # Получаем строку байтов encrypted_message = base64.b64decode(message[MESSAGE_TEXT]) # Декодируем строку, при ошибке выдаём сообщение и завершаем функцию try: decrypted_message = self.decrypter.decrypt(encrypted_message) except (ValueError, TypeError): self.messages.warning( self, 'Ошибка', 'Не удалось декодировать сообщение.') return # Сохраняем сообщение в базу и обновляем историю сообщений или # открываем новый чат. self.database.save_message( self.current_chat, 'in', decrypted_message.decode('utf8')) sender = message[SENDER] if sender == self.current_chat: self.history_list_update() else: # Проверим есть ли такой пользователь у нас в контактах: if self.database.check_contact(sender): # Если есть, спрашиваем и желании открыть с ним чат и открываем # при желании if self.messages.question( self, 'Новое сообщение', f'Получено новое сообщение от {sender}, открыть чат с ним?', QMessageBox.Yes, QMessageBox.No) == QMessageBox.Yes: self.current_chat = sender self.set_active_user() else: print('NO') # Раз нету,спрашиваем хотим ли добавить юзера в контакты. if self.messages.question( self, 'Новое сообщение', f'Получено новое сообщение от {sender}.\n Данного пользователя нет в вашем контакт-листе.\n ' f'Добавить в контакты и открыть чат с ним?', QMessageBox.Yes, QMessageBox.No) == QMessageBox.Yes: self.add_contact(sender) self.current_chat = sender # Нужно заново сохранить сообщение, иначе оно будет потеряно, # т.к. на момент предыдущего вызова контакта не было. self.database.save_message( self.current_chat, 'in', decrypted_message.decode('utf8')) self.set_active_user() @pyqtSlot() def connection_lost(self): """ Slot handler for lost connection to the server. Shows a warning window and exits the application. :return: """ self.messages.warning( self, 'Сбой соединения', 'Потеряно соединение с сервером. ') self.close() @pyqtSlot() def sig_205(self): """ Slot performing database update on server command """ if self.current_chat and not self.database.check_user( self.current_chat): self.messages.warning( self, 'Сочувствую', 'К сожалению собеседник был удалён с сервера.') self.set_disabled_input() self.current_chat = None self.clients_list_update() def make_connection(self, trans_obj): """ Method for connecting signals and slots :param trans_obj: :return: """ trans_obj.new_message.connect(self.message) trans_obj.connection_lost.connect(self.connection_lost) trans_obj.message_205.connect(self.sig_205)
class DeleteDialog(QDialog): new_infos = pyqtSignal(object) def __init__(self, infos, parent=None): super(DeleteDialog, self).__init__(parent) self.infos = infos self.out = [] self.initUI() self.setStyleSheet(dialog_qss_style) def set_file_icon(self, name): suffix = name.split(".")[-1] ico_path = "./icon/{}.gif".format(suffix) if os.path.isfile(ico_path): return QIcon(ico_path) else: return QIcon("./icon/file.ico") def initUI(self): self.setWindowTitle("确认删除") self.setWindowIcon(QIcon("./icon/delete.ico")) self.layout = QVBoxLayout() self.list_view = QListView() self.list_view.setViewMode(QListView.ListMode) # 列表 self.slm = QStandardItem() self.model = QStandardItemModel() max_len = 10 count = 0 for i in self.infos: if i[2]: # 有大小,是文件 self.model.appendRow( QStandardItem(self.set_file_icon(i[1]), i[1])) else: self.model.appendRow( QStandardItem(QIcon("./icon/folder.gif"), i[1])) self.out.append({ 'fid': i[0], 'is_file': True if i[2] else False, 'name': i[1] }) # id,文件标示, 文件名 count += 1 if max_len < len(i[1]): # 使用最大文件名长度 max_len = len(i[1]) self.list_view.setModel(self.model) self.lb_name = QLabel("尝试删除以下{}个文件(夹):".format(count)) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.layout.addWidget(self.lb_name) self.layout.addWidget(self.list_view) self.layout.addWidget(self.buttonBox) self.setLayout(self.layout) self.buttonBox.accepted.connect(self.btn_ok) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.setMinimumWidth(400) self.resize(int(max_len * 8), int(count * 34 + 60)) def btn_ok(self): self.new_infos.emit(self.out)
def __init__(self, *args, mz_parent=None, spectrum: np.ndarray = None, base_path=None, selection: dict = None, **kwargs): super().__init__(*args, **kwargs) self._selected_index = QModelIndex() self.setupUi(self) self.setWindowFlags(Qt.Tool | Qt.CustomizeWindowHint | Qt.WindowCloseButtonHint) if spectrum is not None: self.widgetSpectrumDetails.widgetSpectrum.set_spectrum2( spectrum, parent=mz_parent) self.current = selection['current'] if 'current' in selection else 0 model = SpectraModel(base_path, selection) for i in range(model.columnCount()): if model.headerData( i, Qt.Horizontal) == Spectrum.libraryquality.comment: self.tvSpectra.setItemDelegateForColumn( i, LibraryQualityDelegate()) break forbidden_cols = (model.column_index(Spectrum.id), model.column_index(Spectrum.bank_id), model.column_index(Spectrum.peaks)) tree_model = QStandardItemModel() tree_model.setColumnCount(model.columnCount() - len(forbidden_cols)) tree_model.setHorizontalHeaderLabels([ model.headerData(section) for section in range(model.columnCount()) if section not in forbidden_cols ]) standards_item = QStandardItem( QIcon(":/icons/images/library-query.svg"), 'Standards') standards_item.setSelectable(False) standards_item.setData("standards", role=SpectraModel.TypeRole) tree_model.appendRow(standards_item) analogs_item = QStandardItem( QIcon(":/icons/images/library-query-analogs.svg"), 'Analogs') analogs_item.setSelectable(False) analogs_item.setData("analogs", role=SpectraModel.TypeRole) tree_model.appendRow(analogs_item) standards_rows = 0 for row in range(model.rowCount()): values = [] index = None for column in range(model.columnCount()): if column in forbidden_cols: continue index = model.index(row, column) item = QStandardItem(str(model.data(index))) item.setEditable(False) values.append(item) if index is not None: if model.data(index, role=SpectraModel.TypeRole) == 'analogs': analogs_item.appendRow(values) else: standards_item.appendRow(values) standards_rows += 1 standards_item.setData(0, SpectraModel.RowRole) analogs_item.setData(standards_rows, SpectraModel.RowRole) self.tvSpectra.setModel(tree_model) self.tvSpectra.expandAll() self.widgetSpectrumDetails.setModel(model) self.widgetSpectrumDetails.widgetSpectrum.canvas.first_spectrum_label = "Database" self.widgetSpectrumDetails.widgetSpectrum.canvas.second_spectrum_label = "Experimental" # Connect events self.tvSpectra.selectionModel().currentChanged.connect( self.on_selection_changed)
def _populate_plan_ui(ui, model, plan): """ Fill all widgets with current model data. """ from PyQt5.QtGui import QStandardItemModel from PyQt5.QtGui import QStandardItem # ----------- General Info Tab --------------------------- ui.lineEdit.setText(plan.basename) ui.lineEdit_2.setText(plan.comment) ui.lineEdit_3.setText(str(plan.__uuid__)) # ----------- Target Tab --------------------------------- voinames = [voi.name for voi in model.vdx.vois] # TARGET ui.comboBox.clear() for voi in model.vdx.vois: ui.comboBox.addItem(voi.name, voi) if plan.voi_target: _i = ui.comboBox.findData(voi) if _i == -1: # not found logger.warning("_populate_plan_ui() FIXME") else: ui.comboBox.setCurrentIndex(_i) else: plan.voi_target = model.vdx.vois[0] ui.comboBox.setCurrentIndex(0) # OAR list = ui.listView # https://www.pythoncentral.io/pyside-pyqt-tutorial-qlistview-and-qstandarditemmodel/ listmodel = QStandardItemModel(list) for voiname in voinames: item = QStandardItem(voiname) item.setCheckable(True) listmodel.appendRow(item) list.setModel(listmodel) # INCUBE ui.comboBox_2.clear() if model.dos: for dos in model.dos: ui.comboBox_2.addItem(dos.basename, dos) if plan.incube_basename: ui.comboBox_2.setEnabled(True) ui.checkBox.setChecked(True) _i = ui.comboBox_2.findText(dos.basename) if _i == -1: # not found logger.warning("_populate_plan_ui() FIXME 2") else: ui.comboBox_2.setCurrentIndex(_i) else: plan.incube_basename = None ui.comboBox_2.setEnabled(False) ui.checkBox.setChecked(False) # TISSUE ui.comboBox_3.clear() ui.comboBox_3.addItem("(not implemented)") ui.comboBox_3.setEnabled(False) # RESIDUAL TISSUE ui.comboBox_4.clear() ui.comboBox_4.addItem("(not implemented)") ui.comboBox_4.setEnabled(False) # ----------- Dose Delivery Tab --------------------------- # TODO: Projectile PlanController._setup_plan_combobox(ui.comboBox_5, Plan.opt_methods) # TODO: Ripple filter # Target Dose Percent ui.doubleSpinBox.setValue(plan.target_dose_percent) ui.doubleSpinBox_4.setValue(plan.target_dose) # ----------- Optimization Tab ---------------------------- # Iterations ui.spinBox.setValue(plan.iterations) ui.doubleSpinBox_2.setValue(plan.eps) ui.doubleSpinBox_3.setValue(plan.geps) PlanController._setup_plan_combobox(ui.comboBox_7, Plan.opt_methods) PlanController._setup_plan_combobox(ui.comboBox_8, Plan.opt_principles) PlanController._setup_plan_combobox(ui.comboBox_9, Plan.dose_algs) PlanController._setup_plan_combobox(ui.comboBox_10, Plan.bio_algs) PlanController._setup_plan_combobox(ui.comboBox_11, Plan.opt_algs) # ----------- Results Tab -------------------------------- ui.checkBox_2.setChecked(plan.want_phys_dose) ui.checkBox_3.setChecked(plan.want_bio_dose) ui.checkBox_4.setChecked(plan.want_dlet) ui.checkBox_5.setChecked(plan.want_rst) ui.checkBox_6.setEnabled(False) ui.checkBox_7.setEnabled(False)
class HelpWebSearchWidget(E5ClearableLineEdit): """ Class implementing a web search widget for the web browser. @signal search(QUrl) emitted when the search should be done """ search = pyqtSignal(QUrl) def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(HelpWebSearchWidget, self).__init__(parent) from E5Gui.E5LineEdit import E5LineEdit from E5Gui.E5LineEditButton import E5LineEditButton from .OpenSearch.OpenSearchManager import OpenSearchManager self.__mw = parent self.__openSearchManager = OpenSearchManager(self) self.__openSearchManager.currentEngineChanged.connect( self.__currentEngineChanged) self.__currentEngine = "" self.__enginesMenu = QMenu(self) self.__engineButton = E5LineEditButton(self) self.__engineButton.setMenu(self.__enginesMenu) self.addWidget(self.__engineButton, E5LineEdit.LeftSide) self.__searchButton = E5LineEditButton(self) self.__searchButton.setIcon(UI.PixmapCache.getIcon("webSearch.png")) self.addWidget(self.__searchButton, E5LineEdit.LeftSide) self.__model = QStandardItemModel(self) self.__completer = QCompleter() self.__completer.setModel(self.__model) self.__completer.setCompletionMode( QCompleter.UnfilteredPopupCompletion) self.__completer.setWidget(self) self.__searchButton.clicked.connect(self.__searchButtonClicked) self.textEdited.connect(self.__textEdited) self.returnPressed.connect(self.__searchNow) self.__completer.activated[QModelIndex].connect( self.__completerActivated) self.__completer.highlighted[QModelIndex].connect( self.__completerHighlighted) self.__enginesMenu.aboutToShow.connect(self.__showEnginesMenu) self.__suggestionsItem = None self.__suggestions = [] self.__suggestTimer = None self.__suggestionsEnabled = Preferences.getHelp("WebSearchSuggestions") self.__recentSearchesItem = None self.__recentSearches = [] self.__maxSavedSearches = 10 self.__engine = None self.__loadSearches() self.__setupCompleterMenu() self.__currentEngineChanged() def __searchNow(self): """ Private slot to perform the web search. """ searchText = self.text() if not searchText: return globalSettings = QWebSettings.globalSettings() if not globalSettings.testAttribute( QWebSettings.PrivateBrowsingEnabled): if searchText in self.__recentSearches: self.__recentSearches.remove(searchText) self.__recentSearches.insert(0, searchText) if len(self.__recentSearches) > self.__maxSavedSearches: self.__recentSearches = \ self.__recentSearches[:self.__maxSavedSearches] self.__setupCompleterMenu() url = self.__openSearchManager.currentEngine().searchUrl(searchText) self.search.emit(url) def __setupCompleterMenu(self): """ Private method to create the completer menu. """ if not self.__suggestions or \ (self.__model.rowCount() > 0 and self.__model.item(0) != self.__suggestionsItem): self.__model.clear() self.__suggestionsItem = None else: self.__model.removeRows(1, self.__model.rowCount() - 1) boldFont = QFont() boldFont.setBold(True) if self.__suggestions: if self.__model.rowCount() == 0: if not self.__suggestionsItem: self.__suggestionsItem = QStandardItem( self.tr("Suggestions")) self.__suggestionsItem.setFont(boldFont) self.__model.appendRow(self.__suggestionsItem) for suggestion in self.__suggestions: self.__model.appendRow(QStandardItem(suggestion)) if not self.__recentSearches: self.__recentSearchesItem = QStandardItem( self.tr("No Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) else: self.__recentSearchesItem = QStandardItem( self.tr("Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) for recentSearch in self.__recentSearches: self.__model.appendRow(QStandardItem(recentSearch)) view = self.__completer.popup() view.setFixedHeight(view.sizeHintForRow(0) * self.__model.rowCount() + view.frameWidth() * 2) self.__searchButton.setEnabled( bool(self.__recentSearches or self.__suggestions)) def __completerActivated(self, index): """ Private slot handling the selection of an entry from the completer. @param index index of the item (QModelIndex) """ if self.__suggestionsItem and \ self.__suggestionsItem.index().row() == index.row(): return if self.__recentSearchesItem and \ self.__recentSearchesItem.index().row() == index.row(): return self.__searchNow() def __completerHighlighted(self, index): """ Private slot handling the highlighting of an entry of the completer. @param index index of the item (QModelIndex) @return flah indicating a successful highlighting (boolean) """ if self.__suggestionsItem and \ self.__suggestionsItem.index().row() == index.row(): return False if self.__recentSearchesItem and \ self.__recentSearchesItem.index().row() == index.row(): return False self.setText(index.data()) return True def __textEdited(self, txt): """ Private slot to handle changes of the search text. @param txt search text (string) """ if self.__suggestionsEnabled: if self.__suggestTimer is None: self.__suggestTimer = QTimer(self) self.__suggestTimer.setSingleShot(True) self.__suggestTimer.setInterval(200) self.__suggestTimer.timeout.connect(self.__getSuggestions) self.__suggestTimer.start() else: self.__completer.setCompletionPrefix(txt) self.__completer.complete() def __getSuggestions(self): """ Private slot to get search suggestions from the configured search engine. """ searchText = self.text() if searchText: self.__openSearchManager.currentEngine()\ .requestSuggestions(searchText) def __newSuggestions(self, suggestions): """ Private slot to receive a new list of suggestions. @param suggestions list of suggestions (list of strings) """ self.__suggestions = suggestions self.__setupCompleterMenu() self.__completer.complete() def __showEnginesMenu(self): """ Private slot to handle the display of the engines menu. """ self.__enginesMenu.clear() from .OpenSearch.OpenSearchEngineAction import OpenSearchEngineAction engineNames = self.__openSearchManager.allEnginesNames() for engineName in engineNames: engine = self.__openSearchManager.engine(engineName) action = OpenSearchEngineAction(engine, self.__enginesMenu) action.setData(engineName) action.triggered.connect(self.__changeCurrentEngine) self.__enginesMenu.addAction(action) if self.__openSearchManager.currentEngineName() == engineName: action.setCheckable(True) action.setChecked(True) ct = self.__mw.currentBrowser() linkedResources = ct.linkedResources("search") if len(linkedResources) > 0: self.__enginesMenu.addSeparator() for linkedResource in linkedResources: url = QUrl(linkedResource.href) title = linkedResource.title mimetype = linkedResource.type_ if mimetype != "application/opensearchdescription+xml": continue if url.isEmpty(): continue if url.isRelative(): url = ct.url().resolved(url) if not title: if not ct.title(): title = url.host() else: title = ct.title() action = self.__enginesMenu.addAction( self.tr("Add '{0}'").format(title), self.__addEngineFromUrl) action.setData(url) action.setIcon(ct.icon()) self.__enginesMenu.addSeparator() self.__enginesMenu.addAction(self.__mw.searchEnginesAction()) if self.__recentSearches: self.__enginesMenu.addAction(self.tr("Clear Recent Searches"), self.clear) def __changeCurrentEngine(self): """ Private slot to handle the selection of a search engine. """ action = self.sender() if action is not None: name = action.data() self.__openSearchManager.setCurrentEngineName(name) def __addEngineFromUrl(self): """ Private slot to add a search engine given its URL. """ action = self.sender() if action is not None: url = action.data() if not isinstance(url, QUrl): return self.__openSearchManager.addEngine(url) def __searchButtonClicked(self): """ Private slot to show the search menu via the search button. """ self.__setupCompleterMenu() self.__completer.complete() def clear(self): """ Public method to clear all private data. """ self.__recentSearches = [] self.__setupCompleterMenu() super(HelpWebSearchWidget, self).clear() self.clearFocus() def preferencesChanged(self): """ Public method to handle the change of preferences. """ self.__suggestionsEnabled = Preferences.getHelp("WebSearchSuggestions") if not self.__suggestionsEnabled: self.__suggestions = [] self.__setupCompleterMenu() def saveSearches(self): """ Public method to save the recently performed web searches. """ Preferences.Prefs.settings.setValue( 'Help/WebSearches', self.__recentSearches) def __loadSearches(self): """ Private method to load the recently performed web searches. """ searches = Preferences.Prefs.settings.value('Help/WebSearches') if searches is not None: self.__recentSearches = searches def openSearchManager(self): """ Public method to get a reference to the opensearch manager object. @return reference to the opensearch manager object (OpenSearchManager) """ return self.__openSearchManager def __currentEngineChanged(self): """ Private slot to track a change of the current search engine. """ if self.__openSearchManager.engineExists(self.__currentEngine): oldEngine = self.__openSearchManager.engine(self.__currentEngine) oldEngine.imageChanged.disconnect(self.__engineImageChanged) if self.__suggestionsEnabled: oldEngine.suggestions.disconnect(self.__newSuggestions) newEngine = self.__openSearchManager.currentEngine() if newEngine.networkAccessManager() is None: newEngine.setNetworkAccessManager(self.__mw.networkAccessManager()) newEngine.imageChanged.connect(self.__engineImageChanged) if self.__suggestionsEnabled: newEngine.suggestions.connect(self.__newSuggestions) self.setInactiveText(self.__openSearchManager.currentEngineName()) self.__currentEngine = self.__openSearchManager.currentEngineName() self.__engineButton.setIcon(QIcon(QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) self.__suggestions = [] self.__setupCompleterMenu() def __engineImageChanged(self): """ Private slot to handle a change of the current search engine icon. """ self.__engineButton.setIcon(QIcon(QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) def mousePressEvent(self, evt): """ Protected method called by a mouse press event. @param evt reference to the mouse event (QMouseEvent) """ if evt.button() == Qt.XButton1: self.__mw.currentBrowser().pageAction(QWebPage.Back).trigger() elif evt.button() == Qt.XButton2: self.__mw.currentBrowser().pageAction(QWebPage.Forward).trigger() else: super(HelpWebSearchWidget, self).mousePressEvent(evt)
class EventUI(object): def __init__(self, window, uaclient): self.window = window self.uaclient = uaclient self._handler = EventHandler() self._subscribed_nodes = [] # FIXME: not really needed self.model = QStandardItemModel() self.window.ui.evView.setModel(self.model) self.window.ui.actionSubscribeEvent.triggered.connect(self._subscribe) self.window.ui.actionUnsubscribeEvents.triggered.connect( self._unsubscribe) # context menu self.window.addAction(self.window.ui.actionSubscribeEvent) self.window.addAction(self.window.ui.actionUnsubscribeEvents) self.window.addAction(self.window.ui.actionAddToGraph) self._handler.event_fired.connect(self._update_event_model, type=Qt.QueuedConnection) # accept drops self.model.canDropMimeData = self.canDropMimeData self.model.dropMimeData = self.dropMimeData def canDropMimeData(self, mdata, action, row, column, parent): return True def show_error(self, *args): self.window.show_error(*args) def dropMimeData(self, mdata, action, row, column, parent): node = self.uaclient.client.get_node(mdata.text()) self._subscribe(node) return True def clear(self): self._subscribed_nodes = [] self.model.clear() @trycatchslot def _subscribe(self, node=None): logger.info("Subscribing to %s", node) if not node: node = self.window.get_current_node() if node is None: return if node in self._subscribed_nodes: logger.info("already subscribed to event for node: %s", node) return logger.info("Subscribing to events for %s", node) self.window.ui.evDockWidget.raise_() try: self.uaclient.subscribe_events(node, self._handler) except Exception as ex: self.window.show_error(ex) raise else: self._subscribed_nodes.append(node) @trycatchslot def _unsubscribe(self): node = self.window.get_current_node() if node is None: return self._subscribed_nodes.remove(node) self.uaclient.unsubscribe_events(node) @trycatchslot def _update_event_model(self, event): self.model.appendRow([QStandardItem(str(event))])
class PwdManager(QDialog): def __init__(self): super().__init__() self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowTitleHint) self.setWindowTitle('Password Manager') self.setFixedSize(400, 300) self.PwdViewer = QListView() self.PwdViewer.clicked.connect(self.ClickPwd) self.AddButton = QPushButton('Add') self.AddButton.clicked.connect(self.AddPwd) self.DelButton = QPushButton('Delete') self.DelButton.clicked.connect(self.DelPwd) self.PwdBox = QLineEdit() self.PwdBox.textEdited.connect(self.PwdChanged) vbox = QVBoxLayout() vbox.addWidget(self.AddButton) vbox.addWidget(self.DelButton) vbox.addStretch(1) hbox = QHBoxLayout() hbox.addWidget(self.PwdViewer) hbox.addLayout(vbox) MainBox = QVBoxLayout() MainBox.addWidget(self.PwdBox) MainBox.addLayout(hbox) self.setLayout(MainBox) def showEvent(self, event): self.center() self.LoadPwdToList() if self.Model.rowCount() == 0: self.AddPwd() self.IsChanged = False def closeEvent(self, event): self.RemoveEmpty() pwdf = open('password.pwd', 'w') for index in range(self.Model.rowCount()): pwdf.write(self.Model.data(self.Model.index(index, 0)) + '\n') pwdf.close() if self.IsChanged: self.done(1) else: self.done(0) def center(self): frameGm = self.frameGeometry() screen = QApplication.desktop().screenNumber( QApplication.desktop().cursor().pos()) centerPoint = QApplication.desktop().screenGeometry(screen).center() frameGm.moveCenter(centerPoint) self.move(frameGm.topLeft()) def LoadPwdToList(self): if not os.path.exists(r'password.pwd'): open("password.pwd", "wb").close() pwdf = open('password.pwd', 'r') pwdlist = pwdf.read().splitlines() pwdf.close() self.Model = QStandardItemModel(self.PwdViewer) for pwd in pwdlist: item = QStandardItem(pwd) item.setEditable(False) self.Model.appendRow(item) self.PwdViewer.setModel(self.Model) self.PwdViewer.setCurrentIndex(self.Model.index(0, 0)) self.GetPwd(self.PwdViewer.currentIndex()) def ClickPwd(self, index): self.RemoveEmpty() if self.Model.rowCount() == 0: self.AddPwd() self.GetPwd(index) def GetPwd(self, index): self.PwdBox.setText(self.Model.data(index)) self.PwdBox.setFocus() def PwdChanged(self): self.IsChanged = True self.Model.setData(self.PwdViewer.currentIndex(), self.PwdBox.text()) if self.PwdBox.text() == '': self.DelPwd() def DelPwd(self): self.Model.removeRow(self.PwdViewer.currentIndex().row()) self.GetPwd(self.PwdViewer.currentIndex()) if self.Model.rowCount() == 0: self.AddPwd() def AddPwd(self): item = QStandardItem() item.setEditable(False) self.Model.appendRow(item) self.PwdViewer.setCurrentIndex( self.Model.index(self.Model.rowCount() - 1, 0)) self.GetPwd(self.PwdViewer.currentIndex()) def RemoveEmpty(self): for item in self.Model.findItems('', Qt.MatchFixedString): self.Model.removeRow(item.row())
class SelectDialog(QDialog, Ui_SelectDialog): """description of class""" def __init__(self, parent, title, text, options, enabledOptions=None, headerOptions=[], singleSelectionMode=True): QDialog.__init__(self, parent) self.setupUi(self) self.setWindowTitle(title) self.label_text.setText(text) self.headerOptions = headerOptions self.singleSelectionMode = singleSelectionMode self.options = options self.initUi(options, enabledOptions, headerOptions, singleSelectionMode) self.pushButton_sellectAll.clicked.connect( self.pushButton_selectAll_clicked) self.pushButton_deSellectAll.clicked.connect( self.pushButton_deSelectAll_clicked) self.listView_options.clicked.connect(self.listView_options_clicked) def initUi(self, options, enabledOptions, headerOptions, singleSelectionMode): boldFont = QFont() boldFont.setBold(True) # set the selection mode if not singleSelectionMode: self.listView_options.setSelectionMode( QAbstractItemView.ExtendedSelection) # create enableItems if none if enabledOptions is None: enabledOptions = [True for idx in range(len(options))] # Insert the choices self.standaredItemModel = QStandardItemModel(self.listView_options) self.standaredItemModel.itemChanged.connect(self.onItemChanged) for idx in range(len(options)): standaredItem = QStandardItem(options[idx]) standaredItem.setSelectable(enabledOptions[idx]) if idx in headerOptions: standaredItem.setFont(boldFont) self.standaredItemModel.appendRow(standaredItem) self.listView_options.setModel(self.standaredItemModel) # disable select all / de select all buttons if in single selection # mode if singleSelectionMode: self.pushButton_sellectAll.setDisabled(True) self.pushButton_deSellectAll.setDisabled(True) def onItemChanged(self, item): QMessageBox.information(self, "Selec Dialog Message", "Selected Item :" + item.text()) def selection(self): return [ index.row() for index in self.listView_options.selectionModel().selectedIndexes() ] def pushButton_selectAll_clicked(self): selectIndexes = [ idx for idx in range(self.standaredItemModel.rowCount()) ] self.setSelected(selectIndexes, QItemSelectionModel.Select) def pushButton_deSelectAll_clicked(self): selectIndexes = [ idx for idx in range(self.standaredItemModel.rowCount()) ] self.setSelected(selectIndexes, QItemSelectionModel.Deselect) def setSelected(self, selectIndexes, newStatus): modelIndexes = [ self.standaredItemModel.createIndex(rowIndex, 0) for rowIndex in selectIndexes ] selectionModel = self.listView_options.selectionModel() for modelIndex in modelIndexes: selectionModel.select(modelIndex, newStatus) @pyqtSlot("QModelIndex") def listView_options_clicked(self, modelIndex): if self.singleSelectionMode: return row = modelIndex.row() if row in self.headerOptions: indexInHeaderOptions = self.headerOptions.index(row) if indexInHeaderOptions == len(self.headerOptions) - 1: selectedIndexes = [ idx for idx in range(row, len(self.options)) ] else: selectedIndexes = [ idx for idx in range( row, self.headerOptions[indexInHeaderOptions + 1]) ] selectionModel = self.listView_options.selectionModel() if modelIndex in selectionModel.selectedIndexes(): newStatus = QItemSelectionModel.Select else: newStatus = QItemSelectionModel.Deselect self.setSelected(selectedIndexes, newStatus)
class VistaListaProdotti(QWidget): def __init__(self, parent=None): super(VistaListaProdotti, self).__init__(parent) self.carrello = ControlloreCarrello() self.controller = ControlloreListaProdotti() self.setWindowIcon(QtGui.QIcon('logos/logo.png')) main_layout = QHBoxLayout() v_layout = QVBoxLayout() self.list_view = QListView() self.update_ui() #Crea un elenco fittizio sopra l'elenco reale per poter usare la barra di ricerca self.filter_proxy_model = QSortFilterProxyModel() self.filter_proxy_model.setSourceModel(self.listview_model) self.filter_proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.filter_proxy_model.setFilterKeyColumn(0) self.list_view.setModel(self.filter_proxy_model) search_field = QLineEdit() search_field.setStyleSheet('font-size: 15px; height: 30px;') search_field.textChanged.connect( self.filter_proxy_model.setFilterRegExp) v_layout.addWidget(search_field) v_layout.addWidget(self.list_view) main_layout.addLayout(v_layout) buttons_layout = QVBoxLayout() buttons_layout.addItem(QSpacerItem(40, 40)) #Bottone per aprire un prodotto open_button = QPushButton("Apri") open_button.clicked.connect(self.show_selected_info) buttons_layout.addWidget(open_button) #Bottone per creare un nuovo prodotto new_button = QPushButton("Nuovo") new_button.clicked.connect(self.show_new_prodotto) buttons_layout.addWidget(new_button) buttons_layout.addStretch() main_layout.addLayout(buttons_layout) self.setLayout(main_layout) self.resize(600, 300) self.setWindowTitle("Lista Prodotti") #Metodo che mostra a schermo le informazioni del prodotto selezionato def show_selected_info(self): try: sourceindex = self.list_view.selectedIndexes()[0].row() prodotto_selezionato = self.controller.get_prodotto_by_index( sourceindex) self.vista_prodotto = VistaProdotto( prodotto_selezionato, self.controller.elimina_prodotto_by_id, self.update_ui, self.carrello) self.vista_prodotto.show() except IndexError: QMessageBox.critical(self, 'Errore', 'Per favore, seleziona un prodotto', QMessageBox.Ok, QMessageBox.Ok) #Metodo aprire la vista di inserimento del nuovo prodotto def show_new_prodotto(self): self.vista_inserisci_prodotto = VistaInserisciProdotto( self.controller, self.update_ui) self.vista_inserisci_prodotto.show() #Metodo che serve per generare e/o aggiornare la vista def update_ui(self): self.listview_model = QStandardItemModel(self.list_view) for prodotto in self.controller.get_lista_dei_prodotti(): item = QStandardItem() item.setText(prodotto.marca + " " + prodotto.nome) item.setEditable(False) font = item.font() font.setPointSize(18) item.setFont(font) self.listview_model.appendRow(item) self.list_view.setModel(self.listview_model) # salva i dati sul file pickle alla chiusura della view def closeEvent(self, event): self.controller.save_data() #Metodo per collegare l'indice selezionato all'elenco fittizio all'indice dell'elenco reale def toSourceIndex(self, index): return self.filter_proxy_model.mapToSource(index).row()
class QuickSettingsDocker(DockWidget): # Init the docker def __init__(self): super(QuickSettingsDocker, self).__init__() # make base-widget and layout widget = QWidget() layout = QVBoxLayout() widget.setLayout(layout) self.setWindowTitle("Quick Settings Docker") tabWidget = QTabWidget() self.brushSizeTableView = QListView() self.brushSizeTableView.setViewMode(QListView.IconMode) self.brushSizeTableView.setMovement(QListView.Static) self.brushSizeTableView.setResizeMode(QListView.Adjust) self.brushSizeTableView.setUniformItemSizes(True) self.brushSizeTableView.setSelectionMode(QListView.SingleSelection) self.brushOpacityTableView = QListView() self.brushOpacityTableView.setViewMode(QListView.IconMode) self.brushOpacityTableView.setMovement(QListView.Static) self.brushOpacityTableView.setResizeMode(QListView.Adjust) self.brushOpacityTableView.setUniformItemSizes(True) self.brushOpacityTableView.setSelectionMode(QListView.SingleSelection) self.brushFlowTableView = QListView() self.brushFlowTableView.setViewMode(QListView.IconMode) self.brushFlowTableView.setMovement(QListView.Static) self.brushFlowTableView.setResizeMode(QListView.Adjust) self.brushFlowTableView.setUniformItemSizes(True) self.brushFlowTableView.setSelectionMode(QListView.SingleSelection) tabWidget.addTab(self.brushSizeTableView, "Size") tabWidget.addTab(self.brushOpacityTableView, "Opacity") tabWidget.addTab(self.brushFlowTableView, "Flow") layout.addWidget(tabWidget) self.setWidget(widget) # Add the widget to the docker. # amount of columns in each row for all the tables. # We want a grid with possible options to select. # To do this, we'll make a ListView widget and use a standarditemmodel for the entries. # The entries are filled out based on the sizes and opacity lists. # Sizes and opacity lists. The former is half-way copied from ptsai, the latter is based on personal experience of useful opacities. self.sizesList = [ 0.7, 1.0, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 20, 25, 30, 35, 40, 50, 60, 70, 80, 100, 120, 160, 200, 250, 300, 350, 400, 450, 500 ] self.opacityList = [ 0.1, 0.5, 1, 5, 10, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100 ] self.brushSizeModel = QStandardItemModel() self.brushOpacityModel = QStandardItemModel() self.brushFlowModel = QStandardItemModel() self.fillSizesModel() self.fillOpacityModel() # Now we're done filling out our tables, we connect the views to the functions that'll change the settings. self.brushSizeTableView.clicked.connect(self.setBrushSize) self.brushOpacityTableView.clicked.connect(self.setBrushOpacity) self.brushFlowTableView.clicked.connect(self.setBrushFlow) def fillSizesModel(self): # First we empty the old model. We might wanna use this function in the future to fill it with the brushmask of the selected brush, but there's currently no API for recognising changes in the current brush nor is there a way to get its brushmask. self.brushSizeModel.clear() for s in range(len(self.sizesList)): # we're gonna itterate over our list, and make a new item for each entry. # We need to disable a bunch of stuff to make sure people won't do funny things to our entries. item = QStandardItem() item.setCheckable(False) item.setEditable(False) item.setDragEnabled(False) item.setText(str(self.sizesList[s]) + " px") # And from here on we'll make an icon. brushImage = QPixmap(64, 64) img = QImage(64, 64, QImage.Format_RGBA8888) circlePainter = QPainter() img.fill(Qt.transparent) circlePainter.begin(img) brush = QBrush(Qt.SolidPattern) brush.setColor(self.brushSizeTableView.palette().color( QPalette.Text)) circlePainter.setBrush(brush) circlePainter.setPen(QPen(QBrush(Qt.transparent), 0)) brushSize = max(min(self.sizesList[s], 64), 1) brushSize = brushSize * 0.5 circlePainter.drawEllipse(QPointF(32, 32), brushSize, brushSize) circlePainter.end() brushImage = QPixmap.fromImage(img) # now we're done with drawing the icon, so we set it on the item. item.setIcon(QIcon(brushImage)) self.brushSizeModel.appendRow(item) self.brushSizeTableView.setModel(self.brushSizeModel) def fillOpacityModel(self): self.brushOpacityModel.clear() self.brushFlowModel.clear() for s in range(len(self.opacityList)): # we're gonna itterate over our list, and make a new item for each entry. item = QStandardItem() item.setCheckable(False) item.setEditable(False) item.setDragEnabled(False) item.setText(str(self.opacityList[s]) + " %") brushImage = QPixmap(64, 64) img = QImage(64, 64, QImage.Format_RGBA8888) circlePainter = QPainter() img.fill(Qt.transparent) circlePainter.begin(img) brush = QBrush(Qt.SolidPattern) brush.setColor(self.brushSizeTableView.palette().color( QPalette.Text)) circlePainter.setBrush(brush) circlePainter.setPen(QPen(QBrush(Qt.transparent), 0)) circlePainter.setOpacity(float(self.opacityList[s]) / 100.0) circlePainter.drawEllipse(QPointF(32, 32), 32, 32) circlePainter.end() brushImage = QPixmap.fromImage(img) item.setIcon(QIcon(brushImage)) # the flow and opacity models will use virtually the same items, but Qt would like us to make sure we understand # these are not really the same items, so hence the clone. itemFlow = item.clone() self.brushOpacityModel.appendRow(item) self.brushFlowModel.appendRow(itemFlow) self.brushOpacityTableView.setModel(self.brushOpacityModel) self.brushFlowTableView.setModel(self.brushFlowModel) def canvasChanged(self, canvas): pass @pyqtSlot('QModelIndex') def setBrushSize(self, index): i = index.row() brushSize = self.sizesList[i] if Application.activeWindow() and len( Application.activeWindow().views()) > 0: Application.activeWindow().views()[0].setBrushSize(brushSize) @pyqtSlot('QModelIndex') def setBrushOpacity(self, index): i = index.row() brushOpacity = float(self.opacityList[i]) / 100.0 if Application.activeWindow() and len( Application.activeWindow().views()) > 0: Application.activeWindow().views()[0].setPaintingOpacity( brushOpacity) @pyqtSlot('QModelIndex') def setBrushFlow(self, index): i = index.row() brushOpacity = float(self.opacityList[i]) / 100.0 if Application.activeWindow() and len( Application.activeWindow().views()) > 0: Application.activeWindow().views()[0].setPaintingFlow(brushOpacity)
class AttrsUI(object): def __init__(self, window, uaclient): self.window = window self.uaclient = uaclient self.model = QStandardItemModel() self.window.ui.attrView.setModel(self.model) self.window.ui.attrView.doubleClicked.connect(self.edit_attr) self.model.itemChanged.connect(self.edit_attr_finished) self.window.ui.attrView.header().setSectionResizeMode(1) self.window.ui.treeView.activated.connect(self.show_attrs) self.window.ui.treeView.clicked.connect(self.show_attrs) self.window.ui.attrRefreshButton.clicked.connect(self.show_attrs) # Context menu self.window.ui.attrView.setContextMenuPolicy(Qt.CustomContextMenu) self.window.ui.attrView.customContextMenuRequested.connect(self.showContextMenu) copyaction = QAction("&Copy Value", self.model) copyaction.triggered.connect(self._copy_value) self._contextMenu = QMenu() self._contextMenu.addAction(copyaction) def _check_edit(self, item): """ filter only element we want to edit. take either idx eller item as argument """ if item.column() != 1: return False name_item = self.model.item(item.row(), 0) if name_item.text() != "Value": return False return True def edit_attr(self, idx): if not self._check_edit(idx): return attritem = self.model.item(idx.row(), 0) if attritem.text() == "Value": self.window.ui.attrView.edit(idx) def edit_attr_finished(self, item): if not self._check_edit(item): return try: var = item.data() val = item.text() var = string_to_variant(val, var.VariantType) self.current_node.set_value(var) except Exception as ex: self.window.show_error(ex) raise finally: dv = self.current_node.get_data_value() item.setText(variant_to_string(dv.Value)) name_item = self.model.item(item.row(), 0) name_item.child(0, 1).setText(val_to_string(dv.ServerTimestamp)) name_item.child(1, 1).setText(val_to_string(dv.SourceTimestamp)) def showContextMenu(self, position): item = self.get_current_item() if item: self._contextMenu.exec_(self.window.ui.attrView.mapToGlobal(position)) def get_current_item(self, col_idx=0): idx = self.window.ui.attrView.currentIndex() return self.model.item(idx.row(), col_idx) def _copy_value(self, position): it = self.get_current_item(1) if it: QApplication.clipboard().setText(it.text()) def clear(self): self.model.clear() def show_attrs(self, idx): if not isinstance(idx, QModelIndex): idx = None self.current_node = self.window.get_current_node(idx) self.model.clear() if self.current_node: self._show_attrs(self.current_node) self.window.ui.attrView.expandAll() def _show_attrs(self, node): try: attrs = self.uaclient.get_all_attrs(node) except Exception as ex: self.window.show_error(ex) raise self.model.setHorizontalHeaderLabels(['Attribute', 'Value', 'DataType']) for name, dv in attrs: if name == "DataType": if isinstance(dv.Value.Value.Identifier, int) and dv.Value.Value.Identifier < 63: string = ua.DataType_to_VariantType(dv.Value.Value).name elif dv.Value.Value.Identifier in ua.ObjectIdNames: string = ua.ObjectIdNames[dv.Value.Value.Identifier] else: string = dv.Value.Value.to_string() elif name in ("AccessLevel", "UserAccessLevel"): string = ",".join([e.name for e in ua.int_to_AccessLevel(dv.Value.Value)]) elif name in ("WriteMask", "UserWriteMask"): string = ",".join([e.name for e in ua.int_to_WriteMask(dv.Value.Value)]) elif name in ("EventNotifier"): string = ",".join([e.name for e in ua.int_to_EventNotifier(dv.Value.Value)]) else: string = variant_to_string(dv.Value) name_item = QStandardItem(name) vitem = QStandardItem(string) vitem.setData(dv.Value) self.model.appendRow([name_item, vitem, QStandardItem(dv.Value.VariantType.name)]) if name == "Value": string = val_to_string(dv.ServerTimestamp) name_item.appendRow([QStandardItem("Server Timestamp"), QStandardItem(string), QStandardItem(ua.VariantType.DateTime.name)]) string = val_to_string(dv.SourceTimestamp) name_item.appendRow([QStandardItem("Source Timestamp"), QStandardItem(string), QStandardItem(ua.VariantType.DateTime.name)])
class DataPanel(QSplitter): def __init__(self, app): super(DataPanel, self).__init__(app) self.app = app self.data = {} self.setOrientation(Qt.Horizontal) self._key_list_model = QStandardItemModel(0, 1) self.key_lists = DwarfListView(parent=self.app) self.key_lists.setHeaderHidden(True) self.key_lists.setModel(self._key_list_model) self.key_lists.selectionModel().selectionChanged.connect( self.item_selected) self.key_lists.setContextMenuPolicy(Qt.CustomContextMenu) self.key_lists.customContextMenuRequested.connect( self._on_context_menu) self.addWidget(self.key_lists) self.editor = QPlainTextEdit() self.addWidget(self.editor) self.hex_view = HexEditor(self.app) self.hex_view.have_context_menu = False self.hex_view.setVisible(False) self.addWidget(self.hex_view) #self.setStretchFactor(0, 8) self.setStretchFactor(1, 4) self.setStretchFactor(2, 4) def clear(self): self._key_list_model.clear() self.editor.setPlainText('') self.hex_view.clear_panel() def append_data(self, data_type, key, text_data): if key not in self.data: self._key_list_model.appendRow([QStandardItem(key)]) self.data[key] = [data_type, text_data] def item_selected(self, item1, item2): item = self._key_list_model.itemFromIndex(item1.indexes()[0]) if self.data[item.text()][0] == 'plain': self.hex_view.setVisible(False) self.editor.setVisible(True) self.editor.setPlainText(self.data[item.text()][1]) else: data = self.data[item.text()][1] try: as_tx = data.decode('utf8') self.editor.setVisible(True) self.editor.setPlainText(as_tx) except: self.editor.setVisible(False) self.hex_view.setVisible(True) self.hex_view.bytes_per_line = 16 self.hex_view.set_data(data) def _on_context_menu(self, pos): context_menu = QMenu(self) index = self.key_lists.indexAt(pos).row() if index != -1: context_menu.addAction('Clear', self.clear) global_pt = self.key_lists.mapToGlobal(pos) context_menu.exec(global_pt)
class StackImages(QWidget): def __init__(self, fits_file, settings): QWidget.__init__(self) self.fits_file = fits_file self.ui = Ui_StackImages() self.ui.setupUi(self) self.settings = settings self.degrees = 0. # TODO self.files_model = QStandardItemModel() self.files_model.setHorizontalHeaderLabels(['File', 'Quality', 'Align']) self.ui.files.setModel(self.files_model) self.__add_file_to_model(fits_file) self.plot = QtCommons.nestWidget(self.ui.plot, QImPlotWidget(self.__files_data()[0]['data'], cmap='gray')) self.__set_ref(0) self.toolbar = QToolBar() self.add = self.toolbar.addAction('Add', lambda: open_files_sticky('Open FITS Images',FITS_IMG_EXTS, self.__open, settings, IMPORT_IMG, parent=self )) self.remove = self.toolbar.addAction('Remove', self.__remove_selected_rows) self.reference_action = self.toolbar.addAction('Reference', lambda: self.__set_ref(self.ui.files.selectionModel().selectedRows()[0].row() ) ) self.toolbar.addAction('Select alignment region', lambda: self.plot.add_rectangle_selector('select_align', self.__alignment_region_selected)) self.toolbar.addAction('Rotate', lambda: self.rotate_dialog.show() ) self.ui.files.selectionModel().selectionChanged.connect(lambda sel, unsel: self.__selection_changed() ) self.ui.files.clicked.connect(lambda index: self.__draw_image(index.row())) #self.accepted.connect(self.stack) self.__selection_changed() def __selection_changed(self): sel = len(self.ui.files.selectionModel().selectedRows()) self.remove.setEnabled(sel) self.reference_action.setEnabled(sel == 1) def __draw_image(self,index): image_view = self.plot.axes_image image_view.set_data(self.files_model.item(index).data()['data']) image_view.figure.canvas.draw() def __open(self, files): existing_files = [d['file'] for d in self.__files_data()] progress = QProgressDialog("Loading files", None, 0, len(files), self); progress.setWindowModality(Qt.WindowModal); progress.show() for index, file in enumerate(files): progress.setValue(index+1) QApplication.instance().processEvents() if file not in existing_files: self.__add_file(fits.open(file)) def __row_index(self, data): return [i for i, d in enumerate(self.__files_data()) if d['file'] == data['file']][0] def __add_file_to_model(self, fits_file): item = QStandardItem(os.path.basename(fits_file.filename())) data = fits_file[0].data data = scipy.ndimage.interpolation.rotate(data, self.degrees, reshape=True, order=5, mode='constant') spatial = data.sum(1) profile = data.sum(0) roots = UnivariateSpline(range(0, len(spatial)), spatial-np.max(spatial)/2, s=0.2, k=3).roots() quality = roots[1]-roots[0] item.setData({'file': fits_file.filename(), 'fits': fits_file, 'data': data, 'spatial': spatial, 'profile': profile, 'quality': quality}) offset = QStandardItem('N/A') # TODO quality_item = QStandardItem("") self.files_model.appendRow([item, quality_item, offset]) return item def __add_file(self, fits_file): item = self.__add_file_to_model(fits_file) if self.files_model.rowCount() == 1: self.__set_ref(0) else: self.align(item.data()) self.__update_qualities() def __update_qualities(self): qualities = [d['quality'] for d in self.__files_data()] self.qualities = (min(qualities), max(qualities)) for index in range(0, self.files_model.rowCount()): self.files_model.item(index, 1).setText("{}%".format(self.__quality_percent(self.files_model.item(index).data()['quality']))) def __quality_percent(self, quality): return 100. - (100. * (quality-self.qualities[0]) / (self.qualities[1]-self.qualities[0])) def align(self, data): if data['file'] == self.reference['file']: self.__update_offset(data, (0, 0)) return offset_range = lambda n: range(1-int(n), int(n)-1) offsets = lambda name, indexes: [ (pearsonr(self.reference[name][indexes[0]:indexes[1]], data[name][indexes[0]-offset:indexes[1]-offset] )[0], offset) for offset in offset_range(indexes[0]) ] x_offset = sorted(offsets('profile', self.reference_indexes['h']), key=lambda x: x[0])[-1] y_offset = sorted(offsets('spatial', self.reference_indexes['v']), key=lambda y: y[0])[-1] self.__update_offset(data, (x_offset[1], y_offset[1])) def __update_offset(self, data, offset): row = self.__row_index(data) self.files_model.item(row, 2).setText('{}, {}'.format(offset[0], offset[1])) data.update({'offset': {'x': offset[0], 'y': offset[1]}}) self.files_model.item(row).setData(data) def __files_data(self): return [self.files_model.item(i).data() for i in range(0, self.files_model.rowCount())] def __remove_selected_rows(self): for row in sorted([r.row() for r in self.ui.files.selectionModel().selectedRows()], reverse=True): self.files_model.removeRows(row, 1) if self.files_model.rowCount() == 0: return if len([f for f in self.__files_data() if f['file'] == self.reference['file']]) == 0: self.__set_ref(0) def __set_ref(self, index): self.reference = self.files_model.item(index).data() self.rotate_dialog = RotateImageDialog(self.fits_file, 0) self.rotate_dialog.rotated.connect(self.__rotated) indexes = lambda data: (int(len(data)/4), int(len(data)/4*3)) self.__set_reference_indexes(indexes(self.reference['profile']), indexes(self.reference['spatial']) ) #self.reference_indexes = { 'h': indexes(self.reference['profile']), 'v': indexes(self.reference['spatial']) } for data in self.__files_data() : self.align(data) def __rotated(self): self.degrees = self.rotate_dialog.degrees() for index in range(0, self.files_model.rowCount()): self.files_model.removeRow(index) self.__add_file(self.fits_file) self.__draw_image(0) def __alignment_region_selected(self, eclick, erelease): self.__set_reference_indexes((eclick.xdata, erelease.xdata), (eclick.ydata, erelease.ydata)) def __set_reference_indexes(self, x, y): self.reference_indexes = { 'h': x, 'v': y } self.__draw_reference_rect() def __draw_reference_rect(self): self.plot.rm_element('reference_indexes') x, y = self.reference_indexes['h'], self.reference_indexes['v'] rect = Rectangle((x[0], y[0]), x[1]-x[0], y[1]-y[0], fill=True, alpha=0.3, color='green') self.plot.figure.axes[0].add_artist(rect) self.plot.add_element(rect, 'reference_indexes') self.plot.figure.canvas.draw() def stack(self): dataset = self.__files_data() median = MedianStacker(dataset).median() self.fits_file[0].data = median
class Explorer(QWidget): """ This class implements the diagram predicate node explorer. """ def __init__(self, mainwindow): """ Initialize the Explorer. :type mainwindow: MainWindow """ super().__init__(mainwindow) self.expanded = {} self.searched = {} self.scrolled = {} self.mainview = None self.iconA = QIcon(':/icons/treeview-icon-attribute') self.iconC = QIcon(':/icons/treeview-icon-concept') self.iconD = QIcon(':/icons/treeview-icon-datarange') self.iconI = QIcon(':/icons/treeview-icon-instance') self.iconR = QIcon(':/icons/treeview-icon-role') self.iconV = QIcon(':/icons/treeview-icon-value') self.search = StringField(self) self.search.setAcceptDrops(False) self.search.setClearButtonEnabled(True) self.search.setPlaceholderText('Search...') self.search.setFixedHeight(30) self.model = QStandardItemModel(self) self.proxy = QSortFilterProxyModel(self) self.proxy.setDynamicSortFilter(False) self.proxy.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxy.setSortCaseSensitivity(Qt.CaseSensitive) self.proxy.setSourceModel(self.model) self.view = ExplorerView(mainwindow, self) self.view.setModel(self.proxy) self.mainLayout = QVBoxLayout(self) self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.addWidget(self.search) self.mainLayout.addWidget(self.view) self.setContentsMargins(0, 0, 0, 0) self.setMinimumWidth(216) self.setMinimumHeight(160) connect(self.view.doubleClicked, self.itemDoubleClicked) connect(self.view.pressed, self.itemPressed) connect(self.view.collapsed, self.itemCollapsed) connect(self.view.expanded, self.itemExpanded) connect(self.search.textChanged, self.filterItem) #################################################################################################################### # # # EVENTS # # # #################################################################################################################### def paintEvent(self, paintEvent): """ This is needed for the widget to pick the stylesheet. :type paintEvent: QPaintEvent """ option = QStyleOption() option.initFrom(self) painter = QPainter(self) style = self.style() style.drawPrimitive(QStyle.PE_Widget, option, painter, self) #################################################################################################################### # # # SLOTS # # # #################################################################################################################### @pyqtSlot('QGraphicsItem') def add(self, item): """ Add a node in the tree view. :type item: AbstractItem """ if item.node and item.predicate: parent = self.parentFor(item) if not parent: parent = ParentItem(item) parent.setIcon(self.iconFor(item)) self.model.appendRow(parent) self.proxy.sort(0, Qt.AscendingOrder) child = ChildItem(item) child.setData(item) parent.appendRow(child) self.proxy.sort(0, Qt.AscendingOrder) @pyqtSlot(str) def filterItem(self, key): """ Executed when the search box is filled with data. :type key: str """ if self.mainview: self.proxy.setFilterFixedString(key) self.proxy.sort(Qt.AscendingOrder) self.searched[self.mainview] = key @pyqtSlot('QModelIndex') def itemCollapsed(self, index): """ Executed when an item in the tree view is collapsed. :type index: QModelIndex """ if self.mainview: if self.mainview in self.expanded: item = self.model.itemFromIndex(self.proxy.mapToSource(index)) expanded = self.expanded[self.mainview] expanded.remove(item.text()) @pyqtSlot('QModelIndex') def itemDoubleClicked(self, index): """ Executed when an item in the tree view is double clicked. :type index: QModelIndex """ item = self.model.itemFromIndex(self.proxy.mapToSource(index)) node = item.data() if node: self.selectNode(node) self.focusNode(node) @pyqtSlot('QModelIndex') def itemExpanded(self, index): """ Executed when an item in the tree view is expanded. :type index: QModelIndex """ if self.mainview: item = self.model.itemFromIndex(self.proxy.mapToSource(index)) if self.mainview not in self.expanded: self.expanded[self.mainview] = set() expanded = self.expanded[self.mainview] expanded.add(item.text()) @pyqtSlot('QModelIndex') def itemPressed(self, index): """ Executed when an item in the tree view is clicked. :type index: QModelIndex """ item = self.model.itemFromIndex(self.proxy.mapToSource(index)) node = item.data() if node: self.selectNode(node) @pyqtSlot('QGraphicsItem') def remove(self, item): """ Remove a node from the tree view. :type item: AbstractItem """ if item.node and item.predicate: parent = self.parentFor(item) if parent: child = self.childFor(parent, item) if child: parent.removeRow(child.index().row()) if not parent.rowCount(): self.model.removeRow(parent.index().row()) #################################################################################################################### # # # AUXILIARY METHODS # # # #################################################################################################################### @staticmethod def childFor(parent, node): """ Search the item representing this node among parent children. :type parent: QStandardItem :type node: AbstractNode """ key = ChildItem.key(node) for i in range(parent.rowCount()): child = parent.child(i) if child.text() == key: return child return None def parentFor(self, node): """ Search the parent element of the given node. :type node: AbstractNode :rtype: QStandardItem """ key = ParentItem.key(node) for i in self.model.findItems(key, Qt.MatchExactly): n = i.child(0).data() if node.item is n.item: return i return None #################################################################################################################### # # # INTERFACE # # # #################################################################################################################### def browse(self, view): """ Set the widget to inspect the given view. :type view: MainView """ self.reset() self.mainview = view if self.mainview: scene = self.mainview.scene() connect(scene.index.sgnItemAdded, self.add) connect(scene.index.sgnItemRemoved, self.remove) for item in scene.index.nodes(): self.add(item) if self.mainview in self.expanded: expanded = self.expanded[self.mainview] for i in range(self.model.rowCount()): item = self.model.item(i) index = self.proxy.mapFromSource( self.model.indexFromItem(item)) self.view.setExpanded(index, item.text() in expanded) key = '' if self.mainview in self.searched: key = self.searched[self.mainview] self.search.setText(key) if self.mainview in self.scrolled: rect = self.rect() item = first(self.model.findItems( self.scrolled[self.mainview])) for i in range(self.model.rowCount()): self.view.scrollTo( self.proxy.mapFromSource( self.model.indexFromItem(self.model.item(i)))) index = self.proxy.mapToSource( self.view.indexAt(rect.topLeft())) if self.model.itemFromIndex(index) is item: break def reset(self): """ Clear the widget from inspecting the current view. """ if self.mainview: rect = self.rect() item = self.model.itemFromIndex( self.proxy.mapToSource(self.view.indexAt(rect.topLeft()))) if item: node = item.data() key = ParentItem.key(node) if node else item.text() self.scrolled[self.mainview] = key else: self.scrolled.pop(self.mainview, None) try: scene = self.mainview.scene() disconnect(scene.index.sgnItemAdded, self.add) disconnect(scene.index.sgnItemRemoved, self.remove) except RuntimeError: pass finally: self.mainview = None self.model.clear() def flush(self, view): """ Flush the cache of the given mainview. :type view: MainView """ self.expanded.pop(view, None) self.searched.pop(view, None) self.scrolled.pop(view, None) def iconFor(self, node): """ Returns the icon for the given node. :type node: """ if node.item is Item.AttributeNode: return self.iconA if node.item is Item.ConceptNode: return self.iconC if node.item is Item.ValueDomainNode: return self.iconD if node.item is Item.ValueRestrictionNode: return self.iconD if node.item is Item.IndividualNode: if node.identity is Identity.Instance: return self.iconI if node.identity is Identity.Value: return self.iconV if node.item is Item.RoleNode: return self.iconR def focusNode(self, node): """ Focus the given node in the main view. :type node: AbstractNode """ if self.mainview: self.mainview.centerOn(node) def selectNode(self, node): """ Select the given node in the main view. :type node: AbstractNode """ if self.mainview: scene = self.mainview.scene() scene.clearSelection() node.setSelected(True)
class ContextPanel(QTabWidget): # consts CONTEXT_TYPE_NATIVE = 0 CONTEXT_TYPE_JAVA = 1 onShowMemoryRequest = pyqtSignal(str, name='onShowMemoryRequest') def __init__(self, parent=None): super(ContextPanel, self).__init__(parent=parent) self._app_window = parent self.setAutoFillBackground(True) self._app_window.dwarf.onContextChanged.connect( self._on_context_changed) self._nativectx_model = QStandardItemModel(0, 4) self._nativectx_model.setHeaderData(0, Qt.Horizontal, 'Reg') self._nativectx_model.setHeaderData(0, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._nativectx_model.setHeaderData(1, Qt.Horizontal, 'Value') self._nativectx_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._nativectx_model.setHeaderData(2, Qt.Horizontal, 'Decimal') self._nativectx_model.setHeaderData(3, Qt.Horizontal, 'Telescope') self._nativectx_list = DwarfListView() self._nativectx_list.setModel(self._nativectx_model) self._nativectx_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self._nativectx_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self._nativectx_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self._nativectx_list.setContextMenuPolicy(Qt.CustomContextMenu) self._nativectx_list.customContextMenuRequested.connect( self._on_native_contextmenu) self._javactx_model = QStandardItemModel(0, 3) self._javactx_model.setHeaderData(0, Qt.Horizontal, 'Argument') self._javactx_model.setHeaderData(0, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._javactx_model.setHeaderData(1, Qt.Horizontal, 'Class') self._javactx_model.setHeaderData(2, Qt.Horizontal, 'Value') self._javactx_list = DwarfListView() self._javactx_list.setModel(self._javactx_model) self._javactx_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self._javactx_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self._javactx_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self._javactx_list.setContextMenuPolicy(Qt.CustomContextMenu) self._javactx_list.customContextMenuRequested.connect( self._on_java_contextmenu) self.addTab(self._nativectx_list, 'Native') self.show_context_tab('Native') # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def clear(self): self._nativectx_list.clear() self._javactx_list.clear() def set_context(self, ptr, context_type, context): if isinstance(context, str): context = json.loads(context) if context_type == ContextPanel.CONTEXT_TYPE_NATIVE: self._nativectx_list.clear() self._set_native_context(ptr, context) elif context_type == ContextPanel.CONTEXT_TYPE_JAVA: self._javactx_list.clear() self._set_java_context(ptr, context) else: raise Exception('unknown context type') def have_context(self): return self.count() > 0 def show_context_tab(self, tab_name): index = 0 tab_name = tab_name.join(tab_name.split()).lower() if tab_name == 'native': index = self.indexOf(self._nativectx_list) elif tab_name == 'java': index = self.indexOf(self._javactx_list) if self.count() > 0: self.setCurrentIndex(index) def _set_native_context(self, ptr, context): if self.indexOf(self._nativectx_list) == -1: self.addTab(self._nativectx_list, 'Native') self.show_context_tab('Native') else: self.show_context_tab('Native') context_ptr = ptr sorted_regs = self.get_sort_order() for register in sorted(context, key=lambda x: sorted_regs[x] if x in sorted_regs else len(sorted_regs)): reg_name = QStandardItem() reg_name.setTextAlignment(Qt.AlignCenter) if context[register]['isValidPointer']: reg_name.setData(context_ptr, Qt.UserRole + 1) value_x = QStandardItem() if context[register]['isValidPointer']: value_x.setForeground(Qt.red) value_dec = QStandardItem() telescope = QStandardItem() reg_name.setText(register) if context[register] is not None: str_fmt = '0x{0:x}' if self._nativectx_list.uppercase_hex: str_fmt = '0x{0:X}' value_x.setText( str_fmt.format(int(context[register]['value'], 16))) value_dec.setText('{0:d}'.format( int(context[register]['value'], 16))) if context[register]['isValidPointer']: if 'telescope' in context[register] and context[register][ 'telescope'] is not None: telescope = QStandardItem() telescope_value = str( context[register]['telescope'][1]).replace( '\n', ' ') if len(telescope_value) > 50: telescope_value = telescope_value[:50] + '...' telescope.setText(telescope_value) if context[register]['telescope'][0] == 2: telescope.setData( context[register]['telescope'][1], Qt.UserRole + 1) if context[register]['telescope'][0] == 0: telescope.setForeground(Qt.darkGreen) elif context[register]['telescope'][0] == 2: telescope.setForeground(Qt.white) elif context[register]['telescope'][0] != 1: telescope.setForeground(Qt.darkGray) self._nativectx_model.appendRow( [reg_name, value_x, value_dec, telescope]) self._nativectx_list.resizeColumnToContents(0) def _set_java_context(self, ptr, context): if self.indexOf(self._javactx_list) == -1: self.addTab(self._javactx_list, 'Java') self.show_context_tab('Java') else: self.show_context_tab('Java') for arg in context: _arg = QStandardItem() _arg.setText(arg) _class = QStandardItem() _class.setText(context[arg]['className']) if isinstance(context[arg]['handle'], str): _class.setForeground(Qt.lightGray) _value = QStandardItem() if 'arg' not in context[arg] or context[arg]['arg'] is None: _value.setText('null') _value.setForeground(Qt.gray) else: _value.setText(context[arg]['arg']) self._javactx_model.appendRow([_arg, _class, _value]) self._javactx_list.resizeColumnToContents(0) self._javactx_list.resizeColumnToContents(1) def get_sort_order(self): reg_order = [] if self._app_window.dwarf.arch == 'arm': # arm reg_order = [ 'r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'sp', 'lr', 'sb', 'sl', 'fp', 'ip', 'pc', 'cspr' ] elif self._app_window.dwarf.arch == 'arm64': reg_order = [ 'x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10', 'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18', 'x19', 'x20', 'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27', 'x28', 'x29', 'x30', 'w0', 'w1', 'w2', 'w3', 'w4', 'w5', 'w6', 'w7', 'w8', 'w9', 'w10', 'w11', 'w12', 'w13', 'w14', 'w15', 'w16', 'w17', 'w18', 'w19', 'w20', 'w21', 'w22', 'w23', 'w24', 'w25', 'w26', 'w27', 'w28', 'w29', 'w30', 'sp', 'lr', 'fp', 'wsp', 'wzr', 'xzr', 'nzcv', 'ip0', 'ip1', 's0', 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11', 's12', 's13', 's14', 's15', 's16', 's17', 's18', 's19', 's20', 's21', 's22', 's23', 's24', 's25', 's26', 's27', 's28', 's29', 's30', 's31', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'd10', 'd11', 'd12', 'd13', 'd14', 'd15', 'd16', 'd17', 'd18', 'd19', 'd20', 'd21', 'd22', 'd23', 'd24', 'd25', 'd26', 'd27', 'd28', 'd29', 'd30', 'd31', 'q0', 'q1', 'q2', 'q3', 'q4', 'q5', 'q6', 'q7', 'q8', 'q9', 'q10', 'q11', 'q12', 'q13', 'q14', 'q15', 'q16', 'q17', 'q18', 'q19', 'q20', 'q21', 'q22', 'q23', 'q24', 'q25', 'q26', 'q27', 'q28', 'q29', 'q30', 'q31', 'sp', 'lr', 'sb', 'sl', 'fp', 'ip', 'pc', 'cspr' ] elif self._app_window.dwarf.arch == 'ia32': reg_order = [ 'eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'esp', 'r8d', 'r9d', 'r10d', 'r11d', 'r12d', 'r13d', 'r14d', 'r15d', 'ebp', 'eip', 'sp', 'pc' ] elif self._app_window.dwarf.arch == 'x64': # x64 reg_order = [ 'rax', 'rbx', 'rcx', 'rdx', 'rsi', 'rdi', 'rbp', 'rsp', 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'esp', 'ebp', 'rip', 'eip', 'sp', 'pc' ] sorted_regs = {b: i for i, b in enumerate(reg_order)} return sorted_regs # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _on_native_contextmenu(self, pos): index = self._nativectx_list.indexAt(pos).row() glbl_pt = self._nativectx_list.mapToGlobal(pos) context_menu = QMenu(self) if index != -1: item = self._nativectx_model.item(index, 1) dec = self._nativectx_model.item(index, 2) telescope = self._nativectx_model.item(index, 3) # show contextmenu if self._nativectx_model.item(index, 0).data(Qt.UserRole + 1): context_menu.addAction( 'Jump to {0}'.format(item.text()), lambda: self._app_window.jump_to_address(item.text())) context_menu.addSeparator() # copy menu context_sub_menu = QMenu('Copy', context_menu) context_sub_menu.addAction( 'Value', lambda: utils.copy_str_to_clipboard(item.text())) if dec.text(): context_sub_menu.addAction( 'Decimal', lambda: utils.copy_str_to_clipboard(dec.text())) if telescope.text(): context_sub_menu.addAction( 'Telescope', lambda: utils.copy_str_to_clipboard(telescope.text())) context_menu.addMenu(context_sub_menu) context_menu.exec_(glbl_pt) def _on_java_contextmenu(self, pos): index = self._javactx_list.indexAt(pos).row() glbl_pt = self._javactx_list.mapToGlobal(pos) context_menu = QMenu(self) if index != -1: # show contextmenu argument = self._javactx_model.item(index, 1) _class = self._javactx_model.item(index, 2) value = self._javactx_model.item(index, 3) context_sub_menu = QMenu('Copy', context_menu) context_sub_menu.addAction( 'Argument', lambda: utils.copy_str_to_clipboard(argument.text())) if _class.text(): context_sub_menu.addAction( 'Class', lambda: utils.copy_str_to_clipboard(_class.text())) if value.text(): context_sub_menu.addAction( 'Value', lambda: utils.copy_str_to_clipboard(value.text())) context_menu.addMenu(context_sub_menu) context_menu.exec_(glbl_pt) def _on_context_changed(self, reg_name, reg_val): x_in = 0 for c in reg_val: if c.lower() not in '1234567890abcdef': if c.lower() == 'x' and x_in == 0: x_in += 1 continue self._app_window.dwarf.onLogToConsole.emit( 'error: invalid reg_value: ' + reg_val + ' - expected dec/hex') return if isinstance(reg_val, str) and reg_val.startswith('0x'): try: reg_val = int(reg_val, 16) except ValueError: self._app_window.dwarf.onLogToConsole.emit( 'error: invalid reg_value: ' + reg_val + ' - expected dec/hex') return try: reg_val = int(reg_val) except ValueError: self._app_window.dwarf.onLogToConsole.emit( 'error: invalid reg_value: ' + reg_val + ' - expected dec/hex') return reg_val = hex(reg_val) was_found, find_result = self._nativectx_list.contains_text( reg_name, True, True, True) if was_found: if len(find_result) == 1: find_result = find_result[0] if self._nativectx_model.item(find_result[0], 0).text() == reg_name: str_fmt = '0x{0:x}' if self._nativectx_list.uppercase_hex: str_fmt = '0x{0:X}' value_x = str_fmt.format(int(reg_val, 16)) value_dec = '{0:d}'.format(int(reg_val, 16)) self._nativectx_model.item(find_result[0], 1).setText(value_x) self._nativectx_model.item(find_result[0], 2).setText(value_dec) self._nativectx_model.item(find_result[0], 3).setText("")
class comics_project_manager_docker(DockWidget): setupDictionary = {} stringName = i18n("Comics Manager") projecturl = None def __init__(self): super().__init__() self.setWindowTitle(self.stringName) # Setup layout: base = QHBoxLayout() widget = QWidget() widget.setLayout(base) baseLayout = QSplitter() base.addWidget(baseLayout) self.setWidget(widget) buttonLayout = QVBoxLayout() buttonBox = QWidget() buttonBox.setLayout(buttonLayout) baseLayout.addWidget(buttonBox) # Comic page list and pages model self.comicPageList = QListView() self.comicPageList.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.comicPageList.setDragEnabled(True) self.comicPageList.setDragDropMode(QAbstractItemView.InternalMove) self.comicPageList.setDefaultDropAction(Qt.MoveAction) self.comicPageList.setAcceptDrops(True) self.comicPageList.setItemDelegate(comic_page_delegate()) self.pagesModel = QStandardItemModel() self.comicPageList.doubleClicked.connect(self.slot_open_page) self.comicPageList.setIconSize(QSize(128, 128)) # self.comicPageList.itemDelegate().closeEditor.connect(self.slot_write_description) self.pagesModel.layoutChanged.connect(self.slot_write_config) self.pagesModel.rowsInserted.connect(self.slot_write_config) self.pagesModel.rowsRemoved.connect(self.slot_write_config) self.pagesModel.rowsMoved.connect(self.slot_write_config) self.comicPageList.setModel(self.pagesModel) pageBox = QWidget() pageBox.setLayout(QVBoxLayout()) zoomSlider = QSlider(Qt.Horizontal, None) zoomSlider.setRange(1, 8) zoomSlider.setValue(4) zoomSlider.setTickInterval(1) zoomSlider.setMinimumWidth(10) zoomSlider.valueChanged.connect(self.slot_scale_thumbnails) self.projectName = Elided_Text_Label() pageBox.layout().addWidget(self.projectName) pageBox.layout().addWidget(zoomSlider) pageBox.layout().addWidget(self.comicPageList) baseLayout.addWidget(pageBox) self.btn_project = QToolButton() self.btn_project.setPopupMode(QToolButton.MenuButtonPopup) self.btn_project.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) menu_project = QMenu() self.action_new_project = QAction(i18n("New Project"), self) self.action_new_project.triggered.connect(self.slot_new_project) self.action_load_project = QAction(i18n("Open Project"), self) self.action_load_project.triggered.connect(self.slot_open_config) menu_project.addAction(self.action_new_project) menu_project.addAction(self.action_load_project) self.btn_project.setMenu(menu_project) self.btn_project.setDefaultAction(self.action_load_project) buttonLayout.addWidget(self.btn_project) # Settings dropdown with actions for the different settings menus. self.btn_settings = QToolButton() self.btn_settings.setPopupMode(QToolButton.MenuButtonPopup) self.btn_settings.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.action_edit_project_settings = QAction(i18n("Project Settings"), self) self.action_edit_project_settings.triggered.connect( self.slot_edit_project_settings) self.action_edit_meta_data = QAction(i18n("Meta Data"), self) self.action_edit_meta_data.triggered.connect(self.slot_edit_meta_data) self.action_edit_export_settings = QAction(i18n("Export Settings"), self) self.action_edit_export_settings.triggered.connect( self.slot_edit_export_settings) menu_settings = QMenu() menu_settings.addAction(self.action_edit_project_settings) menu_settings.addAction(self.action_edit_meta_data) menu_settings.addAction(self.action_edit_export_settings) self.btn_settings.setDefaultAction(self.action_edit_project_settings) self.btn_settings.setMenu(menu_settings) buttonLayout.addWidget(self.btn_settings) self.btn_settings.setDisabled(True) # Add page drop down with different page actions. self.btn_add_page = QToolButton() self.btn_add_page.setPopupMode(QToolButton.MenuButtonPopup) self.btn_add_page.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.action_add_page = QAction(i18n("Add Page"), self) self.action_add_page.triggered.connect(self.slot_add_new_page_single) self.action_add_template = QAction(i18n("Add Page from Template"), self) self.action_add_template.triggered.connect( self.slot_add_new_page_from_template) self.action_add_existing = QAction(i18n("Add Existing Pages"), self) self.action_add_existing.triggered.connect(self.slot_add_page_from_url) self.action_remove_selected_page = QAction(i18n("Remove Page"), self) self.action_remove_selected_page.triggered.connect( self.slot_remove_selected_page) self.action_resize_all_pages = QAction(i18n("Batch Resize"), self) self.action_resize_all_pages.triggered.connect(self.slot_batch_resize) self.btn_add_page.setDefaultAction(self.action_add_page) self.action_show_page_viewer = QAction(i18n("View Page In Window"), self) self.action_show_page_viewer.triggered.connect( self.slot_show_page_viewer) self.action_scrape_authors = QAction(i18n("Scrape Author Info"), self) self.action_scrape_authors.setToolTip( i18n( "Search for author information in documents and add it to the author list. This does not check for duplicates." )) self.action_scrape_authors.triggered.connect( self.slot_scrape_author_list) self.action_scrape_translations = QAction( i18n("Scrape Text for Translation"), self) self.action_scrape_translations.triggered.connect( self.slot_scrape_translations) actionList = [] menu_page = QMenu() actionList.append(self.action_add_page) actionList.append(self.action_add_template) actionList.append(self.action_add_existing) actionList.append(self.action_remove_selected_page) actionList.append(self.action_resize_all_pages) actionList.append(self.action_show_page_viewer) actionList.append(self.action_scrape_authors) actionList.append(self.action_scrape_translations) menu_page.addActions(actionList) self.btn_add_page.setMenu(menu_page) buttonLayout.addWidget(self.btn_add_page) self.btn_add_page.setDisabled(True) self.comicPageList.setContextMenuPolicy(Qt.ActionsContextMenu) self.comicPageList.addActions(actionList) # Export button that... exports. self.btn_export = QPushButton(i18n("Export Comic")) self.btn_export.clicked.connect(self.slot_export) buttonLayout.addWidget(self.btn_export) self.btn_export.setDisabled(True) self.btn_project_url = QPushButton(i18n("Copy Location")) self.btn_project_url.setToolTip( i18n( "Copies the path of the project to the clipboard. Useful for quickly copying to a file manager or the like." )) self.btn_project_url.clicked.connect(self.slot_copy_project_url) self.btn_project_url.setDisabled(True) buttonLayout.addWidget(self.btn_project_url) self.page_viewer_dialog = comics_project_page_viewer.comics_project_page_viewer( ) Application.notifier().imageSaved.connect( self.slot_check_for_page_update) buttonLayout.addItem( QSpacerItem(0, 0, QSizePolicy.Minimum, QSizePolicy.MinimumExpanding)) """ Open the config file and load the json file into a dictionary. """ def slot_open_config(self): self.path_to_config = QFileDialog.getOpenFileName( caption=i18n("Please select the JSON comic config file."), filter=str(i18n("JSON files") + "(*.json)"))[0] if os.path.exists(self.path_to_config) is True: configFile = open(self.path_to_config, "r", newline="", encoding="utf-16") self.setupDictionary = json.load(configFile) self.projecturl = os.path.dirname(str(self.path_to_config)) configFile.close() self.load_config() """ Further config loading. """ def load_config(self): self.projectName.setMainText( text=str(self.setupDictionary["projectName"])) self.fill_pages() self.btn_settings.setEnabled(True) self.btn_add_page.setEnabled(True) self.btn_export.setEnabled(True) self.btn_project_url.setEnabled(True) """ Fill the pages model with the pages from the pages list. """ def fill_pages(self): self.loadingPages = True self.pagesModel.clear() pagesList = [] if "pages" in self.setupDictionary.keys(): pagesList = self.setupDictionary["pages"] progress = QProgressDialog() progress.setMinimum(0) progress.setMaximum(len(pagesList)) progress.setWindowTitle(i18n("Loading Pages...")) for url in pagesList: absurl = os.path.join(self.projecturl, url) if (os.path.exists(absurl)): #page = Application.openDocument(absurl) page = zipfile.ZipFile(absurl, "r") thumbnail = QImage.fromData(page.read("preview.png")) pageItem = QStandardItem() dataList = self.get_description_and_title( page.read("documentinfo.xml")) if (dataList[0].isspace() or len(dataList[0]) < 1): dataList[0] = os.path.basename(url) pageItem.setText(dataList[0].replace("_", " ")) pageItem.setDragEnabled(True) pageItem.setDropEnabled(False) pageItem.setEditable(False) pageItem.setIcon(QIcon(QPixmap.fromImage(thumbnail))) pageItem.setData(dataList[1], role=CPE.DESCRIPTION) pageItem.setData(url, role=CPE.URL) pageItem.setData(dataList[2], role=CPE.KEYWORDS) pageItem.setData(dataList[3], role=CPE.LASTEDIT) pageItem.setData(dataList[4], role=CPE.EDITOR) pageItem.setToolTip(url) page.close() self.pagesModel.appendRow(pageItem) progress.setValue(progress.value() + 1) progress.setValue(len(pagesList)) self.loadingPages = False """ Function that is triggered by the zoomSlider Resizes the thumbnails. """ def slot_scale_thumbnails(self, multiplier=4): self.comicPageList.setIconSize(QSize(multiplier * 32, multiplier * 32)) """ Function that takes the documentinfo.xml and parses it for the title, subject and abstract tags, to get the title and description. @returns a stringlist with the name on 0 and the description on 1. """ def get_description_and_title(self, string): xmlDoc = ET.fromstring(string) calligra = str("{http://www.calligra.org/DTD/document-info}") name = "" if ET.iselement(xmlDoc[0].find(calligra + 'title')): name = xmlDoc[0].find(calligra + 'title').text if name is None: name = " " desc = "" if ET.iselement(xmlDoc[0].find(calligra + 'subject')): desc = xmlDoc[0].find(calligra + 'subject').text if desc is None or desc.isspace() or len(desc) < 1: if ET.iselement(xmlDoc[0].find(calligra + 'abstract')): desc = xmlDoc[0].find(calligra + 'abstract').text if desc is not None: if desc.startswith("<![CDATA["): desc = desc[len("<![CDATA["):] if desc.startswith("]]>"): desc = desc[:-len("]]>")] keywords = "" if ET.iselement(xmlDoc[0].find(calligra + 'keyword')): keywords = xmlDoc[0].find(calligra + 'keyword').text date = "" if ET.iselement(xmlDoc[0].find(calligra + 'date')): date = xmlDoc[0].find(calligra + 'date').text author = [] if ET.iselement(xmlDoc[1].find(calligra + 'creator-first-name')): string = xmlDoc[1].find(calligra + 'creator-first-name').text if string is not None: author.append(string) if ET.iselement(xmlDoc[1].find(calligra + 'creator-last-name')): string = xmlDoc[1].find(calligra + 'creator-last-name').text if string is not None: author.append(string) if ET.iselement(xmlDoc[1].find(calligra + 'full-name')): string = xmlDoc[1].find(calligra + 'full-name').text if string is not None: author.append(string) return [name, desc, keywords, date, " ".join(author)] """ Scrapes authors from the author data in the document info and puts them into the author list. Doesn't check for duplicates. """ def slot_scrape_author_list(self): listOfAuthors = [] if "authorList" in self.setupDictionary.keys(): listOfAuthors = self.setupDictionary["authorList"] if "pages" in self.setupDictionary.keys(): for relurl in self.setupDictionary["pages"]: absurl = os.path.join(self.projecturl, relurl) page = zipfile.ZipFile(absurl, "r") xmlDoc = ET.fromstring(page.read("documentinfo.xml")) calligra = str("{http://www.calligra.org/DTD/document-info}") authorelem = xmlDoc.find(calligra + 'author') author = {} if ET.iselement(authorelem.find(calligra + 'full-name')): author["nickname"] = str( authorelem.find(calligra + 'full-name').text) if ET.iselement( authorelem.find(calligra + 'creator-first-name')): author["first-name"] = str( authorelem.find(calligra + 'creator-first-name').text) if ET.iselement(authorelem.find(calligra + 'initial')): author["initials"] = str( authorelem.find(calligra + 'initial').text) if ET.iselement(authorelem.find(calligra + 'creator-last-name')): author["last-name"] = str( authorelem.find(calligra + 'creator-last-name').text) if ET.iselement(authorelem.find(calligra + 'email')): author["email"] = str( authorelem.find(calligra + 'email').text) if ET.iselement(authorelem.find(calligra + 'contact')): contact = authorelem.find(calligra + 'contact') contactMode = contact.get("type") if contactMode == "email": author["email"] = str(contact.text) if contactMode == "homepage": author["homepage"] = str(contact.text) if ET.iselement(authorelem.find(calligra + 'position')): author["role"] = str( authorelem.find(calligra + 'position').text) listOfAuthors.append(author) page.close() self.setupDictionary["authorList"] = listOfAuthors """ Edit the general project settings like the project name, concept, pages location, export location, template location, metadata """ def slot_edit_project_settings(self): dialog = comics_project_settings_dialog.comics_project_details_editor( self.projecturl) dialog.setConfig(self.setupDictionary, self.projecturl) if dialog.exec_() == QDialog.Accepted: self.setupDictionary = dialog.getConfig(self.setupDictionary) self.slot_write_config() self.projectName.setMainText( str(self.setupDictionary["projectName"])) """ This allows users to select existing pages and add them to the pages list. The pages are currently not copied to the pages folder. Useful for existing projects. """ def slot_add_page_from_url(self): # get the pages. urlList = QFileDialog.getOpenFileNames( caption=i18n("Which existing pages to add?"), directory=self.projecturl, filter=str(i18n("Krita files") + "(*.kra)"))[0] # get the existing pages list. pagesList = [] if "pages" in self.setupDictionary.keys(): pagesList = self.setupDictionary["pages"] # And add each url in the url list to the pages list and the model. for url in urlList: if self.projecturl not in urlList: newUrl = os.path.join(self.projecturl, self.setupDictionary["pagesLocation"], os.path.basename(url)) shutil.move(url, newUrl) url = newUrl relative = os.path.relpath(url, self.projecturl) if url not in pagesList: page = zipfile.ZipFile(url, "r") thumbnail = QImage.fromData(page.read("preview.png")) dataList = self.get_description_and_title( page.read("documentinfo.xml")) if (dataList[0].isspace() or len(dataList[0]) < 1): dataList[0] = os.path.basename(url) newPageItem = QStandardItem() newPageItem.setIcon(QIcon(QPixmap.fromImage(thumbnail))) newPageItem.setDragEnabled(True) newPageItem.setDropEnabled(False) newPageItem.setEditable(False) newPageItem.setText(dataList[0].replace("_", " ")) newPageItem.setData(dataList[1], role=CPE.DESCRIPTION) newPageItem.setData(relative, role=CPE.URL) newPageItem.setData(dataList[2], role=CPE.KEYWORDS) newPageItem.setData(dataList[3], role=CPE.LASTEDIT) newPageItem.setData(dataList[4], role=CPE.EDITOR) newPageItem.setToolTip(relative) page.close() self.pagesModel.appendRow(newPageItem) """ Remove the selected page from the list of pages. This does not remove it from disk(far too dangerous). """ def slot_remove_selected_page(self): index = self.comicPageList.currentIndex() self.pagesModel.removeRow(index.row()) """ This function adds a new page from the default template. If there's no default template, or the file does not exist, it will show the create/import template dialog. It will remember the selected item as the default template. """ def slot_add_new_page_single(self): templateUrl = "templatepage" templateExists = False if "singlePageTemplate" in self.setupDictionary.keys(): templateUrl = self.setupDictionary["singlePageTemplate"] if os.path.exists(os.path.join(self.projecturl, templateUrl)): templateExists = True if templateExists is False: if "templateLocation" not in self.setupDictionary.keys(): self.setupDictionary["templateLocation"] = os.path.relpath( QFileDialog.getExistingDirectory( caption=i18n("Where are the templates located?"), options=QFileDialog.ShowDirsOnly), self.projecturl) templateDir = os.path.join( self.projecturl, self.setupDictionary["templateLocation"]) template = comics_template_dialog.comics_template_dialog( templateDir) if template.exec_() == QDialog.Accepted: templateUrl = os.path.relpath(template.url(), self.projecturl) self.setupDictionary["singlePageTemplate"] = templateUrl if os.path.exists(os.path.join(self.projecturl, templateUrl)): self.add_new_page(templateUrl) """ This function always asks for a template showing the new template window. This allows users to have multiple different templates created for back covers, spreads, other and have them accessible, while still having the convenience of a singular "add page" that adds a default. """ def slot_add_new_page_from_template(self): if "templateLocation" not in self.setupDictionary.keys(): self.setupDictionary["templateLocation"] = os.path.relpath( QFileDialog.getExistingDirectory( caption=i18n("Where are the templates located?"), options=QFileDialog.ShowDirsOnly), self.projecturl) templateDir = os.path.join(self.projecturl, self.setupDictionary["templateLocation"]) template = comics_template_dialog.comics_template_dialog(templateDir) if template.exec_() == QDialog.Accepted: templateUrl = os.path.relpath(template.url(), self.projecturl) self.add_new_page(templateUrl) """ This is the actual function that adds the template using the template url. It will attempt to name the new page projectName+number. """ def add_new_page(self, templateUrl): # check for page list and or location. pagesList = [] if "pages" in self.setupDictionary.keys(): pagesList = self.setupDictionary["pages"] if not "pageNumber" in self.setupDictionary.keys(): self.setupDictionary['pageNumber'] = 0 if (str(self.setupDictionary["pagesLocation"]).isspace()): self.setupDictionary["pagesLocation"] = os.path.relpath( QFileDialog.getExistingDirectory( caption=i18n("Where should the pages go?"), options=QFileDialog.ShowDirsOnly), self.projecturl) # Search for the possible name. extraUnderscore = str() if str(self.setupDictionary["projectName"])[-1].isdigit(): extraUnderscore = "_" self.setupDictionary['pageNumber'] += 1 pageName = str(self.setupDictionary["projectName"]).replace( " ", "_") + extraUnderscore + str( format(self.setupDictionary['pageNumber'], "03d")) url = os.path.join(str(self.setupDictionary["pagesLocation"]), pageName + ".kra") # open the page by opening the template and resaving it, or just opening it. absoluteUrl = os.path.join(self.projecturl, url) if (os.path.exists(absoluteUrl)): newPage = Application.openDocument(absoluteUrl) else: booltemplateExists = os.path.exists( os.path.join(self.projecturl, templateUrl)) if booltemplateExists is False: templateUrl = os.path.relpath( QFileDialog.getOpenFileName( caption=i18n( "Which image should be the basis the new page?"), directory=self.projecturl, filter=str(i18n("Krita files") + "(*.kra)"))[0], self.projecturl) newPage = Application.openDocument( os.path.join(self.projecturl, templateUrl)) newPage.waitForDone() newPage.setFileName(absoluteUrl) newPage.setName(pageName.replace("_", " ")) newPage.save() newPage.waitForDone() # Get out the extra data for the standard item. newPageItem = QStandardItem() newPageItem.setIcon( QIcon(QPixmap.fromImage(newPage.thumbnail(256, 256)))) newPageItem.setDragEnabled(True) newPageItem.setDropEnabled(False) newPageItem.setEditable(False) newPageItem.setText(pageName.replace("_", " ")) newPageItem.setData("", role=CPE.DESCRIPTION) newPageItem.setData(url, role=CPE.URL) newPageItem.setData("", role=CPE.KEYWORDS) newPageItem.setData("", role=CPE.LASTEDIT) newPageItem.setData("", role=CPE.EDITOR) newPageItem.setToolTip(url) # close page document. while os.path.exists(absoluteUrl) is False: qApp.processEvents() newPage.close() # add item to page. self.pagesModel.appendRow(newPageItem) """ Write to the json configuration file. This also checks the current state of the pages list. """ def slot_write_config(self): # Don't load when the pages are still being loaded, otherwise we'll be overwriting our own pages list. if (self.loadingPages is False): print("CPMT: writing comic configuration...") # Generate a pages list from the pagesmodel. pagesList = [] for i in range(self.pagesModel.rowCount()): index = self.pagesModel.index(i, 0) url = str(self.pagesModel.data(index, role=CPE.URL)) if url not in pagesList: pagesList.append(url) self.setupDictionary["pages"] = pagesList # Save to our json file. configFile = open(self.path_to_config, "w", newline="", encoding="utf-16") json.dump(self.setupDictionary, configFile, indent=4, sort_keys=True, ensure_ascii=False) configFile.close() print("CPMT: done") """ Open a page in the pagesmodel in Krita. """ def slot_open_page(self, index): if index.column() is 0: # Get the absolute url from the relative one in the pages model. absoluteUrl = os.path.join( self.projecturl, str(self.pagesModel.data(index, role=CPE.URL))) # Make sure the page exists. if os.path.exists(absoluteUrl): page = Application.openDocument(absoluteUrl) # Set the title to the filename if it was empty. It looks a bit neater. if page.name().isspace or len(page.name()) < 1: page.setName( str(self.pagesModel.data(index, role=Qt.DisplayRole)).replace( "_", " ")) # Add views for the document so the user can use it. Application.activeWindow().addView(page) Application.setActiveDocument(page) else: print( "CPMT: The page cannot be opened because the file doesn't exist:", absoluteUrl) """ Call up the metadata editor dialog. Only when the dialog is "Accepted" will the metadata be saved. """ def slot_edit_meta_data(self): dialog = comics_metadata_dialog.comic_meta_data_editor() dialog.setConfig(self.setupDictionary) if (dialog.exec_() == QDialog.Accepted): self.setupDictionary = dialog.getConfig(self.setupDictionary) self.slot_write_config() """ An attempt at making the description editable from the comic pages list. It is currently not working because ZipFile has no overwrite mechanism, and I don't have the energy to write one yet. """ def slot_write_description(self, index): for row in range(self.pagesModel.rowCount()): index = self.pagesModel.index(row, 1) indexUrl = self.pagesModel.index(row, 0) absoluteUrl = os.path.join( self.projecturl, str(self.pagesModel.data(indexUrl, role=CPE.URL))) page = zipfile.ZipFile(absoluteUrl, "a") xmlDoc = ET.ElementTree() ET.register_namespace("", "http://www.calligra.org/DTD/document-info") location = os.path.join(self.projecturl, "documentinfo.xml") xmlDoc.parse(location) xmlroot = ET.fromstring(page.read("documentinfo.xml")) calligra = "{http://www.calligra.org/DTD/document-info}" aboutelem = xmlroot.find(calligra + 'about') if ET.iselement(aboutelem.find(calligra + 'subject')): desc = aboutelem.find(calligra + 'subject') desc.text = self.pagesModel.data(index, role=Qt.EditRole) xmlstring = ET.tostring(xmlroot, encoding='unicode', method='xml', short_empty_elements=False) page.writestr(zinfo_or_arcname="documentinfo.xml", data=xmlstring) for document in Application.documents(): if str(document.fileName()) == str(absoluteUrl): document.setDocumentInfo(xmlstring) page.close() """ Calls up the export settings dialog. Only when accepted will the configuration be written. """ def slot_edit_export_settings(self): dialog = comics_export_dialog.comic_export_setting_dialog() dialog.setConfig(self.setupDictionary) if (dialog.exec_() == QDialog.Accepted): self.setupDictionary = dialog.getConfig(self.setupDictionary) self.slot_write_config() """ Export the comic. Won't work without export settings set. """ def slot_export(self): #ensure there is a unique identifier if "uuid" not in self.setupDictionary.keys(): uuid = str() if "acbfID" in self.setupDictionary.keys(): uuid = str(self.setupDictionary["acbfID"]) else: uuid = QUuid.createUuid().toString() self.setupDictionary["uuid"] = uuid exporter = comics_exporter.comicsExporter() exporter.set_config(self.setupDictionary, self.projecturl) exportSuccess = exporter.export() if exportSuccess: print( "CPMT: Export success! The files have been written to the export folder!" ) QMessageBox.information( self, i18n("Export success"), i18n("The files have been written to the export folder."), QMessageBox.Ok) """ Calls up the comics project setup wizard so users can create a new json file with the basic information. """ def slot_new_project(self): setup = comics_project_setup_wizard.ComicsProjectSetupWizard() setup.showDialog() """ This is triggered by any document save. It checks if the given url in in the pages list, and if so, updates the appropriate page thumbnail. This helps with the management of the pages, because the user will be able to see the thumbnails as a todo for the whole comic, giving a good overview over whether they still need to ink, color or the like for a given page, and it thus also rewards the user whenever they save. """ def slot_check_for_page_update(self, url): if "pages" in self.setupDictionary.keys(): relUrl = os.path.relpath(url, self.projecturl) if relUrl in self.setupDictionary["pages"]: index = self.pagesModel.index( self.setupDictionary["pages"].index(relUrl), 0) if index.isValid(): pageItem = self.pagesModel.itemFromIndex(index) page = zipfile.ZipFile(url, "r") dataList = self.get_description_and_title( page.read("documentinfo.xml")) if (dataList[0].isspace() or len(dataList[0]) < 1): dataList[0] = os.path.basename(url) thumbnail = QImage.fromData(page.read("preview.png")) pageItem.setIcon(QIcon(QPixmap.fromImage(thumbnail))) pageItem.setText(dataList[0]) pageItem.setData(dataList[1], role=CPE.DESCRIPTION) pageItem.setData(url, role=CPE.URL) pageItem.setData(dataList[2], role=CPE.KEYWORDS) pageItem.setData(dataList[3], role=CPE.LASTEDIT) pageItem.setData(dataList[4], role=CPE.EDITOR) self.pagesModel.setItem(index.row(), index.column(), pageItem) """ Resize all the pages in the pages list. It will show a dialog with the options for resizing. Then, it will try to pop up a progress dialog while resizing. The progress dialog shows the remaining time and pages. """ def slot_batch_resize(self): dialog = QDialog() dialog.setWindowTitle(i18n("Resize all Pages")) buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) buttons.accepted.connect(dialog.accept) buttons.rejected.connect(dialog.reject) sizesBox = comics_export_dialog.comic_export_resize_widget( "Scale", batch=True, fileType=False) exporterSizes = comics_exporter.sizesCalculator() dialog.setLayout(QVBoxLayout()) dialog.layout().addWidget(sizesBox) dialog.layout().addWidget(buttons) if dialog.exec_() == QDialog.Accepted: progress = QProgressDialog(i18n("Resizing pages..."), str(), 0, len(self.setupDictionary["pages"])) progress.setWindowTitle(i18n("Resizing Pages")) progress.setCancelButton(None) timer = QElapsedTimer() timer.start() config = {} config = sizesBox.get_config(config) for p in range(len(self.setupDictionary["pages"])): absoluteUrl = os.path.join(self.projecturl, self.setupDictionary["pages"][p]) progress.setValue(p) timePassed = timer.elapsed() if (p > 0): timeEstimated = (len(self.setupDictionary["pages"]) - p) * (timePassed / p) passedString = str(int(timePassed / 60000)) + ":" + format( int(timePassed / 1000), "02d") + ":" + format( timePassed % 1000, "03d") estimatedString = str(int( timeEstimated / 60000)) + ":" + format( int(timeEstimated / 1000), "02d") + ":" + format( int(timeEstimated % 1000), "03d") progress.setLabelText( str( i18n( "{pages} of {pagesTotal} done. \nTime passed: {passedString}:\n Estimated:{estimated}" )).format(pages=p, pagesTotal=len( self.setupDictionary["pages"]), passedString=passedString, estimated=estimatedString)) qApp.processEvents() if os.path.exists(absoluteUrl): doc = Application.openDocument(absoluteUrl) listScales = exporterSizes.get_scale_from_resize_config( config["Scale"], [ doc.width(), doc.height(), doc.resolution(), doc.resolution() ]) doc.scaleImage(listScales[0], listScales[1], listScales[2], listScales[3], "bicubic") doc.waitForDone() doc.save() doc.waitForDone() doc.close() def slot_show_page_viewer(self): index = int(self.comicPageList.currentIndex().row()) self.page_viewer_dialog.load_comic(self.path_to_config) self.page_viewer_dialog.go_to_page_index(index) self.page_viewer_dialog.show() """ Function to copy the current project location into the clipboard. This is useful for users because they'll be able to use that url to quickly move to the project location in outside applications. """ def slot_copy_project_url(self): if self.projecturl is not None: clipboard = qApp.clipboard() clipboard.setText(str(self.projecturl)) """ Scrape text files with the textlayer keys for text, and put those in a POT file. This makes it possible to handle translations. """ def slot_scrape_translations(self): translationFolder = self.setupDictionary.get("translationLocation", "translations") fullTranslationPath = os.path.join(self.projecturl, translationFolder) os.makedirs(fullTranslationPath, exist_ok=True) textLayersToSearch = self.setupDictionary.get("textLayerNames", ["text"]) scraper = comics_project_translation_scraper.translation_scraper( self.projecturl, translationFolder, textLayersToSearch, self.setupDictionary["projectName"]) # Run text scraper. language = self.setupDictionary.get("language", "en") metadata = {} metadata["title"] = self.setupDictionary.get("title", "") metadata["summary"] = self.setupDictionary.get("summary", "") metadata["keywords"] = ", ".join( self.setupDictionary.get("otherKeywords", [""])) metadata["transnotes"] = self.setupDictionary.get( "translatorHeader", "Translator's Notes") scraper.start(self.setupDictionary["pages"], language, metadata) QMessageBox.information( self, i18n("Scraping success"), str(i18n("POT file has been written to: {file}")).format( file=fullTranslationPath), QMessageBox.Ok) """ This is required by the dockwidget class, otherwise unused. """ def canvasChanged(self, canvas): pass
class ModulesPanel(QWidget): """ ModulesPanel Signals: onAddHook([ptr, funcname]) - MenuItem AddHook onDumpBinary([ptr, size#int]) - MenuItem DumpBinary onModuleSelected([ptr, size#int]) - ModuleDoubleClicked onModuleFuncSelected(ptr) - FunctionDoubleClicked """ # pylint: disable=too-many-instance-attributes onAddHook = pyqtSignal(list, name='onAddHook') onDumpBinary = pyqtSignal(list, name='onDumpBinary') onModuleSelected = pyqtSignal(list, name='onModuleSelected') onModuleFuncSelected = pyqtSignal(str, name='onModuleFuncSelected') def __init__(self, parent=None): # pylint: disable=too-many-statements super(ModulesPanel, self).__init__(parent) self._app_window = parent if self._app_window.dwarf is None: print('ModulesPanel created before Dwarf exists') return self._app_window.dwarf.onSetModules.connect(self.set_modules) self._uppercase_hex = True # setup models self.modules_list = None self.modules_model = QStandardItemModel(0, 4, self) self.modules_model.setHeaderData(0, Qt.Horizontal, 'Name') self.modules_model.setHeaderData(1, Qt.Horizontal, 'Base') self.modules_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.modules_model.setHeaderData(2, Qt.Horizontal, 'Size') self.modules_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.modules_model.setHeaderData(3, Qt.Horizontal, 'Path') self.imports_list = None self.imports_model = QStandardItemModel(0, 4, self) self.imports_model.setHeaderData(0, Qt.Horizontal, 'Import') self.imports_model.setHeaderData(1, Qt.Horizontal, 'Address') self.imports_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.imports_model.setHeaderData(2, Qt.Horizontal, 'Module') self.imports_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.imports_model.setHeaderData(3, Qt.Horizontal, 'Type') self.exports_list = None self.exports_model = QStandardItemModel(0, 3, self) self.exports_model.setHeaderData(0, Qt.Horizontal, 'Export') self.exports_model.setHeaderData(1, Qt.Horizontal, 'Address') self.exports_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.exports_model.setHeaderData(2, Qt.Horizontal, 'Type') self.symbols_list = None self.symbols_model = QStandardItemModel(0, 3, self) self.symbols_model.setHeaderData(0, Qt.Horizontal, 'Export') self.symbols_model.setHeaderData(1, Qt.Horizontal, 'Address') self.symbols_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.symbols_model.setHeaderData(2, Qt.Horizontal, 'Type') # setup ui main_wrapper = QVBoxLayout() main_wrapper.setContentsMargins(0, 0, 0, 0) h_box = QHBoxLayout() self.modules_list = DwarfListView() self.modules_list.setContextMenuPolicy(Qt.CustomContextMenu) self.modules_list.customContextMenuRequested.connect( self._on_modules_contextmenu) self.modules_list.setEditTriggers(self.modules_list.NoEditTriggers) self.modules_list.clicked.connect(self._module_clicked) self.modules_list.doubleClicked.connect(self._module_dblclicked) self.modules_list.setModel(self.modules_model) self.modules_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.modules_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.modules_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) h_box.addWidget(self.modules_list) self.modules_list.selectionModel().selectionChanged.connect( self._module_clicked) hv_box = QVBoxLayout() self.imports_list = DwarfListView() self.imports_list.setContextMenuPolicy(Qt.CustomContextMenu) self.imports_list.customContextMenuRequested.connect( self._on_imports_contextmenu) self.imports_list.setEditTriggers(self.modules_list.NoEditTriggers) self.imports_list.doubleClicked.connect(self._import_dblclicked) self.imports_list.setModel(self.imports_model) self.imports_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.imports_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.imports_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self.imports_list.setVisible(False) self.exports_list = DwarfListView() self.exports_list.setContextMenuPolicy(Qt.CustomContextMenu) self.exports_list.customContextMenuRequested.connect( self._on_exports_contextmenu) self.exports_list.setEditTriggers(self.modules_list.NoEditTriggers) self.exports_list.doubleClicked.connect(self._export_dblclicked) self.exports_list.setModel(self.exports_model) self.exports_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.exports_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.exports_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self.exports_list.setVisible(False) self.symbols_list = DwarfListView() self.symbols_list.setContextMenuPolicy(Qt.CustomContextMenu) self.symbols_list.doubleClicked.connect(self._symbol_dblclicked) self.symbols_list.setModel(self.symbols_model) self.symbols_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.symbols_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.symbols_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self.symbols_list.setVisible(False) hv_box.addWidget(self.imports_list) hv_box.addWidget(self.exports_list) hv_box.addWidget(self.symbols_list) h_box.addLayout(hv_box) main_wrapper.addLayout(h_box) self.setLayout(main_wrapper) # ************************************************************************ # **************************** Properties ******************************** # ************************************************************************ @property def uppercase_hex(self): """ HexDisplayStyle """ return self._uppercase_hex @uppercase_hex.setter def uppercase_hex(self, value): """ HexDisplayStyle """ if isinstance(value, bool): self._uppercase_hex = value elif isinstance(value, str): self._uppercase_hex = (value == 'upper') # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def set_modules(self, modules): """ Fills the ModulesList with data """ self.modules_list.clear() for module in modules: name = QStandardItem() name.setTextAlignment(Qt.AlignLeft) name.setText(module['name']) base = QStandardItem() base.setTextAlignment(Qt.AlignCenter) str_fmt = '0x{0:X}' if not self.uppercase_hex: str_fmt = '0x{0:x}' base.setText(str_fmt.format(int(module['base'], 16))) size = QStandardItem() size.setTextAlignment(Qt.AlignRight) size.setText("{0:,d}".format(int(module['size']))) path = QStandardItem() path.setTextAlignment(Qt.AlignLeft) path.setText(module['path']) self.modules_model.appendRow([name, base, size, path]) def update_modules(self): """ DwarfApiCall updateModules """ return self._app_window.dwarf.dwarf_api('updateModules') def set_imports(self, imports): """ Fills the ImportsList with data """ self.imports_list.clear() for import_ in imports: name = QStandardItem() name.setTextAlignment(Qt.AlignLeft) name.setText(import_['name']) address = QStandardItem() address.setTextAlignment(Qt.AlignCenter) str_fmt = '0x{0:X}' if not self.uppercase_hex: str_fmt = '0x{0:x}' address.setText(str_fmt.format(int(import_['address'], 16))) module = QStandardItem() if 'module' in import_: module.setTextAlignment(Qt.AlignLeft) module.setText(import_['module']) type_ = QStandardItem() if 'type' in import_: type_.setTextAlignment(Qt.AlignLeft) type_.setText(import_['type']) self.imports_model.appendRow([name, address, module, type_]) def set_exports(self, exports): """ Fills the ExportsList with data """ self.exports_list.clear() for export in exports: name = QStandardItem() name.setTextAlignment(Qt.AlignLeft) name.setText(export['name']) address = QStandardItem() address.setTextAlignment(Qt.AlignCenter) str_fmt = '0x{0:X}' if not self.uppercase_hex: str_fmt = '0x{0:x}' address.setText(str_fmt.format(int(export['address'], 16))) type_ = QStandardItem() type_.setTextAlignment(Qt.AlignLeft) type_.setText(export['type']) self.exports_model.appendRow([name, address, type_]) def set_symbols(self, symbols): """ Fills the SymbolsList with data """ self.symbols_list.clear() for symbol in symbols: name = QStandardItem() name.setTextAlignment(Qt.AlignLeft) name.setText(symbol['name']) address = QStandardItem() address.setTextAlignment(Qt.AlignCenter) str_fmt = '0x{0:X}' if not self.uppercase_hex: str_fmt = '0x{0:x}' address.setText(str_fmt.format(int(symbol['address'], 16))) type_ = QStandardItem() type_.setTextAlignment(Qt.AlignLeft) type_.setText(symbol['type']) self.symbols_model.appendRow([name, address, type_]) # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _module_clicked(self): """ Module Clicked updates imports/exports/symbols """ module_index = self.modules_list.selectionModel().currentIndex().row() module = self.modules_model.item(module_index, 0) # module name if module is None: return imports = self._app_window.dwarf.dwarf_api('enumerateImports', module.text()) if imports and (imports is not None): imports = json.loads(imports) if imports: self.set_imports(imports) self.imports_list.setVisible(True) else: self.imports_list.setVisible(False) exports = self._app_window.dwarf.dwarf_api('enumerateExports', module.text()) if exports and exports is not None: exports = json.loads(exports) if exports: self.set_exports(exports) self.exports_list.setVisible(True) else: self.exports_list.setVisible(False) symbols = self._app_window.dwarf.dwarf_api('enumerateSymbols', module.text()) if symbols and symbols is not None: symbols = json.loads(symbols) if symbols: self.set_symbols(symbols) self.symbols_list.setVisible(True) else: self.symbols_list.setVisible(False) def _module_dblclicked(self): """ Module DoubleClicked """ module_index = self.modules_list.selectionModel().currentIndex().row() base = self.modules_model.item(module_index, 1).text() size = self.modules_model.item(module_index, 2).text().replace(',', '') self.onModuleSelected.emit([base, size]) def _import_dblclicked(self): """ ImportFunction DoubleClicked """ index = self.imports_list.selectionModel().currentIndex().row() addr = self.imports_model.item(index, 1).text() self.onModuleFuncSelected.emit(addr) def _export_dblclicked(self): """ ExportFunction DoubleClicked """ index = self.exports_list.selectionModel().currentIndex().row() addr = self.exports_model.item(index, 1).text() self.onModuleFuncSelected.emit(addr) def _symbol_dblclicked(self): """ Symbol DoubleClicked """ index = self.symbols_list.selectionModel().currentIndex().row() addr = self.symbols_model.item(index, 1).text() self.onModuleFuncSelected.emit(addr) def _on_modules_contextmenu(self, pos): """ Modules ContextMenu """ index = self.modules_list.indexAt(pos).row() glbl_pt = self.modules_list.mapToGlobal(pos) context_menu = QMenu(self) if index != -1: context_menu.addAction( 'Dump Binary', lambda: self._on_dumpmodule( self.modules_model.item(index, 1).text(), self.modules_model.item(index, 2).text())) context_menu.addAction( 'Copy address', lambda: utils.copy_hex_to_clipboard( self.modules_model.item(index, 1).text())) context_menu.addSeparator() context_menu.addAction( 'Copy Name', lambda: utils.copy_str_to_clipboard( self.modules_model.item(index, 0).text())) context_menu.addAction( 'Copy Path', lambda: utils.copy_str_to_clipboard( self.modules_model.item(index, 3).text())) context_menu.addSeparator() context_menu.addAction('Refresh', self.update_modules) context_menu.exec_(glbl_pt) def _on_imports_contextmenu(self, pos): """ ImportList ContextMenu """ index = self.imports_list.indexAt(pos).row() if index != -1: glbl_pt = self.imports_list.mapToGlobal(pos) context_menu = QMenu(self) func_name = self.imports_model.item(index, 0).text() addr = self.imports_model.item(index, 1).text() context_menu.addAction('Add Hook', lambda: self._add_hook(addr, func_name)) context_menu.addSeparator() context_menu.addAction( 'Copy address', lambda: utils.copy_hex_to_clipboard( self.imports_model.item(index, 1).text())) context_menu.addSeparator() context_menu.addAction( 'Copy FunctionName', lambda: utils.copy_str_to_clipboard(func_name)) context_menu.addAction( 'Copy ModuleName', lambda: utils.copy_str_to_clipboard( self.imports_model.item(index, 2).text())) context_menu.exec_(glbl_pt) def _on_exports_contextmenu(self, pos): """ ExportsList ContextMenu """ index = self.exports_list.indexAt(pos).row() if index != -1: glbl_pt = self.exports_list.mapToGlobal(pos) context_menu = QMenu(self) func_name = self.exports_model.item(index, 0).text() addr = self.exports_model.item(index, 1).text() context_menu.addAction('Add Hook', lambda: self._add_hook(addr, func_name)) context_menu.addSeparator() context_menu.addAction( 'Copy address', lambda: utils.copy_hex_to_clipboard( self.exports_model.item(index, 1).text())) context_menu.addSeparator() context_menu.addAction( 'Copy FunctionName', lambda: utils.copy_str_to_clipboard(func_name)) context_menu.exec_(glbl_pt) def _on_dumpmodule(self, ptr, size): """ MenuItem DumpBinary """ if isinstance(ptr, int): str_fmt = '0x{0:X}' if not self.uppercase_hex: str_fmt = '0x{0:x}' ptr = str_fmt.format(ptr) size = size.replace(',', '') self.onDumpBinary.emit([ptr, size]) def _add_hook(self, ptr, name=None): """ MenuItem AddHook """ if name is None: name = ptr if isinstance(ptr, str): if ptr.startswith('0x') or ptr.startswith('#'): self.onAddHook.emit([ptr, name]) elif isinstance(ptr, int): str_fmt = '0x{0:x}' self.onAddHook.emit(str_fmt.format([ptr, name]))
class AttrsUI(object): def __init__(self, window, uaclient): self.window = window self.uaclient = uaclient self.model = QStandardItemModel() self.window.ui.attrView.setModel(self.model) self.window.ui.attrView.header().setSectionResizeMode(1) self.window.ui.treeView.activated.connect(self.show_attrs) self.window.ui.treeView.clicked.connect(self.show_attrs) self.window.ui.attrRefreshButton.clicked.connect(self.show_attrs) # Context menu self.window.ui.attrView.setContextMenuPolicy(Qt.CustomContextMenu) self.window.ui.attrView.customContextMenuRequested.connect(self.showContextMenu) copyaction = QAction("&Copy Value", self.model) copyaction.triggered.connect(self._copy_value) self._contextMenu = QMenu() self._contextMenu.addAction(copyaction) def showContextMenu(self, position): item = self.get_current_item() if item: self._contextMenu.exec_(self.window.ui.attrView.mapToGlobal(position)) def get_current_item(self, col_idx=0): idx = self.window.ui.attrView.currentIndex() return self.model.item(idx.row(), col_idx) def _copy_value(self, position): it = self.get_current_item(1) if it: QApplication.clipboard().setText(it.text()) def clear(self): self.model.clear() def show_attrs(self, idx): if not isinstance(idx, QModelIndex): idx = None node = self.window.get_current_node(idx) self.model.clear() if node: self._show_attrs(node) def _show_attrs(self, node): try: attrs = self.uaclient.get_all_attrs(node) except Exception as ex: self.window.show_error(ex) raise self.model.setHorizontalHeaderLabels(['Attribute', 'Value']) for k, v in attrs.items(): if isinstance(v, (ua.NodeId)): v = str(v) elif isinstance(v, (ua.QualifiedName, ua.LocalizedText)): v = v.to_string() elif isinstance(v, Enum): v = repr(v) elif isinstance(v, ua.DataValue): v = repr(v) else: v = str(v) self.model.appendRow([QStandardItem(k), QStandardItem(v)])
class UploadDialog(QDialog): """文件上传对话框""" new_infos = pyqtSignal(object) def __init__(self): super().__init__() self.cwd = os.getcwd() self.selected = [] self.max_len = 400 self.initUI() self.set_size() self.setStyleSheet(dialog_qss_style) def set_values(self, folder_name): self.setWindowTitle("上传文件至 ➩ " + str(folder_name)) def initUI(self): self.setWindowTitle("上传文件") self.setWindowIcon(QIcon("./icon/upload.ico")) self.logo = QLabel() self.logo.setPixmap(QPixmap("./icon/logo3.gif")) self.logo.setStyleSheet("background-color:rgb(0,153,255);") self.logo.setAlignment(Qt.AlignCenter) # btn 1 self.btn_chooseDir = QPushButton("选择文件夹", self) self.btn_chooseDir.setObjectName("btn_chooseDir") self.btn_chooseDir.setObjectName("btn_chooseDir") self.btn_chooseDir.setIcon(QIcon("./icon/folder.gif")) # btn 2 self.btn_chooseMutiFile = QPushButton("选择多文件", self) self.btn_chooseDir.setObjectName("btn_chooseMutiFile") self.btn_chooseMutiFile.setObjectName("btn_chooseMutiFile") self.btn_chooseMutiFile.setIcon(QIcon("./icon/file.ico")) # btn 3 self.btn_deleteSelect = QPushButton("删除", self) self.btn_deleteSelect.setObjectName("btn_deleteSelect") self.btn_deleteSelect.setIcon(QIcon("./icon/delete.ico")) # 列表 self.list_view = QListView(self) self.list_view.setViewMode(QListView.ListMode) self.slm = QStandardItem() self.model = QStandardItemModel() self.list_view.setModel(self.model) self.model.removeRows(0, self.model.rowCount()) # 清除旧的选择 self.list_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.list_view.setSelectionBehavior(QAbstractItemView.SelectRows) self.list_view.setSelectionMode(QAbstractItemView.ExtendedSelection) self.buttonBox = QDialogButtonBox() self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) grid = QGridLayout() grid.setSpacing(10) grid.addWidget(self.logo, 1, 0, 1, 3) grid.addWidget(self.btn_chooseDir, 2, 0) grid.addWidget(self.btn_chooseMutiFile, 2, 2) grid.addWidget(self.list_view, 3, 0, 2, 3) grid.addWidget(self.btn_deleteSelect, 5, 0) grid.addWidget(self.buttonBox, 5, 1, 1, 2) self.setLayout(grid) self.setMinimumWidth(350) # 设置信号 self.btn_chooseDir.clicked.connect(self.slot_btn_chooseDir) self.btn_chooseMutiFile.clicked.connect(self.slot_btn_chooseMutiFile) self.btn_deleteSelect.clicked.connect(self.slot_btn_deleteSelect) self.buttonBox.accepted.connect(self.slot_btn_ok) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.clear_old) self.buttonBox.rejected.connect(self.reject) def set_size(self): rows = self.model.rowCount() for i in range(rows): m_len = int(len(self.model.item(i, 0).text()) * 4) if m_len > self.max_len: self.max_len = m_len rows = 10 if rows >= 10 else rows # 限制最大高度 self.resize(self.max_len, 250 + rows * 28) def clear_old(self): self.selected = [] self.model.removeRows(0, self.model.rowCount()) self.set_size() def slot_btn_ok(self): if self.selected: self.new_infos.emit(self.selected) self.clear_old() def slot_btn_deleteSelect(self): _indexes = self.list_view.selectionModel().selection().indexes() if not _indexes: return indexes = [] for i in _indexes: # 获取所选行号 indexes.append(i.row()) indexes = set(indexes) for i in sorted(indexes, reverse=True): self.selected.remove(self.model.item(i, 0).text()) self.model.removeRow(i) self.set_size() def slot_btn_chooseDir(self): dir_choose = QFileDialog.getExistingDirectory(self, "选择文件夹", self.cwd) # 起始路径 if dir_choose == "": return if dir_choose not in self.selected: self.selected.append(dir_choose) self.model.appendRow( QStandardItem(QIcon("./icon/folder.gif"), dir_choose)) self.set_size() def slot_btn_chooseMutiFile(self): files, _ = QFileDialog.getOpenFileNames(self, "选择多文件", self.cwd, "All Files (*)") if len(files) == 0: return for _file in files: if _file not in self.selected: self.selected.append(_file) self.model.appendRow( QStandardItem(QIcon("./icon/file.ico"), _file)) self.set_size()
class DataChangeUI(object): def __init__(self, window, uaclient, hub_manager): self.window = window self.uaclient = uaclient # FIXME IoT stuff self.hub_manager = hub_manager self._subhandler = DataChangeHandler(self.hub_manager) self._subscribed_nodes = [] self.model = QStandardItemModel() self.window.ui.subView.setModel(self.model) self.window.ui.subView.horizontalHeader().setSectionResizeMode(1) self.window.ui.actionSubscribeDataChange.triggered.connect(self._subscribe) self.window.ui.actionUnsubscribeDataChange.triggered.connect(self._unsubscribe) # populate contextual menu self.window.ui.treeView.addAction(self.window.ui.actionSubscribeDataChange) self.window.ui.treeView.addAction(self.window.ui.actionUnsubscribeDataChange) # handle subscriptions self._subhandler.data_change_fired.connect(self._update_subscription_model, type=Qt.QueuedConnection) def clear(self): self._subscribed_nodes = [] self.model.clear() def _subscribe(self): node = self.window.get_current_node() if node is None: return if node in self._subscribed_nodes: print("allready subscribed to node: ", node) return self.model.setHorizontalHeaderLabels(["DisplayName", "Value", "Timestamp"]) row = [QStandardItem(node.display_name), QStandardItem("No Data yet"), QStandardItem("")] row[0].setData(node) self.model.appendRow(row) self._subscribed_nodes.append(node) self.window.ui.subDockWidget.raise_() try: self.uaclient.subscribe_datachange(node, self._subhandler) except Exception as ex: self.window.show_error(ex) idx = self.model.indexFromItem(row[0]) self.model.takeRow(idx.row()) def _unsubscribe(self): node = self.window.get_current_node() if node is None: return self.uaclient.unsubscribe_datachange(node) self._subscribed_nodes.remove(node) i = 0 while self.model.item(i): item = self.model.item(i) if item.data() == node: self.model.removeRow(i) i += 1 def _update_subscription_model(self, node, value, timestamp): i = 0 while self.model.item(i): item = self.model.item(i) if item.data() == node: it = self.model.item(i, 1) it.setText(value) it_ts = self.model.item(i, 2) it_ts.setText(timestamp) i += 1
class MainWindow(QMainWindow, Ui_MainWindow): sig_ctrl_reload = pyqtSignal() sig_net_args_update = pyqtSignal([str]) sig_opt_args_update = pyqtSignal([str]) sig_err_box = pyqtSignal([str]) sig_set_state = pyqtSignal([int]) sig_add_log = pyqtSignal([str]) sig_update_pgbar = pyqtSignal([int]) sig_update_table = pyqtSignal([list]) data_model = None net = None training_Thread = None pause_flag = False stop_flag = False def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.setup_signal() self.config = Configs() self.init_table() self.sig_ctrl_reload.emit() def setup_signal(self): self.sig_ctrl_reload.connect(self.ctrl_reload) self.net_file_btn.clicked.connect(self.choose_net) self.net_args_btn.clicked.connect(self.net_args) self.load_net_btn.clicked.connect(self.load_net) self.opt_args_btn.clicked.connect(self.opt_args) self.param_file_btn.clicked.connect(self.choose_param) self.dir_edit_btn.clicked.connect(self.choose_dir) self.datadir_edit_btn.clicked.connect(self.choose_datadir) self.start_btn.clicked.connect(self.start_training) self.pause_btn.clicked.connect(self.pause_training) self.stop_btn.clicked.connect(self.stop_training) self.sig_net_args_update.connect(self.set_net_args) self.sig_opt_args_update.connect(self.set_opt_args) self.decay_btn_group.buttonClicked.connect(self.check_decay) self.param_btn_group.buttonClicked.connect(self.check_init) self.sig_err_box.connect(self.err_box) self.sig_set_state.connect(self.set_state) self.sig_add_log.connect(self.add_log) self.sig_update_pgbar.connect(self.update_pgbar) self.sig_update_table.connect(self.add_row) def init_table(self): self.data_model = QStandardItemModel(0, 7) headlist = [ 'Epoch', 'train_loss', 'train_acc', 'valid_loss', 'valid_acc', 'lr', 'time' ] self.data_model.setHorizontalHeaderLabels(headlist) self.data_table.setModel(self.data_model) self.data_table.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) # self.data_table.horizontalHeader().setSectionResizeMode( # QHeaderView.ResizeToContents) def ctrl_reload(self): # 控制页重置 print('ctrl refresh') self.opt_box.clear() self.opt_box.addItems( ['sgd', 'nag', 'adagrad', 'adadelta', 'adam', 'rmsprop']) self.opt_box.setCurrentText(self.config.lr_cfg.optimizer) self.init_box.clear() self.init_box.addItems([ 'Normal', 'Orthogonal', 'Uniform', 'One', 'Zero', 'Xavier', 'MSRAPrelu' ]) self.init_box.setCurrentText(self.config.train_cfg.init) self.net_class_box.setText(self.config.net_cfg.class_name) self.net_name_box.setText(self.config.net_cfg.name) self.lr_box.setText(str(self.config.lr_cfg.lr)) self.wd_box.setText(str(self.config.lr_cfg.wd)) self.factor_epoch_box.setText(str(self.config.lr_cfg.factor_epoch)) self.factor_pow_box.setText(str(self.config.lr_cfg.factor)) self.warmup_box.setValue(self.config.lr_cfg.warmup) if self.config.lr_cfg.decay == 'factor': self.factor_decay_btn.setChecked(True) self.factor_epoch_box.setEnabled(True) self.factor_pow_box.setEnabled(True) else: self.cosine_decay_btn.setChecked(True) self.factor_epoch_box.setEnabled(False) self.factor_pow_box.setEnabled(False) self.workdir_box.setText(self.config.dir_cfg.dir) self.datadir_box.setText(self.config.dir_cfg.dataset) self.img_size_box.setValue(self.config.data_cfg.size) self.worker_box.setValue(self.config.data_cfg.worker) self.crop_btn.setChecked(self.config.data_cfg.crop) self.crop_pad_box.setValue(self.config.data_cfg.crop_pad) self.cutout_btn.setChecked(self.config.data_cfg.cutout) self.cutout_size_box.setValue(self.config.data_cfg.cutout_size) self.flip_btn.setChecked(self.config.data_cfg.flip) self.erase_btn.setChecked(self.config.data_cfg.erase) self.mixup_btn.setChecked(self.config.data_cfg.mixup) self.mixup_alpha_box.setText(str(self.config.data_cfg.alpha)) self.epoch_box.setText(str(self.config.train_cfg.epoch)) self.batchsize_box.setText(str(self.config.train_cfg.batchsize)) if self.config.train_cfg.param_init: self.init_btn.setChecked(True) self.init_box.setEnabled(True) self.param_file_btn.setEnabled(False) self.init_btn.setChecked(True) else: self.load_param_btn.setChecked(True) self.param_file_btn.setEnabled(True) self.init_box.setEnabled(False) self.load_param_btn.setEnabled(True) self.gpu_num_box.setValue(self.config.train_cfg.gpu) self.gpu_num_box.setMaximum(mx.context.num_gpus()) self.amp_btn.setChecked(self.config.train_cfg.amp) self.tensorboard_btn.setChecked(self.config.save_cfg.tensorboard) self.profiler_btn.setChecked(self.config.save_cfg.profiler) self.main_pgbar.setValue(0) self.sub_pgbar.setValue(0) def choose_net(self): print('choose_net') fileName, fileType = QFileDialog.getOpenFileName( self, "Choose Net", "./", "Python Files (*.py)") if fileName == '': print('No change.') # self.net_file_btn.setText('选择文件') return self.net_file_btn.setText(os.path.basename(fileName)) self.config.net_cfg.filename = os.path.basename(fileName) self.config.net_cfg.dir = os.path.dirname(fileName) def net_args(self): print('net_args') dialog = Dialog(title='网络参数(字典)', text=self.config.net_cfg.extra_arg, signal=self.sig_net_args_update, parent=self) dialog.show() def load_net(self): print('load net') self.config.net_cfg.class_name = self.net_class_box.text() if self.net_name_box.text() == '': self.config.net_cfg.name = self.net_class_box.text() else: self.config.net_cfg.name = self.net_name_box.text() self.load_net_btn.setText('Loading') t = Thread(target=self.load_net_by_config, name='load_net') t.start() def load_net_by_config(self): try: sys.path.append(self.config.net_cfg.dir) # print(sys.path) test = import_module( os.path.splitext(self.config.net_cfg.filename)[0]) test = reload(test) # print(test) func = getattr(test, self.config.net_cfg.class_name) if self.config.net_cfg.extra_arg != '': self.net = func(kwargs=self.config.net_cfg.extra_arg) else: self.net = func() # X = mx.nd.uniform(shape=(1, 3, 32, 32)) # self.net.initialize(init=mx.init.MSRAPrelu()) # print(self.net(X)) sys.path.remove(self.config.net_cfg.dir) self.load_net_btn.setText("成功读取" + self.config.net_cfg.name) except Exception as e: print(e) self.net = None self.load_net_btn.setText("读取失败") def opt_args(self): print('opt_args') dialog = Dialog(title='额外参数(字典)', text=self.config.lr_cfg.extra_arg, signal=self.sig_opt_args_update, parent=self) dialog.show() def choose_param(self): print('choose_param') fileName, fileType = QFileDialog.getOpenFileName( self, "Choose Params", "./", "Python Files (*.params)") if fileName == '': print('No change.') # self.net_file_btn.setText('选择文件') return self.param_file_btn.setText(os.path.basename(fileName)) self.config.train_cfg.param_file = fileName def choose_dir(self): print('choose_dir') wk_dir = QFileDialog.getExistingDirectory(self, "Choose Dir", "./") if wk_dir == '': print('No change.') # self.net_file_btn.setText('选择文件') return self.workdir_box.setText(wk_dir) self.config.dir_cfg.dir = wk_dir def choose_datadir(self): print('choose_datadir') wk_dir = QFileDialog.getExistingDirectory(self, "Choose Dataset Dir", "./") if wk_dir == '': print('No change.') # self.net_file_btn.setText('选择文件') return self.datadir_box.setText(wk_dir) self.config.dir_cfg.dataset = wk_dir def start_training(self): if self.training_Thread is not None: if self.training_Thread.is_alive(): self.pause_flag = False self.sig_set_state.emit(1) return print('start_training') self.sig_set_state.emit(-1) self.init_table() self.pause_flag = False self.stop_flag = False os.chdir(self.config.dir_cfg.dir) if self.net == None: self.sig_err_box.emit('未导入网络') self.sig_set_state.emit(0) return self.collect_config() logger = self.GuiLogger() logger.sig = self.sig_add_log self.training_Thread = Thread(target=train_net, args=( self.net, self.config, self.check_flag, logger, self.sig_set_state, self.sig_update_pgbar, self.sig_update_table, )) # train_net(self.net, self.config) self.training_Thread.start() def pause_training(self): print('pause_training') self.sig_set_state.emit(-1) self.pause_flag = True def stop_training(self): print('stop_training') self.sig_set_state.emit(-1) self.stop_flag = True self.net = None def set_net_args(self, argstr): print('Net args:' + argstr) self.config.net_cfg.extra_arg = argstr def set_opt_args(self, argstr): print('Opt args:' + argstr) self.config.lr_cfg.extra_arg = argstr def check_decay(self): # print(self.decay_btn_group.checkedId()) if self.decay_btn_group.checkedId() == -2: self.config.lr_cfg.decay = 'factor' self.factor_epoch_box.setEnabled(True) self.factor_pow_box.setEnabled(True) else: self.config.lr_cfg.decay = 'cosine' self.factor_epoch_box.setEnabled(False) self.factor_pow_box.setEnabled(False) def check_init(self): # print(self.param_btn_group.checkedId()) if self.param_btn_group.checkedId() == -3: self.config.train_cfg.param_init = True self.init_box.setEnabled(True) self.param_file_btn.setEnabled(False) else: self.config.train_cfg.param_init = False self.init_box.setEnabled(False) self.param_file_btn.setEnabled(True) def err_box(self, text='Error'): QMessageBox.critical(self, 'Error', text, QMessageBox.Ok) def collect_config(self): print('collect_config') self.config.lr_cfg.lr = float(self.lr_box.text()) self.config.lr_cfg.wd = float(self.wd_box.text()) self.config.lr_cfg.optimizer = self.opt_box.currentText() if self.cosine_decay_btn.isChecked(): self.config.lr_cfg.decay = 'cosine' else: self.config.lr_cfg.decay = 'factor' self.config.lr_cfg.factor_epoch = int(self.factor_epoch_box.text()) self.config.lr_cfg.factor = float(self.factor_pow_box.text()) self.config.lr_cfg.warmup = self.warmup_box.value() self.config.data_cfg.size = self.img_size_box.value() self.config.data_cfg.worker = self.worker_box.value() self.config.data_cfg.crop = self.crop_btn.isChecked() self.config.data_cfg.crop_pad = self.crop_pad_box.value() self.config.data_cfg.cutout = self.cutout_btn.isChecked() self.config.data_cfg.cutout_size = self.cutout_size_box.value() self.config.data_cfg.flip = self.flip_btn.isChecked() self.config.data_cfg.erase = self.erase_btn.isChecked() self.config.data_cfg.mixup = self.mixup_btn.isChecked() self.config.data_cfg.alpha = float(self.mixup_alpha_box.text()) self.config.train_cfg.epoch = int(self.epoch_box.text()) self.config.train_cfg.batchsize = int(self.batchsize_box.text()) if self.init_btn.isChecked(): self.config.train_cfg.param_init = True else: self.config.train_cfg.param_init = False self.config.train_cfg.init = self.init_box.currentText() self.config.train_cfg.gpu = self.gpu_num_box.value() self.config.train_cfg.amp = self.amp_btn.isChecked() self.config.save_cfg.tensorboard = self.tensorboard_btn.isChecked() self.config.save_cfg.profiler = self.profiler_btn.isChecked() def check_flag(self): return [self.pause_flag, self.stop_flag] def add_log(self, text): self.log_box.append(text) # self.log_box.verticalScrollBar().setValue( # self.log_box.verticalScrollBar().maximum()) def set_state(self, state): if state == -1: print('waiting') self.start_btn.setEnabled(False) self.pause_btn.setEnabled(False) self.stop_btn.setEnabled(False) # mx.gpu(0).empty_cache() elif state == 0: print('init') self.start_btn.setEnabled(True) self.pause_btn.setEnabled(False) self.stop_btn.setEnabled(False) elif state == 1: print('start training') self.start_btn.setEnabled(False) self.pause_btn.setEnabled(True) self.stop_btn.setEnabled(True) elif state == 2: print('Pause') self.start_btn.setEnabled(True) self.pause_btn.setEnabled(False) self.stop_btn.setEnabled(True) def update_pgbar(self, iteration): iter_max = 50000 // self.config.train_cfg.batchsize if iteration == -1: self.main_pgbar.setMaximum(0) self.sub_pgbar.setMaximum(0) return self.main_pgbar.setMaximum(self.config.train_cfg.epoch) self.sub_pgbar.setMaximum(iter_max) self.main_pgbar.setValue(iteration // iter_max) main_percent = 100 * (iteration // iter_max) / self.config.train_cfg.epoch self.label_main.setText('%.1f%%' % main_percent) self.sub_pgbar.setValue(iteration % iter_max) sub_percent = 100 * (iteration % iter_max) / iter_max self.label_sub.setText('%.1f%%' % sub_percent) def add_row(self, datalist): itemlist = [] for data in datalist: itemlist.append(self.get_table_item(data)) self.data_model.appendRow(itemlist) self.data_table.setModel(self.data_model) # self.data_table.verticalScrollBar().setValue( # self.data_table.verticalScrollBar().maximum()) self.data_table.scrollToBottom() # self.data_table.horizontalHeader().setSectionResizeMode(0,QHeaderView.ResizeToContents) def get_table_item(self, text): # print(type(text)) if not isinstance(text, int): text = '%.6f' % text item = QStandardItem(str(text)) item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) return item class GuiLogger(logging.Handler): def emit(self, record): # implementation of append_line omitted self.sig.emit(self.format(record)) def closeEvent(self, event): result = QMessageBox.question(self, "GUI Training", "Do you want to exit?", QMessageBox.Yes | QMessageBox.No) if (result == QMessageBox.Yes): self.stop_flag = True event.accept() else: event.ignore()
class MDIHistory(QWidget, _HalWidgetBase): def __init__(self, parent=None): super(MDIHistory, self).__init__(parent) self.setMinimumSize(QSize(300, 200)) self.setWindowTitle("PyQt5 editor test example") lay = QVBoxLayout() lay.setContentsMargins(0, 0, 0, 0) self.setLayout(lay) self.list = QListView() self.list.setEditTriggers(QListView.NoEditTriggers) self.list.activated.connect(self.activated) self.list.setAlternatingRowColors(True) self.list.selectionChanged = self.selectionChanged self.model = QStandardItemModel(self.list) self.MDILine = MDILine() self.MDILine.soft_keyboard = False self.MDILine.line_up = self.line_up self.MDILine.line_down = self.line_down # add widgets lay.addWidget(self.list) lay.addWidget(self.MDILine) self.reload() def _hal_init(self): STATUS.connect('state-off', lambda w: self.setEnabled(False)) STATUS.connect('state-estop', lambda w: self.setEnabled(False)) STATUS.connect( 'interp-idle', lambda w: self.setEnabled(STATUS.machine_is_on( ) and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED))) STATUS.connect('interp-run', lambda w: self.setEnabled(not STATUS.is_auto_mode())) STATUS.connect('all-homed', lambda w: self.setEnabled(STATUS.machine_is_on())) def reload(self, w=None): print 'RELOAD' try: fp = os.path.expanduser(INFO.MDI_HISTORY_PATH) with open(fp, 'r') as inputfile: for line in inputfile: line = line.rstrip('\n') item = QStandardItem(line) self.model.appendRow(item) self.list.setModel(self.model) self.list.scrollToBottom() except Exception as e: print e LOG.error('File path is not valid: {}]n,()'.format(fp), e) def line_up(self): print 'up' def line_down(self): print 'down' def selectionChanged(self, old, new): cmd = self.getSelected() self.MDILine.setText(cmd) def getSelected(self): selected_indexes = self.list.selectedIndexes() selected_rows = [item.row() for item in selected_indexes] # iterates each selected row in descending order for selected_row in sorted(selected_rows, reverse=True): text = self.model.item(selected_row).text() return text def activated(self): cmd = self.getSelected() self.MDILine.setText(cmd) self.MDILine.submit() item = QStandardItem(cmd) self.model.appendRow(item) self.list.update() ######################################################################### # This is how designer can interact with our widget properties. # designer will show the pyqtProperty properties in the editor # it will use the get set and reset calls to do those actions ######################################################################### def set_soft_keyboard(self, data): self.MDILine.soft_keyboard = data def get_soft_keyboard(self): return self.MDILine.soft_keyboard def reset_soft_keyboard(self): self.MDILine.soft_keyboard = False # designer will show these properties in this order: soft_keyboard_option = pyqtProperty(bool, get_soft_keyboard, set_soft_keyboard, reset_soft_keyboard)
class Audio: def __init__(self): self.mediaLength = 0 self.mediaList = [] self.formats = ['*.mp3', '*.ogg'] self.playing = False self.initUI() def togglePlay(self): if len(self.mediaList) != 0: if self.playing: self.pause() self.playing = False else: self.play() self.playing = True def initUI(self): self.list = QListView() self.list.setWindowTitle("Music Files") self.list.setMinimumSize(300, 400) self.list.setSpacing(10) self.model = QStandardItemModel(self.list) self.resourceMusic = ResourceMusic() for root, dirs, files in os.walk("MusicFiles"): for extensions in self.formats: for filename in fnmatch.filter(files, extensions): item = QStandardItem(filename) item.setFont(QFont(filename, 10)) self.model.appendRow(item) self.mediaList.append(item) self.mediaLength = len(self.mediaList) self.currentItem = 1 self.prevItem = self.mediaLength if len(self.mediaList) != 0: self.mediaList[0].setBackground(QColor(97, 138, 204)) self.list.setModel(self.model) self.list.show() def nextItem(self): if len(self.mediaList) != 0: if self.currentItem == self.mediaLength: self.prevItem = self.mediaLength self.currentItem = 1 else: self.prevItem = self.currentItem self.currentItem = self.currentItem + 1 self.mediaList[self.prevItem - 1].setBackground(QColor(35, 38, 41)) self.mediaList[self.currentItem - 1].setBackground( QColor(97, 138, 204)) # def previousItem(self): # if len(self.mediaList) != 0: # if self.currentItem == 1: # self.prevItem = 1 # self.currentItem = self.mediaLength # # else: # self.prevItem = self.currentItem # self.currentItem = self.currentItem - 1 # # self.mediaList[self.prevItem - 1].setBackground(QColor(35, 38, 41)) # self.mediaList[self.currentItem - 1].setBackground(QColor(97, 138, 204)) def play(self): print("Music Player : " + str(self.mediaList[self.currentItem - 1].text())) self.resourceMusic.playMusic(self.mediaList[self.currentItem - 1].text()) return 1 def pause(self): self.resourceMusic.pauseMusic() def stop(self): if len(self.mediaList) != 0: self.resourceMusic.stopMusic() self.playing = False # def resume(self): # self.resourceMusic.resumeMusic() def Close(self): self.list.close() return -1
class ApkList(DwarfListView): """ Displays installed APKs """ onApkSelected = pyqtSignal(list, name='onApkSelected') def __init__(self, parent=None, show_path=True): super(ApkList, self).__init__(parent=parent) self.adb = Adb() self.adb.device = parent.dwarf.device.id if not self.adb.available(): return self.retrieve_thread = PackageRetrieveThread(self.adb) if self.retrieve_thread is not None: self.retrieve_thread.onAddPackage.connect(self._on_addpackage) if show_path: self.apk_model = QStandardItemModel(0, 2) else: self.apk_model = QStandardItemModel(0, 1) self.apk_model.setHeaderData(0, Qt.Horizontal, 'Name') if show_path: self.apk_model.setHeaderData(1, Qt.Horizontal, 'Path') self.setModel(self.apk_model) self.header().setSectionResizeMode(0, QHeaderView.ResizeToContents) self.doubleClicked.connect(self._on_apk_selected) if self.retrieve_thread is not None: if not self.retrieve_thread.isRunning(): self.retrieve_thread.start() # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def refresh(self): """ Refresh Packages """ if self.retrieve_thread is not None: if not self.retrieve_thread.isRunning(): self.clear() self.retrieve_thread.start() # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _on_addpackage(self, package): if package: name = QStandardItem() name.setText(package[0]) if self.apk_model.columnCount() == 2: path = QStandardItem() path.setText(package[1]) self.apk_model.appendRow([name, path]) else: self.apk_model.appendRow([name]) def _on_apk_selected(self, model_index): item = self.apk_model.itemFromIndex(model_index).row() if item != -1: package = self.apk_model.item(item, 0).text() if self.apk_model.columnCount() == 2: path = self.apk_model.item(item, 1).text() self.onApkSelected.emit([package, path]) else: self.onApkSelected.emit([package, None])
class BacktraceWidget(DwarfListView): onShowMemoryRequest = pyqtSignal(list, name='onShowMemoryRequest') def __init__(self, parent=None): super(BacktraceWidget, self).__init__(parent=parent) self._app_window = parent self._app_window.dwarf.onBackTrace.connect(self.set_backtrace) self._model = QStandardItemModel(0, 2) self._model.setHeaderData(0, Qt.Horizontal, 'Address') self._model.setHeaderData(1, Qt.Horizontal, 'Symbol') self.setModel(self._model) self.header().setSectionResizeMode(0, QHeaderView.ResizeToContents) self.header().setSectionResizeMode(1, QHeaderView.ResizeToContents) self.doubleClicked.connect(self._item_double_clicked) self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self._on_context_menu) self._mode = 'native' def set_backtrace(self, bt): if 'type' not in bt: return if 'bt' not in bt: return self.clear() if bt['type'] == 'native': self._mode = 'native' self._model.setHeaderData(0, Qt.Horizontal, 'Address') self._model.setHeaderData(1, Qt.Horizontal, 'Symbol') bt = bt['bt'] for a in bt: addr = a['address'] if self.uppercase_hex: addr = addr.upper().replace('0X', '0x') addr_item = QStandardItem() addr_item.setText(addr) addr_item.setForeground(Qt.red) name = a['name'] if name is None: name = '-' self._model.appendRow([addr_item, QStandardItem(name)]) elif bt['type'] == 'java': self._mode = 'java' self._model.setHeaderData(0, Qt.Horizontal, 'Method') self._model.setHeaderData(1, Qt.Horizontal, 'Source') bt = bt['bt'] parts = bt.split('\n') for i in range(0, len(parts)): if i == 0: continue p = parts[i].replace('\t', '') p = p.split('(') if len(p) != 2: continue self._model.appendRow([ QStandardItem(p[0]), QStandardItem(p[1].replace(')', '')) ]) def _item_double_clicked(self, model_index): row = self._model.itemFromIndex(model_index).row() if row != -1: if self._mode == 'native': self.onShowMemoryRequest.emit( ['bt', self._model.item(row, 0).text()]) def _on_context_menu(self, pos): index = self.indexAt(pos).row() glbl_pt = self.mapToGlobal(pos) context_menu = QMenu(self) if index != -1: if self._mode == 'native': addr_item = self.model().item(index, 0).text() symbol_item = self.model().item(index, 1).text() # show contextmenu context_menu.addAction( 'Jump to {0}'.format(addr_item), lambda: self.onShowMemoryRequest.emit(['bt', addr_item])) context_menu.addSeparator() context_menu.addAction( 'Copy Address', lambda: utils.copy_hex_to_clipboard(addr_item)) if symbol_item and symbol_item != '-': context_menu.addAction( 'Copy Symbol', lambda: utils.copy_str_to_clipboard(symbol_item)) elif self._mode == 'java': method_item = self.model().item(index, 0).text() if method_item.startswith('at '): method_item = method_item.replace('at ', '') source_item = self.model().item(index, 1).text() if ':' in source_item: source_item = source_item.split(':')[0] # show contextmenu # context_menu.addAction('Jump to', lambda: self._app_window.jump_to_address(addr_item.text())) # context_menu.addSeparator() # TODO: add jumpto java context_menu.addAction( 'Copy Method', lambda: utils.copy_str_to_clipboard(method_item)) context_menu.addAction( 'Copy Source', lambda: utils.copy_str_to_clipboard(source_item)) context_menu.exec_(glbl_pt)
class RefNodeSetsWidget(QObject): error = pyqtSignal(Exception) nodeset_added = pyqtSignal(str) nodeset_removed = pyqtSignal(str) def __init__(self, view): QObject.__init__(self, view) self.view = view self.model = QStandardItemModel() self.view.setModel(self.model) self.nodesets = [] self.server_mgr = None self._nodeset_to_delete = None self.view.header().setSectionResizeMode(1) addNodeSetAction = QAction("Add Reference Node Set", self.model) addNodeSetAction.triggered.connect(self.add_nodeset) self.removeNodeSetAction = QAction("Remove Reference Node Set", self.model) self.removeNodeSetAction.triggered.connect(self.remove_nodeset) self.view.setContextMenuPolicy(Qt.CustomContextMenu) self.view.customContextMenuRequested.connect(self.showContextMenu) self._contextMenu = QMenu() self._contextMenu.addAction(addNodeSetAction) self._contextMenu.addAction(self.removeNodeSetAction) @trycatchslot def add_nodeset(self): path, ok = QFileDialog.getOpenFileName( self.view, caption="Import OPC UA XML Node Set", filter="XML Files (*.xml *.XML)", directory=".") if not ok: return None name = os.path.basename(path) if name in self.nodesets: return try: self.server_mgr.import_xml(path) except Exception as ex: self.error.emit(ex) raise item = QStandardItem(name) self.model.appendRow([item]) self.nodesets.append(name) self.view.expandAll() self.nodeset_added.emit(path) @trycatchslot def remove_nodeset(self): idx = self.view.currentIndex() if not idx.isValid() or idx.row() == 0: return item = self.model.itemFromIndex(idx) name = item.text() self.nodesets.remove(name) self.model.removeRow(idx.row()) self.nodeset_removed.emit(name) def set_server_mgr(self, server_mgr): self.server_mgr = server_mgr self.nodesets = [] self.model.clear() self.model.setHorizontalHeaderLabels(['Node Sets']) item = QStandardItem("Opc.Ua.NodeSet2.xml") item.setFlags(Qt.NoItemFlags) self.model.appendRow([item]) self.view.expandAll() def clear(self): self.model.clear() @trycatchslot def showContextMenu(self, position): if not self.server_mgr: return idx = self.view.currentIndex() if not idx.isValid() or idx.row() == 0: self.removeNodeSetAction.setEnabled(False) else: self.removeNodeSetAction.setEnabled(True) self._contextMenu.exec_(self.view.viewport().mapToGlobal(position))
class HelpWebSearchWidget(E5ClearableLineEdit): """ Class implementing a web search widget for the web browser. @signal search(QUrl) emitted when the search should be done """ search = pyqtSignal(QUrl) def __init__(self, parent=None): """ Constructor @param parent reference to the parent widget (QWidget) """ super(HelpWebSearchWidget, self).__init__(parent) from E5Gui.E5LineEdit import E5LineEdit from E5Gui.E5LineEditButton import E5LineEditButton from .OpenSearch.OpenSearchManager import OpenSearchManager self.__mw = parent self.__openSearchManager = OpenSearchManager(self) self.__openSearchManager.currentEngineChanged.connect( self.__currentEngineChanged) self.__currentEngine = "" self.__enginesMenu = QMenu(self) self.__engineButton = E5LineEditButton(self) self.__engineButton.setMenu(self.__enginesMenu) self.addWidget(self.__engineButton, E5LineEdit.LeftSide) self.__searchButton = E5LineEditButton(self) self.__searchButton.setIcon(UI.PixmapCache.getIcon("webSearch.png")) self.addWidget(self.__searchButton, E5LineEdit.LeftSide) self.__model = QStandardItemModel(self) self.__completer = QCompleter() self.__completer.setModel(self.__model) self.__completer.setCompletionMode( QCompleter.UnfilteredPopupCompletion) self.__completer.setWidget(self) self.__searchButton.clicked.connect(self.__searchButtonClicked) self.textEdited.connect(self.__textEdited) self.returnPressed.connect(self.__searchNow) self.__completer.activated[QModelIndex].connect( self.__completerActivated) self.__completer.highlighted[QModelIndex].connect( self.__completerHighlighted) self.__enginesMenu.aboutToShow.connect(self.__showEnginesMenu) self.__suggestionsItem = None self.__suggestions = [] self.__suggestTimer = None self.__suggestionsEnabled = Preferences.getHelp("WebSearchSuggestions") self.__recentSearchesItem = None self.__recentSearches = [] self.__maxSavedSearches = 10 self.__engine = None self.__loadSearches() self.__setupCompleterMenu() self.__currentEngineChanged() def __searchNow(self): """ Private slot to perform the web search. """ searchText = self.text() if not searchText: return globalSettings = QWebSettings.globalSettings() if not globalSettings.testAttribute( QWebSettings.PrivateBrowsingEnabled): if searchText in self.__recentSearches: self.__recentSearches.remove(searchText) self.__recentSearches.insert(0, searchText) if len(self.__recentSearches) > self.__maxSavedSearches: self.__recentSearches = \ self.__recentSearches[:self.__maxSavedSearches] self.__setupCompleterMenu() url = self.__openSearchManager.currentEngine().searchUrl(searchText) self.search.emit(url) def __setupCompleterMenu(self): """ Private method to create the completer menu. """ if not self.__suggestions or \ (self.__model.rowCount() > 0 and self.__model.item(0) != self.__suggestionsItem): self.__model.clear() self.__suggestionsItem = None else: self.__model.removeRows(1, self.__model.rowCount() - 1) boldFont = QFont() boldFont.setBold(True) if self.__suggestions: if self.__model.rowCount() == 0: if not self.__suggestionsItem: self.__suggestionsItem = QStandardItem( self.tr("Suggestions")) self.__suggestionsItem.setFont(boldFont) self.__model.appendRow(self.__suggestionsItem) for suggestion in self.__suggestions: self.__model.appendRow(QStandardItem(suggestion)) if not self.__recentSearches: self.__recentSearchesItem = QStandardItem( self.tr("No Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) else: self.__recentSearchesItem = QStandardItem( self.tr("Recent Searches")) self.__recentSearchesItem.setFont(boldFont) self.__model.appendRow(self.__recentSearchesItem) for recentSearch in self.__recentSearches: self.__model.appendRow(QStandardItem(recentSearch)) view = self.__completer.popup() view.setFixedHeight( view.sizeHintForRow(0) * self.__model.rowCount() + view.frameWidth() * 2) self.__searchButton.setEnabled( bool(self.__recentSearches or self.__suggestions)) def __completerActivated(self, index): """ Private slot handling the selection of an entry from the completer. @param index index of the item (QModelIndex) """ if self.__suggestionsItem and \ self.__suggestionsItem.index().row() == index.row(): return if self.__recentSearchesItem and \ self.__recentSearchesItem.index().row() == index.row(): return self.__searchNow() def __completerHighlighted(self, index): """ Private slot handling the highlighting of an entry of the completer. @param index index of the item (QModelIndex) @return flah indicating a successful highlighting (boolean) """ if self.__suggestionsItem and \ self.__suggestionsItem.index().row() == index.row(): return False if self.__recentSearchesItem and \ self.__recentSearchesItem.index().row() == index.row(): return False self.setText(index.data()) return True def __textEdited(self, txt): """ Private slot to handle changes of the search text. @param txt search text (string) """ if self.__suggestionsEnabled: if self.__suggestTimer is None: self.__suggestTimer = QTimer(self) self.__suggestTimer.setSingleShot(True) self.__suggestTimer.setInterval(200) self.__suggestTimer.timeout.connect(self.__getSuggestions) self.__suggestTimer.start() else: self.__completer.setCompletionPrefix(txt) self.__completer.complete() def __getSuggestions(self): """ Private slot to get search suggestions from the configured search engine. """ searchText = self.text() if searchText: self.__openSearchManager.currentEngine()\ .requestSuggestions(searchText) def __newSuggestions(self, suggestions): """ Private slot to receive a new list of suggestions. @param suggestions list of suggestions (list of strings) """ self.__suggestions = suggestions self.__setupCompleterMenu() self.__completer.complete() def __showEnginesMenu(self): """ Private slot to handle the display of the engines menu. """ self.__enginesMenu.clear() from .OpenSearch.OpenSearchEngineAction import OpenSearchEngineAction engineNames = self.__openSearchManager.allEnginesNames() for engineName in engineNames: engine = self.__openSearchManager.engine(engineName) action = OpenSearchEngineAction(engine, self.__enginesMenu) action.setData(engineName) action.triggered.connect(self.__changeCurrentEngine) self.__enginesMenu.addAction(action) if self.__openSearchManager.currentEngineName() == engineName: action.setCheckable(True) action.setChecked(True) ct = self.__mw.currentBrowser() linkedResources = ct.linkedResources("search") if len(linkedResources) > 0: self.__enginesMenu.addSeparator() for linkedResource in linkedResources: url = QUrl(linkedResource.href) title = linkedResource.title mimetype = linkedResource.type_ if mimetype != "application/opensearchdescription+xml": continue if url.isEmpty(): continue if url.isRelative(): url = ct.url().resolved(url) if not title: if not ct.title(): title = url.host() else: title = ct.title() action = self.__enginesMenu.addAction( self.tr("Add '{0}'").format(title), self.__addEngineFromUrl) action.setData(url) action.setIcon(ct.icon()) self.__enginesMenu.addSeparator() self.__enginesMenu.addAction(self.__mw.searchEnginesAction()) if self.__recentSearches: self.__enginesMenu.addAction(self.tr("Clear Recent Searches"), self.clear) def __changeCurrentEngine(self): """ Private slot to handle the selection of a search engine. """ action = self.sender() if action is not None: name = action.data() self.__openSearchManager.setCurrentEngineName(name) def __addEngineFromUrl(self): """ Private slot to add a search engine given its URL. """ action = self.sender() if action is not None: url = action.data() if not isinstance(url, QUrl): return self.__openSearchManager.addEngine(url) def __searchButtonClicked(self): """ Private slot to show the search menu via the search button. """ self.__setupCompleterMenu() self.__completer.complete() def clear(self): """ Public method to clear all private data. """ self.__recentSearches = [] self.__setupCompleterMenu() super(HelpWebSearchWidget, self).clear() self.clearFocus() def preferencesChanged(self): """ Public method to handle the change of preferences. """ self.__suggestionsEnabled = Preferences.getHelp("WebSearchSuggestions") if not self.__suggestionsEnabled: self.__suggestions = [] self.__setupCompleterMenu() def saveSearches(self): """ Public method to save the recently performed web searches. """ Preferences.Prefs.settings.setValue('Help/WebSearches', self.__recentSearches) def __loadSearches(self): """ Private method to load the recently performed web searches. """ searches = Preferences.Prefs.settings.value('Help/WebSearches') if searches is not None: self.__recentSearches = searches def openSearchManager(self): """ Public method to get a reference to the opensearch manager object. @return reference to the opensearch manager object (OpenSearchManager) """ return self.__openSearchManager def __currentEngineChanged(self): """ Private slot to track a change of the current search engine. """ if self.__openSearchManager.engineExists(self.__currentEngine): oldEngine = self.__openSearchManager.engine(self.__currentEngine) oldEngine.imageChanged.disconnect(self.__engineImageChanged) if self.__suggestionsEnabled: oldEngine.suggestions.disconnect(self.__newSuggestions) newEngine = self.__openSearchManager.currentEngine() if newEngine.networkAccessManager() is None: newEngine.setNetworkAccessManager(self.__mw.networkAccessManager()) newEngine.imageChanged.connect(self.__engineImageChanged) if self.__suggestionsEnabled: newEngine.suggestions.connect(self.__newSuggestions) self.setInactiveText(self.__openSearchManager.currentEngineName()) self.__currentEngine = self.__openSearchManager.currentEngineName() self.__engineButton.setIcon( QIcon( QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) self.__suggestions = [] self.__setupCompleterMenu() def __engineImageChanged(self): """ Private slot to handle a change of the current search engine icon. """ self.__engineButton.setIcon( QIcon( QPixmap.fromImage( self.__openSearchManager.currentEngine().image()))) def mousePressEvent(self, evt): """ Protected method called by a mouse press event. @param evt reference to the mouse event (QMouseEvent) """ if evt.button() == Qt.XButton1: self.__mw.currentBrowser().pageAction(QWebPage.Back).trigger() elif evt.button() == Qt.XButton2: self.__mw.currentBrowser().pageAction(QWebPage.Forward).trigger() else: super(HelpWebSearchWidget, self).mousePressEvent(evt)
class MainWindow(Ui_MainWindow): def __init__(self): super().__init__() self.filename = ":memory:" self.engine = create_engine('sqlite:///' + self.filename) Base.metadata.create_all(self.engine) self.Session = sessionmaker(bind=self.engine) self.session = self.Session() def setupUi(self, main_window): super().setupUi(main_window) #Toolbar self.actionAdd_student.triggered.connect(self.addStudent) self.actionAdd_subject.triggered.connect(self.addSubject) #File menu actions self.actionOpen.triggered.connect(self.openFile) self.actionSave.triggered.connect(self.saveFile) self.actionSave_as.triggered.connect(self.saveFileAs) self.actionQuit.triggered.connect(QApplication.instance().quit) #Help menu actions self.actionAbout.triggered.connect(self.showAbout) #Students dock self.studentLineEdit.textChanged['QString'].connect( self.searchStudents) self.student_model = QStandardItemModel() self.studentList.setModel(self.student_model) self.studentList.doubleClicked['QModelIndex'].connect(self.editStudent) self.studentList.selectionModel().selectionChanged[ 'QItemSelection', 'QItemSelection'].connect(self.changeSelectedStudent) self.studentList.setSelectionMode( QAbstractItemView.SelectionMode.SingleSelection) self.updateStudentModel() #Subjects dock self.subjectLineEdit.textChanged['QString'].connect( self.searchSubjects) self.subject_model = QStandardItemModel() self.subjectList.setModel(self.subject_model) self.subjectList.doubleClicked['QModelIndex'].connect(self.editSubject) self.updateSubjectModel() #Main view self.schedule_model = QStandardItemModel(12, 7) self.scheduleView.setModel(self.schedule_model) self.scheduleView.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.scheduleView.verticalHeader().setSectionResizeMode( QHeaderView.Stretch) self.updateScheduleModel() def searchStudents(self, name): self.student_model.clear() for i in self.session.query(Student).filter( Student.name.ilike('%' + name + '%')): it = QStandardItem() it.setText(i.name) it.setData(i) it.setEditable(False) self.student_model.appendRow(it) def searchSubjects(self, name): self.subject_model.clear() for i in self.session.query(Subject).filter( Subject.name.ilike('%' + name + '%')): it = QStandardItem() it.setText(i.name) it.setData(i) it.setEditable(False) self.subject_model.appendRow(it) def updateStudentModel(self): self.searchStudents(self.studentLineEdit.text()) def updateSubjectModel(self): self.searchSubjects(self.subjectLineEdit.text()) def updateScheduleModel(self): self.schedule_model.clear() self.schedule_model.setHorizontalHeaderLabels([ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" ]) for i in DayOfWeek: for j in range(12): q = self.session.query(TimePlace).filter( TimePlace.day == i, TimePlace.slot == j) subjects = [] for tp in q: subjects.append(' '.join( [tp.subject.abbreviation, tp.place.name])) it = QStandardItem() it.setText('\n'.join(subjects)) it.setEditable(False) self.schedule_model.setItem(j, int(i), it) def addStudent(self): dialog = QDialog() content = EditStudent(self.session) content.setupUi(dialog) dialog.exec_() self.updateStudentModel() self.updateScheduleModel() def addSubject(self): dialog = QDialog() content = EditSubject(self.session) content.setupUi(dialog) dialog.exec_() self.updateSubjectModel() self.updateScheduleModel() def editStudent(self, idx): dialog = QDialog() content = EditStudent(self.session, self.student_model.itemFromIndex(idx).data()) content.setupUi(dialog) dialog.exec_() self.updateStudentModel() self.updateScheduleModel() def editSubject(self, idx): dialog = QDialog() content = EditSubject(self.session, self.subject_model.itemFromIndex(idx).data()) content.setupUi(dialog) dialog.exec_() self.updateSubjectModel() self.updateScheduleModel() def changeSelectedStudent(self, selected, unselected): tps = [] for s in selected: for i in s.indexes(): student = self.student_model.itemFromIndex(i).data() for subject in student.subjects: for tp in subject.time_places: tps.append((tp.slot, int(tp.day))) crossed = QBrush(Qt.black, Qt.BDiagPattern) normal = QBrush(Qt.white) for i in range(12): for j in map(int, DayOfWeek): if (i, j) in tps: self.schedule_model.item(i, j).setBackground(normal) else: self.schedule_model.item(i, j).setBackground(crossed) def openFile(self): new_filename = QFileDialog.getOpenFileName( caption="Open file", filter="SQLite3 file (*.sqlite3)")[0] if new_filename: self.filename = new_filename self.engine.dispose() self.engine = create_engine('sqlite:///' + self.filename) Base.metadata.create_all(self.engine) self.Session = sessionmaker(bind=self.engine) self.session = self.Session() self.updateStudentModel() self.updateSubjectModel() self.updateScheduleModel() def saveFile(self): if self.filename == ':memory:': self.saveFileAs() else: self.session.commit() def saveFileAs(self): new_filename = QFileDialog.getSaveFileName( caption="Save file as", filter="SQLite3 file (*.sqlite3)")[0] if new_filename: new_engine = create_engine('sqlite:///' + new_filename) Base.metadata.drop_all(new_engine) Base.metadata.create_all(new_engine) tables = Base.metadata.tables for tbl in tables: data = self.engine.execute(tables[tbl].select()).fetchall() if data: new_engine.execute(tables[tbl].insert(), data) self.engine.dispose() self.engine = new_engine self.Session = sessionmaker(bind=self.engine) self.session = self.Session() self.filename = new_filename self.updateSubjectModel() self.updateStudentModel() self.updateScheduleModel() def showAbout(self): dialog = QDialog() ui = About() ui.setupUi(dialog) dialog.exec_()
class JavaExplorerPanel(QWidget): def __init__(self, parent=None): super().__init__(parent=parent) self._app_window = parent self._handle_history = [] self._setup_ui() self._setup_models() def _setup_ui(self): self.setContentsMargins(0, 0, 0, 0) top_font = QFont() top_font.setBold(True) top_font.setPixelSize(19) # main wrapper main_wrapper = QVBoxLayout() main_wrapper.setContentsMargins(1, 1, 1, 1) # wrapwdgt wrap_wdgt = QWidget() self._top_class_name = QLabel(wrap_wdgt) self._top_class_name.setContentsMargins(10, 10, 10, 10) self._top_class_name.setAttribute(Qt.WA_TranslucentBackground, True) # keep this self._top_class_name.setFont(top_font) self._top_class_name.setStyleSheet('color: #ef5350;') wrap_wdgt.setMaximumHeight(self._top_class_name.height() + 20) main_wrapper.addWidget(wrap_wdgt) # left list left_wrap_wdgt = QWidget() left_v_box = QVBoxLayout(left_wrap_wdgt) left_v_box.setContentsMargins(0, 0, 0, 0) methods_label = QLabel('METHODS') font = methods_label.font() font.setBold(True) methods_label.setFont(font) methods_label.setContentsMargins(10, 0, 10, 2) methods_label.setAttribute(Qt.WA_TranslucentBackground, True) # keep this left_v_box.addWidget(methods_label) self._methods_list = DwarfListView() left_v_box.addWidget(self._methods_list) # center list center_wrap_wdgt = QWidget() center_v_box = QVBoxLayout(center_wrap_wdgt) center_v_box.setContentsMargins(0, 0, 0, 0) methods_label = QLabel('NATIVE FIELDS') methods_label.setFont(font) methods_label.setContentsMargins(10, 0, 10, 2) methods_label.setAttribute(Qt.WA_TranslucentBackground, True) # keep this center_v_box.addWidget(methods_label) self._native_fields_list = DwarfListView() self._native_fields_list.doubleClicked.connect( self._on_native_field_dblclicked) center_v_box.addWidget(self._native_fields_list) # right list right_wrap_wdgt = QWidget() right_v_box = QVBoxLayout(right_wrap_wdgt) right_v_box.setContentsMargins(0, 0, 0, 0) methods_label = QLabel('FIELDS') methods_label.setFont(font) methods_label.setContentsMargins(10, 0, 10, 2) methods_label.setAttribute(Qt.WA_TranslucentBackground, True) # keep this right_v_box.addWidget(methods_label) self._fields_list = DwarfListView() self._fields_list.doubleClicked.connect(self._on_field_dblclicked) right_v_box.addWidget(self._fields_list) # main splitter main_splitter = QSplitter(Qt.Horizontal) main_splitter.setContentsMargins(0, 0, 0, 0) main_splitter.addWidget(left_wrap_wdgt) main_splitter.addWidget(center_wrap_wdgt) main_splitter.addWidget(right_wrap_wdgt) main_splitter.setSizes([250, 100, 100]) main_wrapper.addWidget(main_splitter) main_wrapper.setSpacing(0) self.setLayout(main_wrapper) def _setup_models(self): # left list self._methods_model = QStandardItemModel(0, 3) self._methods_model.setHeaderData(0, Qt.Horizontal, 'Name') self._methods_model.setHeaderData(1, Qt.Horizontal, 'Return') self._methods_model.setHeaderData(2, Qt.Horizontal, 'Arguments') self._methods_list.setModel(self._methods_model) self._methods_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self._methods_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) # center list self._native_fields_model = QStandardItemModel(0, 2) self._native_fields_model.setHeaderData(0, Qt.Horizontal, 'Name') self._native_fields_model.setHeaderData(1, Qt.Horizontal, 'Value') self._native_fields_list.setModel(self._native_fields_model) self._native_fields_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) # right list self._fields_model = QStandardItemModel(0, 2) self._fields_model.setHeaderData(0, Qt.Horizontal, 'Name') self._fields_model.setHeaderData(1, Qt.Horizontal, 'Class') self._fields_list.setModel(self._fields_model) self._fields_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def _set_data(self, data): if 'class' not in data: return self._top_class_name.setText(data['class']) data = data['data'] self._methods_list.clear() self._native_fields_list.clear() self._fields_list.clear() for key in data: ref = data[key] if ref['type'] == 'function': if not key.startswith('$'): self._add_method(key, ref) elif ref['type'] == 'object': if ref['handle'] is not None: if not key.startswith('$'): self._add_field(key, ref['value'], ref['handle'], ref['handle_class']) else: if not key.startswith('$'): self._add_field(key, ref['value'], is_native=True) self._methods_list.sortByColumn(0, 0) self._native_fields_list.sortByColumn(0, 0) self._fields_list.sortByColumn(0, 0) def _add_method(self, name, ref): ref_overloads = ref['overloads'] for _, ref_overload in enumerate(ref_overloads): args = [] if 'args' in ref_overload: for arg in ref_overload['args']: if 'className' in arg: args.append(arg['className']) self._methods_model.appendRow([ QStandardItem(name), QStandardItem(ref_overload['return']['className']), QStandardItem('(%s)' % ', '.join(args)), ]) def _add_field(self, name, value, handle=None, handle_class=None, is_native=False): if handle: handle = {'handle': handle, 'handle_class': handle_class} handle_item = QStandardItem(name) handle_item.setData(handle, Qt.UserRole + 1) else: handle_item = QStandardItem(name) if not is_native: self._fields_model.appendRow( [handle_item, QStandardItem(str(value))]) else: self._native_fields_model.appendRow( [handle_item, QStandardItem(str(value))]) def _set_handle(self, handle): data = self._app_window.dwarf.dwarf_api('javaExplorer', handle) if not data: return self._handle_history.append({'handle': handle}) self._set_data(data) def _set_handle_arg(self, arg): data = self._app_window.dwarf.dwarf_api('javaExplorer', arg) if not data: return self._handle_history.append({'handle': arg}) self._set_data(data) def clear_panel(self): self._top_class_name.setText('') self._handle_history = [] self._methods_list.clear() self._native_fields_list.clear() self._fields_list.clear() def _back(self): if len(self._handle_history) < 2: return self._handle_history.pop() data = self._handle_history.pop(len(self._handle_history) - 1)['handle'] if isinstance(data, int): self._set_handle_arg(data) else: self._set_handle(data) # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _on_field_dblclicked(self, _): field_row = self._fields_list.selectionModel().currentIndex().row() if field_row >= 0: field_handle = self._fields_model.item(field_row, 0).data(Qt.UserRole + 1) if field_handle: self._set_handle(field_handle) def _on_native_field_dblclicked(self, _): field_row = self._native_fields_list.selectionModel().currentIndex( ).row() if field_row: field_handle = self._native_fields_model.item( field_row, 0).data(Qt.UserRole + 1) if field_handle: self._set_handle(field_handle) def keyPressEvent(self, event): # pylint: disable=invalid-name if event.key() == Qt.Key_Backspace: self._back() return super().keyPressEvent(event)
class AttrsUI(object): def __init__(self, window, uaclient): self.window = window self.uaclient = uaclient self.model = QStandardItemModel() self.window.ui.attrView.setModel(self.model) self.window.ui.attrView.header().setSectionResizeMode(1) self.window.ui.treeView.activated.connect(self.show_attrs) self.window.ui.treeView.clicked.connect(self.show_attrs) self.window.ui.attrRefreshButton.clicked.connect(self.show_attrs) # Context menu self.window.ui.attrView.setContextMenuPolicy(Qt.CustomContextMenu) self.window.ui.attrView.customContextMenuRequested.connect( self.showContextMenu) copyaction = QAction("&Copy Value", self.model) copyaction.triggered.connect(self._copy_value) self._contextMenu = QMenu() self._contextMenu.addAction(copyaction) def showContextMenu(self, position): item = self.get_current_item() if item: self._contextMenu.exec_( self.window.ui.attrView.mapToGlobal(position)) def get_current_item(self, col_idx=0): idx = self.window.ui.attrView.currentIndex() return self.model.item(idx.row(), col_idx) def _copy_value(self, position): it = self.get_current_item(1) if it: QApplication.clipboard().setText(it.text()) def clear(self): self.model.clear() def show_attrs(self, idx): if not isinstance(idx, QModelIndex): idx = None node = self.window.get_current_node(idx) self.model.clear() if node: self._show_attrs(node) def _show_attrs(self, node): try: attrs = self.uaclient.get_all_attrs(node) except Exception as ex: self.window.show_error(ex) raise self.model.setHorizontalHeaderLabels(['Attribute', 'Value']) for k, v in attrs.items(): if isinstance(v, (ua.NodeId)): v = str(v) elif isinstance(v, (ua.QualifiedName, ua.LocalizedText)): v = v.to_string() elif isinstance(v, Enum): v = repr(v) elif isinstance(v, ua.DataValue): v = repr(v) else: v = str(v) self.model.appendRow([QStandardItem(k), QStandardItem(v)])
class JavaTracePanel(QWidget): def __init__(self, app, *__args): super().__init__(app) self.app = app self.app.dwarf.onJavaTraceEvent.connect(self.on_event) self.app.dwarf.onEnumerateJavaClassesStart.connect( self.on_enumeration_start) self.app.dwarf.onEnumerateJavaClassesMatch.connect( self.on_enumeration_match) self.app.dwarf.onEnumerateJavaClassesComplete.connect( self.on_enumeration_complete) self.tracing = False self.trace_classes = [] self.trace_depth = 0 layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) self._record_icon = QIcon( utils.resource_path('assets/icons/record.png')) self._pause_icon = QIcon(utils.resource_path('assets/icons/pause.png')) self._stop_icon = QIcon(utils.resource_path('assets/icons/stop.png')) self._tool_bar = QToolBar() self._tool_bar.addAction('Start', self.start_trace) self._tool_bar.addAction('Pause', self.pause_trace) self._tool_bar.addAction('Stop', self.stop_trace) self._tool_bar.addSeparator() self._entries_lbl = QLabel('Entries: 0') self._entries_lbl.setStyleSheet('color: #ef5350;') self._entries_lbl.setContentsMargins(10, 0, 10, 2) self._entries_lbl.setAttribute(Qt.WA_TranslucentBackground, True) # keep this self._entries_lbl.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self._entries_lbl.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self._tool_bar.addWidget(self._entries_lbl) layout.addWidget(self._tool_bar) self.setup_splitter = QSplitter() self.events_list = JavaTraceView(self) self.events_list.setVisible(False) self.trace_list = DwarfListView() self.trace_list_model = QStandardItemModel(0, 1) self.trace_list_model.setHeaderData(0, Qt.Horizontal, 'Traced') self.trace_list.setModel(self.trace_list_model) self.trace_list.doubleClicked.connect(self.trace_list_double_click) self.class_list = DwarfListView() self.class_list_model = QStandardItemModel(0, 1) self.class_list_model.setHeaderData(0, Qt.Horizontal, 'Classes') self.class_list.setModel(self.class_list_model) self.class_list.setContextMenuPolicy(Qt.CustomContextMenu) self.class_list.customContextMenuRequested.connect( self.show_class_list_menu) self.class_list.doubleClicked.connect(self.class_list_double_click) self.current_class_search = '' bar = QScrollBar() bar.setFixedWidth(0) bar.setFixedHeight(0) self.trace_list.setHorizontalScrollBar(bar) bar = QScrollBar() bar.setFixedWidth(0) bar.setFixedHeight(0) self.class_list.setHorizontalScrollBar(bar) self.setup_splitter.addWidget(self.trace_list) self.setup_splitter.addWidget(self.class_list) layout.addWidget(self.setup_splitter) layout.addWidget(self.events_list) self.setLayout(layout) def class_list_double_click(self, item): item = self.class_list_model.itemFromIndex(item) try: if self.trace_classes.index(item.text()) >= 0: return except: pass self.trace_classes.append(item.text()) self.trace_list_model.appendRow([QStandardItem(item.text())]) self.trace_list_model.sort(0, Qt.AscendingOrder) def on_enumeration_start(self): self.class_list_model.setRowCount(0) def on_enumeration_match(self, java_class): try: if PREFIXED_CLASS.index(java_class) >= 0: try: if self.trace_classes.index(java_class) >= 0: return except: pass self.trace_list_model.appendRow(QStandardItem(java_class)) self.trace_classes.append(java_class) except: pass self.class_list_model.appendRow([QStandardItem(java_class)]) def on_enumeration_complete(self): self.class_list_model.sort(0, Qt.AscendingOrder) self.trace_list_model.sort(0, Qt.AscendingOrder) def on_event(self, data): trace, event, clazz, data = data if trace == 'java_trace': self.events_list.add_event({ 'event': event, 'class': clazz, 'data': data.replace(',', ', ') }) self._entries_lbl.setText('Events: %d' % len(self.events_list.data)) def pause_trace(self): self.app.dwarf.dwarf_api('stopJavaTracer') self.tracing = False def show_class_list_menu(self, pos): menu = QMenu() search = menu.addAction('Search') action = menu.exec_(self.class_list.mapToGlobal(pos)) if action: if action == search: self.class_list._on_cm_search() def start_trace(self): self.app.dwarf.dwarf_api('startJavaTracer', [self.trace_classes]) self.trace_depth = 0 self.tracing = True self.setup_splitter.setVisible(False) self.events_list.setVisible(True) def stop_trace(self): self.app.dwarf.dwarf_api('stopJavaTracer') self.tracing = False self.setup_splitter.setVisible(True) self.events_list.setVisible(False) self.events_list.clear() def trace_list_double_click(self, model_index): row = self.trace_list_model.itemFromIndex(model_index).row() if row != -1: trace_entry = self.trace_list_model.item(row, 0).text() if not trace_entry: return try: index = self.trace_classes.index(trace_entry) self.trace_classes.pop(index) self.trace_list_model.removeRow(row) except ValueError: pass def keyPressEvent(self, event): if event.modifiers() & Qt.ControlModifier: if event.key() == Qt.Key_F: self.search() super(JavaTracePanel, self).keyPressEvent(event)
class Window(QMainWindow): """ Defines the look and characteristics of the application's main window. """ title = _("Mu {}").format(__version__) icon = "icon" timer = None usb_checker = None serial = None repl = None plotter = None _zoom_in = pyqtSignal(int) _zoom_out = pyqtSignal(int) close_serial = pyqtSignal() write_to_serial = pyqtSignal(bytes) data_received = pyqtSignal(bytes) def zoom_in(self): """ Handles zooming in. """ self._zoom_in.emit(2) def zoom_out(self): """ Handles zooming out. """ self._zoom_out.emit(2) def connect_zoom(self, widget): """ Connects a referenced widget to the zoom related signals. """ self._zoom_in.connect(widget.zoomIn) self._zoom_out.connect(widget.zoomOut) @property def current_tab(self): """ Returns the currently focussed tab. """ return self.tabs.currentWidget() def set_read_only(self, is_readonly): """ Set all tabs read-only. """ self.read_only_tabs = is_readonly for tab in self.widgets: tab.setReadOnly(is_readonly) def get_load_path(self, folder): """ Displays a dialog for selecting a file to load. Returns the selected path. Defaults to start in the referenced folder. """ path, _ = QFileDialog.getOpenFileName(self.widget, 'Open file', folder, '*.py *.PY *.hex') logger.debug('Getting load path: {}'.format(path)) return path def get_save_path(self, folder): """ Displays a dialog for selecting a file to save. Returns the selected path. Defaults to start in the referenced folder. """ path, _ = QFileDialog.getSaveFileName(self.widget, 'Save file', folder) logger.debug('Getting save path: {}'.format(path)) return path def get_microbit_path(self, folder): """ Displays a dialog for locating the location of the BBC micro:bit in the host computer's filesystem. Returns the selected path. Defaults to start in the referenced folder. """ path = QFileDialog.getExistingDirectory(self.widget, 'Locate BBC micro:bit', folder, QFileDialog.ShowDirsOnly) logger.debug('Getting micro:bit path: {}'.format(path)) return path def add_tab(self, path, text, api): """ Adds a tab with the referenced path and text to the editor. """ new_tab = EditorPane(path, text) new_tab.connect_margin(self.breakpoint_toggle) new_tab_index = self.tabs.addTab(new_tab, new_tab.label) new_tab.set_api(api) @new_tab.modificationChanged.connect def on_modified(): modified_tab_index = self.tabs.currentIndex() self.tabs.setTabText(modified_tab_index, new_tab.label) self.update_title(new_tab.label) self.tabs.setCurrentIndex(new_tab_index) self.connect_zoom(new_tab) self.set_theme(self.theme) new_tab.setFocus() if self.read_only_tabs: new_tab.setReadOnly(self.read_only_tabs) def focus_tab(self, tab): index = self.tabs.indexOf(tab) self.tabs.setCurrentIndex(index) tab.setFocus() @property def tab_count(self): """ Returns the number of active tabs. """ return self.tabs.count() @property def widgets(self): """ Returns a list of references to the widgets representing tabs in the editor. """ return [self.tabs.widget(i) for i in range(self.tab_count)] @property def modified(self): """ Returns a boolean indication if there are any modified tabs in the editor. """ for widget in self.widgets: if widget.isModified(): return True return False def on_serial_read(self): """ Called when the connected device is ready to send data via the serial connection. It reads all the available data, emits the data_received signal with the received bytes and, if appropriate, emits the tuple_received signal with the tuple created from the bytes received. """ data = bytes(self.serial.readAll()) # get all the available bytes. self.data_received.emit(data) def open_serial_link(self, port): """ Creates a new serial link instance. """ self.input_buffer = [] self.serial = QSerialPort() self.serial.setPortName(port) if self.serial.open(QIODevice.ReadWrite): self.serial.dataTerminalReady = True if not self.serial.isDataTerminalReady(): # Using pyserial as a 'hack' to open the port and set DTR # as QtSerial does not seem to work on some Windows :( # See issues #281 and #302 for details. self.serial.close() pyser = serial.Serial(port) # open serial port w/pyserial pyser.dtr = True pyser.close() self.serial.open(QIODevice.ReadWrite) self.serial.setBaudRate(115200) self.serial.readyRead.connect(self.on_serial_read) else: raise IOError("Cannot connect to device on port {}".format(port)) def close_serial_link(self): """ Close and clean up the currently open serial link. """ self.serial.close() self.serial = None def add_filesystem(self, home, file_manager): """ Adds the file system pane to the application. """ self.fs_pane = FileSystemPane(home) self.fs = QDockWidget(_('Filesystem on micro:bit')) self.fs.setWidget(self.fs_pane) self.fs.setFeatures(QDockWidget.DockWidgetMovable) self.fs.setAllowedAreas(Qt.BottomDockWidgetArea) self.addDockWidget(Qt.BottomDockWidgetArea, self.fs) self.fs_pane.setFocus() file_manager.on_list_files.connect(self.fs_pane.on_ls) self.fs_pane.list_files.connect(file_manager.ls) self.fs_pane.microbit_fs.put.connect(file_manager.put) self.fs_pane.microbit_fs.delete.connect(file_manager.delete) self.fs_pane.microbit_fs.list_files.connect(file_manager.ls) self.fs_pane.local_fs.get.connect(file_manager.get) self.fs_pane.local_fs.list_files.connect(file_manager.ls) file_manager.on_put_file.connect(self.fs_pane.microbit_fs.on_put) file_manager.on_delete_file.connect(self.fs_pane.microbit_fs.on_delete) file_manager.on_get_file.connect(self.fs_pane.local_fs.on_get) file_manager.on_list_fail.connect(self.fs_pane.on_ls_fail) file_manager.on_put_fail.connect(self.fs_pane.on_put_fail) file_manager.on_delete_fail.connect(self.fs_pane.on_delete_fail) file_manager.on_get_fail.connect(self.fs_pane.on_get_fail) self.connect_zoom(self.fs_pane) return self.fs_pane def add_micropython_repl(self, port, name): """ Adds a MicroPython based REPL pane to the application. """ if not self.serial: self.open_serial_link(port) # Send a Control-C / keyboard interrupt. self.serial.write(b'\x03') repl_pane = MicroPythonREPLPane(serial=self.serial, theme=self.theme) self.data_received.connect(repl_pane.process_bytes) self.add_repl(repl_pane, name) def add_micropython_plotter(self, port, name): """ Adds a plotter that reads data from a serial connection. """ if not self.serial: self.open_serial_link(port) plotter_pane = PlotterPane(theme=self.theme) self.data_received.connect(plotter_pane.process_bytes) self.add_plotter(plotter_pane, name) def add_jupyter_repl(self, kernel_manager, kernel_client): """ Adds a Jupyter based REPL pane to the application. """ kernel_manager.kernel.gui = 'qt4' kernel_client.start_channels() ipython_widget = JupyterREPLPane(theme=self.theme) ipython_widget.kernel_manager = kernel_manager ipython_widget.kernel_client = kernel_client self.add_repl(ipython_widget, _('Python3 (Jupyter)')) def add_repl(self, repl_pane, name): """ Adds the referenced REPL pane to the application. """ self.repl_pane = repl_pane self.repl = QDockWidget(_('{} REPL').format(name)) self.repl.setWidget(repl_pane) self.repl.setFeatures(QDockWidget.DockWidgetMovable) self.repl.setAllowedAreas(Qt.BottomDockWidgetArea | Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.addDockWidget(Qt.BottomDockWidgetArea, self.repl) self.connect_zoom(self.repl_pane) self.repl_pane.set_theme(self.theme) self.repl_pane.setFocus() def add_plotter(self, plotter_pane, name): """ Adds the referenced plotter pane to the application. """ self.plotter_pane = plotter_pane self.plotter = QDockWidget(_('{} Plotter').format(name)) self.plotter.setWidget(plotter_pane) self.plotter.setFeatures(QDockWidget.DockWidgetMovable) self.plotter.setAllowedAreas(Qt.BottomDockWidgetArea | Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.addDockWidget(Qt.BottomDockWidgetArea, self.plotter) self.plotter_pane.set_theme(self.theme) self.plotter_pane.setFocus() def add_python3_runner(self, script_name, working_directory, interactive=False, debugger=False, command_args=None, runner=None): """ Display console output for the referenced Python script. The script will be run within the workspace_path directory. If interactive is True (default is False) the Python process will run in interactive mode (dropping the user into the REPL when the script completes). If debugger is True (default is False) the script will be run within a debug runner session. The debugger overrides the interactive flag (you cannot run the debugger in interactive mode). If there is a list of command_args (the default is None) then these will be passed as further arguments into the command run in the new process. If runner is give, this is used as the command to start the Python process. """ self.process_runner = PythonProcessPane(self) self.runner = QDockWidget(_("Running: {}").format( os.path.basename(script_name))) self.runner.setWidget(self.process_runner) self.runner.setFeatures(QDockWidget.DockWidgetMovable) self.runner.setAllowedAreas(Qt.BottomDockWidgetArea | Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.addDockWidget(Qt.BottomDockWidgetArea, self.runner) self.process_runner.start_process(script_name, working_directory, interactive, debugger, command_args, runner) self.process_runner.setFocus() self.connect_zoom(self.process_runner) return self.process_runner def add_debug_inspector(self): """ Display a debug inspector to view the call stack. """ self.debug_inspector = DebugInspector() self.debug_model = QStandardItemModel() self.debug_inspector.setModel(self.debug_model) self.debug_inspector.setUniformRowHeights(True) self.inspector = QDockWidget(_('Debug Inspector')) self.inspector.setWidget(self.debug_inspector) self.inspector.setFeatures(QDockWidget.DockWidgetMovable) self.inspector.setAllowedAreas(Qt.BottomDockWidgetArea | Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.addDockWidget(Qt.RightDockWidgetArea, self.inspector) self.connect_zoom(self.debug_inspector) def update_debug_inspector(self, locals_dict): """ Given the contents of a dict representation of the locals in the current stack frame, update the debug inspector with the new values. """ excluded_names = ['__builtins__', '__debug_code__', '__debug_script__', ] names = sorted([x for x in locals_dict if x not in excluded_names]) self.debug_model.clear() self.debug_model.setHorizontalHeaderLabels([_('Name'), _('Value'), ]) for name in names: try: # DANGER! val = eval(locals_dict[name]) except Exception: val = None if isinstance(val, list): # Show a list consisting of rows of position/value list_item = QStandardItem(name) for i, i_val in enumerate(val): list_item.appendRow([ QStandardItem(str(i)), QStandardItem(repr(i_val)) ]) self.debug_model.appendRow([ list_item, QStandardItem(_('(A list of {} items.)').format(len(val))) ]) elif isinstance(val, dict): # Show a dict consisting of rows of key/value pairs. dict_item = QStandardItem(name) for k, k_val in val.items(): dict_item.appendRow([ QStandardItem(repr(k)), QStandardItem(repr(k_val)) ]) self.debug_model.appendRow([ dict_item, QStandardItem(_('(A dict of {} items.)').format(len(val))) ]) else: self.debug_model.appendRow([ QStandardItem(name), QStandardItem(locals_dict[name]), ]) def remove_filesystem(self): """ Removes the file system pane from the application. """ if hasattr(self, 'fs') and self.fs: self.fs_pane = None self.fs.setParent(None) self.fs.deleteLater() self.fs = None def remove_repl(self): """ Removes the REPL pane from the application. """ if self.repl: self.repl_pane = None self.repl.setParent(None) self.repl.deleteLater() self.repl = None if not self.plotter: self.serial = None def remove_plotter(self): """ Removes the plotter pane from the application. """ if self.plotter: self.plotter_pane = None self.plotter.setParent(None) self.plotter.deleteLater() self.plotter = None if not self.repl: self.serial = None def remove_python_runner(self): """ Removes the runner pane from the application. """ if hasattr(self, 'runner') and self.runner: self.process_runner = None self.runner.setParent(None) self.runner.deleteLater() self.runner = None def remove_debug_inspector(self): """ Removes the debug inspector pane from the application. """ if hasattr(self, 'inspector') and self.inspector: self.debug_inspector = None self.debug_model = None self.inspector.setParent(None) self.inspector.deleteLater() self.inspector = None def set_theme(self, theme): """ Sets the theme for the REPL and editor tabs. """ self.theme = theme if theme == 'contrast': self.setStyleSheet(CONTRAST_STYLE) new_theme = ContrastTheme new_icon = 'theme_day' elif theme == 'night': new_theme = NightTheme new_icon = 'theme_contrast' self.setStyleSheet(NIGHT_STYLE) else: self.setStyleSheet(DAY_STYLE) new_theme = DayTheme new_icon = 'theme' for widget in self.widgets: widget.set_theme(new_theme) self.button_bar.slots['theme'].setIcon(load_icon(new_icon)) if hasattr(self, 'repl') and self.repl: self.repl_pane.set_theme(theme) if hasattr(self, 'plotter') and self.plotter: self.plotter_pane.set_theme(theme) def show_logs(self, log, theme): """ Display the referenced content of the log. """ log_box = LogDisplay() log_box.setup(log, theme) log_box.exec() def show_message(self, message, information=None, icon=None): """ Displays a modal message to the user. If information is passed in this will be set as the additional informative text in the modal dialog. Since this mechanism will be used mainly for warning users that something is awry the default icon is set to "Warning". It's possible to override the icon to one of the following settings: NoIcon, Question, Information, Warning or Critical. """ message_box = QMessageBox(self) message_box.setText(message) message_box.setWindowTitle('Mu') if information: message_box.setInformativeText(information) if icon and hasattr(message_box, icon): message_box.setIcon(getattr(message_box, icon)) else: message_box.setIcon(message_box.Warning) logger.debug(message) logger.debug(information) message_box.exec() def show_confirmation(self, message, information=None, icon=None): """ Displays a modal message to the user to which they need to confirm or cancel. If information is passed in this will be set as the additional informative text in the modal dialog. Since this mechanism will be used mainly for warning users that something is awry the default icon is set to "Warning". It's possible to override the icon to one of the following settings: NoIcon, Question, Information, Warning or Critical. """ message_box = QMessageBox() message_box.setText(message) message_box.setWindowTitle(_('Mu')) if information: message_box.setInformativeText(information) if icon and hasattr(message_box, icon): message_box.setIcon(getattr(message_box, icon)) else: message_box.setIcon(message_box.Warning) message_box.setStandardButtons(message_box.Cancel | message_box.Ok) message_box.setDefaultButton(message_box.Cancel) logger.debug(message) logger.debug(information) return message_box.exec() def update_title(self, filename=None): """ Updates the title bar of the application. If a filename (representing the name of the file currently the focus of the editor) is supplied, append it to the end of the title. """ title = self.title if filename: title += ' - ' + filename self.setWindowTitle(title) def autosize_window(self): """ Makes the editor 80% of the width*height of the screen and centres it. """ screen = QDesktopWidget().screenGeometry() w = int(screen.width() * 0.8) h = int(screen.height() * 0.8) self.resize(w, h) size = self.geometry() self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) def reset_annotations(self): """ Resets the state of annotations on the current tab. """ self.current_tab.reset_annotations() def annotate_code(self, feedback, annotation_type): """ Given a list of annotations about the code in the current tab, add the annotations to the editor window so the user can make appropriate changes. """ self.current_tab.annotate_code(feedback, annotation_type) def show_annotations(self): """ Show the annotations added to the current tab. """ self.current_tab.show_annotations() def setup(self, breakpoint_toggle, theme): """ Sets up the window. Defines the various attributes of the window and defines how the user interface is laid out. """ self.theme = theme self.breakpoint_toggle = breakpoint_toggle # Give the window a default icon, title and minimum size. self.setWindowIcon(load_icon(self.icon)) self.update_title() self.read_only_tabs = False self.setMinimumSize(800, 400) self.widget = QWidget() widget_layout = QVBoxLayout() self.widget.setLayout(widget_layout) self.button_bar = ButtonBar(self.widget) self.tabs = FileTabs() self.tabs.setMovable(True) self.setCentralWidget(self.tabs) self.status_bar = StatusBar(parent=self) self.setStatusBar(self.status_bar) self.addToolBar(self.button_bar) self.show() self.autosize_window() def resizeEvent(self, resizeEvent): """ Respond to window getting too small for the button bar to fit well. """ size = resizeEvent.size() self.button_bar.set_responsive_mode(size.width(), size.height()) def select_mode(self, modes, current_mode, theme): """ Display the mode selector dialog and return the result. """ mode_select = ModeSelector() mode_select.setup(modes, current_mode, theme) mode_select.exec() try: return mode_select.get_mode() except Exception as ex: return None def change_mode(self, mode): """ Given a an object representing a mode, recreates the button bar with the expected functionality. """ self.button_bar.change_mode(mode) # Update the autocomplete / tooltip APIs for each tab to the new mode. api = mode.api() for widget in self.widgets: widget.set_api(api) def set_usb_checker(self, duration, callback): """ Sets up a timer that polls for USB changes via the "callback" every "duration" seconds. """ self.usb_checker = QTimer() self.usb_checker.timeout.connect(callback) self.usb_checker.start(duration * 1000) def set_timer(self, duration, callback): """ Set a repeating timer to call "callback" every "duration" seconds. """ self.timer = QTimer() self.timer.timeout.connect(callback) self.timer.start(duration * 1000) # Measured in milliseconds. def stop_timer(self): """ Stop the repeating timer. """ if self.timer: self.timer.stop() self.timer = None def connect_tab_rename(self, handler, shortcut): """ Connect the double-click event on a tab and the keyboard shortcut to the referenced handler (causing the Save As dialog). """ self.tabs.shortcut = QShortcut(QKeySequence(shortcut), self) self.tabs.shortcut.activated.connect(handler) self.tabs.tabBarDoubleClicked.connect(handler) def open_directory_from_os(self, path): """ Given the path to a directoy, open the OS's built in filesystem explorer for that path. Works with Windows, OSX and Linux. """ if sys.platform == 'win32': # Windows os.startfile(path) elif sys.platform == 'darwin': # OSX os.system('open "{}"'.format(path)) else: # Assume freedesktop.org on unix-y. os.system('xdg-open "{}"'.format(path))
class VideoPlayer(QWidget): def __init__(self, aPath, parent=None): super(VideoPlayer, self).__init__(parent) self.setAttribute(Qt.WA_NoSystemBackground, True) self.setAcceptDrops(True) self.mediaPlayer = QMediaPlayer(None, QMediaPlayer.StreamPlayback) self.mediaPlayer.setVolume(80) self.videoWidget = QVideoWidget(self) self.videoWidget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.videoWidget.setMinimumSize(QSize(640, 360)) self.lbl = QLineEdit('00:00:00') self.lbl.setReadOnly(True) self.lbl.setFixedWidth(70) self.lbl.setUpdatesEnabled(True) self.lbl.setStyleSheet(stylesheet(self)) self.elbl = QLineEdit('00:00:00') self.elbl.setReadOnly(True) self.elbl.setFixedWidth(70) self.elbl.setUpdatesEnabled(True) self.elbl.setStyleSheet(stylesheet(self)) self.playButton = QPushButton() self.playButton.setEnabled(False) self.playButton.setFixedWidth(32) self.playButton.setStyleSheet("background-color: black") self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay)) self.playButton.clicked.connect(self.play) self.positionSlider = QSlider(Qt.Horizontal, self) self.positionSlider.setStyleSheet(stylesheet(self)) self.positionSlider.setRange(0, 100) self.positionSlider.sliderMoved.connect(self.setPosition) self.positionSlider.sliderMoved.connect(self.handleLabel) self.positionSlider.setSingleStep(2) self.positionSlider.setPageStep(20) self.positionSlider.setAttribute(Qt.WA_TranslucentBackground, True) self.clip = QApplication.clipboard() self.process = QProcess(self) self.process.readyRead.connect(self.dataReady) self.process.finished.connect(self.playFromURL) self.myurl = "" # channel list self.channelList = QListView(self) self.channelList.setMinimumSize(QSize(150, 0)) self.channelList.setMaximumSize(QSize(150, 4000)) self.channelList.setFrameShape(QFrame.Box) self.channelList.setObjectName("channelList") self.channelList.setStyleSheet("background-color: black; color: #585858;") self.channelList.setFocus() # for adding items to list must create a model self.model = QStandardItemModel() self.channelList.setModel(self.model) self.controlLayout = QHBoxLayout() self.controlLayout.setContentsMargins(5, 0, 5, 0) self.controlLayout.addWidget(self.playButton) self.controlLayout.addWidget(self.lbl) self.controlLayout.addWidget(self.positionSlider) self.controlLayout.addWidget(self.elbl) self.mainLayout = QHBoxLayout() # contains video and cotrol widgets to the left side self.layout = QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.layout.addWidget(self.videoWidget) self.layout.addLayout(self.controlLayout) # adds channels list to the right self.mainLayout.addLayout(self.layout) self.mainLayout.addWidget(self.channelList) self.setLayout(self.mainLayout) self.myinfo = "©2020\nTIVOpy v1.0" self.widescreen = True #### shortcuts #### self.shortcut = QShortcut(QKeySequence("q"), self) self.shortcut.activated.connect(self.handleQuit) self.shortcut = QShortcut(QKeySequence("u"), self) self.shortcut.activated.connect(self.playFromURL) self.shortcut = QShortcut(QKeySequence(Qt.Key_Space), self) self.shortcut.activated.connect(self.play) self.shortcut = QShortcut(QKeySequence(Qt.Key_F), self) self.shortcut.activated.connect(self.handleFullscreen) self.shortcut = QShortcut(QKeySequence(Qt.Key_Escape), self) self.shortcut.activated.connect(self.exitFullscreen) self.shortcut.activated.connect(self.handleFullscreen) self.shortcut = QShortcut(QKeySequence("i"), self) self.shortcut.activated.connect(self.handleInfo) self.shortcut = QShortcut(QKeySequence("s"), self) self.shortcut.activated.connect(self.toggleSlider) self.shortcut = QShortcut(QKeySequence(Qt.Key_Right), self) self.shortcut.activated.connect(self.forwardSlider) self.shortcut = QShortcut(QKeySequence(Qt.Key_Left), self) self.shortcut.activated.connect(self.backSlider) self.mediaPlayer.setVideoOutput(self.videoWidget) self.mediaPlayer.stateChanged.connect(self.mediaStateChanged) self.mediaPlayer.positionChanged.connect(self.positionChanged) self.mediaPlayer.positionChanged.connect(self.handleLabel) self.mediaPlayer.durationChanged.connect(self.durationChanged) self.mediaPlayer.error.connect(self.handleError) self.populateChannelList() self.selectChannel() self.initialPlay() def playFromURL(self): self.mediaPlayer.pause() self.myurl = self.clip.text() self.mediaPlayer.setMedia(QMediaContent(QUrl(self.myurl))) self.playButton.setEnabled(True) self.mediaPlayer.play() self.hideSlider() print(self.myurl) def dataReady(self): self.myurl = str(self.process.readAll(), encoding='utf8').rstrip() ### self.myurl = self.myurl.partition("\n")[0] print(self.myurl) self.clip.setText(self.myurl) self.playFromURL() def play(self): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.mediaPlayer.pause() else: self.mediaPlayer.play() def mediaStateChanged(self, state): if self.mediaPlayer.state() == QMediaPlayer.PlayingState: self.playButton.setIcon( self.style().standardIcon(QStyle.SP_MediaPause)) else: self.playButton.setIcon( self.style().standardIcon(QStyle.SP_MediaPlay)) def positionChanged(self, position): self.positionSlider.setValue(position) def durationChanged(self, duration): self.positionSlider.setRange(0, duration) mtime = QTime(0, 0, 0, 0) mtime = mtime.addMSecs(self.mediaPlayer.duration()) self.elbl.setText(mtime.toString()) def setPosition(self, position): self.mediaPlayer.setPosition(position) def handleError(self): self.playButton.setEnabled(False) print("Error: ", self.mediaPlayer.errorString()) def handleQuit(self): self.mediaPlayer.stop() print("Goodbye ...") app.quit() def contextMenuRequested(self, point): menu = QMenu() actionURL = menu.addAction(QIcon.fromTheme("browser"), "URL from Clipboard (u)") menu.addSeparator() actionToggle = menu.addAction(QIcon.fromTheme("next"), "Show / Hide Channels (s)") actionFull = menu.addAction(QIcon.fromTheme("view-fullscreen"), "Fullscreen (f)") menu.addSeparator() actionInfo = menu.addAction(QIcon.fromTheme("help-about"), "About (i)") menu.addSeparator() actionQuit = menu.addAction(QIcon.fromTheme("application-exit"), "Exit (q)") actionQuit.triggered.connect(self.handleQuit) actionFull.triggered.connect(self.handleFullscreen) actionInfo.triggered.connect(self.handleInfo) actionToggle.triggered.connect(self.toggleSlider) actionURL.triggered.connect(self.playFromURL) menu.exec_(self.mapToGlobal(point)) def wheelEvent(self, event): mscale = event.angleDelta().y() / 13 self.mediaPlayer.setVolume(self.mediaPlayer.volume() + mscale) print("Volume: " + str(self.mediaPlayer.volume())) def mouseDoubleClickEvent(self, event): if event.buttons() == Qt.LeftButton: self.handleFullscreen() def handleFullscreen(self): if self.windowState() and Qt.WindowFullScreen: self.showNormal() else: self.showFullScreen() def exitFullscreen(self): self.showNormal() def handleInfo(self): QMessageBox.about(self, "About", self.myinfo) def toggleSlider(self): if self.positionSlider.isVisible(): self.hideSlider() else: self.showSlider() def hideSlider(self): self.channelList.hide() self.playButton.hide() self.lbl.hide() self.positionSlider.hide() self.elbl.hide() def showSlider(self): self.channelList.show() self.playButton.show() self.lbl.show() self.positionSlider.show() self.elbl.show() self.channelList.setFocus() def forwardSlider(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() + 1000 * 60) def backSlider(self): self.mediaPlayer.setPosition(self.mediaPlayer.position() - 1000 * 60) def volumeUp(self): self.mediaPlayer.setVolume(self.mediaPlayer.volume() + 10) print("Volume: " + str(self.mediaPlayer.volume())) def volumeDown(self): self.mediaPlayer.setVolume(self.mediaPlayer.volume() - 10) print("Volume: " + str(self.mediaPlayer.volume())) def dragEnterEvent(self, event): if event.mimeData().hasUrls(): event.accept() elif event.mimeData().hasText(): event.accept() else: event.ignore() def dropEvent(self, event): print("drop") if event.mimeData().hasUrls(): url = event.mimeData().urls()[0].toString() print("url = ", url) self.mediaPlayer.stop() self.mediaPlayer.setMedia(QMediaContent(QUrl(url))) self.playButton.setEnabled(True) self.mediaPlayer.play() elif event.mimeData().hasText(): mydrop = event.mimeData().text() print("generic url = ", mydrop) self.mediaPlayer.setMedia(QMediaContent(QUrl(mydrop))) self.playButton.setEnabled(True) self.mediaPlayer.play() self.hideSlider() def loadFilm(self, f): self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile(f))) self.playButton.setEnabled(True) self.mediaPlayer.play() def populateChannelList(self): # file must be in same directory as the script FILEPATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "canaletv.txt") # lines from file with "channel name" -- "link" channelArray = [] # split file by line and adding it to the array with open(FILEPATH) as f: for line in f: channelArray.append(line.rstrip()) # dictionary with key = channel name and value = link self.channelDict = dict(ch.split(" -- ") for ch in channelArray) for channel in self.channelDict.keys(): item = QStandardItem(channel) self.model.appendRow(item) def selectedItemBehavior(self, index): # gets the link for the selected channel and plays it itms = self.channelList.selectedIndexes() for it in itms: channel = it.data() link = self.channelDict[channel] self.mediaPlayer.setMedia(QMediaContent(QUrl(link))) self.play() def selectChannel(self): # selecting channel from sidebar calls selectedItemBehavior self.selModel = self.channelList.selectionModel() self.selModel.selectionChanged.connect(self.selectedItemBehavior) def initialPlay(self): # play somenting when app opens self.mediaPlayer.setMedia(QMediaContent(QUrl("https://vid.hls.protv.ro/proxhdn/proxhd_3_34/index.m3u8?1"))) self.play() def handleLabel(self): self.lbl.clear() mtime = QTime(0, 0, 0, 0) self.time = mtime.addMSecs(self.mediaPlayer.position()) self.lbl.setText(self.time.toString())