How to change minimize event behavior in PyQt or PySide? - python

I'm developing a Qt application and changed the closing behavior with the closeEvent virtual function this way:
class MainWindow(QMainWindow):
def closeEvent(self, event):
event.ignore()
self.hide()
self.trayicon.showMessage('Running', 'Running in the background.')
This works as expected. If I remove event.ignore() the application quits as expected, everything is fine.
I want to control the minimize event too, so when the user clicks the minimize button on the title bar, I want to move the window instead of minimize.
I cannot use the hideEvent virtual function, because the event will be sent to the window anyway, so this code:
def hideEvent(self, event):
event.ignore()
self.move(0,0)
moves the window to the top left AND then minimize it. event.ignore() has no effect here, so I tried using QtCore.QObject.event this way:
def event(self, event):
if event.type() == QEvent.WindowStateChange:
if self.isMinimized():
event.ignore()
self.move(0,0)
return True
return False
The window moves but minimizes again. What is wrong with this ? How can I override the minimize event completely ?

Try the changeEvent and filter for WindowMinimized events, something like this:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from PyQt4 import QtGui, QtCore
class MyWindow(QtGui.QWidget):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.systemTrayIcon = QtGui.QSystemTrayIcon(self)
self.systemTrayIcon.setIcon(QtGui.QIcon.fromTheme("face-smile"))
self.systemTrayIcon.setVisible(True)
self.systemTrayIcon.activated.connect(self.on_systemTrayIcon_activated)
self.label = QtGui.QLabel(self)
self.label.setText("Minimize me!")
self.layoutVertical = QtGui.QVBoxLayout(self)
self.layoutVertical.addWidget(self.label)
#QtCore.pyqtSlot(QtGui.QSystemTrayIcon.ActivationReason)
def on_systemTrayIcon_activated(self, reason):
if reason == QtGui.QSystemTrayIcon.DoubleClick:
if self.isHidden():
self.show()
else:
self.hide()
def changeEvent(self, event):
if event.type() == QtCore.QEvent.WindowStateChange:
if self.windowState() & QtCore.Qt.WindowMinimized:
event.ignore()
self.close()
return
super(MyWindow, self).changeEvent(event)
def closeEvent(self, event):
event.ignore()
self.hide()
self.systemTrayIcon.showMessage('Running', 'Running in the background.')
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
app.setApplicationName('MyWindow')
main = MyWindow()
main.show()
sys.exit(app.exec_())

Related

How to detect a mouseButtonPressed event outside a QGraphicsView widget?

I have a QGraphicsView widget with a QGraphicsScene set in it. I wish to detect a mouseButtonPressed event anywhere in my application window outside of the QGraphicsView widget.
I tried to install an eventFilter to the central widget of the application as follows:
self.centralwidget.installEventFilter(self)
My QGraphicsView widget is self.viewStartImg. In my eventFilter method, I have the following:
def eventFilter(self, obj, event):
if obj != self.viewStartImg and event.type() == QEvent.MouseButtonPress:
print('Outside the QGraphicsView')
In my application, when I click inside of the QGraphicsView, I still get 'Outside the QGraphicsView' printed out. I believe that's happening because QGraphicsView is a child of the centralWidget, but I am not sure.
Any alternate method of achieving this functionality is highly appreciated!
There are several ways to achieve this. One way would be to subclass QGraphicsView and override mousePressEvent in the subclass. Another possibility is to install an event filter on the view object. For the first method you would do something like this
from PyQt5 import QtWidgets, QtCore
class MyView(QtWidgets.QGraphicsView):
def mousePressEvent(self, event):
print('mouse pressed inside view')
event.accept()
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.central = QtWidgets.QWidget(self)
self.view = MyView(self.central)
self.scene = QtWidgets.QGraphicsScene()
self.view.setScene(self.scene)
hlayout = QtWidgets.QHBoxLayout(self.central)
hlayout.setContentsMargins(50,50,50,50)
hlayout.addWidget(self.view)
self.setCentralWidget(self.central)
def mousePressEvent(self, event):
print('mouse pressed ouside view')
event.accept()
if __name__ == "__main__" :
app = QtWidgets.QApplication([])
win = MyWindow()
win.show()
app.exec()
And for the second one
class MyWindow(QtWidgets.QMainWindow):
money_changed = QtCore.pyqtSignal(int)
def __init__(self):
super().__init__()
self.central = QtWidgets.QWidget(self)
self.view = QtWidgets.QGraphicsView(self.central)
self.scene = QtWidgets.QGraphicsScene()
self.view.setScene(self.scene)
hlayout = QtWidgets.QHBoxLayout(self.central)
hlayout.setContentsMargins(50,50,50,50)
hlayout.addWidget(self.view)
self.setCentralWidget(self.central)
self.view.installEventFilter(self)
def mousePressEvent(self, event):
print('mouse pressed ouside view')
event.accept()
def eventFilter(self, object, event):
if object == self.view and event.type() == QtCore.QEvent.MouseButtonPress:
print('mouse pressed inside view')
return True
return super().eventFilter(object, event)
It may not be exactly what you're looking for, but I recently needed to register when the user clicked outside my widget and used this method:
def event(self, event):
if event == None:
return False
if event.type() == QtCore.QEvent.WindowDeactivate:
print "Clicked outside"
self.main_widget.close()
return super(_ComboBoxPlusDialog, self).event(event)
I used this to close the window, as you can see. So it only registers the outside click once. I used this to get around an issue with Qt.Popup disabling input for the IME.

