【发布时间】:2021-10-07 21:18:08
【问题描述】:
在here by eyllanesc 的精彩回答的基础上,我已将 QListWidget 添加到可折叠框中。
下面有一个文本框,用户将在其中提供搜索字符串,我的目标是在 QListWidget 中突出显示该项目(如果存在)。
当前代码工作正常,即如果 QToolButton 已经展开,它将突出显示文本并将其滚动到顶部,但它只会打开并突出显示该项目,但如果它尚未展开,则不会将其滚动到顶部。 (所以用户现在不知道他是否找到了该项目,因为他看不到它突出显示。) 奇怪的是,如果我再次按 Enter 键,它会滚动到顶部。
我尝试了各种方法,例如使 QlistWidget 处于活动状态、聚焦等,但没有帮助。
请告诉我我缺少什么,这样我就不需要按两次 Enter 以防 QToolButton 尚未展开。
编辑:按照建议从代码中删除动画部分。
import time
from PyQt5 import QtCore
from PyQt5.QtWidgets import QMainWindow, QListWidget, QLabel, QApplication, QVBoxLayout, \
QWidget, QSizePolicy, QToolButton, QScrollArea, QFrame, QDockWidget, QLineEdit, QHBoxLayout, QAbstractItemView
collapsed_list = ['What', 'should', 'be', 'done', 'to', 'fix', 'this', 'issue?', 'I', 'am', 'confused']
class CollapsibleDemo(QWidget):
def __init__(self, title="", parent=None):
super(CollapsibleDemo, self).__init__(parent)
self.toggle_button = QToolButton(
text=title, checkable=True, checked=False
)
self.toggle_button.setSizePolicy(
QSizePolicy.Expanding, QSizePolicy.Fixed
)
self.toggle_button.setToolButtonStyle(
QtCore.Qt.ToolButtonTextBesideIcon
)
self.toggle_button.setArrowType(QtCore.Qt.RightArrow)
self.toggle_button.pressed.connect(self.on_pressed)
self.content_area = QScrollArea(
maximumHeight=0, minimumHeight=0
)
self.content_area.setSizePolicy(
QSizePolicy.Expanding, QSizePolicy.Fixed
)
self.content_area.setFrameShape(QFrame.NoFrame)
lay = QVBoxLayout(self)
lay.setSpacing(0)
lay.setContentsMargins(10, 10, 1, 1)
lay.addWidget(self.toggle_button)
lay.addWidget(self.content_area)
@QtCore.pyqtSlot()
def on_pressed(self):
checked = self.toggle_button.isChecked()
self.toggle_button.setArrowType(
QtCore.Qt.DownArrow if not checked else QtCore.Qt.RightArrow
)
if not checked:
self.content_area.setMaximumHeight(self.content_height + self.collapsed_height)
else:
self.content_area.setMaximumHeight(0)
def setContentLayout(self, layout):
self.content_area.setLayout(layout)
self.collapsed_height = (
self.sizeHint().height() - self.content_area.maximumHeight()
)
self.content_height = layout.sizeHint().height()
def set_text(self, title):
self.toggle_button.setText(title)
class Try(QMainWindow):
def __init__(self, ):
super().__init__()
self.width = 800
self.height = 800
self.init_ui()
def init_ui(self):
self.resize(self.width, self.height)
self.create_background()
self.add_collapsed_list_box()
self.add_find_text_box()
self.vlay.addStretch()
self.find_text_box.setFocus()
def create_background(self):
dock = QDockWidget()
self.setCentralWidget(dock)
dock.setFeatures(QDockWidget.NoDockWidgetFeatures)
scroll = QScrollArea()
dock.setWidget(scroll)
content = QWidget()
scroll.setWidget(content)
scroll.setWidgetResizable(True)
self.vlay = QVBoxLayout(content)
self.vlay.setSpacing(10)
def add_collapsed_list_box(self):
self.box = CollapsibleDemo(f"Whats inside here!")
self.vlay.addWidget(self.box)
lay_diff = QVBoxLayout()
self.qlist = QListWidget()
self.qlist.addItems(collapsed_list)
lay_diff.addWidget(self.qlist)
self.box.setContentLayout(lay_diff)
def add_find_text_box(self):
self.find_text_box = QLineEdit("am")
find_label = QLabel(" Search text: ")
enter_label = QLabel(" (Press Enter)")
hlayout = QHBoxLayout()
hlayout.addWidget(find_label)
hlayout.addWidget(self.find_text_box)
hlayout.addWidget(enter_label)
hlayout.addStretch(3)
self.vlay.addLayout(hlayout)
self.find_text_box.returnPressed.connect(self.find_selected)
def find_selected(self):
user_text = self.find_text_box.text()
if user_text in collapsed_list:
if not self.box.toggle_button.isChecked():
self.box.on_pressed()
self.box.toggle_button.setChecked(True)
item = self.qlist.findItems(user_text, QtCore.Qt.MatchRegExp)[0]
item.setSelected(True)
self.qlist.scrollToItem(item, QAbstractItemView.PositionAtTop)
if __name__ == "__main__":
app = QApplication([])
window = Try()
window.show()
app.exec()
【问题讨论】:
-
我看到您为动画设置了非常短的持续时间:5 毫秒是 1/200 秒,即使假设计算机速度如此之快以至于能够真正快速地显示动画,人眼无法看到(我们几乎看不到任何持续 20-30 毫秒的差异)。因此,在我看来,使用动画完全没有意义,这将解决您的问题,因为当您调用
scrollToItem时,列表视图仍然折叠,因此无法滚动到任何内容。 -
我认为保持低数字,即使它在用户眼中看起来是即时的,实际上应该有利于滚动的情况。仍然要测试,我在原始代码中将持续时间增加到 500,我还在滚动命令之前添加了 1 秒睡眠,但它仍然没有解决它。
-
添加睡眠不会改变任何事情,因为阻塞函数会阻止事件循环正确处理所有内容(包括动画)。相信你还是不明白问题所在:动画是异步的,当你调用
on_pressed时,动画开始,而scrollToItem之后就调用了,那时盒子仍然折叠,所以它不会滚动到任何东西。对动画使用较短的间隔不会解决问题,因为问题在于存在动画这一事实。即使将持续时间设置为 0 也不会改变这一点。 -
还有另一个方面需要考虑:项目视图需要一些时间才能正确处理大小变化,并且滚动条不会立即更新:即使您不使用动画并立即调整框大小,
scrollToItem在任何情况下都不会像那样工作。因此,您必须澄清您是否仍然想要动画功能(具有适当的持续时间)或者您对此不感兴趣,因为解决方案会非常不同。 -
知道了。我应该如何编写没有任何动画的 setContentLayout?它基本上应该在点击时在 content_height 和 collapsed_height 之间切换。
标签: python-3.x pyqt5 qlistwidget qlistwidgetitem