Esempio n. 1
0
class Demo(QWidget):
    def __init__(self):
        super(Demo, self).__init__()
        self.resize(600, 300)
        self.model = QDirModel(self)  # 1
        self.model.setReadOnly(False)
        self.model.setSorting(QDir.Name | QDir.IgnoreCase)

        self.tree = QTreeView(self)  # 2
        self.tree.setModel(self.model)
        self.tree.clicked.connect(self.show_info)
        self.index = self.model.index(QDir.currentPath())
        self.tree.expand(self.index)
        self.tree.scrollTo(self.index)

        self.info_label = QLabel(self)  # 3

        self.v_layout = QVBoxLayout()
        self.v_layout.addWidget(self.tree)
        self.v_layout.addWidget(self.info_label)
        self.setLayout(self.v_layout)

    def show_info(self):  # 4
        index = self.tree.currentIndex()
        file_name = self.model.fileName(index)
        file_path = self.model.filePath(index)
        file_info = 'File Name: {}\nFile Path: {}'.format(file_name, file_path)
        self.info_label.setText(file_info)
Esempio n. 2
0
class QTreeSelection(QToolButton):
    currentIndexChanged = pyqtSignal(QModelIndex, QModelIndex)
    currentDataChanged = pyqtSignal(object)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setPopupMode(QToolButton.MenuButtonPopup)
        self.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)

        self.tree = QTreeView(self)
        self.tree.setMinimumWidth(640)
        self.tree.setSelectionMode(QTreeView.SingleSelection)
        self.tree.setSelectionBehavior(QTreeView.SelectRows)

        act = QWidgetAction(self)
        act.setDefaultWidget(self.tree)

        self.menu = QMenu(self)
        self.menu.addAction(act)
        self.setMenu(self.menu)
        self.clicked.connect(self.showMenu)

    def _currentIndexChanged(self, newindex, oldindex):
        self.menu.close()
        selected = newindex.sibling(newindex.row(), 0)

        display = selected.data(Qt.DisplayRole)
        icon = selected.data(Qt.DecorationRole)

        self.setText(display or "")
        self.setIcon(icon or QIcon())
        self.currentIndexChanged.emit(newindex, oldindex)
        self.currentDataChanged.emit(newindex.data(Qt.UserRole))

    def currentIndex(self):
        return self.tree.currentIndex()

    def currentData(self):
        return self.currentIndex().data(Qt.UserRole)

    def setCurrentIndex(self, index):
        return self.tree.setCurrentIndex(index)

    def model(self):
        return self.tree.model()

    def setModel(self, model):
        self.tree.setModel(model)
        self.tree.selectionModel().currentChanged.connect(
            self._currentIndexChanged)

    def keyPressEvent(self, event):
        if (event.key() in (Qt.Key_Up, Qt.Key_Down)
                and not (event.modifiers()
                         & (Qt.ShiftModifier | Qt.AltModifier
                            | Qt.ControlModifier))):
            self.tree.keyPressEvent(event)

        return super().keyPressEvent(event)
Esempio n. 3
0
class ItemsView(QDockWidget):
    def __init__(self, title, parent, items):
        super().__init__(title, parent)
        self.setFloating(False)
        self.set_items(items)

    def set_items(self, items):
        self.items = items
        self.itemsTree = QTreeView()
        self.itemsTree.setHeaderHidden(True)

        treeModel = QStandardItemModel()
        rootNode = treeModel.invisibleRootItem()

        groups = {}
        for item in items:
            category = item['data']['category']
            node = self.__get_node(item['name'])
            if (category not in groups):
                groups[category] = self.__get_node(category)
            groups[category].appendRow(node)

        for key in groups:
            rootNode.appendRow(groups[key])

        self.itemsTree.setModel(treeModel)
        self.itemsTree.expandAll()
        self.setWidget(self.itemsTree)

    def set_on_item_selected(self, method):
        self.itemsTree.selectionModel().selectionChanged.connect(
            lambda: method(self.__get_selected_item()))

    def __get_selected_item(self):
        current = self.itemsTree.currentIndex()
        found_item = None
        if (current.parent() != None):
            for item in self.items:
                if (item['name'] == current.data()):
                    found_item = item
        return found_item

    def __get_node(self, title):
        node = QStandardItem()
        node.setEditable(False)
        node.setText(title)
        return node
Esempio n. 4
0
class SearchDir(QWidget):
    def __init__(self):
        super().__init__()
        self.title = ''
        self.initUI()

    def initUI(self):
        self.model = QFileSystemModel()
        self.model.setRootPath('')
        self.tree = QTreeView()
        self.tree.setModel(self.model)

        self.tree.setAnimated(False)
        self.tree.setIndentation(20)
        self.tree.setSortingEnabled(True)

        windowLayout = QVBoxLayout()
        windowLayout.addWidget(self.tree)
        self.setLayout(windowLayout)
        self.show()

    def getPath(self):
        index = self.tree.currentIndex()
        return self.model.filePath(index)
Esempio n. 5
0
class Widget(QWidget):
    def __init__(self, panel):
        super(Widget, self).__init__(panel)
        
        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.setSpacing(0)
        
        self.searchEntry = SearchLineEdit()
        self.treeView = QTreeView(contextMenuPolicy=Qt.CustomContextMenu)
        self.textView = QTextBrowser()
        
        applyButton = QToolButton(autoRaise=True)
        editButton = QToolButton(autoRaise=True)
        addButton = QToolButton(autoRaise=True)
        self.menuButton = QPushButton(flat=True)
        menu = QMenu(self.menuButton)
        self.menuButton.setMenu(menu)
        
        splitter = QSplitter(Qt.Vertical)
        top = QHBoxLayout()
        layout.addLayout(top)
        splitter.addWidget(self.treeView)
        splitter.addWidget(self.textView)
        layout.addWidget(splitter)
        splitter.setSizes([200, 100])
        splitter.setCollapsible(0, False)
        
        top.addWidget(self.searchEntry)
        top.addWidget(applyButton)
        top.addSpacing(10)
        top.addWidget(addButton)
        top.addWidget(editButton)
        top.addWidget(self.menuButton)
        
        # action generator for actions added to search entry
        def act(slot, icon=None):
            a = QAction(self, triggered=slot)
            self.addAction(a)
            a.setShortcutContext(Qt.WidgetWithChildrenShortcut)
            icon and a.setIcon(icons.get(icon))
            return a
        
        # hide if ESC pressed in lineedit
        a = act(self.slotEscapePressed)
        a.setShortcut(QKeySequence(Qt.Key_Escape))
        
        # import action
        a = self.importAction = act(self.slotImport, 'document-open')
        menu.addAction(a)
        
        # export action
        a = self.exportAction = act(self.slotExport, 'document-save-as')
        menu.addAction(a)
        
        # apply button
        a = self.applyAction = act(self.slotApply, 'edit-paste')
        applyButton.setDefaultAction(a)
        menu.addSeparator()
        menu.addAction(a)
        
        # add button
        a = self.addAction_ = act(self.slotAdd, 'list-add')
        a.setShortcut(QKeySequence(Qt.Key_Insert))
        addButton.setDefaultAction(a)
        menu.addSeparator()
        menu.addAction(a)
        
        # edit button
        a = self.editAction = act(self.slotEdit, 'document-edit')
        a.setShortcut(QKeySequence(Qt.Key_F2))
        editButton.setDefaultAction(a)
        menu.addAction(a)
        
        # set shortcut action
        a = self.shortcutAction = act(self.slotShortcut, 'preferences-desktop-keyboard-shortcuts')
        menu.addAction(a)
        
        # delete action
        a = self.deleteAction = act(self.slotDelete, 'list-remove')
        a.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Delete))
        menu.addAction(a)
        
        # restore action
        a = self.restoreAction = act(self.slotRestore)
        menu.addSeparator()
        menu.addAction(a)
        
        # help button
        a = self.helpAction = act(self.slotHelp, 'help-contents')
        menu.addSeparator()
        menu.addAction(a)
        
        self.treeView.setSelectionBehavior(QTreeView.SelectRows)
        self.treeView.setSelectionMode(QTreeView.ExtendedSelection)
        self.treeView.setRootIsDecorated(False)
        self.treeView.setAllColumnsShowFocus(True)
        self.treeView.setModel(model.model())
        self.treeView.setCurrentIndex(QModelIndex())
        
        # signals
        self.searchEntry.returnPressed.connect(self.slotReturnPressed)
        self.searchEntry.textChanged.connect(self.updateFilter)
        self.treeView.doubleClicked.connect(self.slotDoubleClicked)
        self.treeView.customContextMenuRequested.connect(self.showContextMenu)
        self.treeView.selectionModel().currentChanged.connect(self.updateText)
        self.treeView.model().dataChanged.connect(self.updateFilter)
        
        # highlight text
        self.highlighter = highlight.Highlighter(self.textView.document())
        
        # complete on snippet variables
        self.searchEntry.setCompleter(QCompleter([
            ':icon', ':indent', ':menu', ':name', ':python', ':selection',
            ':set', ':symbol', ':template', ':template-run'], self.searchEntry))
        self.readSettings()
        app.settingsChanged.connect(self.readSettings)
        app.translateUI(self)
        self.updateColumnSizes()
        self.setAcceptDrops(True)

    def dropEvent(self, ev):
        if not ev.source() and ev.mimeData().hasUrls():
            filename = ev.mimeData().urls()[0].toLocalFile()
            if filename:
                ev.accept()
                from . import import_export
                import_export.load(filename, self)
        
    def dragEnterEvent(self, ev):
        if not ev.source() and ev.mimeData().hasUrls():
            ev.accept()
        
    def translateUI(self):
        try:
            self.searchEntry.setPlaceholderText(_("Search..."))
        except AttributeError:
            pass # not in Qt 4.6
        shortcut = lambda a: a.shortcut().toString(QKeySequence.NativeText)
        self.menuButton.setText(_("&Menu"))
        self.addAction_.setText(_("&Add..."))
        self.addAction_.setToolTip(
            _("Add a new snippet. ({key})").format(key=shortcut(self.addAction_)))
        self.editAction.setText(_("&Edit..."))
        self.editAction.setToolTip(
            _("Edit the current snippet. ({key})").format(key=shortcut(self.editAction)))
        self.shortcutAction.setText(_("Configure Keyboard &Shortcut..."))
        self.deleteAction.setText(_("&Remove"))
        self.deleteAction.setToolTip(_("Remove the selected snippets."))
        self.applyAction.setText(_("A&pply"))
        self.applyAction.setToolTip(_("Apply the current snippet."))
        self.importAction.setText(_("&Import..."))
        self.importAction.setToolTip(_("Import snippets from a file."))
        self.exportAction.setText(_("E&xport..."))
        self.exportAction.setToolTip(_("Export snippets to a file."))
        self.restoreAction.setText(_("Restore &Built-in Snippets..."))
        self.restoreAction.setToolTip(
            _("Restore deleted or changed built-in snippets."))
        self.helpAction.setText(_("&Help"))
        self.searchEntry.setToolTip(_(
            "Enter text to search in the snippets list.\n"
            "See \"What's This\" for more information."))
        self.searchEntry.setWhatsThis(''.join(map("<p>{0}</p>\n".format, (
            _("Enter text to search in the snippets list, and "
              "press Enter to apply the currently selected snippet."),
            _("If the search text fully matches the value of the '{name}' variable "
              "of a snippet, that snippet is selected.").format(name="name"),
            _("If the search text starts with a colon ':', the rest of the "
              "search text filters snippets that define the given variable. "
              "After a space a value can also be entered, snippets will then "
              "match if the value of the given variable contains the text after "
              "the space."),
            _("E.g. entering {menu} will show all snippets that are displayed "
              "in the insert menu.").format(menu="<code>:menu</code>"),
            ))))
    
    def sizeHint(self):
        return self.parent().mainwindow().size() / 4
        
    def readSettings(self):
        data = textformats.formatData('editor')
        self.textView.setFont(data.font)
        self.textView.setPalette(data.palette())

    def showContextMenu(self, pos):
        """Called when the user right-clicks the tree view."""
        self.menuButton.menu().popup(self.treeView.viewport().mapToGlobal(pos))
    
    def slotReturnPressed(self):
        """Called when the user presses Return in the search entry. Applies current snippet."""
        name = self.currentSnippet()
        if name:
            view = self.parent().mainwindow().currentView()
            insert.insert(name, view)
            self.parent().hide() # make configurable?
            view.setFocus()

    def slotEscapePressed(self):
        """Called when the user presses ESC in the search entry. Hides the panel."""
        self.parent().hide()
        self.parent().mainwindow().currentView().setFocus()
    
    def slotDoubleClicked(self, index):
        name = self.treeView.model().name(index)
        view = self.parent().mainwindow().currentView()
        insert.insert(name, view)
        
    def slotAdd(self):
        """Called when the user wants to add a new snippet."""
        edit.Edit(self, None)
        
    def slotEdit(self):
        """Called when the user wants to edit a snippet."""
        name = self.currentSnippet()
        if name:
            edit.Edit(self, name)
        
    def slotShortcut(self):
        """Called when the user selects the Configure Shortcut action."""
        from widgets import shortcuteditdialog
        name = self.currentSnippet()
        if name:
            collection = self.parent().snippetActions
            action = actions.action(name, None, collection)
            default = collection.defaults().get(name)
            mgr = actioncollectionmanager.manager(self.parent().mainwindow())
            cb = mgr.findShortcutConflict
            dlg = shortcuteditdialog.ShortcutEditDialog(self, cb, (collection, name))
            
            if dlg.editAction(action, default):
                mgr.removeShortcuts(action.shortcuts())
                collection.setShortcuts(name, action.shortcuts())
                self.treeView.update()
            
    def slotDelete(self):
        """Called when the user wants to delete the selected rows."""
        rows = sorted(set(i.row() for i in self.treeView.selectedIndexes()), reverse=True)
        if rows:
            for row in rows:
                name = self.treeView.model().names()[row]
                self.parent().snippetActions.setShortcuts(name, [])
                self.treeView.model().removeRow(row)
            self.updateFilter()
    
    def slotApply(self):
        """Called when the user clicks the apply button. Applies current snippet."""
        name = self.currentSnippet()
        if name:
            view = self.parent().mainwindow().currentView()
            insert.insert(name, view)
    
    def slotImport(self):
        """Called when the user activates the import action."""
        filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files"))
        caption = app.caption(_("dialog title", "Import Snippets"))
        filename = None
        filename = QFileDialog.getOpenFileName(self, caption, filename, filetypes)[0]
        if filename:
            from . import import_export
            import_export.load(filename, self)
        
    def slotExport(self):
        """Called when the user activates the export action."""
        allrows = [row for row in range(model.model().rowCount())
                       if not self.treeView.isRowHidden(row, QModelIndex())]
        selectedrows = [i.row() for i in self.treeView.selectedIndexes()
                                if i.column() == 0 and i.row() in allrows]
        names = self.treeView.model().names()
        names = [names[row] for row in selectedrows or allrows]
        
        filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files"))
        n = len(names)
        caption = app.caption(_("dialog title",
            "Export {num} Snippet", "Export {num} Snippets", n).format(num=n))
        filename = QFileDialog.getSaveFileName(self, caption, None, filetypes)[0]
        if filename:
            from . import import_export
            try:
                import_export.save(names, filename)
            except (IOError, OSError) as e:
                QMessageBox.critical(self, _("Error"), _(
                    "Can't write to destination:\n\n{url}\n\n{error}").format(
                    url=filename, error=e.strerror))
        
    def slotRestore(self):
        """Called when the user activates the Restore action."""
        from . import restore
        dlg = restore.RestoreDialog(self)
        dlg.setWindowModality(Qt.WindowModal)
        dlg.populate()
        dlg.show()
        dlg.finished.connect(dlg.deleteLater)
        
    def slotHelp(self):
        """Called when the user clicks the small help button."""
        userguide.show("snippets")
        
    def currentSnippet(self):
        """Returns the name of the current snippet if it is visible."""
        row = self.treeView.currentIndex().row()
        if row != -1 and not self.treeView.isRowHidden(row, QModelIndex()):
            return self.treeView.model().names()[row]

    def updateFilter(self):
        """Called when the text in the entry changes, updates search results."""
        text = self.searchEntry.text()
        ltext = text.lower()
        filterVars = text.startswith(':')
        if filterVars:
            try:
                fvar, fval = text[1:].split(None, 1)
                fhide = lambda v: v.get(fvar) in (True, None) or fval not in v.get(fvar)
            except ValueError:
                fvar = text[1:].strip()
                fhide = lambda v: not v.get(fvar)
        for row in range(self.treeView.model().rowCount()):
            name = self.treeView.model().names()[row]
            nameid = snippets.get(name).variables.get('name', '')
            if filterVars:
                hide = fhide(snippets.get(name).variables)
            elif nameid == text:
                i = self.treeView.model().createIndex(row, 0)
                self.treeView.selectionModel().setCurrentIndex(i, QItemSelectionModel.SelectCurrent | QItemSelectionModel.Rows)
                hide = False
            elif nameid.lower().startswith(ltext):
                hide = False
            elif ltext in snippets.title(name).lower():
                hide = False
            else:
                hide = True
            self.treeView.setRowHidden(row, QModelIndex(), hide)
        self.updateText()
            
    def updateText(self):
        """Called when the current snippet changes."""
        name = self.currentSnippet()
        self.textView.clear()
        if name:
            s = snippets.get(name)
            self.highlighter.setPython('python' in s.variables)
            self.textView.setPlainText(s.text)
        
    def updateColumnSizes(self):
        self.treeView.resizeColumnToContents(0)
        self.treeView.resizeColumnToContents(1)
