Search code examples
user-interfacepyqtqcombobox

PyQt QComboBox.setEditText leaves the entered text selected/highlighted; how to unhighlight?


UPDATE3 - SOLVED with reservations, please see my solution below; leaving it open since the cause of the problem is unclear, and I don't know how robust the solution is.


UPDATE1: here's the short short version.

Currently, after .setEditText on a QComboBox, I get this:

enter image description here

so the next thing you type will overwrite 'Team '.

But the desired effect is this (unhighlighted / unselected), so that the next thing you type will be appended to 'Team ' instead of overwriting it:

enter image description here

Thanks for any help. The rambling details are below, which is the original post:

(this is all PyQt 5.4)

UPDATE2: Apparently python doesn't think anything is actually selected:

    self.entryForm.ui.teamField.lineEdit().setText("Team ")
    print("selected text:"+self.entryForm.ui.teamField.lineEdit().selectedText())

prints "selected text:" and nothing else. To make sure that's working:

self.entryForm.ui.teamField.lineEdit().setText("Team ")
self.entryForm.ui.teamField.lineEdit().setSelection(0,4)
print("selected text:"+self.entryForm.ui.teamField.lineEdit().selectedText())

prints "selected text:Team"

So that might be why many of the methods that affect selection are not working as expected (.deselect(), .setSelection(5,5), etc, and even some of the other methods give unexpected behavior, i.e. cursorForward(False,1) or cursorBackward(False,1) and such.

Original post:

This is for a radio log GUI, so keyboard interactions must be minimal and intuitive. openNewEntryForm (below) is called as a slot from a pushbutton on the main application GUI window:

self.ui.pushButton.clicked.connect(self.openNewEntryDialog)

It can also be called using a keyPressEvent in the same class:

def keyPressEvent(self,event):
    if type(event)==QKeyEvent:
        print("QKeyEvent:"+str(event.key()))
        if event.key()==Qt.Key_T:
            self.openNewEntryDialog('t')
        event.accept()
    else:
        event.ignore()

Here's the method in question:

def openNewEntryDialog(self,key=None):
    self.entryDialog=newEntryDialog()
    if key=='t':
        self.entryDialog.ui.to_fromField.setCurrentIndex(1)
        self.entryDialog.ui.teamField.setFocus()
        self.entryDialog.ui.teamField.setEditText("Team ")

    if self.entryDialog.exec_():
        self.newEntry(self.entryDialog.getValues()) # adds the log entry

so, the intended key press sequence is (from the main application GUI window):

a single keyboard press of 't' will open the entryForm, set the to_fromField to index 1 (which happens to be "TO"), give focus to teamField (also a QComboBox), set its text to "Team " and set itself up so that the very next keypress will appear as the text following "Team " in teamField.

So, starting from the main app GUI again, the plan is that typing 't3' should open the new entry window, set the to_fromField to "TO", and set the teamField to "Team 3", ready for a keypress of the tab key to move on to the next field in the entryForm.

The problem is that the teamField.setEditText("Team ") call leaves all of the text highlighted/selected, so that a subsequent key press of '3' would replace "Team " with "3"; I'm looking for a way to unhighlight/unselect "Team " but leave the cursor active at the right of that string, so that the subsequent key press of '3' would make the entire string "Team 3".

Ideas? Thanks in advance.


Solution

  • You can access the line-edit of the combo box, and then remove the selection:

        self.entryDialog.ui.teamField.setEditText("Team ")
        self.entryDialog.ui.teamField.lineEdit().deselect()
    

    UPDATE:

    The above code is correct, but it seems that the dialog will then clobber it when it initialises the focus handling for its child widgets after it is shown. If a dialog is opened with exec(), it will start its own event-loop, and some events (including focus events) will only be processed after it is fully shown. This is why it may appear that some changes made to child widgets before the dialog is shown are being ignored.

    One way to work around this is to use a single-shot timer to ensure the changes are only attempted after the dialog is shown.

    So add a method to the entry dialog class something like this:

        def resetUI(self, key):
            if key == 't':
                self.ui.to_fromField.setCurrentIndex(1)
                self.ui.teamField.setFocus()
                self.ui.teamField.setEditText('Team ')
                QtCore.QTimer.singleShot(0, self.ui.teamField.lineEdit().deselect)
    

    and then use it like this:

    def openNewEntryDialog(self, key=None):
        self.entryDialog = newEntryDialog()
        self.entryDialog.resetUI(key)
        if self.entryDialog.exec_():
            self.newEntry(self.entryDialog.getValues())