import pathlibimport timeimport tracebackfrom typing import Optional Callable Generator Unionimport pandas as pdfrom PySide6QtCore import QAbstractTableModel Qt QRunnable Signal QThreadPool QThread QT
该代码实现了一个逻辑回归真假新闻分类的GUI界面,下面对代码进行详细解释说明:
- 导入必要的模块和包:
import pathlib # 路径操作模块
import time # 时间模块
import traceback # 异常跟踪模块
from typing import Optional, Callable, Generator, Union # 类型注解
import pandas as pd # 数据处理模块
from PySide6.QtCore import QAbstractTableModel, Qt, QRunnable, Signal, QThreadPool, QThread, QTimer # Qt相关模块
from PySide6.QtGui import QPixmap, QIcon # Qt相关模块
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QLabel, QFileDialog, QTableView, QStackedWidget, \
QVBoxLayout, QScrollArea, QWidget, QHeaderView, QMessageBox, QDialog # Qt相关模块
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas # matplotlib相关模块
from matplotlib.figure import Figure # matplotlib相关模块
from ui_windows import Ui_MainWindow # GUI界面设计模块
import main # 主要的业务逻辑实现模块
- 自定义一个TableModel类,继承自QAbstractTableModel,用于将DataFrame数据展示在GUI界面的TableView中:
class TableModel(QAbstractTableModel):
def __init__(self, data: pd.DataFrame):
super().__init__()
self._data = data
def rowCount(self, parent=None):
return self._data.shape[0]
def columnCount(self, parent=None):
return self._data.shape[1]
def data(self, index, role=None):
if role == Qt.DisplayRole:
return str(self._data.iloc[index.row(), index.column()])
return None
def headerData(self, section, orientation, role=None):
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
else:
return str(self._data.index[section])
return None
def sort(self, column, order=None):
self.layoutAboutToBeChanged.emit()
self._data.sort_values(self._data.columns[column], ascending=order == Qt.AscendingOrder, inplace=True)
self.layoutChanged.emit()
@property
def df(self):
return self._data
- 自定义一个TableWidget类,继承自QTableView,用于展示数据:
class TableWidget(QTableView):
def __init__(self, data: Optional[pd.DataFrame] = None):
super().__init__()
if data is not None:
self.setModel(TableModel(data))
- 自定义一个Task类,用于封装需要执行的任务:
class Task:
def __init__(
self,
func: Callable[..., Generator[Union[str, pd.DataFrame], None, None]],
*args,
**kwargs
):
self._func = func
self._args = args
self._kwargs = kwargs
def __call__(self):
try:
yield from self._func(*self._args, **self._kwargs)
except Exception as e:
traceback.print_exc()
yield str(e)
- 自定义一个Worker类,继承自QThread,用于执行任务:
class Worker(QThread):
resulted = Signal(pd.DataFrame)
step = Signal(str)
image = Signal(pathlib.Path)
wrapper = Signal(list)
stop = Signal()
def __init__(self, parent=None):
super().__init__(parent)
self._task = None
def bind_task(self, func, *args, **kwargs):
self._task = Task(func, *args, **kwargs)
def run(self):
if self._task is None:
raise ValueError("Task not bind")
print("开始执行")
for value in self._task():
if isinstance(value, pd.DataFrame):
self.resulted.emit(value)
elif isinstance(value, str):
print("收到", value)
self.step.emit(value)
time.sleep(0.1)
elif isinstance(value, pathlib.Path):
print("图片", value)
self.image.emit(value)
elif isinstance(value, list):
self.wrapper.emit(value)
self.stop.emit()
- 自定义一个MatplotlibWidget类,继承自QWidget,用于展示matplotlib绘制的可视化图:
class MatplotlibWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.figure = Figure()
self.canvas = FigureCanvas(self.figure)
layout = QVBoxLayout(self)
layout.addWidget(self.canvas)
self.setLayout(layout)
def draw(self, data: pd.DataFrame):
pass
- 自定义一个DrawWorker类,继承自QRunnable,用于执行matplotlib绘制图形的任务:
class DrawWorker(QRunnable):
def __init__(self, func, *args, **kwargs):
super().__init__()
self._func = func
self._args = args
self._kwargs = kwargs
def run(self):
self._func(*self._args, **self._kwargs)
- 自定义一个ImageWidget类,继承自QWidget,用于展示图片:
class ImageWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.label = QLabel()
self.label.setAlignment(Qt.AlignCenter)
layout = QVBoxLayout(self)
layout.addWidget(self.label)
self.setLayout(layout)
self.setObjectName("#image")
self.setStyleSheet(
"""
#image {
background-color: white;
border: 1px solid black;
border-radius: 5px;
}
"""
)
def draw(self, path: pathlib.Path):
max_width = 1200
pix = QPixmap(str(path))
if pix.width() > max_width:
pix = pix.scaledToWidth(max_width, Qt.SmoothTransformation)
self.label.setPixmap(
pix
)
self.setFixedSize(
pix.width() + 2, pix.height() + 2
)
- 自定义一个TipsDialog类,继承自QDialog,用于展示提示信息:
class TipsDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Tips")
self.resize(300, 200)
self.setLayout(QVBoxLayout())
self.label = QLabel("正在处理数据,请稍后……")
self.layout().addWidget(self.label)
- 自定义一个MainWindow类,继承自QMainWindow和Ui_MainWindow,用于实现GUI界面:
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super().__init__()
self.setupUi(self)
self.setWindowTitle("逻辑回归真假新闻分类") # 设置窗口标题
self.setWindowIcon(
QIcon(str(pathlib.Path(__file__).parents[1] / "ui/icon.png"))
)
self.stack = QStackedWidget()
self.widget.setLayout(QVBoxLayout())
self.widget.layout().addWidget(self.stack)
self.thread_pool = QThreadPool(self)
self.table = TableWidget()
# 全部字段拉伸
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.stack.addWidget(self.table)
self.area = QScrollArea()
self.area.setWidgetResizable(True)
self.container = QWidget()
self.area.setWidget(self.container)
self.container.setLayout(QVBoxLayout())
self.stack.addWidget(self.area)
self.checkBox.setChecked(True)
self.checkBox_2.setChecked(True)
self.checkBox_3.setChecked(True)
self.checkBox.setEnabled(False)
self.checkBox_2.setEnabled(False)
self.checkBox_3.setEnabled(False)
self.worker = Worker()
self.worker.resulted.connect(self.update_table)
self.worker.step.connect(self.update_step)
self.worker.stop.connect(self.end)
self.worker.image.connect(self.update_image)
self.worker.wrapper.connect(self.update_wrapper)
self.widget_2.setFixedWidth(300)
self.btn_load.clicked.connec
原文地址: http://www.cveoy.top/t/topic/g0ME 著作权归作者所有。请勿转载和采集!