Close Event are not called Keyboard Event and MessageBox

I programming with QT Designer a GUI and would like disable the Close Event with ESC-Key. The User has the possibility to close the Application with X on the top but not with any Keys.
The Function closeEvent(self,event) are not working.
def closeEvent(self, event):
close = QMessageBox()
close.setText("You sure?")
close.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
close = close.exec()
if close == QMessageBox.Yes:
event.accept()
else:
event.ignore()
The function are on my main class: class Ui_Tool(object)
class Ui_LabelTool(object):
def setupUi(self, LabelTool):
Tool.setObjectName("Tool")
Tool.resize(650, 569)
Tool.setMinimumSize(QtCore.QSize(650, 569))
Tool.setMaximumSize(QtCore.QSize(650, 569))
Tool.setAutoFillBackground(False)
Tool.setSizeGripEnabled(False)
...
#Events for Buttons
self.SelectFolder.clicked.connect(self.setfolder)
self.SelectOutputFolder.clicked.connect(self.SetOutputFolder)
self.LoadeMeasurement.clicked.connect(self.LoadRecording)
self.StartButton.clicked.connect(self.startProcessing)
self.Next_btn.clicked.connect(self.nextOperation)
self.Prev_Btn.clicked.connect(self.prefOperation)
self.Reset_btn.clicked.connect(self.resetApp)
self.treeView.clicked.connect(self.ClickMeasurement)
self.treeWidget.clicked.connect(self.CheckTopicSelect)
self.horizontalSlider.valueChanged.connect(self.SliderValueChange)
self.horizontalSlider.sliderMoved.connect(self.dispSlider)
self.BBObject_btn.clicked.connect(self.CreateBBObj)
self.BBNoObject_btn.clicked.connect(self.CreateBBNoObj)
self.ShowAll_btn.clicked.connect(self.SaveImages)
def retranslateUi(self, LabelTool):
_translate = QtCore.QCoreApplication.translate
Tool.setWindowTitle(_translate("Tool", "Tool"))
self.LoadeMeasurement.setText(_translate("Tool", "Load Measurement"))
self.StartButton.setText(_translate("Tool", "Start "))
self.Reset_btn.setText(_translate("Tool", "Reset"))
self.Header_lbl.setText(_translate("Tool", "Test"))
...
And this is my main Function:
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Tool = QtWidgets.QDialog()
ui = Ui_Tool()
ui.setupUi(Tool)
Tool.show()
sys.exit(app.exec_())
What I do wrong?
I add the following class:
class Logic(QMainWindow, Ui_Tool):
def __init__(self, *args, **kwargs):
QMainWindow.__init__(self, *args, **kwargs)
self.setupUi(self)
def closeEvent(self, event):
answer = QtWidgets.QMessageBox.question(
self,
'Are you sure you want to quit ?',
'Task is in progress !',
QtWidgets.QMessageBox.Yes,
QtWidgets.QMessageBox.No)
if answer == QtWidgets.QMessageBox.Yes:
event.accept()
else:
event.ignore()
If the docs is reviewed:
Escape Key
If the user presses the Esc key in a dialog, QDialog::reject() will be
called. This will cause the window to close: The close event cannot be
ignored.
So there are 2 possible solutions:
Override keyPressEvent() method, when you press the escape key, call close().
Override reject() method to make the verification of the QMessageBox and according to it make its logic.
In addition to this implementation you must do it in the widget and not in the class generated by Qt Designer(1), considering the above the implementations of both solutions are:
1.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_LabelTool(object):
def setupUi(self, Tool):
Tool.setObjectName("Tool")
Tool.resize(650, 569)
Tool.setMinimumSize(QtCore.QSize(650, 569))
Tool.setMaximumSize(QtCore.QSize(650, 569))
Tool.setAutoFillBackground(False)
Tool.setSizeGripEnabled(False)
# ....
class LabelTool(QtWidgets.QDialog, Ui_LabelTool):
def __init__(self, parent=None):
super(LabelTool, self).__init__(parent)
self.setupUi(self)
def verify_by_user(self):
answer = QtWidgets.QMessageBox.question(
self,
"Are you sure you want to quit ?",
"Task is in progress !",
QtWidgets.QMessageBox.Yes,
QtWidgets.QMessageBox.No,
)
return answer == QtWidgets.QMessageBox.Yes
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key_Escape:
self.close()
else:
super(LabelTool, self).keyPressEvent(event)
def closeEvent(self, event):
if self.verify_by_user():
event.accept()
else:
event.ignore()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = LabelTool()
w.show()
sys.exit(app.exec_())
2.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_LabelTool(object):
def setupUi(self, Tool):
Tool.setObjectName("Tool")
Tool.resize(650, 569)
Tool.setMinimumSize(QtCore.QSize(650, 569))
Tool.setMaximumSize(QtCore.QSize(650, 569))
Tool.setAutoFillBackground(False)
Tool.setSizeGripEnabled(False)
# ....
class LabelTool(QtWidgets.QDialog, Ui_LabelTool):
def __init__(self, parent=None):
super(LabelTool, self).__init__(parent)
self.setupUi(self)
def verify_by_user(self):
answer = QtWidgets.QMessageBox.question(
self,
"Are you sure you want to quit ?",
"Task is in progress !",
QtWidgets.QMessageBox.Yes,
QtWidgets.QMessageBox.No,
)
return answer == QtWidgets.QMessageBox.Yes
def reject(self):
if self.verify_by_user():
super(LabelTool, self).reject()
def closeEvent(self, event):
if self.verify_by_user():
event.accept()
else:
event.ignore()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = LabelTool()
w.show()
sys.exit(app.exec_())
(1) Using the Generated Code

