无法连接 PySide QTableView selectionChanged 信号
Cannot connect PySide QTableView selectionChanged signal
我正在设计一个由 3D 查看器和 table 以及 Python 3.4 和 PySide 绑定组成的程序。
我用这个 class:
创建了一个 TableView
from PySide import QtGui
from PySide.QtCore import Qt
class MyTableView(QtGui.QWidget):
def __init__(self, parent=None):
super(MyTableView, self).__init__()
self.parent = parent
self.title = "Results"
self.initUI()
def initUI(self):
self.grid = QtGui.QGridLayout(self)
self.table = QtGui.QTableView()
self.grid.addWidget(self.table, 0, 0)
# Configure table
self.table.verticalHeader().setVisible(False)
self.table.horizontalHeader().setDefaultAlignment(Qt.AlignLeft)
self.table.setSortingEnabled(True)
self.table.setAlternatingRowColors(True)
self.table.setShowGrid(False)
self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
和这个其他的模型 class:
class MyModel(QStandardItemModel):
def __init__(self, path, *args, **kwargs):
super(MyModel, self).__init__()
self.path = path
self.parse()
def parse(self):
with open(self.path) as f:
self.mydata = yaml.load(f)
self.setColumnCount(len(self.mydata['headers']) + 1)
self.setHorizontalHeaderLabels(
['ID'] + self.mydata['headers'])
row = 0
for ind, val in self.mydata['rows'].items():
col = 0
self.insertRow(row)
self.setItem(row, col, QStandardItem(ind))
for v in val:
col += 1
self.setItem(row, col, QStandardItem(str(v)))
row += 1
然后在这个控制器中绑定在一起:
from PySide.QtCore import Qt
class MyController(object):
def __init__(self, model, view):
self.model = model
self.tableview = view.table
self.fill_table()
self.connect_signals()
def fill_table(self):
self.tableview.setModel(self.model)
self.tableview.sortByColumn(0, Qt.AscendingOrder)
def connect_signals(self):
selectionModel = self.tableview.selectionModel()
selectionModel.selectionChanged.connect(self.selection_changed)
def selection_changed(self, selected, deselected):
print("Selection changed.")
然后,通过这个脚本执行程序:
def main():
app = QtGui.QApplication(sys.argv)
MyController(MyModel(sys.argv[1]), MyView())
sys.exit(app.exec_())
if __name__ == '__main__':
main()
(请注意,我还没有发布主要 window class,但你明白了)
table 渲染正常,但我无法将 selectionChanged 信号连接到处理程序(它应该更新查看器,但出于测试目的,它只是一个打印语句)。
我做错了什么?谢谢!
[编辑]
我发现如果我使用 lambda 函数调用处理程序方法,它会起作用。谁能解释一下为什么?!
selectionModel.selectionChanged.connect(lambda: self.selection_changed(selectionModel.selectedRows()))
我尝试实施您编写的内容并且它有效 - 所以我不能 100% 确定您遇到问题的原因。但我怀疑这是因为我必须修复才能让它正常工作:我必须解决你在垃圾收集方面遇到的一些问题。
在您提供的示例代码中,您创建了一个 MyController
、一个 MyModel
和一个 MyView
。但是它们都将被垃圾收集(在 CPython 中),因为您没有保留对它们的引用。如果您添加对 MyController
的引用
my_controller = MyController(MyModel(sys.argv[1]), MyView())
你快到了,但我认为 MyTableView
也可能会被垃圾收集,因为控制器只保留对 QTableVIew
的引用而不是 MyTableView
.
大概使用 lanbda 函数会更改您保留的引用 - 它会保留控制器和选择模型 - 这可能就是它在这种情况下起作用的原因。
一般来说,使用 Qt 父子机制是个好主意。如果您只是将所有这些对象添加到主 window(或它们的自然父小部件)上,那将可以避免大多数这些问题。
我正在设计一个由 3D 查看器和 table 以及 Python 3.4 和 PySide 绑定组成的程序。
我用这个 class:
创建了一个 TableViewfrom PySide import QtGui
from PySide.QtCore import Qt
class MyTableView(QtGui.QWidget):
def __init__(self, parent=None):
super(MyTableView, self).__init__()
self.parent = parent
self.title = "Results"
self.initUI()
def initUI(self):
self.grid = QtGui.QGridLayout(self)
self.table = QtGui.QTableView()
self.grid.addWidget(self.table, 0, 0)
# Configure table
self.table.verticalHeader().setVisible(False)
self.table.horizontalHeader().setDefaultAlignment(Qt.AlignLeft)
self.table.setSortingEnabled(True)
self.table.setAlternatingRowColors(True)
self.table.setShowGrid(False)
self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
和这个其他的模型 class:
class MyModel(QStandardItemModel):
def __init__(self, path, *args, **kwargs):
super(MyModel, self).__init__()
self.path = path
self.parse()
def parse(self):
with open(self.path) as f:
self.mydata = yaml.load(f)
self.setColumnCount(len(self.mydata['headers']) + 1)
self.setHorizontalHeaderLabels(
['ID'] + self.mydata['headers'])
row = 0
for ind, val in self.mydata['rows'].items():
col = 0
self.insertRow(row)
self.setItem(row, col, QStandardItem(ind))
for v in val:
col += 1
self.setItem(row, col, QStandardItem(str(v)))
row += 1
然后在这个控制器中绑定在一起:
from PySide.QtCore import Qt
class MyController(object):
def __init__(self, model, view):
self.model = model
self.tableview = view.table
self.fill_table()
self.connect_signals()
def fill_table(self):
self.tableview.setModel(self.model)
self.tableview.sortByColumn(0, Qt.AscendingOrder)
def connect_signals(self):
selectionModel = self.tableview.selectionModel()
selectionModel.selectionChanged.connect(self.selection_changed)
def selection_changed(self, selected, deselected):
print("Selection changed.")
然后,通过这个脚本执行程序:
def main():
app = QtGui.QApplication(sys.argv)
MyController(MyModel(sys.argv[1]), MyView())
sys.exit(app.exec_())
if __name__ == '__main__':
main()
(请注意,我还没有发布主要 window class,但你明白了)
table 渲染正常,但我无法将 selectionChanged 信号连接到处理程序(它应该更新查看器,但出于测试目的,它只是一个打印语句)。
我做错了什么?谢谢!
[编辑] 我发现如果我使用 lambda 函数调用处理程序方法,它会起作用。谁能解释一下为什么?!
selectionModel.selectionChanged.connect(lambda: self.selection_changed(selectionModel.selectedRows()))
我尝试实施您编写的内容并且它有效 - 所以我不能 100% 确定您遇到问题的原因。但我怀疑这是因为我必须修复才能让它正常工作:我必须解决你在垃圾收集方面遇到的一些问题。
在您提供的示例代码中,您创建了一个 MyController
、一个 MyModel
和一个 MyView
。但是它们都将被垃圾收集(在 CPython 中),因为您没有保留对它们的引用。如果您添加对 MyController
my_controller = MyController(MyModel(sys.argv[1]), MyView())
你快到了,但我认为 MyTableView
也可能会被垃圾收集,因为控制器只保留对 QTableVIew
的引用而不是 MyTableView
.
大概使用 lanbda 函数会更改您保留的引用 - 它会保留控制器和选择模型 - 这可能就是它在这种情况下起作用的原因。
一般来说,使用 Qt 父子机制是个好主意。如果您只是将所有这些对象添加到主 window(或它们的自然父小部件)上,那将可以避免大多数这些问题。