Why is the error not getting caught by try...except? [closed] - python

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 13 days ago.
Improve this question
from PyQt6.QtWidgets import QMainWindow
from PyQt6.QtCore import QThread
class Window_dev(QMainWindow):
def __init__(self):
super(Window_dev, self).__init__()
self.initUI()
def initUI(self):
self.thread()
self.worker = WorkerThread()
self.worker.start()
self.show()
class WorkerThread1(QThread):
def run(self):
try:
os.system("python server.py")
except Exception as err:
print(err)
def main():
app = QApplication(sys.argv)
ex = Window_dev()
sys.exit(app.exec())
if __name__ == '__main__':
main()
When server.py encounters an error and crashes, the error should be captured by my python program. When I run this nothing is being caught.
sorry, this was closed by the admins because they think that there is no solution but there is a solution. here is the solution for everyone's benefit:
import sys
from PyQt5.QtCore import QProcess
from PyQt5.QtWidgets import QApplication, QMainWindow, QAction, QFileDialog, QTextEdit, QVBoxLayout, QWidget
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
# Create a "Open File" action with a keyboard shortcut
open_file_action = QAction('Open File', self)
open_file_action.triggered.connect(self.run_file)
open_file_action.setShortcut('Ctrl+O')
# Add the action to a menu
file_menu = self.menuBar().addMenu('File')
file_menu.addAction(open_file_action)
# Create a QTextEdit widget to display error output
self.text_edit = QTextEdit(self)
self.setCentralWidget(self.text_edit)
def run_file(self):
# Open a file dialog to choose a Python file
file_name, _ = QFileDialog.getOpenFileName(self, 'Open Python File', '', 'Python Files (*.py)')
if file_name:
# Create a QProcess to run the Python program
process = QProcess(self)
process.setProgram('python')
process.setArguments([file_name])
process.readyReadStandardError.connect(self.read_error_output)
# Start the process
process.start()
def read_error_output(self):
# Read the error output from the QProcess
data = self.sender().readAllStandardError().data().decode('utf-8')
# Display the error output in the QTextEdit widget
self.text_edit.insertPlainText(data)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
this program is very simple, it can run the selected python program and capture the standard errors like attribute or division by zero errors

While I do not know what the script server.py is supposed to do, the general rule here is to use the subprocess package, intended to replace things like os.system in the near future.
For your case in hand, the following code throws if any error is found when running the command given (python3) with the given parameters (server.py):
import subprocess as sp
def run():
try:
a = sp.run(["python3", "server.py"])
a.check_returncode()
except sp.CalledProcessError as err:
raise err
run()
print("This line will not run!")
check_returncode() returns the exit code of the command run. If the code is nonzero (i.e. if an error occurred), such a function will throw a subprocess.CalledProcessError, which you can catch and raise if wanted.
Note that if you do not rise, the script will continue its normal course. Compare the above code with
import subprocess as sp
def run():
try:
a = sp.run(["python3", "server.py"])
a.check_returncode()
except sp.CalledProcessError as err:
print(err)
run()
print("This line will still run!")

Related

PYQt5: QThread: Destroyed while thread is still running ( with instance assigned as QDialog )

