Qt QTableWidget 最后一览自适应宽度

本文介绍如何在Qt应用中使QTableWidget的最后一列自适应窗口大小,通过设置特定属性实现良好的视觉效果及用户体验。代码示例展示了如何初始化表格、设置列宽以及确保最后一列能自动调整。

     以前使用QTableWidget时,窗口固定大小,可以设置最后一览固定大小,但是如果窗口自适应,那最后一览常常出现问题,还好Qt有自己的解决方法,代码:

    QTableWidget *logTableWidget = new QTableWidget;
    ui->logTableWidget->setColumnCount(3);
    QStringList header;
    header<<"time" << "tag" << "log";
    ui->logTableWidget->setHorizontalHeaderLabels(header);
    ui->logTableWidget->horizontalHeader()->setStretchLastSection(true);//关键

    ui->logTableWidget->setColumnWidth(0, 100);
    ui->logTableWidget->setColumnWidth(1, 100);
    ui->logTableWidget->setContextMenuPolicy(Qt::CustomContextMenu);
    ui->logTableWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);


当然可以!以下是 **完整的 `DutyDetailCardWindow` 类代码**,已集成你所需的所有功能: ✅ 照片居中、尽量放大 ✅ 文字清晰可读(自动换行 + 字体适配) ✅ 支持页面整体缩放(按钮 + Ctrl+滚轮) ✅ 兼容最新 PySide6(不使用废弃 API) ✅ 自动查找照片文件(如 `photos/张三.jpg`) ✅ 响应式布局,放大缩小后依然美观 --- ### ✅ 完整类:`DutyDetailCardWindow` ```python from PySide6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QTableWidget, QTableWidgetItem, QHeaderView, QScrollArea, QPushButton, QApplication ) from PySide6.QtCore import Qt from PySide6.QtGui import QPixmap, QFont, QWheelEvent import os # 照片存放目录(与脚本同级的文件夹) PHOTO_DIR = "photos" class DutyDetailCardWindow(QWidget): def __init__(self, person_data=None, duty_data=None): super().__init__() self.person = person_data or {} self.duty = duty_data or {} self.scale_factor = 1.0 # 初始缩放比例 self.min_scale = 0.5 # 最小缩放 self.max_scale = 2.0 # 最大缩放 self.init_ui() def init_ui(self): """初始化主界面:滚动区域 + 缩放控制""" self.setWindowTitle("岗位职责卡") self.resize(900, 700) # === 主布局:外层垂直布局 === main_layout = QVBoxLayout(self) # === 滚动区域(内容自适应)=== scroll = QScrollArea() scroll.setWidgetResizable(True) # 内容随窗口拉伸 scroll.setStyleSheet("QScrollArea { border: none; }") self.content_widget = QWidget() self.content_layout = QVBoxLayout(self.content_widget) self.content_layout.setAlignment(Qt.AlignTop) self.content_layout.setSpacing(int(10 * self.scale_factor)) self.content_layout.setContentsMargins( int(20 * self.scale_factor), int(20 * self.scale_factor), int(20 * self.scale_factor), int(20 * self.scale_factor) ) scroll.setWidget(self.content_widget) main_layout.addWidget(scroll) # === 缩放控制按钮栏 === ctrl_layout = QHBoxLayout() ctrl_layout.addStretch() zoom_in_btn = QPushButton("🔍 放大 (+)") zoom_out_btn = QPushButton("🔎 缩小 (-)") reset_btn = QPushButton("🔄 重置") export_btn = QPushButton("📤 导出图片") zoom_in_btn.clicked.connect(self.zoom_in) zoom_out_btn.clicked.connect(self.zoom_out) reset_btn.clicked.connect(self.reset_zoom) export_btn.clicked.connect(self.export_to_image) ctrl_layout.addWidget(zoom_in_btn) ctrl_layout.addWidget(zoom_out_btn) ctrl_layout.addWidget(reset_btn) ctrl_layout.addWidget(export_btn) main_layout.addLayout(ctrl_layout) # === 构建表格内容 === self.build_table() def build_table(self): """构建主表格:包含标题、照片、信息、流程等""" t = QTableWidget() t.setRowCount(12) t.setColumnCount(4) t.setHorizontalHeaderLabels(["", "", "", ""]) t.verticalHeader().setVisible(False) t.setEditTriggers(QTableWidget.NoEditTriggers) t.setWordWrap(True) t.setSizeAdjustPolicy(QTableWidget.AdjustToContents) # 动态样式(随缩放变化) base_font_size = 14 scaled_font = int(base_font_size * self.scale_factor) padding = int(10 * self.scale_factor) radius = int(12 * self.scale_factor) t.setStyleSheet(f""" QTableWidget {{ gridline-color: #ddd; border: 1px solid #bbb; background: white; font-family: "Microsoft YaHei", SimSun, sans-serif; font-size: {scaled_font}px; border-radius: {radius}px; margin: {padding}px; }} QTableWidget::item {{ padding: {padding}px; border: 1px solid #eee; }} QLabel {{ background: transparent; border: none; margin: 0; padding: {padding}px; font-size: {scaled_font}px; line-height: 1.6; }} """) # 设置列宽均分 header = t.horizontalHeader() for i in range(4): header.setSectionResizeMode(i, QHeaderView.Stretch) row = 0 # === 第0行:大标题 === title_item = QTableWidgetItem("岗 位 职 责 卡") title_item.setTextAlignment(Qt.AlignCenter) title_font = QFont() title_font.setBold(True) title_font.setPointSize(int(18 * self.scale_factor)) title_item.setFont(title_font) t.setItem(row, 0, title_item) t.setSpan(row, 0, 1, 4) t.setRowHeight(row, int(80 * self.scale_factor)) row += 1 # === 第1-3行:照片 + 基本信息 === current_row = row photo_label = QLabel() photo_label.setAlignment(Qt.AlignCenter) photo_size = int(140 * self.scale_factor) # 随缩放变大 photo_label.setFixedSize(photo_size, photo_size) photo_label.setStyleSheet(f""" border: 2px dashed #ccc; background: #f9f9f9; color: #888; font-size: {int(14 * self.scale_factor)}px; border-radius: {int(10 * self.scale_factor)}px; """) photo_label.setText("📷\n照\n片") name = str(self.person.get('name', '')).strip() if name and os.path.exists(PHOTO_DIR): for ext in ['.jpg', '.png', '.jpeg']: path = os.path.join(PHOTO_DIR, f"{name}{ext}") if os.path.exists(path): pixmap = QPixmap(path) scaled_pixmap = pixmap.scaled( photo_size, photo_size, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation ) photo_label.setPixmap(scaled_pixmap) photo_label.setText("") break t.setCellWidget(current_row, 0, photo_label) t.setSpan(current_row, 0, 3, 1) # 合并三行高度 def create_item(text): item = QTableWidgetItem(text) item.setTextAlignment(Qt.AlignVCenter | Qt.AlignLeft) item.setFlags(item.flags() & ~Qt.ItemIsEditable) return item t.setItem(current_row, 1, create_item(f"姓 名:{name}")) t.setItem(current_row, 2, create_item(f"年 龄:{self.person.get('age', '?')}")) t.setItem(current_row, 3, create_item(f"工 龄:{self.person.get('work_years', '?')}年")) row += 1 # --- 第2行:技能等级 / 职名 / 所在车站 --- current_row = row t.setItem(current_row, 1, create_item(f"技能等级:{self.person.get('skill_level', '无')}")) t.setItem(current_row, 2, create_item(f"职 名:{self.person.get('rank', '未评级')}")) t.setItem(current_row, 3, create_item(f"所在车站:{self.person.get('station', '未知')}")) row += 1 # --- 第3行:任职经历(跨后三列)--- current_row = row exp_text = str(self.person.get('experience', '暂无')) exp_item = create_item(f"任职经历:{exp_text}") t.setItem(current_row, 1, exp_item) t.setSpan(current_row, 1, 1, 3) row += 1 # === 模块化添加区块函数 === def add_section(title_text, content_text, bg_color_hex): nonlocal row # 标题行 title_item = QTableWidgetItem(title_text) title_item.setTextAlignment(Qt.AlignCenter) title_font = QFont() title_font.setBold(True) title_font.setPointSize(int(14 * self.scale_factor)) title_item.setFont(title_font) title_item.setBackground(bg_color_hex) t.setItem(row, 0, title_item) t.setSpan(row, 0, 1, 4) row += 1 # 内容行(使用 QLabel 实现富文本和自动换行) content_label = QLabel(content_text.replace('\n', '<br>').replace(' ', ' ')) content_label.setWordWrap(True) content_label.setAlignment(Qt.AlignTop | Qt.AlignLeft) content_label.setStyleSheet(f""" background: #fafafa; border-radius: {int(6 * self.scale_factor)}px; padding: {padding}px; margin: 0; """) content_label.setTextFormat(Qt.TextFormat.RichText) t.setCellWidget(row, 0, content_label) t.setSpan(row, 0, 1, 4) t.resizeRowToContents(row) row += 1 add_section("流 程", str(self.duty.get("流程", "暂无")), "#f0f8ff") add_section("风 险", str(self.duty.get("风险", "暂无")), "#fff8f8") add_section("标 准", str(self.duty.get("标准", "暂无")), "#f8fff8") add_section("注意事项", str(self.duty.get("注意事项", "暂无")), "#fffff8") # === 完成表格构建 === t.resizeRowsToContents() self.table_widget = t self.content_layout.addWidget(t) def zoom_in(self): """放大视图""" if self.scale_factor < self.max_scale: self.scale_factor = round(self.scale_factor + 0.1, 2) self.rebuild() def zoom_out(self): """缩小视图""" if self.scale_factor > self.min_scale: self.scale_factor = round(self.scale_factor - 0.1, 2) self.rebuild() def reset_zoom(self): """重置为原始大小""" self.scale_factor = 1.0 self.rebuild() def rebuild(self): """完全重建 UI 以应用新缩放""" # 清空原内容 while self.content_layout.count(): child = self.content_layout.takeAt(0) if child.widget(): child.widget().deleteLater() # 重新构建表格 self.build_table() def wheelEvent(self, event: QWheelEvent): """支持 Ctrl + 滚轮缩放""" if event.modifiers() == Qt.ControlModifier: if event.angleDelta().y() > 0: self.zoom_in() else: self.zoom_out() event.accept() else: super().wheelEvent(event) def export_to_image(self): """将当前卡片导出为 PNG 图片""" from PySide6.QtGui import QPainter from PySide6.QtCore import QSize # 创建一个足够大的 pixmap self.content_widget.setFixedWidth(self.table_widget.width()) self.table_widget.resizeRowsToContents() size = self.content_widget.sizeHint() self.content_widget.setFixedSize(size) pixmap = QPixmap(size) pixmap.fill(Qt.white) painter = QPainter(pixmap) self.content_widget.render(painter) painter.end() # 保存文件 filename = f"{self.person.get('name', '职责卡')}.png" pixmap.save(filename, "PNG") from PySide6.QtWidgets import QMessageBox QMessageBox.information(self, "导出成功", f"已保存为:{filename}") # === 示例运行代码(测试用)=== if __name__ == "__main__": app = QApplication(sys.argv) # 模拟数据 person_data = { "name": "张三", "age": 35, "work_years": 12, "rank": "高级工", "skill_level": "技师", "station": "北京西站", "experience": "曾获全路技术能手称号,连续三年评为优秀员工。", } duty_data = { "流程": "1. 接班检查设备状态\n2. 登记作业计划\n3. 执行调度指令\n4. 记录操作日志", "风险": "• 误操作可能导致信号错误<br>• 夜间疲劳作业易出错<br>• 通信中断时应急处置不当", "标准": "• 必须双人确认关键操作<br>• 每半小时巡视一次<br>• 异常情况立即上报", "注意事项": "遇到极端天气需提高警惕,雷雨天禁止室外高空作业。" } window = DutyDetailCardWindow(person_data, duty_data) window.show() sys.exit(app.exec()) ``` --- ## ✅ 使用说明 ### 1. 文件结构建议: ``` project/ │ ├── tip_demo.py ← 把上面代码放这里 ├── photos/ ← 存放员工照片 │ ├── 张三.jpg │ └── 李四.png └── 张三.png ← 或直接放在根目录 ``` ### 2. 调用方式不变: ```python win = DutyDetailCardWindow(person_dict, duty_dict) win.show() ``` ### 3. 功能一览 | 操作 | 方法 | |------|------| | 🔍 放大 | 点击“放大”按钮 或 `Ctrl + 滚轮向上` | | 🔎 缩小 | 点击“缩小”按钮 或 `Ctrl + 滚轮向下` | | 🔄 重置 | 点击“重置”按钮 | | 📤 导出图片 | 点击“导出图片”,生成 PNG 文件 | --- 你现在可以直接复制这段完整代码到你的项目中,替换原来的 `DutyDetailCardWindow` 类,就能获得一个 **高清、可缩放、图文并茂、专业美观的岗位职责卡界面**! 是否需要我再帮你加上“打印”或“导出为 PDF”功能?
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值