QThread 更新桌面思维导图

当点击开始按钮后,程序会为表格每一行数据创建一个线程对象,在把数据实时显示到界面并且保证不卡顿。这就需要多线程模式。在界面和线程之间建立以个单独处理的类,然后做为单例模式给界面来调用。文字不好表示,直接上图

 



import os
import json
import sys
from PyQt5.Qt import *
from utlis.threads import NewTaskThread
from utlis.scheduler import SCHEDULER
from utlis.dialog import AlertDialog,ProxyDialog,LogDialog



BASE_FIR=os.path.dirname(os.path.realpath(sys.argv[0]))#显示到本项目文件根目录
## C:\Users\Administrator\Desktop\amazon
#相对文件路径要从一个文件开始查找,运行。不能其他文件也用,不然会出问题
STATUS_MAPPING={
    0:"初始化中",
    1:"待执行",
    2:"正在执行",
    3:"完成并提醒",
    10:"异常并停止",
    11:"初始化失败",
}


class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(1000,600)
        self.setWindowIcon(QIcon('{}//sources//miss.png'.format(BASE_FIR)))
        self.setup_ui()
        self.init_table()#初始化表格
    def setup_ui(self):

        btn_start=QPushButton("开始")
        btn_stop=QPushButton("停止")
        btn_add=QPushButton("添加")
        btn_reset=QPushButton("重新初始化")
        btn_recheck=QPushButton("重新检测")
        btn_delete=QPushButton("删除")
        btn_send_stm=QPushButton("STM")
        btn_ip=QPushButton("代理ip")
        self.le_edit=QLineEdit()
        self.le_edit.setText("EF806=1988")
        self.le_edit.setPlaceholderText("请输入商品信息,例如:FR001=987")
        self.table_widget=QTableWidget(0,8)
        self.label=QLabel("未检测")
        QMetaObject.connectSlotsByName(self)

        #绑定信号与槽函数
        btn_start.clicked.connect(self.event_btn_start_click)
        btn_stop.clicked.connect(self.event_btn_stop_click)
        btn_add.clicked.connect(self.event_btn_add_click)
        btn_reset.clicked.connect(self.event_btn_reset_click)
        btn_recheck.clicked.connect(self.event_btn_recheck_click)
        btn_delete.clicked.connect(self.event_btn_delete_click)
        btn_send_stm.clicked.connect(self.event_btn_send_stm_clilck)
        btn_ip.clicked.connect(self.event_btn_ip_click)

        #总体布局
        layout=QVBoxLayout()
        # 子布局
        header_layout=QHBoxLayout()
        header_layout.addWidget(btn_start)
        header_layout.addWidget(btn_stop)
        header_layout.addStretch()

        form_layout=QHBoxLayout()
        form_layout.addWidget(self.le_edit)
        form_layout.addWidget(btn_add)

        table_layout=QHBoxLayout()
        table_layout.addWidget(self.table_widget)

        foot_layout=QHBoxLayout()
        foot_layout.addWidget(self.label)
        foot_layout.addStretch()
        for m in [btn_reset,btn_recheck,btn_delete,btn_send_stm,btn_ip]:
            foot_layout.addWidget(m)

        #子布局添加到总布局
        for i in [header_layout,form_layout,table_layout,foot_layout]:
            layout.addLayout(i)
        self.setLayout(layout)
    def init_table(self):
        self.show_table_header_info()
        file_path=os.path.join(BASE_FIR,'db','db.json')
        if not os.path.exists(file_path):
            QMessageBox.warning(self,"错误","db.json文件不存在")
            return
        with open(file_path,mode='r',encoding='utf-8')as f:
            data=f.read()
        data_list=json.loads(data)
        self.show_table_info(data_list)

    def show_table_header_info(self):
        table_header = [
            {"filed": "asin", 'text': 'TEXT', "width": 100},
            {"filed": "title", "text": "标题", "width": 100},
            {"filed": "url", "text": "URL", "width": 200},
            {"filed": "price", "text": "底价", "width": 100},
            {"filed": "sucess", "text": "成功次数", "width": 100},
            {"filed": "error", "text": "错误次数", "width": 100},
            {"filed": "states", "text": "状态", "width": 100},
            {"filed": "frequeny", "text": "频率次数(次/秒)", "width": 100}
        ]
        for i, info in enumerate(table_header):
            item = QTableWidgetItem()
            item.setText(info.get("text"))#这里是item.setText 后面是item = QTableWidgetItem("abc")
            self.table_widget.setHorizontalHeaderItem(i, item)
            self.table_widget.setColumnWidth(i, info.get("width"))
        ###########################################################################################
    def show_table_info(self, obj_list):
        "显示表格信息"
        for data_obj in obj_list:
            current_rows = self.table_widget.rowCount()
            self.table_widget.insertRow(current_rows)
            for i, val in enumerate(data_obj):
                val = STATUS_MAPPING[val] if i == 6 else val
                item = QTableWidgetItem(str(val))
                if i in [0, 4, 5, 6]:
                    item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)  # 单元格不可被编辑状态
                self.table_widget.setItem(current_rows, i, item)
        self.table_widget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.table_widget.customContextMenuRequested.connect(self.__right_menu)
    def event_btn_start_click(self):
        #TODO
        SCHEDULER.start(
            self,
            self.task_start_callback,
            self.task_count_callback,
            BASE_FIR
        )
    def event_btn_stop_click(self):
        print('stop')
        pass
    def event_btn_add_click(self):
        text=self.le_edit.text().strip()
        if not text:
            QMessageBox.warning(self,"提示","商品信息不能为空")
            return
        asin,price=text.split("=")
        price=float(price)
        new_list=[asin,"","",price,0,0,2,0]
        current_row=self.table_widget.rowCount()
        self.table_widget.insertRow(current_row)
        for i,elv in enumerate(new_list):
            elv=STATUS_MAPPING[elv] if i==6 else elv
            item=QTableWidgetItem(str(elv))
            if i in [0, 4, 5, 6]:
                item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)  # 单元格不可被编辑状态
            self.table_widget.setItem(current_row,i,item)

            thread=NewTaskThread(current_row,self)
            thread.sucess_singal.connect(self.thread_sucess_singal_callback)
            thread.start()
    def event_btn_reset_click(self):
        row_list=self.table_widget.selectionModel().selectedRows()
        if not row_list:
            QMessageBox.warning(self,"错误","未选中行")
            return
        for row_table in row_list:
            index=row_table.row()
            self.table_widget.setItem(index,6,QTableWidgetItem(STATUS_MAPPING[0]))
            thread=NewTaskThread(index,self)
            thread.sucess_singal.connect(self.thread_sucess_singal_callback)
            thread.start()
    def event_btn_recheck_click(self):
        row_list=self.table_widget.selectionModel().selectedRows()
        if not row_list:
            QMessageBox.warning(self,"提示","没有选中行")
            return
        for row_obj in row_list:
            index=row_obj.row()
            self.table_widget.setItem(index,6, QTableWidgetItem(STATUS_MAPPING[1]))
    def event_btn_delete_click(self):
        row_list=self.table_widget.selectionModel().selectedRows()
        if not row_list:
            QMessageBox.warning(self,"提醒","没有选中行")
            return
        row_list.reverse()#反转下
        for row_obj in row_list:
            index=row_obj.row()
            self.table_widget.removeRow(index)
    def event_btn_send_stm_clilck(self):
        dialog=AlertDialog(self)
        dialog.exec_()
    def event_btn_ip_click(self):
        print("ip")
        dialog=ProxyDialog(self)
        dialog.exec_()
    def __right_menu(self,point):

        menu=QMenu(self)
        item_copy=menu.addAction(QIcon(os.path.join('sources','miss.png')),"复制")
        item_log=menu.addAction(QIcon(os.path.join('sources','stock.png')),"查看日志")
        item_log_clear=menu.addAction(QIcon(os.path.join('sources','roll.png')),"清除日志")



        desk_point=self.mapToGlobal(QPoint(point.x(),point.y()+100))
        action=menu.exec_(desk_point)
        # print(action,item_copy)
        #<PyQt5.QtWidgets.QAction object at 0x00000000037EA948> <PyQt5.QtWidgets.QAction object at 0x00000000037EA948>


        selected_item_list=self.table_widget.selectedItems()#获取选中的QTiableWidgetItem对象,是个列表

        if len(selected_item_list)==0:
            return
        if action==item_copy:
            clipboard=QApplication.clipboard()
            clipboard.setText(selected_item_list[0].text())
        if action==item_log:
            #获取型号
            row_index=selected_item_list[0].row()
            asin=self.table_widget.item(row_index,0).text().strip()
            log_dialog = LogDialog(asin,self)
            log_dialog.exec_()
        if action==item_log_clear:
            row_index=selected_item_list[0].row()
            asin=self.table_widget.item(row_index,0).text().strip()

            file_path=os.path.join("log","{}.log".format(asin))
            if os.path.exists(file_path):
                os.remove(file_path)
    def thread_sucess_singal_callback(self,index,asin,text,url):
        print(index,asin,text,url)
        asin=QTableWidgetItem(asin)
        text=QTableWidgetItem(text)
        url=QTableWidgetItem(url)
        self.table_widget.setItem(index,0,asin)
        self.table_widget.setItem(index,1,text)
        self.table_widget.setItem(index,2,url)
    def task_start_callback(self,row_index):
        '''对表格数据更新'''
        item=QTableWidgetItem(STATUS_MAPPING[2])
        item.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
        self.table_widget.setItem(row_index,6,item)
    def task_count_callback(self,row_index):
        #原有个数加1
        old_count=self.table_widget.item(row_index, 4).text().strip()
        new_count=int(old_count)+1
        # 重新表格赋值
        cell=QTableWidgetItem(str(new_count))
        cell.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
        self.table_widget.setItem(row_index,4,cell)