I am encountering this problem QThread: Destroyed while thread is still running when I close the NextWindow QDialog.
The code I wrote is
import sys
from PyQt5 import QtWidgets as qtw
from PyQt5 import QtGui as qtg
from PyQt5 import QtCore as qtc
from pathlib import Path
from hashlib import sha1
def recursive_hashes(path):
"""Generate name and SHA1 hash of all files under the given path"""
if path.is_file():
sha1_obj = sha1()
try:
with open(path, 'rb') as handle:
while True:
data = handle.read(8192)
if not data:
break
sha1_obj.update(data)
sha1_hash = sha1_obj.hexdigest()
except PermissionError:
sha1_hash = 'Permission Denied'
yield (str(path), sha1_hash)
elif path.is_dir():
try:
for child in path.iterdir():
yield from recursive_hashes(child)
except PermissionError:
yield(str(path), 'Permission Denied')
else:
yield (str(path), 'Not a file or dir')
class Worker(qtc.QObject):
hashed = qtc.pyqtSignal(str, str)
#qtc.pyqtSlot(str)
def hash_directory(self, root):
hash_gen = recursive_hashes(Path(root))
for path, sha1_hash in hash_gen:
self.hashed.emit(path, sha1_hash)
class NextWindow(qtw.QDialog):
hash_requested = qtc.pyqtSignal(str)
def __init__(self):
"""MainWindow constructor."""
super().__init__()
layout = qtw.QFormLayout()
self.setLayout(layout)
self.file_root = qtw.QLineEdit(returnPressed=self.start_hashing)
self.go_button = qtw.QPushButton('Start Hashing', clicked=self.start_hashing)
self.results = qtw.QTableWidget(0, 2)
self.results.setHorizontalHeaderLabels(['Name', 'SHA1-sum'])
self.results.horizontalHeader().setSectionResizeMode(qtw.QHeaderView.Stretch)
self.results.setSizePolicy(qtw.QSizePolicy.Expanding, qtw.QSizePolicy.Expanding)
layout.addRow(qtw.QLabel('SHA1 Recursive Hasher'))
layout.addRow('File Root', self.file_root)
layout.addRow('', self.go_button)
layout.addRow(self.results)
# Create a worker object and a thread
self.worker = Worker()
self.worker_thread = qtc.QThread(self)
self.worker.hashed.connect(self.add_hash_to_table)
self.hash_requested.connect(self.worker.hash_directory)
# Assign the worker to the thread and start the thread
self.worker.moveToThread(self.worker_thread)
self.worker_thread.start()
# Connect signals & slots AFTER moving the object to the thread
# End main UI code
self.show()
def add_hash_to_table(self, name, sha1_sum):
"""Add the given name and sha1 sum to the table"""
row = self.results.rowCount()
self.results.insertRow(row)
self.results.setItem(row, 0, qtw.QTableWidgetItem(name))
self.results.setItem(row, 1, qtw.QTableWidgetItem(sha1_sum))
print(name, sha1_sum)
def start_hashing(self):
"""Prepare the GUI and emit the hash_requested signal"""
# Clear the table
while self.results.rowCount() > 0:
self.results.removeRow(0)
# Get the file root and validate it
file_root = self.file_root.text()
if not Path(file_root).exists():
qtw.QMessageBox.critical(self, 'Invalid Path', 'The specified file root does not exist.')
return
# Emit the signal
self.hash_requested.emit(file_root)
#self.worker.hash_directory(file_root)
class MainWindow(qtw.QDialog):
def __init__(self):
super().__init__()
layout = qtw.QFormLayout()
self.next_button = qtw.QPushButton('Next', clicked=self.next_button)
layout.addWidget(self.next_button)
self.setLayout(layout)
def next_button(self):
dialog = NextWindow()
dialog.exec_()
if __name__ == '__main__':
app = qtw.QApplication(sys.argv)
mw = MainWindow()
mw.show()
sys.exit(app.exec())
I have checked reference answers in PyQt5 - QThread: Destroyed while thread is still running and QThread: Destroyed while thread is still running.
The answers are basically didnt assign instance to the QThread like
self.thread = QThread(parent=self)
While I did so but the issue persisted.
The debug message of QThread: Destroyed while thread is still running can be ubiquitous but the cause of the reason can be not always the same. For this case the thread has not been ended well when you close the dialog even you include the self as
self.worker_thread = qtc.QThread(self)
In this case you should reject the Qthread as mentioned in the question Terminating QThread gracefully on QDialog reject()
So you should add one more line in your code
self.worker_thread.start()
self.rejected.connect(self.thread.terminate)
According to the PYQT5 document
When the thread is terminated, all threads waiting for the thread to finish will be woken up.
Warning: This function is dangerous and its use is discouraged. The thread can be terminated at any point in its code path. Threads can be terminated while modifying data. There is no chance for the thread to clean up after itself, unlock any held mutexes, etc. In short, use this function only if absolutely necessary.
So I would suggest to add
self.rejected.connect(self.thread.quit)
or
self.rejected.connect(self.thread.exit)

