class ModelNetClients: def __init__(self, table_view): self._model_net_clients = QStandardItemModel(table_view) self._table_view = table_view @property def list_clients(self): return self._model_net_clients def _update(self): self._table_view.setModel(self._model_net_clients) self._table_view.resizeColumnsToContents() self._table_view.scrollToBottom() self._table_view.selectRow(self._model_net_clients.rowCount() - 1) self._table_view.setModel(self._model_net_clients) def add_row(self, row_str): item = QStandardItem(row_str) item.setEditable(False) self._model_net_clients.appendRow([item]) self._update() def del_row(self, row_str): items = self._model_net_clients.findItems(row_str) if items: self._model_net_clients.removeRow(items[0].row()) self._update() def clear(self): self._model_net_clients.clear()
class NameList(QDockWidget): def __init__(self, window): super(NameList, self).__init__('Current Plots') self.namelist_model = QStandardItemModel() self.namelist_view = QListView() self.namelist_view.setModel(self.namelist_model) self.setWidget(self.namelist_view) self.window = window self.plot_dict = {} self.namelist_view.doubleClicked.connect(self.activate_item) self.namelist_view.setContextMenuPolicy(QtConst.ActionsContextMenu) delete_action = QAction("Delete Selected", self.namelist_view) ### pause_action = QAction("Stop Script", self.namelist_view) delete_action.triggered.connect(self.delete_item) pause_action.triggered.connect(self.pause) self.namelist_view.addAction(delete_action) ### self.namelist_view.addAction(pause_action) def activate_item(self, index): item = self.namelist_model.itemFromIndex(index) plot = self.plot_dict[str(item.text())] if plot.closed: plot.closed = False self.window.add_plot(plot) def delete_item(self): index = self.namelist_view.currentIndex() item = self.namelist_model.itemFromIndex(index) del self[str(item.text())] def pause(self): sock = socket.socket() sock.connect(('localhost', 9091)) sock.send(b'stop') sock.close() def __getitem__(self, item): return self.plot_dict[item] def __setitem__(self, name, plot): model = QStandardItem(name) model.setEditable(False) self.namelist_model.appendRow(model) self.plot_dict[name] = plot def __contains__(self, value): return value in self.plot_dict def __delitem__(self, name): self.namelist_model.removeRow(self.namelist_model.findItems(name)[0].index().row()) self.plot_dict[name].close() del self.plot_dict[name] def keys(self): return list(self.plot_dict.keys());
class TreeView(QTreeView): def __init__(self): super().__init__() self.model = QStandardItemModel(4, 2) self.model.setHeaderData(0, Qt.Horizontal, "service") self.model.setHeaderData(1, Qt.Horizontal, "Details") ''' item1 = QStandardItem("avahi-daemon") item2 = QStandardItem("bluetooth") item3 = QStandardItem("crond") item4 = QStandardItem("cups") item5 = QStandardItem("fifth") self.model.setItem(0, 0, item1) self.model.setItem(1, 0, item2) self.model.setItem(2, 0, item3) self.model.setItem(3, 0, item4) item4.appendRow(item5) parent = QModelIndex() for i in range(0, 4): parent = self.model.index(0, 0, parent) self.model.insertRows(0, 1, parent) self.model.insertColumns(0, 1, parent) index = self.model.index(0, 0, parent) self.model.setData(index, i) ''' self.setModel(self.model) def returnTheItems(self): return self.model.findItems("*", Qt.MatchWildcard | Qt.MatchRecursive) # item1.setIcon() def mouseDoubleClickEvent(self, event): if event.button() == Qt.LeftButton: index0 = self.currentIndex() print(index0.data().toString())
class ComboItemModel(FilterModel): """Класс модели для комбобоксов с фильтрацией""" def __init__(self, parent=None): FilterModel.__init__(self, parent) self.parent = parent self._model = QStandardItemModel(0, 0) self._display = "" self.setSourceModel(self._model) self.setDynamicSortFilter(True) @property def display(self) -> str: """возвращает имя отображаемого поля""" return self._display def model(self) -> QStandardItemModel: """возвращает модель""" return self._model def fill(self, rows: list, display: str): """заполняет комбобокс элементами из списка""" self._display = display for _, value in enumerate(rows): self._model.appendRow(self.createRow(value)) def createRow(self, data: dict) -> QStandardItem: """создаёт элемент-строку для комбобокса""" result = QStandardItem() result.setText(data[self._display] if self._display in data else 'Error') result.setData(data, Qt.UserRole) return result def findIndex(self, value) -> int: """возвращает индекс элемента содержащего значение""" items = self._model.findItems('', Qt.MatchContains) if not items: return 0 # функция поиска если value - словарь if isinstance(value, dict): key = next(iter(value)) val = value[key] func = lambda data: key in data and data[key] == val # -/- если value - значение else: func = lambda data: value in data.values() return next((index for index, item in enumerate(items) \ if func(item.data(Qt.UserRole))), 0) def getItem(self, index=-1) -> QStandardItem: """возвращает элемент по индексу""" items = self._model.findItems('', Qt.MatchContains) if not items: return None if 0 <= index < self.rowCount(): items = self._model.findItems('', Qt.MatchContains) return items[index] index = self.parent.currentIndex() return items[index] def selectContains(self, value): """устанавливает текущим элемент содержащий значение""" index = self.findIndex(value) self.applyFilter() self.select(index) def select(self, index): """устанавливает текущим элемент по индексу""" if self.parent: self.parent.setCurrentIndex(index) def checkAlreadySelected(self, condition): """проверяет выбран ли элемент списка отвечающий условию""" if not condition or not self.parent: return False item = self.parent.currentData() if not item: return False if isinstance(condition, dict): key = next(iter(condition)) return (key in item) and (item[key] == condition[key]) return condition in item.values()
class NameList(QDockWidget): def __init__(self, window): super(NameList, self).__init__('Current Plots') self.namelist_model = QStandardItemModel() self.namelist_view = QListView() self.namelist_view.setModel(self.namelist_model) self.setWidget(self.namelist_view) self.window = window self.plot_dict = {} self.namelist_view.doubleClicked.connect(self.activate_item) self.namelist_view.setContextMenuPolicy(QtConst.ActionsContextMenu) delete_action = QAction("Delete Selected", self.namelist_view) ### pause_action = QAction("Stop Script", self.namelist_view) delete_action.triggered.connect(self.delete_item) pause_action.triggered.connect(self.pause) self.namelist_view.addAction(delete_action) self.namelist_view.addAction(pause_action) open_action = QAction('Open 1D Data', self) open_action.triggered.connect(self.open_file_dialog) self.namelist_view.addAction(open_action) open_action_2 = QAction('Open 2D Data', self) open_action_2.triggered.connect(self.open_file_dialog_2) self.namelist_view.addAction(open_action_2) def open_file_dialog(self, directory='', header=0): file_path = self.file_dialog(directory=directory) header_array = [] file_to_read = open(file_path, 'r') for i, line in enumerate(file_to_read): if i is header: break temp = line.split(":") header_array.append(temp) file_to_read.close() temp = np.genfromtxt(file_path, dtype=float, delimiter=',', skip_header=1) data = np.transpose(temp) name_plot = datetime.now().strftime('%Y-%m-%d %H:%M:%S') pw = self.window.add_new_plot(1, name_plot) pw.plot(data[0], data[1], parametric=True, name=file_path, xname='X', xscale ='Arb. U.',\ yname='Y', yscale ='Arb. u.', scatter='False') def open_file_dialog_2(self, directory='', header=0): file_path = self.file_dialog(directory=directory) header_array = [] file_to_read = open(file_path, 'r') for i, line in enumerate(file_to_read): if i is header: break temp = line.split(":") header_array.append(temp) file_to_read.close() temp = np.genfromtxt(file_path, dtype=float, delimiter=',') data = temp name_plot = datetime.now().strftime('%Y-%m-%d %H:%M:%S') pw = self.window.add_new_plot(2, name_plot) pw.setAxisLabels(xname='X', xscale ='Arb. U.',yname='X', yscale ='Arb. U.',\ zname='X', zscale ='Arb. U.') pw.setImage(data, axes={'y': 0, 'x': 1}) def file_dialog(self, directory=''): root = tkinter.Tk() root.withdraw() file_path = filedialog.askopenfilename(**dict( initialdir = directory, filetypes = [("CSV", "*.csv"), ("TXT", "*.txt"),\ ("DAT", "*.dat"), ("all", "*.*")], title = 'Select file to open') ) return file_path def activate_item(self, index): item = self.namelist_model.itemFromIndex(index) plot = self.plot_dict[str(item.text())] if plot.closed: plot.closed = False self.window.add_plot(plot) def delete_item(self): index = self.namelist_view.currentIndex() item = self.namelist_model.itemFromIndex(index) del self[str(item.text())] def pause(self): sock = socket.socket() sock.connect(('localhost', 9091)) sock.send(b'Script stopped') sock.close() def __getitem__(self, item): return self.plot_dict[item] def __setitem__(self, name, plot): model = QStandardItem(name) model.setEditable(False) self.namelist_model.appendRow(model) self.plot_dict[name] = plot def __contains__(self, value): return value in self.plot_dict def __delitem__(self, name): self.namelist_model.removeRow( self.namelist_model.findItems(name)[0].index().row()) self.plot_dict[name].close() del self.plot_dict[name] def keys(self): return list(self.plot_dict.keys())
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 BookmarksWidget(QWidget): def __init__(self, parent=None): # pylint: disable=too-many-statements super(BookmarksWidget, self).__init__(parent=parent) self._app_window = parent if self._app_window.dwarf is None: print('BookmarksPanel created before Dwarf exists') return self.bookmarks = {} self._bookmarks_list = DwarfListView() self._bookmarks_list.doubleClicked.connect(self._on_double_clicked) self._bookmarks_list.setContextMenuPolicy(Qt.CustomContextMenu) self._bookmarks_list.customContextMenuRequested.connect( self._on_contextmenu) self._bookmarks_model = QStandardItemModel(0, 2) self._bookmarks_model.setHeaderData(0, Qt.Horizontal, 'Address') self._bookmarks_model.setHeaderData(1, Qt.Horizontal, 'Notes') self._bookmarks_list.setModel(self._bookmarks_model) self._bookmarks_list.header().setStretchLastSection(False) self._bookmarks_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents | QHeaderView.Interactive) self._bookmarks_list.header().setSectionResizeMode( 1, QHeaderView.Stretch | QHeaderView.Interactive) v_box = QVBoxLayout(self) v_box.setContentsMargins(0, 0, 0, 0) v_box.addWidget(self._bookmarks_list) #header = QHeaderView(Qt.Horizontal, self) h_box = QHBoxLayout() h_box.setContentsMargins(5, 2, 5, 5) self.btn1 = QPushButton( QIcon(utils.resource_path('assets/icons/plus.svg')), '') self.btn1.setFixedSize(20, 20) self.btn1.clicked.connect(lambda: self._create_bookmark(-1)) btn2 = QPushButton(QIcon(utils.resource_path('assets/icons/dash.svg')), '') btn2.setFixedSize(20, 20) btn2.clicked.connect(self.delete_items) btn3 = QPushButton( QIcon(utils.resource_path('assets/icons/trashcan.svg')), '') btn3.setFixedSize(20, 20) btn3.clicked.connect(self.clear_list) h_box.addWidget(self.btn1) h_box.addWidget(btn2) h_box.addSpacerItem( QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred)) h_box.addWidget(btn3) # header.setLayout(h_box) # header.setFixedHeight(25) # v_box.addWidget(header) v_box.addLayout(h_box) self.setLayout(v_box) self._bold_font = QFont(self._bookmarks_list.font()) self._bold_font.setBold(True) shortcut_addnative = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_B), self._app_window, self._create_bookmark) shortcut_addnative.setAutoRepeat(False) # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def delete_items(self): """ Delete selected Items """ index = self._bookmarks_list.selectionModel().currentIndex().row() if index != -1: self._on_delete_bookmark(index) self._bookmarks_model.removeRow(index) def clear_list(self): """ Clear the List """ # go through all items and tell it gets removed for item in range(self._bookmarks_model.rowCount()): self._on_delete_bookmark(item) if self._bookmarks_model.rowCount() > 0: # something was wrong it should be empty self._bookmarks_model.removeRows(0, self._bookmarks_model.rowCount()) # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _on_double_clicked(self, index): index = self._bookmarks_list.selectionModel().currentIndex().row() if index != -1: addr = self._bookmarks_model.item(index, 0).text() if addr: self._app_window.jump_to_address(addr) def _on_contextmenu(self, pos): context_menu = QMenu(self) index = self._bookmarks_list.indexAt(pos).row() if index != -1: context_menu.addAction( 'Copy address', lambda: utils.copy_hex_to_clipboard( self._bookmarks_model.item(index, 0).text())) context_menu.addAction( 'Jump to address', lambda: self._app_window.jump_to_address( self._bookmarks_model.item(index, 0).text())) # todo: add hook address menu context_menu.addSeparator() context_menu.addAction('Edit', lambda: self._create_bookmark(index=index)) context_menu.addAction('Delete', lambda: self._on_delete_bookmark(index)) context_menu.addSeparator() if self._bookmarks_list.search_enabled: context_menu.addSeparator() context_menu.addAction('Search', self._bookmarks_list._on_cm_search) context_menu.addSeparator() context_menu.addAction('New', self._create_bookmark) global_pt = self._bookmarks_list.mapToGlobal(pos) context_menu.exec(global_pt) # + button def _create_bookmark(self, index=-1, ptr=''): note = '' if ptr == '': if isinstance(index, int) and index >= 0: ptr = self._bookmarks_model.item(index, 0).text() note = self._bookmarks_model.item(index, 1).text() ptr, _ = InputDialog.input_pointer(parent=self._app_window, input_content=ptr) else: if not isinstance(ptr, int): try: if ptr.startswith('0x'): ptr = int(ptr, 16) else: ptr = int(ptr) except ValueError: ptr = 0 if ptr > 0: ptr = hex(ptr) if self._bookmarks_list.uppercase_hex: ptr = ptr.upper().replace('0X', '0x') index = self._bookmarks_model.findItems(ptr, Qt.MatchExactly) if len(index) > 0: index = index[0].row() note = self._bookmarks_model.item(index, 1).text() else: index = -1 accept, note = InputDialog.input(hint='Insert notes for %s' % ptr, input_content=note) if accept: if index < 0: self.insert_bookmark(ptr, note) else: item = self._bookmarks_model.item(index, 0) item.setText(ptr) item = self._bookmarks_model.item(index, 1) item.setText(note) self.bookmarks[ptr] = note def insert_bookmark(self, ptr_as_hex, note): if self._bookmarks_list.uppercase_hex: ptr_as_hex = ptr_as_hex.upper().replace('0X', '0x') self._bookmarks_model.appendRow( [QStandardItem(ptr_as_hex), QStandardItem(note)]) self._bookmarks_list.resizeColumnToContents(0) # shortcuts/menu def _on_delete_bookmark(self, index): ptr = self._bookmarks_model.item(index, 0).text() del self.bookmarks[ptr] self._bookmarks_model.removeRow(index)
class HooksWidget(QWidget): """ HooksPanel Signals: onShowMemoryRequest(str) - ptr onHookChanged(str) - ptr onHookRemoved(str) - ptr """ onHookChanged = pyqtSignal(str, name='onHookChanged') onHookRemoved = pyqtSignal(str, name='onHookRemoved') def __init__(self, parent=None): # pylint: disable=too-many-statements super(HooksWidget, self).__init__(parent=parent) self._app_window = parent if self._app_window.dwarf is None: print('HooksPanel created before Dwarf exists') return # connect to dwarf self._app_window.dwarf.onAddJavaHook.connect(self._on_add_hook) self._app_window.dwarf.onAddNativeHook.connect(self._on_add_hook) self._app_window.dwarf.onAddNativeOnLoadHook.connect(self._on_add_hook) self._app_window.dwarf.onAddJavaOnLoadHook.connect(self._on_add_hook) self._app_window.dwarf.onHitNativeOnLoad.connect( self._on_hit_native_on_load) self._app_window.dwarf.onHitJavaOnLoad.connect( self._on_hit_java_on_load) self._app_window.dwarf.onDeleteHook.connect(self._on_hook_deleted) self._hooks_list = DwarfListView() self._hooks_list.doubleClicked.connect(self._on_double_clicked) self._hooks_list.setContextMenuPolicy(Qt.CustomContextMenu) self._hooks_list.customContextMenuRequested.connect( self._on_context_menu) self._hooks_model = QStandardItemModel(0, 5) self._hooks_model.setHeaderData(0, Qt.Horizontal, 'Address') self._hooks_model.setHeaderData(1, Qt.Horizontal, 'T') self._hooks_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._hooks_model.setHeaderData(2, Qt.Horizontal, 'Input') self._hooks_model.setHeaderData(3, Qt.Horizontal, '{}') self._hooks_model.setHeaderData(3, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._hooks_model.setHeaderData(4, Qt.Horizontal, '<>') self._hooks_model.setHeaderData(4, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._hooks_list.setModel(self._hooks_model) self._hooks_list.header().setStretchLastSection(False) self._hooks_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents | QHeaderView.Interactive) self._hooks_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self._hooks_list.header().setSectionResizeMode(2, QHeaderView.Stretch) self._hooks_list.header().setSectionResizeMode( 3, QHeaderView.ResizeToContents) self._hooks_list.header().setSectionResizeMode( 4, QHeaderView.ResizeToContents) v_box = QVBoxLayout(self) v_box.setContentsMargins(0, 0, 0, 0) v_box.addWidget(self._hooks_list) #header = QHeaderView(Qt.Horizontal, self) h_box = QHBoxLayout() h_box.setContentsMargins(5, 2, 5, 5) self.btn1 = QPushButton( QIcon(utils.resource_path('assets/icons/plus.svg')), '') self.btn1.setFixedSize(20, 20) self.btn1.clicked.connect(self._on_additem_clicked) btn2 = QPushButton(QIcon(utils.resource_path('assets/icons/dash.svg')), '') btn2.setFixedSize(20, 20) btn2.clicked.connect(self.delete_items) btn3 = QPushButton( QIcon(utils.resource_path('assets/icons/trashcan.svg')), '') btn3.setFixedSize(20, 20) btn3.clicked.connect(self.clear_list) h_box.addWidget(self.btn1) h_box.addWidget(btn2) h_box.addSpacerItem( QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred)) h_box.addWidget(btn3) # header.setLayout(h_box) # header.setFixedHeight(25) # v_box.addWidget(header) v_box.addLayout(h_box) self.setLayout(v_box) self._bold_font = QFont(self._hooks_list.font()) self._bold_font.setBold(True) shortcut_addnative = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_N), self._app_window, self._on_addnative) shortcut_addnative.setAutoRepeat(False) shortcut_addjava = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_J), self._app_window, self._on_addjava) shortcut_addjava.setAutoRepeat(False) shortcut_add_native_on_load = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_O), self._app_window, self._on_add_native_on_load) shortcut_add_native_on_load.setAutoRepeat(False) # new menu self.new_menu = QMenu('New') self.new_menu.addAction('Native', self._on_addnative) self.new_menu.addAction('Java', self._on_addjava) self.new_menu.addAction('Module loading', self._on_add_native_on_load) # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def delete_items(self): """ Delete selected Items """ index = self._hooks_list.selectionModel().currentIndex().row() if index != -1: self._on_delete_hook(index) self._hooks_model.removeRow(index) def clear_list(self): """ Clear the List """ # go through all items and tell it gets removed for item in range(self._hooks_model.rowCount()): self._on_delete_hook(item) if self._hooks_model.rowCount() > 0: # something was wrong it should be empty self._hooks_model.removeRows(0, self._hooks_model.rowCount()) # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _on_add_hook(self, hook): type_ = QStandardItem() type_.setFont(self._bold_font) type_.setTextAlignment(Qt.AlignCenter) if hook.hook_type == HOOK_NATIVE: type_.setText('N') type_.setToolTip('Native hook') elif hook.hook_type == HOOK_JAVA: type_.setText('J') type_.setToolTip('Java hook') elif hook.hook_type == HOOK_ONLOAD: type_.setText('O') type_.setToolTip('On load hook') else: type_.setText('U') type_.setToolTip('Unknown Type') addr = QStandardItem() if hook.hook_type == HOOK_JAVA: parts = hook.get_input().split('.') addr.setText('.'.join(parts[:len(parts) - 1])) else: str_fmt = '0x{0:x}' if self._hooks_list.uppercase_hex: str_fmt = '0x{0:X}' # addr.setTextAlignment(Qt.AlignCenter) addr.setText(str_fmt.format(hook.get_ptr())) inp = QStandardItem() inp_text = hook.get_input() if hook.hook_type == HOOK_JAVA: parts = inp_text.split('.') inp_text = parts[len(parts) - 1] # if len(inp_text) > 15: # inp_text = inp_text[:15] + '...' # inp.setToolTip(hook.get_input()) inp.setText(inp_text) inp.setData(hook.get_input(), Qt.UserRole + 2) inp.setToolTip(hook.get_input()) logic = QStandardItem() logic.setTextAlignment(Qt.AlignCenter) logic.setFont(self._bold_font) if hook.logic and hook.logic != 'null' and hook.logic != 'undefined': logic.setText('ƒ') logic.setToolTip(hook.logic) logic.setData(hook.logic, Qt.UserRole + 2) condition = QStandardItem() condition.setTextAlignment(Qt.AlignCenter) condition.setFont(self._bold_font) if hook.condition and hook.condition != 'null' and hook.condition != 'undefined': condition.setText('ƒ') condition.setToolTip(hook.condition) condition.setData(hook.condition, Qt.UserRole + 2) self._hooks_model.appendRow([addr, type_, inp, logic, condition]) def _on_hit_native_on_load(self, data): items = self._hooks_model.findItems(data[1]['module'], Qt.MatchExactly, 2) if len(items) > 0: self._hooks_model.item(items[0].row(), 0).setText(data[1]['moduleBase']) def _on_hit_java_on_load(self, data): items = self._hooks_model.findItems(data[0], Qt.MatchExactly, 2) if len(items) > 0: pass def _on_double_clicked(self, model_index): item = self._hooks_model.itemFromIndex(model_index) if model_index.column() == 3 and item.text() == 'ƒ': self._on_modify_logic(model_index.row()) elif model_index.column() == 4 and item.text() == 'ƒ': self._on_modify_condition(model_index.row()) else: self._app_window.jump_to_address(self._hooks_model.item( model_index.row(), 0).text(), view=1) def _on_context_menu(self, pos): context_menu = QMenu(self) context_menu.addMenu(self.new_menu) context_menu.addSeparator() index = self._hooks_list.indexAt(pos).row() if index != -1: context_menu.addAction( 'Copy address', lambda: utils.copy_hex_to_clipboard( self._hooks_model.item(index, 0).text())) context_menu.addAction( 'Jump to address', lambda: self._app_window.jump_to_address( self._hooks_model.item(index, 0).text())) context_menu.addSeparator() context_menu.addAction('Edit Logic', lambda: self._on_modify_logic(index)) context_menu.addAction('Edit Condition', lambda: self._on_modify_condition(index)) context_menu.addSeparator() context_menu.addAction('Delete Hook', lambda: self._on_delete_hook(index)) if self._hooks_list.search_enabled: context_menu.addSeparator() context_menu.addAction('Search', self._hooks_list._on_cm_search) # show context menu global_pt = self._hooks_list.mapToGlobal(pos) context_menu.exec(global_pt) def _on_modify_logic(self, num_row): item = self._hooks_model.item(num_row, 3) data = item.data(Qt.UserRole + 2) if data is None: data = '' ptr = self._hooks_model.item(num_row, 0).text() accept, input_ = InputMultilineDialog().input('Insert logic for %s' % ptr, input_content=data) if accept: what = utils.parse_ptr(ptr) if what == 0: what = self._hooks_model.item(num_row, 2).data(Qt.UserRole + 2) if self._app_window.dwarf.dwarf_api( 'setHookLogic', [what, input_.replace('\n', '')]): item.setData(input_, Qt.UserRole + 2) if not item.text(): item.setText('ƒ') item.setToolTip(input_) self.onHookChanged.emit(ptr) def _on_modify_condition(self, num_row): item = self._hooks_model.item(num_row, 4) data = item.data(Qt.UserRole + 2) if data is None: data = '' ptr = self._hooks_model.item(num_row, 0).text() accept, input_ = InputDialog().input(self._app_window, 'Insert condition for %s' % ptr, input_content=data) if accept: what = utils.parse_ptr(ptr) if what == 0: what = self._hooks_model.item(num_row, 2).data(Qt.UserRole + 2) if self._app_window.dwarf.dwarf_api('setHookCondition', [what, input_]): item.setData(input_, Qt.UserRole + 2) if not item.text(): item.setText('ƒ') item.setToolTip(input_) self.onHookChanged.emit(ptr) # + button def _on_additem_clicked(self): self.new_menu.exec_(QCursor.pos()) # shortcuts/menu def _on_addnative(self): self._app_window.dwarf.hook_native() def _on_addjava(self): self._app_window.dwarf.hook_java() def _on_add_native_on_load(self): self._app_window.dwarf.hook_native_on_load() def _on_add_java_on_load(self): self._app_window.dwarf.hook_java_on_load() def _on_delete_hook(self, num_row): hook_type = self._hooks_model.item(num_row, 1).text() if hook_type == 'N': ptr = self._hooks_model.item(num_row, 0).text() ptr = utils.parse_ptr(ptr) self._app_window.dwarf.dwarf_api('deleteHook', ptr) self.onHookRemoved.emit(str(ptr)) elif hook_type == 'J': input_ = self._hooks_model.item(num_row, 2).data(Qt.UserRole + 2) self._app_window.dwarf.dwarf_api('deleteHook', input_) elif hook_type == 'O': input_ = self._hooks_model.item(num_row, 2).data(Qt.UserRole + 2) self._app_window.dwarf.dwarf_api('deleteHook', input_) elif hook_type == 'U': ptr = self._hooks_model.item(num_row, 0).text() ptr = utils.parse_ptr(ptr) self._app_window.dwarf.dwarf_api('deleteHook', ptr) self.onHookRemoved.emit(str(ptr)) def _on_hook_deleted(self, parts): _msg, _type, _val = parts additional = None if _type == 'java' or _type == 'java_on_load': _val = _val.split('.') str_frmt = '.'.join(_val[:-1]) additional = _val[-1] item_index = 0 elif _type == 'native_on_load': str_frmt = _val item_index = 2 else: _ptr = utils.parse_ptr(_val) if self._hooks_list._uppercase_hex: str_frmt = '0x{0:X}'.format(_ptr) else: str_frmt = '0x{0:x}'.format(_ptr) item_index = 0 for _item in range(self._hooks_model.rowCount()): item = self._hooks_model.item(_item, item_index) if item is None: continue if str_frmt == item.text(): if additional is not None: if additional == self._hooks_model.item(_item, 2).text(): self._hooks_model.removeRow(_item) else: self._hooks_model.removeRow(_item)
class ClientGui(QWidget): def __init__(self): self.service_msg_deq = [] # очередь сервисных сообщений, очищать! self.icon_user = QIcon("user.svg") self.icon_new_msg = QIcon("message.svg") super().__init__() self.get_login_dialog() def initUI(self): # Кнопки: добавить/удалить контакты в контакт лист self.button_add_contact = QPushButton('add', self) self.button_add_contact.clicked.connect(self.add_contact) self.button_del_contact = QPushButton('del', self) self.button_del_contact.clicked.connect(self.del_contact) self.button_settings = QPushButton('men', self) self.button_settings.setEnabled(False) # не работает self.button_connect = QPushButton('conn', self) self.button_connect.setEnabled(False) # не работает self.box_button = QHBoxLayout() self.box_button.addWidget(self.button_add_contact) self.box_button.addWidget(self.button_del_contact) self.box_button.addWidget(self.button_settings) self.box_button.addWidget(self.button_connect) # создаю модель для листа контактов, подключаю отображение cl = bd_client_app.BDContacts().get_contacts() self.model_cl = QStandardItemModel() for user in cl: row = QStandardItem(self.icon_user, user) self.model_cl.appendRow(row) self.contact_list = QListView() self.contact_list.setModel(self.model_cl) self.contact_list.setSelectionMode(QListView.SingleSelection) self.contact_list.setEditTriggers(QListView.NoEditTriggers) self.contact_list.clicked.connect(self.select_conlist) # строка и кнопка отправки сообщений qButton = QPushButton('>>', self) qButton.clicked.connect(self.send_click) self.sendBox = QLineEdit(self) self.sendBox.returnPressed.connect(self.send_click) self.messageBox = QStackedWidget() # два словаря, в первом: логин ключ виджет значение, второй наоборот self.messageBox_dict_ctw = {} self.messageBox_dict_wtc = {} for user in cl: self.messageBox_dict_ctw[user] = QListWidget() self.messageBox_dict_wtc[self.messageBox_dict_ctw[user]] = user self.messageBox.addWidget(self.messageBox_dict_ctw[user]) grid = QGridLayout() # строка, столбец, растянуть на строк, растянуть на столбцов grid.addWidget(self.contact_list, 0, 0, 2, 3) grid.addLayout(self.box_button, 2, 0) grid.addWidget(self.messageBox, 0, 3, 2, 3) grid.addWidget(self.sendBox, 2, 3, 1, 2) grid.addWidget(qButton, 2, 5) grid.setSpacing(5) grid.setColumnMinimumWidth(3, 200) grid.setColumnStretch(3, 10) self.setLayout(grid) self.resize(800, 300) self.center() self.setWindowTitle('Avocado') self.setWindowIcon(QIcon('icon.svg')) self.show() def initThreads(self): self.print_thread = ClientThreads(self.client, self) self.print_thread.print_signal.connect(self.add_message) self.print_thread.start() def get_login_dialog(self): text, ok = QInputDialog.getText(self, 'Login', 'Connect with login:') self.login_name = str(text) if ok: self.service_msg_deq.clear() # жду свой ответ self.init_client() self.initThreads() # while not self.service_msg_deq: # print(self.service_msg_deq) # pass # жду ответ # if self.service_msg_deq[0] is True: time.sleep(1) self.initUI() # else: # self.exit() else: self.exit() def init_client(self): self.client = client.Client(self.login_name, "localhost", 7777) self.client.start_th_gui_client() def center(self): # центрирую окно qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) @pyqtSlot() def send_click(self): text_to_send = self.sendBox.text() if text_to_send.rstrip(): self.messageBox.currentWidget().addItem("<< " + text_to_send) self.client.inp_queue.put(text_to_send) self.sendBox.clear() @pyqtSlot(QModelIndex) def select_conlist(self, curr): self.messageBox.setCurrentIndex(curr.row()) self.model_cl.itemFromIndex(curr).setIcon(self.icon_user) self.client.to_user = self.messageBox_dict_wtc[ self.messageBox.currentWidget()] @pyqtSlot(tuple) def add_message(self, message): msg = message[0] from_u = message[1] try: client_widget = self.messageBox_dict_ctw[from_u] except KeyError: mesg_con_log.error("Message from user from not in contact list:") mesg_con_log.error("%s, %s" % (from_u, msg)) else: client_widget.addItem(">> " + msg) message_from = self.model_cl.findItems(from_u)[0] if self.contact_list.currentIndex() != self.model_cl.indexFromItem( message_from): message_from.setIcon(self.icon_new_msg) @pyqtSlot() def del_contact(self): user = self.client.to_user self.client.inp_queue.put("del_contact " + user) self.messageBox.removeWidget(self.messageBox_dict_ctw[user]) self.model_cl.takeRow( self.model_cl.indexFromItem( self.model_cl.findItems(user)[0]).row()) @pyqtSlot() def add_contact(self): user = self.sendBox.text() self.service_msg_deq.clear() # жду свой ответ self.client.inp_queue.put("add_contact " + user) while not self.service_msg_deq: pass # жду ответ if self.service_msg_deq[0] is True: row = QStandardItem(self.icon_user, user) self.model_cl.appendRow(row) self.messageBox_dict_ctw[user] = QListWidget() self.messageBox_dict_wtc[self.messageBox_dict_ctw[user]] = user self.messageBox.addWidget(self.messageBox_dict_ctw[user]) else: pass self.sendBox.clear()
class TreeWidget(QTreeView): """ Displays objects as an object tree """ def __init__(self, parent): super(TreeWidget, self).__init__(parent) self.parent = parent # add menu self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(self.open_menu) self.model = QStandardItemModel() self.setModel(self.model) self.model.setHorizontalHeaderLabels(["Objects"]) self.model.itemChanged.connect(self.edit_name) def edit_name(self, item): """ Makes the name of the item in the tree matches the object name """ obj = item.data() if obj: obj.name = item.text() def addItem(self, item, header, mainheader=None, editable=True): """ Adds item to tree """ if mainheader is None: finds = self.model.findItems(header) if not finds: itemheader = QStandardItem(header) self.model.appendRow(itemheader) else: itemheader = finds[0] else: finds = self.model.findItems(mainheader) if not finds: raise Exception('main header %s does not exist' % mainheader) main_itemheader = finds[0] for i in range(main_itemheader.rowCount()): itemheader = main_itemheader.child(i, 0) if itemheader: if header is itemheader.data(): break standardItem = QStandardItem(item.name) standardItem.setEditable(editable) standardItem.setData(item) itemheader.appendRow(standardItem) def open_menu(self, position): # pragma: no cover """ Activates when right click in tree """ index = self.selectedIndexes() if not index: return item = self.model.itemFromIndex(index[0]) obj = item.data() if hasattr(obj, 'menu'): menu = obj.menu menu.exec_(self.viewport().mapToGlobal(position)) return menu def remove_item(self, data): """ removes an item from the modal list """ removed = False # check each header for a match for i in range(self.model.rowCount()): header = self.model.item(i) for i in range(header.rowCount()): item = header.child(i, 0) if item: if data is item.data(): item.setData(None) header.removeRow(i) removed = removed or True break if item.hasChildren(): for j in range(item.rowCount()): subitem = item.child(j, 0) if data is subitem.data(): subitem.setData(None) item.removeRow(j) removed = removed or True break # remove empty headers if not header.rowCount(): self.model.removeRow(header.row()) break return removed
class RemoteTree(QObject): def __init__(self, tv: QTreeView, fv: FileView, clnt_socket: ClntSocket): super(RemoteTree, self).__init__() self.tv = tv self.fv = fv self.clnt_socket = clnt_socket self.remote_model = QStandardItemModel(self) self.tv.header().hide() self.tv.setModel(self.remote_model) self.clnt_list = list() self.tv.clicked.connect(self.item_click) self.tv.expanded.connect(self.item_expand) self.rt_model = QStandardItemModel(self) self.rt_model.setColumnCount(4) self.rt_model.setHeaderData(0, Qt.Horizontal, "Name") self.rt_model.setHeaderData(1, Qt.Horizontal, "Size") self.rt_model.setHeaderData(2, Qt.Horizontal, "Type") self.rt_model.setHeaderData(3, Qt.Horizontal, "Date Modified") def tree_view_update(self, msg): print("get a signal: ", msg) if msg[0] == HW_DATA_TYPE_LOGIN: name_list = list() cid_list = list() for i in msg[2]: name_list.append(i["name"]) cid_list.append(i["cid"]) self.clnt_list.append(i) self.children_item_update(self.remote_model, name_list) for cid in cid_list: self.clnt_socket.hw_cmd_list("tree", cid, "/") elif msg[0] == HW_DATA_TYPE_CMD: if msg[2] == "tree": # recved_data = [data_type, cid, act["cmd"], act["path"], act["list"]] root_item = self.remote_model.findItems(self.get_name_by_cid(msg[1]))[0] print("dest device item: ", root_item.text()) # dell tail_item = root_item if msg[3] is not "/": tail_item = self.get_item_by_path(msg[3], root_item) print("tail item: ", tail_item.text()) self.children_item_update(tail_item, msg[4]) @staticmethod def children_item_update(item, name_list): item_type = type(item) children_item = list() if item_type == QStandardItemModel: children_item = item.findItems("", Qt.MatchContains) else: print("start update item: ", item.text()) row_cnt = item.rowCount() for i in range(row_cnt): ic = item.child(i) if ic is None: print("child item is None???") continue children_item.append(ic) children = list() for child in children_item: child_name = child.text() children.append(child_name) print("children: ", children) for name in children: if name not in name_list: child_index = children.index(name) # print("child name: %s, index: %d" % (name, child_index)) if item_type == QStandardItemModel: item.takeChild(item.child(child_index)) else: item.takeChild(child_index) for name in name_list: if name not in children: item.appendRow(QStandardItem(QFileIconProvider().icon(QFileIconProvider.Folder), name)) print("children item update done") def get_item_by_path(self, path: str, item: QStandardItem): item_name_list = path.strip('/').split('/') des_item = item for i in item_name_list: des_item = self.get_item_by_name(i, des_item) if des_item is None: return None return des_item @staticmethod def get_item_by_name(name: str, item: QStandardItem): cnt = item.rowCount() for i in range(cnt): if item.child(i).text() == name: return item.child(i) return None @staticmethod def get_full_path(item: QStandardItem): full_path = "/" + item.text() + "/" pitem = item while True: pitem = pitem.parent() if pitem is None: break full_path = "/" + pitem.text() + full_path return full_path def get_cid_by_name(self, name: str): for c in self.clnt_list: if c["name"] == name: return c["cid"] return 0 def get_name_by_cid(self, cid: int): for c in self.clnt_list: if c["cid"] == cid: return c["name"] return None def item_click(self, index): if self.fv.item_model is not self.rt_model: self.fv.item_model = self.rt_model self.fv.file_tv.setModel(self.rt_model) item = self.remote_model.itemFromIndex(index) full_path = self.get_full_path(item) print("ls path: ", full_path) path_info = full_path.split('/', 2) print("clnt: %s, path: %s, path len: %d" % (path_info[1], path_info[2], len(path_info))) abs_path = "/" if len(path_info[2]) == 0 else path_info[2] self.clnt_socket.hw_cmd_list("ls", self.get_cid_by_name(path_info[1]), abs_path) def item_expand(self, index): item = self.remote_model.itemFromIndex(index) for ic in range(item.rowCount()): full_path = self.get_full_path(item.child(ic)) print("expand path: ", full_path) path_info = full_path.split('/', 2) print("clnt: %s, path: %s, path len: %d" % (path_info[1], path_info[2], len(path_info))) abs_path = "/" if len(path_info[2]) == 0 else path_info[2] cid = self.get_cid_by_name(path_info[1]) print("expand get tree list, abs path: %s, clint: %s, cid: %d" % (abs_path, path_info[1], cid)) self.clnt_socket.hw_cmd_list("tree", cid, abs_path) '''
class FaceDataSet(QMainWindow): faceDB = FaceDB() count = 0 addToDatasetEnabled = False isOpenFaceDataAdded = False faceDetector = None openFaceImg = None capture = None face_id = None faceDetectionEnabled = True folderName = None folderPath = None def __init__(self): super(FaceDataSet, self).__init__() loadUi('01_data_entry.ui', self) self.faceDetector = cv2.CascadeClassifier( 'haarcascade_frontalface_default.xml') self.addToDatasetButton.clicked.connect(self.detect_webcam_face) self.student_list_model = QStandardItemModel(self.studentListView) self.studentListView.setModel(self.student_list_model) self.studentListView.selectionModel().selectionChanged.connect( self.listview_change) self.tabWidget.currentChanged.connect(self.tab_change) self.load_course_students() self.reset_counters() self.courseComboBox.currentIndexChanged.connect(self.combo_change) self.combo_change(0) self.updateDatabaseButton.clicked.connect(self.button_clicked) self.timer = QTimer(self) self.start_web_cam() def load_course_students(self): # ALL COURSES data = self.faceDB.query_all_courses() for row in data: item_string = row[0] + "-" + row[1] self.courseComboBox.addItem(item_string) # ALL_STUDENTS data = self.faceDB.query_all_student_no_images() for row in data: item_string = row[0] + "-" + row[1] item = QStandardItem(item_string) item.setCheckable(True) item.setSelectable(False) self.student_list_model.appendRow(item) def reset_counters(self): self.count = 0 self.addToDatasetEnabled = False self.isOpenFaceDataAdded = False self.openFaceImg = None self.face_id = "" def detect_webcam_face(self): self.reset_counters() self.face_id = self.studentIDText.text() self.folderName = "user" + self.face_id self.folderPath = os.path.join( os.path.dirname(os.path.realpath(__file__)), "dataset/" + self.folderName) if not os.path.exists(self.folderPath): os.makedirs(self.folderPath) self.addToDatasetButton.setText("Progress") self.addDatasetStatus.clear() self.pictureLabel.clear() QApplication.processEvents() self.addToDatasetEnabled = True def start_web_cam(self): self.capture = cv2.VideoCapture(0) self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640) self.timer.timeout.connect(self.update_frame) self.timer.start(5) def update_frame(self): ret, image = self.capture.read() image = cv2.flip(image, 1) if self.faceDetectionEnabled: detected_image = self.detect_face(image) self.display_image(detected_image) else: self.display_image(image, 1) def detect_face(self, img): gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces = self.faceDetector.detectMultiScale(gray, 1.2, 5, minSize=(90, 90)) for (x, y, w, h) in faces: if self.addToDatasetEnabled: self.count += 1 if not self.isOpenFaceDataAdded: y0 = max(y - 120, 0) y1 = min(y + h + 80, 480) x0 = max(x - 80, 0) x1 = min(x + 80 + w, 640) self.openFaceImg = img[y0:y1, x0:x1] cv2.imwrite( self.folderPath + "/User." + self.face_id + ".0.jpg", self.openFaceImg) self.isOpenFaceDataAdded = True self.add_data_to_db() self.addDatasetStatus.setText("Dataset Added") image_path = QImage(self.folderPath + "/User." + self.face_id + ".0.jpg") self.pictureLabel.setPixmap(QPixmap.fromImage(image_path)) self.pictureLabel.setScaledContents(True) self.addToDatasetEnabled = False self.addToDatasetButton.setText("Add to Dataset") cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2) return img def get_image(self, img): qformat = QImage.Format_Indexed8 if len(img.shape) == 3: # [0]rows, [1]=cols [2]=channels if img.shape[2] == 4: qformat = QImage.Format_RGBA8888 else: qformat = QImage.Format_RGB888 out_image = QImage(img, img.shape[1], img.shape[0], img.strides[0], qformat) # BGB>>RGB out_image = out_image.rgbSwapped() return out_image def display_image(self, img): out_image = self.get_image(img) self.webCamLabel.setPixmap(QPixmap.fromImage(out_image)) self.webCamLabel.setScaledContents(True) def stop_web_cam(self): self.timer.stop() self.capture = None def add_data_to_db(self): student_name = self.studentNameText.text() student_email = self.studentEmailText.text() # ADD to MYSQL self.faceDB.insert_student( self.face_id, student_name, student_email, (self.folderPath + "/User." + self.face_id + ".0.jpg")) item = QStandardItem(self.face_id + '-' + student_name) item.setCheckable(True) item.setSelectable(False) self.student_list_model.insertRow(0, item) def tab_change(self, i): if i == 0: self.start_web_cam() else: self.stop_web_cam() self.updateDatabaseStatus.clear() def combo_change(self, i): # COURSES-STUDENTS course_id = str(self.courseComboBox.currentText()).split('-', 1)[0] for index in range(self.student_list_model.rowCount()): item = self.student_list_model.item(index) item.setCheckState(Qt.Unchecked) data = self.faceDB.query_course_students(course_id) for row in data: for item in self.student_list_model.findItems( row[1], Qt.MatchStartsWith): item.setCheckState(Qt.Checked) self.updateDatabaseStatus.clear() def button_clicked(self): self.updateDatabaseStatus.setText('') self.updateDatabaseButton.setText('Progress..') QApplication.processEvents() # REMOVE COURSES-STUDENTS course_id = str(self.courseComboBox.currentText()).split('-', 1)[0] self.faceDB.delete_course_students(course_id) # ADD NEW COURSES-STUDENTS data_tuple_list = [] for index in range(self.student_list_model.rowCount()): item = self.student_list_model.item(index) if item.checkState() == Qt.Checked: student_id = str(item.text()).split('-', 1)[0] data_tuple_list.append((course_id, student_id)) self.faceDB.insert_course_students(data_tuple_list) self.updateDatabaseStatus.setText('Database Updated') self.updateDatabaseButton.setText('Update Database') def listview_change(self): self.updateDatabaseStatus.clear() self.updateDatabaseStatus.repaint()
class TreeView(QTreeView): def __init__(self, *args, **kwargs): super(TreeView, self).__init__(*args, **kwargs) self._initModel() self._initSignals() def _initModel(self): """设置目录树Model""" self._dmodel = QStandardItemModel(self) self._fmodel = SortFilterModel(self) self._fmodel.setSourceModel(self._dmodel) self.setModel(self._fmodel) def _initSignals(self): Signals.itemJumped.connect(self.onItemJumped) Signals.filterChanged.connect(self._fmodel.setFilterRegExp) self.clicked.connect(self.onClicked) self.doubleClicked.connect(self.onDoubleClicked) def rootItem(self): """得到根节点Item""" return self._dmodel.invisibleRootItem() def findItems(self, name): """根据名字查找item :param name: """ return self._dmodel.findItems(name) def onItemJumped(self, name): items = self.findItems(name) if not items: return index = self._fmodel.mapFromSource(self._dmodel.indexFromItem( items[0])) self.setCurrentIndex(index) self.expand(index) # # 显示readme Signals.urlLoaded.emit(name) def listSubDir(self, pitem, path): """遍历子目录 :param item: 上级Item :param path: 目录 """ paths = os.listdir(path) files = [] for name in paths: spath = os.path.join(path, name) if not os.path.isfile(spath): continue spath = os.path.splitext(spath) if len(spath) == 0: continue if spath[1] == '.py' and spath[0].endswith('__init__') == False: files.append(name) # 已经存在的item existsItems = [pitem.child(i).text() for i in range(pitem.rowCount())] for name in files: if name in existsItems: continue file = os.path.join(path, name).replace('\\', '/') item = QStandardItem(name) # 添加自定义的数据 item.setData(False, Constants.RoleRoot) # 不是根目录 item.setData(file, Constants.RolePath) try: item.setData( open(file, 'rb').read().decode(errors='ignore'), Constants.RoleCode) except Exception as e: AppLog.warn('read file({}) code error: {}'.format( file, str(e))) pitem.appendRow(item) def initCatalog(self): """初始化本地仓库结构树 """ AppLog.debug('') if not os.path.exists(Constants.DirProjects): return pitem = self._dmodel.invisibleRootItem() # 只遍历根目录 for name in os.listdir(Constants.DirProjects): file = os.path.join(Constants.DirProjects, name).replace('\\', '/') if os.path.isfile(file): # 跳过文件 continue if name.startswith( '.') or name == 'Donate' or name == 'Test': # 不显示.开头的文件夹 continue items = self.findItems(name) if items: item = items[0] else: item = QStandardItem(name) # 添加自定义的数据 # 用于绘制进度条的item标识 item.setData(True, Constants.RoleRoot) # 目录或者文件的绝对路径 item.setData( os.path.abspath(os.path.join(Constants.DirProjects, name)), Constants.RolePath) pitem.appendRow(item) # 遍历子目录 self.listSubDir(item, file) # 排序 self._fmodel.sort(0, Qt.AscendingOrder) def onClicked(self, modelIndex): """Item单击 :param modelIndex: 此处是代理模型中的QModelIndex, 并不是真实的 """ root = modelIndex.data(Constants.RoleRoot) path = modelIndex.data(Constants.RolePath) code = modelIndex.data(Constants.RoleCode) AppLog.debug('is root: {}'.format(root)) AppLog.debug('path: {}'.format(path)) if not root and os.path.isfile(path) and code: # 右侧显示代码 Signals.showCoded.emit(code) if root and os.path.isdir(path): if self.isExpanded(modelIndex): self.collapse(modelIndex) else: self.expand(modelIndex) # 显示readme Signals.showReadmed.emit(os.path.join(path, 'README.md')) def onDoubleClicked(self, modelIndex): """Item双击 :param modelIndex: 此处是代理模型中的QModelIndex, 并不是真实的 """ root = modelIndex.data(Constants.RoleRoot) path = modelIndex.data(Constants.RolePath) AppLog.debug('is root: {}'.format(root)) AppLog.debug('path: {}'.format(path)) if not root and os.path.isfile(path): # 运行代码 Signals.runExampled.emit(path) # if root and os.path.isdir(path): # # 显示readme # Signals.showReadmed.emit(os.path.join(path, 'README.md')) def enterEvent(self, event): super(TreeView, self).enterEvent(event) # 鼠标进入显示滚动条 self.verticalScrollBar().setVisible(True) def leaveEvent(self, event): super(TreeView, self).leaveEvent(event) # 鼠标离开隐藏滚动条 self.verticalScrollBar().setVisible(False)
class RightsDialog(QDialog, Ui_Dialog): """Rights Management Dialog.""" def __init__(self, Dialog): """参数: lst (list) 为左侧表, tableMapping (dict)为右侧表格.""" super(RightsDialog, self).__init__() super().setupUi(Dialog) # select rights role list from database self.rightsList = self.getRightsList() # 初始化权限名称 self.comboBox.addItem('<请选择权限名称>') if self.rightsList: for i in self.rightsList: self.comboBox.addItem(i) # 是否选中权限名称 self.selectedCombo = '' # TODO (miles-20170502): 是否需要枚举所有 action? sql = 'select distinct ActionName from u_right_action' self.allAction = getData(sql, 'ActionName') # self.allAction = [ # 'A_SaveNewTask', # 'A_EditTask', # 'A_ViewTask', # 'A_DeleteTask', # 'A_ChangeUser', # 'A_TB', # 'A_SB', # 'A_EmailServer', # 'A_UserManager', # 'A_EditUser', # 'A_RightControl', # 'A_CloseFunc', # 'A_Exit', # 'A_About', # 'A_ViewAllTask', # 'A_RuleQuery' # ] # role's action -> list self.currentAction = [] # 新选的 action -> list self.checkedAction = [] # 新取消的 action -> list self.uncheckedAction = [] # action 列表 self.listModel = QStandardItemModel() self.leftListView.setModel(self.listModel) # 初始化 action 列表 self.initListView() # comboBox binding self.comboBox.activated[str].connect(self.onComboActivated) # listModel Checkbox binding self.listModel.itemChanged.connect(self.onListModelChanged) # exitButton self.exitButton.clicked.connect(self.clickExitButton) # delButton self.submitButton.clicked.connect(self.clickSubmitButton) @staticmethod def getRightsList() -> list: """获取权限名称列表.""" sql = 'select * from general_info where type="U_RIGHT"' return getData(sql, 'name') def initListView(self): """初始化 action 列表.""" if self.allAction: for i in self.allAction: item = QStandardItem(i) item.setCheckable(True) self.listModel.appendRow(item) def updateListView(self): """更新 action 列表.""" self.listModel.clear() self.initListView() for text in self.currentAction: items = self.listModel.findItems(text, Qt.MatchExactly) for item in items: item.setCheckState(Qt.Checked) @staticmethod def clickExitButton(): """退出.""" app.quit() def clickSubmitButton(self): """提交数据.""" # 未选择权限 if self.selectedCombo not in self.rightsList: if (QMessageBox.warning( self, '警告: ', '请选择权限名称!', QMessageBox.Ok, QMessageBox.Ok) == QMessageBox.Ok): return # 不更改数据管理员的 action elif self.selectedCombo == '数据管理员': if (QMessageBox.warning( self, '警告: ', '请选择权限名称!', QMessageBox.Ok, QMessageBox.Ok) == QMessageBox.Ok): return else: ret = QMessageBox.question( self, '确认', '是否提交数据?', QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Save) if ret == QMessageBox.Save: for i in self.checkedAction: sql = 'insert into u_right_action ' \ '(RightName, ActionName) values ("{}", "{}")' g_DB.Execute(sql.format(self.selectedCombo, i)) for i in self.uncheckedAction: sql = 'delete from u_right_action where RightName="{}" ' \ 'and ActionName="{}"' g_DB.Execute(sql.format(self.selectedCombo, i)) g_DB.commit() QMessageBox.warning(self, '成功', '提交成功') self.checkedAction = [] self.uncheckedAction = [] # app.quit() elif ret == QMessageBox.Discard: self.checkedAction = [] self.uncheckedAction = [] self.updateListView() return elif ret == QMessageBox.Cancel: return def onComboActivated(self, text): """选定权限角色时, 更新 action 列表.""" sql = 'select * from u_right_action where RightName="{}"'.format(text) self.currentAction = getData(sql, 'ActionName') self.checkedAction = [] self.uncheckedAction = [] self.selectedCombo = text self.updateListView() def onListModelChanged(self, item): """更改新选择和新取消列表.""" if item.checkState(): # 添加新选择的 action if item.text() not in self.currentAction: self.checkedAction.append(item.text()) # 从新取消的 action 中删除, 防止重复勾选与取消 if item.text() in self.uncheckedAction: self.uncheckedAction.remove(item.text()) else: # 添加新取消的 action if item.text() in self.currentAction: self.uncheckedAction.append(item.text()) # 从新选 action 中删除, 防止重复勾选与取消 if item.text() in self.checkedAction: self.checkedAction.remove(item.text())
class IsosViewer(QMainWindow, mageiaSyncUI.Ui_mainWindow): # Display the main window def __init__(self, parent=None): super(IsosViewer, self).__init__(parent) self.setupUi(self) self.connectActions() self.IprogressBar.setMinimum(0) self.IprogressBar.setMaximum(100) self.IprogressBar.setValue(0) self.IprogressBar.setEnabled(False) self.selectAllState=True self.stop.setEnabled(False) self.destination='' self.rsyncThread = mageiaSyncExt.syncThread(self) # create a thread to launch rsync self.rsyncThread.progressSignal.connect(self.setProgress) self.rsyncThread.speedSignal.connect(self.setSpeed) self.rsyncThread.sizeSignal.connect(self.setSize) self.rsyncThread.remainSignal.connect(self.setRemain) self.rsyncThread.endSignal.connect(self.syncEnd) self.rsyncThread.lvM.connect(self.lvMessage) self.rsyncThread.checkSignal.connect(self.checks) self.checkThreads=[] # A list of thread for each iso # Model for list view in a table self.model = QStandardItemModel(0, 6, self) headers=[self.tr("Directory"),self.tr("Name"),self.tr("Size"),self.tr("Date"),"SHA1","MD5"] i=0 for label in headers: self.model.setHeaderData(i, QtCore.Qt.Horizontal,label ) i+=1 # settings for the list view self.localList.setModel(self.model) self.localList.setColumnWidth(0,220) self.localList.setColumnWidth(1,220) self.localList.horizontalHeader().setStretchLastSection(True) # settings for local iso names management self.localListNames=[] def multiSelect(self): # allows to select multiple lines in remote ISOs list self.listIsos.setSelectionMode(2) def add(self, iso): # Add an remote ISO in list self.listIsos.addItem(iso) def localAdd(self, path,iso,isoSize): # Add an entry in local ISOs list, with indications about checking itemPath=QStandardItem(path) itemIso=QStandardItem(iso) if isoSize==0: itemSize=QStandardItem('--') else: formatedSize='{:n}'.format(isoSize).replace(","," ") itemSize=QStandardItem(formatedSize) itemSize.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter) itemDate=QStandardItem("--/--/--") itemDate.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter) itemCheck1=QStandardItem("--") itemCheck1.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter) itemCheck5=QStandardItem("--") itemCheck5.setTextAlignment(QtCore.Qt.AlignVCenter|QtCore.Qt.AlignHCenter) self.model.appendRow([itemPath,itemIso,itemSize,itemDate, itemCheck1, itemCheck5,]) self.localListNames.append([path,iso]) def setProgress(self, value): # Update the progress bar self.IprogressBar.setValue(value) def setSpeed(self, value): # Update the speed field self.speedLCD.display(value) def setSize(self, size): # Update the size field self.Lsize.setText(size+self.tr(" bytes")) def setRemain(self,remainTime): content=QtCore.QTime.fromString(remainTime,"h:mm:ss") self.timeRemaining.setTime(content) def manualChecks(self): for iso in self.listIsos.selectedItems(): path,name=iso.text().split('/') try: # Look for ISO in local list item=self.model.findItems(name,QtCore.Qt.MatchExactly,1)[0] except: # Remote ISO is not yet in local directory. We add it in localList and create the directory self.localAdd(path,name,0) basedir=QtCore.QDir(self.destination) basedir.mkdir(path) item=self.model.findItems(name,QtCore.Qt.MatchExactly,1)[0] row=self.model.indexFromItem(item).row() self.checks(row) def checks(self,isoIndex): # processes a checking for each iso # launches a thread for each iso newThread=mageiaSyncExt.checkThread(self) self.checkThreads.append(newThread) self.checkThreads[-1].setup(self.destination, self.model.data(self.model.index(isoIndex,0)) , self.model.data(self.model.index(isoIndex,1)), isoIndex) self.checkThreads[-1].md5Signal.connect(self.md5Check) self.checkThreads[-1].sha1Signal.connect(self.sha1Check) self.checkThreads[-1].dateSignal.connect(self.dateCheck) self.checkThreads[-1].sizeFinalSignal.connect(self.sizeUpdate) self.checkThreads[-1].checkStartSignal.connect(self.checkStart) self.checkThreads[-1].start() def checkStart(self,isoIndex): # the function indicates that checking is in progress # the hundred contains index of the value to check, the minor value contains the row col=(int)(isoIndex/100) row=isoIndex-col*100 self.model.setData(self.model.index(row, col, QtCore.QModelIndex()), self.tr("Checking")) def md5Check(self,check): if check>=128: val=self.tr("OK") row=check-128 else: val=self.tr("Failed") row=check self.model.setData(self.model.index(row, 5, QtCore.QModelIndex()), val) def sha1Check(self,check): if check>=128: val=self.tr("OK") row=check-128 else: val=self.tr("Failed") row=check self.model.setData(self.model.index(row, 4, QtCore.QModelIndex()), val) def dateCheck(self,check): if check>=128: val=self.tr("OK") row=check-128 else: val=self.tr("Failed") row=check self.model.setData(self.model.index(row, 3, QtCore.QModelIndex()), val) def sizeUpdate(self,signal,isoSize): col=(int)(signal/100) row=signal-col*100 self.model.setData(self.model.index(row, col, QtCore.QModelIndex()), isoSize) def syncEnd(self, rc): if rc==1: self.lvMessage(self.tr("Command rsync not found")) elif rc==2: self.lvMessage(self.tr("Error in rsync parameters")) elif rc==3: self.lvMessage(self.tr("Unknown error in rsync")) self.IprogressBar.setEnabled(False) self.syncGo.setEnabled(True) self.listIsos.setEnabled(True) self.selectAll.setEnabled(True) self.stop.setEnabled(False) def prefsInit(self): # Load the parameters at first params=QtCore.QSettings("Mageia","mageiaSync") paramRelease="" try: paramRelease=params.value("release", type="QString") # the parameters already initialised? except: pass if paramRelease =="": # Values are not yet set self.pd0=prefsDialog0() self.pd0.user.setFocus() answer=self.pd0.exec_() if answer: # Update params self.user=self.pd0.user.text() self.password=self.pd0.password.text() self.location=self.pd0.location.text() params=QtCore.QSettings("Mageia","mageiaSync") params.setValue("user",self.user) params.setValue("password",self.password) params.setValue("location",self.location) else: pass # answer=QDialogButtonBox(QDialogButtonBox.Ok) # the user must set values or default values self.pd0.close() self.pd=prefsDialog() if self.password !="": code,list=mageiaSyncExt.findRelease('rsync://'+self.user+'@bcd.mageia.org/isos/',self.password) if code==0: for item in list: self.pd.release.addItem(item) self.pd.password.setText(self.password) self.pd.user.setText(self.user) self.pd.location.setText(self.location) self.pd.selectDest.setText(QtCore.QDir.currentPath()) self.pd.release.setFocus() answer=self.pd.exec_() if answer: # Update params self.user=self.pd.user.text() self.password=self.pd.password.text() self.location=self.pd.location.text() params=QtCore.QSettings("Mageia","mageiaSync") self.release= self.pd.release.currentText() self.destination=self.pd.selectDest.text() self.bwl=self.pd.bwl.value() params.setValue("release", self.release) params.setValue("user",self.user) params.setValue("password",self.password) params.setValue("location",self.location) params.setValue("destination",self.destination) params.setValue("bwl",str(self.bwl)) else: pass # answer=QDialogButtonBox(QDialogButtonBox.Ok) print(self.tr("the user must set values or default values")) self.pd.close() else: self.release=params.value("release", type="QString") self.user=params.value("user", type="QString") self.location=params.value("location", type="QString") self.password=params.value("password", type="QString") self.destination=params.value("destination", type="QString") self.bwl=params.value("bwl",type=int) self.localDirLabel.setText(self.tr("Local directory: ")+self.destination) if self.location !="": self.remoteDirLabel.setText(self.tr("Remote directory: ")+self.location) def selectDestination(self): # dialog box to select the destination (local directory) directory = QFileDialog.getExistingDirectory(self, self.tr('Select a directory'),'~/') isosSync.destination = directory self.pd.selectDest.setText(isosSync.destination) def selectAllIsos(self): # Select or unselect the ISOs in remote list if self.selectAllState : for i in range(self.listIsos.count()): self.listIsos.item(i).setSelected(True) self.selectAll.setText(self.tr("Unselect &All")) else: for i in range(self.listIsos.count()): self.listIsos.item(i).setSelected(False) self.selectAll.setText(self.tr("Select &All")) self.selectAllState=not self.selectAllState def connectActions(self): self.actionQuit.triggered.connect(app.quit) self.quit.clicked.connect(app.quit) self.actionRename.triggered.connect(self.rename) self.actionUpdate.triggered.connect(self.updateList) self.actionCheck.triggered.connect(self.manualChecks) self.actionPreferences.triggered.connect(self.prefs) self.syncGo.clicked.connect(self.launchSync) self.selectAll.clicked.connect(self.selectAllIsos) def updateList(self): # From the menu entry self.lw = LogWindow() self.lw.show() self.listIsos.clear() self.model.removeRows(0,self.model.rowCount()) if self.location == "" : self.nameWithPath='rsync://'+self.user+'@bcd.mageia.org/isos/'+self.release+'/' # print self.nameWithPath else: self.nameWithPath=self.location+'/' self.lvMessage(self.tr("Source: ")+self.nameWithPath) self.fillList = mageiaSyncExt.findIsos() self.fillList.setup(self.nameWithPath, self.password,self.destination) self.fillList.endSignal.connect(self.closeFill) self.fillList.start() # Reset the button self.selectAll.setText(self.tr("Select &All")) self.selectAllState=True def lvMessage( self,message): # Add a line in the logview self.lvText.append(message) def renameDir(self): # Choose the directory where isos are stored directory = QFileDialog.getExistingDirectory(self, self.tr('Select a directory'),self.destination) self.rd.chooseDir.setText(directory) def rename(self): # rename old isos and directories to a new release self.rd=renameDialog() loc=[] loc=self.location.split('/') self.rd.oldRelease.setText(loc[-1]) self.rd.chooseDir.setText(self.destination) answer=self.rd.exec_() if answer: returnMsg=mageiaSyncExt.rename(self.rd.chooseDir.text(),self.rd.oldRelease.text(),str(self.rd.newRelease.text())) self.lvMessage(returnMsg) self.rd.close() def prefs(self): # From the menu entry self.pd=prefsDialog() self.pd.release.addItem(self.release) self.pd.password.setText(self.password) self.pd.user.setText(self.user) self.pd.location.setText(self.location) self.pd.selectDest.setText(self.destination) self.pd.bwl.setValue(self.bwl) params=QtCore.QSettings("Mageia","mageiaSync") answer=self.pd.exec_() if answer: params.setValue("release", self.pd.release.currentText()) params.setValue("user",self.pd.user.text()) params.setValue("password",self.pd.password.text()) params.setValue("location",self.pd.location.text()) params.setValue("destination",self.pd.selectDest.text()) params.setValue("bwl",str(self.pd.bwl.value())) self.prefsInit() self.pd.close() def launchSync(self): self.IprogressBar.setEnabled(True) self.stop.setEnabled(True) self.syncGo.setEnabled(False) self.listIsos.setEnabled(False) self.selectAll.setEnabled(False) # Connect the button Stop self.stop.clicked.connect(self.stopSync) self.rsyncThread.params(self.password, self.bwl) for iso in self.listIsos.selectedItems(): path,name=iso.text().split('/') try: # Look for ISO in local list item=self.model.findItems(name,QtCore.Qt.MatchExactly,1)[0] except: # Remote ISO is not yet in local directory. We add it in localList and create the directory self.localAdd(path,name,0) basedir=QtCore.QDir(self.destination) basedir.mkdir(path) item=self.model.findItems(name,QtCore.Qt.MatchExactly,1)[0] row=self.model.indexFromItem(item).row() if self.location == "" : self.nameWithPath='rsync://'+self.user+'@bcd.mageia.org/isos/'+self.release+'/'+path else: self.nameWithPath=self.location+path if (not str(path).endswith('/')): self.nameWithPath+='/' self.rsyncThread.setup(self.nameWithPath, self.destination+'/'+path+'/',row) self.rsyncThread.start() # start the thread # Pour les tests uniquement #rsync://[email protected]/isos/$release/ #self.nameWithPath='rsync://ftp5.gwdg.de/pub/linux/mageia/iso/4.1/Mageia-4.1-LiveCD-GNOME-en-i586-CD/' def closeFill(self,code): if code==0: # list returned list=self.fillList.getList() for iso in list: self.add(iso) elif code==1: self.lvMessage(self.tr("Command rsync not found")) elif code==2: self.lvMessage(self.tr("Error in rsync parameters")) elif code==3: self.lvMessage(self.tr("Unknown error in rsync")) list=self.fillList.getList() list=self.fillList.getLocal() for path,iso,isoSize in list: self.localAdd(path,iso, isoSize) self.fillList.quit() self.lw.hide() def stopSync(self): self.rsyncThread.stop() self.IprogressBar.setEnabled(False) self.stop.setEnabled(False) self.syncGo.setEnabled(True) self.listIsos.setEnabled(True) self.selectAll.setEnabled(True) def main(self): self.show() # Load or look for intitial parameters self.prefsInit() # look for Isos list and add it to the isoSync list. Update preferences self.updateList() self.multiSelect() def close(self): self.rsyncThread.stop() exit(0)
class MysqlModel: def __init__(self, host, user, password, port=3306, charset='utf8mb4'): self.schema_model = QStandardItemModel() self.table_info_model = QStandardItemModel() self.data_model = QStandardItemModel() self.host = host self.user = user self.password = password self.port = port self.charset = charset self.db_icon = icon_font.icon('database') self.table_icon = icon_font.icon('table') self.connection = None self.init_connection() def init_connection(self): self.connection = pymysql.connect(host=self.host, port=self.port, user=self.user, password=self.password, charset=self.charset, database='test', autocommit=True) with self.connection.cursor() as cursor: cursor.execute('USE `information_schema`') cursor.execute( 'SELECT `TABLE_SCHEMA`, `TABLE_NAME` FROM `information_schema`.`TABLES`;' ) res = cursor.fetchall() for r in res: db_name = r[0] table_name = r[1] db_item = self.schema_model.findItems(db_name) if len(db_item) > 0: table_item = QStandardItem(table_name) table_item.setIcon(self.table_icon) table_item.setEditable(False) table_item.setFont(QFont("微软雅黑")) db_item[0].appendRow(table_item) else: db_item = QStandardItem(db_name) db_item.setEditable(False) db_item.setIcon(self.db_icon) db_item.setFont(QFont("微软雅黑")) table_item = QStandardItem(table_name) table_item.setIcon(self.table_icon) table_item.setEditable(False) table_item.setFont(QFont("微软雅黑")) db_item.appendRow(table_item) self.schema_model.appendRow(db_item) def show_table_data(self, db_name, table_name, overview_filter=None): self.data_model.clear() with self.connection.cursor() as cursor: cursor.execute('use %s' % db_name) cursor.execute('desc `%s`.`%s`;' % (db_name, table_name)) desc_table_res = cursor.fetchall() select_fields = [] for index, r in enumerate(desc_table_res): print(r) self.data_model.setHorizontalHeaderItem( index, QStandardItem(r[0])) if r[1] == 'text': select_fields.append('LEFT(`%s`, 128)' % r[0]) else: select_fields.append('`%s`' % r[0]) if overview_filter is None or overview_filter == '': cursor.execute('select %s from `%s` limit 1000' % (', '.join(select_fields), table_name)) else: cursor.execute( 'select %s from `%s` where %s limit 1000' % (', '.join(select_fields), table_name, overview_filter)) select_res = cursor.fetchall() for row_index, row_data in enumerate(select_res): for col_index, r in enumerate(row_data): # print(type(r)) if r is None: self.data_model.setItem(row_index, col_index, QStandardItem('NULL')) self.data_model.item(row_index, col_index).setForeground( QBrush(QColor(225, 225, 225))) elif isinstance(r, int): self.data_model.setItem(row_index, col_index, QStandardItem(str(r))) self.data_model.item(row_index, col_index).setForeground( QBrush(QColor(10, 20, 225))) self.data_model.item( row_index, col_index).setTextAlignment(Qt.AlignVCenter | Qt.AlignRight) else: self.data_model.setItem(row_index, col_index, QStandardItem(str(r))) self.data_model.item(row_index, col_index).setForeground( QBrush(QColor(80, 80, 80))) self.data_model.item(row_index, col_index).setFont(QFont("微软雅黑")) def add_db(self): self.model.appendRow(QStandardItem('new db')) # def __parse_create_table(self, sql): # sql = sql.replace('\n', ' ') # print(sql) # parten = re.compile('^CREATE\s+TABLE\s+`(.+?)`\s+\((\s+`(.+?)`.+){1,}\).*') # match = parten.match(sql) # print(match) # if match: # print(match.groups()) def __del__(self): print("close connection===") if self.connection: self.connection.close()
class MainWindow(QMainWindow): BASE_DIR = os.path.dirname(os.path.abspath(__file__)) MAIN_UI_FILE = os.path.join(BASE_DIR, "main.ui") NEW_DISH_POPUP_UI_FILE = os.path.join(BASE_DIR, "new_dish_popup.ui") NEW_DISH_MULTI_POPUP_UI_FILE = os.path.join(BASE_DIR, "new_dish_multi_popup.ui") NEW_DISH_DATA_POPUP_UI_FILE = os.path.join(BASE_DIR, "new_dish_data_popup.ui") MODIFY_DISH_POPUP_UI_FILE = os.path.join(BASE_DIR, "modify_dish_popup.ui") DB_FILE = os.path.join(BASE_DIR, "restaurant.db") def __init__(self): super(MainWindow, self).__init__() # Initialize variable self.db_connection = None self.new_dish_popup = QWidget() self.new_dish_multi_popup = QWidget() self.new_dish_data_popup = QWidget() self.modify_dish_popup = QWidget() self.dish_table_model = QStandardItemModel(0, 6) self.dish_table_proxy = TableFilter() self.dish_data_table_model = QStandardItemModel(0, 6) self.dish_data_table_proxy = TableFilter() self.graph_chart = None self.graph_series = {} # Load UI designs uic.loadUi(self.MAIN_UI_FILE, self) uic.loadUi(self.NEW_DISH_POPUP_UI_FILE, self.new_dish_popup) uic.loadUi(self.NEW_DISH_MULTI_POPUP_UI_FILE, self.new_dish_multi_popup) uic.loadUi(self.NEW_DISH_DATA_POPUP_UI_FILE, self.new_dish_data_popup) uic.loadUi(self.MODIFY_DISH_POPUP_UI_FILE, self.modify_dish_popup) self.init_dish_table() self.init_dish_data_table() self.init_graph() # Connect to database self.init_db_connection() # MainWindow Bind action triggers self.action_new_dish.triggered.connect(self.show_new_dish_popup) self.action_new_dish_multi.triggered.connect( self.show_new_dish_multi_popup) self.action_new_data_multi.triggered.connect( lambda: self.modify_new_dish_data_popup_table(show=True)) self.tabWidget.currentChanged.connect(self.update_graph) # Dish Table filter bind self.dish_lineEdit.textChanged.connect( lambda text, col_idx=1: self.dish_table_proxy.set_col_regex_filter( col_idx, text)) self.lower_price_doubleSpinBox.valueChanged.connect( lambda value, col_idx=2: self.dish_table_proxy. set_col_number_filter(col_idx, value, -1)) self.higher_price_doubleSpinBox.valueChanged.connect( lambda value, col_idx=2: self.dish_table_proxy. set_col_number_filter(col_idx, -1, value)) self.lower_week_sell_spinBox.valueChanged.connect( lambda value, col_idx=3: self.dish_table_proxy. set_col_number_filter(col_idx, value, -1)) self.higher_week_sell_spinBox.valueChanged.connect( lambda value, col_idx=3: self.dish_table_proxy. set_col_number_filter(col_idx, -1, value)) # Dish Data Table filter bind self.lower_data_dateEdit.dateChanged.connect( lambda date, col_idx=1: self.dish_data_table_proxy. set_col_date_filter(col_idx, date, -1)) self.higher_data_dateEdit.dateChanged.connect( lambda date, col_idx=1: self.dish_data_table_proxy. set_col_date_filter(col_idx, -1, date)) self.data_lineEdit.textChanged.connect( lambda text, col_idx=2: self.dish_data_table_proxy. set_col_regex_filter(col_idx, text)) self.lower_data_doubleSpinBox.valueChanged.connect( lambda value, col_idx=3: self.dish_data_table_proxy. set_col_number_filter(col_idx, value, -1)) self.higher_data_doubleSpinBox.valueChanged.connect( lambda value, col_idx=3: self.dish_data_table_proxy. set_col_number_filter(col_idx, -1, value)) self.lower_data_spinBox.valueChanged.connect( lambda value, col_idx=4: self.dish_data_table_proxy. set_col_number_filter(col_idx, value, -1)) self.higher_data_spinBox.valueChanged.connect( lambda value, col_idx=4: self.dish_data_table_proxy. set_col_number_filter(col_idx, -1, value)) self.data_all_check_checkBox.stateChanged.connect( lambda state, col_idx=5: self.data_table_check_state( state, col_idx)) self.dish_data_table_model.itemChanged.connect(self.update_series) # Popup bind action triggers self.new_dish_popup.create_new_dish_btn.clicked.connect( self.create_new_dish) self.new_dish_multi_popup.pushButton_ok.clicked.connect( self.create_new_dish_multi) self.new_dish_data_popup.dateEdit.dateChanged.connect( self.modify_new_dish_data_popup_table) self.new_dish_data_popup.pushButton_ok.clicked.connect( self.create_new_dish_data) # Get current dishes self.load_dish_table() self.load_dish_data_table() self.new_dish_data_popup.dateEdit.setDate(QtCore.QDate.currentDate()) def init_dish_table(self): self.dish_tableView.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) # Set Header data and stretch for col, col_name in enumerate( ["ID", "菜品", "价格", "近7天总售出", "操作", "备注"]): self.dish_table_model.setHeaderData(col, Qt.Horizontal, col_name, Qt.DisplayRole) self.dish_table_proxy.setSourceModel(self.dish_table_model) self.dish_tableView.setModel(self.dish_table_proxy) self.dish_tableView.setColumnHidden(0, True) for (col, method) in [(1, "Regex"), (2, "Number"), (3, "Number"), (5, "Regex")]: self.dish_table_proxy.filter_method[col] = method def init_dish_data_table(self): self.data_tableView.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) for col, col_name in enumerate( ["Dish_ID", "日期", "菜品", "价格", "售出", "选择"]): self.dish_data_table_model.setHeaderData(col, Qt.Horizontal, col_name, Qt.DisplayRole) self.dish_data_table_proxy.setSourceModel(self.dish_data_table_model) self.data_tableView.setModel(self.dish_data_table_proxy) self.data_tableView.setColumnHidden(0, True) for (col, method) in [(1, "Date"), (2, "Regex"), (3, "Number"), (4, "Number")]: self.dish_data_table_proxy.filter_method[col] = method def init_graph(self): self.graph_chart = QChart(title="售出图") self.graph_chart.legend().setVisible(True) self.graph_chart.setAcceptHoverEvents(True) graph_view = QChartView(self.graph_chart) graph_view.setRenderHint(QPainter.Antialiasing) self.gridLayout_5.addWidget(graph_view) def init_db_connection(self): self.db_connection = sqlite3.connect(self.DB_FILE) cursor = self.db_connection.cursor() # check create table if not exist sql_create_dish_table = """ CREATE TABLE IF NOT EXISTS dish ( id integer PRIMARY KEY, name text NOT NULL, price numeric Not NULL, remarks text, UNIQUE (name, price) ); """ sql_create_dish_data_table = """ CREATE TABLE IF NOT EXISTS dish_data ( dish_id integer NOT NULL REFERENCES dish(id) ON DELETE CASCADE, date date, sell_num integer DEFAULT 0, PRIMARY KEY (dish_id, date), CONSTRAINT dish_fk FOREIGN KEY (dish_id) REFERENCES dish (id) ON DELETE CASCADE ); """ sql_trigger = """ CREATE TRIGGER IF NOT EXISTS place_holder_data AFTER INSERT ON dish BEGIN INSERT INTO dish_data (dish_id, date, sell_num) VALUES(new.id, null, 0); END; """ cursor.execute(sql_create_dish_table) cursor.execute(sql_create_dish_data_table) cursor.execute("PRAGMA FOREIGN_KEYS = on") cursor.execute(sql_trigger) cursor.close() def load_dish_table(self): today = datetime.today() sql_select_query = """ SELECT dish.id, dish.name, dish.price, COALESCE(SUM(dish_data.sell_num), 0), dish.remarks FROM dish LEFT JOIN dish_data ON dish.id = dish_data.dish_id WHERE dish_data.date IS NULL OR dish_data.date BETWEEN date('{}') and date('{}') GROUP BY dish.id ORDER BY dish.name, dish.price;""".format( (today - timedelta(days=7)).strftime("%Y-%m-%d"), today.strftime("%Y-%m-%d")) cursor = self.db_connection.cursor() cursor.execute(sql_select_query) records = cursor.fetchall() for row_idx, record in enumerate(records): self.dish_table_model.appendRow(create_dish_table_row(*record)) cursor.close() self.dish_tableView.setItemDelegateForColumn( 4, DishTableDelegateCell(self.show_modify_dish_popup, self.delete_dish, self.dish_tableView)) def load_dish_data_table(self): sql_select_query = """ SELECT dish_data.dish_id, dish_data.date, dish.name, dish.price, dish_data.sell_num FROM dish_data LEFT JOIN dish ON dish_data.dish_id = dish.id WHERE dish_data.date IS NOT NULL ORDER BY dish_data.date DESC, dish.name, dish.price, dish_data.sell_num;""" cursor = self.db_connection.cursor() cursor.execute(sql_select_query) records = cursor.fetchall() for row_idx, record in enumerate(records): self.dish_data_table_model.appendRow( create_dish_data_table_row(*record)) cursor.close() self.lower_data_dateEdit.setDate(QDate.currentDate().addDays(-7)) self.higher_data_dateEdit.setDate(QDate.currentDate()) self.data_tableView.setItemDelegateForColumn( 5, DishDataTableDelegateCell(self.data_tableView)) def data_table_check_state(self, state, col): for row in range(self.dish_data_table_proxy.rowCount()): index = self.dish_data_table_proxy.mapToSource( self.dish_data_table_proxy.index(row, col)) if index.isValid(): self.dish_data_table_model.setData(index, str(state), Qt.DisplayRole) def show_new_dish_popup(self): # Move popup to center point = self.rect().center() global_point = self.mapToGlobal(point) self.new_dish_popup.move( global_point - QtCore.QPoint(self.new_dish_popup.width() // 2, self.new_dish_popup.height() // 2)) self.new_dish_popup.show() def show_new_dish_multi_popup(self): file_name = QFileDialog().getOpenFileName(None, "选择文件", "", self.tr("CSV文件 (*.csv)"))[0] self.new_dish_multi_popup.tableWidget.setRowCount(0) if file_name: with open(file_name, "r") as file: csv_reader = csv.reader(file, delimiter=",") for idx, row_data in enumerate(csv_reader): if len(row_data) == 2: name, price = row_data remark = "" elif len(row_data) == 3: name, price, remark = row_data else: QMessageBox.warning( self, "格式错误", self.tr('格式为"菜品 价格"或者"菜品 价格 备注"\n第{}行输入有误'.format( idx))) return self.new_dish_multi_popup.tableWidget.insertRow( self.new_dish_multi_popup.tableWidget.rowCount()) self.new_dish_multi_popup.tableWidget.setItem( idx, 0, QTableWidgetItem(name)) price_type = str_type(price) if price_type == str or (isinstance( price_type, (float, int)) and float(price) < 0): QMessageBox.warning( self, "格式错误", self.tr('第{}行价格输入有误'.format(idx + 1))) return self.new_dish_multi_popup.tableWidget.setItem( idx, 1, QTableWidgetItem("{:.2f}".format(float(price)))) self.new_dish_multi_popup.tableWidget.setItem( idx, 2, QTableWidgetItem(remark)) self.new_dish_multi_popup.show() def modify_new_dish_data_popup_table(self, *args, show=False): sql_select_query = """ SELECT id, name, price, dish_data.sell_num FROM dish LEFT JOIN dish_data ON dish.id=dish_data.dish_id WHERE dish_data.date IS NULL OR dish_data.date = date('{}') GROUP BY id, name, price ORDER BY dish.name, dish.price;""".format( self.new_dish_data_popup.dateEdit.date().toString("yyyy-MM-dd")) cursor = self.db_connection.cursor() cursor.execute(sql_select_query) records = cursor.fetchall() self.new_dish_data_popup.tableWidget.setRowCount(len(records)) self.new_dish_data_popup.tableWidget.setColumnHidden(0, True) for row_idx, record in enumerate(records): dish_id, name, price, sell_num = record self.new_dish_data_popup.tableWidget.setItem( row_idx, 0, QTableWidgetItem(str(dish_id))) self.new_dish_data_popup.tableWidget.setItem( row_idx, 1, QTableWidgetItem(name)) self.new_dish_data_popup.tableWidget.setItem( row_idx, 2, QTableWidgetItem("{:.2f}".format(price))) spin_box = QSpinBox() spin_box.setMaximum(9999) spin_box.setValue(sell_num) self.new_dish_data_popup.tableWidget.setCellWidget( row_idx, 3, spin_box) cursor.close() if show: self.new_dish_data_popup.show() def create_new_dish(self): cursor = self.db_connection.cursor() sql_insert = """ INSERT INTO dish(name, price, remarks) VALUES(?,?,?)""" dish_name = self.new_dish_popup.dish_name.text() dish_price = self.new_dish_popup.dish_price.value() dish_remark = self.new_dish_popup.dish_remark.toPlainText() try: cursor.execute(sql_insert, (dish_name, dish_price, dish_remark)) new_dish_id = cursor.lastrowid cursor.close() self.db_connection.commit() # Update dish table and dish comboBox in UI self.dish_table_model.appendRow( create_dish_table_row(new_dish_id, dish_name, dish_price, 0, dish_remark)) self.new_dish_popup.hide() except sqlite3.Error: cursor.close() QMessageBox.warning(self, "菜品价格重复", self.tr('菜品价格组合重复,请检查')) def create_new_dish_multi(self): cursor = self.db_connection.cursor() sql_insert = """ INSERT INTO dish(name, price, remarks) VALUES (?, ?, ?)""" for row in range(self.new_dish_multi_popup.tableWidget.rowCount()): dish_name = self.new_dish_multi_popup.tableWidget.item(row, 0).text() dish_price = float( self.new_dish_multi_popup.tableWidget.item(row, 1).text()) dish_remark = self.new_dish_multi_popup.tableWidget.item(row, 2).text() try: cursor.execute(sql_insert, (dish_name, dish_price, dish_remark)) new_dish_id = cursor.lastrowid self.dish_table_model.appendRow( create_dish_table_row(new_dish_id, dish_name, dish_price, 0, dish_remark)) except sqlite3.Error: cursor.close() QMessageBox.warning( self, "菜品价格重复", self.tr('前{}行已插入。\n第{}行菜品价格组合重复,请检查'.format(row, row + 1))) return cursor.close() self.db_connection.commit() self.new_dish_multi_popup.hide() def create_new_dish_data(self): current_date = self.new_dish_data_popup.dateEdit.date().toString( "yyyy-MM-dd") table_filter = TableFilter() table_filter.setSourceModel(self.dish_data_table_model) table_filter.set_col_regex_filter(1, current_date) for row in range(table_filter.rowCount()): index = table_filter.mapToSource(table_filter.index(0, 1)) if index.isValid(): self.dish_data_table_model.removeRow(index.row()) del table_filter cursor = self.db_connection.cursor() sql_insert = """ INSERT OR REPLACE INTO dish_data(dish_id, date, sell_num) VALUES (?, ?, ?)""" for row in range(self.new_dish_data_popup.tableWidget.rowCount()): dish_id = int( self.new_dish_data_popup.tableWidget.item(row, 0).text()) name = self.new_dish_data_popup.tableWidget.item(row, 1).text() price = float( self.new_dish_data_popup.tableWidget.item(row, 2).text()) sell_num = self.new_dish_data_popup.tableWidget.cellWidget( row, 3).value() cursor.execute(sql_insert, (dish_id, current_date, sell_num)) self.dish_data_table_model.appendRow( create_dish_data_table_row(dish_id, current_date, name, price, sell_num)) cursor.close() self.db_connection.commit() self.new_dish_data_popup.hide() def delete_dish(self, dish_id): cursor = self.db_connection.cursor() sql_delete = """ DELETE FROM dish WHERE id=?""" cursor.execute(sql_delete, tuple([dish_id])) cursor.close() self.db_connection.commit() # Update dish table and dish comboBox in UI for row in self.dish_data_table_model.findItems(str(dish_id)): index = row.index() if index.isValid(): self.dish_data_table_model.removeRow(index.row()) for row in self.dish_table_model.findItems(str(dish_id)): index = row.index() if index.isValid(): self.dish_table_model.removeRow(index.row()) def show_modify_dish_popup(self, dish_id): point = self.rect().center() global_point = self.mapToGlobal(point) self.modify_dish_popup.move( global_point - QtCore.QPoint(self.modify_dish_popup.width() // 2, self.modify_dish_popup.height() // 2)) # Find the row and get necessary info index = self.dish_table_model.match(self.dish_table_model.index(0, 0), Qt.DisplayRole, str(dish_id)) if index: row_idx = index[0] dish_name = self.dish_table_model.data(row_idx.siblingAtColumn(1)) dish_price = self.dish_table_model.data(row_idx.siblingAtColumn(2)) dish_remark = self.dish_table_model.data( row_idx.siblingAtColumn(5)) self.modify_dish_popup.dish_name.setText(dish_name) self.modify_dish_popup.dish_price.setValue(float(dish_price)) self.modify_dish_popup.dish_remark.setText(dish_remark) try: self.modify_dish_popup.modify_dish_btn.clicked.disconnect() except TypeError: pass self.modify_dish_popup.modify_dish_btn.clicked.connect( lambda: self.modify_dish(row_idx, dish_id)) self.modify_dish_popup.show() def modify_dish(self, row, dish_id): cursor = self.db_connection.cursor() sql_update = """ UPDATE dish SET name = ?, price = ?, remarks = ? WHERE id=?""" dish_name = self.modify_dish_popup.dish_name.text() dish_price = self.modify_dish_popup.dish_price.value() dish_remark = self.modify_dish_popup.dish_remark.toPlainText() cursor.execute(sql_update, (dish_name, dish_price, dish_remark, dish_id)) cursor.close() self.db_connection.commit() self.modify_dish_popup.hide() # Update dish table and dish comboBox in UI old_name = self.dish_table_model.data(row.siblingAtColumn(1)) old_price = self.dish_table_model.data(row.siblingAtColumn(2)) sell_num = self.dish_table_model.data(row.siblingAtColumn(3)) row_idx = row.row() self.dish_table_model.removeRow(row_idx) self.dish_table_model.insertRow( row_idx, create_dish_table_row(dish_id, dish_name, dish_price, sell_num, dish_remark)) for row in self.dish_data_table_model.findItems(str(dish_id)): index = row.index() if index.isValid(): self.dish_data_table_model.setData(index.siblingAtColumn(2), dish_name) self.dish_data_table_model.setData(index.siblingAtColumn(3), "{:.2f}".format(dish_price)) old_key = old_name + '(' + old_price + ')' if old_key in self.graph_line_series: self.graph_line_series[dish_name + '(' + str(dish_price) + ')'] = self.graph_line_series[old_key] del self.graph_line_series[old_key] def update_series(self, item: QStandardItem): if item.column() == 5: # check for checkbox column item_idx = item.index() date = self.dish_data_table_model.data(item_idx.siblingAtColumn(1)) dish_name = self.dish_data_table_model.data( item_idx.siblingAtColumn(2)) dish_price = self.dish_data_table_model.data( item_idx.siblingAtColumn(3)) sell_num = self.dish_data_table_model.data( item_idx.siblingAtColumn(4)) set_name = dish_name + "(" + dish_price + ")" key = str( QDateTime(QDate.fromString(date, "yyyy-MM-dd")).toSecsSinceEpoch()) if key not in self.graph_series: self.graph_series[key] = {} if int(item.text()) == 0: if set_name in self.graph_series[key]: del self.graph_series[key][set_name] if not self.graph_series[key]: del self.graph_series[key] else: self.graph_series[key][set_name] = int(sell_num) def update_graph(self, index): if index == 2: self.graph_chart.removeAllSeries() axis_x = QBarCategoryAxis() axis_x.setTitleText("日期") if self.graph_chart.axisX(): self.graph_chart.removeAxis(self.graph_chart.axisX()) self.graph_chart.addAxis(axis_x, Qt.AlignBottom) axis_y = QValueAxis() axis_y.setLabelFormat("%i") axis_y.setTitleText("售出量") if self.graph_chart.axisY(): self.graph_chart.removeAxis(self.graph_chart.axisY()) self.graph_chart.addAxis(axis_y, Qt.AlignLeft) max_num = 0 total_date = 0 set_dict = {} for key, data in sorted(self.graph_series.items(), key=lambda i: int(i[0])): axis_x.append( QDateTime.fromSecsSinceEpoch( int(key)).toString("yyyy年MM月dd日")) for set_name, value in data.items(): if set_name not in set_dict: set_dict[set_name] = QBarSet(set_name) for _ in range(total_date): set_dict[set_name].append(0) set_dict[set_name].append(value) max_num = max(max_num, value) total_date += 1 for _, bar_set in set_dict.items(): if bar_set.count() < total_date: bar_set.append(0) bar_series = QBarSeries() for _, bar_set in set_dict.items(): bar_series.append(bar_set) bar_series.hovered.connect(self.graph_tooltip) axis_y.setMax(max_num + 1) axis_y.setMin(0) self.graph_chart.addSeries(bar_series) bar_series.attachAxis(axis_x) bar_series.attachAxis(axis_y) def graph_tooltip(self, status, index, bar_set: QBarSet): if status: QToolTip.showText( QCursor.pos(), "{}\n日期: {}\n售出: {}".format(bar_set.label(), self.graph_chart.axisX().at(index), int(bar_set.at(index))))
class Table(QWidget): def __init__(self): super(Table, self).__init__() self.setWindowTitle('TableView示例') self.resize(600, 600) self.model = QStandardItemModel(4, 4) ''' QStandardItemModel:'Q标准化模型'类提供了一个用于存储定制数据的通用模型。 'Q标准化模型'可以作为标准Qt数据类型的存储库。 它是模型/视图类之一,也是Qt模型/视图框架的一部分。 'Q标准化模型'提供了一种经典的基于项目的方法来处理模型。 'Q标准化模型'提供了Q标准化模型中的项目。 'Q标准化模型'实现了QAbstractItemModel接口,这意味着该模型可以用于提供支持该接口 的任何视图中的数据(如QListView、QTableView和QTreeView,以及自定义的类型)。 当您需要一个列表或树时,通常会创建一个空的'q标准化模型', 并使用appendRow()将项目添加到模型中,并使用item()来访问项目。 如果您的模型代表一个表,那么您通常将表的维度传递给'q标准化模型'构造函数, 并使用setItem()将条目放置到表中。您还可以使用setRowCount()和setColumnCount()来改变模型的维度。 要插入项,请使用insertRow()或insertColumn(),并删除项目,使用removeRow()或removeColumn()。 ''' tableTittleList = ['行数', '针数', '次数', '收针'] self.model.setHorizontalHeaderLabels(tableTittleList) dataList = [ '5', '7', 'dd', '90', '34', '', '1', '33', '45', '31', '34', '12', '89', '12', '1', '513' ] for [n, (i, j)] in enumerate([(i, j) for i in range(4) for j in range(4)]): item = QStandardItem(dataList[n]) self.model.setItem(i, j, item) self.tabletView = QTableView() self.tabletView.setModel(self.model) # 设置tableView的最后一列会跟随窗口水平伸缩 self.tabletView.horizontalHeader().setStretchLastSection(True) # 设置tableView的所有列都会跟谁窗口伸缩 self.tabletView.horizontalHeader().setSectionResizeMode( QHeaderView.Stretch) self.stateLabel = QLabel('Size:(0x0),Mouse:(0,0)') self.setStateLabel() self.tabletView.clicked.connect(self.setStateLabel) # 添加一些按钮:删除行,插入行,清空行, delBtn = QPushButton('删除') insertRowBtn = QPushButton('插入') clrBtn = QPushButton('清除') # 添加2个lineEdit和一个btn 用来查询用户指定的位置的数据 rowLine = QLineEdit() colLine = QLineEdit() findBtn = QPushButton('查询') # 创建一个整数验证器,用来限制输入的数据为0~300的整数 intValidator = QIntValidator() intValidator.setRange(0, 300) # 添加信号槽 rowLine.setValidator(intValidator) colLine.setValidator(intValidator) rowLine.setPlaceholderText('输入查询的行数') colLine.setPlaceholderText('输入要查询的列数') findBtn.clicked.connect( lambda: self.findData(int(rowLine.text()), int(colLine.text()))) # 为按钮添加信号槽 delBtn.clicked.connect(self.removeRow) insertRowBtn.clicked.connect(self.insertRow) clrBtn.clicked.connect(self.clearSelectedItem) btnGridLayout = QGridLayout() btnGridLayout.addWidget(delBtn, 0, 0) btnGridLayout.addWidget(insertRowBtn, 0, 1) btnGridLayout.addWidget(clrBtn, 0, 2) btnGridLayout.addWidget(rowLine, 1, 0) btnGridLayout.addWidget(colLine, 1, 1) btnGridLayout.addWidget(findBtn, 1, 2) # 创建查询框和查询按钮 searchLine = QLineEdit() columnNumLine = QLineEdit() searchBtn = QPushButton('搜索') columnNumLine.setValidator(intValidator) # 为搜索按钮添加槽 searchBtn.clicked.connect( lambda: self.searchData(searchLine.text(), columnNumLine.text())) btnGridLayout.addWidget(searchLine, 2, 0) btnGridLayout.addWidget(columnNumLine, 2, 1) btnGridLayout.addWidget(searchBtn, 2, 2) layout = QVBoxLayout() layout.addWidget(self.tabletView) layout.addLayout(btnGridLayout) layout.addWidget(self.stateLabel) self.setLayout(layout) def setStateLabel(self, p_arg): print(p_arg) '''获取当前tableView的大小和鼠标点击的位置,以及选择和框选区大小''' selectedIndexes = self.tabletView.selectedIndexes() stateList = [ self.model.rowCount(), self.model.columnCount(), self.tabletView.currentIndex().row(), self.tabletView.currentIndex().column() ] self.stateLabel.setText( 'Size:(%dx%d),Mouse:(%d,%d)' % (stateList[0], stateList[1], stateList[2] + 1, stateList[3] + 1)) print(stateList) def insertRow(self): if self.model.rowCount() < 300: if self.model.rowCount() == 0: self.model.setItem(0, QStandardItem('')) else: self.model.insertRow(self.tabletView.currentIndex().row()) print('rowCount = ', self.model.rowCount()) else: QMessageBox.warning(self, '停止', '最大支持300行数据!') self.setStateLabel() def removeRow(self): indexes = self.tabletView.selectedIndexes() rowIndexList = [] for index in indexes: rowIndexList.append(index.row()) rowIndexSet = set(rowIndexList) print(len(indexes), len(rowIndexList), len(rowIndexSet)) self.model.removeRows(self.tabletView.currentIndex().row(), len(rowIndexSet)) self.setStateLabel() def findData(self, n_row, n_col): print('开始查询第{0}行,第{1}列的数据...'.format(n_row, n_col)) index = self.model.index(n_row - 1, n_col - 1) # 检查输入的数据是否超出表格范围,并检查表格内容是否为空 if (n_row - 1) > self.model.rowCount(): QMessageBox.critical(self, '错误', '输入的行数超过表格最大行数') elif (n_col - 1) > self.model.columnCount(): QMessageBox.critical(self, '错误', '输入的列数超过表格最大列数') else: data = self.model.data(index) if data: QMessageBox.information( self, '查询', '({0},{1})位置处的数值是{2}'.format(n_row, n_col, data)) else: QMessageBox.information( self, '查询', '({0},{1})位置处的数值是{2}'.format(n_row, n_col, '空的')) def searchData(self, data, column_num): # 用来在指定的column_num列找那个查找有没有data的item # 如果找到,返回其行数, 如果找不到,告知找不到 column_num = int(column_num) dataItem = QStandardItem(data) indexList = self.model.findItems(data, column=column_num - 1) list = [] for i in range(len(indexList)): list.append(indexList[i].row()) if len(list) == 0: QMessageBox.information( self, '找不到', '在第{0}列中找不到\n任何值是{1}元素'.format(column_num, data)) else: for i in range(len(list)): list[i] = list[i] + 1 dlgText = """在第{0}列中找到了值是'{1}'的元素共<font color = red>{2}</font>个,分别在第{3}列""".format( column_num, data, len(list), list) QMessageBox.information(self, '找到了', dlgText) def clearSelectedItem(self): indexes = self.tabletView.selectedIndexes() for index in indexes: self.model.setItem(index.row(), index.column(), QStandardItem(''))
class BreakpointsWidget(QWidget): """ BreakpointsWidget Signals: onBreakpointChanged(str) - ptr onBreakpointRemoved(str) - ptr """ onBreakpointChanged = pyqtSignal(str, name='onBreakpointChanged') onBreakpointsRemoved = pyqtSignal(str, name='onBreakpointRemoved') def __init__(self, parent=None): # pylint: disable=too-many-statements super(BreakpointsWidget, self).__init__(parent=parent) self._app_window = parent if self._app_window.dwarf is None: print('BreakpointsWidget created before Dwarf exists') return # connect to dwarf self._app_window.dwarf.onApplyContext.connect(self._on_apply_context) self._app_window.dwarf.onAddJavaBreakpoint.connect( self._on_add_breakpoint) self._app_window.dwarf.onAddObjCBreakpoint.connect( self._on_add_breakpoint) self._app_window.dwarf.onAddNativeBreakpoint.connect( self._on_add_breakpoint) self._app_window.dwarf.onAddModuleInitializationBreakpoint.connect( self._on_add_breakpoint) self._app_window.dwarf.onAddJavaClassInitializationBreakpoint.connect( self._on_add_breakpoint) self._app_window.dwarf.onHitModuleInitializationBreakpoint.connect( self._on_hit_module_initialization_breakpoint) self._app_window.dwarf.onHitJavaClassInitializationBreakpoint.connect( self._on_hit_java_class_initialization_breakpoint) self._app_window.dwarf.onDeleteBreakpoint.connect( self._on_breakpoint_deleted) self._breakpoints_list = DwarfListView() self._breakpoints_list.doubleClicked.connect(self._on_double_clicked) self._breakpoints_list.setContextMenuPolicy(Qt.CustomContextMenu) self._breakpoints_list.customContextMenuRequested.connect( self._on_context_menu) self._breakpoints_model = QStandardItemModel(0, 3) self._breakpoints_model.setHeaderData(0, Qt.Horizontal, 'Address') self._breakpoints_model.setHeaderData(1, Qt.Horizontal, 'T') self._breakpoints_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._breakpoints_model.setHeaderData(2, Qt.Horizontal, '<>') self._breakpoints_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._breakpoints_list.setModel(self._breakpoints_model) self._breakpoints_list.header().setStretchLastSection(False) self._breakpoints_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents | QHeaderView.Interactive) self._breakpoints_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self._breakpoints_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) v_box = QVBoxLayout(self) v_box.setContentsMargins(0, 0, 0, 0) v_box.addWidget(self._breakpoints_list) h_box = QHBoxLayout() h_box.setContentsMargins(5, 2, 5, 5) self.btn1 = QPushButton( QIcon(utils.resource_path('assets/icons/plus.svg')), '') self.btn1.setFixedSize(20, 20) self.btn1.clicked.connect(self._on_add_item_clicked) btn2 = QPushButton(QIcon(utils.resource_path('assets/icons/dash.svg')), '') btn2.setFixedSize(20, 20) btn2.clicked.connect(self.delete_items) btn3 = QPushButton( QIcon(utils.resource_path('assets/icons/trashcan.svg')), '') btn3.setFixedSize(20, 20) btn3.clicked.connect(self.clear_list) h_box.addWidget(self.btn1) h_box.addWidget(btn2) h_box.addSpacerItem( QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred)) h_box.addWidget(btn3) # header.setLayout(h_box) # header.setFixedHeight(25) # v_box.addWidget(header) v_box.addLayout(h_box) self.setLayout(v_box) self._bold_font = QFont(self._breakpoints_list.font()) self._bold_font.setBold(True) shortcut_addnative = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_N), self._app_window, self._on_add_native_breakpoint) shortcut_addnative.setAutoRepeat(False) shortcut_addjava = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_J), self._app_window, self._on_add_java_breakpoint) shortcut_addjava.setAutoRepeat(False) shortcut_add_native_onload = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_O), self._app_window, self._on_add_module_initialization_breakpoint) shortcut_add_native_onload.setAutoRepeat(False) # new menu self.new_menu = QMenu('New') self.new_menu.addAction('Native', self._on_add_native_breakpoint) self.new_menu.addAction('Module initialization', self._on_add_module_initialization_breakpoint) # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def delete_items(self): """ Delete selected Items """ index = self._breakpoints_list.selectionModel().currentIndex().row() if index != -1: self._on_delete_breakpoint(index) self._breakpoints_model.removeRow(index) def clear_list(self): """ Clear the List """ # go through all items and tell it gets removed for item in range(self._breakpoints_model.rowCount()): self._on_delete_breakpoint(item) if self._breakpoints_model.rowCount() > 0: # something was wrong it should be empty self._breakpoints_model.removeRows( 0, self._breakpoints_model.rowCount()) # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _on_apply_context(self, context): if context['reason'] == -1: if self._app_window.dwarf.java_available: self.new_menu.addAction('Java', self._on_add_java_breakpoint) self.new_menu.addAction( 'Java class initialization', self._on_add_java_class_initialization_breakpoint) def _on_add_breakpoint(self, breakpoint): type_ = QStandardItem() type_.setFont(self._bold_font) type_.setTextAlignment(Qt.AlignCenter) if breakpoint.breakpoint_type == BREAKPOINT_NATIVE: type_.setText('N') type_.setToolTip('Native breakpoint') elif breakpoint.breakpoint_type == BREAKPOINT_JAVA: type_.setText('J') type_.setToolTip('Java breakpoint') elif breakpoint.breakpoint_type == BREAKPOINT_INITIALIZATION: type_.setText('C') type_.setToolTip('Initialization breakpoint') elif breakpoint.breakpoint_type == BREAKPOINT_OBJC: type_.setText('O') type_.setToolTip('ObjC breakpoint') else: type_.setText('U') type_.setToolTip('Unknown Type') addr = QStandardItem() if breakpoint.breakpoint_type == BREAKPOINT_JAVA: addr.setText(breakpoint.get_target()) elif breakpoint.breakpoint_type == BREAKPOINT_OBJC: addr.setText(breakpoint.get_target()) elif breakpoint.breakpoint_type == BREAKPOINT_INITIALIZATION: addr.setText(breakpoint.get_target()) addr.setData(breakpoint.debug_symbol, Qt.UserRole + 2) else: str_fmt = '0x{0:x}' if self._breakpoints_list.uppercase_hex: str_fmt = '0x{0:X}' # addr.setTextAlignment(Qt.AlignCenter) addr.setText(str_fmt.format(breakpoint.get_target())) condition = QStandardItem() condition.setTextAlignment(Qt.AlignCenter) condition.setFont(self._bold_font) if breakpoint.condition and breakpoint.condition != 'null' and breakpoint.condition != 'undefined': condition.setText('ƒ') condition.setToolTip(breakpoint.condition) condition.setData(breakpoint.condition, Qt.UserRole + 2) self._breakpoints_model.appendRow([addr, type_, condition]) self._breakpoints_list.resizeColumnToContents(0) def _on_hit_module_initialization_breakpoint(self, data): items = self._breakpoints_model.findItems(data[1]['module'], Qt.MatchExactly, 2) if len(items) > 0: self._breakpoints_model.item(items[0].row(), 0).setText(data[1]['moduleBase']) def _on_hit_java_class_initialization_breakpoint(self, data): items = self._breakpoints_model.findItems(data[0], Qt.MatchExactly, 2) if len(items) > 0: pass def _on_double_clicked(self, model_index): item = self._breakpoints_model.itemFromIndex(model_index) if model_index.column() == 2 and item.text() == 'ƒ': self._on_modify_condition(model_index.row()) else: self._app_window.jump_to_address(self._breakpoints_model.item( model_index.row(), 0).text(), view=1) def _on_context_menu(self, pos): context_menu = QMenu(self) context_menu.addMenu(self.new_menu) context_menu.addSeparator() index = self._breakpoints_list.indexAt(pos).row() if index != -1: context_menu.addAction( 'Copy address', lambda: utils.copy_hex_to_clipboard( self._breakpoints_model.item(index, 0).text())) context_menu.addAction( 'Jump to address', lambda: self._app_window.jump_to_address( self._breakpoints_model.item(index, 0).text())) context_menu.addSeparator() context_menu.addAction('Edit Condition', lambda: self._on_modify_condition(index)) context_menu.addSeparator() context_menu.addAction('Delete Breakpoint', lambda: self._on_delete_breakpoint(index)) if self._breakpoints_list.search_enabled: context_menu.addSeparator() context_menu.addAction('Search', self._breakpoints_list._on_cm_search) # show context menu global_pt = self._breakpoints_list.mapToGlobal(pos) context_menu.exec(global_pt) def _on_modify_condition(self, num_row): item = self._breakpoints_model.item(num_row, 2) data = item.data(Qt.UserRole + 2) if data is None: data = '' ptr = self._breakpoints_model.item(num_row, 0).text() accept, input_ = InputMultilineDialog().input( 'Condition for breakpoint %s' % ptr, input_content=data) if accept: what = utils.parse_ptr(ptr) if what == 0: what = self._breakpoints_model.item(num_row, 2).data(Qt.UserRole + 2) if self._app_window.dwarf.dwarf_api( 'setBreakpointCondition', [what, input_.replace('\n', '')]): item.setData(input_, Qt.UserRole + 2) if not item.text(): item.setText('ƒ') item.setToolTip(input_) self.onBreakpointChanged.emit(ptr) # + button def _on_add_item_clicked(self): self.new_menu.exec_(QCursor.pos()) # shortcuts/menu def _on_add_native_breakpoint(self): self._app_window.dwarf.breakpoint_native() def _on_add_java_breakpoint(self): self._app_window.dwarf.breakpoint_java() def _on_add_module_initialization_breakpoint(self): self._app_window.dwarf.breakpoint_module_initialization() def _on_add_java_class_initialization_breakpoint(self): self._app_window.dwarf.breakpoint_java_class_initialization() def _on_delete_breakpoint(self, num_row): breakpoint_type = self._breakpoints_model.item(num_row, 1).text() if breakpoint_type == 'N': ptr = self._breakpoints_model.item(num_row, 0).text() ptr = utils.parse_ptr(ptr) self._app_window.dwarf.dwarf_api('removeBreakpoint', ptr) self.onBreakpointRemoved.emit(str(ptr)) elif breakpoint_type == 'J': target = self._breakpoints_model.item(num_row, 0).text() self._app_window.dwarf.dwarf_api('removeBreakpoint', target) elif breakpoint_type == 'O': target = self._breakpoints_model.item(num_row, 0).text() self._app_window.dwarf.dwarf_api('removeBreakpoint', target) elif breakpoint_type == 'C': item = self._breakpoints_model.item(num_row, 0) target = item.text() is_native = item.data(Qt.UserRole + 2) is None if is_native: self._app_window.dwarf.dwarf_api( 'removeModuleInitializationBreakpoint', target) else: self._app_window.dwarf.dwarf_api( 'removeJavaClassInitializationBreakpoint', target) elif breakpoint_type == 'U': ptr = self._breakpoints_model.item(num_row, 0).text() ptr = utils.parse_ptr(ptr) self._app_window.dwarf.dwarf_api('removeBreakpoint', ptr) self.onBreakpointRemoved.emit(str(ptr)) def _on_breakpoint_deleted(self, parts): _msg, _type, _val = parts additional = None if _type == 'objc' or _type == 'java' or _type == 'java_class_initialization': str_frmt = _val item_index = 0 elif _type == 'module_initialization': str_frmt = _val item_index = 0 else: _ptr = utils.parse_ptr(_val) if self._breakpoints_list._uppercase_hex: str_frmt = '0x{0:X}'.format(_ptr) else: str_frmt = '0x{0:x}'.format(_ptr) item_index = 0 for _item in range(self._breakpoints_model.rowCount()): item = self._breakpoints_model.item(_item, item_index) if item is None: continue if str_frmt == item.text(): if additional is not None: if additional == self._breakpoints_model.item(_item, 2).text(): self._breakpoints_model.removeRow(_item) else: self._breakpoints_model.removeRow(_item)
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 OlsDialog(QDialog): """Dialog to search the OLS for an ontology The OlsDialog uses threads to connect to the Ontology Lookup Service, and search for a query, get results, and get informations about ontologies. Selected ontology is returned as a json serialized dict. """ SigSearchCompleted = pyqtSignal('QString') def __init__(self, parent=None, allow_onto=False): super(OlsDialog, self).__init__(parent) self.ui = Ui_Ols() self.ui.setupUi(self) self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) self.onto = {} self.ontothreads = {} self.allow_onto = allow_onto self.entry = None self.ui.buttonBox.button(QDialogButtonBox.Ok).clicked.connect( self.accept) self.ui.buttonBox.button(QDialogButtonBox.Cancel).clicked.connect( self.reject) self.ui.searchButton.clicked.connect(self.search) self.ui.queryLine.setFocus() def search(self): """Launch the OlsSearchThread""" self.searcher = OlsSearcher(self.ui.queryLine.text()) self.searcher.Finished.connect(self.updateSearchResults) self.searcher.start() def updateSearchResults(self, jresults): """Update TreeView with search results.""" self.model = QStandardItemModel() self.orderedResults = {} if jresults: self.results = json.loads(jresults) for result in self.results: prefix = result['ontology_prefix'] # Create a new node & append it to StandardItemModel # if the ontology of the result is not already in StandardItemModel if not self.model.findItems(prefix): node = QStandardItem(prefix) self.model.appendRow(node) # Look for details of that new ontology if that ontology is not # already memo table and not other OlsOntologist is querying # informations about that ontology if prefix not in self.ontothreads and prefix not in self.onto: thread = OlsOntologist(prefix) thread.Finished.connect(self._memo_onto) thread.start() self.ontothreads[prefix] = thread # Add the entry to its ontology node result['tag'] = result['short_form'].replace( '_', ':') + ' - ' + result['label'] self.model.findItems(prefix)[0].appendRow( QStandardItem(result['tag'])) self.model.sort(0) self.ui.ontoTree.setModel(self.model) #self.ui.ontoTree.expandAll() self.model.setHorizontalHeaderLabels(["Object"]) self.ui.ontoTree.selectionModel().selectionChanged.connect( lambda selection: self.updateInterface(selection.indexes()[0])) self.ui.ontoTree.clicked.connect(self.updateInterface) self.ui.ontoTree.doubleClicked.connect(self.onDoubleClick) def _getResultFromIndex(self, index): """Iterate on self.results to get the right entry""" crawler = self.model.itemFromIndex(index) self.entry = None for x in self.results: if x['tag'] == crawler.text(): self.entry = x return crawler.text() def updateInterface(self, index): """Update the interface fields with the value from the TreeView""" if index: tag = self._getResultFromIndex(index) # selection is an ontology if self.entry is None: if not self.allow_onto: self.ui.buttonBox.button( QDialogButtonBox.Ok).setEnabled(False) # information about ontology is present in memoization table if tag in self.onto.keys(): self.entry = self.onto[tag] self.ui.value.setPlainText(self.entry['title']) self.ui.prefix.setText(self.entry['preferredPrefix']) self.ui.iri.setPlainText(self.entry['id']) self.ui.description.setPlainText(self.entry['description']) # No information is to be found else: self.ui.value.setPlainText("") self.ui.prefix.setText("") self.ui.iri.setPlainText("") self.ui.description.setPlainText("") self.ui.prefix.setText(tag) self.ui.type.setText('Ontology') # selection is a class else: self.ui.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) self.ui.value.setPlainText(self.entry['label']) self.ui.prefix.setText(self.entry['ontology_prefix']) self.ui.iri.setPlainText(self.entry['iri']) self.ui.type.setText('Class') self.ui.description.setPlainText( self.entry['description'][0] if 'description' in self.entry else '') def onDoubleClick(self, index): """Return class when double clicked""" self._getResultFromIndex(index) if self.entry is not None: self.accept() def _memo_onto(self, prefix, jsonconfig): self.onto[prefix] = json.loads(jsonconfig)
class GeneralView(QWidget): def __init__(self, parent=None): super().__init__() self.ui = Ui_AggrPortfolioView() self.ui.setupUi(self) self.ui.treeView.setRootIsDecorated(True) self.ui.treeView.setAlternatingRowColors(True) self.model = QStandardItemModel(0, 3, self) self.ui.treeView.setModel(self.model) self.model.setHorizontalHeaderLabels( ['Exchange', 'Volume', 'Totla in']) # balance = [ {'exchange':'binance', 'account': 'xxx1', 'symbol':'BTC', 'balance':{'total':234} }, # {'exchange':'binance', 'account': 'xxx1', 'symbol':'ETH', 'balance':{'total':15} }, # {'exchange':'kraken', 'account': 'xxx1', 'symbol':'ETH', 'balance':{'total':115} }, ] # for i in balance: # self.update_tree(i) self.ui.treeView.expandAll() #@pyqtSlot() def on_click(self): pass @pyqtSlot(str) def slot_update_tree(self, balance): #print(balance) balance = json.loads(balance) # parent1 = QStandardItem('binance') # currency1 = QStandardItem('BTC') # parent1.appendRow(currency1) # currency2 = QStandardItem('ETH') # parent1.appendRow(currency2) # self.model.appendRow(parent1) exchange = self.model.findItems(balance['exchange']) if exchange: all_currency = exchange[0].rowCount() new_currency = True print(all_currency) for c in range(all_currency): curency = exchange[0].itme(0, 0) if curency.text() == balance['symbol']: new_currency = False for i in range(curency.columnCount()): print(curency.text(), curency.item(0, i).text()) #print('update:', exchange[0].text(), curency.text() ) print(curency.text(), curency.model().item(0, 0).text(), curency.rowCount(), curency.columnCount()) curency.takeChild(0, 1).setText(balance['balance']['total']) break if new_currency: name = QStandardItem(balance['symbol']) total = QStandardItem(str(balance['balance']['total'])) exchange[0].appendRow([name, total]) else: parent1 = QStandardItem(balance['exchange']) child1 = QStandardItem(balance['symbol']) child2 = QStandardItem(str(balance['balance']['total'])) parent1.appendRow([child1, child2]) self.model.appendRow(parent1)
class FieldView(QWidget): keywordHighlight = pyqtSignal(str) # tabledChanged = pyqtSignal() def __init__(self, cols: dict, items: [dict], parent=None, undoStack=None): super(FieldView, self).__init__(parent) self.buffer = None self.cols = cols self.items = items self.table = TableView(self) self.model = QStandardItemModel() self.undoStack = undoStack self.table.setUndoStack(self.undoStack) self.table.setModel(self.model) # self.table.setWordWrap(True) self.entry = QLineEdit(self) self.entry.setClearButtonEnabled(True) self.entry.setPlaceholderText('Filter table by ...') self.entry.setMinimumWidth(300) self.entry.setMinimumHeight(35) self.caption = QLabel('') # self.hidden_rows = [] # self.valid_index = [] self.col_resize = [] self.is_reserve_show = QCheckBox(self) self.linting_en = QCheckBox(self) self.linting_en.setCheckState(Qt.Checked) hbox = QHBoxLayout() hbox.addWidget(self.is_reserve_show) hbox.addWidget(QLabel("Show RESERVED")) hbox.addWidget(self.linting_en) hbox.addWidget(QLabel("Linting")) hbox.addWidget(self.entry) hbox.addStretch(1) vbox = QVBoxLayout() vbox.addLayout(hbox) vbox.addWidget(self.caption) vbox.addWidget(self.table) self.setLayout(vbox) self.entry.textChanged.connect(self.tableFilter) self.is_reserve_show.stateChanged.connect(self.show_reserved) self.table.deleteKeyPress.connect(self.dataBeforeChangedEvent) self.table.replaceSignal.connect(self.dataBeforeChangedEvent) self.create_ui() def show_reserved(self): if self.is_reserve_show.checkState() == Qt.Checked: for row in range(self.model.rowCount()): for col in range(self.model.columnCount()): item = self.model.item(row, col) if item.data(Qt.UserRole) == 'reserved': item.setData('', Qt.UserRole) else: for row in range(self.model.rowCount()): field = list(self.cols.keys()).index('Field') item = self.model.item(row, field) if item.text().lower() == 'reserved': for col in range(self.model.columnCount()): cell = self.model.item(row, col) cell.setData('reserved', Qt.UserRole) def dataBeforeChangedEvent(self, index: QModelIndex, new: str, old: str): if not index.isValid(): return row = index.row() col = index.column() selected_indexes = self.table.selectedIndexes() indexes = [] oldText = [] for selected_index in selected_indexes: if selected_index.column() != index.column(): continue indexes.append(selected_index) oldText.append(selected_index.data()) cmd = DataChanged(widget=self.model, newtext=new, oldtext=oldText, index=indexes, description=f'Table Data changed at ({row}, {col})', obj=self.items) self.undoStack.push(cmd) def chgRowHeight(self, index: QModelIndex, size: QSizeF): if not index.isValid(): return row = index.row() self.table.setRowHeight(row, size.height()) def create_ui(self, items: list = None): self.create_actions() self.create_cols() self.create_rows(items) def create_rows(self, items: list = None, caption=None): if items is not None: self.items = items if caption is not None: self.caption.setText(caption) self.table.resetMatches() self.model.removeRows(0, self.model.rowCount()) for row, item in enumerate(self.items): is_reserved = (item.get('Field', '').lower() == 'reserved') & \ (self.is_reserve_show.checkState() == Qt.Unchecked) rows = [] for col in self.cols.keys(): cell = QStandardItem(str(item[col])) if is_reserved: cell.setData('reserved', Qt.UserRole) rows.append(cell) self.model.appendRow(rows) # to adjust row size which is better than resizeRowToContents # use self.chgRowHeight to adjust base on QTextEdit height # the sizeHint in QStyledItemDelegate cannot get correct height # due to text wrap for col in self.col_resize: self.table.openPersistentEditor(self.model.index(row, col)) self.table.closePersistentEditor(self.model.index(row, col)) if self.entry.text().strip() != "": self.tableFilter() def create_cols(self): self.model.setHorizontalHeaderLabels(self.cols.keys()) # self.model.head for col, config in enumerate(self.cols.values()): widget = config.get('widget', None) width = config.get('width', None) resize = config.get('resize', None) if width: self.table.setColumnWidth(col, width) if resize: self.table.horizontalHeader().setSectionResizeMode( col, QHeaderView.Stretch) self.col_resize.append(col) if widget == "list": items = config.get('items', []) delegate = TableListViewDelegate(self.table, items) elif widget == 'textEdit': delegate = TableTextEditDelegate(self.table) delegate.sizeChanged.connect(self.chgRowHeight) else: validator = config.get('type', None) items = config.get('items', None) delegate = TableLineEditDelegate( self.table, items, validator, minValue=config.get('minValue', 0), maxValue=config.get('maxValue', 31)) self.table.setItemDelegateForColumn(col, delegate) delegate.dataBeforeChanged.connect(self.dataBeforeChangedEvent) self.keywordHighlight.connect(delegate.setKeyword) def create_actions(self): for config in field_contextmenu: sc = config.get('shortcut', None) if sc is None: continue label = config.get('label') qaction = QAction(label, self.table) # func = actions.pop() func = getattr(self, config.get('action')) qaction.setShortcut(sc) qaction.setShortcutContext(Qt.WidgetWithChildrenShortcut) qaction.triggered.connect(func) self.addAction(qaction) def tableFilter(self): text = self.entry.text().strip() self.keywordHighlight.emit(text) self.table.matches.clear() for row in range(self.model.rowCount()): self.table.hideRow(row) for col in range(self.model.columnCount()): matches = self.model.findItems( text, Qt.MatchContains, column=col, ) for match in matches: self.table.showRow(match.row()) self.table.matches.extend(matches) def contextMenuEvent(self, event: QContextMenuEvent): actions = {} menu = QMenu(self.table) for config in field_contextmenu: label = config.get('label') buffer = config.get('buffer', False) action = menu.addAction(label) icon = config.get('icon', None) if buffer and self.buffer is None: action.setEnabled(False) if icon: action.setIcon(qta.icon(icon, color='gray')) sc = config.get('shortcut', None) func = getattr(self, config.get('action')) if sc: action.setShortcut(sc) actions[action] = func action = menu.exec_(self.mapToGlobal(event.pos())) func = actions.get(action) if callable(func): func() def iterSelectedRows(self): indexes = self.table.selectedIndexes() for i in range(0, len(indexes), len(self.cols.keys())): yield indexes[i].row() @property def selectedRows(self): indexes = self.table.selectedIndexes() rows = [] for i in range(0, len(indexes), len(self.cols.keys())): rows.append(indexes[i].row()) return rows def iterRowValues(self): # if headers is None: headers = self.cols.keys() for row in range(self.model.rowCount()): yield row, { header: self.model.item(row, col).text().strip() for col, header in enumerate(headers) } def searchAndReplace(self): self.table.dialog.show() def remove(self): if not self.items: return items = {} for row in sorted(self.selectedRows): # self.model.removeRow(row) rows = [] for col in range(len(self.cols.keys())): rows.append(self.model.item(row, col).text()) items[row] = rows if not items: return cmd = TableRemoveCommand(description='remove command', widget=self.table, items=items, obj=self.items) # self.table.selectRow(rows[-1]) self.undoStack.push(cmd) def copy(self): values = [] for row in self.iterSelectedRows(): rows = {} for col, header in enumerate(self.cols.keys()): rows[header] = self.model.item(row, col).text() values.append(rows) self.buffer = values def cut(self): self.copy() self.remove() def insertRow(self, row, item): cmd = TableInsertCommand(widget=self.table, row=row, description='Insert table', items=[ str( item.get(col, config.get('default', ''))) for col, config in self.cols.items() ], obj=self.items) self.undoStack.push(cmd) def append_new(self): row = self.table.currentRow col = list(self.cols.keys()).index('LSB') index = self.model.index(row, col) if not index.isValid(): lsb = 31 else: lsb = int(index.data()) - 1 if lsb < 0: lsb = 0 new = {"MSB": lsb, "LSB": lsb} self.insertRow(row + 1, item={**new_field, **new}) def append_copy(self): if self.buffer is None: return row = self.table.currentRow + 1 for item in reversed(self.buffer): self.insertRow(row, item=copy.deepcopy(item)) def prepend_new(self): row = self.table.currentRow col = list(self.cols.keys()).index('MSB') index = self.model.index(row, col) if not index.isValid(): msb = 31 row = 0 else: msb = int(index.data()) + 1 if msb > 31: msb = 31 new = {"MSB": msb, "LSB": msb} self.insertRow(row, item={**new_field, **new}) def prepend_copy(self): if self.buffer is None: return row = self.table.currentRow if row < 0: row = 0 for item in reversed(self.buffer): self.insertRow(row, item=copy.deepcopy(item)) def linting(self) -> (bool, str): msg = str() if not self.items or (self.linting_en.checkState() == Qt.Unchecked): return True, msg success = False msb = int(self.cols.get('MSB').get('maxValue', 31)) run = None try: for row, values in self.iterRowValues(): row += 1 run = row for header, config in self.cols.items(): require = config.get('require', True) value = values.get(header, '') if require: if value == '': msg = f'Value Error at Row: {row}, column: {header}\n' \ f'This entry cannot be empty' success = False break if header == "MSB": value = int(value) lsb = int(values.get('LSB', msb)) success = (value == msb) & (msb >= lsb) msb = lsb - 1 if self.model.rowCount() == row and success: success = lsb == int( self.cols.get('LSB').get('minValue', 0)) if not success: msg = f'Index Error at Row: {row}\n' \ f'Please Check MSB or LSB value' break if not success: break except Exception as e: return False, f"Something went wrong when handle Row: {row}\nError message: {str(e)}" if run is None: msg = 'Fields cannot be empty.\n' return success, msg def reserved(self): max_value = int(self.cols.get('MSB').get('maxValue', 31)) min_value = int(self.cols.get('LSB').get('minValue', 0)) reserves = {} for row, values in self.iterRowValues(): msb = int(values['MSB']) lsb = int(values['LSB']) if msb != max_value: reserves[row] = { **reserve_field, **dict(MSB=max_value, LSB=msb + 1) } max_value = lsb - 1 if max_value >= 0: reserves[self.model.rowCount()] = { **reserve_field, **dict(MSB=max_value, LSB=min_value) } for row in sorted(reserves.keys(), reverse=True): self.insertRow(row, reserves[row]) # def saveTable(self): # # self.items.clear() # for row, values in self.iterRowValues(): # self.items.append(values) # self.tableSaved.emit() # def checkTableChanged(self): # if len(self.items) != self.model.rowCount(): # return True # else: # for row, values in self.iterRowValues(): # old = self.items[row] # if old != values: # return True # else: # return False def setFocus(self, r=None): self.table.setFocus()