AttributeError: 'QCoreApplication' object has no attribute 'setQuitOnLastWindowClosed' - python

I'm getting this error sometimes when I try to quit the Qt event loop. Using a code sample code,
from PyQt5 import QtCore, QtSerialPort
maxcounts = 10
counter = []
app = QtCore.QCoreApplication([])
serial_port = QtSerialPort.QSerialPort('COM3')
serial_port.setBaudRate(QtSerialPort.QSerialPort.Baud115200)
serial_port.open(QtCore.QIODevice.ReadWrite)
serial_port.setDataTerminalReady(1)
serial_port.setDataTerminalReady(0)
serial_port.setDataTerminalReady(1)
def handle_ready_read():
while serial_port.canReadLine():
resp = serial_port.readLine().data().decode().strip()
if len(counter) == maxcounts:
print('closing')
serial_port.setDataTerminalReady(0)
serial_port.setDataTerminalReady(1)
serial_port.setDataTerminalReady(0)
serial_port.close()
app.quit()
counter.append(1)
if resp == 'end':
print('closing')
serial_port.setDataTerminalReady(0)
serial_port.setDataTerminalReady(1)
serial_port.setDataTerminalReady(0)
serial_port.close()
app.quit()
serial_port.readyRead.connect(handle_ready_read)
app.exec_()
Any ideas it would output this error response?
ERROR:tornado.application:Exception in callback functools.partial(<function Kernel.enter_eventloop.<locals>.advance_eventloop at 0x000001DD311F8280>)
Traceback (most recent call last):
File "C:\Users\me\Anaconda3\lib\site-packages\tornado\ioloop.py", line 741, in _run_callback
ret = callback()
File "C:\Users\me\Anaconda3\lib\site-packages\ipykernel\kernelbase.py", line 314, in advance_eventloop
eventloop(self)
File "C:\Users\me\Anaconda3\lib\site-packages\ipykernel\eventloops.py", line 131, in loop_qt5
return loop_qt4(kernel)
File "C:\Users\me\Anaconda3\lib\site-packages\ipykernel\eventloops.py", line 117, in loop_qt4
kernel.app.setQuitOnLastWindowClosed(False)
AttributeError: 'QCoreApplication' object has no attribute 'setQuitOnLastWindowClosed'
I'm not using any windows. It doesn't happen every time though, after running once I usually don't see the error again.
Edit: It may be that setting spyder preference ipython console graphics backend as 'automatic' which may use Qt causes this error, instead of setting it as 'inline'. I found that using app = QtWidgets.QApplication([]) with app.setQuitOnLastWindowClosed(True) while commenting out app.quit() let's the plot show while using the automatic setting, without an error.

It seems that you are using some special environment(seems Spyder) where the developer assumes that you are using QGuiApplication or QApplication as an eventloop that have the setQuitOnLastWindowClosed method. A workaround is to create a class that inherits from QCoreApplication and has that method.
class CoreApplication(QtCore.QCoreApplication):
def setQuitOnLastWindowClosed(self, quit):
pass
maxcounts = 10
counter = []
app = CoreApplication([])
# ...

Related

python 3.4, setting up QWidgets.QMessageBox

