Differentiate single click from double click in pyside - python

I have tried to implement in Pyside the method described in How to distinguish between mouseReleaseEvent and mousedoubleClickEvent on QGrapnhicsScene, but I had to add a crufty flag to keep it from showing single click after the second button release in a double click.
Is there a better way?
import sys
from PySide import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
"""an attempt to implement
https://stackoverflow.com/questions/18021691/how-to-distinguish-between-mousereleaseevent-and-mousedoubleclickevent-on-qgrapn
main()
connect(timer, SIGNAL(timeout()), this, SLOT(singleClick()));
mouseReleaseEvent()
timer->start();
mouseDoubleClickEvent()
timer->stop();
singleClick()
// Do single click behavior
"""
self.timer = QtCore.QTimer()
self.timer.setSingleShot(True)
# had to add a "double_clicked" flag
self.double_clicked = False
self.timer.timeout.connect(self.singleClick)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Single click, double click')
self.show()
def mouseReleaseEvent(self, event):
if not self.double_clicked:
self.timer.start(200)
else:
self.double_clicked = False
def mouseDoubleClickEvent(self, event):
self.timer.stop()
self.double_clicked = True
print 'double click'
def singleClick(self):
print 'singleClick'
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())

Well, as you've discovered, the original description is incomplete.
It gives a solution for distinguishing between the first click of a double-click and single-click, but not the second click of a double-click and a single-click.
The simplest solution for distinguishing the second click is to use a flag.
PS: you could slightly improve your example by using QtGui.qApp.doubleClickInterval for the timer interval.

Related

How to detect any mouse click on PySide Gui?

