I have three Python(3.4.3) scripts. One of them is for controlling the .ui file generated by PyQt5. When I run the GUI program it accepts all the data and everything and when I press the OK button on an InputDialog the window closes and the console displays.
Process finished with exit code 1
When I run the same code on Python IDLE, it shows:
<<<<<<RESTART>>>>>>
This never happenned when I used this same Python(3.4.3 or 2.7) code on Visual Studio. What could be the reason?
Here is the code of the python file controlling the .ui file.
import sys
from PyQt5 import QtCore, QtGui, uic, QtWidgets
from Email import encrypt_email
from Email import decrypt_email
from Email import newuser
qtCreatorFile = "rsegui.ui" # Enter file here.
Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)
class MyApp(QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
QtWidgets.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
user, ok = QtWidgets.QInputDialog.getText(self, 'New User',
'Are you a new user?')
user=str(user)
if user in "YESYesyesYy":
email, ok = QtWidgets.QInputDialog.getText(self, 'New User',
'Enter Your Email ID:')
email1=str(email)
self.sender.setText(email)
newuser(email1)
self.encrypt_and_send.clicked.connect(self.EncryptEmail)
self.decrypt.clicked.connect(self.DecryptEmail)
self.clear.clicked.connect(self.ClearEncrypt)
self.clear_2.clicked.connect(self.ClearDecrypt)
self.sender.setPlaceholderText("Your Email ID")
self.receiver.setPlaceholderText("Receivers, Separate them by ';'")
self.subject.setPlaceholderText("Enter Subject")
self.message.setPlaceholderText("Enter Message")
self.sender_2.setPlaceholderText("Your Email ID")
self.message_2.setPlaceholderText("Encrypted Text")
def EncryptEmail(self):
sender = str(self.sender.text())
receiver = str(self.receiver.text())
receivers = receiver.split(';')
subject = str(self.subject.text())
message = str(self.message.text())
password, ok = QtWidgets.QInputDialog.getText(self, 'Password',
'Enter your password:',QtWidgets.QLineEdit.Password)
encrypt_email(sender,receivers,subject,message,password)
def DecryptEmail(self):
email = str(self.sender_2.text())
message = str(self.message_2.text())
self.decrypted.setText(decrypt_email(email,message))
def ClearDecrypt(self):
self.sender_2.clear()
self.message_2.clear()
def ClearEncrypt(self):
self.sender.clear()
self.message.clear()
self.receiver.clear()
self.subject.clear()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
I have dealt with the same problem, and the answer is twofold:
The reason it's crashing could be any number of things. It's probably a programming bug, calling a function that doesn't exist, passing a widget instead of a layout, etc. But since you're not getting useful output you don't know where to look for the culprit. This is caused by:
PyQT raises and catches exceptions, but doesn't pass them along. Instead it just exits with a status of 1 to show an exception was caught.
To catch the exceptions, you need to overwrite the sys exception handler:
# Back up the reference to the exceptionhook
sys._excepthook = sys.excepthook
def my_exception_hook(exctype, value, traceback):
# Print the error and traceback
print(exctype, value, traceback)
# Call the normal Exception hook after
sys._excepthook(exctype, value, traceback)
sys.exit(1)
# Set the exception hook to our wrapping function
sys.excepthook = my_exception_hook
Then in your execution code, wrap it in a try/catch.
try:
sys.exit(app.exec_())
except:
print("Exiting")
I had the same problem in pycharm, python 3.8, qt5. The stacktrace was never shown for qt errors inside pycharm; running the file from cmd the error was shown correctly instead.
I solved by doing the following:
open Edit Configurations of the file you want to run, scroll down and check the box Emulate terminal in output console.
You have used self.sender.setText(email)
This is probably causing the problem in my opinion because, "sender" is the name in QObject's function and it does not have any setText attribute, so there may be the problem.
You have to specifically call the widget and setText to it.
For this, you can use instances of the py file of the layout creator.
I had the same issue when I was trying to use this self.ui.lineEdit().text()
Here, the problem was -> I was calling the lineEdit function, whereas I had to use it's one attribute.
Related
Given this code:
from time import sleep
class TemporaryFileCreator(object):
def __init__(self):
print 'create temporary file'
# create_temp_file('temp.txt')
def watch(self):
try:
print 'watching tempoary file'
while True:
# add_a_line_in_temp_file('temp.txt', 'new line')
sleep(4)
except (KeyboardInterrupt, SystemExit), e:
print 'deleting the temporary file..'
# delete_temporary_file('temp.txt')
sleep(3)
print str(e)
t = TemporaryFileCreator()
t.watch()
during the t.watch(), I want to close this application in the console..
I tried using CTRL+C and it works:
However, if I click the exit button:
it doesn't work.. I checked many related questions about this but it seems that I cannot find the right answer..
What I want to do:
The console can be exited while the program is still running.. to handle that, when the exit button is pressed, I want to make a cleanup of the objects (deleting of created temporary files), rollback of temporary changes, etc..
Question:
how can I handle console exit?
how can I integrate it on object destructors (__exit__())
Is it even possible? (how about py2exe?)
Note: code will be compiled on py2exe.. "hopes that the effect is the same"
You may want to have a look at signals. When a *nix terminal is closed with a running process, this process receives a couple signals. For instance this code waits for the SIGHUB hangup signal and writes a final message. This codes works under OSX and Linux. I know you are specifically asking for Windows but you might want to give it a shot or investigate what signals a Windows command prompt is emitting during shutdown.
import signal
import sys
def signal_handler(signal, frame):
with open('./log.log', 'w') as f:
f.write('event received!')
signal.signal(signal.SIGHUP, signal_handler)
print('Waiting for the final blow...')
#signal.pause() # does not work under windows
sleep(10) # so let us just wait here
Quote from the documentation:
On Windows, signal() can only be called with SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, or SIGTERM. A ValueError will be raised in any other case.
Update:
Actually, the closest thing in Windows is win32api.setConsoleCtrlHandler (doc). This was already discussed here:
When using win32api.setConsoleCtrlHandler(), I'm able to receive shutdown/logoff/etc events from Windows, and cleanly shut down my app.
And if Daniel's code still works, this might be a nice way to use both (signals and CtrlHandler) for cross-platform purposes:
import os, sys
def set_exit_handler(func):
if os.name == "nt":
try:
import win32api
win32api.SetConsoleCtrlHandler(func, True)
except ImportError:
version = “.”.join(map(str, sys.version_info[:2]))
raise Exception(”pywin32 not installed for Python ” + version)
else:
import signal
signal.signal(signal.SIGTERM, func)
if __name__ == "__main__":
def on_exit(sig, func=None):
print "exit handler triggered"
import time
time.sleep(5)
set_exit_handler(on_exit)
print "Press to quit"
raw_input()
print "quit!"
If you use tempfile to create your temporary file, it will be automatically deleted when the Python process is killed.
Try it with:
>>> foo = tempfile.NamedTemporaryFile()
>>> foo.name
'c:\\users\\blah\\appdata\\local\\temp\\tmpxxxxxx'
Now check that the named file is there. You can write to and read from this file like any other.
Now kill the Python window and check that file is gone (it should be)
You can simply call foo.close() to delete it manually in your code.
How can I introduce a text in a lineEdit from a thread that are getting the data whithout colapse the program? The important line is in the class "fil" where it shows Principal.self.aplicacio.actual_lineEdit.setText(self.temp)
# !/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import serial
import threading
from time import sleep
from PyQt4 import QtCore, QtGui
from temperaturaUI import Ui_Form
class Principal(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.aplicacio = Ui_Form()
self.aplicacio.setupUi(self)
self.aplicacio.sortir_Button.clicked.connect(exit)
self.aplicacio.connectar_Button.clicked.connect(self.connectar)
def connectar(self):
try:
arduino = serial.Serial('/dev/ttyACM0', 9600)
print "Connectat amb èxit"
temperatura = fil(0, arduino, self.aplicacio.actual_lineEdit)
temperatura.start()
except:
print "Impossible connectar a l'Arduino"
class fil(threading.Thread):
def __init__(self, temp, serie, line):
threading.Thread.__init__(self)
self.temp = temp
self.serie = serie
self.line = line
def run(self):
try:
while 1:
self.temp = self.serie.readline()
if self.temp != 0:
**Principal.self.aplicacio.actual_lineEdit.setText(self.temp)**
sleep(0.2)
except:
print "Error al llegir de l'Arduino"
def main():
app = QtGui.QApplication(sys.argv)
aplicacio = Principal()
aplicacio.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
You can use signals. You would add a signal to the fil class that emits the new text:
class fil(threading.Thread):
update_line_edit = pyqtSignal(str)
def __init__(self, temp, serie, line):
...
def run(self):
try:
while True:
self.temp = self.serie.readline()
if not self.temp:
update_line_edit.emit(self.temp)
...
Then, simply connect that signal to a slot function in your Principal class:
class Principal(QtGui.QWidget):
def __init__(self):
...
def connectar(self):
try:
arduino = serial.Serial('/dev/ttyACM0', 9600)
print "Connectat amb èxit"
temperatura = fil(0, arduino, self.aplicacio.actual_lineEdit)
temperatura.change_line_edit.connect(self.update_line_edit)
...
def update_line_edit(self, text):
self.aplicacio.actual_lineEdit.setText(text)
There are a few ways to do this correctly.
The first is to use a QThread instead of a python thread. You can then use Qt signals to pass a message back from the fil thread to the Qt MainThread and append the message to the QLineEdit there. Another similar approach is to continue using a Python thread, but place your message in a Python Queue.Queue() object. This Queue is then read by a secondary QThread, whose sole purpose is to read messages out of the Queue and emit a signal back to the MainThread.
The common feature of these two methods is that you only access Qt GUI objects from the MainThread and use signals/slots to communicate between threads. Here are some other questions where I've answered similar questions (you should be able to adapt them to your program):
Redirecting stdout and stderr to a PyQt4 QTextEdit from a secondary thread
Syncing activity in PyQt QThreads
However, since answering those questions, my colleagues and I have created a project that helps simplify writing multi-threaded Qt applications. The project is called qtutils and is on PyPi so it can be installed with pip or easy_install (just run pip install qtutils or easy_install qtutils from a commandline/terminal window).
This library has (among others) some functions inmain and inmain_later which will run a specified method in the Qt MainThread (regardless of the thread the call is made from) synchronously or asynchronously. Documentation on how to use these methods is here. I've modified your example code to use my inmain method and put the code here: http://pastebin.com/QM1Y6zBx -- obviously you need to install qtutils for it to work!
I've spent ages looking for a way to do this, and I've so far come up with nothing. :(
I'm trying to make a GUI for a little CLI program that I've made - so I thought using Ubuntu's "Quickly" would be the easiest way. Basically it appears to use Glade for making the GUI. I know that I need to run my CLI backend in a subprocess and then send the stdout and stderr to a textview. But I can't figure out how to do this.
This is the code that Glade/Quickly created for the Dialog box that I want the output to appear into:
from gi.repository import Gtk # pylint: disable=E0611
from onice_lib.helpers import get_builder
import gettext
from gettext import gettext as _
gettext.textdomain('onice')
class BackupDialog(Gtk.Dialog):
__gtype_name__ = "BackupDialog"
def __new__(cls):
"""Special static method that's automatically called by Python when
constructing a new instance of this class.
Returns a fully instantiated BackupDialog object.
"""
builder = get_builder('BackupDialog')
new_object = builder.get_object('backup_dialog')
new_object.finish_initializing(builder)
return new_object
def finish_initializing(self, builder):
"""Called when we're finished initializing.
finish_initalizing should be called after parsing the ui definition
and creating a BackupDialog object with it in order to
finish initializing the start of the new BackupDialog
instance.
"""
# Get a reference to the builder and set up the signals.
self.builder = builder
self.ui = builder.get_ui(self)
self.test = False
def on_btn_cancel_now_clicked(self, widget, data=None):
# TODO: Send SIGTERM to the subprocess
self.destroy()
if __name__ == "__main__":
dialog = BackupDialog()
dialog.show()
Gtk.main()
If I put this in the finish_initializing function
backend_process = subprocess.Popen(["python", <path to backend>], stdout=subprocess.PIPE, shell=False)
then the process starts and runs as another PID, which is what I want, but now how do I send backend_process.stdout to the TextView? I can write to the textview with:
BackupDialog.ui.backup_output.get_buffer().insert_at_cursor("TEXT")
But I just need to know how to have this be called each time there is a new line of stdout.
But I just need to know how to have this be called each time there is a new line of stdout.
You could use GObject.io_add_watch to monitor the subprocess output or create a separate thread to read from the subprocess.
# read from subprocess
def read_data(source, condition):
line = source.readline() # might block
if not line:
source.close()
return False # stop reading
# update text
label.set_text('Subprocess output: %r' % (line.strip(),))
return True # continue reading
io_id = GObject.io_add_watch(proc.stdout, GObject.IO_IN, read_data)
Or using a thread:
# read from subprocess in a separate thread
def reader_thread(proc, update_text):
with closing(proc.stdout) as file:
for line in iter(file.readline, b''):
# execute update_text() in GUI thread
GObject.idle_add(update_text, 'Subprocess output: %r' % (
line.strip(),))
t = Thread(target=reader_thread, args=[proc, label.set_text])
t.daemon = True # exit with the program
t.start()
Complete code examples.
I am building application with interactive console interface (line htop, atop utilities) using urwid library, so my trouble is: as interface takes all the space in console window - I could not see python's errors, I tried to do that:
import sys
f = open("test_err", "w")
original_stderr = sys.stderr
sys.stderr = f
print a #a is undefined
sys.stderr = original_stderr
f.close()
It works when I dont use urwid, but not when I use it...
you could try redirecting errors to a file. after each time you run the program, you will need to refresh the file. most editors let you easily do this by pushing f5
def main():
#your code here
print someError #raises an error
try: #run main function
main()
except BaseException as err: #catch all errors
with open('errors.txt','a') as errors: #open a file to write the errors to
errors.write(err.message+'\n')#write the error
change the 'a' to 'w' in the open function if you only want to see one error in the file at a time (instead of having multiple error over a long period of time in one file).
if you want to see the error right when it happens, you can make the error catcher open a window that has the error on it.
def main():
#your code here
print someErr
try: #run main function
main()
except BaseException as err: #catch all errors
import Tkinter as tk #imports the ui module
root = tk.Tk() #creates the root of the window
#creates the text and attaches it to the root
window = tk.Label(root, text=err.message)
window.pack()
#runs the window
root.mainloop()
if you want to build your own window to catch errors, you can learn about Tkinter here. (it is built into python, you don't have to install anything)
Here's what I came up with. I'm taking advantage of unicode-rxvt (urxvt) feature to be passed in a file descriptor. Of course this means you need to be developing this in an X environment, and not a console.
from __future__ import print_function
import os
from datetime import datetime
_debugfile = None
def _close_debug(fo):
fo.close()
def DEBUG(*obj):
"""Open a terminal emulator and write messages to it for debugging."""
global _debugfile
if _debugfile is None:
import atexit
masterfd, slavefd = os.openpty()
pid = os.fork()
if pid: # parent
os.close(masterfd)
_debugfile = os.fdopen(slavefd, "w+", 0)
atexit.register(_close_debug, _debugfile)
else: # child
os.close(slavefd)
os.execlp("urxvt", "urxvt", "-pty-fd", str(masterfd))
print(datetime.now(), ":", ", ".join(map(repr, obj)), file=_debugfile)
This will open a new terminal window automatically when you call DEBUG for the first time and close it at exit. Then any messages passed to it are shown in this new window. This is your "debug window". So your main app works normally, without cluttering it up with messages, but you can still see debug output in this new terminal.
I have a Django app in which the users can create reports at various places throughout the webapp. As a little nifty feature I would like to make a "send as PDF" feature for these reports, and I'm almost there.
What I do is that before the report is returned as a HttpResponse through Django I send the raw HTML content through a small PySide/QT snippet (as seen below).
The problem is that I can't get the QApplication to quit.
I have tried with the standard QCoreApplication.exit() but with no luck. If I try to convert a new report right after the first one, the console says a "QApplication instance already exists".
I am using Django 1.2.5, Python 2.7, QT 4.8 and PySide 1.1 on OS X 10.7.3 (for testing).
Code:
def makepdf(response,filename):
try:
app = QApplication(sys.argv)
except:
app = QCoreApplication.instance()
web = QWebView()
stylelink = "%s%s" % (media_root,'images/css/reportGenerator.css')
web.settings().setUserStyleSheetUrl(QUrl.fromLocalFile(stylelink))
web.setHtml(response)
printer = QPrinter()
printer.setPageSize(QPrinter.A4)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName(filename)
def convertToPdf():
web.print_(printer)
#webresponse = web.close()
#QObject.disconnect()
#print QCoreApplication.instance().children()[0].interrupt()
#qthread = QCoreApplication.instance().thread()#.cleanup()
#qthread.exit()
QCoreApplication.exit()
QObject.connect(web, SIGNAL("loadFinished(bool)"), convertToPdf())
Code comments:
Currently I have made a try/except clause to be able to keep the code running, by using the current QApplication instance (to avoid the 'instance exists error'), but this just doesn't seem right?. I mean, having a QAppliation running for the duration of my Apache server (which will be the case in production) seems a bit off. Shouldn't it be possible to quit it after the PDF conversion is done?
Other than the QCoreApplication.exit() I have tried to use the sys.exit(app.exec_()) method, as is often seen in examples and snippets. This however just causes an error and makes Python crash and the code works perfectly without this. (well it creates the PDF, but won't quit).
All the lines commented out are the previous attempts I have made to quit the QApplication.
In short: I don't know what it is, but it just won't quit. Can anyone suggest why?
Update: After the latest answer I have edited the code to respond to the input. This is how the final part looks now:
def convertToPdf():
web.print_(printer)
app.exit()
web.loadFinished.connect(convertToPdf)
app.exec_()
I do, however, still get an error:
2012-04-30 00:16:10.791 Python[21241:1803] * Assertion failure in +[NSUndoManager _endTopLevelGroupings], /SourceCache/Foundation/Foundation-833.24/Misc.subproj/NSUndoManager.m:324
Qt has caught an exception thrown from an event handler. Throwing exceptions from an event handler is not supported in Qt. You must reimplement QApplication::notify() and catch all exceptions there.
This error only occurs when I implement app.exec_(). However without app.exec_() it is just the normal issue again, with no quitting.
Any ideas?
Update 2: this is the newest code, fixed in accordance with matas latest suggestion:
app = QApplication(sys.argv)
web = QWebView()
printer = QPrinter()
printer.setPageSize(QPrinter.A4)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName(filename)
def convertToPdf():
web.print_(printer)
app.exit()
web.loadFinished.connect(convertToPdf)
web.setHtml(response)
I still have the same problem however.
what i can say is this:
QObject.connect(web, SIGNAL("loadFinished(bool)"), convertToPdf())
here you call convertToPdf(), if you want to connect the signal, omit the parenthesis!
you could also use this much clearer syntax:
web.loadFinished.connect(convertToPdf)
you may also want to add a parameter to convertToPdf, as it is called with a boolean indicating wheather loading was successful or not.
And using app.exit() should be enough.
oh, and when you use Gui-Components you need to use a QApplication. A QCoreApplication won't do!
edit: it's important to call web.setHtml after you've connected the loadFinished signal! otherwise if the loading already is finished, your function will never be executed!
edit: this works without any problem for me:
#!/usr/bin/env python
from PySide.QtGui import *
from PySide.QtWebKit import *
import sys
from subprocess import Popen, PIPE
def createPdf(html, filename):
app = QApplication(sys.argv)
web = QWebView()
printer = QPrinter()
printer.setPageSize(QPrinter.A4)
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName(filename)
def convertToPdf():
web.print_(printer)
app.exit()
app.deleteLater()
web.loadFinished.connect(convertToPdf)
web.setHtml(html)
app.processEvents()
def createPdfInSubprocess(html, filename):
p = Popen(["python", __file__, filename],
stdin=PIPE, stdout=PIPE, stderr=PIPE)
out, err = p.communicate(html)
return (p.returncode, out, err)
if __name__ == '__main__':
if len(sys.argv) > 1:
# read html from stdin, filename from cmdline
html = "\n".join(sys.stdin.readlines())
createPdf(html, sys.argv[1])
# print("done")
else:
# test if it's working
ret = createPdfInSubprocess(
"<html><h1>test</h1>it's working...</html>", "test.pdf")
print(ret)
Even without all the calls to app.exit(), app.deleteLater(), app.processEvents(), it still works... but can't hurt to have that.
One more important thing: QApplications have to be created from the main thread! So if it's running inside a django app it's probably not going to work, that's why i added the subprocess stuff...
PySide is designed such that there is only ever one instance of QCoreApplication within a process. The best reference to this (possibly undocumented fact) that I could find was http://bugs.pyside.org/show_bug.cgi?id=855.
Basically, there may be dangling references to the qapplication that prevent it from being reaped by the garbage collector, so even if you tell the application to exit and you delete your reference, there may still be other references lying around.