class CustomQCompleter(QCompleter): def __init__(self, *args): super(CustomQCompleter, self).__init__(*args) self.local_completion_prefix = "" self.source_model = None self.filterProxyModel = QSortFilterProxyModel(self) self.usingOriginalModel = False def setModel(self, model): self.source_model = model self.filterProxyModel = QSortFilterProxyModel(self) self.filterProxyModel.setSourceModel(self.source_model) super(CustomQCompleter, self).setModel(self.filterProxyModel) self.usingOriginalModel = True def updateModel(self): if not self.usingOriginalModel: self.filterProxyModel.setSourceModel(self.source_model) pattern = QRegExp(self.local_completion_prefix, Qt.CaseInsensitive, QRegExp.FixedString) self.filterProxyModel.setFilterRegExp(pattern) def splitPath(self, path): self.local_completion_prefix = path self.updateModel() if self.filterProxyModel.rowCount() == 0: self.usingOriginalModel = False self.filterProxyModel.setSourceModel(QStringListModel([path])) return [path] return []
class CustomQCompleter(QCompleter): """ adapted from: http://stackoverflow.com/a/7767999/2156909 """ def __init__(self, parent=None): super().__init__(parent) self.local_completion_prefix = "" self.source_model = None self.filterProxyModel = QSortFilterProxyModel(self) self.usingOriginalModel = False def setModel(self, strList): self.source_model = QStringListModel(strList) self.filterProxyModel = QSortFilterProxyModel(self) self.filterProxyModel.setSourceModel(self.source_model) super().setModel(self.filterProxyModel) self.usingOriginalModel = True def updateModel(self): if not self.usingOriginalModel: self.filterProxyModel.setSourceModel(self.source_model) pattern = QRegExp(self.local_completion_prefix, Qt.CaseInsensitive, QRegExp.FixedString) self.filterProxyModel.setFilterRegExp(pattern) def splitPath(self, path): self.local_completion_prefix = path self.updateModel() if self.filterProxyModel.rowCount() == 0: self.usingOriginalModel = False self.filterProxyModel.setSourceModel(QStringListModel([path])) return [path] self.usingOriginalModel = True return []
class CustomQCompleter(QCompleter): """ adapted from: http://stackoverflow.com/a/7767999/2156909 """ def __init__(self, parent=None): super().__init__(parent) self.local_completion_prefix = "" self.source_model = None self.filterProxyModel = QSortFilterProxyModel(self) self.usingOriginalModel = False def setModel(self, strList): self.source_model = QStringListModel(strList) self.filterProxyModel = QSortFilterProxyModel(self) self.filterProxyModel.setSourceModel(self.source_model) super().setModel(self.filterProxyModel) self.usingOriginalModel = True def updateModel(self): if not self.usingOriginalModel: self.filterProxyModel.setSourceModel(self.source_model) pattern = QRegExp(self.local_completion_prefix, Qt.CaseInsensitive, QRegExp.FixedString) self.filterProxyModel.setFilterRegExp(pattern) def splitPath(self, path): self.local_completion_prefix = path self.updateModel() if self.filterProxyModel.rowCount() == 0: self.usingOriginalModel = False self.filterProxyModel.setSourceModel(QStringListModel([path])) return [path] self.usingOriginalModel = True return []
class MinibufferInput(QLineEdit): completion_activated = Signal(QModelIndex) FuzzyMatch = Prompt.FuzzyMatch SimpleMatch = Prompt.SimpleMatch def __init__(self, parent, window): QLineEdit.__init__(self, parent) self._completer_model = None self._popup = Popup(window, self) self.textEdited.connect(self._show_completions) self._popup.installEventFilter(self) self.installEventFilter(self) self._eat_focusout = False self._proxy_model = QSortFilterProxyModel(self) self._proxy_model.setFilterKeyColumn(-1) self._popup.setModel(self._proxy_model) self._popup.activated.connect(self._on_completion_activated) self._popup.selectionModel().currentRowChanged.connect( self._on_row_changed) self._right_italic_text = "" self._mark = False self.configure_completer({}) from ..keyboardhandler import LOCAL_KEYMAP_SETTER LOCAL_KEYMAP_SETTER.register_minibuffer_input(self) def configure_completer(self, opts): self._popup._max_visible_items = opts.get("max-visible-items", 10) self._match = opts.get("match", self.SimpleMatch) self._autocomplete_single = opts.get("autocomplete-single", True) self._autocomplete = opts.get("autocomplete", False) if self._autocomplete: self._autocomplete_single = False self._complete_empty = opts.get("complete-empty", False) def keymap(self): prompt = self.parent()._prompt if prompt and prompt.keymap: return prompt.keymap return KEYMAP def eventFilter(self, obj, event): etype = event.type() if etype == QEvent.FocusOut and obj == self and self._eat_focusout \ and self._popup.isVisible(): # keep the focus on the line edit return True elif etype == QEvent.MouseButtonPress: # if we've clicked in the widget (or its descendant), let it handle # the click pos = obj.mapToGlobal(event.pos()) target = QApplication.widgetAt(pos) if target and (self.isAncestorOf(target) or target == self): if not self._popup.underMouse(): self._popup.hide() target.event(event) return True if not self._popup.underMouse(): self._popup.hide() return True elif etype in (QEvent.KeyPress, QEvent.KeyRelease): # send event to the line edit self._eat_focusout = True self.event(event) self._eat_focusout = False return True return QLineEdit.eventFilter(self, obj, event) def set_completer_model(self, completer_model): self._proxy_model.setSourceModel(completer_model) def completer_model(self): return self._proxy_model.sourceModel() def set_match(self, type): self._match = type if self._popup.isVisible(): self._show_completions(self.text) def _on_row_changed(self, current, old): if self._autocomplete: self.complete(hide_popup=False) def _show_completions(self, txt, force=False): force = force or self._complete_empty if self._match == self.SimpleMatch: pattern = "^" + QRegExp.escape(txt) else: pattern = ".*".join(QRegExp.escape(t) for t in txt.split()) self._proxy_model.setFilterRegExp(QRegExp(pattern, Qt.CaseInsensitive)) if self._proxy_model.rowCount() == 0: self._popup.hide() elif not txt and not force: self._popup.hide() else: self._popup.popup() def show_completions(self): self._show_completions(self.text(), True) def _on_completion_activated(self, index, hide_popup=True): if hide_popup: self._popup.hide() model = index.model() if index.column() != 0: index = model.index(index.row(), 0) self.setText(model.data(index)) self.completion_activated.emit(model.mapToSource(index)) def popup(self): return self._popup def complete(self, hide_popup=True): if not self._popup.isVisible(): return index = self._popup.selectionModel().currentIndex() if index.isValid(): self._on_completion_activated(index, hide_popup=hide_popup) elif self._autocomplete_single and self._proxy_model.rowCount() == 1: self._on_completion_activated(self._proxy_model.index(0, 0), hide_popup=hide_popup) def select_next_completion(self, forward=True): model = self._proxy_model entries = model.rowCount() if entries == 0: return selection = self._popup.selectionModel().currentIndex() if not selection.isValid(): row = 0 if forward else (entries - 1) else: row = selection.row() if forward: row = row + 1 if row >= entries: row = 0 else: row = row - 1 if row < 0: row = (entries - 1) self._popup.selectRow(row) def mark(self): return self._mark def set_mark(self, value=None): if value is None: value = not self._mark self._mark = value return self._mark def reinit(self): self.setText("") self.setEchoMode(self.Normal) self.setValidator(None) self._right_italic_text = "" def set_right_italic_text(self, text): self._right_italic_text = text self.update() def paintEvent(self, event): QLineEdit.paintEvent(self, event) if not self._right_italic_text: return painter = QPainter(self) font = painter.font() font.setItalic(True) painter.setFont(font) painter.setRenderHint(QPainter.Antialiasing, True) painter.drawText(self.rect().adjusted(0, 0, -10, 0), Qt.AlignRight, self._right_italic_text)
class MasternodesWidget(QWidget): """Widget that displays masternodes.""" def __init__(self, manager, parent=None): super(MasternodesWidget, self).__init__(parent) self.manager = manager self.model = MasternodesModel(self.manager) self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(self.model) self.view = QTableView() self.view.setModel(self.proxy_model) for header in [self.view.horizontalHeader(), self.view.verticalHeader()]: header.setHighlightSections(False) header = self.view.horizontalHeader() header.setSectionResizeMode(MasternodesModel.ALIAS, QHeaderView.ResizeToContents) header.setSectionResizeMode(MasternodesModel.VIN, QHeaderView.Stretch) header.setSectionResizeMode(MasternodesModel.COLLATERAL, QHeaderView.ResizeToContents) header.setSectionResizeMode(MasternodesModel.DELEGATE, QHeaderView.ResizeToContents) self.view.verticalHeader().setVisible(False) self.view.setSelectionMode(QAbstractItemView.SingleSelection) self.view.setSelectionBehavior(QAbstractItemView.SelectRows) self.view.setSortingEnabled(True) self.view.sortByColumn(self.model.ALIAS, Qt.AscendingOrder) vbox = QVBoxLayout() vbox.setContentsMargins(0, 0, 0, 0) vbox.addWidget(self.view) self.setLayout(vbox) def select_masternode(self, alias): """Select the row that represents alias.""" self.view.clearSelection() for i in range(self.proxy_model.rowCount()): idx = self.proxy_model.index(i, 0) mn_alias = str(self.proxy_model.data(idx)) if mn_alias == alias: self.view.selectRow(i) break def populate_collateral_key(self, row): """Fill in the collateral key for a masternode based on its collateral output. row refers to the desired row in the proxy model, not the actual model. """ mn = self.masternode_for_row(row) self.manager.populate_masternode_output(mn.alias) # Emit dataChanged for the collateral key. index = self.model.index(row, self.model.COLLATERAL) self.model.dataChanged.emit(index, index) def refresh_items(self): self.model.dataChanged.emit(QModelIndex(), QModelIndex()) def add_masternode(self, masternode, save = True): self.model.add_masternode(masternode, save=save) def remove_masternode(self, alias, save = True): self.model.remove_masternode(alias, save=save) def masternode_for_row(self, row): idx = self.proxy_model.mapToSource(self.proxy_model.index(row, 0)) return self.model.masternode_for_row(idx.row()) def import_masternode_conf_lines(self, conf_lines, pw): return self.model.import_masternode_conf_lines(conf_lines, pw)
class ListViewSearch(QListView): """ An QListView with an implicit and transparent row filtering. """ def __init__(self, *a, preferred_size=None, **ak): super().__init__(*a, **ak) self.__search = QLineEdit(self, placeholderText="筛选...") self.__search.textEdited.connect(self.__setFilterString) # Use an QSortFilterProxyModel for filtering. Note that this is # never set on the view, only its rows insertes/removed signals are # connected to observe an update row hidden state. self.__pmodel = QSortFilterProxyModel( self, filterCaseSensitivity=Qt.CaseInsensitive) self.__pmodel.rowsAboutToBeRemoved.connect( self.__filter_rowsAboutToBeRemoved) self.__pmodel.rowsInserted.connect(self.__filter_rowsInserted) self.__layout() self.preferred_size = preferred_size def setFilterPlaceholderText(self, text: str): self.__search.setPlaceholderText(text) def filterPlaceholderText(self) -> str: return self.__search.placeholderText() def setFilterProxyModel(self, proxy: QSortFilterProxyModel) -> None: """ Set an instance of QSortFilterProxyModel that will be used for filtering the model. The `proxy` must be a filtering proxy only; it MUST not sort the row of the model. The FilterListView takes ownership of the proxy. """ self.__pmodel.rowsAboutToBeRemoved.disconnect( self.__filter_rowsAboutToBeRemoved) self.__pmodel.rowsInserted.disconnect(self.__filter_rowsInserted) self.__pmodel = proxy proxy.setParent(self) self.__pmodel.rowsAboutToBeRemoved.connect( self.__filter_rowsAboutToBeRemoved) self.__pmodel.rowsInserted.connect(self.__filter_rowsInserted) self.__pmodel.setSourceModel(self.model()) self.__filter_reset() def filterProxyModel(self) -> QSortFilterProxyModel: return self.__pmodel def setModel(self, model: QAbstractItemModel) -> None: super().setModel(model) self.__pmodel.setSourceModel(model) self.__filter_reset() def setRootIndex(self, index: QModelIndex) -> None: super().setRootIndex(index) self.__filter_reset() def __filter_reset(self): root = self.rootIndex() pm = self.__pmodel for i in range(self.__pmodel.rowCount(root)): self.setRowHidden(i, not pm.filterAcceptsRow(i, root)) def __setFilterString(self, string: str): self.__pmodel.setFilterFixedString(string) def setFilterString(self, string: str): """Set the filter string.""" self.__search.setText(string) self.__pmodel.setFilterFixedString(string) def filterString(self): """Return the filter string.""" return self.__search.text() def __filter_set(self, rows: Iterable[int], state: bool): for r in rows: self.setRowHidden(r, state) def __filter_rowsAboutToBeRemoved(self, parent: QModelIndex, start: int, end: int) -> None: fmodel = self.__pmodel mrange = QItemSelection(fmodel.index(start, 0, parent), fmodel.index(end, 0, parent)) mranges = fmodel.mapSelectionToSource(mrange) for mrange in mranges: self.__filter_set(range(mrange.top(), mrange.bottom() + 1), True) def __filter_rowsInserted(self, parent: QModelIndex, start: int, end: int) -> None: fmodel = self.__pmodel mrange = QItemSelection(fmodel.index(start, 0, parent), fmodel.index(end, 0, parent)) mranges = fmodel.mapSelectionToSource(mrange) for mrange in mranges: self.__filter_set(range(mrange.top(), mrange.bottom() + 1), False) def resizeEvent(self, event: QResizeEvent) -> None: super().resizeEvent(event) def updateGeometries(self) -> None: super().updateGeometries() self.__layout() def __layout(self): margins = self.viewportMargins() search = self.__search sh = search.sizeHint() size = self.size() margins.setTop(sh.height()) vscroll = self.verticalScrollBar() style = self.style() transient = style.styleHint(QStyle.SH_ScrollBar_Transient, None, vscroll) w = size.width() if vscroll.isVisibleTo(self) and not transient: w = w - vscroll.width() - 1 search.setGeometry(0, 0, w, sh.height()) self.setViewportMargins(margins) def sizeHint(self): return (self.preferred_size if self.preferred_size is not None else super().sizeHint())
class FileChooser(BaseFileChooser): def __init__(self, **kwargs): super(FileChooser, self).__init__(**kwargs) self.options = PropDict() self.edit.textEdited.connect(self._onTextEdited) self.view.activated.connect(self._onActivated) # models self.rootChanger = RootChangerProxy() fsModel = QFileSystemModel(self) self.setModel(fsModel) self.filter = QSortFilterProxyModel() self.filter.setSourceModel(self.rootChanger) self.view.setModel(self.filter) def setModel(self, model): self.baseModel = model self.rootChanger.setSourceModel(self.baseModel) @Slot(str) def setRoot(self, path): self.root = path srcIdx = self.baseModel.setRootPath(path) self.rootChanger.setRootSource(srcIdx) self.view.setRootIndex(QModelIndex()) @Slot(str) def _onTextEdited(self, txt): elems = txt.rsplit('/', 1) if len(elems) == 2: dir, base = elems else: dir, base = '', elems[0] path = os.path.join(self.root, dir) self.rootChanger.setRootSource(self.baseModel.index(path)) self.filter.setFilterRegExp(QRegExp(base, Qt.CaseInsensitive, QRegExp.Wildcard)) if self.options.get('autosuggest'): names = [self.filter.data(self.filter.index(i, 0)).toString() for i in range(self.filter.rowCount(QModelIndex()))] names = [n[len(base):] for n in names] add = commonPrefix(names) cursor = self.edit.cursorPosition() self.edit.setText(self.edit.text()[:cursor] + add) self.edit.setSelection(cursor, len(self.edit.text())) @Slot(QModelIndex) def _onActivated(self, idx): idx = self.filter.mapToSource(idx) idx = self.rootChanger.mapToSource(idx) info = self.baseModel.fileInfo(idx) if info.isDir(): return path = info.absoluteFilePath() self.openFile(path)
class FileChooser(BaseFileChooser): def __init__(self, **kwargs): super(FileChooser, self).__init__(**kwargs) self.options = PropDict() self.edit.textEdited.connect(self._onTextEdited) self.view.activated.connect(self._onActivated) # models self.rootChanger = RootChangerProxy() fsModel = QFileSystemModel(self) self.setModel(fsModel) self.filter = QSortFilterProxyModel() self.filter.setSourceModel(self.rootChanger) self.view.setModel(self.filter) def setModel(self, model): self.baseModel = model self.rootChanger.setSourceModel(self.baseModel) @Slot(str) def setRoot(self, path): self.root = path srcIdx = self.baseModel.setRootPath(path) self.rootChanger.setRootSource(srcIdx) self.view.setRootIndex(QModelIndex()) @Slot(str) def _onTextEdited(self, txt): elems = txt.rsplit('/', 1) if len(elems) == 2: dir, base = elems else: dir, base = '', elems[0] path = os.path.join(self.root, dir) self.rootChanger.setRootSource(self.baseModel.index(path)) self.filter.setFilterRegExp(QRegExp(base, Qt.CaseInsensitive, QRegExp.Wildcard)) if self.options.get('autosuggest'): names = [self.filter.data(self.filter.index(i, 0)).toString() for i in range(self.filter.rowCount(QModelIndex()))] names = [n[len(base):] for n in names] add = commonPrefix(names) cursor = self.edit.cursorPosition() self.edit.setText(self.edit.text()[:cursor] + add) self.edit.setSelection(cursor, len(self.edit.text())) @Slot(QModelIndex) def _onActivated(self, idx): idx = self.filter.mapToSource(idx) idx = self.rootChanger.mapToSource(idx) info = self.baseModel.fileInfo(idx) if info.isDir(): return path = info.absoluteFilePath() self.openFile(path)
class Window(QWidget): def __init__(self, connection): super(Window, self).__init__() self.conn = connection self.proxyModel = MySortFilterProxyModel(self) self.proxyModel.setDynamicSortFilter(True) self.proxyView = QTreeView() set_tree_view(self.proxyView) self.proxyView.setModel(self.proxyModel) self.proxyView.customContextMenuRequested.connect(self.pop_menu) self.filterType = QComboBox() self.filterModule = QComboBox() self.filterClass = QComboBox() self.filterNote = QComboBox() self.infLabel = QLabel() self.link_type = QComboBox() self.resView = QTreeView() set_tree_view(self.resView) self.resModel = QSortFilterProxyModel(self.resView) self.resView.setModel(self.resModel) self.resView.customContextMenuRequested.connect(self.menu_res_view) self.link_box = self.set_layout() self.sort_key = None self.repo = [] self.old_links = [] self.new_links = [] self.query_time = time_run() self.curr_id_db = 0 self.setWindowTitle("Custom Sort/Filter Model") self.resize(900, 750) def set_layout(self): filter_box: QGroupBox = self.set_filter_box() height = 92 filter_box.setMaximumHeight(height) link_box = self.set_link_box() link_box.setMaximumHeight(height) link_box.hide() stack_layout = QVBoxLayout() stack_layout.addWidget(filter_box) stack_layout.addWidget(link_box) proxyLayout = QGridLayout() proxyLayout.addWidget(self.proxyView, 0, 0) proxyLayout.addLayout(stack_layout, 1, 0) proxyLayout.addWidget(self.resView, 2, 0) proxyLayout.setRowStretch(0, 5) proxyLayout.setRowStretch(1, 0) proxyLayout.setRowStretch(2, 3) proxyGroupBox = QGroupBox("Module/Class/Method list") proxyGroupBox.setLayout(proxyLayout) mainLayout = QVBoxLayout() mainLayout.addWidget(proxyGroupBox) self.setLayout(mainLayout) return link_box def save_clicked(self, btn): { "Unload DB": save_init, "Copy all links": copy_to_clipboard }[btn.text()]() def set_filter_box(self): save_btn = QDialogButtonBox(Qt.Vertical) save_btn.addButton("Unload DB", QDialogButtonBox.ActionRole) save_btn.addButton("Copy all links", QDialogButtonBox.ActionRole) save_btn.clicked.connect(self.save_clicked) self.filterNote.addItem("All") self.filterNote.addItem("Not blank") filterTypeLabel = QLabel("&Type Filter") filterTypeLabel.setBuddy(self.filterType) filterModuleLabel = QLabel("&Module Filter") filterModuleLabel.setBuddy(self.filterModule) filterClassLabel = QLabel("&Class Filter") filterClassLabel.setBuddy(self.filterClass) filterNoteLabel = QLabel("&Remark Filter") filterNoteLabel.setBuddy(self.filterNote) filter_box = QGridLayout() filter_box.addWidget(filterTypeLabel, 0, 0) filter_box.addWidget(filterModuleLabel, 0, 1) filter_box.addWidget(filterClassLabel, 0, 2) filter_box.addWidget(filterNoteLabel, 0, 3) filter_box.addWidget(save_btn, 0, 4, 2, 1) filter_box.addWidget(self.filterType, 1, 0) filter_box.addWidget(self.filterModule, 1, 1) filter_box.addWidget(self.filterClass, 1, 2) filter_box.addWidget(self.filterNote, 1, 3) self.set_filters_combo() self.textFilterChanged() self.filterType.currentIndexChanged.connect(self.textFilterChanged) self.filterModule.currentIndexChanged.connect( self.textFilterModuleChanged) self.filterClass.currentIndexChanged.connect(self.textFilterChanged) self.filterNote.currentIndexChanged.connect(self.textFilterChanged) grp_box = QGroupBox() grp_box.setFlat(True) grp_box.setLayout(filter_box) return grp_box def set_filters_combo(self): curs = self.conn.cursor() self.filterType.clear() self.filterType.addItem("All") curs.execute(qsel3) for cc in curs: self.filterType.addItem(memb_type[cc[0]]) self.filterModule.clear() self.filterModule.addItem("All") self.filterModule.addItem("") curs.execute(qsel0) for cc in curs: self.filterModule.addItem(cc[0]) self.filterClass.clear() self.filterClass.addItem("All") curs.execute(qsel1) for cc in curs: self.filterClass.addItem(cc[0]) def textFilterModuleChanged(self): curs = self.conn.cursor() self.filterClass.clear() self.filterClass.addItem("All") if self.filterModule.currentText() == "All": curs.execute(qsel1) else: curs.execute( ("select distinct class from methods2 " "where module = ? order by class;"), (self.filterModule.currentText(), ), ) for cc in curs: self.filterClass.addItem(cc[0]) for cc in curs: self.filterType.addItem(memb_type[cc[0]]) def menu_res_view(self, pos): """ only copy to clipboard """ menu = QMenu(self) menu.addAction("clipboard") # menu.addAction("refresh") action = menu.exec_(self.resView.mapToGlobal(pos)) if action: self._to_clipboard() def _to_clipboard(self): rr = [] for rep in self.repo: pp = [str(x) for x in rep] rr.append("\t".join(pp)) QApplication.clipboard().setText("\n".join(rr)) def pop_menu(self, pos): idx = self.proxyView.indexAt(pos) menu = QMenu(self) if idx.isValid(): menu.addAction("First level only") menu.addSeparator() menu.addAction("sort by level") menu.addAction("sort by module") menu.addSeparator() menu.addAction("append row") if idx.isValid(): menu.addAction("delete rows") menu.addAction("edit links") menu.addSeparator() menu.addAction("not called") menu.addSeparator() menu.addAction("complexity") menu.addSeparator() menu.addAction("refresh") menu.addAction("reload DB") action = menu.exec_(self.proxyView.mapToGlobal(pos)) if action: self.menu_action(action.text()) def setSourceModel(self, model: QStandardItemModel): self.proxyModel.setSourceModel(model) set_columns_width(self.proxyView) set_headers(self.proxyModel, main_headers) def textFilterChanged(self): self.proxyModel.filter_changed( self.filterType.currentText(), self.filterModule.currentText(), self.filterClass.currentText(), self.filterNote.currentText(), ) def menu_action(self, act: str): { "First level only": self.first_level_only, "sort by level": self.sort_by_level, "sort by module": self.sort_by_module, "append row": self.append_row, "refresh": self.refresh, "not called": self.is_not_called, "complexity": self.recalc_complexity, "reload DB": self.reload_data, "edit links": self.edit_links, "delete rows": self.delete_selected_rows, }[act]() def recalc_complexity(self): """ add radon cyclomatic complexity repor data """ mm = self.filterModule.currentText() module = "" if mm == "All" else mm cc_list = cc_report(module) for row in cc_list: self.update_cc(row) mark_deleted_methods(cc_list, module) def update_cc(self, row: Iterable): """ @param row: CC, length, type(C/F/M), module, class, method """ sql_sel = ("select id from methods2 where " "type = ? and module = ? and class = ? and method = ?") sql_upd = "update methods2 set cc = ?, length = ? " "where id = ?" sql_ins = ("insert into methods2 (CC, length, type, module, " "Class, method, remark) values(?,?,?,?,?,?,?);") rr = (*row, ) qq = self.conn.cursor() id = qq.execute(sql_sel, rr[2:]).fetchone() if id: qq.execute(sql_upd, (*rr[:2], id[0])) else: tt = datetime.now().strftime("%Y-%m-%d %H:%M") qq.execute(sql_ins, (*rr, tt)) self.conn.commit() def is_not_called(self): qq = self.conn.cursor() qq.execute(not_called) self.set_res_model(qq, call_headers, False) set_columns_width(self.resView, proportion=(2, 2, 5, 7, 7, 2, 3, 5)) set_headers(self.resModel, call_headers) def reload_data(self): sql1 = ( "delete from methods2;", "insert into methods2 (" "ID, type, module, class, method, CC, length, remark) " "values (?, ?, ?, ?, ?, ?, ?, ?);", ) input_file = prj_path / input_meth load_table(input_file, sql1) sql2 = ( "delete from one_link;", "insert into one_link (id, call_id) values (?, ?);", ) input_file = prj_path / input_link load_table(input_file, sql2) curs = conn.cursor() curs.execute("delete from links;") conn.commit() curs.execute(all_levels_link) conn.commit() self.refresh() def refresh(self): model = QStandardItemModel(0, len(main_headers.split(",")), self.proxyView) qq = conn.cursor() qq.execute(qsel2) vv = ((x[0], memb_type[x[1]], *x[2:-2], x[-2].rjust(4), x[-1]) for x in qq) fill_in_model(model, vv) self.setSourceModel(model) def clear_report_view(self): self.repo.clear() model = QStandardItemModel(0, len(rep_headers.split(",")), self.resView) self.resModel.setSourceModel(model) set_columns_width(self.resView, proportion=(3, 2, 2, 2, 7, 7, 7, 2, 2, 1)) set_headers(self.resModel, rep_headers) self.query_time = time_run() def append_row(self): crs = conn.cursor() items = ( memb_key[self.proxyModel.type_filter], self.proxyModel.module_filter, self.proxyModel.class_filter, "", "", "", "", self.query_time[0], ) crs.execute(ins0, items) idn = crs.lastrowid conn.commit() param = ( self.proxyModel.rowCount(), (self.proxyModel.type_filter, *items[1:]), idn, ) add_row(self.proxyModel, param) def delete_selected_rows(self): idx_list = self.proxyView.selectionModel().selectedRows() idx_list.reverse() for p_idx in idx_list: if p_idx.isValid(): row = p_idx.row() self.delete_from_db(p_idx) self.proxyModel.removeRows(row, 1) def delete_from_db(self, index: QModelIndex): id_db = self.proxyModel.get_data(index, Qt.UserRole) conn.execute("delete from methods2 where id=?;", (id_db, )) conn.commit() def edit_links(self): index = self.proxyView.currentIndex() ss = self.proxyModel.get_data(index) id_db = self.proxyModel.get_data(index, Qt.UserRole) self.infLabel.setText("{:04d}: {}".format(id_db, ".".join(ss[1:4]))) self.link_box.show() qq = conn.cursor() qq.execute(sql_links.format(id_db, id_db)) self.set_res_model(qq, link_headers, True) self.repo.append((id_db, 'Sel', *ss[:4])) set_columns_width(self.resView, proportion=(3, 2, 8, 8, 8)) set_headers(self.resModel, link_headers) self.old_links = qq.execute(sql_id2.format(id_db, id_db)).fetchall() self.new_links = self.old_links[:] self.curr_id_db = id_db def set_res_model(self, qq: Iterable, headers: str, user_data: bool): self.repo.clear() for row in qq: self.repo.append(row) model = QStandardItemModel(0, len(headers.split(",")), self.resView) fill_in_model(model, self.repo, user_data) self.resModel.setSourceModel(model) def set_link_box(self): self.link_type.addItem("What") self.link_type.addItem("From") f_type = QLabel("Link &type:") f_type.setBuddy(self.link_type) ok_btn = QDialogButtonBox() ok_btn.setStandardButtons(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) ok_btn.addButton("+", QDialogButtonBox.ActionRole) ok_btn.addButton("-", QDialogButtonBox.ActionRole) ok_btn.clicked.connect(self.btn_clicked) l_box = QGridLayout() l_box.addWidget(self.infLabel, 0, 0) l_box.addWidget(f_type, 1, 0) l_box.addWidget(self.link_type, 1, 1) l_box.addWidget(ok_btn, 1, 2) l_box.setRowStretch(0, 1) l_box.setRowStretch(1, 0) l_box.setRowStretch(2, 1) grp = QGroupBox() grp.setFlat(True) grp.setLayout(l_box) return grp def btn_clicked(self, btn): { "OK": self.ok_clicked, "Cancel": self.cancel_cliked, "+": self.plus_clicked, "-": self.minus_clicked, }[btn.text()]() def ok_clicked(self): s_new = set(self.new_links) s_old = set(self.old_links) added = s_new - s_old removed = s_old - s_new if removed: for link in removed: conn.execute("delete from one_link where id=? and call_id=?;", link) if added: for link in added: conn.execute( "insert into one_link (id, call_id) values (?, ?);", link) conn.commit() self.resModel.sourceModel().clear() self.link_box.hide() if removed or added: recreate_links() def cancel_cliked(self): self.resModel.sourceModel().clear() self.link_box.hide() def plus_clicked(self): """ add link to resModel """ to_insert = self.collect_links_with_selected() row_no = self.resModel.rowCount() for row in to_insert: add_row(self.resModel, (row_no, row[1:], row[0])) row_no += 1 def collect_links_with_selected(self): """ creation links according to selected rows in proxyView and direction of link selected in self.link_type: self.curr_id_db - DB id of edited method (object) link is a pair of ids (what called, called from) """ stat = self.link_type.currentText() idx_sel = self.proxyView.selectedIndexes() idx_col0 = [ix for ix in idx_sel if ix.column() == 0] to_insert = [] for idx in idx_col0: id = self.proxyModel.get_data(idx, Qt.UserRole) link = (id, self.curr_id_db) if stat == "What" else (self.curr_id_db, id) if link in self.new_links or link[::-1] in self.new_links: continue self.new_links.append(link) row = self.proxyModel.get_data(idx)[:-1] to_insert.append([id, stat] + row) return to_insert def minus_clicked(self): idx_sel = self.resView.selectionModel().selectedRows() idx_sel.reverse() for idx in idx_sel: self.remove_in_new_links(idx) self.remove_in_model(idx) def remove_in_new_links(self, index: QModelIndex): link_type = self.resModel.data(index) id_db = self.resModel.data(index, Qt.UserRole) link = ((id_db, self.curr_id_db) if link_type == "What" else (self.curr_id_db, id_db)) self.new_links.remove(link) def remove_in_model(self, index): row = index.row() self.resModel.removeRows(row, 1) def get_selected_methods(self): """ Returns lists of rows selected in the proxyView: @return: list of selected methods """ indexes = self.proxyView.selectionModel().selectedRows() methods = [] for idx in indexes: methods.append(self.proxyModel.get_data(idx)) return methods def first_level_only(self): """ select method to create link-report depending on number of selected methods @return: None """ self.clear_report_view() self.sort_key = sort_keys["by module"] ids = self.proxyView.selectionModel().selectedRows() opt = len(ids) if len(ids) < 3 else "more than 2" { 1: self.selected_only_one, 2: self.selected_exactly_two, "more than 2": self.selected_more_than_two }[opt](1) def prep_sql(self, sql: str, lvl: int = 0) -> str: mod = self.filterModule.currentText() cls = self.filterClass.currentText() return (sql + ("" if mod == "All" else where_mod.format(mod)) + ("" if cls == "All" else where_cls.format(cls)) + (and_level if lvl else "") + group_by) def selected_only_one(self, lvl): pre = (self.query_time[1], "Sel", "") names = self.get_selected_methods() self.sorted_report(self.repo, (pre, names, "")) lst = self.first_1_part(what_call_1, lvl) pre = (self.query_time[1], "What", "") self.sorted_report(self.repo, (pre, lst, "")) lst = self.first_1_part(called_from_1, lvl) pre = (self.query_time[1], "From", "") self.sorted_report(self.repo, (pre, lst, "")) fill_in_model(self.resModel.sourceModel(), self.repo, user_data=False) def first_1_part(self, sql: str, lvl: int): p_sql = self.prep_sql(sql, lvl) ids = self.get_db_ids() lst = self.exec_sql_b(p_sql, ids) return [(*map(str, x), ) for x in lst] def get_db_ids(self): ids = [] indexes = self.proxyView.selectionModel().selectedRows() for idx in indexes: ids.append(self.proxyModel.get_data(idx, Qt.UserRole)) return ids def selected_exactly_two(self, lvl): pre = (self.query_time[1], "Sel") names = self.get_selected_methods() n_names = [("A", *names[0]), ("B", *names[1])] self.sorted_report(self.repo, (pre, n_names, "")) self.report_four("What", lvl) self.report_four("From", lvl) fill_in_model(self.resModel.sourceModel(), self.repo, user_data=False) def report_four(self, what, lvl): sql = {"What": what_call_1, "From": called_from_1}[what] p_sql = self.prep_sql(sql, lvl) ids = self.get_db_ids() lst_a = self.first_2_part((ids[0], ), sql) lst_b = self.first_2_part((ids[1], ), sql) self.sorted_report(self.repo, ( (self.query_time[1], what, "A | B"), list(set(lst_a) | set(lst_b)), "", )) self.sorted_report(self.repo, ( (self.query_time[1], what, "A - B"), list(set(lst_a) - set(lst_b)), "", )) self.sorted_report(self.repo, ( (self.query_time[1], what, "B - A"), list(set(lst_b) - set(lst_a)), "", )) self.sorted_report(self.repo, ( (self.query_time[1], what, "A & B"), list(set(lst_a) & set(lst_b)), "", )) def first_2_part(self, ids: Iterable, sql: str) -> list: lst = self.exec_sql_b(sql, ids) return [(*map(str, x), ) for x in lst] def selected_more_than_two(self, lvl): pre = (self.query_time[1], "Sel", "") names = self.get_selected_methods() self.sorted_report(self.repo, (pre, names, "")) self.report_23("What", lvl) self.report_23("From", lvl) fill_in_model(self.resModel.sourceModel(), self.repo, user_data=False) def report_23(self, param, lvl): sql = {"What": what_id, "From": from_id}[param] ids = self.get_db_ids() links = self.exec_sql_2(ids, lvl, sql) rep_prep = pre_report(links) self.methods_by_id_list(three_or_more, rep_prep[0:3:2], param, "ALL") self.methods_by_id_list(three_or_more, rep_prep[1:], param, "ANY") def exec_sql_2(self, ids, lvl, sql) -> list: """ @param: ids - list of id of selected rows @param: lvl - level of call: all or only first @param: sql - select methods by type of link: "call What"/"called From" @return: list of tuples (method_id, level of call) """ res = [] curs = self.conn.cursor() loc_sql = sql.format("and level=1" if lvl else "") for id_ in ids: w_id = curs.execute(loc_sql, (id_, )) res.append(dict(w_id)) return res def methods_by_id_list(self, sql: str, ids: list, what: str, all_any: str): if ids: cc = self.exec_sql_f(sql, (",".join((map(str, ids[0]))), )) pre = (self.query_time[1], what, all_any) vv = insert_levels(cc, ids[1]) self.sorted_report(self.repo, (pre, vv, "")) def sort_by_level(self): """ Show lists of methods sorted by level @param ids: indexes of selected methods @param names: selected methods as (module, class, method) list @return: None """ self.clear_report_view() self.sort_key = sort_keys["by level"] self.sel_count_handle() def sort_by_module(self): """ Show lists of methods sorted by module name @param ids: indexes of selected methods @param names: selected methods as (module, class, method) list @return: None """ self.clear_report_view() self.sort_key = sort_keys["by module"] self.sel_count_handle() def sel_count_handle(self): """ This method does the same as the "first_level_only" method @return: None """ ids = self.proxyView.selectionModel().selectedRows() opt = len(ids) if len(ids) < 3 else "more than 2" { 1: self.selected_only_one, 2: self.selected_exactly_two, "more than 2": self.selected_more_than_two }[opt](0) def exec_sql_b(self, sql: str, sql_par: tuple): """ exesute SQL - bind parameters with '?' @param sql: @param sql_par: @return: list of lists of strings """ curs = self.conn.cursor() cc = curs.execute(sql, sql_par) return [(*map(str, x), ) for x in cc] def exec_sql_f(self, sql: str, sql_par: tuple): """ exesute SQL - insert parameters into SQL with str.format method @param sql: @param sql_par: @return: list of lists of strings """ curs = self.conn.cursor() cc = curs.execute(sql.format(*sql_par)) return [(*map(str, x), ) for x in cc] def sorted_report(self, report: list, rep_data: tuple): pre, lst, post = rep_data lst.sort(key=self.sort_key) for ll in lst: report.append((*pre, *ll, *post))