Update widget with data from another Python process, without blocking it

I'm running a Python script that extracts and localizes some files. I'd like to use a QDialog to show the progress status through a QProgressBar, and the list of files that are being copied.
Let me start by saying that the localization script cannot be integrated in the PyQt script - otherwise I'm aware that the solution would be quite straightforward. I need to keep the localization script separated from the UI and have them running at the same time.
I thought about running the UI from the localization script through a thread, to avoid it blocking the localization process. But at that point I have no idea how to update the UI elements as I don't have an instance I can call and update since I've launched it with the thread.
This is a simplified example of the dialog code:
from PyQt5 import QtCore, QtWidgets
import sys
class Ui_dialog_main(object):
def setupUi(self, dialog_main):
dialog_main.setWindowTitle("Test")
dialog_main.resize(390, 120)
self.progress_bar = QtWidgets.QProgressBar(dialog_main)
self.progress_bar.setGeometry(QtCore.QRect(10, 60, 371, 30))
self.label_localizing = QtWidgets.QLabel(dialog_main)
self.label_localizing.setGeometry(QtCore.QRect(10, 10, 81, 25))
self.label_localizing.setText("Package:")
QtCore.QMetaObject.connectSlotsByName(dialog_main)
def start():
app = QtWidgets.QApplication(sys.argv)
dialog_main = QtWidgets.QDialog()
ui = Ui_dialog_main()
ui.setupUi(dialog_main)
dialog_main.show()
sys.exit(app.exec_())
And this is how I'm starting the thread in the localizer file:
thread = Thread(target=LocManager.start)
thread.start()
where LocManager is the name of the ui .py file.
Of course this way the main script doesn't get stuck by the ui, which is one of my goals - but I have no idea how to update the progress bar and label in this situation. I've found several threads discussing similar problems, but nothing that would exactly help with mine.
I hope my description was clear enough.
UPDATE:
I found a solution for this here, using pipes. Even if I'm not sure this is the appropriate way to tackle this issue, it definitely did the trick. Instead of working with a "Bi-directional Communication" between two PyQt GUIs, as described in the link, I modified the code to have a bi-directional communication between my GUI and my localization script.
One way to solve this is to run the dialog in a separate process, and then use some form of IPC to send the updates. The solution below uses Qt's QLocalServer and QLocalSocket classes to pass a dict encoded with json to the dialog process. The server emits a signal whenever new data is received, which the dialog connects to in order to process updates. When the sending process exits, the server process is automatically shut down.
test.py:
import time
from dlg_server import send_data
for message in 'One Two Three Four Five'.split():
send_data(message=message)
time.sleep(2)
dlg_server.py:
import sys, os, json, atexit
from PyQt5 import QtCore, QtWidgets, QtNetwork
SERVER = 'dlg_server'
_tries = 0
def send_data(**data):
socket = QtNetwork.QLocalSocket()
socket.connectToServer(SERVER, QtCore.QIODevice.WriteOnly)
if socket.waitForConnected(500):
socket.write(json.dumps(data).encode('utf-8'))
if not socket.waitForBytesWritten(2000):
raise RuntimeError('could not write to socket: %s' %
socket.errorString())
socket.disconnectFromServer()
elif socket.error() == QtNetwork.QAbstractSocket.HostNotFoundError:
global _tries
if _tries < 10:
if not _tries:
if QtCore.QProcess.startDetached(
'python', [os.path.abspath(__file__)]):
atexit.register(lambda: send_data(shutdown=True))
else:
raise RuntimeError('could not start dialog server')
_tries += 1
QtCore.QThread.msleep(100)
send_data(**data)
else:
raise RuntimeError('could not connect to server: %s' %
socket.errorString())
else:
raise RuntimeError('could not send data: %s' % socket.errorString())
class Server(QtNetwork.QLocalServer):
dataReceived = QtCore.pyqtSignal(object)
def __init__(self):
super().__init__()
self.newConnection.connect(self.handleConnection)
if not self.listen(SERVER):
raise RuntimeError(self.errorString())
def handleConnection(self):
data = {}
socket = self.nextPendingConnection()
if socket is not None:
if socket.waitForReadyRead(2000):
data = json.loads(str(socket.readAll().data(), 'utf-8'))
socket.disconnectFromServer()
socket.deleteLater()
if 'shutdown' in data:
self.close()
self.removeServer(self.fullServerName())
QtWidgets.qApp.quit()
else:
self.dataReceived.emit(data)
class Dialog(QtWidgets.QDialog):
def __init__(self):
super().__init__()
self.setGeometry(50, 50, 200, 30)
layout = QtWidgets.QVBoxLayout(self)
self.label = QtWidgets.QLabel()
layout.addWidget(self.label)
def handleDataReceived(self, data):
self.show()
self.label.setText(data.get('message', ''))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
dialog = Dialog()
server = Server()
server.dataReceived.connect(dialog.handleDataReceived)
app.exec_()

