9.SignalAndSlot

本文深入探讨了QT框架中的信号与槽机制,详细解析了信号与槽的定义、连接和使用方法。信号与槽是QT的核心特性,用于组件间的通信,文章涵盖了信号和槽的定义规范、连接方式、调用顺序、信号传导等关键知识点。

 

/*
1) 信号的定义必须在signals:保留字下,并且不需要实现
2)槽的定义必须在slots:保留字下,需要实现
3)信号和槽通过QObject::connect函数连接
4)当信号被触发时,槽函数被调用

需要注意的是:
1)信号和槽,是QT的拓展,所以实现信号和槽的类,必须是QObject的子类
2)实现信号和槽的类,必须以宏Q_OBJECT开始
3)连接信号和槽,要用到SIGNAL和SLOT宏,转换函数为字符串
4)一个信号可以和多个槽连接,槽函数调用的顺序是不确定的
5)多个信号可以同时连接一个槽
6)信号可以连接信号,形成信号传导
7)信号和槽的参数应该一样多,而且类型必须相同
8)信号和槽都可以重载
9)信号和槽都可以有默认参数
10)槽函数可以像普通函数一样被调用
11)在槽函数中,调用sender可以获得信号调用者

总结下:
一个类:QObject
三个宏:Q_OBJECT SIGNAL SLOT
三个保留字:signals, slots, emit
*/

 



#include <QCoreApplication>
#include "mysignal.h"
#include "myslot.h"

/*
1) 信号的定义必须在signals:保留字下,并且不需要实现
2)槽的定义必须在slots:保留字下,需要实现
3)信号和槽通过QObject::connect函数连接
4)当信号被触发时,槽函数被调用

需要注意的是:
1)信号和槽,是QT的拓展,所以实现信号和槽的类,必须是QObject的子类
2)实现信号和槽的类,必须以宏Q_OBJECT开始
3)连接信号和槽,要用到SIGNAL和SLOT宏,转换函数为字符串
4)一个信号可以和多个槽连接,槽函数调用的顺序是不确定的
5)多个信号可以同时连接一个槽
6)信号可以连接信号,形成信号传导
7)信号和槽的参数应该一样多,而且类型必须相同
8)信号和槽都可以重载
9)信号和槽都可以有默认参数
10)槽函数可以像普通函数一样被调用
11)在槽函数中,调用sender可以获得信号调用者

总结下:
一个类:QObject
三个宏:Q_OBJECT SIGNAL SLOT
三个保留字:signals, slots, emit
*/

int main(int argc, char** argv)
{
    QCoreApplication app(argc, argv);

    MySignal sig;
    MySlot slot;

    QObject::connect(&sig, SIGNAL(sig()), &slot, SLOT(slot()));

    emit sig.sig();

    return app.exec();
}
#ifndef MYSLOT_H
#define MYSLOT_H

#include <QObject>

class MySlot : public QObject
{
    Q_OBJECT
public:
    explicit MySlot(QObject *parent = 0);

signals:

public slots:
    void slot();

};

#endif // MYSLOT_H
#include "myslot.h"
#include <QDebug>

MySlot::MySlot(QObject *parent) :
    QObject(parent)
{
}

void MySlot::slot()
{
    qDebug() << "MySlot::slot is called";
}
#ifndef MYSIGNAL_H
#define MYSIGNAL_H

#include <QObject>

class MySignal : public QObject
{
    Q_OBJECT
public:
    explicit MySignal(QObject *parent = 0);

signals:
    void sig();

public slots:

};

#endif // MYSIGNAL_H
#include "mysignal.h"

MySignal::MySignal(QObject *parent) :
    QObject(parent)
{
}

 

