更改 QTreeWidgetItem 标志时防止递归

Posted

技术标签:

【中文标题】更改 QTreeWidgetItem 标志时防止递归【英文标题】:Prevent recursion when changing QTreeWidgetItem flags 【发布时间】:2021-12-18 19:38:53 【问题描述】:

我正在尝试制作一个非常基本的密码管理器,但在尝试编辑项目时遇到了问题。当按下“编辑密码”按钮时,它会使当前选定的项目可编辑,我希望在用户完成修改后将其删除。试图删除标志ItemIsEditable 会导致它在item.setFlags(item.flags() & ~Qt.ItemIsEditable) 行上进入无限递归。

# app class -------------------------------------------------------------------------- #
class App(QApplication):
    # initialisation ----------------------------------------------------------------- #
    def __init__(self, argv):
        super().__init__(argv)

        self.__ready = False

        self.__setup__()
        self.__load_data__()

        self.__ready = True

    # private methods ---------------------------------------------------------------- #
    def __load_data__(self):
        self.data_tree.headerItem().setText(0, "Client")
        self.data_tree.headerItem().setText(1, "Workstation")
        self.data_tree.headerItem().setText(2, "Login")
        self.data_tree.headerItem().setText(3, "Password")

        for level_1, client in enumerate(self.data["clients"]):
            row_1 = QTreeWidgetItem(self.data_tree)

            self.data_tree.topLevelItem(level_1).setText(0, client["name"])

            for level_2, workstation in enumerate(client["workstations"]):
                row_2 = QTreeWidgetItem(row_1)

                self.data_tree.topLevelItem(level_1).child(level_2).setText(
                    1, workstation["name"]
                )

                for level_3, login in enumerate(workstation["logins"]):
                    row_3 = QTreeWidgetItem(row_2)

                    row_3.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)

                    self.data_tree.topLevelItem(level_1).child(level_2).child(
                        level_3
                    ).setText(2, login["username"])
                    self.data_tree.topLevelItem(level_1).child(level_2).child(
                        level_3
                    ).setText(3, login["password"])

        self.data_tree.setSortingEnabled(True)

    def __setup__(self):
        self.data = pd.read_json("list.json")
        self.main_window = QMainWindow()
        self.central_widget = QWidget(self.main_window)
        self.data_tree = QTreeWidget(self.central_widget)
        self.edit_password_button = QPushButton(self.central_widget)

        self.central_widget.setGeometry(QRect(0, 0, 500, 500))
        self.central_widget.setObjectName("central_widget")

        self.data_tree.itemChanged.connect(self.save_password)
        self.data_tree.setGeometry(QRect(0, 0, 500, 450))
        self.data_tree.setObjectName("data_tree")
        self.data_tree.sortByColumn(0, Qt.SortOrder.AscendingOrder)

        self.main_window.setCentralWidget(self.central_widget)
        self.main_window.setGeometry(QRect(200, 200, 500, 500))
        self.main_window.setWindowTitle("Password Manager")
        self.main_window.show()

        self.edit_password_button.clicked.connect(self.edit_password)
        self.edit_password_button.setGeometry(QRect(345, 455, 150, 40))
        self.edit_password_button.setText("Edit Password")

    # events ------------------------------------------------------------------------- #
    @pyqtSlot()
    def edit_password(self):
        try:
            item = self.data_tree.currentItem()

            item.setFlags(item.flags() | Qt.ItemIsEditable)
            self.data_tree.scrollToItem(item)
            self.data_tree.editItem(item, 3)
        except Exception as e:
            print(e)

    @pyqtSlot(QTreeWidgetItem, int)
    def save_password(self, item, column):
        if not self.__ready:
            return

        for client in self.data["clients"]:
            if client["name"] == item.parent().parent().text(0):
                for workstation in client["workstations"]:
                    if workstation["name"] == item.parent().text(1):
                        for login in workstation["logins"]:
                            if login["username"] == item.text(2):
                                login["password"] = item.text(3)

        with open("list.json", "w") as file:
            json.dump(json.loads(self.data.to_json()), file, indent=4)

        item.setFlags(item.flags() & ~Qt.ItemIsEditable)

    def simulate_password(self):
        keyboard.write(self.data_tree.currentItem().text(3))

    def test_func(self):
        print("test")


app = App(sys.argv)

keyboard.add_hotkey("ctrl+insert", app.simulate_password)

sys.exit(app.exec())

【问题讨论】:

【参考方案1】:

itemChanged signal 是针对项目的所有 更改发出的,而不仅仅是其文本。为避免递归,您可以在设置标志时暂时阻止信号,这样save_password 插槽就不会再次被触发:

@pyqtSlot()
def edit_password(self):
    item = self.data_tree.currentItem()

    blocked = item.treeWidget().blockSignals(True)
    item.setFlags(item.flags() | Qt.ItemIsEditable)
    item.treeWidget().blockSignals(blocked)

    self.data_tree.scrollToItem(item)
    self.data_tree.editItem(item, 3)

@pyqtSlot(QTreeWidgetItem, int)
def save_password(self, item, column):
    ...
    blocked = item.treeWidget().blockSignals(True)
    item.setFlags(item.flags() & ~Qt.ItemIsEditable)
    item.treeWidget().blockSignals(blocked)

【讨论】:

添加此项可防止崩溃,但现在也可防止该字段可编辑。 对不起,让我详细说明一下。这些项目不能通过双击它们或类似的东西来编辑,只能通过单击按钮。为了做到这一点,他们最初都没有itemIsEditable,但是当按下按钮时,它使他们可以编辑,直到用户完成编辑,然后他们删除了标志。 非常感谢。你是人间的神。

以上是关于更改 QTreeWidgetItem 标志时防止递归的主要内容,如果未能解决你的问题,请参考以下文章

连接 QTreeWidgetItem 时出错

有没有办法防止 MASM 更改指令?

仅使 QTreeWidgetItem 的一列可编辑

QTreeWidgetItem 使用键盘多选

如何将 QTableWidget 添加到 QTreeWidget 但作为 QTreeWidgetItem

-g 标志更改程序的运行时和编译