Unable to get key event when using uic.loadUI() in PyQt4 - pyqt4

I have the following code that cannot capture the key event.
I used the uic.loadUi() to load my GUI.
But I can't seem to capture the keyboard event.
Pls help!
class cMyApp(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QMainWindow.__init__(self)
self.ui = uic.loadUi("myApp.ui")
#~ self.ui.show() # Show myApp UI but key event Doesn't Work :(
self.show() # Show a small window but key event works.
def keyPressEvent(self, event):
if type(event)==QtGui.QKeyEvent:
print ("type(event) = ",type(event))
if event.key()==QtCore.Qt.Key_Escape:
print("Esc pressed!!!")
self.close()
event.accept()
else:
event.ignore()
if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
myApp = cMyApp()
sys.exit(app.exec_())

Found the problem! ;P
When loading with uic.loadUI(), must supply 'self' as another parameter for baseinstance; otherwise it's default to None.
The corrected codes portion should be:
self.ui = uic.loadUi("myApp.ui", self) # Must supply 'self' as baseinstance.
self.ui.show() # Show myApp UI can work with key event now! :)

Related

App crashes when using QWidgets.QMessageBox

So I've been trying my luck with PyQT5 to give a GUI to an app I've been working on.
I've encountered an issue with QMessageBox feature.
I've been trying to create an "Exit" Action on the MenuBar of the app.
And at first I only made it exit when clicked and it worked.
Now I want to make it give a pop up message of "Are you sure?", which is exactly what the QMessageBox does. So this is my code now:
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.ui = uic.loadUi('rent_creation.ui', self)
self.home()
def home(self):
self.ui.actionExit.triggered.connect(self.close_application)
self.show()
def close_application(self):
choice = QMessageBox.question(self, 'Quit?',
"Are you sure you want to quit?",
QMessageBox.Yes | QMessageBox.No)
if choice == QMessageBox.Yes:
sys.exit()
else:
pass
Now every time I click on the Exit button when I run this code, The Python crashes.
I'm not sure what I'm doing wrong... I've been looking around the internet and it all look well.... I've tried all the variation possible of passing QmessageBox (for example I tried adding QWidgets.QMessageBox.Yes/No and it didn't fix this issue).
I've been following a tutorial on the internet where This code is practically the same as his, and it works for him in the tutorial somehow.
caveat: I am on linux, so things are likely a bit different.
However I wouldn't be surprised if the problem is related with the fact that you use sys.exit to quit the GUI. You probably should cleanly close the window, the QApplication and then exit the program.
The following example might solve your issue. Since I don't have you ui file, I just added a menu action to close the the window and connect it with the QMainWindow.close slot and then override the closeEvent method. See the comments in the code:
import sys
from PyQt5 import QtWidgets
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.home()
def home(self):
# add a menu bar with a File menu and a Close action
menu_bar = QtWidgets.QMenuBar(self)
menu = QtWidgets.QMenu('File', menu_bar)
menu_bar.addMenu(menu)
action = menu.addAction('Close')
# connect the Close action with the QMainWindow.close slot
action.triggered.connect(self.close)
self.setMenuBar(menu_bar)
def closeEvent(self, event):
"""override the QMainWindow.closeEvent method to:
* fire up a QMessageBox with a question
* accept the close event if the user click yes
* ignore it otherwise.
Parameters
----------
event : QtCloseEvent
emitted when someone or something asks to close the window
"""
if self.ask_quit():
event.accept()
else:
event.ignore()
def ask_quit(self):
choice = QtWidgets.QMessageBox.question(self, 'Quit?',
"Are you sure you want to quit?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
return choice == QtWidgets.QMessageBox.Yes
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
w = Window()
w.resize(250, 150)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())
The above way of closing the window, i.e. using the closeEvent and connect the menu action to close, has the advantage that the confirmation box is opened every time someone asks to close the window, independently of the method: you get the message box also clicking on the window X button or with alt+F4
Edit: example of how to cleanly close the QApplication only from the Close menu. This should be more in line with the original behavior of the app in the question (see comment).
class Window(QtWidgets.QMainWindow):
def __init__(self):
super(Window, self).__init__()
self.home()
def home(self):
menu_bar = QtWidgets.QMenuBar(self)
menu = QtWidgets.QMenu('File', menu_bar)
menu_bar.addMenu(menu)
action = menu.addAction('Close')
# connect the Close menu to the ``ask_quit`` slot to ask and exit the
# application on "yes"
action.triggered.connect(self.ask_quit)
self.setMenuBar(menu_bar)
def closeEvent(self, event):
"""Ignore all ways of closing"""
event.ignore()
#QtCore.pyqtSlot()
def ask_quit(self):
choice = QtWidgets.QMessageBox.question(self, 'Quit?',
"Are you sure you want to quit?",
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No)
if choice == QtWidgets.QMessageBox.Yes:
QtWidgets.QApplication.quit()

PyQt5 - How to return application to initial state after error handling with QMessageBox

Just starting out with Python3 and PyQt5 and I'm kinda stuck here.
My main window takes two ticker codes as input and, after the user presses the Show Me! button, outputs ratio averages for each of them. I created a QMessageBox with an OK button that pops up when the user enters invalid ticker codes.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import good_morning as gm
import MainUI
class MainWindow(QMainWindow, MainUI.Ui_MyStockratios):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setupUi(self)
self.home()
def home(self):
#If Show Me! button is clicked, go grab_user_input()
self.show_me_btn.clicked.connect(self.grab_user_input)
def grab_user_input(self):
#Grab user input for QLineEdits
self.ticker1_value = self.ticker1_label.text()
self.ticker2_value = self.ticker2_label.text()
#Fetch the ratios and place them in a dataframe
self.kr = gm.KeyRatiosDownloader()
try:
self.kr_frame1 = self.kr.download(self.ticker1_value)
self.kr_frame2 = self.kr.download(self.ticker2_value)
#Error handling
except ValueError:
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText("Invalid ticker code")
msg.setInformativeText("Please verify the data you entered and try again.")
msg.setWindowTitle("Error")
msg.setStandardButtons(QMessageBox.Ok)
reply = msg.exec_()
if reply:
self.ticker2_label.clear()
self.ticker1_label.clear()
self.home()
[...]
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
if __name__ == "__main__":
main()
Here's my problem: I want the application to return to its' initial state after the user presses the QMessageBox's OK button, which means the QLineEdits must be cleared and the application must wait for the user to input new data and press the Show Me! button again. I cleared the QLineEdits with the clear() function, but can't seem to make the application wait for new user input.
Thanks in advance !
For future reference you're posted code is a bit incomplete. I took some liberties to get a working example. You can ignore most of the changes except for the button handler part. You only need to connect the button once. Your home() method is not needed.
import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
# import good_morning as gm
# import MainUI
class MainWindow(QMainWindow):#, MainUI.Ui_MyStockratios):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
# self.setupUi(self)
layout = QVBoxLayout()
widget = QWidget(self)
self.setCentralWidget(widget)
widget.setLayout(layout)
self.show_me_btn = QPushButton('Show Me!', self)
layout.addWidget(self.show_me_btn)
self.ticker1_label = QLineEdit(self)
layout.addWidget(self.ticker1_label)
self.ticker2_label = QLineEdit(self)
layout.addWidget(self.ticker2_label)
# self.home()
self.show_me_btn.clicked.connect(self.grab_user_input)
# def home(self):
# #If Show Me! button is clicked, go grab_user_input()
# self.show_me_btn.clicked.connect(self.grab_user_input)
def grab_user_input(self):
#Grab user input for QLineEdits
self.ticker1_value = self.ticker1_label.text()
self.ticker2_value = self.ticker2_label.text()
# #Fetch the ratios and place them in a dataframe
# self.kr = gm.KeyRatiosDownloader()
#
# try:
# self.kr_frame1 = self.kr.download(self.ticker1_value)
# self.kr_frame2 = self.kr.download(self.ticker2_value)
#
# #Error handling
# except ValueError:
if 1:
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText("Invalid ticker code")
msg.setInformativeText("Please verify the data you entered and try again.")
msg.setWindowTitle("Error")
msg.setStandardButtons(QMessageBox.Ok)
reply = msg.exec_()
if reply:
self.ticker2_label.clear()
self.ticker1_label.clear()
# self.home()
# [...]
def main():
app = QApplication(sys.argv)
form = MainWindow()
form.show()
app.exec_()
if __name__ == "__main__":
main()

Handle arrow key events by setting the focus policy

I want to handle key events of the arrow keys in my application. I have already read that for doing so the focus must be disabled. I follow this method: PyQt not recognizing arrow keys. Indeed, when calling self.setChildrenFocusPolicy(QtCore.Qt.NoFocus) (as defined in the linked thread and in my source code below) within MyApp.__init__, hitting an arrow key raises a key event. However, I do not want to keep the focus disabled during the entire runtime of the application but only upon clicking a button. So I move self.setChildrenFocusPolicy(QtCore.Qt.NoFocus) to the button click function:
def __init__(self):
self.pushButton.clicked.connect(self.pushButtonClicked)
def pushButtonClicked(self):
self.setChildrenFocusPolicy(QtCore.Qt.NoFocus)
Indeed, hitting the push button disables the focus (e.g. a line edit cannot take the text cursor anymore). However, hitting an arrow key still does not raise a key event.
The whole application (you will need a mainwindow.ui with a push button):
import sys
from PyQt4 import QtCore, QtGui, uic
qtCreatorFile = "d:/test/mainwindow.ui"
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
def setChildrenFocusPolicy(self, policy):
def recursiveSetChildFocusPolicy (parentQWidget):
for childQWidget in parentQWidget.findChildren(QtGui.QWidget):
childQWidget.setFocusPolicy(policy)
recursiveSetChildFocusPolicy(childQWidget)
recursiveSetChildFocusPolicy(self)
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.pushButton.clicked.connect(self.pushButtonClicked)
def pushButtonClicked(self):
self.setChildrenFocusPolicy(QtCore.Qt.NoFocus)
def keyPressEvent(self, event):
print "a"
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
There's no need to disable focus. You can get access to all key events by installing an event filter on the application:
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
def __init__(self):
...
QtGui.qApp.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.KeyPress:
print(event.key())
return super(MyApp, self).eventFilter(source, event)
But note that this really does get everything, so you may end up with more than you bargained for...

Pause execution until button press

I have a QStackedWidget. In the logic (not the UI) I am trying to change pages and wait there until a button on that page is pressed (basically an OK/Cancel). I pass the UI to the function in the class.
Something like this:
def func1(self, window):
window.stackedWidget.setCurrentIndex(4)
while True:
window.btn_OK.clicked.connect(self.OK_func)
window.btn_Cancel.clicked.connect(self.Can_func)
def OK_func(self, window):
do_something
window.stackedWidget.setCurrentIndex(3)
break
def Can_func(self, window):
window.stackedWidget.setCurrentIndex(3)
break
for i in range(5):
#stuff
func1(window) #this is where I want to pause
#other stuff
Now I know that I can't break with the function like that or pass the window variable through connect, but I hope that makes my point clearly enough.
A simple way to do this is to process pending events inside the loop (so the UI remains responsive), and set/unset an internal flag to control starting and stopping of the loop.
The following demo script shows a basic implementation of this idea:
import time
from PyQt4 import QtCore, QtGui
class Window(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
layout = QtGui.QVBoxLayout(self)
self.label = QtGui.QLabel(self)
layout.addWidget(self.label)
self.buttonStart = QtGui.QPushButton('Start', self)
self.buttonStart.clicked.connect(self.handleStart)
layout.addWidget(self.buttonStart)
self.buttonStop = QtGui.QPushButton('Stop', self)
self.buttonStop.clicked.connect(self.handleStop)
layout.addWidget(self.buttonStop)
self._running = False
def handleStart(self):
self.buttonStart.setDisabled(True)
self._running = True
while self._running:
self.label.setText(str(time.clock()))
QtGui.qApp.processEvents()
time.sleep(0.05)
self.buttonStart.setDisabled(False)
def handleStop(self):
self._running = False
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(500, 300, 200, 100)
window.show()
sys.exit(app.exec_())
Just remove while and break.
def func1(self, window):
window.stackedWidget.setCurrentIndex(4)
window.btn_OK.clicked.connect(self.OK_func)
window.btn_Cancel.clicked.connect(self.Can_func)
def OK_func(self, window):
# do_something
window.stackedWidget.setCurrentIndex(3)
def Can_func(self, window):
window.stackedWidget.setCurrentIndex(3)

How does the keyPressEvent method work in this program?

I'm having trouble understanding how the keyPressEvent method works in this program. Specifically, what is "e" here? Is keyPressEvent a redefined method using a pre-existing instance "e"?
import sys
from PyQt4 import QtGui, QtCore
class Example(QtGui.QWidget):
def __init__(self):
super(Example, self).__init__()
self.initUI()
def initUI(self):
self.setGeometry(300,300,250,150)
self.setWindowTitle('Event handler')
self.show()
def keyPressEvent(self, e):
if e.key() == QtCore.Qt.Key_Escape:
self.close()
def main():
app = QtGui.QApplication(sys.argv)
ex = Example()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
e is the "event" that is generated when a user presses a key. This is pretty common in event handlers, it's a great way to pass information (such as which key got pressed - which is what's getting pulled with e.key()) to event handlers, so that we can combine related events and handle them with a single function.
# A key has been pressed!
def keyPressEvent(self, event):
# Did the user press the Escape key?
if event.key() == QtCore.Qt.Key_Escape: # QtCore.Qt.Key_Escape is a value that equates to what the operating system passes to python from the keyboard when the escape key is pressed.
# Yes: Close the window
self.close()
# No: Do nothing.

Resources