英文:
using keys to navigate a QComboBox in a QDialog
问题
以下是代码的翻译部分:
class OptionsDialog(QDialog):
def __init__(self, title, selected, options, as_list=False):
super().__init__()
self.type = title
self.options = options
self.combo_box = None
self.list_box = None
self.selected_option = None
self.setModal(True)
top_layout = QVBoxLayout()
layout = QHBoxLayout()
if as_list:
self.list_box = QListWidget()
layout.addWidget(self.list_box)
i = 0
for option in self.options:
self.list_box.insertItem(i, option)
if option == selected:
self.list_box.setCurrentRow(i)
i += 1
self.list_box.setSelectionMode(QListWidget.SingleSelection)
self.list_box.clicked.connect(self.list_box_changed)
else:
self.combo_box = QComboBox()
layout.addWidget(self.combo_box)
for option in self.options:
self.combo_box.addItem(option)
self.combo_box.currentIndexChanged.connect(self.combo_box_changed)
top_layout.addLayout(layout)
layout = QHBoxLayout()
button = QPushButton('Cancel')
layout.addWidget(button)
button.clicked.connect(self.cancel_pressed)
top_layout.addLayout(layout)
self.setLayout(top_layout)
def combo_box_changed(self, index):
self.selected_option = self.combo_box.currentText()
self.close()
def list_box_changed(self):
self.selected_option = self.list_box.currentItem().text()
self.close()
def cancel_pressed(self):
self.selected_option = None
self.close()
请注意,我已经移除了不需要翻译的部分。如果您有任何其他问题或需要进一步的帮助,请随时提出。
英文:
I have a QDialog with a QComboBox and a cancel button. The two options the user has is to either select an item from the QComboBox or hit the cancel button.
I would like the user to have the option of navigating with keys instead of just the mouse. ESC would press the cancel button, moving the keys up and down move the selection in the QComboBox, and enter/return selects the item. This code supports either a QComboBox or a QListWidget (depending on the as_list parameter). Ultimately I would like this concept to work with both.
Here is the code:
class OptionsDialog(QDialog):
def __init__(self, title, selected, options, as_list=False):
super().__init__()
self.type = title
self.options = options
self.ignore_keys = ignore_keys
self.combo_box = None
self.list_box = None
self.selected_option = None
self.setModal(True)
top_layout = QVBoxLayout()
layout = QHBoxLayout()
if as_list:
self.list_box = QListWidget()
layout.addWidget(self.list_box)
i = 0
for option in self.options:
self.list_box.insertItem(i, option)
if option == selected:
self.list_box.setCurrentRow(i)
i += 1
self.list_box.setSelectionMode(QListWidget.SingleSelection)
self.list_box.clicked.connect(self.list_box_changed)
else:
self.combo_box = QComboBox()
layout.addWidget(self.combo_box)
for option in self.options:
self.combo_box.addItem(option)
self.combo_box.currentIndexChanged.connect(self.combo_box_changed)
top_layout.addLayout(layout)
layout = QHBoxLayout()
button = QPushButton('Cancel')
layout.addWidget(button)
button.clicked.connect(self.cancel_pressed)
top_layout.addLayout(layout)
self.setLayout(top_layout)
def combo_box_changed(self, index):
self.selected_option = self.combo_box.currentText()
self.close()
def list_box_changed(self):
self.selected_option = self.list_box.currentItem().text()
self.close()
def cancel_pressed(self):
self.selected_option = None
self.close()
答案1
得分: 1
为了实现这些功能,我对您的已有代码进行了一些修改和添加:
- 为了让导航和控制方式与
QComboBox
和QListWidget
类似,我将两个成员变量替换为self.options_widget
对象,然后根据isinstance(self.options_widget, QComboBox)
或其他方式缩小实现控件的不同方法。 - 我重写了
keyPressEvent()
方法,以将控件(move_up()
,move_down()
,confirm()
,cancel()
)委派给不同的键盘按键。 - 为了成功过滤
keyPressEvent()
,我不得不完全禁用焦点到组合框/列表窗口和取消按钮。希望这对您没有问题。 - 最后,我使用了
QComboBox
/QListWidget
的activated
/itemActivated
信号来处理用户的鼠标输入,因为它们只在用户输入时发送,而不像currentIndexChanged
/clicked
那样发送。
以下是我尝试并测试的代码,它使用PySide2和Python 3.7编写,但应该适用于其他版本:
from PySide2.QtCore import Qt
from PySide2.QtGui import QKeyEvent
from PySide2.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QListWidget, \
QComboBox, QPushButton, QApplication, QAbstractItemView
class OptionsDialog(QDialog):
def __init__(self, title, selected, options, as_list=False):
super().__init__()
self.type = title
self.options = options
self.options_widget = None
self.selected_option = None
self.setModal(True)
top_layout = QVBoxLayout()
layout = QHBoxLayout()
if as_list:
self.options_widget = QListWidget()
self.options_widget.setSelectionMode(QAbstractItemView.SingleSelection)
self.options_widget.itemActivated.connect(self.confirm)
i = 0
for option in self.options:
self.options_widget.insertItem(i, option)
if option == selected:
self.options_widget.setCurrentRow(i)
i += 1
else:
self.options_widget = QComboBox()
self.options_widget.activated.connect(self.confirm)
for option in self.options:
self.options_widget.addItem(option)
self.options_widget.setFocusPolicy(Qt.NoFocus)
layout.addWidget(self.options_widget)
top_layout.addLayout(layout)
layout = QHBoxLayout()
button = QPushButton('Cancel')
button.setFocusPolicy(Qt.NoFocus)
layout.addWidget(button)
button.clicked.connect(self.cancel)
top_layout.addLayout(layout)
self.setLayout(top_layout)
def keyPressEvent(self, event: QKeyEvent):
if event.key() == Qt.Key_Up:
self.move_up()
elif event.key() == Qt.Key_Down:
self.move_down()
elif event.key() == Qt.Key_Return:
self.confirm()
elif event.key() == Qt.Key_Escape:
self.cancel()
def move_up(self):
w = self.options_widget
if isinstance(w, QComboBox):
i = w.currentIndex() - 1
if i < 0:
i += w.count()
w.setCurrentIndex(i)
elif isinstance(w, QListWidget):
i = w.currentRow() - 1
if i < 0:
i += w.count()
w.setCurrentRow(i)
def move_down(self):
w = self options_widget
if isinstance(w, QComboBox):
i = (w.currentIndex() + 1) % w.count()
w.setCurrentIndex(i)
elif isinstance(w, QListWidget):
i = (w.currentRow() + 1) % w.count()
w.setCurrentRow(i)
def confirm(self):
w = self.options_widget
if isinstance(w, QComboBox):
self.selected_option = w.currentText()
elif isinstance(w, QListWidget):
self.selected_option = w.currentItem().text()
self.close()
def cancel(self):
self.selected_option = None
self.close()
英文:
To implement the features, there are a few things I modified and added into your established code
- To have the navigation and control scheme similar with both
QComboBox
andQListWidget
, I assigned either objects toself.options_widget
instead of two member variables, then narrow down into different ways to implement the controls withisinstance(self.options_widget, QComboBox)
or the other one - I overrode the
keyPressEvent()
method to delegate the controls (move_up()
,move_down()
,confirm()
,cancel()
) to different keyboard presses - To have the
keyPressEvent()
successfully filtered, I had to disable focus completely to the combobox/list widget and cancel button. I hope this is okay with you - Finally, I used
activated
/itemActivated
signal fromQComboBox
/QListWidget
respectively for user mouse input, since they only send on user input as opposed tocurrentIndexChanged
/clicked
Here is the code I tried and tested, it uses PySide2 and Python 3.7 but it should work for other versions too
from PySide2.QtCore import Qt
from PySide2.QtGui import QKeyEvent
from PySide2.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QListWidget, \
QComboBox, QPushButton, QApplication, QAbstractItemView
class OptionsDialog(QDialog):
def __init__(self, title, selected, options, as_list=False):
super().__init__()
self.type = title
self.options = options
# self.ignore_keys = ignore_keys # idk what this does
self.options_widget = None # combine the combo_box and list_box into one
# self.combo_box = None
# self.list_box = None
self.selected_option = None
self.setModal(True)
top_layout = QVBoxLayout()
layout = QHBoxLayout()
if as_list:
self.options_widget = QListWidget()
self.options_widget.setSelectionMode(QAbstractItemView.SingleSelection)
# Single selection is important
self.options_widget.itemActivated.connect(self.confirm)
# itemActivated is emitted on USER double click
i = 0
for option in self.options:
self.options_widget.insertItem(i, option)
if option == selected:
self.options_widget.setCurrentRow(i)
i += 1
else:
self.options_widget = QComboBox()
self.options_widget.activated.connect(self.confirm)
# activated is emitted on USER chooses an item
for option in self.options:
self.options_widget.addItem(option)
self.options_widget.setFocusPolicy(Qt.NoFocus) # IMPORTANT for keypress
layout.addWidget(self.options_widget)
top_layout.addLayout(layout)
layout = QHBoxLayout()
button = QPushButton('Cancel')
button.setFocusPolicy(Qt.NoFocus) # IMPORTANT for keypress
layout.addWidget(button)
button.clicked.connect(self.cancel)
top_layout.addLayout(layout)
self.setLayout(top_layout)
def keyPressEvent(self, event: QKeyEvent):
if event.key() == Qt.Key_Up:
self.move_up()
elif event.key() == Qt.Key_Down:
self.move_down()
elif event.key() == Qt.Key_Return:
self.confirm()
elif event.key() == Qt.Key_Escape:
self.cancel()
def move_up(self):
w = self.options_widget
if isinstance(w, QComboBox):
# calculate next index
i = w.currentIndex() - 1
if i < 0: # overflow index
i += w.count()
w.setCurrentIndex(i)
elif isinstance(w, QListWidget):
i = w.currentRow() - 1
if i < 0: # overflow index
i += w.count()
w.setCurrentRow(i)
def move_down(self):
w = self.options_widget
if isinstance(w, QComboBox):
# calculate next index
i = (w.currentIndex() + 1) % w.count() # mod resolves overflow
w.setCurrentIndex(i)
elif isinstance(w, QListWidget):
i = (w.currentRow() + 1) % w.count() # mod resolves overflow
w.setCurrentRow(i)
def confirm(self):
w = self.options_widget
if isinstance(w, QComboBox):
self.selected_option = w.currentText()
elif isinstance(w, QListWidget):
self.selected_option = w.currentItem().text()
self.close()
def cancel(self):
self.selected_option = None
self.close()
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论