C:\Users\48318\Desktop\Qt\2048\interface_44.cpp:35: error: static assertion failed: Signal and slot arguments are not compatible. In file included from C:\Qt\5.15.2\mingw81_64\include/QtGui/qtguiglobal.h:43, from C:\Qt\5.15.2\mingw81_64\include/QtWidgets/qtwidgetsglobal.h:43, from C:\Qt\5.15.2\mingw81_64\include\QtWidgets/qmainwindow.h:43, from C:\Qt\5.15.2\mingw81_64\include\QtWidgets/QMainWindow:1, from ..\2048\interface_44.h:4, from ..\2048\interface_44.cpp:1: C:\Qt\5.15.2\mingw81_64\include/QtCore/qobject.h: In instantiation of &#39;static QMetaObject::Connection QObject::connect(const typename QtPrivate::FunctionPointer<Func>::Object*, Func1, const typename QtPrivate::FunctionPointer<Func2>::Object*, Func2, Qt::ConnectionType) [with Func1 = void (QTimer::*)(QTimer::QPrivateSignal); Func2 = void (Interface_44::*)(QTimerEvent*); typename QtPrivate::FunctionPointer<Func>::Object = QTimer; typename QtPrivate::FunctionPointer<Func2>::Object = Interface_44]&#39;: ..\2048\interface_44.cpp:35:69: required from here C:\Qt\5.15.2\mingw81_64\include/QtCore/qglobal.h:121:63: error: static assertion failed: Signal and slot arguments are not compatible. # define Q_STATIC_ASSERT_X(Condition, Message) static_assert(bool(Condition), Message) ^~~~~~~~~~~~~~~ C:\Qt\5.15.2\mingw81_64\include/QtCore/qobject.h:255:9: note: in expansion of macro &#39;Q_STATIC_ASSERT_X&#39; Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value), ^~~~~~~~~~~~~~~~~
06-08
descriptive_stats_view.py: # -*- coding: UTF-8 -*- # """ @filename: descriptive_stats_view.py @author : Sun S Z @time : 2025/9/15 14:24 @software: PyCharm """ """ 描述性统计视图(MVVM架构中的View层) 负责描述性统计界面的展示和用户交互处理 """ import matplotlib.pyplot as plt from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QLabel, QHBoxLayout, QComboBox, QCheckBox, QPushButton, QTableWidget, QSplitter, QTableWidgetItem, QMessageBox, QHeaderView ) from PySide6.QtGui import QFont from PySide6.QtCore import Qt, Signal, Slot from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure class DescriptiveStatsView(QWidget): """描述性统计视图类""" # 定义信号 analyze_requested = Signal(str, list) # (变量名, 选中的统计量) variable_changed = Signal(str) # 变量选择变化 request_valid_variables = Signal() # 请求有效变量列表 def __init__(self, viewmodel=None, parent=None): super().__init__(parent) self.viewmodel = viewmodel # 现在是DescriptiveStatsViewModel实例 self.parent = parent self.col_type_map = {} # 列类型映射 self.current_figure = None # 保存当前图表引用 self._data = None # 存储视图使用的数据 # 初始化UI self.init_ui() # 连接信号与槽 self.connect_signals() # 初始化数据 self.initialize() def init_ui(self): """初始化UI组件""" main_layout = QVBoxLayout(self) main_layout.setContentsMargins(15, 15, 15, 15) main_layout.setSpacing(12) # 标题 self.title = QLabel("描述性统计分析") self.title.setFont(QFont("Arial", 18, QFont.Bold)) self.title.setStyleSheet("color: #0ea5e9;") main_layout.addWidget(self.title) # 变量选择区域 self.var_widget = QWidget() self.var_layout = QHBoxLayout(self.var_widget) # 变量选择标签和下拉框 self.var_label = QLabel("选择变量:") self.var_combo = QComboBox() # 统计量选择区域 self.stats_label = QLabel("统计量:") self.stats_container = QWidget() self.stats_container_layout = QHBoxLayout(self.stats_container) # 创建统计量复选框 self.sample_count_check = QCheckBox("样本个数") self.mean_check = QCheckBox("平均值") self.median_check = QCheckBox("中位数") self.mode_check = QCheckBox("众数") self.std_check = QCheckBox("标准差") self.min_check = QCheckBox("最小值") self.max_check = QCheckBox("最大值") self.range_check = QCheckBox("范围") self.p10_check = QCheckBox("10%分位数") self.p25_check = QCheckBox("25%分位数") self.p75_check = QCheckBox("75%分位数") self.p90_check = QCheckBox("90%分位数") # 存储所有统计量复选框 self.stats_checks = [ self.sample_count_check, self.mean_check, self.median_check, self.mode_check, self.std_check, self.min_check, self.max_check, self.range_check, self.p10_check, self.p25_check, self.p75_check, self.p90_check ] # 添加到布局 for check in self.stats_checks: check.setChecked(True) self.stats_container_layout.addWidget(check) # 分析按钮 self.analyze_btn = QPushButton("执行分析") self.analyze_btn.setMinimumWidth(100) # 确保按钮可见 self.analyze_btn.setStyleSheet(""" QPushButton { background-color: #0ea5e9; color: white; border-radius: 4px; padding: 6px; } QPushButton:hover { background-color: #0284c7; } """) # 组装变量选择区域 self.var_layout.addWidget(self.var_label) self.var_layout.addWidget(self.var_combo) self.var_layout.addSpacing(20) self.var_layout.addWidget(self.stats_label) self.var_layout.addWidget(self.stats_container) self.var_layout.addStretch() self.var_layout.addWidget(self.analyze_btn) main_layout.addWidget(self.var_widget) # 结果区域 self.result_table = QTableWidget() self.result_table.setRowCount(1) self.result_table.setHorizontalHeaderLabels(["统计量名称", "统计值"]) # 图表区域 - 使用Figure而非plt.figure()以避免全局状态问题 self.figure = Figure(figsize=(10, 6)) self.canvas = FigureCanvas(self.figure) # 分割器 self.splitter = QSplitter(Qt.Vertical) self.splitter.addWidget(self.result_table) self.splitter.addWidget(self.canvas) self.splitter.setSizes([100, 600]) main_layout.addWidget(self.splitter, 1) def connect_signals(self): """连接信号与槽""" # 确保视图模型存在再连接信号 if not self.viewmodel: return # 视图 -> 视图模型 self.analyze_btn.clicked.connect(self.on_analyze_clicked) self.var_combo.currentIndexChanged.connect(self.on_var_changed) self.request_valid_variables.connect(self.viewmodel.request_valid_variables) self.analyze_requested.connect(self.viewmodel.calculate_descriptive_stats) # 视图模型 -> 视图 self.viewmodel.stats_results_updated.connect(self.update_results) self.viewmodel.chart_updated.connect(self.update_chart) self.viewmodel.valid_variables_updated.connect(self.update_variable_list) self.viewmodel.error_occurred.connect(self.show_error_message) def initialize(self): """初始化视图""" # 请求有效变量列表 self.request_valid_variables.emit() def on_analyze_clicked(self): """处理分析按钮点击事件 - 仅在点击时触发更新""" current_var = self.var_combo.currentText() if current_var and current_var != "无可用变量": # 收集选中的统计量 selected_stats = [check.text() for check in self.stats_checks if check.isChecked()] # 发送分析请求信号 self.analyze_requested.emit(current_var, selected_stats) def on_var_changed(self, index): """处理变量选择变化事件 - 只调整统计量可用性,自动分析""" current_var = self.var_combo.currentText() if current_var in self.col_type_map: col_type = self.col_type_map[current_var] self.adjust_stats_for_col_type(col_type) self.variable_changed.emit(current_var) def adjust_stats_for_col_type(self, col_type): """根据列类型调整统计量的可用性""" if col_type == "datetime": for check in self.stats_checks: if check.text() in ["样本个数", "最小值", "最大值", "范围"]: check.setEnabled(True) else: check.setEnabled(False) else: for check in self.stats_checks: check.setEnabled(True) @Slot(list, dict) def update_variable_list(self, variables, col_type_map): """更新变量下拉列表""" self.col_type_map = col_type_map self.var_combo.clear() if variables: self.var_combo.addItems(variables) self.var_combo.setEnabled(True) self.analyze_btn.setEnabled(True) else: self.var_combo.addItem("无可用变量") self.var_combo.setEnabled(False) self.analyze_btn.setEnabled(False) @Slot(dict) def update_results(self, results): """更新结果表格""" if not results: return # 清空表格 self.result_table.clear() # 设置表格尺寸 self.result_table.setRowCount(1) self.result_table.setColumnCount(len(results)) self.result_table.setHorizontalHeaderLabels(list(results.keys())) # 填充数据 for col_idx, (stat_name, value) in enumerate(results.items()): if isinstance(value, (int, float)): # 样本个数显示为整数 if stat_name == "样本个数": value_item = QTableWidgetItem(f"{int(value)}") else: value_item = QTableWidgetItem(f"{value:.4f}") else: value_item = QTableWidgetItem(str(value)) value_item.setFlags(value_item.flags() & ~Qt.ItemIsEditable) value_item.setTextAlignment(Qt.AlignCenter) self.result_table.setItem(0, col_idx, value_item) # 自动调整列宽 self.result_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) @Slot(object) def update_chart(self, figure): """更新图表显示""" try: # 清除现有图表 self.figure.clear() # 保存新图表引用 self.current_figure = figure # 复制新图表内容 for ax in figure.axes: # 创建新的坐标轴 new_ax = self.figure.add_subplot(111) # 复制数据和设置 if ax.get_title(): new_ax.set_title(ax.get_title()) if ax.get_xlabel(): new_ax.set_xlabel(ax.get_xlabel()) if ax.get_ylabel(): new_ax.set_ylabel(ax.get_ylabel()) # 复制绘图数据 for line in ax.get_lines(): new_ax.plot(line.get_xdata(), line.get_ydata(), color=line.get_color(), alpha=line.get_alpha()) # 复制直方图数据 for patch in ax.patches: new_ax.hist(patch.get_xy(), bins=ax.get_bins(), color=patch.get_facecolor(), edgecolor=patch.get_edgecolor(), alpha=patch.get_alpha()) # 应用旋转设置 tick_params = ax.xaxis.get_tick_params() rotation = tick_params.get(&#39;rotation&#39;, 0) if rotation: new_ax.tick_params(axis=&#39;x&#39;, rotation=rotation) self.figure.tight_layout() self.canvas.draw() except Exception as e: self.show_error_message(f"图表更新错误: {str(e)}") finally: # 释放原始图表资源 if figure: plt.close(figure) @Slot(str) def show_error_message(self, message): """显示错误消息""" QMessageBox.critical(self, "错误", message) def set_view_model(self, viewmodel): """设置视图模型""" self.viewmodel = viewmodel self.connect_signals() self.initialize() def set_data(self, data): """设置视图数据""" self._data = data # 如果已经设置了视图模型,同步数据 if self.viewmodel and hasattr(self.viewmodel.model, &#39;set_data&#39;): self.viewmodel.model.set_data(data) # 重新初始化以刷新界面 self.initialize() def closeEvent(self, event): """关闭窗口时释放图表资源""" if self.current_figure: plt.close(self.current_figure) plt.close(self.figure) event.accept() descriptive_stats_viewmodel.py: # -*- coding: UTF-8 -*- # """ @filename: descriptive_stats_viewmodel.py @author : Sun S Z @time : 2025/9/15 14:24 @software: PyCharm """ """ 描述性统计视图模型(MVVM架构中的ViewModel层) 负责处理描述性统计的业务逻辑,协调视图和模型 """ from PySide6.QtCore import QObject, Signal, Slot from models.statistical_analysis_models.descriptive_stats_model import DescriptiveStatsModel class DescriptiveStatsViewModel(QObject): """描述性统计视图模型类""" # 定义信号 stats_results_updated = Signal(dict) # 传递统计结果 chart_updated = Signal(object) # 传递图表对象 valid_variables_updated = Signal(list, dict) # 传递有效变量和类型映射 error_occurred = Signal(str) # 传递错误信息 def __init__(self, data_model=None, parent=None): super().__init__(parent) self.data_model = data_model # 数据模型引用 self.col_type_map = {} # 列类型映射 # 初始化模型 self.model = DescriptiveStatsModel() # 连接模型信号 self.model.calculation_completed.connect(self._on_calculation_completed) self.model.chart_generated.connect(self._on_chart_generated) self.model.error_occurred.connect(self._on_error_occurred) def has_data(self): """检查是否有可用数据""" return self.data_model is not None and self.data_model.has_data() @Slot() def request_valid_variables(self): """请求获取有效变量列表""" if not self.has_data(): self.valid_variables_updated.emit([], {}) return try: # 从数据模型获取数据 dataframe = self.data_model.get_data() valid_cols, col_type_map = self.model.get_valid_variables(dataframe) self.col_type_map = col_type_map self.valid_variables_updated.emit(valid_cols, col_type_map) except Exception as e: self.error_occurred.emit(f"获取变量列表错误: {str(e)}") @Slot(str, list) def calculate_descriptive_stats(self, column, selected_stats): """计算描述性统计 - 确保正确处理并转发结果""" if not self.has_data() or not column: self.error_occurred.emit("没有可用数据或未选择变量") return try: # 获取数据并计算统计量 dataframe = self.data_model.get_data() self.model.calculate_stats(dataframe, column, selected_stats) except Exception as e: self.error_occurred.emit(f"处理分析请求错误: {str(e)}") def _on_calculation_completed(self, results): """处理计算完成信号""" self.stats_results_updated.emit(results) def _on_chart_generated(self, figure): """处理图表生成信号""" self.chart_updated.emit(figure) def _on_error_occurred(self, message): """处理错误信号""" self.error_occurred.emit(message) descriptive_stats_model.py: # -*- coding: UTF-8 -*- # """ @filename: descriptive_stats_model.py @author : Sun S Z @time : 2025/9/15 14:24 @software: PyCharm """ """ 描述性统计模型(MVVM架构中的Model层) 负责执行描述性统计的核心计算和图表生成 """ import pandas as pd import matplotlib.pyplot as plt from PySide6.QtCore import QObject, Signal # 导入工具类 from models.font_setting import FontSetting class DescriptiveStatsModel(QObject): """描述性统计模型,处理统计计算和图表生成""" # 定义信号 calculation_completed = Signal(dict) # 传递统计结果 chart_generated = Signal(object) # 传递图表对象 error_occurred = Signal(str) # 传递错误信息 def __init__(self): super().__init__() # 确保中文字体正确设置 FontSetting.set_chinese_font() self._data = None # 存储模型数据 def set_data(self, data): """设置模型数据""" self._data = data.copy() # 复制数据避免外部修改影响 def has_data(self): """检查是否有可用数据""" return self._data is not None and not self._data.empty def get_data(self): """获取模型数据""" return self._data def get_valid_variables(self, dataframe): """ 获取有效变量列表和类型映射 Args: dataframe: 输入的DataFrame Returns: tuple: (有效变量列表, 变量类型映射) """ valid_cols = [] col_type_map = {} if dataframe is None or dataframe.empty: return valid_cols, col_type_map for col in dataframe.columns: # 跳过空列 col_data = dataframe[col].dropna() if len(col_data) == 0: continue # 判断列类型 if col_data.dtype.kind in &#39;iufc&#39;: # 数值型 valid_cols.append(col) col_type_map[col] = "numeric" else: # 尝试判断是否为时间型 try: pd.to_datetime(col_data, errors=&#39;raise&#39;) valid_cols.append(col) col_type_map[col] = "datetime" except: continue # 非数值、非时间列 return valid_cols, col_type_map def calculate_stats(self, dataframe, column, selected_stats): """ 计算描述性统计量 Args: dataframe: 输入的DataFrame column: 要分析的列名 selected_stats: 选中的统计量列表 """ try: if dataframe is None or column not in dataframe.columns: self.error_occurred.emit("无效的数据或列名") return # 准备数据 data = dataframe[column].dropna() if data.empty: self.error_occurred.emit("所选列没有有效数据") return # 确定列类型 col_type = "numeric" try: # 尝试转换为时间格式 pd.to_datetime(data, errors=&#39;raise&#39;) col_type = "datetime" except: # 确保是数值型 data = pd.to_numeric(data, errors=&#39;coerce&#39;).dropna() if data.empty: self.error_occurred.emit("无法将列转换为数值型数据") return # 计算统计量 results = {} sample_count = len(data) # 处理样本个数 if "样本个数" in selected_stats: results["样本个数"] = sample_count # 处理数值型列 if col_type == "numeric": if "平均值" in selected_stats: results["平均值"] = round(data.mean(), 4) if "中位数" in selected_stats: results["中位数"] = round(data.median(), 4) if "众数" in selected_stats: mode_value = data.mode().iloc[0] if not data.mode().empty else None results["众数"] = round(mode_value, 4) if mode_value is not None else "无" if "标准差" in selected_stats: results["标准差"] = round(data.std(), 4) if "最小值" in selected_stats: results["最小值"] = round(data.min(), 4) if "最大值" in selected_stats: results["最大值"] = round(data.max(), 4) if "范围" in selected_stats: results["范围"] = round(data.max() - data.min(), 4) if "10%分位数" in selected_stats: results["10%分位数"] = round(data.quantile(0.10), 4) if "25%分位数" in selected_stats: results["25%分位数"] = round(data.quantile(0.25), 4) if "75%分位数" in selected_stats: results["75%分位数"] = round(data.quantile(0.75), 4) if "90%分位数" in selected_stats: results["90%分位数"] = round(data.quantile(0.90), 4) # 处理时间型列 else: data = pd.to_datetime(data) if "最小值" in selected_stats: results["最小值(最早时间)"] = data.min().strftime("%Y-%m-%d %H:%M:%S") if "最大值" in selected_stats: results["最大值(最晚时间)"] = data.max().strftime("%Y-%m-%d %H:%M:%S") if "范围" in selected_stats: # 计算时间差(转为小时) time_diff = (data.max() - data.min()).total_seconds() / 3600 results["范围(小时)"] = round(time_diff, 2) # 发送计算结果 self.calculation_completed.emit(results) # 生成图表 self.generate_chart(data, column, col_type) except Exception as e: self.error_occurred.emit(f"统计计算错误: {str(e)}") def generate_chart(self, data, column, col_type): """ 生成统计图表 Args: data: 要可视化的数据 column: 列名 col_type: 列类型(numeric/datetime) """ try: # 创建图表,确保使用正确的字体 plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"] figure, ax = plt.subplots(figsize=(10, 6)) if col_type == "numeric": # 数值列:直方图 ax.hist(data, bins=15, color=&#39;#0ea5e9&#39;, edgecolor=&#39;white&#39;, alpha=0.7) ax.set_title(f&#39;"{column}" 数值分布直方图&#39;) ax.set_xlabel(&#39;数值&#39;) ax.set_ylabel(&#39;频数&#39;) else: # 时间列:时间序列趋势图 data_sorted = data.sort_values() ax.plot(data_sorted, range(len(data_sorted)), color=&#39;#0ea5e9&#39;, alpha=0.8) ax.set_title(f&#39;"{column}" 时间分布趋势&#39;) ax.set_xlabel(&#39;时间&#39;) ax.set_ylabel(&#39;累计数量&#39;) ax.tick_params(axis=&#39;x&#39;, rotation=45) plt.tight_layout() self.chart_generated.emit(figure) except Exception as e: self.error_occurred.emit(f"图表生成错误: {str(e)}") 统计分析表格为什么一直显示时间列的统计结果?我需要可以显示我选择对应列后点击执行分析按钮后表格显示的是对应列的统计结果。是否descriptive_stats_model.py中的calculate_stats和generate_chart并没有连接到descriptive_stats_view.py中,或者descriptive_stats_view.py中的update_results和update_chart有问题?给出完整修改方案代码,其他
09-17
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sunxiaopengsun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值