使用键盘在 QDialog 中导航 QComboBox。

huangapple go评论64阅读模式
英文:

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

为了实现这些功能,我对您的已有代码进行了一些修改和添加:

  • 为了让导航和控制方式与QComboBoxQListWidget类似,我将两个成员变量替换为self.options_widget对象,然后根据isinstance(self.options_widget, QComboBox)或其他方式缩小实现控件的不同方法。
  • 我重写了keyPressEvent()方法,以将控件(move_up()move_down()confirm()cancel())委派给不同的键盘按键。
  • 为了成功过滤keyPressEvent(),我不得不完全禁用焦点到组合框/列表窗口和取消按钮。希望这对您没有问题。
  • 最后,我使用了QComboBox/QListWidgetactivated/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 and QListWidget, I assigned either objects to self.options_widget instead of two member variables, then narrow down into different ways to implement the controls with isinstance(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 from QComboBox/QListWidget respectively for user mouse input, since they only send on user input as opposed to currentIndexChanged/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(&#39;Cancel&#39;)
        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 &lt; 0:  # overflow index
                i += w.count()
            w.setCurrentIndex(i)
        elif isinstance(w, QListWidget):
            i = w.currentRow() - 1
            if i &lt; 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()

huangapple
  • 本文由 发表于 2023年3月7日 13:27:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/75658318.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定