an update to my code, based on the reply of Israel Unterman:
The Error-Class is now
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QMainWindow
class Error(QtWidgets.QMainWindow):
reply = False
last_reply_id = None
last_id = 0
def __init__(self, error_code_string, parent=None):
super().__init__(parent)
QtWidgets.QMessageBox.warning(self, "Warnung", error_code_string, QtWidgets.QMessageBox.Ok)
id = give_id(self)
def give_id(self):
self.last_id += 1
return self.last_id
def give_reply(self):
if last_id == last_reply_id:
return self.reply
else:
return None
def set_reply(self, button, id):
if button in (QMessageBox.Ok, QMessageBox.Yes):
reply = True
else:
reply = False
self.last_reply_id = id
return reply
And the Test-Script comes with
from ErrorHandling import Error
Error('Test')
If I am using the normal Code (practically the same Code, just wrapped in a class) the message appears and then at the line
id = give_id(self)
the Code stops without any errormessage from python, just:
Process finished with exit code 1
If I use the test-script, there is nothing (No QMessageBox!) than this:
Process finished with exit code 1
If I debug the Code, init() gets the same Objects and variables, but
super().__init__(parent)
fails without any message.
So where is the mistake, or difference.
Here a shortend Version of the Class (it's too long to show all code here), from which the "Error" works nearly fine:
from ErrorHandling import Error
class MainWindow(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# set some variables
self.create_layout()
def create_layout(self):
# creates a GUI using QWidgets with some Inputboxes and one button
[...]
def button_start_clicked(self):
Error('Check the input')
Here is the old question:
I have a problem regarding the setup of QtWidgets.QMessageBox.
All Code follows the description.
The ErrorHandling-Modul should give a message about an error.
If needed it may ask a question too.
The function ErrorMsg.errorMessage is called from other Moduls in case of an catched exception.
There will be more functions added.
If I run the code the following error occurs:
Connected to pydev debugger (build 145.1504)
Traceback (most recent call last):
File "C:\Program Files (x86)\JetBrains\PyCharm Community Edition 2016.1.4\helpers\pydev\pydevd.py", line 1531, in <module>
globals = debugger.run(setup['file'], None, None, is_module)
File "C:\Program Files (x86)\JetBrains\PyCharm Community Edition 2016.1.4\helpers\pydev\pydevd.py", line 938, in run
pydev_imports.execfile(file, globals, locals) # execute the script
File "C:\Program Files (x86)\JetBrains\PyCharm Community Edition 2016.1.4\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "C:/Quellcode/AllgTest.py", line 5, in <module>
reply = errm.errorMessage('Test')
File "C:/Quellcode\ErrorHandling.py", line 20, in errorMessage
msg_box.setIcon(QMessageBox.Warning)
TypeError: QMessageBox.setIcon(QMessageBox.Icon): first argument of unbound method must have type 'QMessageBox'
Process finished with exit code 1
I tried quite some variations and googled, but I have no idea what the problem is since I found some examples that are using the line
QMessageBox.setIcon(QMessageBox.Icon)
So where is my mistake?
And now the Code:
There is the following testscript to test my ErrorMsg-class
from ErrorHandling import ErrorMsg
errm = ErrorMsg()
reply = errm.errorMessage('Test')
And here is my ErrorHandling-Modul
from PyQt5.QtWidgets import QMessageBox
from PyQt5.QtWidgets import QMainWindow
class ErrorMsg(QMainWindow):
def __init__(self):
pass
def giveback(self,button):
if button in (QMessageBox.Ok, QMessageBox.Yes):
reply = True
else:
reply = False
return reply
def errorMessage(self, error_msg, buttons='OK'):
msg_box = QMessageBox
msg_box.setIcon(QMessageBox.Warning)
msg_box.setWindowTitle('Warning')
if buttons == 'OK':
msg_box.setStandardButtons(QMessageBox.Ok)
elif buttons == 'YesNo':
msg_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
else:
error_msg = 'Unknown Button >' + buttons + '<, use >OK< or >YesNo<'
raise ValueError(error_msg)
msg_box.setText(error_msg)
clicked_button = msg_box.exec()
return giveback(clicked_button)
Thanks for your help
James
You didn't create an object of the message box. To create an object use:
msg_box = QMessageBox()
But you don'y need to go through all this, since QMessageBox has static functions for showing messages, which you can call directly on the QMessageBox class. For example:
QMessageBox.warning(None, 'title', 'msg')
You also have some control over the butons, see QMessageBox

Saving exceptions in Tkinter using Python

I have developed several Python programs for others that use Tkinter to receive input from the user. In order to keep things simple and user-friendly, the command line or python console are never brought up (ie. .pyw files are used), so I'm looking into using the logging library to write error text to a file when an exception occurs. However, I'm having difficulty getting it to actually catch the exceptions. For example:
We write a function that will cause an error:
def cause_an_error():
a = 3/0
Now we try to log the error normally:
import logging
logging.basicConfig(filename='errors.log', level=logging.ERROR)
try:
cause_an_error()
except:
logging.exception('simple-exception')
As expected, the program errors, and logging writes the error to errors.log. Nothing appears in the console. However, there is a different result when we implement a Tkinter interface, like so:
import logging
import Tkinter
logging.basicConfig(filename='errors.log', level=logging.ERROR)
try:
root = Tkinter.Tk()
Tkinter.Button(root, text='Test', command=cause_an_error).pack()
root.mainloop()
except:
logging.exception('simple-exception')
In this case, pressing the button in the Tkinter window causes the error. However, this time, nothing is written to the file, and the following error appears in the console:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1536, in __call__
return self.func(*args)
File "C:/Users/samk/Documents/GitHub/sandbox/sandbox2.pyw", line 8, in cause_an_error
a = 3/0
ZeroDivisionError: integer division or modulo by zero
Is there a different way to catch and log this error?
It's not very well documented, but tkinter calls a method for exceptions that happen as the result of a callback. You can write your own method to do whatever you want by setting the attribute report_callback_exception on the root window.
For example:
import tkinter as tk
def handle_exception(exception, value, traceback):
print("Caught exception:", exception)
def raise_error():
raise Exception("Sad trombone!")
root = tk.Tk()
# setup custom exception handling
root.report_callback_exception=handle_exception
# create button that causes exception
b = tk.Button(root, text="Generate Exception", command=raise_error)
b.pack(padx=20, pady=20)
root.mainloop()
For reference:
http://epydoc.sourceforge.net/stdlib/Tkinter.Tk-class.html#report_callback_exception
Since this question was about logging and being explicit where the error is occurring, I will expand on Bryan (totally correct) answer.
First, you have to import some additional useful modules.
import logging
import functools # for the logging decorator
import traceback # for printing the traceback to the log
Then, you have to configure the logger and the logging function:
logging.basicConfig(filename='/full/path/to_your/app.log',
filemode='w',
level=logging.INFO,
format='%(levelname)s: %(message)s')
def log(func):
# logging decorator
#functools.wraps(func)
def wrapper_log(*args, **kwargs):
msg = ' '.join(map(str, args))
level = kwargs.get('level', logging.INFO)
if level == logging.INFO:
logging.info(msg)
elif level == logging.ERROR:
logging.exception(msg)
traceback.print_exc()
return wrapper_log
#log
def print_to_log(s):
pass
Note that the function doing the logging is actually log and you can use it for both printing errors and regular info into your log file. How you use it: with the function print_to_log decorated with log. And instead of pass you can put some regular print(), so that you save the message to the log and print it to the console. You can replace pass with whatever commands you prefer.
Note nb. 2 we use the traceback in the log function to track where exactly your code generated an error.
To handle the exception, you do as in the already accepted answer. The addition is that you pass the message (i.e. the traceback) to the print_to_log function:
def handle_exception(exc_type, exc_value, exc_traceback):
# prints traceback in the log
message = ''.join(traceback.format_exception(exc_type,
exc_value,
exc_traceback))
print_to_log(message)
Having configured all this, now you you tell your Tkinter app that it has to use the handle_exception function:
class App(tk.Tk):
# [the rest of your app code]
if __name__ == '__main__':
app = App()
app.report_callback_exception = handle_exception
app.mainloop()
Or alternatively, if you work with multiple classes, you can even instruct the class itself to use the andle_exception function:
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
tk.Tk.report_callback_exception = handle_exception
# [the rest of your app code]
if __name__ == '__main__':
app = App()
app.mainloop()
In this way, your app code looks lean, you don't need any try/except method, and you can log at info level whichever event in your app.

PyQt: No error msg (traceback) on exit

My PyQt application no longer prints the error (stderr?) to the console.
I use QtDesigner and import the UI like this:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
from PyQt5.uic import loadUiType
Ui_MainWindow, QMainWindow = loadUiType("test.ui")
class Main(QMainWindow, Ui_MainWindow):
"""Main window"""
def __init__(self,parent=None):
super(Main, self).__init__(parent)
self.setupUi(self)
self.pushButton.clicked.connect(self.testfunc)
def testfunc(self):
print(9/0)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main = Main()
main.show()
sys.exit(app.exec_())
test.ui contains a QPushButton and a label. When I call testfunc (which obviously gives an error) in a non-Qt application, I get the error message, traceback, etc. When I execute this code, it just exits.
I wrote a PyQt application without QtDesigner before and it printed the errors to the console as expected. What's the difference with QtDesigner and inheritance?
This is probably due to changes in the way exceptions are dealt with in PyQt-5.5. To quote from the PyQt5 Docs:
In PyQt v5.5 an unhandled Python exception will result in a call to
Qt’s qFatal() function. By default this will call abort() and the
application will terminate. Note that an application installed
exception hook will still take precedence.
When I run your example in a normal console, this is what I see:
$ python test.py
Traceback (most recent call last):
File "test.py", line 213, in testfunc
print(9/0)
ZeroDivisionError: division by zero
Aborted (core dumped)
So the main difference is that the application will now immediately abort when encountering an unhandled exception (i.e. just like a normal python script would). Of course, you can still control this behaviour by using a try/except block or globally by overriding sys.excepthook.
If you're not seeing any traceback, this may be due to an issue with the Python IDE you're using to run your application.
PS:
As a bare minimum, the old PyQt4 behaviour of simply printing the traceback to stdout/stderr can be restored like this:
def except_hook(cls, exception, traceback):
sys.__excepthook__(cls, exception, traceback)
if __name__ == "__main__":
import sys
sys.excepthook = except_hook
I've been using python's traceback module in conjunction with a try/except statement to make sure the traceback is printed before exiting:
https://docs.python.org/3/library/traceback.html
Spefically, I use traceback.print_exc()

Ping-Tool in PyQt4 and Scapy

I'm trying to make a little pinging tool with Scapy and PyQt4.
The code is fairly simple and all it does now is pinging an address the user can type in.
from PyQt4 import QtGui
import sys
from scapy.all import *
from scapy.sendrecv import sr, send
def q2s(qstr): return "%s" %qstr
class Application(QtGui.QMainWindow):
def __init__(self):
super(Application, self).__init__()
self.resize(1000,500)
self.centre()
self.initGui()
self.show()
def initGui(self):
self.ipAddress = QtGui.QLineEdit("1.1.1.1",self)
self.label = QtGui.QLabel("...")
self.label.move(50,100)
pingBtn = QtGui.QPushButton("Ping!", self)
pingBtn.move(50,50)
pingBtn.clicked.connect(self.ping)
def ping(self):
ip = q2s(self.ipAddress.text())
ans, unans = sr(IP(dst=ip)/ICMP(), timeout=1, verbose=0)
if ans:
self.label.setText("Host is up")
else:
self.label.setText("Host is down")
def centre(self):
screen = QtGui.QDesktopWidget().screenGeometry()
sizeNow = self.geometry()
self.move((screen.width() - sizeNow.width()) / 2,
(screen.height() - sizeNow.height()) / 2)
def run():
app = QtGui.QApplication(sys.argv)
GUI = Application()
sys.exit(app.exec_())
run()
However, when trying to ping an IP address an error is printed to the console.
Traceback (most recent call last):
File "Application.py", line 71, in ping
ans, unans = sr(IP(dst=ip)/ICMP(), timeout=1, verbose=0)
File "/usr/lib/python2.7/dist-packages/scapy/sendrecv.py", line 317, in sr
a,b=sndrcv(s,x,*args,**kargs)
File "/usr/lib/python2.7/dist-packages/scapy/sendrecv.py", line 129, in sndrcv
inp, out, err = select(inmask,[],[], remaintime)
select.error: (4, 'Unterbrechung w\xc3\xa4hrend des Betriebssystemaufrufs')
The last line means something like "Interruption during call of operating system".
I can not see what might be wrong about the program.
Using the send function instead of the sr function works somehow. So I think the problem might be that the application is waiting for a response. But I still don't know how to fix the error.
It's a bug in Scapy: an exception we want to catch when calling select() is different when in multi-thread environment.
A pull-request (#417) is currently examined (I'll update this answer when it has been merged), so you can check if the patch fixes this issue for you (I hope so!).

Python curses TypeError when exiting wrapper

I'm running Mac OS X 10.9.5 and when wrapping my main() function with curses.wrapper I'm getting the following error after my program exits successfully:
Traceback (most recent call last):
File "test.py", line 42, in <module>
wrapper(main(SCREEN))
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/curses/__init__.py", line 94, in wrapper
return func(stdscr, *args, **kwds)
TypeError: 'NoneType' object is not callable
Some amplifying code:
if __name__ == "__main__":
# Initialize screen
SCREEN = curses.initscr()
# Run program with wrapper in case it fails
wrapper(main(SCREEN))
# Terminal cleanup
curses.nocbreak()
SCREEN.keypad(False)
curses.echo()
If I use CTRL + C to attempt to exit the program while it's running, the exception is shown but the terminal remains in a disarrayed state (wrapper doesn't do it's job). Am I missing an something obvious here?
EDIT *
I confirmed this also happens on Ubuntu 14.10 server edition over a remote SSH terminal session.
As far as I can see, you're calling the curses.wrapper function wrongly.
From the documentation:
curses.wrapper(func, ...) Initialize curses and call another callable object, func, which should be the rest of your curses-using application. (...) The callable object func is then passed the main window ‘stdscr’ as its first argument, followed by any other arguments passed to wrapper().
In your example, it should look like this:
def main(SCREEN):
... # My program code
if __name__ == "__main__":
# The function main gets the stdscr passed by curses itself
wrapper(main)
If you need to access to stdscr before the main call
I would not use wrapper in that case, but use curses.endwin() to deinitialize the curses library. Untested example:
SCREEN = curses.initscr()
# Modify your curses settings here
try:
main(SCREEN)
except: # End curses session before raising the error
curses.endwin()
raise
else: # End curses session if program terminates normally
curses.endwin()

Categories

Resources