I am trying implement a feature such that when a mouse is clicked on the gui, a function is triggered
Below is my mouse click detection, it doesn't work when I click on any part of the gui
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class Main(QWidget):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
layout = QHBoxLayout(self)
layout.addWidget(QLabel("this is the main frame"))
layout.gui_clicked.connect(self.anotherSlot)
def anotherSlot(self, passed):
print passed
print "now I'm in Main.anotherSlot"
class MyLayout(QHBoxLayout):
gui_clicked = Signal(str)
def __init__(self, parent=None):
super(MyLayout, self).__init__(parent)
def mousePressEvent(self, event):
print "Mouse Clicked"
self.gui_clicked.emit("emit the signal")
a = QApplication([])
m = Main()
m.show()
sys.exit(a.exec_())
This is my goal
Mouseclick.gui_clicked.connect(do_something)
Any advice would be appreciated
Define mousePressEvent inside Main:
from PySide.QtCore import *
from PySide.QtGui import *
import sys
class Main(QWidget):
def __init__(self, parent=None):
super(Main, self).__init__(parent)
layout = QHBoxLayout(self)
layout.addWidget(QLabel("this is the main frame"))
def mousePressEvent(self, QMouseEvent):
#print mouse position
print QMouseEvent.pos()
a = QApplication([])
m = Main()
m.show()
sys.exit(a.exec_())
This can get complicated depending on your needs. In short, the solution is an eventFilter installed on the application. This will listen the whole application for an event. The problem is "event propagation". If a widget doesn't handle an event, it'll be passed to the parent (and so on). You'll see those events multiple times. In your case, for example QLabel doesn't do anything with a mouse press event, therefore the parent (your main window) gets it.
If you actually filter the event (i.e. you don't want the original widget to respond to the event), you won't get that problem. But, I doubt that this is your intent.
A simple example for just monitoring:
import sys
from PySide import QtGui, QtCore
class MouseDetector(QtCore.QObject):
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.MouseButtonPress:
print 'mouse pressed', obj
return super(MouseDetector, self).eventFilter(obj, event)
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
layout = QtGui.QHBoxLayout()
layout.addWidget(QtGui.QLabel('this is a label'))
layout.addWidget(QtGui.QPushButton('Button'))
self.setLayout(layout)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
mouseFilter = MouseDetector()
app.installEventFilter(mouseFilter)
main = MainWindow()
main.show()
sys.exit(app.exec_())
You can see that, clicking on the QLabel will give you something like:
mouse pressed <PySide.QtGui.QLabel object at 0x02B92490>
mouse pressed <__main__.MainWindow object at 0x02B92440>
Because, QLabel receives the event and since it doesn't do anything with it, it's ignored and passed to the parent (MainWindow). And it's caught by the filter/monitor again.
Clicking on the QPushButton doesn't have any problem because it uses that event and does not pass to the parent.
PS: Also note that this can cause performance problems since you are inspecting every single event in the application.

Why does itemAt() not always find QGraphicsItem

Consider this extremely simple example in which you can drag a square around a QGraphicsScene (using PyQt, C++ users read self as this)
import sys
from PyQt4 import QtGui, QtCore
class MainWindowUi(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
self.scene = Scene(0, 0, 300, 300, self)
self.view = QtGui.QGraphicsView()
self.setCentralWidget(self.view)
self.view.setScene(self.scene)
self.scene.addItem(Square(0,0,50,50))
class Scene(QtGui.QGraphicsScene):
def mousePressEvent(self, e):
self.currentItem = self.itemAt(e.pos())
print (self.currentItem)
QtGui.QGraphicsScene.mousePressEvent(self, e)
class Square(QtGui.QGraphicsRectItem):
def __init__(self, *args):
QtGui.QGraphicsRectItem.__init__(self, *args)
self.setFlag(QtGui.QGraphicsItem.ItemIsMovable, True)
self.setFlag(QtGui.QGraphicsItem.ItemIsSelectable, True)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
win = MainWindowUi()
win.show()
sys.exit(app.exec_())
When you click the mouse in the scene you should see a print statement telling you that you clicked either on the square or on nothing (ie. None). This works if you just start the program and click on the square.
Now drag the square away from the upper left corner and click on it again. This time itemAt() return None even when you click on the square.
What's going on?
The answer seems to be that I should have used self.itemAt(e.scenePos()) instead of self.itemAt(e.pos()). I found this in this SO question.
I note here that the reason I've been having trouble finding information on this issue is that moving QGraphicsItems around in a QGraphicsScene is not what Qt calls "drag and drop". To search for information on this topic you want to search for things like "move QGraphicsItem".

Can PyQt QWebPage linkClicked signal detect which mouse button was used

I'm currently connecting this signal with a function that handles the click. For example:
QtCore.QObject.connect(self.ui.webView, QtCore.SIGNAL("linkClicked (const QUrl&)"), self.navigate)
def navigate(self, url):
#do something
What I'd like to is for the for the navigate function to know if the link was clicked on by the left or right mouse button. Something like
def navigate(self, url, event):
if event == Qt.LeftClick:
#do something
if event == Qt.MidClick:
#do something else
Is this possible?
Another method is reimplementing the mousePressEvent and filtering mouse events there:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from PyQt4 import QtCore, QtGui
class myWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(myWindow, self).__init__(parent)
self.label = QtGui.QLabel(self)
self.label.setText("Click Me")
self.layout = QtGui.QHBoxLayout(self)
self.layout.addWidget(self.label)
def mousePressEvent(self, event):
if event.buttons() == QtCore.Qt.LeftButton:
self.label.setText("Left Mouse Click!")
elif event.buttons() == QtCore.Qt.RightButton:
self.label.setText("Right Mouse Click!")
return super(myWindow, self).mousePressEvent(event)
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('myWindow')
main = myWindow()
main.resize(150, 150)
main.show()
sys.exit(app.exec_())
You should subclass QWebView, and override mousePressEvent. You could store in a variable the button that was pressed using the button() function of the QMouseEvent .
In your slot you can simply check the value of the last button pressed and handle it the way you want.
If you take a look at the docs there's some information on which buttons have been pressed in the QMouseEvent and you can handle them inside an event filter: QMouseEvent.button docs.

PyQt4 how to restore a minimized window?

PyQt4 how to do if the window is minimized, call a method expand that window
please tell me how to do it
I will be very grateful
You can check the current state of a QWidget by calling its windowState() method. To change the state you pass a new state to setWindowState().
Here's an example app that checks every 5 seconds to see if it's minimised. If it is then the window is restored.
This is just as an example - checking every 5 seconds for a minimised window and restoring it would be an evil thing to do in an app ;).
import sys
import time
from PyQt4.QtGui import QApplication, QWidget
from PyQt4.QtCore import QTimer, Qt
class MyWidget(QWidget):
def __init__(self):
QWidget.__init__(self)
self.timer = QTimer()
self.timer.setInterval(5000)
self.timer.timeout.connect(self.check_state)
self.timer.start()
def check_state(self):
if self.windowState() == Qt.WindowMinimized:
# Window is minimised. Restore it.
self.setWindowState(Qt.WindowNoState)
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = MyWidget()
widget.show()
sys.exit(app.exec_())
The most accurate way to do this would be to watch for the QWindowStateChangeEvent of the widget, and respond immediately when it happens. You can do this more than one way.
Here is how you can re-implement the event method of the target widget:
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
def event(self, e):
if e.type() == e.WindowStateChange:
if self.windowState() & QtCore.Qt.WindowMinimized:
print "Minimized"
# self.showMaximized()
# call the super class event() no matter what
return super(Window, self).event(e)
Now if you have some other widget that you want to watch for Minimize, and you don't want to have to define a new event method on that object, you can create an object that simply watches events for multiple other objects. It is called an event filter:
class Watcher(QtCore.QObject):
def eventFilter(self, obj, e):
if obj.isWidgetType() and e.type() == e.WindowStateChange:
if obj.windowState() & QtCore.Qt.WindowMinimized:
print "Minimized"
# obj.showMaximized()
return False
app = QtGui.QApplication([])
aWindow = QtGui.QWidget()
aWatcher = Watcher(aWindow)
aWindow.installEventFilter(aWatcher)
aWindow.show()
app.exec_()
Note that when checking the windowState, you should use & to compare instead of ==, because the state can be a combination of more than one value and you need to check it with a mask to see if the value is present amongst the others. i.e. if you maximize the window first and then minimize it, it will have multiple window states.
On a side note, you also have the option of customizing the actual window properties in the first place. So if your goal is to prevent minimizing, you could disable the button for it:
aWindow = QtGui.QWidget()
flags = aWindow.windowFlags()
aWindow.setWindowFlags(flags ^ QtCore.Qt.WindowMinimizeButtonHint)
This will subtract the minimize button flag from all the other flags.
Hi guys me better come this way:
if self.windowState() == QtCore.Qt.WindowMinimized:
# Window is minimised. Restore it.
self.setWindowState(QtCore.Qt.WindowActive)
certainly not always this function is `working
probably the problem in the python
**thanks to all**

PyQt4 Minimize to Tray

Is there a way to minimize to tray in PyQt4? I've already worked with the QSystemTrayIcon class, but now I would like to minimize or "hide" my app window, and show only the tray icon.
Has anybody done this? Any direction would be appreciated.
Using Python 2.5.4 and PyQt4 on Window XP Pro
It's pretty straightforward once you remember that there's no way to actually minimize to the system tray.
Instead, you fake it by doing this:
Catch the minimize event on your window
In the minimize event handler, create and show a QSystemTrayIcon
Also in the minimize event handler, call hide() or setVisible(false) on your window
Catch a click/double-click/menu item on your system tray icon
In your system tray icon event handler, call show() or setVisible(true) on your window, and optionally hide your tray icon.
Code helps, so here's something I wrote for an application, except for the closeEvent instead of the minimize event.
Notes:
"closeEvent(event)" is an overridden Qt event, so it must be put in the class that implements the window you want to hide.
"okayToClose()" is a function you might consider implementing (or a boolean flag you might want to store) since sometimes you actually want to exit the application instead of minimizing to systray.
There is also an example of how to show() your window again.
def __init__(self):
traySignal = "activated(QSystemTrayIcon::ActivationReason)"
QtCore.QObject.connect(self.trayIcon, QtCore.SIGNAL(traySignal), self.__icon_activated)
def closeEvent(self, event):
if self.okayToClose():
#user asked for exit
self.trayIcon.hide()
event.accept()
else:
#"minimize"
self.hide()
self.trayIcon.show() #thanks #mojo
event.ignore()
def __icon_activated(self, reason):
if reason == QtGui.QSystemTrayIcon.DoubleClick:
self.show()
Just to add to the example by Chris:
It is crucial that you use the Qt notation when declaring the signal, i.e.
correct:
self.connect(self.icon, SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), self.iconClicked)
and not the PyQt one
incorrect and won't work:
self.connect(self.icon, SIGNAL("activated(QSystemTrayIcon.ActivationReason)"), self.iconClicked)
Note the :: in the signal string. This took me about three hours to figure out.
Here's working code..Thanks Matze for Crucial, the SIGNAL took me more hours of curiosity.. but doing other things. so ta for a #! moment :-)
def create_sys_tray(self):
self.sysTray = QtGui.QSystemTrayIcon(self)
self.sysTray.setIcon( QtGui.QIcon('../images/corp/blip_32.png') )
self.sysTray.setVisible(True)
self.connect(self.sysTray, QtCore.SIGNAL("activated(QSystemTrayIcon::ActivationReason)"), self.on_sys_tray_activated)
self.sysTrayMenu = QtGui.QMenu(self)
act = self.sysTrayMenu.addAction("FOO")
def on_sys_tray_activated(self, reason):
print "reason-=" , reason
This was an edit of vzades response, but it was rejected on a number of grounds. It does the exact same thing as their code but will also obey the minimize event (and run without syntax errors/missing icons).
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
style = self.style()
# Set the window and tray icon to something
icon = style.standardIcon(QtGui.QStyle.SP_MediaSeekForward)
self.tray_icon = QtGui.QSystemTrayIcon()
self.tray_icon.setIcon(QtGui.QIcon(icon))
self.setWindowIcon(QtGui.QIcon(icon))
# Restore the window when the tray icon is double clicked.
self.tray_icon.activated.connect(self.restore_window)
def event(self, event):
if (event.type() == QtCore.QEvent.WindowStateChange and
self.isMinimized()):
# The window is already minimized at this point. AFAIK,
# there is no hook stop a minimize event. Instead,
# removing the Qt.Tool flag should remove the window
# from the taskbar.
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.Tool)
self.tray_icon.show()
return True
else:
return super(Example, self).event(event)
def closeEvent(self, event):
reply = QtGui.QMessageBox.question(
self,
'Message',"Are you sure to quit?",
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No,
QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes:
event.accept()
else:
self.tray_icon.show()
self.hide()
event.ignore()
def restore_window(self, reason):
if reason == QtGui.QSystemTrayIcon.DoubleClick:
self.tray_icon.hide()
# self.showNormal will restore the window even if it was
# minimized.
self.showNormal()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
This is the correct way to handle double click on a tray icon for PyQt5.
def _create_tray(self):
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.activated.connect(self.__icon_activated)
def __icon_activated(self, reason):
if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick):
pass
This is the code and it does help i believe in show me the code
import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtGui import QDialog, QApplication, QPushButton, QLineEdit, QFormLayout, QSystemTrayIcon
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.icon = QSystemTrayIcon()
r = self.icon.isSystemTrayAvailable()
print r
self.icon.setIcon(QtGui.QIcon('/home/vzades/Desktop/web.png'))
self.icon.show()
# self.icon.setVisible(True)
self.setGeometry(300, 300, 250, 150)
self.setWindowIcon(QtGui.QIcon('/home/vzades/Desktop/web.png'))
self.setWindowTitle('Message box')
self.show()
self.icon.activated.connect(self.activate)
self.show()
def closeEvent(self, event):
reply = QtGui.QMessageBox.question(self, 'Message', "Are you sure to quit?", QtGui.QMessageBox.Yes |
QtGui.QMessageBox.No, QtGui.QMessageBox.No)
if reply == QtGui.QMessageBox.Yes:
event.accept()
else:
self.icon.show()
self.hide()
event.ignore()
def activate(self, reason):
print reason
if reason == 2:
self.show()
def __icon_activated(self, reason):
if reason == QtGui.QSystemTrayIcon.DoubleClick:
self.show()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Categories