UI - Keyboard Input Code Question- Arrow Key Input? Tkinter, Gtk+?

Tags: #<Tag:0x00007f1c98fc43b8> #<Tag:0x00007f1c98fc4188>

Typical behavior with the arrow keys and collapsible lists are Left / Right Arrow will open/close the list.

Closed (Left Arrow)

Open (Right Arrow)

Up / Down Arrow moves the selection bar accordingly …

Down one Line (Down Arrow)

All good so far.

Following those actions, if the selection line is in a collapsible list section as shown above, pressing the Left Arrow should return the selection line to the next higher level of the collapsible list.

Using Finder as an example:

If I press Left Arrow with the selection line over folder.JPG

… the selection line will jump up to the next higher level.
…pressing it again will close / collapse that group.

Picard does not move the selection line on that Left Arrow input.

In this example I’m looking at the code for album.py, but the same behavior should be throughout the entire application. Does this need to be changed at each level or are there universal UI/keyboard input settings higher up in the source tree?

…and what am I looking for call wise, for intercepting keyboard input?

Like if I also want to add functionality so that Command-Q (CTRL-Q) or ESC can be used at the Quit Picard Dialog?

The keyboard can be used to fully exit from Picard, or ESC if you didn’t mean to enter Command-Q.

I Presume the code for the Quit is def show_quit_confirmation(self): in mainwindow.py

def show_quit_confirmation(self):
    unsaved_files = sum(a.get_num_unsaved_files() for a in self.tagger.albums.values())
    QMessageBox = QtWidgets.QMessageBox

    if unsaved_files > 0:
        msg = QMessageBox(self)
        msg.setWindowTitle(_("Unsaved Changes"))
        msg.setText(_("Are you sure you want to quit Picard?"))
        txt = ngettext(
            "There is %d unsaved file. Closing Picard will lose all unsaved changes.",
            "There are %d unsaved files. Closing Picard will lose all unsaved changes.",
            unsaved_files) % unsaved_files
        cancel = msg.addButton(QMessageBox.Cancel)
        msg.addButton(_("&Quit Picard"), QMessageBox.YesRole)
        ret = msg.exec_()

        if ret == QMessageBox.Cancel:
            return False

    return True

There (or course) are several ways to process keyboard inputs and do GUI calls… I’m not figuring out which way exactly Picard is doing UI within Python, to follow documentation against.

The way you describe it is actually how it works on Linux and Windows, but I can confirm that this does not work on macOS. I don’t know why the Qt developers decided that macOS should behave differently here. If this is a common UI pattern on macOS as well we could implement it.

To react to different key presses the corresponding widget class needs to override QtWidget.keyPressEvent. For the two itemview columns this could be done in the BaseTreeView in itemviews.py. For the file browser sidebar this is in filebrowser.py.

For manipulating the tree view the documentation in https://doc.qt.io/qt-5/qtreewidget.html is relevant.

ESC already works on all platforms, this is default functionality for Qt dialogs. Command-Q would be special, but I don’t see this being supported on other tools on macOS. Is this common? I kind of find it difficult that hitting Command-Q twice will close immediately. If one wants to implement custom behavior here the QMessageBox needs to be subclassed.

The Qt way :wink:

1 Like

Yes, it is. So it’s the Qt People… That’s it, Pass the buck. It’s been that way on Macintosh since the Exposure triangle / collapsible list was first introduced to the UI.

…furthering that, the horizontal scrolling of the window contents is something that is usually -not- defaulted to the arrow keys. With the left/right arrow, at least Picard is parsing that first, if the selection is on an expandable list line. It will do the list action first, then additional inputs will side scroll the window. That’s kind of a hybrid way to do it, but usually when you’re dealing with lists, the arrows don’t do any window scrolling.

As for the Command-Q, sure. There’s plenty of things I can quit without using the mouse. That’s what the confirmation when there are unsaved documents open is for- to make sure you actually wanted to do that. But if there’s no unchanged windows open, then most will just quit - no questions asked. As in not even display that dialog. There are some that will still confirm, which is what I presumed that Picard was doing, instead of just quitting without asking.

If there are new or changed documents open then you have to intentionally issue the quit command again, or cancel, save, and quit… and some will even combine a “Save Then Quit” option on there as well.

It’s pretty out of the ordinary to have to reach for the mouse to quit from applications.

Some, like e.g. Picard :wink:

So your proposal is if the quit confirmation dialog is shown the user can press Command-Q again and then it will really quit. And you say this is common behavior. My issue with this is that I can’t find a single app that behaves that way. Granted I’m no big macOS user, but the ones I tried (TextEdit, iTerm, Atom) don’t allow you to quit from the confirmation dialog using Command-Q. I don’t know if there is any other shortcut here. Google Chrome has some special behavior, it doesn’t show a dialog but a short message that tells the user to hold Command-Q, and holding Command-Q for a short time then really quits.

1 Like

Yes, you’re right. For some reason I had it on my mind that Picard was not doing this, and that ESC was not working from the quit dialog either. It absolutely was not previously for me though that might have been related to whatever I had going on previously by not using Python3 or something where I got different UI results running from source vs. running from a package.

Anyhow, what I’m trying to get down to is to be able to quit anyway without having to use the mouse, if I want to bail on those unsaved / changed items.