Unexpected SIGABRT and error code when exception encountered in PyQt app

Exceptions are handled in a strange way during Qt apps created from Python (creating using PyQt5 here, but I noticed similar behavior with PySide and PyQt4). Please consider the following script. It is perhaps a little too verbose, but I wanted to create a semi-realistic example, where the app, the main window, and the central widget were all created separately (perhaps by separate Python modules as in a realistic app).
from sys import argv, exit
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QLabel, QVBoxLayout, \
QPushButton
def main():
print("running the main Python function")
run_app()
def run_app():
print("intialising the app")
app = QApplication(argv)
ex = MainWindow()
exit(app.exec_())
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
print("initialising the main window")
gui = GUIWidget(self)
self.setCentralWidget(gui)
self.show()
class GUIWidget(QWidget):
def __init__(self, parent=None):
super(GUIWidget, self).__init__(parent)
print("initialising the gui")
self.vbox = QVBoxLayout()
self.setLayout(self.vbox)
self.button = QPushButton("Push me to produce an error")
self.button.clicked.connect(self.raise_an_expection)
self.vbox.addWidget(self.button)
# raise Exception # nicely handled exception
def raise_an_expection(self):
raise Exception # poorly handled exception
if __name__ == '__main__':
main()
With the final line of GUIWidget.__init__() uncommented, Python raises an exception and Process finished with exit code 1, as expected.
With this commented, an app with a button is created . Pushing the button raises an exception, as expected, but also Process finished with exit code 134 (interrupted by signal 6: SIGABRT), which I don't understand. On my mac, this also causes a Python quit unexpectedly dialog to appear.

Why python console in PyCharm doesn't show any error message when pyqt is used? [duplicate]