PyQt5/Python - Multiple keypress events are called with only 1 keypress

So I have the following basic window, my issue is that whenever I press the tab button once it triggers the event twice. Printing "tab" and "keypress" twice. I looked around and all I found about this issue was a C++ answer, I tried to understand the solution but was unable too.
from PyQt5 import QtCore, QtWidgets
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__(self)
# Install the event filter that will be used later to detect key presses
QtWidgets.qApp.installEventFilter(self)
self.button = QtGui.QPushButton('Test', self)
self.button.clicked.connect(self.handleButton)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.button)
def handleButton(self):
print("Button")
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.KeyPress:
print("keypress")
if event.key() == QtCore.Qt.Key_Escape:
self.close()
if event.key() == QtCore.Qt.Key_Tab:
print("Tab")
pass
return super(ItemPrice, self).eventFilter(obj, event)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
The eventFilter() method needs a boolean result, or 0/1, to return if an event is relevant or not (the filter part). When you return False the event is not blocked, and will hit his target, this lets the application handle the event in a normal way.
In your example, when an expected key is pressed (this is a relevant event), you need to return 1 or True to "intercept" it and prevent the program to handle it, since you provide your own process. In the other cases, you can call the super method like you did :
def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.KeyPress:
print("keypress")
if event.key() == QtCore.Qt.Key_Escape:
self.close()
return 1
if event.key() == QtCore.Qt.Key_Tab:
print("Tab")
return 1
return super().eventFilter(obj, event)

