I'm very new to PyQt and threading (only about a year). I apologize for the long post (most of the questions are at the end). I could use some assistance with why my worker thread is losing output when performing the work. This is PyQt5 and Python 3.5.
#Creates your own signal class to output stdout.
class EmittingStream(QtCore.QObject):
textWritten = QtCore.pyqtSignal(str)
def write(self, text):
self.textWritten.emit(str(text))
def flush(self):
pass
class MainWindow(QtWidgets.QMainWindow, Ui_mainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent=parent)
self.setupUi(self)
self.closeButton.clicked.connect(self.close)
self.clearButton.clicked.connect(self.clear_all)
self.submitButton.clicked.connect(self.submit_ips)
self.addButton.clicked.connect(self.add_ip_on_button_press)
#Keep Save button inactive until an IP(s) have been submitted.
self.saveButton.clicked.connect(self.write_mem)
self.saveButton.setEnabled(False)
sys.stdout = EmittingStream(textWritten=self.normalOutputWritten)
sys.stderr = EmittingStream(textWritten=self.errorOutputWritten)
#Creates instance of WorkerThread in the main window
self.workerThread = WorkerThread()
#Creates isntance of WorkerThreadSave in the main window
self.workerThreadSave = WorkerThreadSave()
def __del__(self):
# Restore sys.stdout
sys.stdout = sys.__stdout__
# Uses EmittingStream class to output stdout to outputTextBox
def normalOutputWritten(self, text):
"""Append text to the QTextEdit."""
cursor = self.outputTextBox.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(text)
# self.outputTextBox.setTextCursor(cursor)
self.outputTextBox.ensureCursorVisible()
def errorOutputWritten(self, text):
cursor = self.outputTextBox.textCursor()
cursor.movePosition(QtGui.QTextCursor.End)
cursor.insertText(text)
self.outputTextBox.ensureCursorVisible()
def submit_ips(self):
msgBox = QtWidgets.QMessageBox()
msgBox.setIcon(QtWidgets.QMessageBox.Warning)
msgBox.setWindowTitle("Error")
if not hostiplist and not cidriplist:
msgBox.setText("Ooops, Please enter an acceptible host or network IP Address.")
msgBox.exec()
else:
print("*************** Process Started ***************\n")
self.submitButton.setEnabled(False)
self.addButton.setEnabled(False)
self.clearButton.setEnabled(False)
self.saveButton.setEnabled(True)
self.workerThread.start()
#Performs Save Function through API
def write_mem(self):
print("*************** Saving Device(s) Started ***************\n")
self.saveButton.setEnabled(False)
self.addButton.setEnabled(True)
self.clearButton.setEnabled(True)
self.submitButton.setEnabled(True)
del hostiplist[:]
del cidriplist[:]
self.workerThreadSave.start()
The above code is obviously my main UI. When clicking the submit button it starts a separate thread to perform the processing and work (and also to not freeze the UI).
Unfortunately, I end up losing some of the print statements (I'm assuming my signals and slots aren't performing or the thread stops before the output is printed).
class WorkerThread(QThread):
def __init__(self, parent=None):
super(WorkerThread, self).__init__(parent)
def run(self):
#Post function for adding host IP's
def post_object (f):
self.api_path = "/api/objects/networkobjects"
self.req = urllib.request.Request(str(server) + self.api_path, json.dumps(self.post_data).encode('utf-8'), headers)
self.base64string = base64.encodestring(('%s:%s' % (username,password)).encode()).decode().replace('\n', '')
self.req.add_header("Authorization", "Basic %s" % self.base64string)
try:
f = urllib.request.urlopen(self.req)#Had to add .request between the url.
self.status_code = f.getcode()
# print("Status code is {}.".format(self.status_code))
if self.status_code == 201:
print ("The following object was created successfully: {}".format(self.post_data["name"]))
except urllib.error.HTTPError as err: #Python3 requires urllib.error.HTTPError to work.
print ("Error received from server: {}. HTTP Status code: {}".format(err.reason, err.code))
try:
print("Yes {}".format(json.loads(err.read))) #json.loads is not being sent...if you format the error it'll send that...not the error itself.
json_error = json.loads(err.read)
if json_error:
print (json.dumps(json_error,sort_keys=True,indent=4, separators=(',', ': '))) #Error won't print json.dumps error
print ("Error received from server: {}. HTTP Status code: {}".format(err.reason, err.code))
except ValueError:
pass
finally:
if f: f.close()
return (f)
I run the post_object function within a for loop below:
try:
#Starts for loop REST API adds.
for s in serverList:
server = s
# Operation for CIDR IPs
for c in cidriplist:
self.network = c
self.patch_data_group = {
"members.add": [
{
"kind": "IPv4Network",
"value": str(self.network)
}]
}
patch_object_group(f)
jsonoutputlist.append(self.patch_data_group)
#Operation for single host IPs
for h in hostiplist:
self.host = h
self.post_data = {
"host": {
"kind": "IPv4Address",
"value": str(self.host)},
"kind": "object#NetworkObj",
"name": str(self.host),
"objectId": str(self.host)
}
self.patch_data_group = {
"members.add": [
{
"kind": "objectRef#NetworkObj",
"objectId": str(self.host)
}]
}
jsonoutputlist.append(self.post_data)
post_object(f)
patch_object_group(f)
# print ("\n JSON Data format for change control: \n{}".format(self.patch_data_group))
print("\n*************** IPs Add Complete ***************\n")
except:
(type, value, traceback) = sys.exc_info()
sys.excepthook(type, value, traceback)
This is where the main issues arise. During the operation of the for loops, the print statements and return API call statements are dropped or cut off.
For example,
if self.status_code == 201:
print ("The following object was created successfully: {}".format(self.post_data["name"]))
The above statement only prints one line below the if statement...it won't allow me to do more than one line...and the one line print statement is usually cut off. As well as the json.dumps statements won't print at all...
Any help would be greatly appreciated.
Thanks,