QTableView timer click and doubleclick event - python

I want to execute other command on click and double click. When doubleclick is executed, click does not want to be executed.
from PySide2 import QtWidgets, QtCore, QtGui
class TestView(QtWidgets.QTableView):
custom_clicked = QtCore.Signal(QtCore.QModelIndex)
custom_double_clicked = QtCore.Signal(QtCore.QModelIndex)
def __init__(self, parent=None):
super(TestView, self).__init__(parent=parent)
self.timer = QtCore.QTimer(self)
self.timer.setInterval(250)
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.timeout)
self.click_number = 0
def mousePressEvent(self, event):
super(TestView, self).mousePressEvent(event)
if event.button() == QtCore.Qt.LeftButton:
self.index = self.indexAt(event.pos())
self.click_number += 1
if not self.timer.isActive():
self.timer.start()
def timeout(self):
if self.click_number < 2:
self.custom_clicked.emit(self.index)
elif self.click_number > 1:
self.custom_double_clicked.emit(self.index)
self.click_number = 0
def click_command(index):
print "single", index
def double_click_command(index):
print "double", index
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
tableView = TestView()
model = QtGui.QStandardItemModel()
item = QtGui.QStandardItem()
item.setData("test", QtCore.Qt.DisplayRole)
model.setItem(0, item)
tableView.setModel(model)
tableView.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
tableView.custom_clicked[QtCore.QModelIndex].connect(click_command)
tableView.custom_double_clicked[QtCore.QModelIndex].connect(double_click_command)
tableView.show()
sys.exit(app.exec_())
This code works well when there is no item.
However, it will break if you double-click the item.
Timer seems to be the cause, but I'm looking for a reason.

To detect the double click you should only use the mouseDoubleClickEvent so you only have to detect that it was not pressed a second time in a certain time:
class TestView(QtWidgets.QTableView):
custom_clicked = QtCore.Signal(QtCore.QModelIndex)
custom_double_clicked = QtCore.Signal(QtCore.QModelIndex)
def __init__(self, parent=None):
super(TestView, self).__init__(parent)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.timeout)
self.timer.setInterval(
QtGui.QGuiApplication.styleHints().mouseDoubleClickInterval()
)
self.timer.setSingleShot(True)
def mousePressEvent(self, event):
super(TestView, self).mousePressEvent(event)
if event.button() == QtCore.Qt.LeftButton:
self.index = self.indexAt(event.pos())
self.timer.start()
def mouseDoubleClickEvent(self, event):
super(TestView, self).mouseDoubleClickEvent(event)
if event.button() == QtCore.Qt.LeftButton:
index = self.indexAt(event.pos())
self.custom_double_clicked.emit(index)
self.timer.stop()
def timeout(self):
self.custom_clicked.emit(self.index)

Related

How to put multiple QPixmap in class which inherits from QtWidgets.QGraphicsPixmapItem?