Typically an application with multiple document window support will tell you there are document(s) to be saved, would you like to review them, save and quit … or cancel.

The ones that only support a single session will ask to quit, save or cancel, and then there’s typically a way to quit anyway. That’s what I’ve got stuck in my mind with the quit dialog.

So there is no settled on definitive way to “quit anyway”, but some things allow to override the unsaved document(s) at the quit dialog. Others, yes, you’ll have to cancel, and close / delete each new / changed document before quitting. In Picard that would mean deleting or saving each item on the right that has unsaved changes.

So yes, I see where my “proposal” is ambiguous against the typical UI methods for the quitting. What I’m simply trying to do there is provide an option to quit anyway, just the same way that ESC will cancel the quit dialog. So I don’t have to reach for the mouse.

As for the arrow key behavior, that is typical UI behavior that could be applied to Picard as evidenced by the other two major platforms working that way. :slight_smile:

So for the Quit, I basically want to check for ESC -or- Q, Command-Q, whatever, so that I can actually just quit from Picard regardless.

I found some similar threads on this subject on other Python based projects. Why the Left Arrow does not jump up to the next parent level, when it’s on a sub level list.

Initial feedback there from other readers agrees that there is different behavior between macOS and Linux/Windows builds too.

I started a thread at qt.io on this as well.

I am totally fine with a keyboard shortcut to quit in this dialog if someone can tell me what other tools use. What I would rule out are:

  • ESC: This cancels a dialog by default. Binding this to quit would be very misleading
  • Command-Q: This would mean pressing Command-Q twice would immediately quit the application. Not a good choice, a) I haven’t found any other application doing this and b) I am sure if we have this we will get an issue report from someone accidentally hitting Command-Q twice and having lost all their changes because the quit dialog did not prevent the accidental closing.
1 Like

For ESC, I didn’t mean to do it against the Quit, since it already cancels… it’s solely down to a key to trigger the Q.

…and in the process of looking at UI guidelines I found that OS X has the option to use TAB to go between dialog buttons and then select them with Space or Return.

It’s just off by default. :wink:
I love how things morph over time.

So the whole thing with this thread basically boils down to Qt not passing the Left Arrow key through consistently across all platforms. Which I’m afraid is just going to end up being finger pointing contest despite the various threads that can be found discussing this.


I find that most such dialogs allow me to either TAB or RIGHT/LEFT ARROW to the “Quit” option and then just ENTER. Some even have it active on “Quit” by default so I can just Ctrl-Q → Enter.

…and Picard pretty much does that now except that there is no way to use a keyboard input method to do it*

Command-Q with no unsaved/new documents? Just quit like it does.
Command-Q with unsaved/new documents? Cancel or Quit anyway.

  • Various developers/applications may take it on themselves to deal with the TAB & LEFT/RIGHT arrow intuitive way of doing so, it’s not consistent. OTOH, the OS does provide a way to manipulate the buttons globally via keyboard as well as pointing device. It’s just not enabled by default.

Ok, maybe I misunderstood this entire discussion a but or I am still confused. I always thought this was about adapting to a specific shortcut that is typical for macOS applications which have those exit dialogs. But this seems to not exist, so any shortcut would be fine, right?

Then this is basically the issue reported at https://tickets.metabrainz.org/browse/PICARD-1312 . Basically the configured shortcuts for dialog buttons are not working on macOS for some yet unknown reason. On other systems the Quit buttons can be triggered with Alt+Q

It’s Alt+Q for Picard. You can always trigger the underlined characters on buttons and menu items with Alt+[underlined char]

Yes, that was basically it. Now that I can read a little more into what is going on at the code level, that ticket seals the deal. The code supports it already, it’s just not happening.

Which could it be the same issue as the Left Arrow not working in list view?

No, it’s different. I found out the reason: Having so called “mnemonics” for menu items and buttons is intentionally not part of macOS. On Windows and most Linux interfaces this is a common UI paradigm: UI elements indicate the key with which you can trigger it by underlining the corresponding character (e.g. the button will show the “Q” of “Quit” underlined). Pressing Alt+[underlined character] will trigger this interface element. The upside of this is that keyboard shortcuts are directly discoverable in the UI and they correspond to a character that is actually part of the label and are automatically localized. The downside is that the shortcuts are localized, so you have different shortcuts in different languages. E.g. quitting in the quit confirmation dialog will be done with Alt+q when The UI is in English (with “q” for “Quit”), but with Alt+b when the UI is in German (with “b” for “beenden”).

macOS UI guidelines discourage the use of mnemonics (I don’t know why, but probably the language thing above is one reason) and hence Qt disables it by default. Instead explicit keyboard shortcuts need to be defined for every action. But we can opt-in to enable support for mnemonics even on macOS. Qt will still not show the underlines, but the shortcuts will work. I think we should do this, otherwise we would need to run through the entire app and define extra shortcuts for every possible button just for macOS.

Would that enabling be something set in the config during the build?

The reason I related it to the same issue as the Left Arrow is that could it be that the Left Arrow behavior was influenced along the same thought process as this has been left disabled for macOS, but still doable.

This arrow key thing is driving me to obsession … further digging after trying to manually fix it myself and I find this:

So much for “Any Platform” if you have to put code in to do something that should be happening.

Maybe using the left arrow to quickly close a node is very seldomly used in the real world.