Esempio n. 6
0
class Win(QMainWindow):
	def __init__(self, options):
		super(Win, self).__init__()

		self.db = dbtag.Db()
		self.db.open(options.db)
		self.db.create_tables()
		self.rootPath = options.filespath

		self._init_widgets()
		self._init_more()

	def _init_widgets(self):
		bigsplitter = QSplitter(Qt.Horizontal, self)
		self.setCentralWidget(bigsplitter)

		leftsplitter = QSplitter(Qt.Vertical, self)

		self.tabWidget = QTabWidget(self)

		self.tagChooser = TagChooser(self.db)
		self.dirChooser = QTreeView(self)

		self.tabWidget.addTab(self.dirChooser, 'Dir')
		self.tabWidget.addTab(self.tagChooser, 'Tags')

		self.tagEditor = TagEditor(self.db)
		self.imageList = ImageList()

		leftsplitter.addWidget(self.tabWidget)
		leftsplitter.addWidget(self.tagEditor)

		bigsplitter.addWidget(leftsplitter)
		bigsplitter.addWidget(self.imageList)

		self.viewer = None

	def _init_more(self):
		self.setWindowTitle('Tags4')

		self.dirModel = QFileSystemModel()
		self.dirModel.setFilter(QDir.AllDirs | QDir.Drives | QDir.Hidden | QDir.NoDotAndDotDot)
		qidx = self.dirModel.setRootPath(self.rootPath)
		self.dirChooser.setModel(self.dirModel)
		self.dirChooser.setRootIndex(qidx)

		self.dirChooser.clicked.connect(self.browseSelectedDir)
		self.imageList.itemSelectionChanged.connect(self._editTagsItems)
		self.imageList.itemDoubleClicked.connect(self._spawnViewerItem)
		self.imageList.setSelectionMode(QAbstractItemView.ExtendedSelection)

		self.tabWidget.currentChanged.connect(self._tabSelected)
		self.tagChooser.changed.connect(self.browseSelectedTags)

	def editTags(self, path):
		self.tagEditor.setFile(path)

	def editTagsItems(self, paths):
		self.tagEditor.setFiles(paths)

	def spawnViewer(self, files, currentFile):
		viewer = ImageViewer(self.db, parent=self)
		viewer.spawn(files, currentFile)

	@Slot()
	def _editTagsItems(self):
		self.editTagsItems([qitem.getPath() for qitem in self.imageList.selectedItems()])

	@Slot(QListWidgetItem)
	def _spawnViewerItem(self, qitem):
		self.spawnViewer(self.imageList.getFiles(), qitem.getPath())

	@Slot()
	def browseSelectedDir(self):
		path = self.dirModel.filePath(self.dirChooser.currentIndex())
		if not path:
			return
		files = [os.path.join(path, f) for f in os.listdir(path)]
		files = [f for f in files if os.path.isfile(f)]
		files.sort()
		self.imageList.setFiles(files)

	@Slot()
	def browseSelectedTags(self):
		self.imageList.setFiles(self.tagChooser.matchingFiles())

	@Slot(int)
	def _tabSelected(self, idx):
		if idx == 0:
			self.browseSelectedDir()
		else:
			self.browseSelectedTags()

	def browsePath(self, path):
		self.imageList.setFiles(os.path.join(path, f) for f in os.listdir(path))
Esempio n. 7
0
class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.setMinimumSize(QSize(480, 80))
        self.setWindowTitle("PyQtSample")
        central_widget = QWidget(self)
        self.setCentralWidget(central_widget)

        hbox_layout = QHBoxLayout(self)
        central_widget.setLayout(hbox_layout)

        buttons_background = QWidget(self)
        buttons_layout = QHBoxLayout()
        buttons_background.setLayout(buttons_layout)

        self.add_button = QPushButton("Add")
        buttons_layout.addWidget(self.add_button)

        self.remove_button = QPushButton("Remove")
        buttons_layout.addWidget(self.remove_button)

        tree_layout = QVBoxLayout(self)
        self.tree_view = QTreeView()
        self.tree_view.header().hide()
        self.tree_view.setMaximumWidth(300)
        self.tree_model = ObjectsModel.TreeModel()
        self.tree_view.setModel(self.tree_model)
        tree_layout.addWidget(buttons_background)
        tree_layout.addWidget(self.tree_view)

        hbox_layout.addLayout(tree_layout)

        self.graphics_view = QGraphicsView()
        self.scene = QGraphicsScene()
        self.graphics_view.setScene(self.scene)
        hbox_layout.addWidget(self.graphics_view)

        self.properties_view = QTableView()
        self.properties_view.setMaximumWidth(300)
        self.properties_model = PropertiesModel.TableModel()
        self.properties_view.setModel(self.properties_model)
        hbox_layout.addWidget(self.properties_view)

        self.init_menu()
        self.test()
        self.connectSignals()

    def connectSignals(self):
        self.add_button.clicked.connect(self.onAddClicked)
        self.remove_button.clicked.connect(self.onRemoveClicked)
        self.tree_view.clicked.connect(self.onClicked)
        self.properties_model.dataChanged.connect(self.onPropertyChanged)

    def onPropertyChanged(self):
        self.rebuildModel()

    def rebuildModel(self):
        #save index hierarhy
        indexes = []
        tmp_index = self.tree_view.currentIndex()
        indexes.append(tmp_index)
        while tmp_index.parent().isValid():
            indexes.append(tmp_index.parent())
            tmp_index = tmp_index.parent()

        self.tree_model.initRoot(self.items)
        self.tree_view.expandAll()

        if len(indexes) == 0:
            self.onClicked(self.tree_model.index(0, 0))
        else:
            last_index = indexes.pop(-1)
            index = self.tree_model.index(last_index.row(),
                                          last_index.column())
            #restore index hierarchy
            while len(indexes) > 0:
                last_index = indexes.pop(-1)
                index = self.tree_model.index(last_index.row(),
                                              last_index.column(), index)

            if index.isValid():
                self.onClicked(index)
            else:
                self.onClicked(self.tree_model.index(0, 0))

    def init_menu(self):
        exit_action = QAction("&Exit", self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.triggered.connect(qApp.quit)
        file_menu = self.menuBar().addMenu("&File")
        file_menu.addAction(QAction("Open", self))
        file_menu.addAction(QAction("Save", self))
        file_menu.addAction(QAction("SaveAs", self))
        file_menu.addSeparator()
        file_menu.addAction(exit_action)

    def appendObjectOnScene(self, object=DataStructures.Object):
        if object.rect.isValid():
            self.scene.addRect(object.rect, object.color)
        for child in object.childrens:
            self.appendObjectOnScene(child)

    def onClicked(self, index):
        object = index.data(Qt.UserRole + 1)

        self.properties_model.initProperties(object)

        self.scene.clear()
        self.appendObjectOnScene(object)
        self.tree_view.setCurrentIndex(index)

    def onAddClicked(self):
        index = self.tree_view.currentIndex()
        print(index)
        object = index.data(Qt.UserRole + 1)
        print(object.description())
        object.add_children(DataStructures.Object("New item"))
        self.rebuildModel()

    def onRemoveClicked(self):
        index = self.tree_view.currentIndex()
        object = index.data(Qt.UserRole + 1)
        if not object.parent:
            self.items.remove(object)
        else:
            object.parent.childrens.remove(object)

        self.rebuildModel()

    def test(self):
        self.items = []
        static = DataStructures.Object(
            "Static", DataStructures.createRect(0, 0, 800, 200))
        static.add_children(DataStructures.Object("child_1"))
        static.add_children(DataStructures.Object("child_2"))
        static.add_children(DataStructures.Object("child_3"))
        static.color = QColor(200, 0, 0).name()
        static.childrens[0].add_children(
            DataStructures.Object("child_1.1",
                                  DataStructures.createRect(40, 40, 80, 40)))

        self.items.append(static)

        dynamic = DataStructures.Object(
            "Dynamic", DataStructures.createRect(0, 0, 200, 800))
        dynamic.add_children(DataStructures.Object("child_1"))
        dynamic.add_children(DataStructures.Object("child_2"))
        dynamic.add_children(DataStructures.Object("child_3"))
        dynamic.childrens[2].add_children(DataStructures.Object("child_2.1"))
        dynamic.color = QColor(0, 0, 200).name()
        self.items.append(dynamic)

        self.rebuildModel()
class AddToProject(QDialog):
    """Dialog to let the user choose one of the folders from the opened proj"""

    def __init__(self, projects, parent=None):
        super(AddToProject, self).__init__(parent)
        #pathProjects must be a list
        self._projects = projects
        self.setWindowTitle(translations.TR_ADD_FILE_TO_PROJECT)
        self.pathSelected = ''
        vbox = QVBoxLayout(self)

        hbox = QHBoxLayout()
        self._list = QListWidget()
        for project in self._projects:
            self._list.addItem(project.name)
        self._list.setCurrentRow(0)
        self._tree = QTreeView()
        #self._tree.header().setHidden(True)
        self._tree.setSelectionMode(QTreeView.SingleSelection)
        self._tree.setAnimated(True)
        self.load_tree(self._projects[0])
        hbox.addWidget(self._list)
        hbox.addWidget(self._tree)
        vbox.addLayout(hbox)

        hbox2 = QHBoxLayout()
        btnAdd = QPushButton(translations.TR_ADD_HERE)
        btnCancel = QPushButton(translations.TR_CANCEL)
        hbox2.addWidget(btnCancel)
        hbox2.addWidget(btnAdd)
        vbox.addLayout(hbox2)

        btnCancel.connect(self.close)
        btnAdd.connect(self._select_path)
        self._list.currentItemChanged['QTreeWidgetItem*', 'QTreeWidgetItem*'].connect(self._project_changed)

    def _project_changed(self, item, previous):
        #FIXME, this is not being called, at least in osx
        for each_project in self._projects:
            if each_project.name == item.text():
                self.load_tree(each_project)

    def load_tree(self, project):
        """Load the tree view on the right based on the project selected."""
        qfsm = QFileSystemModel()
        qfsm.setRootPath(project.path)
        load_index = qfsm.index(qfsm.rootPath())
        qfsm.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot)
        qfsm.setNameFilterDisables(False)
        pext = ["*{0}".format(x) for x in project.extensions]
        qfsm.setNameFilters(pext)

        self._tree.setModel(qfsm)
        self._tree.setRootIndex(load_index)

        t_header = self._tree.header()
        t_header.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        t_header.setSectionResizeMode(0, QHeaderView.Stretch)
        t_header.setStretchLastSection(False)
        t_header.setClickable(True)

        self._tree.hideColumn(1)  # Size
        self._tree.hideColumn(2)  # Type
        self._tree.hideColumn(3)  # Modification date

        #FIXME: Changing the name column's title requires some magic
        #Please look at the project tree

    def _select_path(self):
        """Set pathSelected to the folder selected in the tree."""
        path = self._tree.model().filePath(self._tree.currentIndex())
        if path:
            self.pathSelected = path
            self.close()
Esempio n. 9
0
class FileWidget(QWidget):
    def __init__(self):
        super().__init__()
        # QWidget部件是PyQt5所有用户界面对象的基类。他为QWidget提供默认构造函数。默认构造函数没有父类。
        # 创建一个文件系统模型
        self.file_model = QFileSystemModel()
        # 设置目录为当前工作目录
        self.file_model.setRootPath(QDir.currentPath())
        # 创建树视图,构建文件目录视图
        self.treeview = QTreeView()
        # 绑定此文件模型
        self.treeview.setModel(self.file_model)
        '''
        设置当前勾结点索引为当前工作目录
        如果想从整个文件系统根节点开始浏览视图,
        简单删掉此行即可
        '''
        self.treeview.setRootIndex(self.file_model.index(QDir.currentPath()))
        # 头部显示排序戳
        self.treeview.header().setSortIndicatorShown(True)

        # 创建右键菜单
        self.treeview.setContextMenuPolicy(Qt.CustomContextMenu)
        # point = self.treeview.pos()
        self.treeview.customContextMenuRequested.connect(self.generateMenu)
        '''
        # 底部按钮布局
        self.mkdirButton = QPushButton("Make Directory...")
        self.rmButton = QPushButton("Remove")
        buttonLayout = QHBoxLayout()
        buttonLayout.addWidget(self.mkdirButton)
        buttonLayout.addWidget(self.rmButton)
        '''
        # 文件管理界面布局
        layout = QVBoxLayout()
        layout.addWidget(self.treeview)
        # layout.addLayout(buttonLayout)

        # resize()方法调整窗口的大小。600px宽300px高
        self.resize(600, 300)
        # move()方法移动窗口在屏幕上的位置到x = 300,y = 300坐标。
        self.move(300, 300)
        # 设置窗口的标题
        self.setWindowTitle('File Manage')
        # 设置窗口的图标
        self.setWindowIcon(QIcon('File-Explorer.png'))
        self.setLayout(layout)

    # 生成右键菜单
    def generateMenu(self, position):
        # 索引默认值
        row_num = -1
        # 遍历确定行号
        for i in self.treeview.selectionModel().selection().indexes():
            row_num = i.row()
        # 保证选中有效项
        if row_num != -1:
            # 创建右键菜单
            menu = QMenu()
            # 提供删除和创建文件/文件夹选项
            item1 = menu.addAction("Delete")
            item2 = menu.addAction("NewDirectory")
            # 在光标处显示执行菜单
            action = menu.exec_(self.treeview.mapToGlobal(position))
            if action == item1:
                #弹出消息框确认此次删除操作
                res = self.msgbox()
                if res:
                    self.delete()
                else:
                    return
            elif action == item2:
                self.mkdirectory()
            else:
                return
        else:
            return

    # 删除选定文件/文件夹
    def delete(self):
        index = self.treeview.currentIndex()
        if index.isValid():
            fileInfo = self.file_model.fileInfo(index)
            if fileInfo.isDir():
                self.file_model.rmdir(index)
            else:
                self.file_model.remove(index)

    # 确认框
    def msgbox(self):
        msgBox = QMessageBox()
        msgBox.setWindowTitle("Warning")
        msgBox.setText("Delete the file/dir you selected?")
        msgBox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        msgBox.setDefaultButton(QMessageBox.No)
        # button = QMessageBox.question("Warning", "delete the file/dir?",
        #                               QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        button = msgBox.exec_()
        if button == QMessageBox.No:
            return False
        elif button == QMessageBox.Yes:
            return True

    # 创建文件夹
    def mkdirectory(self):
        index = self.treeview.rootIndex()
        if index.isValid():
            # 弹出输入框录入文件名
            dirname, ok = QInputDialog.getText(self, "File Name",
                                               "Input an unique dir name:")
            if ok:
                self.file_model.mkdir(index, dirname)
            else:
                return

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_F3:
            self.close()
Esempio n. 10
0
class Ui_PoloshirtWindow(QWidget):
    POLOSHİRTID, POLOSHİRTISIM, POLOSHİRTFIYAT, RESIMURL = range(4)

    def setupUi(self, PoloshirtWindow):
        PoloshirtWindow.setObjectName("PoloshirtWindow")
        PoloshirtWindow.resize(997, 704)
        PoloshirtWindow.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.centralwidget = QtWidgets.QWidget(PoloshirtWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.gridLayoutWidget.setGeometry(QtCore.QRect(60, 120, 791, 271))
        self.gridLayoutWidget.setObjectName("gridLayoutWidget")
        self.gridLayout = QtWidgets.QGridLayout(self.gridLayoutWidget)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setObjectName("gridLayout")
        # bukısım c*k onemlı
        self.treeWidget = QTreeView(self.gridLayoutWidget)

        self.treeWidget.setEditTriggers(
            QtWidgets.QTableWidget.NoEditTriggers)
        # QtWidgets.QAbstractItemView.DoubleClicked | QtWidgets.QAbstractItemView.EditKeyPressed | QtWidgets.QAbstractItemView.SelectedClicked)
        self.treeWidget.setObjectName("treeWidget")
        self.gridLayout.addWidget(self.treeWidget, 0, 0, 2, 1)
        self.gridLayoutWidget_3 = QtWidgets.QWidget(self.centralwidget)
        self.gridLayoutWidget_3.setGeometry(QtCore.QRect(60, 390, 341, 231))
        self.gridLayoutWidget_3.setObjectName("gridLayoutWidget_3")
        self.gridLayout_3 = QtWidgets.QGridLayout(self.gridLayoutWidget_3)
        self.gridLayout_3.setContentsMargins(0, 0, 0, 0)
        self.gridLayout_3.setObjectName("gridLayout_3")
        self.poloshirtisimlineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3)
        font = QtGui.QFont()
        font.setPointSize(10)
        self.poloshirtisimlineEdit.setFont(font)
        self.poloshirtisimlineEdit.setObjectName("poloshirtisimlineEdit")
        self.gridLayout_3.addWidget(self.poloshirtisimlineEdit, 1, 1, 1, 1)
        self.poloshirtisimlabel = QtWidgets.QLabel(self.gridLayoutWidget_3)
        font = QtGui.QFont()
        font.setFamily("Microsoft Sans Serif")
        font.setPointSize(10)
        font.setBold(True)
        font.setWeight(75)
        self.poloshirtisimlabel.setFont(font)
        self.poloshirtisimlabel.setObjectName("poloshirtisimlabel")
        self.gridLayout_3.addWidget(self.poloshirtisimlabel, 1, 0, 1, 1)
        self.poloshirtfyatlabel = QtWidgets.QLabel(self.gridLayoutWidget_3)
        font = QtGui.QFont()
        font.setFamily("Microsoft Sans Serif")
        font.setPointSize(10)
        font.setBold(True)
        font.setWeight(75)
        self.poloshirtfyatlabel.setFont(font)
        self.poloshirtfyatlabel.setObjectName("poloshirtfyatlabel")
        self.gridLayout_3.addWidget(self.poloshirtfyatlabel, 2, 0, 1, 1)
        self.poloshirtidlineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3)
        self.poloshirtidlineEdit.setEnabled(False)
        font = QtGui.QFont()
        font.setPointSize(10)
        self.poloshirtidlineEdit.setFont(font)
        self.poloshirtidlineEdit.setObjectName("poloshirtidlineEdit")
        self.gridLayout_3.addWidget(self.poloshirtidlineEdit, 0, 1, 1, 1)
        self.poloshirtidlabel = QtWidgets.QLabel(self.gridLayoutWidget_3)
        font = QtGui.QFont()
        font.setFamily("Microsoft Sans Serif")
        font.setPointSize(10)
        font.setBold(True)
        font.setWeight(75)
        self.poloshirtidlabel.setFont(font)
        self.poloshirtidlabel.setObjectName("poloshirtidlabel")
        self.gridLayout_3.addWidget(self.poloshirtidlabel, 0, 0, 1, 1)
        self.poloshirtfiyatlineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3)
        font = QtGui.QFont()
        font.setPointSize(10)
        self.poloshirtfiyatlineEdit.setFont(font)
        self.poloshirtfiyatlineEdit.setObjectName("poloshirtfiyatlineEdit")
        self.gridLayout_3.addWidget(self.poloshirtfiyatlineEdit, 2, 1, 1, 1)
        self.poloshirtresimurllineEdit = QtWidgets.QLineEdit(self.gridLayoutWidget_3)
        font = QtGui.QFont()
        font.setPointSize(10)
        self.poloshirtresimurllineEdit.setFont(font)
        self.poloshirtresimurllineEdit.setObjectName("poloshirtresimurllineEdit")
        self.gridLayout_3.addWidget(self.poloshirtresimurllineEdit, 3, 1, 1, 1)
        self.poloshirtresimurllabel = QtWidgets.QLabel(self.gridLayoutWidget_3)
        font = QtGui.QFont()
        font.setFamily("Microsoft Sans Serif")
        font.setPointSize(10)
        font.setBold(True)
        font.setWeight(75)
        self.poloshirtresimurllabel.setFont(font)
        self.poloshirtresimurllabel.setObjectName("poloshirtresimurllabel")
        self.gridLayout_3.addWidget(self.poloshirtresimurllabel, 3, 0, 1, 1)
        self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.verticalLayoutWidget.setGeometry(QtCore.QRect(419, 410, 211, 211))
        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout.setObjectName("verticalLayout")
        self.poloshirteklebuton = QtWidgets.QPushButton(self.verticalLayoutWidget)
        font = QtGui.QFont()
        font.setFamily("MS Sans Serif")
        font.setPointSize(10)
        font.setBold(True)
        font.setWeight(75)
        self.poloshirteklebuton.setFont(font)
        self.poloshirteklebuton.setObjectName("poloshirteklebuton")
        self.verticalLayout.addWidget(self.poloshirteklebuton)
        self.poloshirtduzenlebuton = QtWidgets.QPushButton(self.verticalLayoutWidget)
        font = QtGui.QFont()
        font.setFamily("MS Sans Serif")
        font.setPointSize(10)
        font.setBold(True)
        font.setWeight(75)
        self.poloshirtduzenlebuton.setFont(font)
        self.poloshirtduzenlebuton.setObjectName("poloshirtduzenlebuton")
        self.verticalLayout.addWidget(self.poloshirtduzenlebuton)
        self.poloshirtsilbuton = QtWidgets.QPushButton(self.verticalLayoutWidget)
        font = QtGui.QFont()
        font.setFamily("MS Sans Serif")
        font.setPointSize(10)
        font.setBold(True)
        font.setWeight(75)
        self.poloshirtsilbuton.setFont(font)
        self.poloshirtsilbuton.setObjectName("poloshirtsilbuton")
        self.verticalLayout.addWidget(self.poloshirtsilbuton)
        self.formLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.formLayoutWidget.setGeometry(QtCore.QRect(650, 500, 271, 31))
        self.formLayoutWidget.setObjectName("formLayoutWidget")
        self.formLayout = QtWidgets.QFormLayout(self.formLayoutWidget)
        self.formLayout.setContentsMargins(0, 0, 0, 0)
        self.formLayout.setObjectName("formLayout")

        font = QtGui.QFont()
        font.setFamily("MS Sans Serif")
        font.setPointSize(10)
        font.setBold(True)
        font.setWeight(75)


        font = QtGui.QFont()
        font.setPointSize(10)


        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setGeometry(QtCore.QRect(190, 20, 561, 51))
        font = QtGui.QFont()
        font.setFamily("Myanmar Text")
        font.setPointSize(12)
        font.setBold(True)
        font.setWeight(75)
        self.label.setFont(font)
        self.label.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.label.setFrameShadow(QtWidgets.QFrame.Plain)
        self.label.setObjectName("label")
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(350, 70, 170, 51))
        font = QtGui.QFont()
        font.setFamily("Myanmar Text")
        font.setPointSize(18)
        font.setBold(True)
        font.setWeight(75)
        self.label_3.setFont(font)
        self.label_3.setFrameShape(QtWidgets.QFrame.NoFrame)
        self.label_3.setFrameShadow(QtWidgets.QFrame.Plain)
        self.label_3.setObjectName("label_3")
        PoloshirtWindow.setCentralWidget(self.centralwidget)
        self.statusbar = QtWidgets.QStatusBar(PoloshirtWindow)
        self.statusbar.setObjectName("statusbar")
        PoloshirtWindow.setStatusBar(self.statusbar)
        self.actionPoloshirt = QtWidgets.QAction(PoloshirtWindow)
        self.actionPoloshirt.setObjectName("actionPoloshirt")
        self.actionPoloshirt = QtWidgets.QAction(PoloshirtWindow)
        self.actionPoloshirt.setObjectName("actionPoloshirt")
        self.actionPoloshirt = QtWidgets.QAction(PoloshirtWindow)
        self.actionPoloshirt.setObjectName("actionPoloshirt")
        self.actioncgiyim = QtWidgets.QAction(PoloshirtWindow)
        self.actioncgiyim.setObjectName("actioncgiyim")
        self.retranslateUi(PoloshirtWindow)
        QtCore.QMetaObject.connectSlotsByName(PoloshirtWindow)

        #SORT  SIRALAMA
        self.treeWidget.setSortingEnabled(True)

        #only int
        self.onlyInt = QIntValidator()
        self.poloshirtfiyatlineEdit.setValidator(self.onlyInt)

        # bu kısmı kullan nolur
        model = self.treetablemodelyaratma(self)
        self.treetablemodelyaratma2(model)
        self.treeWidget.setModel(model)

        # Buton clickleri
        self.treeWidget.doubleClicked.connect(self.on_doubleClicked)
        self.poloshirteklebuton.clicked.connect(self.eklebuton)
        self.poloshirtsilbuton.clicked.connect(self.silbuton)
        self.poloshirtduzenlebuton.clicked.connect(self.duzenlebuton)

    def treetablemodelyaratma(self, parent):
        model = QStandardItemModel(0, 4, parent)
        model.setHeaderData(self.POLOSHİRTID, Qt.Horizontal, "Poloshirt id")
        model.setHeaderData(self.POLOSHİRTISIM, Qt.Horizontal, "Poloshirt isim")
        model.setHeaderData(self.POLOSHİRTFIYAT, Qt.Horizontal, "Poloshirt fiyat")
        model.setHeaderData(self.RESIMURL, Qt.Horizontal, "Poloshirt Resimurl")

        return model

    def on_doubleClicked(self, index):
        item = self.treeWidget.currentIndex()
        item2 = item.model().itemFromIndex(index).text()

        self.poloshirtidlineEdit.setText(item2)

        # tabloyu update etme yöntemı
        self.updateview()

    def eklebuton(self):
        if (self.poloshirtfiyatlineEdit.text() =="" or self.poloshirtisimlineEdit.text()=="" or self.poloshirtresimurllineEdit.text()==""):
            return
        else:
            conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi")
            cursor = conn.cursor(pymysql.cursors.DictCursor)

            query = "INSERT INTO `poloshirt` (poloshirt_isim, poloshirt_fiyat,poloshirt_resimurl) VALUES (%s,%s,%s)"

            val = (self.poloshirtisimlineEdit.text(), self.poloshirtfiyatlineEdit.text(), self.poloshirtresimurllineEdit.text())
            cursor.execute(query, val)
            conn.commit()
            # tabloyu update etme yöntemı
            self.updateview()
            self.poloshirtfiyatlineEdit.clear()
            self.poloshirtisimlineEdit.clear()
            self.poloshirtresimurllineEdit.clear()
            self.poloshirtidlineEdit.clear()
    def silbuton(self):
        if (self.poloshirtidlineEdit.text()==""):
            return
        else:

            conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi")
            cursor = conn.cursor(pymysql.cursors.DictCursor)

            query = "DELETE FROM `poloshirt` where id=%s"

            val = (self.poloshirtidlineEdit.text())
            cursor.execute(query,val)
            conn.commit()
            self.updateview()
            self.poloshirtidlineEdit.clear()

    def duzenlebuton(self):
        if (self.poloshirtidlineEdit.text()==""or self.poloshirtfiyatlineEdit.text() == "" or self.poloshirtisimlineEdit.text() == "" or self.poloshirtresimurllineEdit.text() == ""):
            return
        else:
            conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi")
            cursor = conn.cursor(pymysql.cursors.DictCursor)

            query = "UPDATE `poloshirt` SET poloshirt_isim=%s,poloshirt_fiyat=%s,poloshirt_resimurl=%s  where id=%s"

            val = (self.poloshirtisimlineEdit.text(),self.poloshirtfiyatlineEdit.text(),self.poloshirtresimurllineEdit.text(),self.poloshirtidlineEdit.text())
            cursor.execute(query, val)
            conn.commit()
            self.updateview()
            self.poloshirtidlineEdit.clear()
            self.poloshirtfiyatlineEdit.clear()
            self.poloshirtisimlineEdit.clear()
            self.poloshirtresimurllineEdit.clear()
            self.poloshirtidlineEdit.clear()

    def updateview(self):
        model = self.treetablemodelyaratma(self)
        self.treeWidget.setModel(model)
        self.treetablemodelyaratma2(model)

    def treetablemodelyaratma2(self, model):
        conn = pymysql.connect(host="localhost", user="******", passwd="12345678", db="bilisim_proje_odevi")

        cursor = conn.cursor(pymysql.cursors.DictCursor)

        query = "SELECT * FROM `poloshirt`"
        cursor.execute(query)

        data = cursor.fetchall()

        for row in data:
            model.insertRow(0)
            model.setData(model.index(0, self.POLOSHİRTID), row['id'])
            model.setData(model.index(0, self.POLOSHİRTISIM), row['poloshirt_isim'])
            model.setData(model.index(0, self.POLOSHİRTFIYAT), row['poloshirt_fiyat'])
            model.setData(model.index(0, self.RESIMURL), row['poloshirt_resimurl'])

    def retranslateUi(self, PoloshirtWindow):
        _translate = QtCore.QCoreApplication.translate
        PoloshirtWindow.setWindowTitle(_translate("PoloshirtWindow", "Poloshirtwindow"))
        self.poloshirtisimlabel.setText(_translate("PoloshirtWindow", "Poloshirt isim:"))
        self.poloshirtfyatlabel.setText(_translate("PoloshirtWindow", "Poloshirt Fiyat:"))
        self.poloshirtidlabel.setText(_translate("PoloshirtWindow", "ID:"))
        self.poloshirtresimurllabel.setText(_translate("PoloshirtWindow", "Resim_Url"))
        self.poloshirteklebuton.setText(_translate("PoloshirtWindow", "Ekle"))
        self.poloshirtduzenlebuton.setText(_translate("PoloshirtWindow", "Düzenle"))
        self.poloshirtsilbuton.setText(_translate("PoloshirtWindow", "Sil"))

        self.label.setText(_translate("PoloshirtWindow", "TAYFUN TESKTİL ÜRÜN DÜZENLEME"))
        self.label_3.setText(_translate("PoloshirtWindow", "POLOSHİRT"))
        self.actionPoloshirt.setText(_translate("PoloshirtWindow", "Poloshirt"))
        self.actionPoloshirt.setText(_translate("PoloshirtWindow", "Poloshirt"))
        self.actionPoloshirt.setText(_translate("PoloshirtWindow", "Poloshirt"))
        self.actioncgiyim.setText(_translate("PoloshirtWindow", "İcgiyim"))
Esempio n. 11
0
class _LocatorDialog(QDialog):
    """Locator widget and implementation
    """

    def __init__(self, parent, commandClasses):
        QDialog.__init__(self, parent)
        self._terminated = False
        self._commandClasses = commandClasses

        self._createUi()

        self._loadingTimer = QTimer(self)
        self._loadingTimer.setSingleShot(True)
        self._loadingTimer.setInterval(200)
        self._loadingTimer.timeout.connect(self._applyLoadingCompleter)

        self._completerLoaderThread = _CompleterLoaderThread(self)

        self.finished.connect(self._terminate)

        self._command = None
        self._updateCurrentCommand()

    def _createUi(self):
        self.setWindowTitle(core.project().path().replace(os.sep, '/') or 'Locator')

        self.setLayout(QVBoxLayout())
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().setSpacing(1)

        biggerFont = self.font()
        biggerFont.setPointSizeF(biggerFont.pointSizeF() * 2)
        self.setFont(biggerFont)

        self._edit = _CompletableLineEdit(self)
        self._edit.updateCurrentCommand.connect(self._updateCurrentCommand)
        self._edit.enterPressed.connect(self._onEnterPressed)
        self._edit.installEventFilter(self)  # catch Up, Down
        self._edit.setFont(biggerFont)
        self.layout().addWidget(self._edit)
        self.setFocusProxy(self._edit)

        self._table = QTreeView(self)
        self._table.setFont(biggerFont)
        self._model = _CompleterModel()
        self._table.setModel(self._model)
        self._table.setItemDelegate(HTMLDelegate(self._table))
        self._table.setRootIsDecorated(False)
        self._table.setHeaderHidden(True)
        self._table.clicked.connect(self._onItemClicked)
        self._table.setAlternatingRowColors(True)
        self._table.installEventFilter(self)  # catch focus and give to the edit
        self.layout().addWidget(self._table)

        width = QFontMetrics(self.font()).width('x' * 64)  # width of 64 'x' letters
        self.resize(width, width * 0.62)

    def _terminate(self):
        if not self._terminated:
            if self._command is not None:
                self._command.terminate()
                self._command = None

            self._edit.terminate()

            self._completerLoaderThread.terminate()
            if self._model:
                self._model.terminate()
            core.workspace().focusCurrentDocument()
            self._terminated = True

    def _updateCurrentCommand(self):
        """Try to parse line edit text and set current command
        """
        if self._terminated:
            return

        newCommand = self._parseCurrentCommand()

        if newCommand is not self._command:
            if self._command is not None:
                self._command.updateCompleter.disconnect(self._updateCompletion)
                self._command.terminate()

            self._command = newCommand
            if self._command is not None:
                self._command.updateCompleter.connect(self._updateCompletion)

        self._updateCompletion()

    def _updateCompletion(self):
        """User edited text or moved cursor. Update inline and TreeView completion
        """
        if self._command is not None:
            completer = self._command.completer()

            if completer is not None and completer.mustBeLoaded:
                self._loadingTimer.start()
                self._completerLoaderThread.loadCompleter(self._command, completer)
            else:
                self._applyCompleter(self._command, completer)
        else:
            self._applyCompleter(None, _HelpCompleter(self._commandClasses))

    def _applyLoadingCompleter(self):
        """Set 'Loading...' message
        """
        self._applyCompleter(None, StatusCompleter('<i>Loading...</i>'))

    def onCompleterLoaded(self, command, completer):
        """The method called from _CompleterLoaderThread when the completer is ready
        This code works in the GUI thread
        """
        self._applyCompleter(command, completer)

    def _applyCompleter(self, command, completer):
        """Apply completer. Called by _updateCompletion or by thread function when Completer is constructed
        """
        self._loadingTimer.stop()

        if command is not None:
            command.onCompleterLoaded(completer)

        if completer is None:
            completer = _HelpCompleter([command])

        if self._edit.cursorPosition() == len(self._edit.text()):  # if cursor at the end of text
            self._edit.setInlineCompletion(completer.inline())

        self._model.setCompleter(completer)
        if completer.columnCount() > 1:
            self._table.resizeColumnToContents(0)
            self._table.setColumnWidth(0, self._table.columnWidth(0) + 20)  # 20 px spacing between columns

        selItem = completer.autoSelectItem()
        if selItem:
            index = self._model.createIndex(selItem[0],
                                            selItem[1])
            self._table.setCurrentIndex(index)

    def _onItemClicked(self, index):
        """Item in the TreeView has been clicked.
        Open file, if user selected it
        """
        if self._command is not None:
            fullText = self._model.completer.getFullText(index.row())
            if fullText is not None:
                self._command.onItemClicked(fullText)
                if self._tryExecCurrentCommand():
                    self.accept()
                    return
                else:
                    self._edit.setText(self._command.lineEditText())
                    self._updateCurrentCommand()

        self._edit.setFocus()

    def _onEnterPressed(self):
        """User pressed Enter or clicked item. Execute command, if possible
        """
        if self._table.currentIndex().isValid():
            self._onItemClicked(self._table.currentIndex())
        else:
            self._tryExecCurrentCommand()

    def _tryExecCurrentCommand(self):
        if self._command is not None and self._command.isReadyToExecute():
            self._command.execute()
            self.accept()
            return True
        else:
            return False

    def _chooseCommand(self, words):
        for cmd in self._commandClasses:
            if cmd.command == words[0]:
                return cmd, words[1:]

        isPath = words and (words[0].startswith('/') or
                            words[0].startswith('./') or
                            words[0].startswith('../') or
                            words[0].startswith('~/') or
                            words[0][1:3] == ':\\' or
                            words[0][1:3] == ':/'
                            )
        isNumber = len(words) == 1 and all([c.isdigit() for c in words[0]])

        def matches(cmd):
            if isPath:
                return cmd.isDefaultPathCommand
            elif isNumber:
                return cmd.isDefaultNumericCommand
            else:
                return cmd.isDefaultCommand

        for cmd in self._commandClasses:
            if matches(cmd):
                return cmd, words

    def _parseCurrentCommand(self):
        """ Parse text and try to get (command, completable word index)
        Return None if failed to parse
        """
        # Split line
        text = self._edit.commandText()
        words = splitLine(text)
        if not words:
            return None

        # Find command
        cmdClass, args = self._chooseCommand(words)

        if isinstance(self._command, cmdClass):
            command = self._command
        else:
            command = cmdClass()

        # Try to make command object
        try:
            command.setArgs(args)
        except InvalidCmdArgs:
            return None
        else:
            return command

    def eventFilter(self, obj, event):
        if obj is self._edit:
            if event.type() == QEvent.KeyPress and \
               event.key() in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown):
                return self._table.event(event)
        elif obj is self._table:
            if event.type() == QEvent.FocusIn:
                self._edit.setFocus()
                return True

        return False
Esempio n. 12
0
class CatalogMainWindow(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)

        # Структура главного окна
        # Создаем меню
        self.menu_bar = self.menuBar()

        # Создаем блоки меню
        # Блок меню 'Учет движения товаров'

        self.gma_menu = self.menu_bar.addMenu('Учет движения товаров')
        self.gma_menu.setEnabled(False)
        self.ro_open_btn = QAction(self)
        self.ro_open_btn.setText('Приходный ордер')
        self.wo_open_btn = QAction(self)
        self.wo_open_btn.setText('Расходный ордер')
        self.gma_menu.addAction(self.ro_open_btn)
        self.gma_menu.addAction(self.wo_open_btn)

        # Блок меню 'Отчеты'

        self.r_menu = self.menu_bar.addMenu('Отчеты')
        self.r_menu.setEnabled(False)
        self.tws_report_btn = QAction(self)
        self.tws_report_btn.setText('Операции с поставщиками')
        self.gis_report_btn = QAction(self)
        self.gis_report_btn.setText('Товары в наличии')
        self.r_menu.addAction(self.tws_report_btn)
        self.r_menu.addAction(self.gis_report_btn)

        # Блок меню 'Справочники'
        self.d_menu = self.menu_bar.addMenu('Справочники')
        self.d_menu.setEnabled(False)

        self.u_catalog_btn = QAction(self)
        self.u_catalog_btn.setText('Пользователи')

        self.r_catalog_btn = QAction(self)
        self.r_catalog_btn.setText('Права пользователей')

        self.c_catalog_btn = QAction(self)
        self.c_catalog_btn.setText('Категории')

        self.n_catalog_btn = QAction(self)
        self.n_catalog_btn.setText('Номенклатура товаров')

        self.un_catalog_btn = QAction(self)
        self.un_catalog_btn.setText('Единицы измерения')

        self.s_catalog_btn = QAction(self)
        self.s_catalog_btn.setText('Поставщики')

        self.p_catalog_btn = QAction(self)
        self.p_catalog_btn.setText('Должности')

        self.e_catalog_btn = QAction(self)
        self.e_catalog_btn.setText('Сотрудники')

        self.d_menu.addAction(self.u_catalog_btn)
        self.d_menu.addAction(self.r_catalog_btn)
        self.d_menu.addAction(self.c_catalog_btn)
        self.d_menu.addAction(self.n_catalog_btn)
        self.d_menu.addAction(self.un_catalog_btn)
        self.d_menu.addAction(self.s_catalog_btn)
        self.d_menu.addAction(self.p_catalog_btn)
        self.d_menu.addAction(self.e_catalog_btn)

        # Верхний виджет с полным путем до файла БД

        self.db_lbl = QLabel()
        self.db_lbl.setText('Путь до БД:')
        self.db_lbl.setStyleSheet("border-style: none;" "font-size: 10pt;")
        self.db_path_lbl = QLineEdit()
        self.db_path_lbl.setStyleSheet(
            "background-color: white;"
            "font-size: 10pt;"
            "color: green;")
        self.db_path_lbl.setFixedSize(700, 25)
        self.db_path_lbl.setEnabled(False)
        self.db_path_btn = QPushButton('...')
        self.db_path_btn.setFixedSize(25, 25)
        self.db_path_btn.setStyleSheet(
            "border-width: 1px;"
            "border-style: solid;"
            "border-color: dimgray;"
            "border-radius: 5px;"
            "background-color: gainsboro;")
        self.db_path_btn.clicked.connect(self.on_db_path_btn_clicked)

        self.tdw = QDockWidget()
        self.tdw.setFixedHeight(65)
        self.tdw.setFeatures(self.tdw.NoDockWidgetFeatures)
        self.tdw_grid = QGridLayout()
        self.tdw_grid.setColumnStretch(3, 1)
        self.tdw_grid.addWidget(self.db_lbl)
        self.tdw_grid.addWidget(self.db_path_lbl)
        self.tdw_grid.addWidget(self.db_path_btn)
        self.tdw_frame = QFrame()
        self.tdw_frame.setStyleSheet(
            "background-color: ghostwhite;"
            "border-width: 0.5px;"
            "border-style: solid;"
            "border-color: silver;")
        self.tdw_frame.setLayout(self.tdw_grid)
        self.tdw.setWidget(self.tdw_frame)
        self.addDockWidget(QtCore.Qt.TopDockWidgetArea, self.tdw)

        # Левый виджет с окном каталога продукции

        self.ldw = QDockWidget()
        self.ldw.setFixedSize(500, 570)
        self.ldw.setFeatures(self.ldw.NoDockWidgetFeatures)
        self.ldw_tree = QTreeView()
        self.ldw_tree.setFixedSize(500, 530)
        self.ldw_tree.setHeaderHidden(True)
        self.ldw_tree_model = QStandardItemModel()
        self.ldw_tree.setModel(self.ldw_tree_model)
        self.ldw_tree.clicked.connect(self.on_ldw_tree_clicked)
        self.outf_scroll = QScrollArea()
        self.outf_scroll.setWidget(self.ldw_tree)
        self.ldw.setWidget(self.outf_scroll)
        self.ldw.setWindowTitle("Каталог продукции")

        # Центральный виджет с карточкой товара

        self.cdw = QDockWidget()
        self.cdw.setFeatures(self.cdw.NoDockWidgetFeatures)
        #self.setCentralWidget(self.cdw)

        # Нижний виджет со служебными сообщениями

        self.smdw = QDockWidget()
        self.smdw.setFixedHeight(140)
        self.smdw.setFeatures(self.smdw.NoDockWidgetFeatures)
        self.sm_list_widget = QListWidget()
        self.smdw.setWidget(self.sm_list_widget)
        self.smdw.setWindowTitle("Служебные сообщения")

    # Функции главного окна
    # Функция выбора файла базы данных
    def on_db_path_btn_clicked(self):
        global db_path
        db_path, _filter = QFileDialog.getOpenFileName(
            None, "Open Data File", '.', "(*.sqlite)")
        self.db_path_lbl.setText(db_path)
        self.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.ldw)
        self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.smdw)

        # Создаем все таблицы, которые нужны
        con = QtSql.QSqlDatabase.addDatabase('QSQLITE')
        con.setDatabaseName(db_path)
        con.open()

        if 'rules' not in con.tables():

            query = QtSql.QSqlQuery()
            query.exec(
                "CREATE TABLE rules(rule STRING UNIQUE, description STRING)")

            r1 = "admin"
            d1 = "Доступны все опции"
            query.exec(
                "INSERT INTO rules(rule, description) VALUES ('%s','%s')" %
                (r1, d1))

            r2 = "Кладовщик"
            d2 = "Доступны опции кладовщика"
            query.exec(
                "INSERT INTO rules(rule, description) VALUES ('%s','%s')" %
                (r2, d2))

        if 'nomenclature' not in con.tables():
            query = QtSql.QSqlQuery()
            query.exec(
                "CREATE TABLE nomenclature(item_numb INTEGER UNIQUE PRIMARY KEY NOT NULL, \
                item_name STRING NOT NULL, item_unit STRING REFERENCES units (unit) NOT NULL, \
                item_cat STRING REFERENCES categories (category) NOT NULL, item_img BLOB)")

        if 'units' not in con.tables():
            query = QtSql.QSqlQuery()
            query.exec("CREATE TABLE units(unit STRING PRIMARY KEY \
            UNIQUE NOT NULL)")

        if 'categories' not in con.tables():
            query = QtSql.QSqlQuery()
            query.exec(
                "CREATE TABLE categories(category STRING PRIMARY KEY UNIQUE \
                NOT NULL, description STRING NOT NULL)")

        # Создаем таблицу 'users', если не существует
        if 'users' not in con.tables():
            query = QtSql.QSqlQuery()
            query.exec(
                "CREATE TABLE users(id_user INTEGER PRIMARY KEY AUTOINCREMENT \
                unique NOT NULL, \
                login STRING UNIQUE NOT NULL, password STRING NOT NULL, \
                name TEXT NOT NULL, \
                rules STRING REFERENCES rules (rule) NOT NULL)")

        if 'suppliers' not in con.tables():
            query = QtSql.QSqlQuery()
            query.exec("CREATE TABLE suppliers( \
                id_supplier INTEGER UNIQUE PRIMARY KEY AUTOINCREMENT NOT NULL, \
                supplier STRING UNIQUE NOT NULL, \
                ownerchipform STRING NOT NULL, \
                address STRING NOT NULL, \
                phone STRING NOT NULL, \
                email STRING NOT NULL \
            )")

        if 'positions' not in con.tables():
            query = QtSql.QSqlQuery()
            query.exec(
                "CREATE TABLE positions(position STRING PRIMARY KEY UNIQUE \
                NOT NULL)")

        if 'employee' not in con.tables():
            query = QtSql.QSqlQuery()
            query.exec(
                "CREATE TABLE employee(employee_id INTEGER UNIQUE PRIMARY KEY AUTOINCREMENT, \
                fio STRING NOT NULL, \
                position STRING REFERENCES positions (position) NOT NULL)")

        con.close()



        con = sqlite3.connect(db_path)
        cur = con.cursor()
        sql_cats_select = """\
        SELECT DISTINCT item_cat FROM nomenclature
        """
        cur.execute(sql_cats_select)
        arr = cur.fetchall()
        #print(arr)

        for cat in arr:
            sql_items_select = "SELECT item_name FROM nomenclature \
            WHERE item_cat = ?"
            cur.execute(sql_items_select, [cat[0]])
            items = cur.fetchall()
            #print(items)
            parent_item = QStandardItem(cat[0])
            self.ldw_tree_model.appendRow(parent_item)

            j = 0

            for item in items:
                child_item = QStandardItem(item[0])
                parent_item.setChild(j, 0, child_item)
                j = j + 1


        cur.close()
        con.close()

        self.gma_menu.setEnabled(True)
        self.r_menu.setEnabled(True)
        self.d_menu.setEnabled(True)

        self.ro_open_btn.triggered.connect(
            lambda: documents_class.on_ro_doc_btn(
                self, db_path))
        self.wo_open_btn.triggered.connect(
            lambda: documents_class.on_wo_doc_btn(
                self, db_path))

        self.gis_report_btn.triggered.connect(
            lambda: gis_report_class.on_gis_report_btn(
                self, db_path))

        self.tws_report_btn.triggered.connect(
            lambda: tws_report_class.on_tws_report_btn(
                self, db_path))

        self.u_catalog_btn.triggered.connect(
            lambda: directories_class.on_u_catalog_btn(
                self, db_path))
        self.r_catalog_btn.triggered.connect(
            lambda: directories_class.on_r_catalog_btn(
                self, db_path))
        self.c_catalog_btn.triggered.connect(
            lambda: directories_class.on_c_catalog_btn(
                self, db_path))
        self.n_catalog_btn.triggered.connect(
            lambda: directories_class.on_n_catalog_btn(
                self, db_path))
        self.un_catalog_btn.triggered.connect(
            lambda: directories_class.on_un_catalog_btn(
                self, db_path))
        self.s_catalog_btn.triggered.connect(
            lambda: directories_class.on_s_catalog_btn(
                self, db_path))
        self.p_catalog_btn.triggered.connect(
            lambda: directories_class.on_p_catalog_btn(
                self, db_path))
        self.e_catalog_btn.triggered.connect(
            lambda: directories_class.on_e_catalog_btn(
                self, db_path))

    # Функция выбора товара из каталога-дерева
    def on_ldw_tree_clicked(self):
        #print('вах')


        try:
            current_index = self.ldw_tree.currentIndex()
            file_name = self.ldw_tree.model().data(current_index)
            con = sqlite3.connect(db_path)
            cur = con.cursor()
            sql_item_select = "SELECT item_name FROM nomenclature \
            WHERE item_name = ?"
            cur.execute(sql_item_select, [file_name])
            item = cur.fetchone()
            if item:
                self.setCentralWidget(self.cdw)
                self.cdw.setWindowTitle("Карточка товара")
                item_card = item_card_class(self, db_path, file_name)
                self.cdw.setWidget(item_card)
            cur.close()
            con.close()
        except Exception as e:
            print('Ошибка:\n', traceback.format_exc())
Esempio n. 13
0
    class ClientList(QWidget):
        def __init__(self, data: ()):
            super().__init__()
            self.tree_view = QTreeView()

            self.model = QStandardItemModel()
            self.rootNode = self.model.invisibleRootItem()
            self.applyModel()

            buttons = QHBoxLayout()
            add = QPushButton("Add")
            add.clicked.connect(self.addClient)
            remove = QPushButton("Remove")
            remove.clicked.connect(self.removeClient)
            buttons.addWidget(add)
            buttons.addWidget(remove)

            layout = QVBoxLayout()
            layout.addLayout(buttons)
            layout.addWidget(self.tree_view)
            self.setLayout(layout)

            if data:
                self.setData(data)

            self.in_progress = False

        def addClient(self):
            branch = [QStandardItem(), QStandardItem()]
            branch[0].setText("/")
            branch[1].setText("client_")
            self.model.appendRow(branch)
            index = self.model.index(self.model.rowCount() - 1, 0)
            self.tree_view.setCurrentIndex(index)
            self.applyModel()

        def removeClient(self):
            index = self.tree_view.currentIndex()
            if index.isValid():
                self.model.removeRow(index.row())
                self.applyModel()

        def applyModel(self):
            self.model.setHeaderData(0, Qt.Horizontal, "Path")
            self.model.setHeaderData(1, Qt.Horizontal, "Client variable")
            self.tree_view.setModel(self.model)

        def getData(self):
            data = {}

            for row in range(self.model.rowCount()):
                path = self.model.data(self.model.index(row, 0))
                client = self.model.data(self.model.index(row, 1))

                if path and client:
                    data[path] = client

            return data

        def setData(self, data):
            for path, client in data.items():
                branch = [QStandardItem(), QStandardItem()]
                branch[0].setText(path)
                branch[1].setText(client)
                self.model.appendRow(branch)
            self.applyModel()
Esempio n. 14
0
class FileBrowserWidget(QWidget):
    on_open = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.model = QFileSystemModel()
        self.rootFolder = ''
        self.model.setRootPath(self.rootFolder)
        self.tree = QTreeView()
        self.tree.setModel(self.model)

        self.tree.setAnimated(False)
        self.tree.setIndentation(20)
        self.tree.setSortingEnabled(True)
        self.tree.sortByColumn(0, 0)
        self.tree.setColumnWidth(0, 200)
        self.tree.setDragEnabled(True)

        self.tree.setWindowTitle("Dir View")
        self.tree.resize(640, 480)
        self.tree.doubleClicked.connect(self.onDblClick)
        self.tree.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.tree.customContextMenuRequested.connect(
            self.onCustomContextMenuRequested)

        windowLayout = QVBoxLayout()
        windowLayout.addWidget(self.tree)
        windowLayout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(windowLayout)

    def onCustomContextMenuRequested(self, point):
        index = self.tree.indexAt(point)
        selectedFile = None
        selectedFolder = None
        ctx = QMenu("Context menu", self)
        if index.isValid():
            file = self.model.fileInfo(index)
            selectedFile = file.absoluteFilePath()
            selectedFolder = selectedFile if file.isDir(
            ) else file.absolutePath()
            if file.isDir():
                ctx.addAction(
                    "Open in file manager", lambda: QDesktopServices.openUrl(
                        QUrl.fromLocalFile(selectedFile)))
            if not file.isDir():
                for wndTyp, meta in WindowTypes.types:
                    text = 'Open with ' + meta.get('displayName', meta['name'])
                    print(wndTyp, meta)
                    ctx.addAction(
                        QAction(text,
                                self,
                                statusTip=text,
                                triggered=lambda dummy, meta=meta: navigate(
                                    "WINDOW", "Type=" + meta['name'],
                                    "FileName=" + selectedFile)))
                ctx.addSeparator()

        ctx.addAction("Set root folder ...",
                      lambda: self.selectRootFolder(preselect=selectedFolder))
        ctx.exec(self.tree.viewport().mapToGlobal(point))

    def selectRootFolder(self, preselect=None):
        if preselect == None: preselect = self.rootFolder
        dir = QFileDialog.getExistingDirectory(self, "Set root folder",
                                               preselect)
        if dir != None:
            self.setRoot(dir)

    def setRoot(self, dir):
        self.rootFolder = dir
        self.model.setRootPath(dir)
        self.tree.setRootIndex(self.model.index(dir))

    def onDblClick(self, index):
        if index.isValid():
            file = self.model.fileInfo(index)
            if not file.isDir():
                navigate("OPEN", "FileName=" + file.absoluteFilePath())

    def saveState(self):
        if self.tree.currentIndex().isValid():
            info = self.model.fileInfo(self.tree.currentIndex())
            return {"sel": info.absoluteFilePath(), "root": self.rootFolder}

    def restoreState(self, state):
        try:
            self.setRoot(state["root"])
        except:
            pass
        try:
            idx = self.model.index(state["sel"])
            if idx.isValid():
                self.tree.expand(idx)
                self.tree.setCurrentIndex(idx)
                self.tree.scrollTo(idx, QAbstractItemView.PositionAtCenter)
        except:
            pass
Esempio n. 15
0
class ModelGui(QDialog):
    """The gui to start and manipulate the analysis model"""
    ANALYSIS, ID, NUMPOINTS = range(3)

    NO_PARENT_ID = 0xFFFFFFFF

    def __init__(self, analysis_event_queue, select, delete, load):
        super(ModelGui, self).__init__()
        self.analysis_event_queue = analysis_event_queue
        self.select_callback = select
        self.delete_callback = delete
        self.load_callback = load

        self.name = None
        self.label_name = None
        self.meta_name = None
        self.hsne_name = None

        self.title = 'Analysis hierarchy viewer'
        self.left = 10
        self.top = 10
        self.width = 1000
        self.height = 400
        self.init_ui()
        self.id_item = {}
        self.root_id = None

    def init_ui(self):
        """All the ui layout in one place. Note: Part of the ui
        is switchable depending on the selected demo type, see the ui_matrix"""
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.model = self.create_analysis_model(self)
        self.analysis_model = None
        self.top_scale = None
        self.thumb_size = (40, 40)

        # The analysis tree
        self.tree = QTreeView()
        self.tree.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.tree.header().setSectionResizeMode(QHeaderView.ResizeToContents)
        self.tree.setIconSize(QSize(*self.thumb_size))
        self.tree.setModel(self.model)

        self.tree.setAnimated(False)
        self.tree.setIndentation(20)
        self.tree.setSortingEnabled(True)

        self.tree.resize(640, 480)

        # Control layout
        controlLayout = QVBoxLayout()

        self.preconfigured_group = QGroupBox("Preconfigured")
        preconfigured_layout = QFormLayout(self)
        self.preconfigured_combo = QComboBox(self)
        self.preconfigured_combo.addItem('None', userData=None)
        for config in CONFIGS:
            self.preconfigured_combo.addItem(config.descriptor,
                                             userData=config)
        self.preconfigured_combo.currentIndexChanged[int].connect(
            self.on_preconfigured)
        preconfigured_layout.addRow(QLabel("Load preset demo:"),
                                    self.preconfigured_combo)
        self.preconfigured_group.setLayout(preconfigured_layout)
        controlLayout.addWidget(self.preconfigured_group)

        # Data type settings
        self.demo_type_group = QGroupBox("Data type")
        data_type_layout = QFormLayout(self)
        self.data_type_combo = QComboBox(self)
        self.data_type_combo.addItem("Image is a data point",
                                     DemoType.LABELLED_DEMO)
        self.data_type_combo.addItem("Point and metadata", DemoType.POINT_DEMO)
        self.data_type_combo.addItem("Hyperspectral image",
                                     DemoType.HYPERSPECTRAL_DEMO)
        data_type_layout.addRow(QLabel("Visualization style:"),
                                self.data_type_combo)
        self.data_type_combo.currentIndexChanged[int].connect(
            self.on_demo_style)

        data_button = QPushButton("Data")
        data_button.clicked.connect(self.on_load)
        self.data_label = QLabel("<choose data .npy>")
        data_type_layout.addRow(data_button, self.data_label)

        self.xy_label = QLabel("Image size")
        self.label_x = QLabel("X:")
        self.label_y = QLabel("Y:")
        self.image_x = QLineEdit()
        self.image_x.setValidator(QIntValidator(1, 10000))
        self.image_y = QLineEdit()
        self.image_y.setValidator(QIntValidator(1, 10000))

        self.xy_container = QWidget()
        self.xy_layout = QHBoxLayout()
        self.xy_layout.setContentsMargins(0, 0, 0, 0)
        self.xy_layout.addWidget(self.label_x)
        self.xy_layout.addWidget(self.image_x)
        self.xy_layout.addWidget(self.label_y)
        self.xy_layout.addWidget(self.image_y)
        self.xy_container.setLayout(self.xy_layout)
        data_type_layout.addRow(self.xy_label, self.xy_container)

        self.label_button = QPushButton("Labels")
        self.label_button.clicked.connect(self.on_load_labels)
        self.label_label = QLabel("<optionally choose labels .npy>")
        data_type_layout.addRow(self.label_button, self.label_label)

        self.meta_button = QPushButton("Label/Color metadata")
        self.meta_button.clicked.connect(self.on_load_labelscolors)
        self.meta_label = QLabel("<optionally choose label/color .csv>")
        data_type_layout.addRow(self.meta_button, self.meta_label)
        self.demo_type_group.setLayout(data_type_layout)
        controlLayout.addWidget(self.demo_type_group)

        # Hsne settings
        self.hsne_group = QGroupBox("hSNE settings")
        self.hsne_form_layout = QFormLayout(self)

        hsne_button = QPushButton("Preload")
        hsne_button.clicked.connect(self.on_load_hsne)
        self.hsne_label = QLabel("<optionally choose existing .hsne>")
        self.hsne_form_layout.addRow(hsne_button, self.hsne_label)

        self.scale_spin = QSpinBox(self)
        self.scale_spin.setRange(1, 10)
        self.scale_spin.setValue(4)
        self.hsne_form_layout.addRow(QLabel("Scales:"), self.scale_spin)

        self.hsne_group.setLayout(self.hsne_form_layout)
        controlLayout.addWidget(self.hsne_group)

        # Embedding settings
        self.embedding_group = QGroupBox("Embedding settings")
        embed_form_layout = QFormLayout(self)
        self.iter_spin = QSpinBox(self)
        self.iter_spin.setRange(350, 1000)
        self.iter_spin.setSingleStep(5)
        self.iter_spin.setValue(500)
        embed_form_layout.addRow(QLabel("Iterations:"), self.iter_spin)
        self.embedding_group.setLayout(embed_form_layout)

        controlLayout.addWidget(self.embedding_group)

        clear_start_layout = QHBoxLayout()
        self.start_button = QPushButton("Start")
        self.start_button.clicked.connect(self.on_start)
        self.start_button.setDisabled(True)
        self.clear_button = QPushButton("Clear")
        self.clear_button.clicked.connect(self.on_clear)

        clear_start_layout.addWidget(self.start_button)
        clear_start_layout.addWidget(self.clear_button)

        controlLayout.addLayout(clear_start_layout)

        self.delete_button = QPushButton("Delete selected")
        self.delete_button.clicked.connect(self.on_delete)

        # Selection
        self.tree.clicked.connect(self.on_selected)

        # Layout
        main_layout = QGridLayout()
        # row, col, rowSpan, colSpan
        main_layout.addWidget(self.tree, 0, 0, 10, 5)
        main_layout.addLayout(controlLayout, 0, 5, 10, 3)

        main_layout.addWidget(self.delete_button, 11, 2, 1, 2)
        self.setLayout(main_layout)
        self.counter = 0
        self.event_timer = QTimer(self)
        self.event_timer.start(500)
        self.event_timer.timeout.connect(self.update_tree)

        # Dynamic UI matrix
        # According to DemoType True widgets are shown False widgets hidden
        self.ui_matrix = {
            DemoType.LABELLED_DEMO: {
                True: [
                    self.label_button, self.label_label, self.xy_container,
                    self.xy_label
                ],
                False: [self.meta_button, self.meta_label]
            },
            DemoType.POINT_DEMO: {
                True: [self.meta_button, self.meta_label],
                False: [
                    self.label_button, self.label_label, self.xy_container,
                    self.xy_label
                ]
            },
            DemoType.HYPERSPECTRAL_DEMO: {
                True: [self.xy_container, self.xy_label],
                False: [
                    self.label_button, self.label_label, self.meta_button,
                    self.meta_label
                ]
            }
        }

        self.show()
        self.on_demo_style(0)

    def set_analysis_model(self, analysis_model):
        # TODO empty event queue
        self.analysis_model = analysis_model
        self.top_scale = self.analysis_model.top_scale_id

    @property
    def iterations(self):
        return self.iter_spin.value()

    @property
    def scales(self):
        return self.scale_spin.value()

    @property
    def demo_type(self):
        return self.data_type_combo.currentData()

    @property
    def im_size_x(self):
        return int(self.image_x.text())

    @property
    def im_size_y(self):
        return int(self.image_y.text())

    @pyqtSlot(int)
    def on_preconfigured(self, index):
        config = self.preconfigured_combo.itemData(index)
        self.on_clear()
        if config is None:
            return
        if type(config).__name__ == "LabelledImage":
            self.data_type_combo.setCurrentIndex(0)
            self.name = config.data.data_file
            self.data_label.setText(config.data.data_file)
            self.label_name = config.data.label_file
            self.label_label.setText(config.data.label_file)
            if config.hsne.hsne_file != "":
                self.hsne_name = config.hsne.hsne_file
                self.__set_scale_from_hsne_file()
                self.scale_spin.setDisabled(True)
            elif config.hsne.scales > 0:
                self.scale_spin.setValue(config.hsne.scales)
            if config.image.dim_x > 0 and config.image.dim_y > 0:
                self.image_x.setText(str(config.image.dim_x))
                self.image_y.setText(str(config.image.dim_y))

        elif type(config).__name__ == "PointMeta":
            self.data_type_combo.setCurrentIndex(1)
            self.name = config.data.data_file
            self.data_label.setText(config.data.data_file)
            self.meta_name = config.data.meta_file
            self.meta_label.setText(config.data.meta_file)
            if config.hsne.hsne_file != "":
                self.hsne_name = config.hsne.hsne_file
                self.__set_scale_from_hsne_file()
                self.scale_spin.setDisabled(True)
            elif config.hsne.scales > 0:
                self.scale_spin.setValue(config.hsne.scales)

        elif type(config).__name__ == "HyperspectralImage":
            self.data_type_combo.setCurrentIndex(2)
            self.name = config.data.data_file
            self.data_label.setText(config.data.data_file)
            if config.hsne.hsne_file != "":
                self.hsne_name = config.hsne.hsne_file
                self.__set_scale_from_hsne_file()
                self.scale_spin.setDisabled(True)
            elif config.hsne.scales > 0:
                self.scale_spin.setValue(config.hsne.scales)
            if config.image.dim_x > 0 and config.image.dim_y > 0:
                self.image_x.setText(str(config.image.dim_x))
                self.image_y.setText(str(config.image.dim_y))

        self.start_button.setEnabled(True)

    @pyqtSlot(int)
    def on_demo_style(self, index):
        # set the visibility of the widgets according to the
        # type of demo being given
        for state in [False, True]:
            for widget in self.ui_matrix[self.demo_type][state]:
                widget.setVisible(state)

    @pyqtSlot()
    def on_load(self):
        workdir = os.path.dirname(os.path.abspath(__file__))
        result = QFileDialog.getOpenFileName(
            self,
            'Open a numpy file where each row is a data point and columns are dimensions',
            workdir, "Numpy files (*.npy)")
        if result[0]:
            self.hsne_label.setText("")
            self.hsne_name = None
            self.scale_spin.setEnabled(True)
            self.name = result[0]
            # print(f"Selected: {self.name}")
            self.start_button.setEnabled(True)
            if (self.demo_type == DemoType.LABELLED_DEMO
                    or self.demo_type == DemoType.HYPERSPECTRAL_DEMO):
                # partial data load (in memory) to read the shape
                the_data = np.load(self.name, mmap_mode='r')
                image_flat_size = the_data.shape[1]
                if self.demo_type == DemoType.HYPERSPECTRAL_DEMO:
                    image_flat_size = the_data.shape[0]
                xsize = int(math.sqrt(image_flat_size))
                ysize = int(image_flat_size / xsize)
                self.image_x.setText(str(xsize))
                self.image_y.setText(str(ysize))
            self.data_label.setText(str(Path(self.name).name))

    def __set_scale_from_hsne_file(self):
        scale_value = nptsne.HSne.read_num_scales(self.hsne_name)
        self.hsne_label.setText(str(Path(self.hsne_name).name))
        self.scale_spin.setValue(scale_value)
        self.scale_spin.setDisabled(True)

    @pyqtSlot()
    def on_load_hsne(self):
        workdir = os.path.dirname(os.path.abspath(__file__))
        result = QFileDialog.getOpenFileName(
            self, 'Open a pre-calculated hSNE analysis file .hsne', workdir,
            "hSNE files (*.hsne)")
        if result[0]:
            self.hsne_name = result[0]
            # print(f"Selected: {self.name}")
            self.__set_scale_from_hsne_file()

    @pyqtSlot()
    def on_load_labels(self):
        workdir = os.path.dirname(os.path.abspath(__file__))
        result = QFileDialog.getOpenFileName(
            self, 'Open a numpy file where each row is an integer label',
            workdir, "Numpy files (*.npy)")
        if result[0]:
            self.label_name = result[0]
            self.label_label.setText(Path(self.label_name).name)

    @pyqtSlot()
    def on_load_labelscolors(self):
        workdir = os.path.dirname(os.path.abspath(__file__))
        result = QFileDialog.getOpenFileName(
            self,
            'Open a CSV file with header where the columns pairs of Label, #COLOR_',
            workdir, "CSV files (*.csv)")

        if result[0]:
            self.meta_name = result[0]
            self.meta_label.setText(Path(self.meta_name).name)

    @pyqtSlot()
    def on_start(self):
        self.load_callback(self.name, self.label_name, self.meta_name,
                           self.hsne_name)

    @pyqtSlot()
    def on_selected(self):
        analysis_id = self._get_selected_id()
        if analysis_id:
            self.select_callback(int(analysis_id))

    @pyqtSlot()
    def on_delete(self):
        analysis_id = self._get_selected_id()
        if analysis_id:
            self.delete_callback([int(analysis_id)])

    @pyqtSlot()
    def on_clear(self):
        if not self.root_id is None:
            self.select_callback(int(self.root_id))
        self.clear()
        self.name = None
        self.data_label.setText("<choose data .npy>")
        self.label_name = None
        self.label_label.setText("<optionally choose labels .npy>")
        self.meta_name = None
        self.meta_label.setText("<optionally choose label/color .csv>")
        self.scale_spin.setValue(4)
        self.scale_spin.setEnabled(True)
        self.start_button.setDisabled(True)
        self.hsne_name = None
        self.hsne_label.setText("<optionally choose existing .hsne>")
        self.image_x.setText("")
        self.image_y.setText("")

    def _get_selected_id(self):
        index = self.tree.currentIndex()
        if index is None:
            return None
        return self.model.itemData(index.siblingAtColumn(self.ID))[0]

    def create_analysis_model(self, parent):
        model = QStandardItemModel(0, 3, parent)
        model.setHeaderData(self.ANALYSIS, Qt.Horizontal, "Analysis")
        model.setHeaderData(self.ID, Qt.Horizontal, "Id")
        model.setHeaderData(self.NUMPOINTS, Qt.Horizontal, "#Points")
        return model

    def add_test_analysis(self):
        parent_id = ModelGui.NO_PARENT_ID
        if self.counter > 0:
            parent_id = self.counter - 1
        self.add_analysis(self.counter, f"{self.counter} Blah blah blah",
                          parent_id, 150)
        self.counter = self.counter + 1

    def update_tree(self):
        # Update tree based on queued events
        while True:
            event = {}
            try:
                event = self.analysis_event_queue.get_nowait()
            except queue.Empty:
                break

            if event['event'] == AnalysisEvent.ADDED:
                self.add_analysis(event['id'], event['name'],
                                  event['parent_id'],
                                  event['number_of_points'])
                continue
            if event['event'] == AnalysisEvent.FINISHED:
                self.finish_analysis(event['id'], event['name'],
                                     event['image_buf'])
                continue
            if event['event'] == AnalysisEvent.REMOVED:
                self.remove_analysis(event['id'])

    def add_analysis(self, analysis_id, name, parent_id, numpoints):
        im = ImageQt.ImageQt(Image.new('RGB', self.thumb_size, (100, 0, 200)))
        item = QStandardItem(QIcon(QPixmap.fromImage(im)), name)
        # Need to persist the thumbnails otherwise the ImageQT will get garbage
        # collected along with the memory
        item.__thumb = im
        if parent_id == ModelGui.NO_PARENT_ID:
            #print("Adding root")
            self.clear()
            self.model.insertRow(0, [
                item,
                QStandardItem(str(analysis_id)),
                QStandardItem(str(numpoints))
            ])
            self.root_id = analysis_id
            self.id_item[analysis_id] = item
        else:
            #print("Adding child")
            parent = self.find_analysis_item(parent_id)
            if parent is not None:
                parent.appendRow([
                    item,
                    QStandardItem(str(analysis_id)),
                    QStandardItem(str(numpoints))
                ])
                self.id_item[analysis_id] = item
                self.tree.expand(parent.index())

    def remove_analysis(self, analysis_id):
        if analysis_id == self.root_id:
            self.clear()
            return
        item = self.find_analysis_item(analysis_id)
        if item:
            if item.parent:
                try:
                    item.parent().removeRow(item.row())
                except RuntimeError:
                    # TODO fix bug that means this is being deleted twice
                    pass

            del self.id_item[analysis_id]

    def finish_analysis(self, analysis_id, name, image_buf):
        print("finished ", analysis_id)
        img = PIL.Image.open(image_buf)
        thumbnail = img.resize(self.thumb_size, PIL.Image.ANTIALIAS)
        # thumbnail.show()
        im = ImageQt.ImageQt(thumbnail)
        item = self.find_analysis_item(analysis_id)

        item.setIcon(QIcon(QPixmap.fromImage(im)))
        item.__thumb = im

    def find_analysis_item(self, analysis_id):
        """ Get the item using the numeric analysis_id """
        return self.id_item.get(analysis_id, None)

    def clear(self):
        print('Clear model content')
        if self.model is not None:
            # print('Remove rows')
            self.model.removeRows(0, self.model.rowCount())
        # print('Reset bookkeeping')
        self.id_item = {}
        self.root_id = None
Esempio n. 16
0
class Win(QMainWindow):
	def __init__(self, options):
		super(Win, self).__init__()

		self.db = dbtag.Db()
		self.db.open(options.db)
		self.db.create_tables()
		self.rootPath = options.filespath

		self._init_widgets()
		self._init_more()

	def _init_widgets(self):
		bigsplitter = QSplitter(Qt.Horizontal, self)
		self.setCentralWidget(bigsplitter)

		leftsplitter = QSplitter(Qt.Vertical, self)

		self.tabWidget = QTabWidget(self)

		self.tagChooser = TagChooser(self.db)
		self.dirChooser = QTreeView(self)

		self.tabWidget.addTab(self.dirChooser, 'Dir')
		self.tabWidget.addTab(self.tagChooser, 'Tags')

		self.tagEditor = TagEditor(self.db)
		self.imageList = ImageList()

		leftsplitter.addWidget(self.tabWidget)
		leftsplitter.addWidget(self.tagEditor)

		bigsplitter.addWidget(leftsplitter)
		bigsplitter.addWidget(self.imageList)

		self.viewer = ImageViewer(self.db)

	def _init_more(self):
		self.setWindowTitle('Tags4')

		self.dirModel = QFileSystemModel()
		self.dirModel.setFilter(QDir.AllDirs | QDir.Drives | QDir.Hidden | QDir.NoDotAndDotDot)
		qidx = self.dirModel.setRootPath(self.rootPath)
		self.dirChooser.setModel(self.dirModel)
		self.dirChooser.setRootIndex(qidx)

		self.dirChooser.clicked.connect(self.browseSelectedDir)
		self.imageList.itemSelectionChanged.connect(self._editTagsItems)
		self.imageList.itemDoubleClicked.connect(self._spawnViewerItem)
		self.imageList.setSelectionMode(QAbstractItemView.ExtendedSelection)

		self.tabWidget.currentChanged.connect(self._tabSelected)
		self.tagChooser.changed.connect(self.browseSelectedTags)

	def editTags(self, path):
		self.tagEditor.setFile(path)

	def editTagsItems(self, paths):
		self.tagEditor.setFiles(paths)

	def spawnViewer(self, files, currentFile):
		self.viewer.spawn(files, currentFile)

	@Slot()
	def _editTagsItems(self):
		self.editTagsItems([qitem.getPath() for qitem in self.imageList.selectedItems()])

	@Slot(QListWidgetItem)
	def _spawnViewerItem(self, qitem):
		self.spawnViewer(self.imageList.getFiles(), qitem.getPath())

	@Slot()
	def browseSelectedDir(self):
		path = self.dirModel.filePath(self.dirChooser.currentIndex())
		if not path:
			return
		files = [os.path.join(path, f) for f in os.listdir(path)]
		files = filter(os.path.isfile, files)
		files.sort()
		self.imageList.setFiles(files)

	@Slot()
	def browseSelectedTags(self):
		self.imageList.setFiles(self.tagChooser.matchingFiles())

	@Slot(int)
	def _tabSelected(self, idx):
		if idx == 0:
			self.browseSelectedDir()
		else:
			self.browseSelectedTags()

	def browsePath(self, path):
		self.imageList.setFiles(os.path.join(path, f) for f in os.listdir(path))
Esempio n. 17
0
class _LocatorDialog(QDialog):
    """Locator widget and implementation
    """
    def __init__(self, parent, commandClasses):
        QDialog.__init__(self, parent)
        self._terminated = False
        self._commandClasses = commandClasses

        self._createUi()

        self._loadingTimer = QTimer(self)
        self._loadingTimer.setSingleShot(True)
        self._loadingTimer.setInterval(200)
        self._loadingTimer.timeout.connect(self._applyLoadingCompleter)

        self._completerLoaderThread = _CompleterLoaderThread(self)

        self.finished.connect(self._terminate)

        self._command = None
        self._updateCurrentCommand()

    def _createUi(self):
        self.setWindowTitle(core.project().path() or 'Locator')

        self.setLayout(QVBoxLayout())
        self.layout().setContentsMargins(0, 0, 0, 0)
        self.layout().setSpacing(1)

        biggerFont = self.font()
        biggerFont.setPointSizeF(biggerFont.pointSizeF() * 2)
        self.setFont(biggerFont)

        self._edit = _CompletableLineEdit(self)
        self._edit.updateCurrentCommand.connect(self._updateCurrentCommand)
        self._edit.enterPressed.connect(self._onEnterPressed)
        self._edit.installEventFilter(self)  # catch Up, Down
        self.layout().addWidget(self._edit)
        self.setFocusProxy(self._edit)

        self._table = QTreeView(self)
        self._table.setFont(biggerFont)
        self._model = _CompleterModel()
        self._table.setModel(self._model)
        self._table.setItemDelegate(HTMLDelegate(self._table))
        self._table.setRootIsDecorated(False)
        self._table.setHeaderHidden(True)
        self._table.clicked.connect(self._onItemClicked)
        self._table.setAlternatingRowColors(True)
        self._table.installEventFilter(
            self)  # catch focus and give to the edit
        self.layout().addWidget(self._table)

        width = QFontMetrics(self.font()).width('x' *
                                                64)  # width of 64 'x' letters
        self.resize(width, width * 0.62)

    def _terminate(self):
        if not self._terminated:
            if self._command is not None:
                self._command.terminate()
                self._command = None

            self._edit.terminate()

            self._completerLoaderThread.terminate()
            core.workspace().focusCurrentDocument()
            self._terminated = True

    def _updateCurrentCommand(self):
        """Try to parse line edit text and set current command
        """
        if self._terminated:
            return

        newCommand = self._parseCurrentCommand()

        if newCommand is not self._command:
            if self._command is not None:
                self._command.updateCompleter.disconnect(
                    self._updateCompletion)
                self._command.terminate()

            self._command = newCommand
            if self._command is not None:
                self._command.updateCompleter.connect(self._updateCompletion)

        self._updateCompletion()

    def _updateCompletion(self):
        """User edited text or moved cursor. Update inline and TreeView completion
        """
        if self._command is not None:
            completer = self._command.completer()

            if completer is not None and completer.mustBeLoaded:
                self._loadingTimer.start()
                self._completerLoaderThread.loadCompleter(
                    self._command, completer)
            else:
                self._applyCompleter(self._command, completer)
        else:
            self._applyCompleter(None, _HelpCompleter(self._commandClasses))

    def _applyLoadingCompleter(self):
        """Set 'Loading...' message
        """
        self._applyCompleter(None, StatusCompleter('<i>Loading...</i>'))

    def onCompleterLoaded(self, command, completer):
        """The method called from _CompleterLoaderThread when the completer is ready
        This code works in the GUI thread
        """
        self._applyCompleter(command, completer)

    def _applyCompleter(self, command, completer):
        """Apply completer. Called by _updateCompletion or by thread function when Completer is constructed
        """
        self._loadingTimer.stop()

        if command is not None:
            command.onCompleterLoaded(completer)

        if completer is None:
            completer = _HelpCompleter([command])

        if self._edit.cursorPosition() == len(
                self._edit.text()):  # if cursor at the end of text
            self._edit.setInlineCompletion(completer.inline())

        self._model.setCompleter(completer)
        if completer.columnCount() > 1:
            self._table.resizeColumnToContents(0)
            self._table.setColumnWidth(0,
                                       self._table.columnWidth(0) +
                                       20)  # 20 px spacing between columns

        selItem = completer.autoSelectItem()
        if selItem:
            index = self._model.createIndex(selItem[0], selItem[1])
            self._table.setCurrentIndex(index)

    def _onItemClicked(self, index):
        """Item in the TreeView has been clicked.
        Open file, if user selected it
        """
        if self._command is not None:
            fullText = self._model.completer.getFullText(index.row())
            if fullText is not None:
                self._command.onItemClicked(fullText)
                if self._tryExecCurrentCommand():
                    self.accept()
                    return
                else:
                    self._edit.setText(self._command.lineEditText())
                    self._updateCurrentCommand()

        self._edit.setFocus()

    def _onEnterPressed(self):
        """User pressed Enter or clicked item. Execute command, if possible
        """
        if self._table.currentIndex().isValid():
            self._onItemClicked(self._table.currentIndex())
        else:
            self._tryExecCurrentCommand()

    def _tryExecCurrentCommand(self):
        if self._command is not None and self._command.isReadyToExecute():
            self._command.execute()
            self.accept()
            return True
        else:
            return False

    def _chooseCommand(self, words):
        for cmd in self._commandClasses:
            if cmd.command == words[0]:
                return cmd, words[1:]

        isPath = words and (words[0].startswith('/')
                            or words[0].startswith('./')
                            or words[0].startswith('../')
                            or words[0].startswith('~/')
                            or words[0][1:3] == ':\\' or words[0][1:3] == ':/')
        isNumber = len(words) == 1 and all([c.isdigit() for c in words[0]])

        def matches(cmd):
            if isPath:
                return cmd.isDefaultPathCommand
            elif isNumber:
                return cmd.isDefaultNumericCommand
            else:
                return cmd.isDefaultCommand

        for cmd in self._commandClasses:
            if matches(cmd):
                return cmd, words

    def _parseCurrentCommand(self):
        """ Parse text and try to get (command, completable word index)
        Return None if failed to parse
        """
        # Split line
        text = self._edit.commandText()
        words = splitLine(text)
        if not words:
            return None

        # Find command
        cmdClass, args = self._chooseCommand(words)

        if isinstance(self._command, cmdClass):
            command = self._command
        else:
            command = cmdClass()

        # Try to make command object
        try:
            command.setArgs(args)
        except InvalidCmdArgs:
            return None
        else:
            return command

    def eventFilter(self, obj, event):
        if obj is self._edit:
            if event.type() == QEvent.KeyPress and \
               event.key() in (Qt.Key_Up, Qt.Key_Down, Qt.Key_PageUp, Qt.Key_PageDown):
                return self._table.event(event)
        elif obj is self._table:
            if event.type() == QEvent.FocusIn:
                self._edit.setFocus()
                return True

        return False
Esempio n. 18
0
class AddToProject(QDialog):
    """Dialog to let the user choose one of the folders from the opened proj"""
    def __init__(self, projects, parent=None):
        super(AddToProject, self).__init__(parent)
        # projects must be a list
        self._projects = projects
        self.setWindowTitle(translations.TR_ADD_FILE_TO_PROJECT)
        self.path_selected = ''
        vbox = QVBoxLayout(self)

        hbox = QHBoxLayout()
        self._list = QListWidget()
        for project in self._projects:
            self._list.addItem(project.name)
        self._list.setCurrentRow(0)
        self._tree = QTreeView()
        self._tree.setHeaderHidden(True)
        self._tree.setSelectionMode(QTreeView.SingleSelection)
        self._tree.setAnimated(True)
        self.load_tree(self._projects[0])
        hbox.addWidget(self._list)
        hbox.addWidget(self._tree)
        vbox.addLayout(hbox)

        hbox2 = QHBoxLayout()
        btn_add = QPushButton(translations.TR_ADD_HERE)
        btn_cancel = QPushButton(translations.TR_CANCEL)
        hbox2.addWidget(btn_cancel)
        hbox2.addWidget(btn_add)
        vbox.addLayout(hbox2)

        btn_add.clicked.connect(self._select_path)
        btn_cancel.clicked.connect(self.close)
        self._list.currentItemChanged.connect(self._project_changed)

    def _project_changed(self, item, previous):
        # FIXME, this is not being called, at least in osx
        for each_project in self._projects:
            if each_project.name == item.text():
                self.load_tree(each_project)

    def load_tree(self, project):
        """Load the tree view on the right based on the project selected."""
        qfsm = QFileSystemModel()
        qfsm.setRootPath(project.path)
        load_index = qfsm.index(qfsm.rootPath())
        qfsm.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot)
        qfsm.setNameFilterDisables(False)
        pext = ["*{0}".format(x) for x in project.extensions]
        qfsm.setNameFilters(pext)

        self._tree.setModel(qfsm)
        self._tree.setRootIndex(load_index)

        t_header = self._tree.header()
        t_header.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        t_header.setSectionResizeMode(0, QHeaderView.Stretch)
        t_header.setStretchLastSection(False)
        t_header.setSectionsClickable(True)

        self._tree.hideColumn(1)  # Size
        self._tree.hideColumn(2)  # Type
        self._tree.hideColumn(3)  # Modification date

        # FIXME: Changing the name column's title requires some magic
        # Please look at the project tree

    def _select_path(self):
        """Set path_selected to the folder selected in the tree."""
        path = self._tree.model().filePath(self._tree.currentIndex())
        if path:
            self.path_selected = path
            self.close()
Esempio n. 19
0
class Dialog_ImageFolder():
    def __init__(self, parent, title, init_path):
        self.w = QDialog(parent)

        self.parent = parent
        self.left = 300
        self.top = 300
        self.width = 600
        self.height = 400
        self.title = title

        self.dirModel = QFileSystemModel()
        self.dirModel.setRootPath(init_path)
        self.dirModel.setFilter(QDir.NoDotAndDotDot | QDir.AllDirs)
        self.treeview = QTreeView()
        self.treeview.setModel(self.dirModel)
        self.treeview.setRootIndex(self.dirModel.index(""))
        self.treeview.clicked.connect(self.on_clicked)
        #--- Hide All Header Sections Except First ----
        header = self.treeview.header()
        for sec in range(1, header.count()):
            header.setSectionHidden(sec, True)
        #--- ---- ---- ---- ---- ---- ---- ---- ---- --

        focus_index = self.dirModel.index(init_path)
        self.treeview.setCurrentIndex(focus_index)
        self.current_row_changed()

        self.listview = QListView()
        self.listview.setViewMode(QListView.IconMode)
        self.listview.setIconSize(QSize(192, 192))

        targetfiles1 = glob.glob(os.path.join(init_path, '*.png'))
        targetfiles2 = glob.glob(os.path.join(init_path, '*.tif'))
        targetfiles3 = glob.glob(os.path.join(init_path, '*.tiff'))
        targetfiles = targetfiles1 + targetfiles2 + targetfiles3
        lm = _MyListModel(targetfiles, self.parent)
        self.listview.setModel(lm)

        self.sub_layout = QHBoxLayout()
        self.sub_layout.addWidget(self.treeview)
        self.sub_layout.addWidget(self.listview)

        self.buttonBox = QDialogButtonBox(QDialogButtonBox.Open
                                          | QDialogButtonBox.Cancel)
        self.buttonBox.accepted.connect(self.accept)
        self.buttonBox.rejected.connect(self.reject)

        self.main_layout = QVBoxLayout()
        self.main_layout.addLayout(self.sub_layout)
        self.main_layout.addWidget(self.buttonBox)

        self.w.setGeometry(self.left, self.top, self.width, self.height)
        self.w.setWindowTitle(self.title)
        self.w.setWindowIcon(QIcon(os.path.join(icon_dir, 'Mojo2_16.png')))
        self.w.setLayout(self.main_layout)

    def current_row_changed(self):
        index = self.treeview.currentIndex()
        self.treeview.scrollTo(index, QAbstractItemView.EnsureVisible)
        self.treeview.resizeColumnToContents(0)

    def on_clicked(self, index):
        path = self.dirModel.fileInfo(index).absoluteFilePath()

        targetfiles1 = glob.glob(os.path.join(path, '*.png'))
        targetfiles2 = glob.glob(os.path.join(path, '*.tif'))
        targetfiles3 = glob.glob(os.path.join(path, '*.tiff'))
        targetfiles = targetfiles1 + targetfiles2 + targetfiles3

        lm = _MyListModel(targetfiles, self.parent)
        self.listview.setModel(lm)

    def accept(self):
        index = self.treeview.currentIndex()
        self.newdir = self.dirModel.filePath(index)
        self.w.done(1)

    def reject(self):
        self.w.done(0)

    def GetValue(self):
        index = self.treeview.currentIndex()
        self.newdir = self.dirModel.filePath(index)
        return self.newdir
Esempio n. 20
0
File: dock.py Progetto: gpa14/enki
class NavigatorDock(DockWidget):

    def __init__(self):
        DockWidget.__init__(self, core.mainWindow(), '&Navigator', QIcon(':/enkiicons/goto.png'), "Alt+N")

        self._tags = []

        self._tree = QTreeView(self)
        self._tree.installEventFilter(self)
        self._tree.setHeaderHidden(True)
        self.setFocusProxy(self._tree)

        self._filterEdit = LineEdit(self)
        self._filterEdit.setClearButtonVisible(True)
        self._filterEdit.textEdited.connect(self._applyFilter)
        self._filterEdit.clearButtonClicked.connect(self._applyFilter)
        self._filterEdit.clearButtonClicked.connect(self._tree.setFocus)
        self._filterEdit.clearButtonClicked.connect(self._hideFilter)
        self._filterEdit.installEventFilter(self)

        self._displayWidget = QWidget(self)
        layout = QVBoxLayout(self._displayWidget)
        layout.addWidget(self._tree)
        layout.addWidget(self._filterEdit)
        layout.setContentsMargins(0, 0, 0, 0)

        self.setWidget(self._displayWidget)

        self._tagModel = _TagModel(self._tree)
        self._tagModel.jumpToTagDone.connect(self._hideFilter)

        self._tree.setModel(self._tagModel)
        self._tree.activated.connect(self._tagModel.onActivated)
        self._tree.clicked.connect(self._tagModel.onActivated)
        self._tagModel.modelAboutToBeReset.connect(self._onModelAboutToBeReset)
        self._tagModel.modelReset.connect(self._onModelReset)
        self._currentTagPath = None

        self._errorLabel = None

        self._installed = False

    def term(self):
        self._tagModel.term()

    def install(self):
        if not self._installed:
            core.mainWindow().addDockWidget(Qt.RightDockWidgetArea, self)
            core.actionManager().addAction("mView/aNavigator", self.showAction())
            self._installed = True

    def remove(self):
        if self._installed:
            core.mainWindow().removeDockWidget(self)
            core.actionManager().removeAction("mView/aNavigator")
            self.hide()
            self._installed = False

    def setTags(self, tags):
        self._tags = tags
        self._setFilteredTags(tags)
        self._hideFilter()

        if self.widget() is not self._displayWidget:
            self.setWidget(self._displayWidget)
            self._displayWidget.show()
        if self._errorLabel is not None:
            self._errorLabel.hide()

    def _setFilteredTags(self, tags):
        self._tagModel.setTags(tags)

    def onError(self, error):
        self._displayWidget.hide()
        if self._errorLabel is None:
            self._errorLabel = QLabel(self)
            self._errorLabel.setWordWrap(True)

        self._errorLabel.setText(error)

        if not self.widget() is self._errorLabel:
            self.setWidget(self._errorLabel)
            self._errorLabel.show()
            self._displayWidget.hide()

    def _onModelAboutToBeReset(self):
        currIndex = self._tree.currentIndex()
        self._currentTagPath = self._tagModel.tagPathForIndex(currIndex) if currIndex.isValid() else None

    def _onModelReset(self):
        self._tree.expandAll()

        # restore current item
        if self._currentTagPath is not None:
            index = self._tagModel.indexForTagPath(self._currentTagPath)
            if index.isValid():
                self._tree.setCurrentIndex(index)

    def eventFilter(self, object_, event):
        if object_ is self._tree:
            if event.type() == QEvent.KeyPress:
                if event.key() == Qt.Key_Backspace:
                    if event.modifiers() == Qt.ControlModifier:
                        self._onTreeCtrlBackspace()
                    else:
                        self._onTreeBackspace()
                    return True
                elif event.text() and \
                        (event.text().isalnum() or event.text() == '_'):
                    self._onTreeTextTyped(event.text())
                    return True
        elif object_ is self._filterEdit:
            if event.type() == QEvent.KeyPress:
                if event.key() in (Qt.Key_Up, Qt.Key_Down):
                    self._tree.setFocus()
                    self._tree.event(event)
                    return True
                elif event.key() in (Qt.Key_Enter, Qt.Key_Return):
                    currIndex = self._tree.currentIndex()
                    if currIndex.isValid():
                        self._tagModel.onActivated(currIndex)

        return DockWidget.eventFilter(self, object_, event)

    def _hideFilter(self):
        hadText = self._filterEdit.text() != ''
        self._filterEdit.clear()
        self._filterEdit.hide()
        if hadText:
            self._applyFilter()

    def _applyFilter(self):
        text = self._filterEdit.text()
        if text:
            if not text.startswith('*'):
                text = '*' + text
            if not text.endswith('*'):
                text = text + '*'

            wildcard = text.lower()

            filteredTags = _filterTags(wildcard, self._tags)

            self._setFilteredTags(filteredTags)
            self._tree.expandAll()
            if filteredTags:
                firstMatchingTag = _findFirstMatching(wildcard, filteredTags)
                path = _tagPath(firstMatchingTag)
                index = self._tagModel.indexForTagPath(path)
                self._tree.setCurrentIndex(index)
        else:
            self._setFilteredTags(self._tags)

        if text:
            self._filterEdit.show()
        elif not self._filterEdit.hasFocus():
            self._hideFilter()

    def _onTreeTextTyped(self, text):
        self._filterEdit.setText(self._filterEdit.text() + text)
        self._applyFilter()

    def _onTreeBackspace(self):
        text = self._filterEdit.text()
        if text:
            self._filterEdit.setText(text[:-1])
            self._applyFilter()

    def _onTreeCtrlBackspace(self):
        self._hideFilter()
        self._applyFilter()
Esempio n. 21
0
class Widget(QWidget):
    def __init__(self, panel):
        super(Widget, self).__init__(panel)

        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.setSpacing(0)

        self.searchEntry = SearchLineEdit()
        self.treeView = QTreeView(contextMenuPolicy=Qt.CustomContextMenu)
        self.textView = QTextBrowser()

        applyButton = QToolButton(autoRaise=True)
        editButton = QToolButton(autoRaise=True)
        addButton = QToolButton(autoRaise=True)
        self.menuButton = QPushButton(flat=True)
        menu = QMenu(self.menuButton)
        self.menuButton.setMenu(menu)

        splitter = QSplitter(Qt.Vertical)
        top = QHBoxLayout()
        layout.addLayout(top)
        splitter.addWidget(self.treeView)
        splitter.addWidget(self.textView)
        layout.addWidget(splitter)
        splitter.setSizes([200, 100])
        splitter.setCollapsible(0, False)

        top.addWidget(self.searchEntry)
        top.addWidget(applyButton)
        top.addSpacing(10)
        top.addWidget(addButton)
        top.addWidget(editButton)
        top.addWidget(self.menuButton)

        # action generator for actions added to search entry
        def act(slot, icon=None):
            a = QAction(self, triggered=slot)
            self.addAction(a)
            a.setShortcutContext(Qt.WidgetWithChildrenShortcut)
            icon and a.setIcon(icons.get(icon))
            return a

        # hide if ESC pressed in lineedit
        a = act(self.slotEscapePressed)
        a.setShortcut(QKeySequence(Qt.Key_Escape))

        # import action
        a = self.importAction = act(self.slotImport, 'document-open')
        menu.addAction(a)

        # export action
        a = self.exportAction = act(self.slotExport, 'document-save-as')
        menu.addAction(a)

        # apply button
        a = self.applyAction = act(self.slotApply, 'edit-paste')
        applyButton.setDefaultAction(a)
        menu.addSeparator()
        menu.addAction(a)

        # add button
        a = self.addAction_ = act(self.slotAdd, 'list-add')
        a.setShortcut(QKeySequence(Qt.Key_Insert))
        addButton.setDefaultAction(a)
        menu.addSeparator()
        menu.addAction(a)

        # edit button
        a = self.editAction = act(self.slotEdit, 'document-edit')
        a.setShortcut(QKeySequence(Qt.Key_F2))
        editButton.setDefaultAction(a)
        menu.addAction(a)

        # set shortcut action
        a = self.shortcutAction = act(
            self.slotShortcut, 'preferences-desktop-keyboard-shortcuts')
        menu.addAction(a)

        # delete action
        a = self.deleteAction = act(self.slotDelete, 'list-remove')
        a.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Delete))
        menu.addAction(a)

        # restore action
        a = self.restoreAction = act(self.slotRestore)
        menu.addSeparator()
        menu.addAction(a)

        # help button
        a = self.helpAction = act(self.slotHelp, 'help-contents')
        menu.addSeparator()
        menu.addAction(a)

        self.treeView.setSelectionBehavior(QTreeView.SelectRows)
        self.treeView.setSelectionMode(QTreeView.ExtendedSelection)
        self.treeView.setRootIsDecorated(False)
        self.treeView.setAllColumnsShowFocus(True)
        self.treeView.setModel(model.model())
        self.treeView.setCurrentIndex(QModelIndex())

        # signals
        self.searchEntry.returnPressed.connect(self.slotReturnPressed)
        self.searchEntry.textChanged.connect(self.updateFilter)
        self.treeView.doubleClicked.connect(self.slotDoubleClicked)
        self.treeView.customContextMenuRequested.connect(self.showContextMenu)
        self.treeView.selectionModel().currentChanged.connect(self.updateText)
        self.treeView.model().dataChanged.connect(self.updateFilter)

        # highlight text
        self.highlighter = highlight.Highlighter(self.textView.document())

        # complete on snippet variables
        self.searchEntry.setCompleter(
            QCompleter([
                ':icon', ':indent', ':menu', ':name', ':python', ':selection',
                ':set', ':symbol', ':template', ':template-run'
            ], self.searchEntry))
        self.readSettings()
        app.settingsChanged.connect(self.readSettings)
        app.translateUI(self)
        self.updateColumnSizes()
        self.setAcceptDrops(True)

    def dropEvent(self, ev):
        if not ev.source() and ev.mimeData().hasUrls():
            filename = ev.mimeData().urls()[0].toLocalFile()
            if filename:
                ev.accept()
                from . import import_export
                import_export.load(filename, self)

    def dragEnterEvent(self, ev):
        if not ev.source() and ev.mimeData().hasUrls():
            ev.accept()

    def translateUI(self):
        try:
            self.searchEntry.setPlaceholderText(_("Search..."))
        except AttributeError:
            pass  # not in Qt 4.6
        shortcut = lambda a: a.shortcut().toString(QKeySequence.NativeText)
        self.menuButton.setText(_("&Menu"))
        self.addAction_.setText(_("&Add..."))
        self.addAction_.setToolTip(
            _("Add a new snippet. ({key})").format(
                key=shortcut(self.addAction_)))
        self.editAction.setText(_("&Edit..."))
        self.editAction.setToolTip(
            _("Edit the current snippet. ({key})").format(
                key=shortcut(self.editAction)))
        self.shortcutAction.setText(_("Configure Keyboard &Shortcut..."))
        self.deleteAction.setText(_("&Remove"))
        self.deleteAction.setToolTip(_("Remove the selected snippets."))
        self.applyAction.setText(_("A&pply"))
        self.applyAction.setToolTip(_("Apply the current snippet."))
        self.importAction.setText(_("&Import..."))
        self.importAction.setToolTip(_("Import snippets from a file."))
        self.exportAction.setText(_("E&xport..."))
        self.exportAction.setToolTip(_("Export snippets to a file."))
        self.restoreAction.setText(_("Restore &Built-in Snippets..."))
        self.restoreAction.setToolTip(
            _("Restore deleted or changed built-in snippets."))
        self.helpAction.setText(_("&Help"))
        self.searchEntry.setToolTip(
            _("Enter text to search in the snippets list.\n"
              "See \"What's This\" for more information."))
        self.searchEntry.setWhatsThis(''.join(
            map("<p>{0}</p>\n".format, (
                _("Enter text to search in the snippets list, and "
                  "press Enter to apply the currently selected snippet."),
                _("If the search text fully matches the value of the '{name}' variable "
                  "of a snippet, that snippet is selected.").format(
                      name="name"),
                _("If the search text starts with a colon ':', the rest of the "
                  "search text filters snippets that define the given variable. "
                  "After a space a value can also be entered, snippets will then "
                  "match if the value of the given variable contains the text after "
                  "the space."),
                _("E.g. entering {menu} will show all snippets that are displayed "
                  "in the insert menu.").format(menu="<code>:menu</code>"),
            ))))

    def sizeHint(self):
        return self.parent().mainwindow().size() / 4

    def readSettings(self):
        data = textformats.formatData('editor')
        self.textView.setFont(data.font)
        self.textView.setPalette(data.palette())

    def showContextMenu(self, pos):
        """Called when the user right-clicks the tree view."""
        self.menuButton.menu().popup(self.treeView.viewport().mapToGlobal(pos))

    def slotReturnPressed(self):
        """Called when the user presses Return in the search entry. Applies current snippet."""
        name = self.currentSnippet()
        if name:
            view = self.parent().mainwindow().currentView()
            insert.insert(name, view)
            self.parent().hide()  # make configurable?
            view.setFocus()

    def slotEscapePressed(self):
        """Called when the user presses ESC in the search entry. Hides the panel."""
        self.parent().hide()
        self.parent().mainwindow().currentView().setFocus()

    def slotDoubleClicked(self, index):
        name = self.treeView.model().name(index)
        view = self.parent().mainwindow().currentView()
        insert.insert(name, view)

    def slotAdd(self):
        """Called when the user wants to add a new snippet."""
        edit.Edit(self, None)

    def slotEdit(self):
        """Called when the user wants to edit a snippet."""
        name = self.currentSnippet()
        if name:
            edit.Edit(self, name)

    def slotShortcut(self):
        """Called when the user selects the Configure Shortcut action."""
        from widgets import shortcuteditdialog
        name = self.currentSnippet()
        if name:
            collection = self.parent().snippetActions
            action = actions.action(name, None, collection)
            default = collection.defaults().get(name)
            mgr = actioncollectionmanager.manager(self.parent().mainwindow())
            cb = mgr.findShortcutConflict
            dlg = shortcuteditdialog.ShortcutEditDialog(
                self, cb, (collection, name))

            if dlg.editAction(action, default):
                mgr.removeShortcuts(action.shortcuts())
                collection.setShortcuts(name, action.shortcuts())
                self.treeView.update()

    def slotDelete(self):
        """Called when the user wants to delete the selected rows."""
        rows = sorted(set(i.row() for i in self.treeView.selectedIndexes()),
                      reverse=True)
        if rows:
            for row in rows:
                name = self.treeView.model().names()[row]
                self.parent().snippetActions.setShortcuts(name, [])
                self.treeView.model().removeRow(row)
            self.updateFilter()

    def slotApply(self):
        """Called when the user clicks the apply button. Applies current snippet."""
        name = self.currentSnippet()
        if name:
            view = self.parent().mainwindow().currentView()
            insert.insert(name, view)

    def slotImport(self):
        """Called when the user activates the import action."""
        filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"),
                                                  _("All Files"))
        caption = app.caption(_("dialog title", "Import Snippets"))
        filename = None
        filename = QFileDialog.getOpenFileName(self, caption, filename,
                                               filetypes)[0]
        if filename:
            from . import import_export
            import_export.load(filename, self)

    def slotExport(self):
        """Called when the user activates the export action."""
        allrows = [
            row for row in range(model.model().rowCount())
            if not self.treeView.isRowHidden(row, QModelIndex())
        ]
        selectedrows = [
            i.row() for i in self.treeView.selectedIndexes()
            if i.column() == 0 and i.row() in allrows
        ]
        names = self.treeView.model().names()
        names = [names[row] for row in selectedrows or allrows]

        filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"),
                                                  _("All Files"))
        n = len(names)
        caption = app.caption(
            _("dialog title", "Export {num} Snippet", "Export {num} Snippets",
              n).format(num=n))
        filename = QFileDialog.getSaveFileName(self, caption, None,
                                               filetypes)[0]
        if filename:
            from . import import_export
            try:
                import_export.save(names, filename)
            except (IOError, OSError) as e:
                QMessageBox.critical(
                    self, _("Error"),
                    _("Can't write to destination:\n\n{url}\n\n{error}").
                    format(url=filename, error=e.strerror))

    def slotRestore(self):
        """Called when the user activates the Restore action."""
        from . import restore
        dlg = restore.RestoreDialog(self)
        dlg.setWindowModality(Qt.WindowModal)
        dlg.populate()
        dlg.show()
        dlg.finished.connect(dlg.deleteLater)

    def slotHelp(self):
        """Called when the user clicks the small help button."""
        userguide.show("snippets")

    def currentSnippet(self):
        """Returns the name of the current snippet if it is visible."""
        row = self.treeView.currentIndex().row()
        if row != -1 and not self.treeView.isRowHidden(row, QModelIndex()):
            return self.treeView.model().names()[row]

    def updateFilter(self):
        """Called when the text in the entry changes, updates search results."""
        text = self.searchEntry.text()
        ltext = text.lower()
        filterVars = text.startswith(':')
        if filterVars:
            try:
                fvar, fval = text[1:].split(None, 1)
                fhide = lambda v: v.get(fvar) in (True, None
                                                  ) or fval not in v.get(fvar)
            except ValueError:
                fvar = text[1:].strip()
                fhide = lambda v: not v.get(fvar)
        for row in range(self.treeView.model().rowCount()):
            name = self.treeView.model().names()[row]
            nameid = snippets.get(name).variables.get('name', '')
            if filterVars:
                hide = fhide(snippets.get(name).variables)
            elif nameid == text:
                i = self.treeView.model().createIndex(row, 0)
                self.treeView.selectionModel().setCurrentIndex(
                    i, QItemSelectionModel.SelectCurrent
                    | QItemSelectionModel.Rows)
                hide = False
            elif nameid.lower().startswith(ltext):
                hide = False
            elif ltext in snippets.title(name).lower():
                hide = False
            else:
                hide = True
            self.treeView.setRowHidden(row, QModelIndex(), hide)
        self.updateText()

    def updateText(self):
        """Called when the current snippet changes."""
        name = self.currentSnippet()
        self.textView.clear()
        if name:
            s = snippets.get(name)
            self.highlighter.setPython('python' in s.variables)
            self.textView.setPlainText(s.text)

    def updateColumnSizes(self):
        self.treeView.resizeColumnToContents(0)
        self.treeView.resizeColumnToContents(1)
Esempio n. 22
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))