if __name__=="__main__":

    try:

        app=QApplication(sys.argv)
        win=Window()
        win.show()
        sys.exit(app.exec_())
    except Exception as e:
        print("e",e)
        os.system("pause")

 

from PyQt5.QtCore import QThread,pyqtSignal

class NewTaskThread(QThread):
    sucess_singal=pyqtSignal(int,str,str,str)
    error_singal=pyqtSignal(int)
    def __init__(self,row_index,parent=None,*args,**kwargs):
        super().__init__(parent,*args,**kwargs)
        self.row_index=row_index
    def run(self):
        import time
        while True:
            time.sleep(1)
            self.sucess_singal.emit(self.row_index,"FE","888","http://162.com")


class TaskThread(QThread):
    start_singal=pyqtSignal(int)
    count_singal=pyqtSignal(int)
    def __init__(self,row_index,asin,log_file_path,parent=None,*args,**kwargs):
        super().__init__(parent,*args,**kwargs)
        self.row_index=row_index
        self.asin=asin
        self.log_file_path=log_file_path

    def run(self):

        self.start_singal.emit(self.row_index)
        import time
        import random
        while True:
            time.sleep(random.randint(1,3))
            self.count_singal.emit(self.row_index)

            with open(self.log_file_path,mode='a',encoding='utf-8')as f:
                f.write("日志\n")

