LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 03-29-2021, 08:57 AM   #1
RockDoctor
Senior Member
 
Registered: Nov 2003
Location: Minnesota, US
Distribution: Fedora, Ubuntu, Manjaro
Posts: 1,791

Rep: Reputation: 427Reputation: 427Reputation: 427Reputation: 427Reputation: 427
tkinter Treeview disable cursor response


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?
Attached Thumbnails
Click image for larger version

Name:	Window1.png
Views:	57
Size:	23.3 KB
ID:	35954  

Last edited by RockDoctor; 03-29-2021 at 10:28 AM. Reason: add screenshot
 
Old 03-29-2021, 12:07 PM   #2
boughtonp
Senior Member
 
Registered: Feb 2007
Location: UK
Distribution: Debian
Posts: 3,596

Rep: Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545

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.


Last edited by boughtonp; 03-29-2021 at 12:08 PM.
 
Old 03-29-2021, 09:17 PM   #3
RockDoctor
Senior Member
 
Registered: Nov 2003
Location: Minnesota, US
Distribution: Fedora, Ubuntu, Manjaro
Posts: 1,791

Original Poster
Rep: Reputation: 427Reputation: 427Reputation: 427Reputation: 427Reputation: 427
Quote:
Originally Posted by boughtonp View Post
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.
 
Old 03-30-2021, 07:51 AM   #4
boughtonp
Senior Member
 
Registered: Feb 2007
Location: UK
Distribution: Debian
Posts: 3,596

Rep: Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545

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:
Code:
treeview.bind('<KeyPress-Down>',self.set_selection)
treeview.bind('<KeyRelease-Down>',self.reset_selection)
treeview.bind('<KeyPress-Up>',self.set_selection)
treeview.bind('<KeyRelease-Up>',self.reset_selection)

def set_selection(self,event):
	self.current_selection = event.widget.selection();

def reset_selection(self,event):
	event.widget.selection_set(self.current_selection)
Still not ideal, but perhaps simpler than what you're describing?

 
1 members found this post helpful.
Old 03-31-2021, 08:33 AM   #5
RockDoctor
Senior Member
 
Registered: Nov 2003
Location: Minnesota, US
Distribution: Fedora, Ubuntu, Manjaro
Posts: 1,791

Original Poster
Rep: Reputation: 427Reputation: 427Reputation: 427Reputation: 427Reputation: 427
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
 
Old 03-31-2021, 11:08 AM   #6
boughtonp
Senior Member
 
Registered: Feb 2007
Location: UK
Distribution: Debian
Posts: 3,596

Rep: Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545Reputation: 2545
Quote:
Originally Posted by RockDoctor View Post
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.)


In summary...

Code:
#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk

class MainWindow(tk.Frame):

   def __init__(self,master=None):
      tk.Frame.__init__(self,master)
      self.grid()

      treeview = ttk.Treeview(self)

      treeview.insert('','end','1',text='one')
      treeview.insert('','end','2',text='two')
      treeview.insert('','end','3',text='three')
      treeview.insert('','end','4',text='four')
      treeview.insert('','end','5',text='five')
      treeview.grid()

      treeview.bind('<KeyPress-Down>',self.set_selection)
      treeview.bind('<KeyPress-Up>'  ,self.set_selection)
      treeview.bind('<KeyRelease-Down>',self.move_item_down)
      treeview.bind('<KeyRelease-Up>'  ,self.move_item_up)

   def set_selection(self,event):
      self.current_selection = event.widget.selection();
      event.widget.configure(selectmode=tk.NONE)

   def reset_selection(self,event):
      event.widget.selection_set(self.current_selection)
      event.widget.focus(self.current_selection)
      event.widget.configure(selectmode=tk.BROWSE)

   def move_item_up(self,event):
      self.reset_selection(event)
      event.widget.move(self.current_selection,'',event.widget.index(self.current_selection)-1)

   def move_item_down(self,event):
      self.reset_selection(event)
      event.widget.move(self.current_selection,'',event.widget.index(self.current_selection)+1)

MainWindow().mainloop()
 
1 members found this post helpful.
Old 03-31-2021, 01:00 PM   #7
RockDoctor
Senior Member
 
Registered: Nov 2003
Location: Minnesota, US
Distribution: Fedora, Ubuntu, Manjaro
Posts: 1,791

Original Poster
Rep: Reputation: 427Reputation: 427Reputation: 427Reputation: 427Reputation: 427
Thank you.

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.
 
Old 04-07-2021, 08:57 PM   #8
RockDoctor
Senior Member
 
Registered: Nov 2003
Location: Minnesota, US
Distribution: Fedora, Ubuntu, Manjaro
Posts: 1,791

Original Poster
Rep: Reputation: 427Reputation: 427Reputation: 427Reputation: 427Reputation: 427
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.
 
Old 04-10-2021, 01:31 PM   #9
RockDoctor
Senior Member
 
Registered: Nov 2003
Location: Minnesota, US
Distribution: Fedora, Ubuntu, Manjaro
Posts: 1,791

Original Poster
Rep: Reputation: 427Reputation: 427Reputation: 427Reputation: 427Reputation: 427
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)
 
  


Reply

Tags
python, tkinter, treeview



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
regarding gtk treeview swift2008 Programming 0 09-05-2008 12:18 AM
treeView and listView chrisliando Programming 1 11-09-2007 05:41 AM
PyGTK TreeView segmentation fault on expand_all() Chrax Programming 1 08-08-2006 05:18 AM
Error in basic button response program in Python 2.4 with the Tkinter module jojotx0 Programming 1 05-23-2006 07:43 PM
TreeView Context Menu Kenji Miyamoto Programming 0 04-18-2006 11:14 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 05:27 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration