Exemple #1
0
class Torrents2Tab(QWidget):
    newtorrents = Signal(int)

    def __init__(self):
        QWidget.__init__(self)
        layout = QVBoxLayout(self)
        self.splitter = QSplitter(self)
        self.list = QTreeView(self)
        self.list.setSortingEnabled(True)
        self.model = NewTorrentModel()
        proxy = QSortFilterProxyModel()
        proxy.setSourceModel(self.model)
        self.list.setModel(proxy)
        self.splitter.addWidget(self.list)
        self.t = QTableWidget(0, 4, self)
        self.splitter.addWidget(self.t)
        layout.addWidget(self.splitter)
        self.setLayout(layout)
        self.ds = DataSource()
        self.worker = UpdateTorrentWorker()
        self.worker_thread = QThread()
        self.worker_thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.worker_thread.quit)
        self.worker.moveToThread(self.worker_thread)
        self.worker_thread.start()
        self.worker.processed.connect(self.processed)

    def finish(self):
        self.worker.finish()
        self.worker_thread.quit()
        self.worker_thread.wait()

    def processed(self, topic):
        self.model.add_topic(topic['published'])
Exemple #2
0
class LeftMenuWidget(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        layout = QVBoxLayout(self)
        self.splitter = QSplitter(self)
        self.tree = QTreeView(self)
        self.tree.setSortingEnabled(True)
        self.tree.setModel(ForumModel())
        self.splitter.addWidget(self.tree)
        layout.addWidget(self.splitter)
        self.setLayout(layout)
Exemple #3
0
    def __initFileTreeView__(self, model):
        treeView = QTreeView()
        treeView.setIndentation(20)
        treeView.setSortingEnabled(True)
        treeView.setMinimumWidth(100)
        treeView.setModel(model)
        treeView.setRootIndex(model.index(r'C:'))

        for i in range(1, 5):
            treeView.hideColumn(i)

        return treeView
Exemple #4
0
class Snippets(QDialog):

    def __init__(self, parent=None):
        super(Snippets, self).__init__(parent)
        # Create widgets
        self.setWindowModality(Qt.NonModal)
        self.title = QLabel(self.tr("Snippet Editor"))
        self.saveButton = QPushButton(self.tr("Save"))
        self.revertButton = QPushButton(self.tr("Revert"))
        self.clearHotkeyButton = QPushButton(self.tr("Clear Hotkey"))
        self.setWindowTitle(self.title.text())
        self.newFolderButton = QPushButton("New Folder")
        self.deleteSnippetButton = QPushButton("Delete")
        self.newSnippetButton = QPushButton("New Snippet")
        self.edit = QPlainTextEdit()
        self.resetting = False
        self.columns = 3

        self.keySequenceEdit = QKeySequenceEdit(self)
        self.currentHotkey = QKeySequence()
        self.currentHotkeyLabel = QLabel("")
        self.currentFileLabel = QLabel()
        self.currentFile = ""
        self.snippetDescription = QLineEdit()
        self.snippetEditsPending = False

        self.clearSelection()

        #Set Editbox Size
        font = getMonospaceFont(self)
        self.edit.setFont(font)
        font = QFontMetrics(font)
        self.edit.setTabStopWidth(4 * font.width(' ')); #TODO, replace with settings API

        #Files
        self.files = QFileSystemModel()
        self.files.setRootPath(snippetPath)
        self.files.setNameFilters(["*.py"])

        #Tree
        self.tree = QTreeView()
        self.tree.setModel(self.files)
        self.tree.setSortingEnabled(True)
        self.tree.hideColumn(2)
        self.tree.sortByColumn(0, Qt.AscendingOrder)
        self.tree.setRootIndex(self.files.index(snippetPath))
        for x in range(self.columns):
            #self.tree.resizeColumnToContents(x)
            self.tree.header().setSectionResizeMode(x, QHeaderView.ResizeToContents) 
        treeLayout = QVBoxLayout()
        treeLayout.addWidget(self.tree)
        treeButtons = QHBoxLayout()
        treeButtons.addWidget(self.newFolderButton)
        treeButtons.addWidget(self.newSnippetButton)
        treeButtons.addWidget(self.deleteSnippetButton)
        treeLayout.addLayout(treeButtons)
        treeWidget = QWidget()
        treeWidget.setLayout(treeLayout)

        # Create layout and add widgets
        buttons = QHBoxLayout()
        buttons.addWidget(self.clearHotkeyButton)
        buttons.addWidget(self.keySequenceEdit)
        buttons.addWidget(self.currentHotkeyLabel)
        buttons.addWidget(self.revertButton)
        buttons.addWidget(self.saveButton)

        description = QHBoxLayout()
        description.addWidget(QLabel(self.tr("Description: ")))
        description.addWidget(self.snippetDescription)

        vlayoutWidget = QWidget()
        vlayout = QVBoxLayout()
        vlayout.addWidget(self.currentFileLabel)
        vlayout.addWidget(self.edit)
        vlayout.addLayout(description)
        vlayout.addLayout(buttons)
        vlayoutWidget.setLayout(vlayout)

        hsplitter = QSplitter()
        hsplitter.addWidget(treeWidget)
        hsplitter.addWidget(vlayoutWidget)

        hlayout = QHBoxLayout()
        hlayout.addWidget(hsplitter)

        self.showNormal() #Fixes bug that maximized windows are "stuck"
        self.settings = QSettings("Vector 35", "Snippet Editor")
        if self.settings.contains("ui/snippeteditor/geometry"):
            self.restoreGeometry(self.settings.value("ui/snippeteditor/geometry"))
        else:
            self.edit.setMinimumWidth(80 * font.averageCharWidth())
            self.edit.setMinimumHeight(30 * font.lineSpacing())

        # Set dialog layout
        self.setLayout(hlayout)

        # Add signals
        self.saveButton.clicked.connect(self.save)
        self.revertButton.clicked.connect(self.loadSnippet)
        self.clearHotkeyButton.clicked.connect(self.clearHotkey)
        self.tree.selectionModel().selectionChanged.connect(self.selectFile)
        self.newSnippetButton.clicked.connect(self.newFileDialog)
        self.deleteSnippetButton.clicked.connect(self.deleteSnippet)
        self.newFolderButton.clicked.connect(self.newFolder)

    def registerAllSnippets(self):
        for action in list(filter(lambda x: x.startswith("Snippet\\"), UIAction.getAllRegisteredActions())):
            UIActionHandler.globalActions().unbindAction(action)
            UIAction.unregisterAction(action)

        for snippet in includeWalk(snippetPath, ".py"):
            (snippetDescription, snippetKey, snippetCode) = loadSnippetFromFile(snippet)
            if not snippetDescription:
                actionText = "Snippet\\" + snippet
            else:
                actionText = "Snippet\\" + snippetDescription
            UIAction.registerAction(actionText, snippetKey)
            UIActionHandler.globalActions().bindAction(actionText, UIAction(makeSnippetFunction(snippetCode)))

    def clearSelection(self):
        self.keySequenceEdit.clear()
        self.currentHotkey = QKeySequence()
        self.currentHotkeyLabel.setText("")
        self.currentFileLabel.setText("")
        self.snippetDescription.setText("")
        self.edit.setPlainText("")

    def reject(self):
        self.settings.setValue("ui/snippeteditor/geometry", self.saveGeometry())

        if self.snippetChanged():
            question = QMessageBox.question(self, self.tr("Discard"), self.tr("You have unsaved changes, quit anyway?"))
            if question != QMessageBox.StandardButton.Yes:
                return
        self.accept()

    def newFolder(self):
        (folderName, ok) = QInputDialog.getText(self, self.tr("Folder Name"), self.tr("Folder Name: "))
        if ok and folderName:
            index = self.tree.selectionModel().currentIndex()
            selection = self.files.filePath(index)
            if QFileInfo(selection).isDir():
                QDir(selection).mkdir(folderName)
            else:
                QDir(snippetPath).mkdir(folderName)    

    def selectFile(self, new, old):
        if (self.resetting):
            self.resetting = False
            return
        newSelection = self.files.filePath(new.indexes()[0])
        if QFileInfo(newSelection).isDir():
            self.clearSelection()
            return

        if old.length() > 0:
            oldSelection = self.files.filePath(old.indexes()[0])
            if not QFileInfo(oldSelection).isDir() and self.snippetChanged():
                question = QMessageBox.question(self, self.tr("Discard"), self.tr("Snippet changed. Discard changes?"))
                if question != QMessageBox.StandardButton.Yes:
                    self.resetting = True
                    self.tree.selectionModel().select(old, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
                    return False

        self.currentFile = newSelection
        self.loadSnippet()

    def loadSnippet(self):
        self.currentFileLabel.setText(QFileInfo(self.currentFile).baseName())
        log_debug("Loading %s as a snippet." % self.currentFile)
        (snippetDescription, snippetKey, snippetCode) = loadSnippetFromFile(self.currentFile)
        self.snippetDescription.setText(snippetDescription) if snippetDescription else self.snippetDescription.setText("")
        self.keySequenceEdit.setKeySequence(snippetKey[0]) if len(snippetKey) != 0 else self.keySequenceEdit.setKeySequence(QKeySequence(""))
        self.edit.setPlainText(snippetCode) if snippetCode else self.edit.setPlainText("")

    def newFileDialog(self):
        (snippetName, ok) = QInputDialog.getText(self, self.tr("Snippet Name"), self.tr("Snippet Name: "))
        if ok and snippetName:
            if not snippetName.endswith(".py"):
                snippetName += ".py"
            index = self.tree.selectionModel().currentIndex()
            selection = self.files.filePath(index)
            if QFileInfo(selection).isDir():
                open(os.path.join(selection, snippetName), "w").close()
            else:
                open(os.path.join(snippetPath, snippetName), "w").close()
            log_debug("Snippet %s created." % snippetName)

    def deleteSnippet(self):
        selection = self.tree.selectedIndexes()[::self.columns][0] #treeview returns each selected element in the row
        snippetName = self.files.fileName(selection)
        question = QMessageBox.question(self, self.tr("Confirm"), self.tr("Confirm deletion: ") + snippetName)
        if (question == QMessageBox.StandardButton.Yes):
            log_debug("Deleting snippet %s." % snippetName)
            self.clearSelection()
            self.files.remove(selection)

    def snippetChanged(self):
        if (self.currentFile == "" or QFileInfo(self.currentFile).isDir()):
            return False
        (snippetDescription, snippetKey, snippetCode) = loadSnippetFromFile(self.currentFile)
        if (not snippetCode):
            return False
        if len(snippetKey) == 0 and not self.keySequenceEdit.keySequence().isEmpty():
            return True
        if len(snippetKey) != 0 and snippetKey[0] != self.keySequenceEdit.keySequence():
            return True
        return self.edit.toPlainText() != snippetCode or \
               self.snippetDescription.text() != snippetDescription

    def save(self):
        log_debug("Saving snippet %s" % self.currentFile)
        outputSnippet = open(self.currentFile, "w")
        outputSnippet.write("#" + self.snippetDescription.text() + "\n")
        outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n")
        outputSnippet.write(self.edit.toPlainText())
        outputSnippet.close()
        self.registerAllSnippets()

    def clearHotkey(self):
        self.keySequenceEdit.clear()
class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()

        self.proxyModel = SortFilterProxyModel()
        self.proxyModel.setDynamicSortFilter(True)

        self.sourceGroupBox = QGroupBox("Original Model")
        self.proxyGroupBox = QGroupBox("Sorted/Filtered Model")

        self.sourceView = QTreeView()
        self.sourceView.setRootIsDecorated(False)
        self.sourceView.setAlternatingRowColors(True)

        self.proxyView = QTreeView()
        self.proxyView.setRootIsDecorated(False)
        self.proxyView.setAlternatingRowColors(True)
        self.proxyView.setModel(self.proxyModel)
        self.proxyView.setSortingEnabled(True)

        self.sortCaseSensitivityCheckBox = QCheckBox("Case sensitive sorting")
        self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter")

        self.filterPatternLineEdit = QLineEdit()
        self.filterPatternLabel = QLabel("&Filter pattern:")
        self.filterPatternLabel.setBuddy(self.filterPatternLineEdit)

        self.filterSyntaxComboBox = QComboBox()
        self.filterSyntaxComboBox.addItem("Regular expression", QRegExp.RegExp)
        self.filterSyntaxComboBox.addItem("Wildcard", QRegExp.Wildcard)
        self.filterSyntaxComboBox.addItem("Fixed string", QRegExp.FixedString)
        self.filterSyntaxLabel = QLabel("Filter &syntax:")
        self.filterSyntaxLabel.setBuddy(self.filterSyntaxComboBox)

        self.filterColumnComboBox = QComboBox()
        self.filterColumnComboBox.addItem("Subject")
        self.filterColumnComboBox.addItem("Sender")
        self.filterColumnComboBox.addItem("Date")
        self.filterColumnLabel = QLabel("Filter &column:")
        self.filterColumnLabel.setBuddy(self.filterColumnComboBox)

        self.filterPatternLineEdit.textChanged.connect(
            self.filterRegExpChanged)
        self.filterSyntaxComboBox.currentIndexChanged.connect(
            self.filterRegExpChanged)
        self.filterColumnComboBox.currentIndexChanged.connect(
            self.filterColumnChanged)
        self.filterCaseSensitivityCheckBox.toggled.connect(
            self.filterRegExpChanged)
        self.sortCaseSensitivityCheckBox.toggled.connect(self.sortChanged)

        sourceLayout = QHBoxLayout()
        sourceLayout.addWidget(self.sourceView)
        self.sourceGroupBox.setLayout(sourceLayout)

        proxyLayout = QGridLayout()
        proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3)
        proxyLayout.addWidget(self.filterPatternLabel, 1, 0)
        proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1, 1, 2)
        proxyLayout.addWidget(self.filterSyntaxLabel, 2, 0)
        proxyLayout.addWidget(self.filterSyntaxComboBox, 2, 1, 1, 2)
        proxyLayout.addWidget(self.filterColumnLabel, 3, 0)
        proxyLayout.addWidget(self.filterColumnComboBox, 3, 1, 1, 2)
        proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 4, 0, 1, 2)
        proxyLayout.addWidget(self.sortCaseSensitivityCheckBox, 4, 2)
        self.proxyGroupBox.setLayout(proxyLayout)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.sourceGroupBox)
        mainLayout.addWidget(self.proxyGroupBox)
        self.setLayout(mainLayout)

        self.setWindowTitle("Basic Sort/Filter Model")
        self.resize(500, 450)

        self.proxyView.sortByColumn(SENDER, Qt.AscendingOrder)
        self.filterColumnComboBox.setCurrentIndex(SENDER)

        self.filterPatternLineEdit.setText("Andy|Grace")
        self.filterCaseSensitivityCheckBox.setChecked(True)
        self.sortCaseSensitivityCheckBox.setChecked(True)

    def setSourceModel(self, model):
        self.proxyModel.setSourceModel(model)
        self.sourceView.setModel(model)

    def filterRegExpChanged(self):
        syntax_nr = self.filterSyntaxComboBox.itemData(
            self.filterSyntaxComboBox.currentIndex())
        syntax = QRegExp.PatternSyntax(syntax_nr)

        if self.filterCaseSensitivityCheckBox.isChecked():
            caseSensitivity = Qt.CaseSensitive
        else:
            caseSensitivity = Qt.CaseInsensitive

        regExp = QRegExp(self.filterPatternLineEdit.text(), caseSensitivity,
                         syntax)
        self.proxyModel.setFilterRegExp(regExp)

    def filterColumnChanged(self):
        self.proxyModel.setFilterKeyColumn(
            self.filterColumnComboBox.currentIndex())

    def sortChanged(self):
        if self.sortCaseSensitivityCheckBox.isChecked():
            caseSensitivity = Qt.CaseSensitive
        else:
            caseSensitivity = Qt.CaseInsensitive

        self.proxyModel.setSortCaseSensitivity(caseSensitivity)
Exemple #6
0
class RssTab(QWidget):
    new_torrents = Signal(int)

    def __init__(self):
        QWidget.__init__(self)
        layout = QVBoxLayout(self)
        self.splitter = QSplitter(self)
        self.cats = QTreeView(self)
        self.cats.setSortingEnabled(True)
        self.cat_model = RssCategoryModel()
        proxy = QSortFilterProxyModel()
        proxy.setSourceModel(self.cat_model)
        self.cats.setModel(proxy)
        self.splitter.addWidget(self.cats)
        self.t = QTableWidget(0, 4, self)
        self.splitter.addWidget(self.t)
        self.stats = [QLabel('{}'.format(datetime.now())) for _ in range(8)]

        stat: QLabel
        for stat in self.stats:
            stat.setFont(QFont(pointSize=14))
            layout.addWidget(stat, 0, Qt.AlignTop)

        layout.addWidget(self.splitter, 0, Qt.AlignTop)

        self.ds = DataSource()
        self.f_model = ForumsModel(self.ds.get_forums())
        self.forums = QTableView(self)
        self.forums.setModel(self.f_model)
        self.forums.resizeColumnsToContents()
        layout.addWidget(self.forums, 10, Qt.AlignTop)

        self.setLayout(layout)

        self.worker = RssWorker()
        self.worker_thread = QThread()
        self.worker_thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.worker_thread.quit)
        self.worker.moveToThread(self.worker_thread)
        self.worker_thread.start()
        self.worker.processed.connect(self.processed)
        self.worker.current.connect(self.current)

    @Slot(str)
    def current(self, topic):
        for i in range(len(self.stats) - 1):
            self.stats[i].setText(self.stats[i + 1].text())
        self.stats[len(self.stats) - 1].setText('{0} - {1}'.format(
            datetime.now(), topic))

    @Slot(int, int)
    def processed(self, forum_id, torrents):
        print('\t\t\tRSS: ' + str(forum_id) + ', ' + str(torrents))
        forum = self.ds.get_forum(forum_id)
        print('\t\t\tRSS FORUM: ' + str(forum))
        cat = self.ds.get_category(forum['category'])
        print('\t\t\tRSS CAT: ' + str(cat))
        self.cat_model.addCategory(cat['title'], torrents)

    def finish(self):
        self.worker.finish()
        self.worker_thread.quit()
        self.worker_thread.wait()
Exemple #7
0
class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()

        self.proxyModel = QSortFilterProxyModel()
        self.proxyModel.setDynamicSortFilter(True)

        self.sourceGroupBox = QGroupBox("Original Model")
        self.proxyGroupBox = QGroupBox("Sorted/Filtered Model")

        self.sourceView = QTreeView()
        self.sourceView.setRootIsDecorated(False)
        self.sourceView.setAlternatingRowColors(True)

        self.proxyView = QTreeView()
        self.proxyView.setRootIsDecorated(False)
        self.proxyView.setAlternatingRowColors(True)
        self.proxyView.setModel(self.proxyModel)
        self.proxyView.setSortingEnabled(True)

        self.sortCaseSensitivityCheckBox = QCheckBox("Case sensitive sorting")
        self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter")

        self.filterPatternLineEdit = QLineEdit()
        self.filterPatternLineEdit.setClearButtonEnabled(True)
        self.filterPatternLabel = QLabel("&Filter pattern:")
        self.filterPatternLabel.setBuddy(self.filterPatternLineEdit)

        self.filterSyntaxComboBox = QComboBox()
        self.filterSyntaxComboBox.addItem("Regular expression",
                                          REGULAR_EXPRESSION)
        self.filterSyntaxComboBox.addItem("Wildcard",
                                          WILDCARD)
        self.filterSyntaxComboBox.addItem("Fixed string",
                                          FIXED_STRING)
        self.filterSyntaxLabel = QLabel("Filter &syntax:")
        self.filterSyntaxLabel.setBuddy(self.filterSyntaxComboBox)

        self.filterColumnComboBox = QComboBox()
        self.filterColumnComboBox.addItem("Subject")
        self.filterColumnComboBox.addItem("Sender")
        self.filterColumnComboBox.addItem("Date")
        self.filterColumnLabel = QLabel("Filter &column:")
        self.filterColumnLabel.setBuddy(self.filterColumnComboBox)

        self.filterPatternLineEdit.textChanged.connect(self.filterRegExpChanged)
        self.filterSyntaxComboBox.currentIndexChanged.connect(self.filterRegExpChanged)
        self.filterColumnComboBox.currentIndexChanged.connect(self.filterColumnChanged)
        self.filterCaseSensitivityCheckBox.toggled.connect(self.filterRegExpChanged)
        self.sortCaseSensitivityCheckBox.toggled.connect(self.sortChanged)

        sourceLayout = QHBoxLayout()
        sourceLayout.addWidget(self.sourceView)
        self.sourceGroupBox.setLayout(sourceLayout)

        proxyLayout = QGridLayout()
        proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3)
        proxyLayout.addWidget(self.filterPatternLabel, 1, 0)
        proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1, 1, 2)
        proxyLayout.addWidget(self.filterSyntaxLabel, 2, 0)
        proxyLayout.addWidget(self.filterSyntaxComboBox, 2, 1, 1, 2)
        proxyLayout.addWidget(self.filterColumnLabel, 3, 0)
        proxyLayout.addWidget(self.filterColumnComboBox, 3, 1, 1, 2)
        proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 4, 0, 1, 2)
        proxyLayout.addWidget(self.sortCaseSensitivityCheckBox, 4, 2)
        self.proxyGroupBox.setLayout(proxyLayout)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.sourceGroupBox)
        mainLayout.addWidget(self.proxyGroupBox)
        self.setLayout(mainLayout)

        self.setWindowTitle("Basic Sort/Filter Model")
        self.resize(500, 450)

        self.proxyView.sortByColumn(1, Qt.AscendingOrder)
        self.filterColumnComboBox.setCurrentIndex(1)

        self.filterPatternLineEdit.setText("Andy|Grace")
        self.filterCaseSensitivityCheckBox.setChecked(True)
        self.sortCaseSensitivityCheckBox.setChecked(True)

    def setSourceModel(self, model):
        self.proxyModel.setSourceModel(model)
        self.sourceView.setModel(model)

    def filterRegExpChanged(self):
        syntax_nr = self.filterSyntaxComboBox.currentData()
        pattern = self.filterPatternLineEdit.text()
        if syntax_nr == WILDCARD:
            pattern = QRegularExpression.wildcardToRegularExpression(pattern)
        elif syntax_nr == FIXED_STRING:
            pattern = QRegularExpression.escape(pattern)

        regExp = QRegularExpression(pattern)
        if not self.filterCaseSensitivityCheckBox.isChecked():
            options = regExp.patternOptions()
            options |= QRegularExpression.CaseInsensitiveOption
            regExp.setPatternOptions(options)
        self.proxyModel.setFilterRegularExpression(regExp)

    def filterColumnChanged(self):
        self.proxyModel.setFilterKeyColumn(self.filterColumnComboBox.currentIndex())

    def sortChanged(self):
        if self.sortCaseSensitivityCheckBox.isChecked():
            caseSensitivity = Qt.CaseSensitive
        else:
            caseSensitivity = Qt.CaseInsensitive

        self.proxyModel.setSortCaseSensitivity(caseSensitivity)
