using pyqtSignal in BaseHTTPRequestsHandler
-
I am using pyqt5 in a python3.9 environment and there is one simple but difficult problem for me. The main purpose is to run an http server using threads in pyqt5 and send a specific API request to that server. Using the BaseHttpRequestHandler class, we set it to throw a callback value only to APIs coming to a specific address. What I'm curious about here is, is it possible to use the value generated from BaseHttpRequestHandler inside MainWindow using pyqtSignal?
- What I want to do is to use this value in MainWindow when the value of screen_data is created in do_POST of the ResponseHandlerAdapter(BaseHTTPRequestHAndler) class. I would like to change the color of each row in the table using the data. *
here is my study code..
import sys import db import json import requests from datetime import datetime from PyQt5.QtGui import QColor from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QWidget, QTableWidgetItem, QHeaderView, QTableWidget from PyQt5.QtCore import Qt, pyqtSlot, QThread, QTimer, pyqtSignal, QObject from login_ui import Ui_Form from sidebar_ui import Ui_MainWindow from http.server import BaseHTTPRequestHandler, HTTPServer _URL = "*" _UpdateScreen_URL = "*" headers = { 'Content-Type': 'application/json', } port = * user = None screen_data = [] callback_data = [] class ApiServer(QThread): def __init__(self, parent=None): super().__init__(parent) self.parent = parent def run(self): http_server = HTTPServer(('', int(port)), ResponseHandlerAdapter) http_server.serve_forever() class ResponseHandlerAdapter(BaseHTTPRequestHandler): def do_POST(self): global screen_data, callback_data content = self.rfile.read(int(self.headers['content-length'])) resource = content.decode('utf8') self.send_response(200) self.end_headers() if self.path == '/led': pass elif self.path == '/screen': print("↓↓↓↓↓↓↓↓↓ /SCREEN Response Data ↓↓↓↓↓↓↓↓↓") screen_data.append(json.loads(resource)) print(screen_data) elif self.path == '/input': print("↓↓↓↓↓↓↓↓↓ /INPUT CallBack Data ↓↓↓↓↓↓↓↓↓") callback_data.append(json.loads(resource)) print(callback_data) class LoginScreen(QWidget, Ui_Form): def __init__(self): super(LoginScreen, self).__init__() self.setupLoginUi(self) self.login_btn.clicked.connect(self.login_function) def login_function(self): global user user = self.input_id.text() password = self.input_password.text() if len(user) == 0 or len(password) == 0: print("아이디와 패스워드를 입력해주세요.") else: check_point = db.login(user, password) if check_point == password: print(f"[로그인 성공] [{user}]") self.hide() self.main = MainWindow() self.main.show() else: print("아이디와 패스워드를 확인해주세요.") class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.server = ApiServer(self) self.server.start() self.ui.icon_only_widget.hide() self.ui.stackedWidget.setCurrentIndex(0) self.ui.home_btn_2.setChecked(True) # 대시보드 메뉴 관련 self.ui.waybill_input.returnPressed.connect(self.select_data_from_waybill) self.ui.waybill_input.returnPressed.connect(self.send_data_to_tags) item = QTableWidgetItem() self.defaultBrush = (item.foreground(), item.background()) self.ui.db_table.itemChanged.connect(self.item_changed) self.set_table() self.ui.complete_btn_1.clicked.connect(self.set_clear) def handle_response(self, data): print(data) def set_clear(self): global user waybill_no = self.ui.waybill_input.text() self.ui.db_table.clear() self.ui.db_table.setRowCount(0) self.set_table() self.ui.waybill_input.clear() self.ui.waybill_input.setFocus() now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') print(now) # db.update_picking_finish_time(user, now, waybill_no) print(user) ## 테이블에서 체크박스 선택 시 row색상 변경 def item_changed(self, item): if bool(item.checkState()): fg = QColor('#004000') bg = QColor('#C6EFCE') else: fg, bg = self.defaultBrush row = item.row() self.ui.db_table.itemChanged.disconnect() for col in range(self.ui.db_table.columnCount()): item = self.ui.db_table.item(row, col) if item: item.setForeground(fg) item.setBackground(bg) self.ui.db_table.itemChanged.connect(self.item_changed) ## Function for searching def on_search_btn_clicked(self): self.ui.stackedWidget.setCurrentIndex(5) search_text = self.ui.search_input.text().strip() if search_text: self.ui.label_9.setText(search_text) ## Function for changing page to user page def on_user_btn_clicked(self): self.ui.stackedWidget.setCurrentIndex(6) ## Change QPushButton Checkable status when stackWidget index changed def on_stackedWidget_currentChanged(self, index): btn_list = self.ui.icon_only_widget.findChildren(QPushButton) + self.ui.full_menu_widget.findChildren(QPushButton) for btn in btn_list: if index in [5, 6]: btn.setAutoExclusive(False) btn.setChecked(False) else: btn.setAutoExclusive(True) ## functions for changing menu page def on_home_btn_1_toggled(self): self.ui.stackedWidget.setCurrentIndex(0) def on_home_btn_2_toggled(self): self.ui.stackedWidget.setCurrentIndex(0) def on_dashboard_btn_1_toggled(self): self.ui.stackedWidget.setCurrentIndex(1) def on_dashboard_btn_2_toggled(self): self.ui.stackedWidget.setCurrentIndex(1) def on_work_btn_1_toggled(self): self.ui.stackedWidget.setCurrentIndex(2) def on_work_btn_2_toggled(self): self.ui.stackedWidget.setCurrentIndex(2) def on_product_btn_1_toggled(self): self.ui.stackedWidget.setCurrentIndex(3) def on_product_btn_2_toggled(self): self.ui.stackedWidget.setCurrentIndex(3) def on_userlist_btn_1_toggled(self): self.ui.stackedWidget.setCurrentIndex(4) def on_userlist_btn_2_toggled(self): self.ui.stackedWidget.setCurrentIndex(4) ## QtableWeidget 설정 def set_table(self): self.ui.db_table.setColumnCount(9) self.ui.db_table.setHorizontalHeaderLabels( ['*', '*', '*', '*', '*', '*', '*', '*', '*'] ) ## 운송장 스캐닝 후 엔터키 구현 # 682679951842 def select_data_from_waybill(self): waybill_no = self.ui.waybill_input.text() print(waybill_no) rows = db.select_from_order_with_waybill(waybill_no) count = len(rows) self.ui.db_table.setRowCount(count) for x in range(count): for y in range(len(rows[x])): item = QTableWidgetItem() item.setText(str(rows[x][y])) if y == 0: item.setCheckState(False) self.ui.db_table.setItem(x, y, item) width = [] for column in range(self.ui.db_table.horizontalHeader().count()): self.ui.db_table.horizontalHeader().setSectionResizeMode(column, QHeaderView.ResizeToContents) width.append(self.ui.db_table.horizontalHeader().sectionSize(column)) def send_data_to_tags(self): # global screen_data waybill_no = self.ui.waybill_input.text() rows = db.select_from_mac(waybill_no) update_data = [] for row in rows: mac = row[5] ea = row[3] update_pre_data = {"mac": mac, "mappingtype": 574, "styleid": 48, "ledrgb": "ff00", "ledstate": "0", "outtime": "0", "EA": ea, "text_company": "copyright(C) by sd-da, All right Reserved."} update_data.append(update_pre_data) response = requests.post(_UpdateScreen_URL, headers=headers, data= json.dumps(update_data)) if response.status_code == 200: print('API success') print('응답 내용:', response.json()) else: print('API failed') print('상태 코드:', response.status_code) print('응답 내용:', response.text) if __name__ == "__main__": login_app = QApplication(sys.argv) login = LoginScreen() login.show() login_app.exec_()
- What I want to do is to use this value in MainWindow when the value of screen_data is created in do_POST of the ResponseHandlerAdapter(BaseHTTPRequestHAndler) class. I would like to change the color of each row in the table using the data. *
-
@check_f The usual way in Qt to exchange data inside an application is to use signals/slots. So, you thread can emit a signal and pass the data you need in MainWindow as signal parameter. In MainWindow you connect a slot to this signal and do whatever you need with received data.
-
@jsulm As you advised, I created a signal in the thread ApiServer class and output the slot in the MainWindow class, but it did not work. Maybe I'm confusing something?
my code:class ApiServer(QThread): server_signal = pyqtSignal(int) def __init__(self, parent=None): super().__init__(parent) self.parent = parent self.server_signal.emit(1) def run(self): http_server = HTTPServer(('', int(port)), ResponseHandlerAdapter) http_server.serve_forever() class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.server = ApiServer(self) self.server.start() self.ui.icon_only_widget.hide() self.ui.stackedWidget.setCurrentIndex(0) self.ui.home_btn_2.setChecked(True) # 대시보드 메뉴 관련 self.ui.waybill_input.returnPressed.connect(self.select_data_from_waybill) self.ui.waybill_input.returnPressed.connect(self.send_data_to_tags) self.server.server_signal.connect(self.handle_response) item = QTableWidgetItem() self.defaultBrush = (item.foreground(), item.background()) self.ui.db_table.itemChanged.connect(self.item_changed) self.set_table() self.ui.complete_btn_1.clicked.connect(self.set_clear) def handle_response(self, data): print("signal ok")
-
@jsulm Sorry, I think I still have little understanding of "Class".
What I want is for a signal to be fired when the do_POST function is executed in the ResponseHandlerAdapter() class. So, is it correct to create a signal in that class? If there is another location, I would appreciate it if you could let me know. -
@check_f I did not say that you placed your signal in the wrong class. I said that EMITTING the signal in the CONSTRUCTOR is wrong, because while the constructor is being executed the connection between signal and slot was not yet established.
"What I want is for a signal to be fired when the do_POST function is executed" - then emit the signal there instead of constructor...
-
@check_f Did you read https://wiki.qt.io/Qt_for_Python_Signals_and_Slots already? If not then please do.