PyQt4 how to restore a minimized window? - python

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**

Related

Switch back and forth between windows in pyqt5 while avoiding circular imports

So I have 2 windows I want to be able to switch between, Login and MainWindow, each one is QWidget in its own seperate file, loginUI.py and MainUI.py respectively.
I can easily switch from login to main upon correct authentification by creating a new instance of MainWindow. But in MainWindow I want to have a 'Disconnect' button that shows the Login screen.
Since both are in different files, importing in this scenario raises a circular import error in python.
Other approaches I tried are:
Using signals and handling them in an intermediate file. This is fine but as I add more buttons/ windows the file started to become a bit of a mess.
Passing the instance of Login to MainWindow.__init__(self, login), and just using self.login.show(). Which seems like a good way, but again as I add more and more windows, I'm scared it might affect performance as so many instances are just running in the background.
Is any of these the correcte way or am I missing an easier way
Edit:
login.py
from PyQt5.QtWidgets import QWidget, QPushButton, QLineEdit
from PyQt5.QtCore import QSize
from mainmenu import MainWindow
from PyQt5 import QtWidgets
import sys
class Login(QWidget):
def __init__(self):
QWidget.__init__(self)
self.setMinimumSize(QSize(300, 200))
self.setWindowTitle("Log in")
self.username = QLineEdit(self)
self.username.move(50, 10)
self.password = QLineEdit(self)
self.password.move(50, 40)
self.connect_button = QPushButton('Connect', self)
self.connect_button.move(50, 100)
self.connect_button.clicked.connect(self.handleConnexion)
def handleConnexion(self):
if self.username.text() == "admin" and self.password.text()=="1":
self.mw = MainWindow()
self.mw.show()
self.close()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
mainWin = Login()
mainWin.show()
sys.exit( app.exec_() )
mainmenu.py
from PyQt5.QtCore import QSize
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QMainWindow
class MainWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(300, 200))
self.setWindowTitle("Main menu")
disconnect_button = QPushButton('Disconnect', self)
disconnect_button.clicked.connect(self.handeDC)
def handeDC(self):
pass
# here I either send a signal to handle it somewhere else
# or
# if i pass the login instance in __init__, just do login.show()
Note: since this is a common question, I'll provide a more broad answer that better reflects the object hierarchy.
Since the "main" window is, as the name suggests, the main one, the script containing it should be the main one, while the login window should be imported and eventually shown as required.
The hierarchy is important: you don't have to consider the order in which the windows are shown, but their relevance.
Considering this, the main script will:
create the main window;
show the login if required;
show the main window if the login is successful;
show again the login if the user disconnects;
clear the contents if the user has changed (*see below);
The above also shows why it's rarely a good idea to continuously create new instances of windows.
The login window should also be a QDialog, which makes things easier: the exec() method is "blocking" (for the function execution, not for the event loop), and waits until the dialog is accepted or *rejected.
main.py
from PyQt5.QtWidgets import *
from login import Login
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setMinimumSize(300, 200)
self.setWindowTitle('Main menu')
disconnect_button = QPushButton('Disconnect')
self.setCentralWidget(disconnect_button)
# only for a *persistent* login dialog (*see below)
self.login = Login()
disconnect_button.clicked.connect(self.disconnect)
def start(self):
# put here some function that might check for a 'previous' logged in
# state, possibly stored using QSettings.
# in this case, we just assume that the user has never previously
# logged in, so we automatically show the login window; if the above
# function returns True instead, we can safely show the main window
logged = False
if logged:
self.show()
else:
self.showLogin()
def disconnect(self):
self.hide()
self.showLogin()
def showLogin(self):
if self.login.exec():
self.show()
# alternatively (*see below):
login = Login()
if login.exec():
self.show()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.start()
sys.exit(app.exec())
login.py
from PyQt5.QtWidgets import *
class Login(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setMinimumSize(300, 200)
self.setWindowTitle('Log in')
self.username = QLineEdit()
self.password = QLineEdit()
self.password.setEchoMode(self.password.Password)
self.connect_button = QPushButton('Connect', enabled=False)
layout = QGridLayout(self)
layout.addWidget(QLabel('Username:'), 0, 0)
layout.addWidget(self.username, 0, 1)
layout.addWidget(QLabel('Password:'), 1, 0)
layout.addWidget(self.password, 1, 1)
layout.addWidget(self.connect_button, 2, 0, 1, 2)
self.connect_button.clicked.connect(self.handleConnexion)
self.username.textChanged.connect(self.checkFields)
self.password.textChanged.connect(self.checkFields)
def checkFields(self):
if self.username.text() and self.password.text():
self.connect_button.setEnabled(True)
else:
self.connect_button.setEnabled(False)
def handleConnexion(self):
if self.username.text() == 'admin' and self.password.text() == '1':
self.accept()
else:
QMessageBox.warning(self, 'Error',
'Invalid username or password!')
Notes:
the above code will always show the existing Login instance, so the username and password fields will "remember" the previous entries; if you don't want that, you can always call clear() on those fields by overriding the exec() (but remember to call the base implementation and return its result!); alternatively, don't create the self.login and always create a new, local instance of Login() in showLogin();
you shall always use layout managers, and never rely on fixed geometries;
QMainWindow should always have a central widget, creating children of a main window using it as the parent is discouraged (unless you really know what you're doing); if you need more widgets, use a basic QWidget, set a layout for it, add the children, and finally call setCentralWidget();
more complex hierarchies can require a "controller" (read more about the MVC pattern) to better organize the whole program and respect the OOP patterns; this is normally achieved by a basic class or by subclassing the QApplication;
about the last (*) point in my initial list, and related to what explained above: a "controller" could/should completely delete the previous "main window" (it would be both easier and safer) and eventually show a new instance whenever the user has disconnected;
Instead of closing the Login widget when creating the MainWindow widget, you could just hide the widget, which will save the overhead of creating a new instance and also keep the connected slots intact.
Then on your MainWindow you can create a diconnected signal that should be emited when the user clicks the disconnect button.
The login window can listen for the signal and call it's show method.
I made inline comments where I made changes in the example below:
mainwindow.py
from PyQt5.QtCore import pyqtSignal # added this
class MainWindow(QMainWindow):
disconnected = pyqtSignal() # added this
def __init__(self):
QMainWindow.__init__(self)
...
disconnect_button = QPushButton('Disconnect', self)
disconnect_button.clicked.connect(self.handeDC)
def handeDC(self):
# ... do some stuff
self.disconnected.emit() # added this
login.py
class Login(QWidget):
def __init__(self):
QWidget.__init__(self)
...
def handleConnexion(self):
if self.username.text() == "admin" and self.password.text()=="1":
self.mw = MainWindow()
self.mw.disconnected.connect(self.show) # added this
self.mw.show()
self.hide() # changed this
...

How to set an event handler for an item (QTableWidget) being enabled/disabled?

I have a QTableWidget that gets enabled and disabled depending on certain data.
I have a button I want to be enabled when the QTableWidget is enabled, and disabled when the widget is disabled.
Is there any event handler that would let me do this?
Such as (and this does not work):
myTable.changeEvent.connect(lambda: print("test"))
Ideally in the above code, 'test' would be printed every time the table gets enabled or disabled.
The simplest solution is that the moment you deactivate the QTableWidget you also deactivate the button (or use a signal to transmit the information).
Instead, another solution is to use an event filter that allows to emit a signal every time the widget's state changes and then use that information to change the button's state.
import random
import sys
from PyQt5 import QtCore, QtWidgets
class EnableHelper(QtCore.QObject):
enableChanged = QtCore.pyqtSignal(bool)
def __init__(self, widget):
super().__init__(widget)
self._widget = widget
self.widget.installEventFilter(self)
#property
def widget(self):
return self._widget
def eventFilter(self, obj, event):
if obj is self.widget and event.type() == QtCore.QEvent.EnabledChange:
self.enableChanged.emit(self.widget.isEnabled())
return super().eventFilter(obj, event)
class Widget(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.table = QtWidgets.QTableWidget(5, 4)
self.button = QtWidgets.QPushButton("Hello world")
lay = QtWidgets.QVBoxLayout(self)
lay.addWidget(self.table)
lay.addWidget(self.button)
helper = EnableHelper(self.table)
helper.enableChanged.connect(self.button.setEnabled)
self.test()
def test(self):
self.table.setEnabled(random.choice([True, False]))
QtCore.QTimer.singleShot(1000, self.test)
def main():
app = QtWidgets.QApplication(sys.argv)
win = Widget()
win.show()
ret = app.exec_()
sys.exit(ret)
if __name__ == "__main__":
main()
Note: changeEvent is not a signal so you should not use connect as this is a class method. Also it is not good to use it if you only want to detect if the widget changes state from enabled to disabled, or vice versa, since this is also called in other cases.

How to speedup the delay of the pressed signal of QPushButton compared to KeyPress event in Qt/ PyQt?

I have a PyQt-GUI with a function to move an object that is connected both via the pressed-signal to a QPushButton and via a QEvent.KeyPress-event to a key from the keyboard. However, I notice a serious lag in the object movement after clicking the PushButton, which is not the case if I press the specified key on the keyboard.
Can you explain why the execution of the same function is slower if triggered by the PushButton compared to the key-event?
Is there a way to make the execution faster/ the lag slower for the QPushButton?
Here is an abstract of the relevant code:
class GUI(QtWidgets.QMainWindow):
def __init__(self, control: 'Controler'):
super(GUI, self).__init__()
uic.loadUi("ressources/interface.ui", self)
# Defining the QPushButton and connecting the signal with the function
self.button_left = self.findChild(QtWidgets.QPushButton, "button_bbox_left")
self.button_left.pressed.connect(lambda: self.translate_along_x(left=True))
# Handling key_press_event with eventFilter
def eventFilter(self, event_object, event):
if (event.type() == QEvent.KeyPress) and (event.key() == QtCore.Qt.Key_A):
self.translate_along_x(left=True)
# The function that moves the object
def translate_along_x(self, distance=0.05, left=False):
if left:
distance *= -1
move_object(distance)
One difference between the two function calls is that for the QPushButton I have to use a lambda-function due to the parameter. But the lag remains if I set it without the lambda-function.
Update
As #eyllanesc suggested I tried to create a minimal reproducible example. You find it below. There I could not measure a significant difference. So (although it is much simpler) it might be more like PyQt is not executing all clicks if I click the button repeatedly a lot of times.
So is there an option to make the button handle many fast clicks better?
MRE:
import sys, time
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWidgets import QPushButton
from PyQt5.QtWidgets import QMainWindow
app = QApplication(sys.argv)
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
button = QPushButton("Click me")
self.setCentralWidget(button)
self.start = None
button.pressed.connect(self.button_clicked)
def button_clicked(self):
self.start = time.time()
self.do_sth()
def keyPressEvent(self, event):
self.start = time.time()
self.do_sth()
def do_sth(self):
print("Reacted to signal/ event!")
print("Took %s seconds!" % str(time.time()-self.start))
window = MainWindow()
window.show()
sys.exit(app.exec_())

QSpinBox signal for arrow buttons

I know this question has been asked in similar ways, but I'm getting lost in the vast world of the many signals I can choose from when using a QSpinBox (or QDoubleSpinBox). I want to connect my function to the editingFinished signal (fine, it works perfectly), but this will not also connect to the arrow-buttons - so I need signals for those as well. I don't want to call my function every time valueChanged is emitted - only when editing is finished, or when the arrows are used.
One way to do this is to reimplement the stepBy method and emit a custom signal. The main advantage of this approach is that it will handle changes made using the up/down keys, as well as the arrow buttons. Here is a basic demo:
import sys
from PyQt4 import QtCore, QtGui
class SpinBox(QtGui.QSpinBox):
stepChanged = QtCore.pyqtSignal()
def stepBy(self, step):
value = self.value()
super(SpinBox, self).stepBy(step)
if self.value() != value:
self.stepChanged.emit()
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.spin = SpinBox()
self.spin.editingFinished.connect(self.handleSpinChanged)
self.spin.stepChanged.connect(self.handleSpinChanged)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.spin)
def handleSpinChanged(self):
print(self.spin.value())
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 150, 50)
window.show()
sys.exit(app.exec_())

Differentiate single click from double click in pyside

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.

Categories