from utlis.threads import TaskThread
import os

class Scheduler(object):
    def __init__(self):
        self.thread_list=[]
        self.window=None
        self.flag=False
    def start(self,window,fn_start,fn_count,base_dir):
        self.window=window
        self.flag=False
        #获取表格索引,为每一行创建一个线程
        for row_index in range(self.window.table_widget.rowCount()):
            asin=window.table_widget.item(row_index,0).text().strip()
            states=window.table_widget.item(row_index,6).text().strip()
            #日志文件
            log_folder=os.path.join(base_dir,'log')
            if not os.path.exists(log_folder):
                os.makedirs(log_folder)
            log_file_path=os.path.join(log_folder,"{}.log".format(asin))

            #没个线程 执行&状态实时的显示在表格中 信号+回调
            t=TaskThread(row_index,asin,log_file_path,window)
            t.start_singal.connect(fn_start)
            t.count_singal.connect(fn_count)
            t.start()









SCHEDULER=Scheduler()

from PyQt5.Qt import *
import os
import json


class AlertDialog(QDialog):
    def __init__(self,parent=None,*args,**kwargs):
        super().__init__(parent,*args,**kwargs)
        self.resize(300,200)
        self.filed_dict={}
        icon_path=os.path.join("sources",'stock.png')
        self.setWindowIcon(QIcon(icon_path))
        self.setup_ui()
    def setup_ui(self):
        form_data_list=[
            {"title":"STM服务器","filed":"smtp"},
            {"title":"发件箱","filed":"from"},
            {"title":"密码","filed":"pwd"},
            {"title":"收件人(多人用逗号隔开)","filed":"to"},
        ]

        layout=QVBoxLayout()
        self.setLayout(layout)
        old_dic=self.read_alert_info()#读取的信息是个字典
        for X in form_data_list:
            label=QLabel(X["title"])
            layout.addWidget(label)
            txt=QLineEdit()
            filed=X['filed']
            txt.setText(old_dic[filed])
            self.filed_dict[X['filed']]=txt #把QLineWidget加入字典
            layout.addWidget(txt)
        btn=QPushButton("保存")
        btn.clicked.connect(self.event_btn_click)
        btn.setFixedSize(50,20)
        layout.addWidget(btn,0,Qt.AlignLeft)

    def read_alert_info(self):
        # 读取文件显示到窗口
        old_data_dic = {}
        file_path = os.path.join("db", "alert.txt")
        if os.path.exists(file_path):
            with open(file_path, 'r', encoding='utf-8')as f:
                old_data_dic = json.loads(f.read())
        return old_data_dic

    def event_btn_click(self):
        data_dict={}
        for key ,line_edit_obj in self.filed_dict.items():
            value=line_edit_obj.text().strip()
            if not value:
                QMessageBox.warning(self,"提示","邮件不能为空")
                return
            data_dict[key]=value
        file_object=open(os.path.join("db",'alert.txt'),mode='w',encoding='utf-8')
        json.dump(data_dict,file_object)
        file_object.close()