Today , I got stuck with QPixmap. My class is inheriting from QtWidgets.QGraphicsPixmapItem and like in example below.
class graphicButton(QtWidgets.QGraphicsPixmapItem):
def __init__(self):
pixmapA = QtGui.QPixmap(r"img.png")
QtWidgets.QGraphicsPixmapItem.__init__(self, pixmapA)
self.setFlags(
self.flags( )
| QtWidgets.QGraphicsItem.ItemIsSelectable
| QtWidgets.QGraphicsItem.ItemIsMovable
)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
print("mouse left press")
event.accept()
elif event.button() == QtCore.Qt.RightButton:
print("mouse right press")
event.accept()
elif event.button() == QtCore.Qt.MidButton:
print("mouse middle press")
event.accept()
It's working fine, but what if I want to put more then one picture? In most cases which I found in google, you need to make multiple QGraphicsPixmapItems. In that case I can't anymore inherit from QGraphicsPixItem and I have to go for QGraphicsItem or am I missing something ?
The simple solution is to load an image associated with its position, for example in the following case I place the images in the vertices of a pentagon:
import random
from PyQt5 import QtCore, QtGui, QtWidgets
class GraphicsButton(QtWidgets.QGraphicsPixmapItem):
def __init__(self, name, pixmap, parent=None):
super(GraphicsButton, self).__init__(pixmap, parent)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsSelectable, True)
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
self._name = name
#property
def name(self):
return self._name
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
print("mouse left press")
elif event.button() == QtCore.Qt.RightButton:
print("mouse right press")
elif event.button() == QtCore.Qt.MidButton:
print("mouse middle press")
print(self.name)
super(GraphicsButton, self).mousePressEvent(event)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
scene = QtWidgets.QGraphicsScene()
view = QtWidgets.QGraphicsView(scene)
self.setCentralWidget(view)
# coordinates of the pentagon
datas = [
("name1", "img0.png", QtCore.QPointF(0, -200)),
("name2", "img1.png", QtCore.QPointF(-190, -62)),
("name3", "img2.png", QtCore.QPointF(-118, 162)),
("name4", "img3.png", QtCore.QPointF(118, 162)),
("name5", "img0.png", QtCore.QPointF(190, -62)),
]
for name, path, position in datas:
item = GraphicsButton(name, QtGui.QPixmap(path))
scene.addItem(item)
item.setPos(position)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

PyQt5 Moveable button causes screen smear