This question already has answers here:
PyQt: No error msg (traceback) on exit
(2 answers)
Closed 5 years ago.
I'm facing some issue with some of my code which use pyqt5.
When something go wrong in my Qt classes, the console doesn't log any information about why the crashes happened.
for instance with this code:
rom PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.centralWidget = QWidget()
self.color = self.centralWidget.palette().color(QPalette.Background)
self.setCentralWidget(self.centralWidget)
self.plotview = QGroupBox(" ")
self.layout_plotview = QVBoxLayout()
self.Button_Crash= QPushButton('Crash!')
self.layout_plotview.addWidget(self.Button_Crash)
self.centralWidget.setLayout(self.layout_plotview)
self.Button_Crash.clicked.connect(self.TestForCrash)
def TestForCrash(self,):
a=b
return
def main():
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
As b is not known in TestForCrash function, the Qt window just quits but I've got nothing in the console. I'm wondering if their is a way to force the console to automatically print some clue of what is going on.
For now I'm using a try except to go around the issue but I don't like this idea much:
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
class SurfViewer(QMainWindow):
def __init__(self, parent=None):
super(SurfViewer, self).__init__()
self.parent = parent
self.centralWidget = QWidget()
self.color = self.centralWidget.palette().color(QPalette.Background)
self.setCentralWidget(self.centralWidget)
self.plotview = QGroupBox(" ")
self.layout_plotview = QVBoxLayout()
self.Button_Crash= QPushButton('Crash!')
self.layout_plotview.addWidget(self.Button_Crash)
self.centralWidget.setLayout(self.layout_plotview)
self.Button_Crash.clicked.connect(self.TestForCrash)
def TestForCrash(self,):
try:
a=b
except BaseException as e:
msg = QMessageBox()
msg.setIcon(QMessageBox.Critical)
msg.setText(str(e))
msg.setStandardButtons(QMessageBox.Ok)
msg.exec_()
return
def main():
app = QApplication(sys.argv)
ex = SurfViewer(app)
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Is their another way to log some info in the console without using a try except?
As mention by #three_pineapples, I've got errors when I execute the script in 'real' windows terminal (with c:\anaconda3\python.exe) but not in the PyCharm console (when I run the script). So is their a way to force error logs in Pycharm directly? maybe it is an option I didn't find yet?
What you can do is redefine the exception hook sys.excepthook. To quote from the documentation:
When an exception is raised and uncaught, the interpreter calls sys.excepthook with three arguments, the exception class, exception instance, and a traceback object. In an interactive session this happens just before control is returned to the prompt; in a Python program this happens just before the program exits. The handling of such top-level exceptions can be customized by assigning another three-argument function to sys.excepthook.
Your custom function could display, for example, a QMessagebox. This is done in the function catch_exceptions() in the following example. That function also calls the old exception hook (stored in old_hook) so that the normal path of handling exceptions is followed in addition to the message box.
import sys
from PyQt5 import QtWidgets
def catch_exceptions(t, val, tb):
QtWidgets.QMessageBox.critical(None,
"An exception was raised",
"Exception type: {}".format(t))
old_hook(t, val, tb)
old_hook = sys.excepthook
sys.excepthook = catch_exceptions
def main():
app = QtWidgets.QApplication(sys.argv)
raise RuntimeError
if __name__ == "__main__":
main()

interactive python - keeping console interactive with a GUI mainloop

I am wondering how one would create a GUI application, and interact with it from the console that started it.
As an example, I would like to create a GUI in PyQt and work with it from the console. This could be for testing settings without restarting the app, but in larger projects also for calling functions etc.
Here is a simple example using PyQt:
import sys
from PyQt4 import QtGui
def main():
app = QtGui.QApplication(sys.argv)
w = QtGui.QWidget()
w.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
when this is run with python -i example.py the console is blocked as long as the main-loop is executed.
How can I call w.resize(100,100) while the GUI is running?
ops, posted wrong answer before
there is a post in Stack about that
Execute Python code from within PyQt event loop
The following example uses the code module to run a console in the command prompt (be sure to run the script from the command line). Subclassing QThread provides a route by which the console can be run in a separate thread from that of the main GUI and enables some interaction with it. The stub example below should be easy enough to incorporate into a larger packaged PyQt program.
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import threading #used only to id the active thread
import code
import sys
class Worker(QThread): #Subclass QThread and re-define run()
signal = pyqtSignal()
def __init__(self):
super().__init__()
def raise_sys_exit(self): #more gracefully exit the console
print('(Deactivated Console)')
raise SystemExit
def setup_console(self,global_dict):
console_exit = {'exit': self.raise_sys_exit}
self.console = code.InteractiveConsole(locals=dict(global_dict,**console_exit))
def run(self):
try:
print('worker', threading.get_ident())
self.console.interact()
except SystemExit:
self.signal.emit()
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args,**kwargs)
self.data = [1,2,3,4] #some data we might want to look at
layout = QVBoxLayout()
self.b = QPushButton("Interact")
self.b.clicked.connect(self.b_clicked)
layout.addWidget(self.b)
w = QWidget()
w.setLayout(layout)
self.setCentralWidget(w)
self.worker = Worker()
self.worker.signal.connect(self.finished)
def finished(self):
self.b.setEnabled(True)
def b_clicked(self):
print('main',threading.get_ident())
self.worker.setup_console(globals()) #pass the global variables to the worker
self.worker.start()
self.b.setEnabled(False) #disable the GUI button until console is exited
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()
The easiest way is to use IPython:
ipython --gui=qt4
See ipython --help or the online documentation for more options (e.g. gtk, tk, etc).

Categories