0

New to Qt. I'm trying to create a general purpose ComboBox that can be used in multiple situations. This code works when the Save button is pressed. However I would like to dispense with the save button and have the combobox selected item returned to the main window when the item is selected. I have been unable to figure this out and none of the examples I've seen help. Grateful for any suggestions.

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QDialog, QPushButton

from demoGen import Ui_demoGen

class demo(QtWidgets.QDialog):
    selected_item = ''

    def Save(self):
        self.selected_item = self.ui.demo_cb.currentText()
        self.done(0)
        return(self.selected_item)

    def closeEvent(self, event):
        event.accept()

    def __init__(self, info, drive_list):
        super(demo, self).__init__()
        self.ui = Ui_demoGen()
        self.ui.setupUi(self)

        self.selected_item = '#'
        self.ui.demo_save_btn.clicked.connect(lambda: self.Save())

        if info:
            self.ui.labe_1.setText(info)
        self.ui.demo_cb.clear()
        self.ui.demo_cb.addItems(item_list)
        self.ui.demo_cb.setCurrentIndex(-1)
        self.ui.demo_save_btn.setFocus()

        if info:
            self.ui.labe_1.setText(info)
        self.ui.demo_cb.clear()
        self.ui.demo_cb.addItems(item_list)
        self.ui.demo_cb.setCurrentIndex(-1)
        self.ui.demo_save_btn.setFocus()

if __name__ == "__main__":

    app = QtWidgets.QApplication([])
    info = 'Select Item'
    item_list = ['A','B','C','D','E']
    application = demo( info, list(reversed(item_list)))
    application.exec_()
    val = application.Save()
    if val == '':
        print('Z')
    else:
        print('val is ',val)
3
  • 2
    I would advise against this from a UX standpoint. What if the user selects something they didn't want / intend to select? Would they have to open the window with the combobox again? Having a separate combobox and save button makes the separation of functionality clear to the user and reduces the possibility of user error. Commented May 1 at 17:31
  • I couldn't agree more with JRiggles; having a dialog with only a combobox would be very confusing to the user (they would wonder when and how the selection is applied), and the behavior of accepting the result upon selection could easily cause annoyances if done by mistake, or limit its usage: if, for instance, you used the currentIndexChanged signal, the user may trigger it inadvertently by scrolling with the wheel by mistake, and if you used activated you'd prevent voluntary wheel and keyboard usage (the user would always have to open/select the wanted item). Just don't: you'll » Commented May 1 at 18:40
  • » get no benefit from making the UI behave like this, a dialog window is expected to have buttons when interaction is required to confirm a choice, removing a button for this isn't going to improve the UI, it will just confuse and annoy users. Unrelated suggestions: 1. you have the same code block duplicated in the __init__; 2. connections to lambdas should be done when it's necessary to change their arguments, so it's pointless in this case; change to .connect(self.Save); 3. calling Save() after exec is inappropriate; 4. classes should have capitalized names, functions should not. Commented May 1 at 18:48

1 Answer 1

0

Premise

Don't do that. It's a bad UX choice.

The concept behind a dialog window is to communicate something to the users and prompt some response from them; hence the term "dialog", which should be clear and unambiguous to both parts: the user must understand and know what they're doing, the program must do what the user expects.

A combo box is an element that shows the current choice between multiple items, so it is important that simply selecting an item doesn't cause a definitive choice.
Users do make mistakes, and when many items are shown in a crowded list (the drop-down popup) it's easy to choose the wrong one; also, since combo boxes usually accept keyboard input and mouse wheel scrolling to cycle through them, it's even easier to trigger an item change by mistake.

Possible implementation

None of the QComboBox signals can be used for this: items can be selected using the keyboard, either by using navigation keys (arrows, page up/down, home/end) or letters (selecting the first match in a sequence of keys), which will trigger "changed" signals such as currentIndexChanged, similarly to what the mouse wheel does, along with the activated signal.

In order to avoid accidental selection or unexpected behavior, you need to use the signals of the view shown in the popup, which is a QListView. Since QComboBox installs an event filter on the view, preventing its clicked signal to be emitted, we can only use its own pressed or activated signals: the former will act on any mouse button pressed on an item on the popup, the latter will allow using the keyboard Return or Enter keys when an item is highlighted.

Then, since you need the result, you could simply override the exec() function of QDialog and return whatever you want.