User defined button that will hold an image and is moveable causes screen smearing when moved to the left and causes a screen smear to the right of the widget. Any ideas?
Image of smearing to the right of the playing card - vertical grey lines.
As i stated this only happens when moving the button to the left.
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from functools import partial
import datetime,psutil,sys
class playingCard(QPushButton):
def __init__(self, Text = '', parent = None):
super(playingCard, self).__init__()
self.ftop = 10
self.fleft = 10
self.fwidth = 87
self.fheight = 124
self.initUI()
def initUI(self):
self.setGeometry(self.fleft, self.ftop, self.fwidth+2, self.fheight+2)
self.setText('')
pixmap = QPixmap('clubs1.png')
pixmap = pixmap.scaled(self.fwidth,self.fheight, Qt.KeepAspectRatio, Qt.FastTransformation)
buttonicon = QIcon(pixmap)
self.setIcon(buttonicon)
self.setIconSize( QSize(self.fwidth,self.fheight))
self.setFixedSize( QSize(self.fwidth+2,self.fheight+2))
def mousePressEvent(self, event):
self.__mousePressPos = None
self.__mouseMovePos = None
if event.button() == Qt.LeftButton:
self.__mousePressPos = event.globalPos()
self.__mouseMovePos = event.globalPos()
super(playingCard, self).mousePressEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() == Qt.LeftButton:
# adjust offset from clicked point to origin of widget
currPos = self.mapToGlobal(self.pos())
globalPos = event.globalPos()
diff = globalPos - self.__mouseMovePos
newPos = self.mapFromGlobal(currPos + diff)
self.move(newPos)
self.__mouseMovePos = globalPos
super(playingCard, self).mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
if self.__mousePressPos is not None:
moved = event.globalPos() - self.__mousePressPos
if moved.manhattanLength() > 3:
event.ignore()
return
super(playingCard, self).mouseReleaseEvent(event)
class Example(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def addAction(self,name,shortcut,status):
self.actions[name] = QAction(name, self)
self.actions[name].setShortcut(shortcut)
self.actions[name].setStatusTip(status)
if name == 'Exit':
self.actions[name].triggered.connect( self.close )
else:
self.actions[name].triggered.connect( partial(self.viewEvent,name) )
def hastab(self,tabname):
return self.tabWidget.findChild(QWidget, tabname) != None
def ontab(self,tabname):
currentIndex = self.tabWidget.currentIndex()
currentTitle = self.tabWidget.tabText(currentIndex)
return tabname == currentTitle
def gettab(self,tabname):
page = self.tabWidget.findChild(QWidget, tabname)
return self.tabWidget.indexOf(page)
def initUI(self):
self.actions = dict() # holds the QActions
self.tabs = dict()
self.tabWidget = QTabWidget()
self.tabWidget.setTabsClosable(True)
self.tabWidget.tabCloseRequested.connect(self.closeTab)
self.setCentralWidget(self.tabWidget)
self.addAction('Exit', 'Ctrl+Q','Exit application')
self.addAction('Game','Ctrl+G','Game')
self.statusBar()
menubar = self.menuBar()
fileMenu = menubar.addMenu('&File')
fileMenu.addAction( self.actions['Game'] )
fileMenu.addAction( self.actions['Exit'] )
self.setWindowTitle('Main window')
self.showMaximized()
def closeTab (self, currentIndex):
currentQWidget = self.tabWidget.widget(currentIndex)
title=self.tabWidget.tabText(currentIndex)
currentQWidget.deleteLater()
self.tabWidget.removeTab(currentIndex)
del self.tabs[title]
del self.tables[title]
self.timers[title].stop()
del self.timers[title]
def keyPressEvent(self, e):
currentIndex=self.tabWidget.currentIndex()
title = None
if currentIndex != -1:
title=self.tabWidget.tabText(currentIndex)
if e.key() == Qt.Key_F11:
if self.isMaximized():
self.showNormal()
else:
self.showMaximized()
def viewEvent(self, name):
if name in self.tabs:
return
self.tabs[name] = QWidget()
vbox = QVBoxLayout()
vbox.addWidget( playingCard() )
# Add box layout, add table to box layout and add box layout to widget
self.tabs[name].layout = vbox
self.tabs[name].setLayout(self.tabs[name].layout)
self.tabWidget.addTab(self.tabs[name],name)
def closeEvent(self, event):
reply = QMessageBox.question(self, 'Message',
"Are you sure to quit?", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

How to enable drag selection after enabling dragging in QListWidget?

I have a list widget which I need to drag items from it to another list widget. After enabling dragging by
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
I can not drag select any more. I figured it's related to mousePressEvent. How can I reimplement it to enable dragging selection at the right side of the blank area of the list widget?
my code
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import Qt, QString
import sys
import os
class ThumbListWidget(QtGui.QListWidget):
_rows_to_del = []
def __init__(self, type, parent=None):
super(ThumbListWidget, self).__init__(parent)
self.setIconSize(QtCore.QSize(124, 124))
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
self.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.setAcceptDrops(True)
self._dropping = False
self.setSelectionRectVisible(True)
self.connect(self, QtCore.SIGNAL("dropped"), self.items_dropped)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.accept()
else:
super(ThumbListWidget, self).dragEnterEvent(event)
def dragMoveEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
else:
super(ThumbListWidget, self).dragMoveEvent(event)
def dropEvent(self, event):
if event.mimeData().hasUrls():
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
links = []
for url in event.mimeData().urls():
links.append(str(url.toLocalFile()))
self.emit(QtCore.SIGNAL("dropped"), links)
else:
if event.source() is self:
event.setDropAction(QtCore.Qt.MoveAction)
else:
event.setDropAction(QtCore.Qt.CopyAction)
self._dropping = True
super(ThumbListWidget, self).dropEvent(event)
self._dropping = False
def rowsInserted(self, parent, start, end):
if self._dropping:
self.emit(QtCore.SIGNAL("dropped"), (start, end))
super(ThumbListWidget, self).rowsInserted(parent, start, end)
def dataChanged(self, start, end):
if self._dropping:
for row in range(start.row(), end.row() + 1):
index = self.indexFromItem(self.item(row))
shot = index.data().toString()
print len(self.findItems(shot, Qt.MatchExactly))
if len(self.findItems(shot, Qt.MatchExactly)) > 1:
self._rows_to_del.append(row)
self._rows_to_del.reverse()
for row in self._rows_to_del:
self.takeItem(row)
self._rows_to_del = []
def items_dropped(self, arg):
start, end = arg
print range(start, end + 1)
for row in range(start, end + 1):
item = self.item(row)
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
item.setCheckState(Qt.Checked)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Space:
if self.selectedItems():
new_state = Qt.Unchecked if self.selectedItems()[0].checkState() else Qt.Checked
for item in self.selectedItems():
if item.flags() & Qt.ItemIsUserCheckable:
# print 'set new state:', new_state
item.setCheckState(new_state)
self.reset()
elif event.key() == Qt.Key_Delete:
for item in self.selectedItems():
self.takeItem(self.row(item))
class Dialog_01(QtGui.QMainWindow):
def __init__(self):
super(QtGui.QMainWindow, self).__init__()
self.listItems = {}
myQWidget = QtGui.QWidget()
myBoxLayout = QtGui.QVBoxLayout()
myQWidget.setLayout(myBoxLayout)
self.setCentralWidget(myQWidget)
self.listWidgetA = ThumbListWidget(self)
for i in range(12):
QtGui.QListWidgetItem('Item ' + str(i), self.listWidgetA)
all_items = self.listWidgetA.findItems(QString('*'), Qt.MatchWrap | Qt.MatchWildcard)
for item in all_items:
item.setFlags(item.flags() & ~Qt.ItemIsUserCheckable)
myBoxLayout.addWidget(self.listWidgetA)
self.listWidgetB = ThumbListWidget(self)
self.listWidgetA.setAcceptDrops(False)
myBoxLayout.addWidget(self.listWidgetB)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
dialog_1 = Dialog_01()
dialog_1.show()
dialog_1.resize(480, 320)
sys.exit(app.exec_())
gyfcat of current look or
If I comment out that line, I'll be able to drag select as the following screen cast shows.
gyfcat of desired look or

Pyqt Mouse MouseButtonDblClick event

I have some trouble to distinguish between a single and a double mouse click event. I have created an event filter but a mouse double click also gives me a single signal back. In my code I have to separate both events to connect to different functions. Can anybody suggest me how to do this?
Here is an example. What I want is, if a double mouse click happen, only the MouseButtonDblClick should give a signal and not the LeftButton and MouseButtonDblClick:
# coding: utf-8
import sys
from PyQt4 import QtCore, QtGui
class MyDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.button1 = QtGui.QPushButton("Button 1")
self.button2 = QtGui.QPushButton("Button 2")
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.button1)
hbox.addWidget(self.button2)
self.setLayout(hbox)
self.button1.installEventFilter(self)
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.MouseButtonPress:
if event.button() == QtCore.Qt.LeftButton:
#If image is left clicked, display a red bar.
print 'one left'
elif event.button() == QtCore.Qt.RightButton:
print 'one right'
elif event.type() == QtCore.QEvent.MouseButtonDblClick:
#If image is double clicked, remove bar.
print 'two'
return super(MyDialog, self).eventFilter(obj, event)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
w = MyDialog()
w.show()
sys.exit(app.exec_())
Thank you in advance!
Stefanie
It's kind of a hack, but it should do the trick.
Also, I used new-style signals instead of your event filter, something you should consider.
Here, the ClickHandler class counts the number of clicks between the first click and the timeout event of its timer.
from PyQt4 import QtCore, QtGui
class ClickHandler():
def __init__(self, time):
self.timer = QtCore.QTimer()
self.timer.setInterval(time)
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.timeout)
self.click_count = 0
def timeout(self):
if self.click_count == 1:
print('Single click')
elif self.click_count > 1:
print('Double click')
self.click_count = 0
def __call__(self):
self.click_count += 1
if not self.timer.isActive():
self.timer.start()
class MyDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.button1 = QtGui.QPushButton("Button 1")
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.button1)
self.setLayout(hbox)
self.click_handler = ClickHandler(300)
self.button1.clicked.connect(self.click_handler)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = MyDialog()
w.show()
sys.exit(app.exec_())
EDIT : A second cleaner version with a CustomButton class that handles left and right click signals:
from PyQt4 import QtCore, QtGui
class CustomButton(QtGui.QPushButton):
left_clicked= QtCore.pyqtSignal(int)
right_clicked = QtCore.pyqtSignal(int)
def __init__(self, *args, **kwargs):
QtGui.QPushButton.__init__(self, *args, **kwargs)
self.timer = QtCore.QTimer()
self.timer.setInterval(250)
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.timeout)
self.left_click_count = self.right_click_count = 0
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
self.left_click_count += 1
if not self.timer.isActive():
self.timer.start()
if event.button() == QtCore.Qt.RightButton:
self.right_click_count += 1
if not self.timer.isActive():
self.timer.start()
def timeout(self):
if self.left_click_count >= self.right_click_count:
self.left_clicked.emit(self.left_click_count)
else:
self.right_clicked.emit(self.right_click_count)
self.left_click_count = self.right_click_count = 0
class MyDialog(QtGui.QDialog):
def __init__(self, parent=None):
super(MyDialog, self).__init__(parent)
self.button1 = CustomButton("Button 1")
hbox = QtGui.QHBoxLayout()
hbox.addWidget(self.button1)
self.setLayout(hbox)
self.button1.left_clicked[int].connect(self.left_click)
self.button1.right_clicked[int].connect(self.right_click)
def left_click(self, nb):
if nb == 1: print('Single left click')
else: print('Double left click')
def right_click(self, nb):
if nb == 1: print('Single right click')
else: print('Double right click')
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
w = MyDialog()
w.show()
sys.exit(app.exec_())
You should have a look at this thread on Double-Click-Capturing.
A Timer might do the job. However, it is probably a bad idea to have unrelated single clicks and double clicks (see Bill's answer to "Distinguish between single and double click events in Qt").

How to capture the Key_tab event

i am trying to capture the key_tab event, but no luck. i realized that it only works if there are no other widgets so the cursor has no where to go only then can i get the event to return. here is a simplified code sample.
class MyCombo(QComboBox):
def __init__(self, parent=None):
super(MyCombo, self).__init__(parent)
self.setEditable(True)
def keyPressEvent(self, event):
if (event.type() == QEvent.KeyPress) and (event.key() == Qt.Key_Tab):
print "tab pressed"
elif event.key() == Qt.Key_Return:
print "return pressed"
else:
QComboBox.keyPressEvent(self, event)
class Form_1(QDialog):
def __init__(self, parent=None):
super(Form_1, self).__init__(parent)
self.combo = MyCombo()
self.line = QLineEdit()
layout = QVBoxLayout()
layout.addWidget(self.combo)
layout.addWidget(self.line)
self.setLayout(layout)
app = QApplication(sys.argv)
form = Form_1()
form.show()
app.exec_()
if i comment out the following two lines
self.line = QLineEdit()
layout.addWidget(self.line)
then it works fine, because there is only one widget left on the form.
where am i going wrong?
Cheers, Joe
Apparently the Key_Tab press event is never passed to any handler but to the setFocus() so in order to intercept the Key_Tab event, we need to implement the event() method itself.
so here is the new code:
class MyCombo(QComboBox):
def __init__(self, parent=None):
super(MyCombo, self).__init__(parent)
self.setEditable(True)
def keyPressEvent(self, event):
if event.key() == Qt.Key_Return:
print "return pressed"
else:
QComboBox.keyPressEvent(self, event)
def event(self, event):
if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Tab:
print "tab pressed"
return False
return QWidget.event(self, event)
class Form_1(QDialog):
def __init__(self, parent=None):
super(Form_1, self).__init__(parent)
self.combo = MyCombo()
self.line = QLineEdit()
layout = QVBoxLayout()
layout.addWidget(self.combo)
layout.addWidget(self.line)
self.setLayout(layout)

Categories

Resources