class ProxyDialog(QDialog):
    def __init__(self,parent=None,*args,**kwargs):
        super().__init__(parent,*args,**kwargs)
        self.setWindowTitle("IP代理")
        self.resize(400,300)
        self.setWindowIcon(QIcon(os.path.join('sources',"roll.png")))
        self.__setup_ui()
    def __setup_ui(self):
        layout=QVBoxLayout()
        self.setLayout(layout)
        self.tex_eidt=QTextEdit()
        btn_reset=QPushButton("重置")
        btn_reset.setFixedSize(50,30)
        #连接信号
        btn_reset.clicked.connect(self.__btn_reset_write_info)
        #初始化信息
        self.__show_init_info()
        #布局
        layout.addWidget(self.tex_eidt)
        layout.addWidget(btn_reset,0,Qt.AlignRight)

    def __show_init_info(self):
        # 初始化
        file_path = os.path.join('db', 'proxy.txt')
        data_info = ""
        if os.path.exists(file_path):
            with open(file_path, 'r', encoding='utf-8')as f:
                data_info = f.read()
        self.tex_eidt.setText(data_info)

    def __btn_reset_write_info(self):
        file_path=os.path.join("db","proxy.txt")
        text=self.tex_eidt.toPlainText().strip()
        if not text:
            return
        with open(file_path,mode='w',encoding='utf-8')as f:
            f.write(text)



class LogDialog(QDialog):
    def __init__(self,asin,parent=None,*args,**kwargs):
        super().__init__(parent,*args,**kwargs)
        self.resize(400,370)
        self.setWindowIcon(QIcon("sources.miss.png"))
        self.asin=asin
        self.setup_ui()
    def setup_ui(self):
        layout=QVBoxLayout()
        self.setLayout(layout)
        self.tex_le=QTextEdit()
        btn=QPushButton("点击")
        btn.setFixedSize(50,30)
        layout.addWidget(self.tex_le)
        layout.addWidget(btn,0,Qt.AlignRight)

        #读取文件出来
        file_path=os.path.join("log","{}.log".format(self.asin))

        if not os.path.exists(file_path):
            return
        with open(file_path,'r',encoding='utf-8')as f:
            data=f.read()
        self.tex_le.setText(data)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值