我有两个列表小部件,它们本质上是项目选择器。当我在任一列表中选择一个项目时,我需要它来触发一个方法。这里只有一个程序有问题。我需要在选择更改或单击某个项目时发生该过程,但我不希望这种情况发生两次,这是我遇到的问题
它有点难以描述,但请看下面的代码。
场景一:两个列表都只连接“itemSelectionChanged” 点击“奶酪” 点击“否” 两者现在都已选中,当您交替单击它们时,没有任何反应,因为没有选择发生变化,但是当我从不同的列表中选择一个项目时,我需要调用 something_happened 方法。
场景 2:两个列表都只连接“已点击” 单击时会发生一些事情,但是当我单击并拖动以更改选择时,不会触发任何事件,因为“单击”没有发生并且 itemSelectionChanged 未连接到任何东西。 I need the something_happened method to be called when the selection changes in this manner also.
场景 3:两个列表都连接“itemSelectionChanged”和“clicked” 单击并拖动以更改选择会发生某些事情(好),但是当我在新项目上单击“单击”时,会发送两个信号,并且我想要发生的事件会发生两次(坏)。
鉴于上述情况,我想同时实现以下所有目标:
- 当我点击一个新项目时处理只有一个事件(同时发出 itemSelectionChanged 和 itemClicked 导致两个事件)
- 在我点击一个已选择的项目(它发出 itemClicked 信号)时处理一个事件
- 在我通过单击和拖动更改选择时处理一个事件(这会发出 itemSelectionChagned 信号)
示例代码如下:
from PyQt4 import QtGui
QtWidgets = QtGui
# from PyQt5 import QtWidgets
import sys
import time
def something_happened():
print(time.time())
app = QtWidgets.QApplication(sys.argv)
widget = QtWidgets.QWidget(None)
vlay = QtWidgets.QVBoxLayout(widget)
list_widget1 = QtWidgets.QListWidget(None)
list_widget1.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
list_widget1.addItems(("Cheese", "Whiz", "tastes", "great"))
list_widget1.itemSelectionChanged.connect(something_happened)
list_widget1.clicked.connect(something_happened)
list_widget2 = QtWidgets.QListWidget(None)
list_widget2.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
list_widget2.addItems(("No", "it", "tastes", "bad"))
list_widget2.itemSelectionChanged.connect(something_happened)
# list_widget2.clicked.connect(something_happened)
vlay.addWidget(list_widget1)
vlay.addWidget(list_widget2)
widget.show()
app.exec_()
最佳答案
在这个例子中,我继承了 QListWidget。这不是必需的,但我认为如果将选择更改的决定封装在小部件本身中,它会使代码更清晰。我制作了两种类型的小部件,每种小部件在决定选择是否更改方面都有自己的行为。您可以尝试使用 mousePress/mouseRelease/selectionChanged 组合,希望这段代码能为您提供所需的工具。 运行该程序,并注意两个小部件之间的差异。
class SomeFrame(object):
"""A container to place all the widgets, and control
present the output from the selections.
"""
def __init__(self):
self.frame = QtWidgets.QFrame()
# Creating content
list_widget1 = MyQListWidgetA()
list_widget1.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
list_widget1.addItems(("Cheese", "Whiz", "tastes", "great"))
list_widget2 = MyQListWidgetB()
list_widget2.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
list_widget2.addItems(("No", "it", "tastes", "bad"))
# Creating Layout
layout = QtWidgets.QHBoxLayout()
layout.addWidget(list_widget1)
layout.addWidget(list_widget2)
self.frame.setLayout(layout)
# Connections
from functools import partial
list_widget1.selection_changed.connect(partial(self.selectionChangedCB, 'list_1'))
list_widget2.selection_changed.connect(partial(self.selectionChangedCB, 'list_2'))
self.frame.show()
def selectionChangedCB(self, list_name, selected_items):
print(list_name + ' changed: ' + str(selected_items))
from PyQt5 import QtWidgets, QtCore
class MyQListWidgetA(QtWidgets.QListWidget):
"""This widget emits selection_changed whenever its
itemSelectionChanged signal is emitted, AND there
was an actual change in the selected items.
"""
selection_changed = QtCore.pyqtSignal(object)
def __init__(self):
QtWidgets.QListWidget.__init__(self)
self.selected_items = set()
self.itemSelectionChanged.connect(self.something_happened)
def something_happened(self):
# Create a set of the newly selected items, so we can compare
# to the old selected items set
newly_selected_items = set([item.text() for item in self.selectedItems()])
if newly_selected_items != self.selected_items:
# Only emit selection_changed signal if a change was detected.
self.selected_items = newly_selected_items
self.selection_changed.emit(self.selected_items)
class MyQListWidgetB(QtWidgets.QListWidget):
"""This widget emits selection_changed whenever it is pressed
(mimic "clicked" signal) and again when the user is done
selecting the items (mouse release) IFF the selection
has changed.
"""
selection_changed = QtCore.pyqtSignal(object)
def __init__(self):
QtWidgets.QListWidget.__init__(self)
self.selected_items = set()
def something_happened(self, initial_click=False):
# Create a set of the newly selected items, so we can
# compare to the old selected items set
newly_selected_items = set([item.text() for item in self.selectedItems()])
if newly_selected_items != self.selected_items:
# Only emit selection_changed signal if a change was detected
self.selected_items = newly_selected_items
self.selection_changed.emit(self.selected_items)
def mousePressEvent(self, event):
QtWidgets.QListWidget.mousePressEvent(self, event)
self.something_happened()
def mouseReleaseEvent(self, event):
QtWidgets.QListWidget.mouseReleaseEvent(self, event)
self.something_happened()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
theFrame = SomeFrame()
sys.exit(app.exec_())
编辑:我意识到我从来没有考虑过“当我点击一个已经选择的项目(它发出 itemClicked 信号)时处理一个事件”。 我把它看作是同一个单项点击,但它也可以解决同一个多项目点击(使用 mouseReleaseEvent,并比较旧列表和新列表)。 只需添加 MyQListWidgetC,并将其连接到它的两个信号。 B 和 C 之间的主要区别在于,在 mousePressEvent 中,我们检查是否按下了单个项目,以及它是否是同一项目。
class MyQListWidgetC(QtWidgets.QListWidget):
"""This widget emits selection_changed whenever it is pressed
(mimic "clicked" signal) and again when the user is done
selecting the items (mouse release) IFF the selection
has changed. If a single item was clicked, AND it is the
same item, the widget will emit same_item_clicked, which
the owner can listen to and decide what to do.
"""
selection_changed = QtCore.pyqtSignal(object)
same_item_clicked = QtCore.pyqtSignal(object)
def __init__(self):
QtWidgets.QListWidget.__init__(self)
self.selected_items = set()
def something_happened(self, initial_click=False):
# Create a set of the newly selected items, so we can
# compare to the old selected items set
newly_selected_items = set([item.text() for item in self.selectedItems()])
if newly_selected_items != self.selected_items:
# Only emit selection_changed signal if a change was detected
self.selected_items = newly_selected_items
self.selection_changed.emit(self.selected_items)
def mousePressEvent(self, event):
QtWidgets.QListWidget.mousePressEvent(self, event)
newly_selected_items = set([item.text() for item in self.selectedItems()])
if len(newly_selected_items) == 1 and newly_selected_items == self.selected_items:
self.same_item_clicked.emit(self.selected_items)
else:
self.something_happened()
def mouseReleaseEvent(self, event):
QtWidgets.QListWidget.mouseReleaseEvent(self, event)
self.something_happened()
关于python - QListWidget 处理来自点击和 itemSelectionChanged 的多个信号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42101214/