PyQt5:如何在QTableWidget跨两单元格添加进度条并实现文本对齐?
解决股票Level2行情QTableWidget进度条跨列对齐问题
核心思路
放弃直接用QProgressBar跨列覆盖单元格的方案,改用自定义复合Widget:将进度条作为背景层,在上方叠加两个独立标签分别对应需要显示的文本,既能实现进度条跨列效果,又能单独控制每个标签的对齐方式。
步骤实现
1. 编写自定义进度条Widget
创建包含进度条+双标签的复合控件,分别处理左右文本的对齐逻辑:
from PyQt5.QtWidgets import QWidget, QProgressBar, QLabel from PyQt5.QtCore import Qt class AskProgressWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) # 进度条作为背景,隐藏自带文本 self.progress_bar = QProgressBar(self) self.progress_bar.setTextVisible(False) self.progress_bar.setStyleSheet("QProgressBar {border: none;}") # 左对齐的ask volume标签 self.label_volume = QLabel(self.progress_bar) self.label_volume.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.label_volume.setStyleSheet("color: #ffffff; padding-left: 4px;") # 右对齐的ask price标签 self.label_price = QLabel(self.progress_bar) self.label_price.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.label_price.setStyleSheet("color: #ffffff; padding-right: 4px;") def resizeEvent(self, event): # 跟随Widget大小调整控件位置,匹配原表格列区域 self.progress_bar.setGeometry(0, 0, self.width(), self.height()) # 按原列宽比例分配标签宽度(可根据实际列宽调整) volume_col_width = self.width() * 120 / (120 + 100) self.label_volume.setGeometry(0, 0, volume_col_width, self.height()) self.label_price.setGeometry(volume_col_width, 0, self.width() - volume_col_width, self.height()) def update_data(self, volume, price, max_volume): self.progress_bar.setMaximum(max_volume) self.progress_bar.setValue(volume) self.label_volume.setText(f"{volume:,}") self.label_price.setText(f"{price:,}") # Bid侧进度条Widget,仅标签对齐逻辑调整 class BidProgressWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) self.progress_bar = QProgressBar(self) self.progress_bar.setTextVisible(False) self.progress_bar.setStyleSheet("QProgressBar {border: none;}") # 左对齐的bid price标签 self.label_price = QLabel(self.progress_bar) self.label_price.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.label_price.setStyleSheet("color: #ffffff; padding-left: 4px;") # 右对齐的bid volume标签 self.label_volume = QLabel(self.progress_bar) self.label_volume.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.label_volume.setStyleSheet("color: #ffffff; padding-right: 4px;") def resizeEvent(self, event): self.progress_bar.setGeometry(0, 0, self.width(), self.height()) price_col_width = self.width() * 100 / (100 + 120) self.label_price.setGeometry(0, 0, price_col_width, self.height()) self.label_volume.setGeometry(price_col_width, 0, self.width() - price_col_width, self.height()) def update_data(self, price, volume, max_volume): self.progress_bar.setMaximum(max_volume) self.progress_bar.setValue(volume) self.label_price.setText(f"{price:,}") self.label_volume.setText(f"{volume:,}")
2. 嵌入表格并配置格式
在表格中设置单元格跨度,插入自定义Widget,并配置非进度条列的对齐:
from PyQt5.QtWidgets import QTableWidget, QTableWidgetItem # 假设stock_data是行情数据列表,每条格式:(ask_count, ask_volume, ask_price, bid_price, bid_volume, bid_count) table_widget = QTableWidget(len(stock_data), 6) table_widget.setHorizontalHeaderLabels(["ask count", "ask volume", "ask", "bid", "bid volume", "bid count"]) # 计算进度条最大值 max_ask_volume = max([row[1] for row in stock_data]) max_bid_volume = max([row[4] for row in stock_data]) # 遍历每行设置内容 for row_idx, data in enumerate(stock_data): ask_count, ask_volume, ask_price, bid_price, bid_volume, bid_count = data # 设置ask count列(居中对齐) item = QTableWidgetItem(f"{ask_count:,}") item.setTextAlignment(Qt.AlignCenter) table_widget.setItem(row_idx, 0, item) # 插入ask侧跨列进度条 ask_widget = AskProgressWidget() ask_widget.update_data(ask_volume, ask_price, max_ask_volume) table_widget.setSpan(row_idx, 1, 1, 2) # 跨列1和列2 table_widget.setCellWidget(row_idx, 1, ask_widget) # 插入bid侧跨列进度条 bid_widget = BidProgressWidget() bid_widget.update_data(bid_price, bid_volume, max_bid_volume) table_widget.setSpan(row_idx, 3, 1, 2) # 跨列3和列4 table_widget.setCellWidget(row_idx, 3, bid_widget) # 设置bid count列(居中对齐) item = QTableWidgetItem(f"{bid_count:,}") item.setTextAlignment(Qt.AlignCenter) table_widget.setItem(row_idx, 5, item) # 设置固定列宽(匹配自定义Widget中的比例) table_widget.setColumnWidth(0, 80) table_widget.setColumnWidth(1, 120) table_widget.setColumnWidth(2, 100) table_widget.setColumnWidth(3, 100) table_widget.setColumnWidth(4, 120) table_widget.setColumnWidth(5, 80)
方案优势
- 完全独立控制两个文本标签的对齐方式,解决格式化字符串无法精准对齐的问题;
- 进度条作为背景层,完美实现跨列填充效果;
- 自定义Widget可灵活调整样式(如颜色、内边距),适配Level2行情的视觉需求。
内容的提问来源于stack exchange,提问作者Kim
相关产品推荐
相关产品推荐

