You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何为两个QListWidget使用单个QScrollBar实现同步滚动

嘿,这个需求我之前做类似对比界面的时候刚好实现过,给你分享两种靠谱的方案,都满足你必须包含两个QListWidget的要求~

方案1:使用单个QScrollBar同步两个ListWidget

这正是你提到的思路,核心是隐藏两个ListWidget自带的滚动条,用一个共用的滚动条来控制两者,同时还要处理双向同步(拖动滚动条/滚动ListWidget内容都能同步)。

实现步骤:

  • 先隐藏两个ListWidget的垂直滚动条,避免和共用滚动条冲突
  • 创建一个独立的QScrollBar,把它的valueChanged信号绑定到两个ListWidget的垂直滚动条的setValue方法,这样拖动共用滚动条时两个列表会一起滚动
  • 监听两个ListWidget的垂直滚动条的rangeChanged信号,同步更新共用滚动条的范围(保证滚动范围和列表内容匹配)
  • 最后还要处理反向同步:当用户用鼠标滚轮滚动某个ListWidget时,把滚动值同步到共用滚动条和另一个ListWidget,这里要注意用blockSignals()避免循环触发信号

方案2:直接同步两个ListWidget的滚动(无需额外滚动条)

如果不需要单独的滚动条,也可以直接把两个ListWidget的滚动信号互相绑定,同样要处理循环触发的问题。

实现步骤:

  • 把第一个ListWidget的垂直滚动条的valueChanged信号绑定到第二个ListWidget的垂直滚动条的setValue方法
  • 反过来再绑定一次,同时用blockSignals()防止循环触发

完整GUI示例代码

下面是用PyQt5写的完整可运行代码,你可以直接测试两种方案:

import sys
from PyQt5.QtWidgets import (QApplication, QWidget, QHBoxLayout, 
                             QListWidget, QScrollBar, QVBoxLayout)
from PyQt5.QtCore import Qt

class SyncScrollWidget(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()
        
    def init_ui(self):
        self.setWindowTitle("双ListWidget同步滚动")
        self.resize(600, 400)
        
        # 创建两个ListWidget并添加测试数据
        self.list1 = QListWidget()
        self.list2 = QListWidget()
        for i in range(50):
            self.list1.addItem(f"列表1 条目 {i+1}")
            self.list2.addItem(f"列表2 条目 {i+1}")
        
        # 方案1:使用共用滚动条(默认启用)
        self.use_shared_scrollbar()
        
        # 如果要测试方案2,注释上面的use_shared_scrollbar(),取消下面的注释
        # self.sync_list_directly()
        
        self.show()
        
    def use_shared_scrollbar(self):
        # 隐藏自带滚动条
        self.list1.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.list2.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        
        # 创建共用滚动条
        self.shared_scroll = QScrollBar(Qt.Vertical)
        
        # 同步滚动范围
        def update_scroll_range(min_val, max_val):
            self.shared_scroll.setRange(min_val, max_val)
            
        self.list1.verticalScrollBar().rangeChanged.connect(update_scroll_range)
        self.list2.verticalScrollBar().rangeChanged.connect(update_scroll_range)
        
        # 共用滚动条控制两个列表
        self.shared_scroll.valueChanged.connect(self.list1.verticalScrollBar().setValue)
        self.shared_scroll.valueChanged.connect(self.list2.verticalScrollBar().setValue)
        
        # 列表滚动时同步到共用滚动条
        def sync_to_shared(value):
            self.shared_scroll.blockSignals(True)
            self.shared_scroll.setValue(value)
            self.shared_scroll.blockSignals(False)
            
        self.list1.verticalScrollBar().valueChanged.connect(sync_to_shared)
        self.list2.verticalScrollBar().valueChanged.connect(sync_to_shared)
        
        # 布局
        h_layout = QHBoxLayout()
        h_layout.addWidget(self.list1)
        h_layout.addWidget(self.list2)
        
        main_layout = QVBoxLayout()
        main_layout.addLayout(h_layout)
        main_layout.addWidget(self.shared_scroll, alignment=Qt.AlignRight)
        
        self.setLayout(main_layout)
        
    def sync_list_directly(self):
        # 直接同步两个列表的滚动,无需额外滚动条
        def sync_list1_to_list2(value):
            self.list2.verticalScrollBar().blockSignals(True)
            self.list2.verticalScrollBar().setValue(value)
            self.list2.verticalScrollBar().blockSignals(False)
            
        def sync_list2_to_list1(value):
            self.list1.verticalScrollBar().blockSignals(True)
            self.list1.verticalScrollBar().setValue(value)
            self.list1.verticalScrollBar().blockSignals(False)
            
        self.list1.verticalScrollBar().valueChanged.connect(sync_list1_to_list2)
        self.list2.verticalScrollBar().valueChanged.connect(sync_list2_to_list1)
        
        # 布局
        h_layout = QHBoxLayout()
        h_layout.addWidget(self.list1)
        h_layout.addWidget(self.list2)
        self.setLayout(h_layout)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = SyncScrollWidget()
    sys.exit(app.exec_())

代码说明:

  • 默认启用的是方案1(共用滚动条),如果要测试方案2,只需要注释掉self.use_shared_scrollbar(),取消self.sync_list_directly()的注释即可
  • blockSignals(True)的作用是暂时阻止控件发送信号,避免出现“List1滚动触发List2滚动,List2滚动又触发List1滚动”的无限循环
  • 测试数据用了50条条目,方便看到滚动效果

内容的提问来源于stack exchange,提问作者Pommesfreak

火山引擎 最新活动