Python Forum
PyQt5 - Threading
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
PyQt5 - Threading
#1
I need help in implementing a worker thread in the code of an app I wrote. The app takes and search user inputs in a SQL db - everything works, but the GUI tends to freeze (Not responding) during long queries, hence the need to implement threading. I have looked at several examples but can't figure out how to apply them to my code. Any help would be appreciated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#!/usr/bin/env python
 
import pyodbc
import sys
 
from PyQt5.QtWidgets import QMainWindow, QApplication, QTableWidgetItem, QMessageBox
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtCore import QRegExp
from PyQt5 import uic
 
from ui_pysearch import Ui_pySearch
 
class pySearchMain(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.ui = Ui_pySearch()
        self.ui.setupUi(self)
        self.ui.tableWidget_ResultsIncident.setHorizontalHeaderLabels(["A", "B", "C"])
        self.ui.tableWidget_ResultsCompany.setHorizontalHeaderLabels(["A", "B", "C"])
 
        self._set_validators()
        self.ui.Button_Search.clicked.connect(self._save_pushed)
 
    def _set_validators(self):
        reg_ex = QRegExp(r"[A-Za-z0-9]{0,6}")
        val_company_custid = QRegExpValidator(reg_ex, self.ui.Input_lineEdit_CompanyPIN)
        self.ui.Input_lineEdit_CompanyPIN.setValidator(val_company_custid)
 
        reg_ex = QRegExp(r"[A-Za-z ]{0,25}")
        val_company_name = QRegExpValidator(reg_ex, self.ui.Input_lineEdit_CompanyName)
        self.ui.Input_lineEdit_CompanyName.setValidator(val_company_name)
 
        reg_ex = QRegExp(r"[A-Za-z ]{0,25}")
        val_incident_service = QRegExpValidator(reg_ex, self.ui.Input_lineEdit_IncidentService)
        self.ui.Input_lineEdit_IncidentService.setValidator(val_incident_service)
 
    def _save_pushed(self):
 
        self.ui.tableWidget_ResultsIncident.clearContents()
        self.ui.tableWidget_ResultsCompany.clearContents()
 
        Company_CustID = self.ui.Input_lineEdit_CompanyPIN.text()
        Company_Name = self.ui.Input_lineEdit_CompanyName.text()
        Incident_Service = self.ui.Input_lineEdit_IncidentService.text()
 
        Set_Company_RecID = set()
        Set_Company_CustID = set()
        Set_Company_CustID_Substring = set()
        Set_Company_Name = set()
        Set_Company_Name_Substring = set()
        Set_IncidentNumber_CompanyName = set()
        Set_IncidentNumber_Service = set()
 
        Match_Input_lineEdit = 1
        Match_RecID_CompanyCustID = -1
        Match_RecID_CompanyName = -1
 
        if len(str(Company_CustID)) == 6:
            with pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=ServerIP;DATABASE=Sqldbname;UID=db_username;PWD=db_password') as DB_Connect:
                SearchCursor = DB_Connect.cursor()
                SearchCursor.execute("select RecId, CustID, Name from Company")
                for row in SearchCursor.fetchall():
                    if str(row[1]).lower() == str(Company_CustID).lower():
                        Match_RecID_CompanyCustID = row[0]
                        Set_Company_RecID.add(str(row[1]))
        if len(str(Company_CustID)) > 1:
            with pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=ServerIP;DATABASE=Sqldbname;UID=db_username;PWD=db_password') as DB_Connect:
                SearchCursor = DB_Connect.cursor()
                SearchCursor.execute("select RecId, CustID, Name from Company")
                for row in SearchCursor.fetchall():
                    if str(row[1]).lower().rfind(str(Company_CustID).lower()) != -1:
                        Set_Company_CustID_Substring.add(row[0])
                        Set_Company_RecID.add(str(row[1]))
        if len(str(Company_Name)) > 2:
            with pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=ServerIP;DATABASE=Sqldbname;UID=db_username;PWD=db_password') as DB_Connect:
                SearchCursor = DB_Connect.cursor()
                SearchCursor.execute("select RecId, CustID, Name from Company")
                for row in SearchCursor.fetchall():
                    if str(row[2]).lower() == str(Company_Name).lower():
                        Match_RecID_CompanyName = row[0]
                        Set_Company_RecID.add(str(row[1]))
                    if str(row[2]).lower().rfind(str(Company_Name).lower()) != -1:
                        Set_Company_Name_Substring.add(row[0])
                        Set_Company_RecID.add(str(row[1]))
        if Match_RecID_CompanyCustID != -1:
            with pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=ServerIP;DATABASE=Sqldbname;UID=db_username;PWD=db_password') as DB_Connect:
                SearchCursor = DB_Connect.cursor()
                SearchCursor.execute("select IncidentNumber from Incident")
                for row in SearchCursor.fetchall():
                    if row[0] == Match_RecID_CompanyCustID:
                        Set_Company_CustID.add(int(row[0]))
        if Match_RecID_CompanyName != -1:
            with pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=ServerIP;DATABASE=Sqldbname;UID=db_username;PWD=db_password') as DB_Connect:
                SearchCursor = DB_Connect.cursor()
                SearchCursor.execute("select IncidentNumber, CompanyLink_RecID from Incident")
                for row in SearchCursor.fetchall():
                    if row[1] == Match_RecID_CompanyName:
                        Set_Company_Name.add(int(row[0]))
        if len(str(Company_Name)) > 2:
            with pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=ServerIP;DATABASE=Sqldbname;UID=db_username;PWD=db_password') as DB_Connect:
                SearchCursor = DB_Connect.cursor()
                SearchCursor.execute("select IncidentNumber, CompanyName from Incident")
                for row in SearchCursor.fetchall():
                    if str(row[1]).lower() == str(Company_Name).lower():
                        Set_IncidentNumber_CompanyName.add(int(row[0]))
                    if str(row[1]).lower().rfind(str(Company_Name).lower()) != -1:
                        Set_IncidentNumber_CompanyName.add(int(row[0]))
        if len(Incident_Service) > 2:
            with pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=ServerIP;DATABASE=Sqldbname;UID=db_username;PWD=db_password') as DB_Connect:
                SearchCursor = DB_Connect.cursor()
                SearchCursor.execute("select IncidentNumber, Service from Incident")
                for row in SearchCursor.fetchall():
                    if (row[1].lower()).rfind(Incident_Service.lower()) != -1:
                        Set_IncidentNumber_Service.add(int(row[0]))
        if len(Set_Company_CustID_Substring) != 0:
            with pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=ServerIP;DATABASE=Sqldbname;UID=db_username;PWD=db_password') as DB_Connect:
                SearchCursor = DB_Connect.cursor()
                SearchCursor.execute("select IncidentNumber, CompanyLink_RecID from Incident")
                for row in SearchCursor.fetchall():
                    for Set_Company_CustID_Substring_Item in Set_Company_CustID_Substring:
                        if row[1] == Set_Company_CustID_Substring_Item:
                            Set_Company_CustID.add(int(row[0]))
        if len(Set_Company_Name_Substring) != 0:
            with pyodbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=ServerIP;DATABASE=Sqldbname;UID=db_username;PWD=db_password') as DB_Connect:
                SearchCursor = DB_Connect.cursor()
                SearchCursor.execute("select IncidentNumber, CompanyLink_RecID from Incident")
                for row in SearchCursor.fetchall():
                    for Set_Company_Name_Substring_Item in Set_Company_Name_Substring:
                        if row[1] == Set_Company_Name_Substring_Item:
                            Set_Company_Name.add(int(row[0]))
 
 
        if len(Set_Company_CustID) == 0 and len(Company_CustID) != 0:
            Match_Input_lineEdit = 0
        if len(Set_Company_Name) == 0 and len(Company_Name) != 0:
            Match_Input_lineEdit = 0
        if len(Set_IncidentNumber_Service) == 0 and len(Incident_Service) != 0:
            Match_Input_lineEdit = 0
        if len(Set_IncidentNumber_CompanyName) == 0 and len(Company_Name) != 0:
            Match_Input_lineEdit = 0
            Match_Input_lineEdit = 0
        if Match_Input_lineEdit == 0:
            alert = QMessageBox()
            alert.setText('No results')
            self.ui.tableWidget_ResultsIncident.clearContents()
            self.ui.tableWidget_ResultsIncident.setRowCount(0)
            alert.exec_()
            return
        List_Of_All_Sets = [Set_Company_CustID, Set_Company_Name, Set_IncidentNumber_Service, Set_IncidentNumber_CompanyName]
 
        try:
            Set_Intersection = set.intersection(*(s for s in List_Of_All_Sets if s))
            if len(Set_Intersection) == 0:
                alert = QMessageBox()
                alert.setText('No results')
                self.ui.tableWidget_ResultsIncident.clearContents()
                self.ui.tableWidget_ResultsIncident.setRowCount(0)
                alert.exec_()
        except TypeError:
            alert = QMessageBox()
            alert.setText('No results')
            self.ui.tableWidget_ResultsIncident.clearContents()
            self.ui.tableWidget_ResultsIncident.setRowCount(0)
            alert.exec_()
        else:
            if self.ui.radioButton_Search_Incident.isChecked():
                self.ui.tableWidget_ResultsIncident.setRowCount(0)
                tableWidget_ResultsIncident_row_num = self.ui.tableWidget_ResultsIncident.rowCount()
                with pyodbc.connect(
                    'DRIVER={ODBC Driver 17 for SQL Server};SERVER=ServerIP;DATABASE=Sqldbname;UID=db_username;PWD=db_password') as DB_Connect:
                    Set_Intersection_Cursor = DB_Connect.cursor()
 
                    Set_Intersection_Cursor.execute("select CompanyLink_RecID, IncidentNumber from Incident")
                    for Set_Intersection_Cursor_Incident_Item in Set_Intersection_Cursor.fetchall():
                        for Set_Intersection_Item in Set_Intersection:
                            if Set_Intersection_Cursor_Incident_Item[1] == Set_Intersection_Item:
                                Match_Intersection_RecID = Set_Intersection_Cursor_Incident_Item[0]
 
                                Set_Intersection_Cursor.execute("select RecId, CustID, Name from Company")
                                for Set_Intersection_Cursor_Company_Item in Set_Intersection_Cursor.fetchall():
                                    if Set_Intersection_Cursor_Company_Item[0] == Match_Intersection_RecID:
                                        self.ui.tableWidget_ResultsIncident.insertRow(tableWidget_ResultsIncident_row_num)
                                        self.ui.tableWidget_ResultsIncident.setItem(tableWidget_ResultsIncident_row_num, 0, QTableWidgetItem(str(Set_Intersection_Item)))
                                        self.ui.tableWidget_ResultsIncident.setItem(tableWidget_ResultsIncident_row_num, 1, QTableWidgetItem(str(Set_Intersection_Cursor_Company_Item[1])))
 
                                        Set_Intersection_Cursor.execute("select IncidentNumber, Priority, Service, TypeOfIncident, CellNum, CreatedDateTime, Status, TACPrime, Owner from Incident")
                                        for row in Set_Intersection_Cursor.fetchall():
                                            if row[0] == Set_Intersection_Item:
                                                self.ui.tableWidget_ResultsIncident.setItem(tableWidget_ResultsIncident_row_num, 2, QTableWidgetItem(str(row[1]))) #Priority
                                            if row[0] == Set_Intersection_Item:
                                                self.ui.tableWidget_ResultsIncident.setItem(tableWidget_ResultsIncident_row_num, 3, QTableWidgetItem(str(row[2]))) #Service
 
            if len(Set_Company_RecID) != 0 and self.ui.radioButton_Search_Company.isChecked():
                self.ui.tableWidget_ResultsCompany.setColumnWidth(0, 215)
                self.ui.tableWidget_ResultsCompany.setColumnWidth(1, 215)
                self.ui.tableWidget_ResultsCompany.setColumnWidth(2, 215)
 
                self.ui.tableWidget_ResultsCompany.setRowCount(0)
                tableWidget_ResultsCompany_row_num = self.ui.tableWidget_ResultsCompany.rowCount()
                with pyodbc.connect(
                    'DRIVER={ODBC Driver 17 for SQL Server};SERVER=ServerIP;DATABASE=Sqldbname;UID=db_username;PWD=db_password') as DB_Connect:
                    Company_RecID_Cursor = DB_Connect.cursor()
 
                    Company_RecID_Cursor.execute("select RecID, CustID, Name from Company")
                    for Company_RecID_Cursor_Item in Company_RecID_Cursor.fetchall():
                        for Set_Company_RecID_Item in Set_Company_RecID:
                            if Company_RecID_Cursor_Item[1] == Set_Company_RecID_Item:
                                self.ui.tableWidget_ResultsCompany.insertRow(tableWidget_ResultsCompany_row_num)
                                self.ui.tableWidget_ResultsCompany.setItem(tableWidget_ResultsCompany_row_num, 0, QTableWidgetItem(str(Set_Company_RecID_Item)))
                                self.ui.tableWidget_ResultsCompany.setItem(tableWidget_ResultsCompany_row_num, 1, QTableWidgetItem(str(Company_RecID_Cursor_Item[2])))
                                self.ui.tableWidget_ResultsCompany.setItem(tableWidget_ResultsCompany_row_num, 2, QTableWidgetItem(str(Company_RecID_Cursor_Item[0])))
 
if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = pySearchMain()
    main.show()
    sys.exit(app.exec_())
Reply
#2
What exactly do you want to run in a separate thread? Is it already in it's own function?
Reply
#3
For additional context: The current UI consists of a QMainWindow with QLineEdit boxes for inputs and a QPushButton to initiate the search. I'm looking to add a progress bar (and a cancel button) alongside the worker thread - As it is now, _set_validators grabs the user input and _save_pushed searches the db (Lines 39-130) and displays the results (Lines 166-211) in a QTabWidget object (within the QMainWindow). So I supposes these two sets of codes are the ones I need to run a separate thread(s) to prevent the GUI from hanging.
Reply
#4
To run in a separate thread you will need 1 function that will do all the work. It should grab all the values it needs, maybe disable the pushbutton, draw the progress bar and cancel button, perform all the work, erase the progress bar and cancel button, and enable the pushbutton. In the simplified example below this is the "thread_func". The "button_func" is the function that is called when the button is pressed.

Things may be as simple as:
1
2
3
4
5
6
7
8
9
10
def thread_func(a1, a2, a3):
    Disable pushbutton
    Draw progress bar and cancel button
    Call functions that do the work
    Erase the progress bar and cancel button
    Enable pushbutton
 
def button_func():
    x = threading.Thread(target=thread_func, args=(a1, 2, a3))  # Whatever args to pass
    x.start
The cancel button would set some flag that the processing code would monitor. You can kill a thread, but it is better to let the application exit gracefully. The worker code will also have to update the progress bar periodically.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Huge code problems (buttons(PyQt5),PyQt5 Threads, Windows etc) ZenWoR 0 3,525 Apr-06-2019, 11:15 PM
Last Post: ZenWoR

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020