ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I've got a little program that draws diagrams based on the selection in a treeview. Selection is only done with the mouse. I use the left/right arrow keys to do horizontal translations, and would like to use the up/down arrows to do vertical translations without moving the selection bar in the treeview. I thought something like
Code:
self.bind('<Key>', self.do_press)
# --------
def do_press(self, event):
""" The idea is to effectively ignore keyboard Up/Down """
pass
would do the trick, but the treeview still responds to the up and down arrows.
I have a working kludge that involves hidden first and last rows in the treeview and making do_press() respond by moving up, then down (or vice-versa) to get back to its original position. Definitely not elegant. Is there a better way?
Last edited by RockDoctor; 03-29-2021 at 10:28 AM.
Reason: add screenshot
Tk event handlers occur after the event; afaict there is no "prevent default" functionality (like HTML/JS has).
Easiest option would be to retain existing keys and use different ones for moving items (e.g. -/+ or PgUp/PgDown).
Otherwise, a workaround might be to create an invisible control which you bind the keyboard events to, then bind an click handler to the Treeview to re-assign focus to the invisible control after mouse selections.
Easiest option would be to retain existing keys and use different ones for moving items (e.g. -/+ or PgUp/PgDown).
Thanks for the response. My first implementation actually used PgUp/PgDown. It has the advantage of being very simple, and seemed logical enough given that +/- is already used for magnification. I've made multiple attempts to keep the Treeview from grabbing the Up/Down keys, all unsuccessful. At this point, I'm probably just being stubborn, but I'm determined to make the program do what I want, and with code that doesn't look like a complete hack.
I can understand that feeling, but looking at the Treeview source I don't think this is something that can be handled on the Python side, since there's no key bindings in that class.
Something which didn't work - but would probably have been the method to use if it did - would be calling .unbind("<Key-Down>") on the treeview. (Unless there's some way to call that on the internal C code or wherever the actual widget is implemented, but I doubt that).
It might be possible to re-create your own version of the widget in Python - there's a file tree example here that could be simplified, but dunno whether you'd consider that approach cleaner than your current kludge.
Actually, I've just noticed you said:
Quote:
I have a working kludge that involves hidden first and last rows in the treeview and making do_press() respond by moving up, then down (or vice-versa) to get back to its original position. Definitely not elegant. Is there a better way?
I'm not sure what the hidden rows are for?
A potentially cleaner hack might be along the lines of:
I found I could not prevent a press of the Down key from moving the selection bar down one row, or the Up key from moving the selection bar up one row. I tried everything I could think of, but there seems to be no way to mask or even cleanly divert the actions of those keys on the treeview.
By trial and error, I found that the actions in do_press() take place prior to the expected up/down movement due to the keypress. Hence, when sitting on the item in what appears to be the top row in the treeview, when I pressing the Down key, the selection is first moved up, then back down. Without the hidden top row, there is no movement upward, and the Down key action ends up moving the selection bar down one row. Similarly, the hidden bottom row accommodates the action of the Up key.
In a quick trial of your proposed replacement event handlers, they didn't quite do what I expected. Click on row 6. Press up arrow. Selection jumps to row 5 then back to row 6. Press up arrow again. Selection jumps to row 4 then back to row 6. Not quite the visual effect I want
In a quick trial of your proposed replacement event handlers, they didn't quite do what I expected. Click on row 6. Press up arrow. Selection jumps to row 5 then back to row 6. Press up arrow again. Selection jumps to row 4 then back to row 6. Not quite the visual effect I want
Which suggests there is internal state which isn't being modified, otherwise it wouldn't jump to 4 the second time. Definitely a bug, but no idea whether it's with Python/tkinter or on the tk side of things.
Anyhow, disabling and re-enabling the selection with "selectmode=tk.NONE and "selectmode=tk.BROWSE" works around that, stopping the blue selection from jumping.
The dotted outline still does - but that can be kept in sync by adding a call to .focus when setting the selection, so when moving the items it simply jumps ahead momentarily. (Not sure if it can be hidden with styling to remove that distraction.)
I decided to adopt your code. The modifications allowed me to eliminate both the very awkward handling of <Up> and <Down> keypresses, the extra code needed to insert and deal with the detached top and bottom rows that were being added, and the docstrings that made sense to me now, but might not next year if I choose to come back to this program.
I could not leave it alone. Using PageUp/PageDown (Prior/Next) and Left/Right to move the image, and Up/Down to change the selected row in the Treeview, the code for the Treeview now is as follows:
Code:
class PartTree(ttk.Treeview):
def __init__(self, parent=None):
super().__init__(parent, columns=('Part1', 'Part2'))
...
# Force Treeview not to respond to PgDn/PgUp
self.unbind_class('Treeview', '<KeyPress-Next>')
self.unbind_class('Treeview', '<KeyPress-Prior>')
self.unbind_class('Treeview', '<KeyPress-Next>')
self.unbind_class('Treeview', '<KeyPress-Prior>')
# grab focus when Up/Dn released (after highlight bar has moved)
self.bind_class('Treeview', '<KeyRelease-Up>', self.set_selection)
self.bind_class('Treeview', '<KeyRelease-Down>', self.set_selection)
# ----------------------------------
def set_selection(self,event):
""" Set focus to currently highlighted partitioning """
self.current_selection = event.widget.selection();
self.focus(self.current_selection)
Unlike my original version, this one allows scrolling through the Treeview to select a partitioning.
Treeview no longer handles any keyboard events that might scroll the tree. The code that calls the PartTree.scroll_the_tree() comes from the event handler for clicking the mouse or pressing (actually releasing) the PageUp and PageDown keys in the main App class
Code:
class PartTree(ttk.Treeview):
""" Using code based on discussion with boughtonp at
https://www.linuxquestions.org/questions/showthread.php?p=6235877#post6235877
to not change selection when using <Up> and <Down>
"""
def __init__(self, parent=None):
super().__init__(parent, columns=('Part1', 'Part2'))
self.heading('#0', text='Index')
self.heading('#1', text='Part1')
self.heading('#2', text='Part2')
self.column('#0', width=50)
self.column('#1', width=80)
self.column('#2', width=120)
self.tag_configure("0", background="#ffcfff")
# Force Treeview not to respond to Up/Down
self.unbind_class('Treeview', '<KeyPress-Down>')
self.unbind_class('Treeview', '<KeyPress-Up>')
self.unbind_class('Treeview', '<KeyPress-Down>')
self.unbind_class('Treeview', '<KeyPress-Up>')
# Force Treeview not to respond normally to PgDn/PgUp
self.unbind_class('Treeview', '<KeyPress-Next>')
self.unbind_class('Treeview', '<KeyPress-Prior>')
self.unbind_class('Treeview', '<KeyRelease-Next>')
self.unbind_class('Treeview', '<KeyRelease-Prior>')
self.selected_row = '0'
# ----------------------------------
def scroll_the_tree(self, key):
""" Finally got this working! Tree will scroll using the
PgUp ('Prior') and PgDown ('Next') keys.
"""
delta = -1 if key=='Prior' else 1
knt = len(self.get_children())
int_cs = int(self.selected_row)
int_cs += delta
int_cs = max(0, int_cs)
int_cs = min(int_cs, knt-1)
self.selection_set(int_cs)
self.focus(int_cs)
self.see(int_cs)
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.