from PyQt5.QtWidgets import *

class ComboDialog(QDialog):
    def __init__(self, items, info='', parent=None):
        super().__init__(parent)
        self.infoLabel = QLabel(info)
        if not info:
            self.infoLabel.setText('Select an item')
        self.combo = QComboBox()
        self.combo.addItems(items)
        self.combo.setCurrentIndex(-1)

        layout = QVBoxLayout(self)
        layout.addWidget(self.infoLabel)
        layout.addWidget(self.combo)

        self.combo.view().activated.connect(self.accept)
        self.combo.view().pressed.connect(self.accept)

    def exec(self):
        super().exec()
        return self.combo.currentText()


if __name__ == "__main__":
    import sys
    app = QApplication(sys.argv)
    info = 'Select Item'
    item_list = ['A','B','C','D','E']
    dialog = ComboDialog(item_list, info)
    val = dialog.exec()
    if val == '':
        print('Z')
    else:
        print('val is ',val)

No, don't do that

While the above may be acceptable for your request, it has issues related to the UX aspects mentioned before.

Seeing a dialog with just a label and a combo box is disorienting to the user. What happens when I select an item? I don't see a button, as most dialog normally have. Will it appear later? As a heavy keyboard user, should I expect that the selection changes when using the keyboard? I need to test it, so I can try to use the arrow key to select an item, but then?

An user interface should make clear to the users what they can do and what happens when they do it, before they do it.

Having only the combo doesn't provide any benefit at all, but does cause many issues in return.

The simplest alternative is, obviously, to use a button. Also, for consistency, you should check if the user has closed the windo from the title bar:

class ComboDialog(QDialog):
    def __init__(self, items, info='', parent=None):
        super().__init__(parent)
        self.infoLabel = QLabel(info)
        if not info:
            self.infoLabel.setText('Select an item')
        self.combo = QComboBox()
        self.combo.addItems(items)
        self.combo.setCurrentIndex(-1)
        self.saveButton = QPushButton('Save')

        layout = QVBoxLayout(self)
        layout.addWidget(self.infoLabel)
        layout.addWidget(self.combo)
        layout.addWidget(self.saveButton)

        self.saveButton.clicked.connect(self.accept)

    def exec(self):
        if super().exec():
            return self.combo.currentText()
        return ''

If the item count isn't high, there are options that may allow "shortening" the user interaction:

  • just use separate buttons for each choice, shown in an appropriate layout;
  • use a QListWidget, which will clearly show all (or most) available items without the need to open the popup (see the sizeAdjustPolicy of scroll areas); in this case, you should still use a button to apply the selection;

For the list widget case, you may consider to avoid the button by allowing the selection through double clicking (through the itemDoubleClicked signal), but only if you clearly explain the user that they need to double click an item to apply the selection.
Yet, I wouldn't suggest that: the button(s) approach is still more appropriate to user expectancy.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks. I was experimenting with a minimal sized dialog to contain only a combobox. I understand all the arguments about using a Save button but I think a double click would work with an explanation. This would still allow a very small size for the dialog
@ssbg What's wrong with a slightly larger dialog? Even more importantly, what's the benefit of an extremely small dialog that sacrifices intuitiveness and usability for a few pixels? Don't confuse minimalism and simplicity aspects as an arbitrary "few things+less space=better": that's graphical obsession, not good design; it's good for movie props, not for programs. Proper design must always consider purpose, and UI elements should never be just about visuals, if they affect usability and intuitiveness: common and expected use, and unexpected reaction to uncommon/unexpected behavior.
@ssbg There are cases for which it may be necessary to "minimize" the visual impact (element count, structure and size), but those should always be carefully considered. For instance, the export dialog in a video editing software (which may include export options), or settings used for printing a whole document. They're complex cases that do require careful attention in UI design in order to not confuse the user with too many options. But if you're just showing a combo with a few other widgets, there's really no point in minimizing the size and UI element count at the expanse of usability.
@ssbg I strongly suggest you to do proper research in general design and UX (User Experience) in graphical user interfaces. It's common to be drawn by design "trends" or personal expectations, but it's also important to be aware about common usage and user expectations (including conventions). Consider taking your time to read from proper UX related websites, starting from ux.stackexchange.com and known resources like nngroup.com. This book may also be a good start for common object usage and design.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.