Exemple #8
0
class BindiffViewerDialog(QDialog):
	def __init__(self, bv, match_db, role, primary_be, secondary_be):
		super(BindiffViewerDialog, self).__init__()

		self.bv = bv
		self.primary_be = primary_be
		self.secondary_be = secondary_be
		self.role = role

		# UI
		self.match_model = BindiffMatchModel(bv, match_db, role, primary_be, secondary_be)

		self.match_view = QTreeView()
		self.match_view.setModel(self.match_model)

		self.match_view.setSelectionMode(QTreeView.ExtendedSelection)

		self.match_view.setContextMenuPolicy(Qt.CustomContextMenu)
		self.match_view.customContextMenuRequested.connect(self.match_view_context_menu_requested)
		self.match_view.doubleClicked.connect(self.match_view_double_clicked)

		self.match_view.setRootIsDecorated(False)
		self.match_view.setFont(binaryninjaui.getMonospaceFont(self))

		for i in range(len(self.match_model.column_infos)):
			self.match_view.resizeColumnToContents(i)

		self.match_view.setSortingEnabled(True)
		self.match_view.sortByColumn(0, Qt.AscendingOrder)

		layout = QVBoxLayout()
		layout.addWidget(self.match_view)

		self.setLayout(layout)
		self.setWindowTitle("BinDiff Viewer")
		self.resize(1000, 640)
		flags = self.windowFlags()
		flags |= Qt.WindowMaximizeButtonHint
		flags &= ~Qt.WindowContextHelpButtonHint
		self.setWindowFlags(flags)

	def match_view_double_clicked(self, index):
		if not index.isValid():
			assert(False)
			return
		if self.role == None:
			return

		entry = self.match_model.entries[index.row()]
		if self.role == 0:
			address = entry["address1"]
		elif self.role == 1:
			address = entry["address2"]
		else:
			assert(False)

		self.bv.navigate(self.bv.file.view, address)

	def match_view_context_menu_requested(self, pos):
		if self.role == None:
			return

		selected_indices = self.match_view.selectionModel().selectedIndexes()

		# This may return each row multiple times, so we uniquify
		selected = set([i.row() for i in selected_indices])

		def action_port_symbols():
			for i in selected:
				self.port_symbols(i)

		menu = QMenu(self.match_view)
		menu.addAction("Port symbols", action_port_symbols)
		menu.exec_(self.match_view.mapToGlobal(pos))

	def port_symbols(self, i):
		if self.role == None:
			return

		entry = self.match_model.entries[i]
		target_index = self.role
		source_index = 1 if target_index == 0 else 0

		source_name = entry["name{}".format(source_index + 1)]
		target_address = entry["address{}".format(target_index + 1)]

		old_sym = self.bv.get_symbol_at(target_address)

		target_name = None
		if old_sym:
			target_name = old_sym.name
		target_text = target_name if target_name else "<unnamed>"

		if not source_name:
			bn.log_warn("Port symbols: {} @ {:x} has no source name, skipping".format(target_text, target_address))
			return

		if old_sym and not old_sym.auto:
			bn.log_warn("Port symbols: {} @ {:x} is already named, skipping".format(target_text, target_address))
			return

		bn.log_info("Port symbols: {} @ {:x} -> {}".format(target_text, target_address, source_name))
		new_sym = bn.Symbol(bn.SymbolType.FunctionSymbol, target_address, source_name)
		self.bv.define_user_symbol(new_sym)
