Beispiel #1
0
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 []
Beispiel #3
0
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 []
Beispiel #4
0
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)
Beispiel #6
0
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())
Beispiel #7
0
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)
Beispiel #8
0
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)
Beispiel #9
0
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))