按钮代理问题
我希望有人能帮我解决这个让我头疼的问题。我已经用附上的代码在表格视图的一个列里插入了按钮。
问题是,要按这个按钮,我需要先双击一下包含这个按钮的单元格,才能激活它。等单元格激活后,我才能按按钮,所以总共需要三次点击才能按下去。这对普通用户来说可能会很困惑。
我把这个问题发到了pyqt的邮件列表,得到的回答是:
“发生的情况是,当TableWidget接收到一次点击时,它会创建一个编辑器,但这个编辑器还没有接收到点击。这在大多数情况下是没问题的,但如果你画了一个按钮,那就不太合适了。”
有没有人遇到过类似的问题?
提前谢谢你们,
Cris
class AnimLinkButtons(QtGui.QStyledItemDelegate):
mouse_isPressed = False
def __init__(self, parent = None):
QtGui.QStyledItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
column = index.column()
button = QtGui.QPushButton(parent)
button.setText(self.text(index))
ButtonLoc = self.ButtonLocation(option)
button.setGeometry(ButtonLoc)
Cellvalue = index.model().data(index, QtCore.Qt.EditRole)
row = index.row()
AssetId = index.model().data(index.model().index(row, 0)).toString()
AnimTurntablePath = shotsGetData.getAssetTurntablePath(AssetId)
browsePath = shotsGetData.getAssetPath(AssetId)
#toAvidComp = shotsGetData.gettoAvidComp(ShotId)
# Connect to button
button.clicked.connect(lambda: self.mousePressEvent(index, browsePath, AssetTurntablePath))
return button
def setEditorData(self, editor, index):
button = editor
if not button:
return
def setModelData(self, editor, model, index):
button = editor
if not button:
return
def updateEditorGeometry(self, editor, option, index):
ButtonLoc = self.ButtonLocation(option)
editor.setGeometry(ButtonLoc)
def paint(self, painter, option, index):
opt = QtGui.QStyleOptionButton()
#opt.icon = self.icon()
opt.text = self.text(index)
opt.rect = option.rect
opt.palette = option.palette
opt.rect = self.ButtonLocation(opt)
QtGui.QApplication.style().drawControl(QtGui.QStyle.CE_PushButton, opt, painter)
def ButtonLocation(self, option):
r = option.rect
x = r.left() + 10
y = r.top() + 10
w = 30;
h = 30
return QRect(x,y,w,h);
def text(self, index):
#print self.column
column = index.column()
if column == 7:
return QtCore.QString("Mov")
def mousePressEvent(self, index, browsePath , AssetTurntablePath):
column = index.column()
print "PRESSSED"
if column == 7:
subprocess.Popen(AnimTurntablePath, shell=True)
2 个回答
这个问题是很久之前写的,所以我不想花太多时间来回答……不过我几天前遇到了同样的问题。在StackOverflow上找到了解决办法,但现在找不到那篇帖子了,忘记收藏和点赞了……不过我还有解决方案文件的链接在GitHub上!
关键是要在QTableView上使用openPersistentEditor(index)
。在初始化视图时调用这个方法,你的委托控件就会立刻显示出来。
如果你使用了代理模型,记得在调用openPersistentEditor(index)时要使用代理模型的QModelInstances。
我最近做了类似的事情。如果你在 createEditor
中创建了按钮,那么在编辑模式下就能激活它。这种情况发生在你双击单元格的时候。所以,邮件列表中的评论是正确的。
如果不进入编辑状态来实现这个功能就比较复杂。你在 paint
方法中画的按钮其实只是个图形,并不是真正的按钮。你需要监控 editorEvent
来捕捉鼠标事件。它会捕捉到 mouseButtonPress
和 mouseButtonRelease
这些事件。
另外,如果你想要点击按钮时的“视觉”效果,那事情就更复杂了。无论如何,下面是一个基本的实现。它会发出 buttonClicked
信号,并带上 row
和 column
的信息。
注意:重绘依赖于代理会接收到其他列的事件。如果你使用 setItemDelegateForColumn
,那么很可能会出现问题,因为在这种情况下,代理只会接收来自那一列的事件。所以,我建议你把它应用于整个表格,并根据列来进行绘制。
class ButtonDelegate(QtGui.QStyledItemDelegate):
buttonClicked = QtCore.pyqtSignal(int, int)
def __init__(self, parent = None):
super(ButtonDelegate, self).__init__(parent)
self._pressed = None
def paint(self, painter, option, index):
painter.save()
opt = QtGui.QStyleOptionButton()
opt.text = index.data().toString()
opt.rect = option.rect
opt.palette = option.palette
if self._pressed and self._pressed == (index.row(), index.column()):
opt.state = QtGui.QStyle.State_Enabled | QtGui.QStyle.State_Sunken
else:
opt.state = QtGui.QStyle.State_Enabled | QtGui.QStyle.State_Raised
QtGui.QApplication.style().drawControl(QtGui.QStyle.CE_PushButton, opt, painter)
painter.restore()
def editorEvent(self, event, model, option, index):
if event.type() == QtCore.QEvent.MouseButtonPress:
# store the position that is clicked
self._pressed = (index.row(), index.column())
return True
elif event.type() == QtCore.QEvent.MouseButtonRelease:
if self._pressed == (index.row(), index.column()):
# we are at the same place, so emit
self.buttonClicked.emit(*self._pressed)
elif self._pressed:
# different place.
# force a repaint on the pressed cell by emitting a dataChanged
# Note: This is probably not the best idea
# but I've yet to find a better solution.
oldIndex = index.model().index(*self._pressed)
self._pressed = None
index.model().dataChanged.emit(oldIndex, oldIndex)
self._pressed = None
return True
else:
# for all other cases, default action will be fine
return super(ButtonDelegate, self).editorEvent(event, model, option, index)