class Snippets(QDialog):

    def __init__(self, context, parent=None):
        super(Snippets, self).__init__(parent)
        # Create widgets
        self.setWindowModality(Qt.ApplicationModal)
        self.title = QLabel(self.tr("Snippet Editor"))
        self.saveButton = QPushButton(self.tr("&Save"))
        self.saveButton.setShortcut(QKeySequence(self.tr("Ctrl+S")))
        self.runButton = QPushButton(self.tr("&Run"))
        self.runButton.setShortcut(QKeySequence(self.tr("Ctrl+R")))
        self.closeButton = QPushButton(self.tr("Close"))
        self.clearHotkeyButton = QPushButton(self.tr("Clear Hotkey"))
        self.setWindowTitle(self.title.text())
        #self.newFolderButton = QPushButton("New Folder")
        self.browseButton = QPushButton("Browse Snippets")
        self.browseButton.setIcon(QIcon.fromTheme("edit-undo"))
        self.deleteSnippetButton = QPushButton("Delete")
        self.newSnippetButton = QPushButton("New Snippet")
        self.edit = QCodeEditor(HIGHLIGHT_CURRENT_LINE=False, SyntaxHighlighter=PythonHighlighter)
        self.edit.setPlaceholderText("python code")
        self.resetting = False
        self.columns = 3
        self.context = context

        self.keySequenceEdit = QKeySequenceEdit(self)
        self.currentHotkey = QKeySequence()
        self.currentHotkeyLabel = QLabel("")
        self.currentFileLabel = QLabel()
        self.currentFile = ""
        self.snippetDescription = QLineEdit()
        self.snippetDescription.setPlaceholderText("optional description")

        #Set Editbox Size
        font = getMonospaceFont(self)
        self.edit.setFont(font)
        font = QFontMetrics(font)
        self.edit.setTabStopWidth(4 * font.width(' ')); #TODO, replace with settings API

        #Files
        self.files = QFileSystemModel()
        self.files.setRootPath(snippetPath)
        self.files.setNameFilters(["*.py"])

        #Tree
        self.tree = QTreeView()
        self.tree.setModel(self.files)
        self.tree.setSortingEnabled(True)
        self.tree.hideColumn(2)
        self.tree.sortByColumn(0, Qt.AscendingOrder)
        self.tree.setRootIndex(self.files.index(snippetPath))
        for x in range(self.columns):
            #self.tree.resizeColumnToContents(x)
            self.tree.header().setSectionResizeMode(x, QHeaderView.ResizeToContents)
        treeLayout = QVBoxLayout()
        treeLayout.addWidget(self.tree)
        treeButtons = QHBoxLayout()
        #treeButtons.addWidget(self.newFolderButton)
        treeButtons.addWidget(self.browseButton)
        treeButtons.addWidget(self.newSnippetButton)
        treeButtons.addWidget(self.deleteSnippetButton)
        treeLayout.addLayout(treeButtons)
        treeWidget = QWidget()
        treeWidget.setLayout(treeLayout)

        # Create layout and add widgets
        buttons = QHBoxLayout()
        buttons.addWidget(self.clearHotkeyButton)
        buttons.addWidget(self.keySequenceEdit)
        buttons.addWidget(self.currentHotkeyLabel)
        buttons.addWidget(self.closeButton)
        buttons.addWidget(self.runButton)
        buttons.addWidget(self.saveButton)

        description = QHBoxLayout()
        description.addWidget(QLabel(self.tr("Description: ")))
        description.addWidget(self.snippetDescription)

        vlayoutWidget = QWidget()
        vlayout = QVBoxLayout()
        vlayout.addLayout(description)
        vlayout.addWidget(self.edit)
        vlayout.addLayout(buttons)
        vlayoutWidget.setLayout(vlayout)

        hsplitter = QSplitter()
        hsplitter.addWidget(treeWidget)
        hsplitter.addWidget(vlayoutWidget)

        hlayout = QHBoxLayout()
        hlayout.addWidget(hsplitter)

        self.showNormal() #Fixes bug that maximized windows are "stuck"
        #Because you can't trust QT to do the right thing here
        if (sys.platform == "darwin"):
            self.settings = QSettings("Vector35", "Snippet Editor")
        else:
            self.settings = QSettings("Vector 35", "Snippet Editor")
        if self.settings.contains("ui/snippeteditor/geometry"):
            self.restoreGeometry(self.settings.value("ui/snippeteditor/geometry"))
        else:
            self.edit.setMinimumWidth(80 * font.averageCharWidth())
            self.edit.setMinimumHeight(30 * font.lineSpacing())

        # Set dialog layout
        self.setLayout(hlayout)

        # Add signals
        self.saveButton.clicked.connect(self.save)
        self.closeButton.clicked.connect(self.close)
        self.runButton.clicked.connect(self.run)
        self.clearHotkeyButton.clicked.connect(self.clearHotkey)
        self.tree.selectionModel().selectionChanged.connect(self.selectFile)
        self.newSnippetButton.clicked.connect(self.newFileDialog)
        self.deleteSnippetButton.clicked.connect(self.deleteSnippet)
        #self.newFolderButton.clicked.connect(self.newFolder)
        self.browseButton.clicked.connect(self.browseSnippets)

        if self.settings.contains("ui/snippeteditor/selected"):
            selectedName = self.settings.value("ui/snippeteditor/selected")
            self.tree.selectionModel().select(self.files.index(selectedName), QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
            if self.tree.selectionModel().hasSelection():
                self.selectFile(self.tree.selectionModel().selection(), None)
                self.edit.setFocus()
                cursor = self.edit.textCursor()
                cursor.setPosition(self.edit.document().characterCount()-1)
                self.edit.setTextCursor(cursor)
            else:
                self.readOnly(True)
        else:
            self.readOnly(True)


    @staticmethod
    def registerAllSnippets():
        for action in list(filter(lambda x: x.startswith("Snippets\\"), UIAction.getAllRegisteredActions())):
            if action == "Snippets\\Snippet Editor...":
                continue
            UIActionHandler.globalActions().unbindAction(action)
            Menu.mainMenu("Tools").removeAction(action)
            UIAction.unregisterAction(action)

        for snippet in includeWalk(snippetPath, ".py"):
            snippetKeys = None
            (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(snippet)
            actionText = actionFromSnippet(snippet, snippetDescription)
            if snippetCode:
                if snippetKeys == None:
                    UIAction.registerAction(actionText)
                else:
                    UIAction.registerAction(actionText, snippetKeys)
                UIActionHandler.globalActions().bindAction(actionText, UIAction(makeSnippetFunction(snippetCode)))
                Menu.mainMenu("Tools").addAction(actionText, "Snippets")

    def clearSelection(self):
        self.keySequenceEdit.clear()
        self.currentHotkey = QKeySequence()
        self.currentHotkeyLabel.setText("")
        self.currentFileLabel.setText("")
        self.snippetDescription.setText("")
        self.edit.clear()
        self.tree.clearSelection()
        self.currentFile = ""

    def reject(self):
        self.settings.setValue("ui/snippeteditor/geometry", self.saveGeometry())

        if self.snippetChanged():
            question = QMessageBox.question(self, self.tr("Discard"), self.tr("You have unsaved changes, quit anyway?"))
            if question != QMessageBox.StandardButton.Yes:
                return
        self.accept()

    def browseSnippets(self):
        url = QUrl.fromLocalFile(snippetPath)
        QDesktopServices.openUrl(url);

    def newFolder(self):
        (folderName, ok) = QInputDialog.getText(self, self.tr("Folder Name"), self.tr("Folder Name: "))
        if ok and folderName:
            index = self.tree.selectionModel().currentIndex()
            selection = self.files.filePath(index)
            if QFileInfo(selection).isDir():
                QDir(selection).mkdir(folderName)
            else:
                QDir(snippetPath).mkdir(folderName)

    def selectFile(self, new, old):
        if (self.resetting):
            self.resetting = False
            return
        if len(new.indexes()) == 0:
            self.clearSelection()
            self.currentFile = ""
            self.readOnly(True)
            return
        newSelection = self.files.filePath(new.indexes()[0])
        self.settings.setValue("ui/snippeteditor/selected", newSelection)
        if QFileInfo(newSelection).isDir():
            self.readOnly(True)
            self.clearSelection()
            self.currentFile = ""
            return

        if old and old.length() > 0:
            oldSelection = self.files.filePath(old.indexes()[0])
            if not QFileInfo(oldSelection).isDir() and self.snippetChanged():
                question = QMessageBox.question(self, self.tr("Discard"), self.tr("Snippet changed. Discard changes?"))
                if question != QMessageBox.StandardButton.Yes:
                    self.resetting = True
                    self.tree.selectionModel().select(old, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
                    return False

        self.currentFile = newSelection
        self.loadSnippet()

    def loadSnippet(self):
        self.currentFileLabel.setText(QFileInfo(self.currentFile).baseName())
        (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile)
        self.snippetDescription.setText(snippetDescription) if snippetDescription else self.snippetDescription.setText("")
        self.keySequenceEdit.setKeySequence(snippetKeys) if snippetKeys else self.keySequenceEdit.setKeySequence(QKeySequence(""))
        self.edit.setPlainText(snippetCode) if snippetCode else self.edit.setPlainText("")
        self.readOnly(False)

    def newFileDialog(self):
        (snippetName, ok) = QInputDialog.getText(self, self.tr("Snippet Name"), self.tr("Snippet Name: "))
        if ok and snippetName:
            if not snippetName.endswith(".py"):
                snippetName += ".py"
            index = self.tree.selectionModel().currentIndex()
            selection = self.files.filePath(index)
            if QFileInfo(selection).isDir():
                path = os.path.join(selection, snippetName)
            else:
                path = os.path.join(snippetPath, snippetName)
                self.readOnly(False)
            open(path, "w").close()
            self.tree.setCurrentIndex(self.files.index(path))
            log_debug("Snippet %s created." % snippetName)

    def readOnly(self, flag):
        self.keySequenceEdit.setEnabled(not flag)
        self.snippetDescription.setReadOnly(flag)
        self.edit.setReadOnly(flag)
        if flag:
            self.snippetDescription.setDisabled(True)
            self.edit.setDisabled(True)
        else:
            self.snippetDescription.setEnabled(True)
            self.edit.setEnabled(True)

    def deleteSnippet(self):
        selection = self.tree.selectedIndexes()[::self.columns][0] #treeview returns each selected element in the row
        snippetName = self.files.fileName(selection)
        question = QMessageBox.question(self, self.tr("Confirm"), self.tr("Confirm deletion: ") + snippetName)
        if (question == QMessageBox.StandardButton.Yes):
            log_debug("Deleting snippet %s." % snippetName)
            self.clearSelection()
            self.files.remove(selection)
            self.registerAllSnippets()

    def snippetChanged(self):
        if (self.currentFile == "" or QFileInfo(self.currentFile).isDir()):
            return False
        (snippetDescription, snippetKeys, snippetCode) = loadSnippetFromFile(self.currentFile)
        if snippetKeys == None and not self.keySequenceEdit.keySequence().isEmpty():
            return True
        if snippetKeys != None and snippetKeys != self.keySequenceEdit.keySequence().toString():
            return True
        return self.edit.toPlainText() != snippetCode or \
               self.snippetDescription.text() != snippetDescription

    def save(self):
        log_debug("Saving snippet %s" % self.currentFile)
        outputSnippet = codecs.open(self.currentFile, "w", "utf-8")
        outputSnippet.write("#" + self.snippetDescription.text() + "\n")
        outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n")
        outputSnippet.write(self.edit.toPlainText())
        outputSnippet.close()
        self.registerAllSnippets()

    def run(self):
        if self.context == None:
            log_warn("Cannot run snippets outside of the UI at this time.")
            return
        if self.snippetChanged():
            question = QMessageBox.question(self, self.tr("Confirm"), self.tr("You have unsaved changes, must save first. Save?"))
            if (question == QMessageBox.StandardButton.No):
                return
            else:
                self.save()
        actionText = actionFromSnippet(self.currentFile, self.snippetDescription.text())
        UIActionHandler.globalActions().executeAction(actionText, self.context)

        log_debug("Saving snippet %s" % self.currentFile)
        outputSnippet = codecs.open(self.currentFile, "w", "utf-8")
        outputSnippet.write("#" + self.snippetDescription.text() + "\n")
        outputSnippet.write("#" + self.keySequenceEdit.keySequence().toString() + "\n")
        outputSnippet.write(self.edit.toPlainText())
        outputSnippet.close()
        self.registerAllSnippets()

    def clearHotkey(self):
        self.keySequenceEdit.clear()
Exemple #10
0
class SearchReplaceDialog(QDialog):
    """Search & Replace window
    """
    def __init__(self, parent, initial_query=None):
        """Initialize the search window
        """

        super(SearchReplaceDialog, self).__init__(parent)

        self.parent = parent
        self.prefs = parent.prefs
        self.items_checked = 0

        self.search_button = QPushButton('Search')
        self.search_text = MySearchField(self.search_button)
        self.search_text.setPlaceholderText("Enter search query here")
        self.search_button.clicked.connect(self.submit_search)
        input_layout = QHBoxLayout()
        input_layout.addWidget(self.search_text)
        input_layout.addWidget(self.search_button)

        self.results_tree = QTreeView()
        self.results_tree.setRootIsDecorated(False)
        self.results_tree.setAlternatingRowColors(True)
        self.results_tree.setAllColumnsShowFocus(True)
        self.results_tree.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.results_tree.setSelectionMode(QAbstractItemView.SingleSelection)
        self.results_tree.doubleClicked.connect(self.go_to_object)

        self.whole_field_checkbox = QCheckBox("Whole Field Only", self)
        self.advanced_search_checkbox = QCheckBox("Advanced Search", self)
        self.advanced_search_checkbox.stateChanged.connect(
            self.advanced_search_checked)
        self.ignore_geometry_checkbox = QCheckBox("Ignore Geometry", self)
        self.go_button = QPushButton('Go')
        self.go_button.clicked.connect(self.go_to_object)
        checks_layout = QHBoxLayout()
        checks_layout.addWidget(self.whole_field_checkbox)
        checks_layout.addWidget(self.advanced_search_checkbox)
        checks_layout.addWidget(self.ignore_geometry_checkbox)
        checks_layout.addStretch()
        checks_layout.addWidget(self.go_button)

        self.query_label = QLabel("Query:")
        self.query_label.setEnabled(False)
        self.query_text = QLineEdit()
        self.query_text.setEnabled(True)
        self.query_text.setReadOnly(True)
        self.query_text.setFrame(False)
        self.query_text.setStyleSheet("""QLineEdit {
                                   background-color: LightGray;
                                   color: white;
                                   } """)
        query_layout = QHBoxLayout()
        query_layout.addWidget(self.query_label)
        query_layout.addWidget(self.query_text)

        self.select_label = QLabel("Select:")
        self.select_all_button = QPushButton("All")
        self.select_none_button = QPushButton("None")
        self.select_invert_button = QPushButton("Invert")
        self.delete_button = QPushButton("Delete Objects")
        self.select_all_button.clicked.connect(self.select_all_clicked)
        self.select_none_button.clicked.connect(self.select_none_clicked)
        self.select_invert_button.clicked.connect(self.select_invert_clicked)
        self.delete_button.clicked.connect(self.delete_button_clicked)
        self.delete_button.setEnabled(False)
        selection_layout = QHBoxLayout()
        selection_layout.addWidget(self.select_label)
        selection_layout.addWidget(self.select_all_button)
        selection_layout.addWidget(self.select_none_button)
        selection_layout.addWidget(self.select_invert_button)
        selection_layout.addStretch()
        selection_layout.addWidget(self.delete_button)

        self.replace_with_text = QLineEdit()
        self.replace_with_label = QLabel("Replace With:")
        self.replace_button = QPushButton("Replace")
        self.replace_button.clicked.connect(self.replace_button_clicked)
        self.replace_button.setEnabled(False)
        replace_layout = QHBoxLayout()
        replace_layout.addWidget(self.replace_with_label)
        replace_layout.addWidget(self.replace_with_text)
        replace_layout.addWidget(self.replace_button)

        layout = QVBoxLayout()
        layout.addLayout(input_layout)
        layout.addLayout(checks_layout)
        layout.addLayout(query_layout)
        layout.addWidget(self.results_tree)
        layout.addLayout(selection_layout)
        layout.addLayout(replace_layout)

        self.resize(650, 450)
        self.setLayout(layout)
        self.setWindowTitle("IDF+ Search & Replace")
        self.search_text.setFocus()
        self.setTabOrder(self.search_text, self.search_button)
        self.setTabOrder(self.search_button, self.whole_field_checkbox)
        self.setTabOrder(self.whole_field_checkbox,
                         self.advanced_search_checkbox)
        self.setTabOrder(self.advanced_search_checkbox, self.select_all_button)
        self.setTabOrder(self.select_all_button, self.select_none_button)
        self.setTabOrder(self.select_none_button, self.select_invert_button)
        self.setTabOrder(self.select_invert_button, self.replace_with_text)
        self.setTabOrder(self.replace_with_text, self.replace_button)
        self.setTabOrder(self.replace_button, self.search_text)

        self.results_tree.setModel(self.create_results_model([]))
        self.results_tree.setColumnHidden(2, True)

        if initial_query is not None:
            self.search_text.setText(initial_query)
            self.search_button.click()

    def create_results_model(self, results):
        def add_result_row(row_model, value, obj_class, uuid):
            row_model.insertRow(0)
            row_model.setData(model.index(0, 0), value)
            row_model.setData(model.index(0, 1), obj_class)
            row_model.setData(model.index(0, 2), uuid)

            item_0 = model.itemFromIndex(model.index(0, 0))
            item_0.setCheckState(Qt.Unchecked)
            item_0.setFlags(item_0.flags() | Qt.ItemIsUserCheckable)
            item_0.setEditable(False)

            item_1 = model.itemFromIndex(model.index(0, 1))
            item_1.setEditable(False)

        model = QStandardItemModel(0, 3, self)
        model.setHeaderData(0, Qt.Horizontal, "Value")
        model.setHeaderData(1, Qt.Horizontal, "Class")
        model.setHeaderData(2, Qt.Horizontal, "UUID")

        for hit in results:
            add_result_row(model, hit['value'], hit['obj_class_display'],
                           hit['uuid'])

        return model

    def submit_search(self):
        """Submits a search based on the current query
        """

        user_query = self.search_text.text()
        idf = self.parent.idf
        if not user_query or len(user_query) < 2 or not idf:
            return [], ""
        results, my_query = idf.search(
            user_query, self.whole_field_checkbox.isChecked(),
            self.advanced_search_checkbox.isChecked(),
            self.ignore_geometry_checkbox.isChecked())
        self.query_text.setText(str(my_query))
        self.results_tree.setModel(self.create_results_model(results))
        self.results_tree.model().itemChanged.connect(self.item_checked)
        # self.results_tree.setColumnHidden(2, True)
        self.results_tree.resizeColumnToContents(0)
        self.results_tree.resizeColumnToContents(1)
        self.results_tree.setSortingEnabled(True)

    def item_checked(self, item):

        if item.checkState() == Qt.Checked:
            self.items_checked += 1
        else:
            self.items_checked -= 1

        if self.items_checked > 0:
            self.delete_button.setEnabled(True)
            self.replace_button.setEnabled(True)
        else:
            self.delete_button.setEnabled(False)
            self.replace_button.setEnabled(False)

    def select_all_clicked(self):

        model = self.results_tree.model()
        result_count = model.rowCount()

        for i in range(result_count):
            model.itemFromIndex(model.index(i, 0)).setCheckState(Qt.Checked)

    def select_none_clicked(self):

        model = self.results_tree.model()
        result_count = model.rowCount()

        for i in range(result_count):
            model.itemFromIndex(model.index(i, 0)).setCheckState(Qt.Unchecked)

    def select_invert_clicked(self):

        model = self.results_tree.model()
        result_count = model.rowCount()

        for i in range(result_count):
            item = model.itemFromIndex(model.index(i, 0))
            if item.checkState() == Qt.Checked:
                new_state = Qt.Unchecked
            else:
                new_state = Qt.Checked
            item.setCheckState(new_state)

    def delete_button_clicked(self):
        model = self.results_tree.model()
        result_count = model.rowCount()
        if result_count <= 0 or self.items_checked <= 0:
            return

        question = "Are you sure you want to perform this deletion?\n" \
                   "Undo is currently NOT supported for this operation."
        response = self.confirm_action(question)
        if response is not True:
            return

        for i in range(result_count):
            item_0 = model.itemFromIndex(model.index(i, 0))
            item_2 = model.itemFromIndex(model.index(i, 2))
            if item_0.checkState() != Qt.Checked:
                continue
            field = self.parent.idf.field_by_uuid(item_2.text())
            obj = field._outer
            obj_class = self.parent.idf.idf_objects(obj.obj_class)
            try:
                index = obj_class.index(obj)
                self.parent.idf.remove_objects(obj.obj_class, index, index + 1)
            except ValueError:
                pass  # already deleted

        self.parent.set_dirty(True)
        self.submit_search()
        self.parent.load_table_view(self.parent.current_obj_class)
        QMessageBox.information(self, "Delete Action", "Deletion Complete!")

    def advanced_search_checked(self):
        if self.advanced_search_checkbox.isChecked():
            self.whole_field_checkbox.setEnabled(False)
            self.whole_field_checkbox.setChecked(True)
            self.ignore_geometry_checkbox.setEnabled(False)
            self.ignore_geometry_checkbox.setChecked(False)
        else:
            self.whole_field_checkbox.setEnabled(True)
            self.whole_field_checkbox.setChecked(False)
            self.ignore_geometry_checkbox.setEnabled(True)
            self.ignore_geometry_checkbox.setChecked(False)

    def replace_button_clicked(self):
        search_text = self.search_text.text()
        replace_with_text = self.replace_with_text.text()
        if not search_text:
            return

        model = self.results_tree.model()
        result_count = model.rowCount()
        if result_count <= 0 or self.items_checked <= 0:
            return

        question = "Are you sure you want to perform this replacement?\n" \
                   "Undo is currently NOT supported for this operation."
        response = self.confirm_action(question)
        if response is not True:
            return

        for i in range(result_count):
            item_0 = model.itemFromIndex(model.index(i, 0))
            item_2 = model.itemFromIndex(model.index(i, 2))
            if item_0.checkState() != Qt.Checked:
                continue
            field = self.parent.idf.field_by_uuid(item_2.text())
            if self.whole_field_checkbox.isChecked(
            ) or self.advanced_search_checkbox.isChecked():
                field.value = replace_with_text
            else:
                regex = re.compile(re.escape(search_text), re.IGNORECASE)
                field.value = regex.sub(replace_with_text, field.value)

        self.parent.set_dirty(True)
        self.submit_search()
        self.parent.load_table_view(self.parent.current_obj_class)
        QMessageBox.information(self, "Replacement", "Replacement Complete!")

    def confirm_action(self, question):
        """Confirm user wants to perform action
        """

        flags = QMessageBox.StandardButton.Yes
        flags |= QMessageBox.StandardButton.No
        response = QMessageBox.question(self, "Are you sure?", question, flags)

        if response == QMessageBox.Yes:
            return True
        elif QMessageBox.No:
            return False
        else:
            return False

    def go_to_object(self, index=None):

        if index is None:
            selected = self.results_tree.selectedIndexes()
            if not selected:
                return
            index = selected[0]

        model = self.results_tree.model()
        item = model.itemFromIndex(model.index(index.row(), 2))
        field = self.parent.idf.field_by_uuid(item.text())
        self.parent.activateWindow()
        self.parent.jump_to_field(field)
Exemple #11
0
class SVDBrowser(QDialog):
    def __init__(self, context, parent=None):
        super(SVDBrowser, self).__init__(parent)
        QWebEngineProfile.defaultProfile().downloadRequested.connect(
            self.on_downloadRequested)

        # Create widgets
        #self.setWindowModality(Qt.ApplicationModal)
        self.title = QLabel(self.tr("SVD Browser"))
        self.closeButton = QPushButton(self.tr("Close"))
        self.setWindowTitle(self.title.text())
        self.browseButton = QPushButton("Browse SVD Folder")
        self.deleteSvdButton = QPushButton("Delete")
        self.applySvdButton = QPushButton("Apply SVD")
        self.view = QWebEngineView()
        url = "https://developer.arm.com/tools-and-software/embedded/cmsis/cmsis-search"
        self.view.load(QUrl(url))
        self.columns = 3
        self.context = context

        self.currentFileLabel = QLabel()
        self.currentFile = ""

        #Files
        self.files = QFileSystemModel()
        self.files.setRootPath(svdPath)
        self.files.setNameFilters(["*.svd", "*.patched"])

        #Tree
        self.tree = QTreeView()
        self.tree.setModel(self.files)
        self.tree.setSortingEnabled(True)
        self.tree.hideColumn(2)
        self.tree.sortByColumn(0, Qt.AscendingOrder)
        self.tree.setRootIndex(self.files.index(svdPath))
        for x in range(self.columns):
            #self.tree.resizeColumnToContents(x)
            self.tree.header().setSectionResizeMode(
                x, QHeaderView.ResizeToContents)
        treeLayout = QVBoxLayout()
        treeLayout.addWidget(self.tree)
        treeButtons = QHBoxLayout()
        #treeButtons.addWidget(self.newFolderButton)
        treeButtons.addWidget(self.browseButton)
        treeButtons.addWidget(self.applySvdButton)
        treeButtons.addWidget(self.deleteSvdButton)
        treeLayout.addLayout(treeButtons)
        treeWidget = QWidget()
        treeWidget.setLayout(treeLayout)

        # Create layout and add widgets
        buttons = QHBoxLayout()
        buttons.addWidget(self.closeButton)

        vlayoutWidget = QWidget()
        vlayout = QVBoxLayout()
        vlayout.addWidget(self.view)
        vlayout.addLayout(buttons)
        vlayoutWidget.setLayout(vlayout)

        hsplitter = QSplitter()
        hsplitter.addWidget(treeWidget)
        hsplitter.addWidget(vlayoutWidget)

        hlayout = QHBoxLayout()
        hlayout.addWidget(hsplitter)

        self.showMaximized()  #Fixes bug that maximized windows are "stuck"
        #Because you can't trust QT to do the right thing here

        # Set dialog layout
        self.setLayout(hlayout)

        # Add signals
        self.closeButton.clicked.connect(self.close)
        self.tree.selectionModel().selectionChanged.connect(self.selectFile)
        self.applySvdButton.clicked.connect(self.applySvd)
        self.deleteSvdButton.clicked.connect(self.deleteSvd)
        self.browseButton.clicked.connect(self.browseSvd)

    def browseSvd(self):
        url = QUrl.fromLocalFile(svdPath)
        QDesktopServices.openUrl(url)

    def selectFile(self, new, old):
        if len(new.indexes()) == 0:
            self.tree.clearSelection()
            self.currentFile = ""
            return
        newSelection = self.files.filePath(new.indexes()[0])
        if QFileInfo(newSelection).isDir():
            self.tree.clearSelection()
            self.currentFile = ""
            return
        self.currentFile = newSelection

    def applySvd(self):
        selection = self.tree.selectedIndexes()[::self.columns][
            0]  #treeview returns each selected element in the row
        svdName = self.files.fileName(selection)
        if (svdName != ""):
            question = QMessageBox.question(
                self, self.tr("Confirm"),
                self.
                tr(f"Confirm applying {svdName} to {os.path.basename(self.context.file.filename)} : "
                   ))
            if (question == QMessageBox.StandardButton.Yes):
                log_debug("SVD Browser: Applying SVD %s." % svdName)
                load_svd(self.context, self.currentFile)
                self.close()

    def deleteSvd(self):
        selection = self.tree.selectedIndexes()[::self.columns][
            0]  #treeview returns each selected element in the row
        svdName = self.files.fileName(selection)
        question = QMessageBox.question(
            self, self.tr("Confirm"),
            self.tr("Confirm deletion: ") + svdName)
        if (question == QMessageBox.StandardButton.Yes):
            log_debug("SVD Browser: Deleting SVD %s." % svdName)
            self.files.remove(selection)
            self.tree.clearSelection()

    def on_downloadRequested(self, download):
        old_path = download.url().path()  # download.path()
        suffix = QFileInfo(old_path).suffix()
        if (suffix.lower() in ["zip", "svd", "pack", "patched"]):
            log_debug(f"SVD Browser: Downloading {str(download.url())}")
            if suffix.lower() == "svd" or suffix.lower() == "patched":
                download.setDownloadDirectory(svdPath)
                download.accept()
            else:
                with TemporaryDirectory() as tempfolder:
                    log_debug(
                        f"SVD Browser: Downloading pack/zip to {tempfolder}")
                    fname = download.url().fileName()
                    r = requests.get(download.url().toString(),
                                     allow_redirects=True)
                    dlfile = os.path.join(tempfolder, fname)
                    open(dlfile, "wb").write(r.content)
                    '''
                    # TODO: See if the original QT Downloader can be fixed since it would
                    # help with situations where the user entered credentials in the browser.
                    download.setDownloadDirectory(tempfolder)
                    download.accept()
                    while not download.finished:
                        import time
                        time.sleep(100)
                    '''
                    if fname.endswith(".zip") or fname.endswith(".pack"):
                        destFolder = os.path.join(svdPath,
                                                  os.path.splitext(fname)[0])
                        log_debug(f"SVD Browser: Creating {destFolder}")
                        if not os.path.exists(destFolder):
                            os.mkdir(destFolder)
                        with ZipFile(dlfile, 'r') as zipp:
                            for ifname in zipp.namelist():
                                if ifname.endswith(".svd"):
                                    info = zipp.getinfo(ifname)
                                    info.filename = os.path.basename(
                                        info.filename)
                                    log_debug(
                                        f"SVD Browser: Extracting {info.filename} from {ifname}"
                                    )
                                    zipp.extract(info, path=destFolder)
                    else:
                        #Move file into place
                        shutil.move(dlfile, svdPath)
        else:
            show_message_box(
                "Invalid file",
                "That download does not appear to be a valid SVD/ZIP/PACK file."
            )
        download.cancel()