How do hide a window on launch, leaving only the tray icon?

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)
# why this doesn't work?
self.hide()
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.Tool)
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()
I want to make it only show the tray icon on startup, the example is from here:
PyQt4 Minimize to Tray
I added the following to initUI(), but still it shows a blank window on startup. If I minimize the window, it disppears, leaving only the tray icon - but how can I make this happen on startup automatically?
self.hide()
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.Tool)
edit: I tried this code on two different machines with Fedora 13 , and CentOS 7
The solution is to show the tray-icon, but hide the window. Your example works for me on Windows if I make the following changes:
def initUI(self):
...
self.hide()
self.setWindowFlags(self.windowFlags() & ~QtCore.Qt.Tool)
# explicitly show the tray-icon
self.tray_icon.show()
def main():
...
ex = Example()
# don't re-show the window!
# ex.show()
sys.exit(app.exec_())
QWidget::hide() should do what you want. The problem is you are calling show() on the new instance which is undoing the call to self.hide() in the constructor.
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
ex.show() # Remove this line
sys.exit(app.exec_())

PyQt4 - Dragging and dropping files into QPushButton

I think the title is fairly self explanatory. I'm working to create a small standalone app that requires the user to drag and drop audio files onto buttons to in turn associate the file with a corresponding button on a piece of hardware by using the filepath, etc...
I've followed a ton of drag and drop tutorials for widgets, and my friend has for lists, however I'm beginning to believe that it can't be done for a button? I'm aware that you can drag and drop text onto a button. I am not fully up to speed with Qt yet so there may just be a glaring error that I'm missing.
Here is the code, many thanks!
import sys
from PyQt4 import QtGui, QtCore
class Button(QtGui.QPushButton):
def __init__(self, parent):
super(Button, self).__init__(parent)
self.setAcceptDrops(True)
self.setDragDropMode(QAbstractItemView.InternalMove)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
else:
super(Button, self).dragEnterEvent(event)
def dragMoveEvent(self, event):
super(Button, self).dragMoveEvent(event)
def dropEvent(self, event):
if event.mimeData().hasUrls():
for url in event.mimeData().urls():
path = self.addItem(url.path())
print path
event.acceptProposedAction()
else:
super(Button,self).dropEvent(event)
class MyWindow(QtGui.QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.setGeometry(100,100,300,400)
self.setWindowTitle("Filenames")
self.btn = QtGui.QPushButton()
self.btn.setGeometry(QtCore.QRect(90, 90, 61, 51))
self.btn.setText("Change Me!")
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.btn)
self.setLayout(layout)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
There are three problems with your posted code, the main being that you aren't even using the custom Button class that you made. You are adding just a regular button to your window with:
self.btn = QtGui.QPushButton()
instead of:
self.btn = Button(self)
Also, QPushButtons don't have a setDragDropMode() method, so you'll need to comment that line out. I'm not sure what it does anyway.
Also, QPushButton doesn't have an addItem() method so I'm not sure what that's about unless you were planning on implementing it. I replaced it below with just printing the file path.
Here is a working version of your code, that just prints the file path of any file dragged into the button:
import sys
from PyQt4 import QtGui, QtCore
class Button(QtGui.QPushButton):
def __init__(self, parent):
super(Button, self).__init__(parent)
self.setAcceptDrops(True)
#self.setDragDropMode(QAbstractItemView.InternalMove)
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
else:
super(Button, self).dragEnterEvent(event)
def dragMoveEvent(self, event):
super(Button, self).dragMoveEvent(event)
def dropEvent(self, event):
if event.mimeData().hasUrls():
for url in event.mimeData().urls():
print str(url.toLocalFile())
event.acceptProposedAction()
else:
super(Button,self).dropEvent(event)
class MyWindow(QtGui.QWidget):
def __init__(self):
super(MyWindow,self).__init__()
self.setGeometry(100,100,300,400)
self.setWindowTitle("Filenames")
self.btn = Button(self)
self.btn.setGeometry(QtCore.QRect(90, 90, 61, 51))
self.btn.setText("Change Me!")
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.btn)
self.setLayout(layout)
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())

Categories