为什么 PyQt5 小部件有时具有相同的 id?
Why PyQt5 widgets sometimes have the same id?
(示例有问题的代码及其输出在底部)
使用 PyQt5,我正在编写一个 QDialog
,里面有 QTabWidget
。
此选项卡小部件具有 QFormLayout
布局。
我想遍历表单并将其右侧的小部件(“字段”)存储为 weakref.WeakKeyDictionary
中的键以供以后使用(直到对话框关闭并且相应的键有望自动消失)。
我发现我的弱键字典没有按预期工作:一些小部件被正确存储,一些没有。
特别是,后来添加的小部件似乎存储得更频繁(当我多次退出并重新打开应用程序时)。
我为表单中的每个标签小部件 label
调用了 print(hex(id(label))
。
这表明某些标签具有相同的 Python id
,我相信只有最后一次迭代的具有任何特定 id 的小部件被存储。
这个“id 共享”真的是我的弱键字典没有存储所有小部件的原因吗?
为什么每个小部件没有自己的 ID?
我可以更改我的代码,使每个小部件都有一个唯一的 ID 吗?
我可以更改我的代码,使每个小部件都可以在我的弱键字典中只存储一次吗?
示例代码:
#!/usr/bin/env python3
from weakref import WeakKeyDictionary
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.weak = WeakKeyDictionary()
self.clickme = QPushButton("Click me", self)
self.clickme.clicked.connect(self.open_settings)
@pyqtSlot()
def open_settings(self) -> None:
dialog = QDialog(self)
self.weak[dialog] = "Settings"
grid = QGridLayout()
tabs = QTabWidget(dialog)
tab0 = QWidget(tabs)
tabs.addTab(tab0, "Sample")
form0 = QFormLayout()
for char in "sample":
form0.addRow(char, QLineEdit(tab0))
tab0.setLayout(form0)
# print information
for row in range(form0.rowCount()):
label = form0.itemAt(row, form0.LabelRole).widget()
print(hex(id(label)), type(label).__name__)
self.weak[label] = "foobar"
print(flush=True)
for k, v in self.weak.items():
print(f"{k!r}: {v!r}")
print(flush=True)
grid.addWidget(tabs, 0, 0, 1, 3)
dialog.show()
dialog.exec()
if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
当整个应用程序为 运行:
时此示例代码的输出
0x7f5956285670 QLabel # this is ok
0x7f5956285700 QLabel # this is ok
0x7f59562855e0 QLabel # this is ok
0x7f5956285670 QLabel # why the repeated id?!
0x7f5956285700 QLabel # why the repeated id?!
0x7f59562855e0 QLabel # why the repeated id?!
# the resulting weak-keys dict:
<PyQt5.QtWidgets.QDialog object at 0x7f5956342f70>: 'Settings'
<PyQt5.QtWidgets.QLabel object at 0x7f59562855e0>: 'foobar'
这似乎是一个错误,它似乎发生在用 C++ 创建对象(如 QFormLayout 的 QWidgetItem)然后从 python 访问它时,似乎 pyqt5 重用了对象(在 pyside2 中不会发生)。
一个可能的解决方案是在 python 中创建 QLabel,这样对象就不会被重复使用。
form0.addRow(<b>QLabel(</b>char<b>)</b>, QLineEdit())
此外,您必须访问 QLabel 而不是 QWidgetItem:
# print information
for row in range(form0.rowCount()):
label = form0.itemAt(row, form0.LabelRole)<b>.widget()</b>
print(hex(id(label)), type(label).__name__)
self.weak[label] = "foobar"
print(flush=True)
输出:
0x7f756703c0d0 QLabel
0x7f756703c1f0 QLabel
0x7f756703c310 QLabel
0x7f756703c430 QLabel
0x7f756703c550 QLabel
0x7f756703c670 QLabel
<PyQt5.QtWidgets.QDialog object at 0x7f756ef61dc0>: 'Settings'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c0d0>: 'foobar'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c1f0>: 'foobar'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c310>: 'foobar'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c430>: 'foobar'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c550>: 'foobar'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c670>: 'foobar'
(示例有问题的代码及其输出在底部)
使用 PyQt5,我正在编写一个 QDialog
,里面有 QTabWidget
。
此选项卡小部件具有 QFormLayout
布局。
我想遍历表单并将其右侧的小部件(“字段”)存储为 weakref.WeakKeyDictionary
中的键以供以后使用(直到对话框关闭并且相应的键有望自动消失)。
我发现我的弱键字典没有按预期工作:一些小部件被正确存储,一些没有。 特别是,后来添加的小部件似乎存储得更频繁(当我多次退出并重新打开应用程序时)。
我为表单中的每个标签小部件 label
调用了 print(hex(id(label))
。
这表明某些标签具有相同的 Python id
,我相信只有最后一次迭代的具有任何特定 id 的小部件被存储。
这个“id 共享”真的是我的弱键字典没有存储所有小部件的原因吗? 为什么每个小部件没有自己的 ID? 我可以更改我的代码,使每个小部件都有一个唯一的 ID 吗? 我可以更改我的代码,使每个小部件都可以在我的弱键字典中只存储一次吗?
示例代码:
#!/usr/bin/env python3
from weakref import WeakKeyDictionary
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtWidgets import *
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.weak = WeakKeyDictionary()
self.clickme = QPushButton("Click me", self)
self.clickme.clicked.connect(self.open_settings)
@pyqtSlot()
def open_settings(self) -> None:
dialog = QDialog(self)
self.weak[dialog] = "Settings"
grid = QGridLayout()
tabs = QTabWidget(dialog)
tab0 = QWidget(tabs)
tabs.addTab(tab0, "Sample")
form0 = QFormLayout()
for char in "sample":
form0.addRow(char, QLineEdit(tab0))
tab0.setLayout(form0)
# print information
for row in range(form0.rowCount()):
label = form0.itemAt(row, form0.LabelRole).widget()
print(hex(id(label)), type(label).__name__)
self.weak[label] = "foobar"
print(flush=True)
for k, v in self.weak.items():
print(f"{k!r}: {v!r}")
print(flush=True)
grid.addWidget(tabs, 0, 0, 1, 3)
dialog.show()
dialog.exec()
if __name__ == "__main__":
app = QApplication([])
window = MainWindow()
window.show()
app.exec()
当整个应用程序为 运行:
时此示例代码的输出0x7f5956285670 QLabel # this is ok
0x7f5956285700 QLabel # this is ok
0x7f59562855e0 QLabel # this is ok
0x7f5956285670 QLabel # why the repeated id?!
0x7f5956285700 QLabel # why the repeated id?!
0x7f59562855e0 QLabel # why the repeated id?!
# the resulting weak-keys dict:
<PyQt5.QtWidgets.QDialog object at 0x7f5956342f70>: 'Settings'
<PyQt5.QtWidgets.QLabel object at 0x7f59562855e0>: 'foobar'
这似乎是一个错误,它似乎发生在用 C++ 创建对象(如 QFormLayout 的 QWidgetItem)然后从 python 访问它时,似乎 pyqt5 重用了对象(在 pyside2 中不会发生)。
一个可能的解决方案是在 python 中创建 QLabel,这样对象就不会被重复使用。
form0.addRow(<b>QLabel(</b>char<b>)</b>, QLineEdit())
此外,您必须访问 QLabel 而不是 QWidgetItem:
# print information
for row in range(form0.rowCount()):
label = form0.itemAt(row, form0.LabelRole)<b>.widget()</b>
print(hex(id(label)), type(label).__name__)
self.weak[label] = "foobar"
print(flush=True)
输出:
0x7f756703c0d0 QLabel
0x7f756703c1f0 QLabel
0x7f756703c310 QLabel
0x7f756703c430 QLabel
0x7f756703c550 QLabel
0x7f756703c670 QLabel
<PyQt5.QtWidgets.QDialog object at 0x7f756ef61dc0>: 'Settings'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c0d0>: 'foobar'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c1f0>: 'foobar'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c310>: 'foobar'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c430>: 'foobar'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c550>: 'foobar'
<PyQt5.QtWidgets.QLabel object at 0x7f756703c670>: 'foobar'