Home > Mobile >  Tkinter : Text highlight appears while pressed but disappear next
Tkinter : Text highlight appears while pressed but disappear next

Time:10-26

I've been working on a Tkinter (Python) project that displays a list of strings using the Text widget recently, but I ran into an issue I couldn't manage to solve : On startup, I want the first line to be highlighted, and when I click on up/down arrows, the highlight goes up/down, as a selection bar. I succeed to do that, but the problem is that the highlight only appears when arrows are pressed, and when they are released, it disappear. I'd like it to stay even when I'm not pressing any key.

Here is my code :

class Ui:
  def __init__(self):
    # the list I want to display in Text
    self.repos = repos

    # here is the entry keys are bind to
    self.entry = Entry(root)
    self.entry.pack()
    self.bind('<Up>', lambda i: self.changeIndex(-1))
    self.bind('<Down>', lambda i: self.changeIndex(1))

    # here is the Text widget
    self.lists = Text(root, state=NORMAL)
    self.lists.pack()
    
    # inits Text value
    for i in self.repos:
      self.lists.insert('insert', i   '\n')
    self.lists['state'] = DISABLED

    # variable I use to navigate with highlight
    self.index = 0
    self.lists.tag_add('curr', str(self.index)   '.0', str(self.index   1)   '.0') # added   '.0' to make it look '0.0' instead of '0'
    self.lists.tag_config('curr', background='#70fffa', background='#000000')

    self.root.mainloop()

  def changeIndex(self, n):
    # error gestion (if < 0 or > len(repos), return)
    self.lists.tag_delete('curr')
    self.lists.tag_add('curr', str(self.index)   '.0', str(self.index   1)   '.0')
    self.index = self.index   n
    # to make it scroll if cannot see :
    self.lists.see(str(self.index)   '.0')

I haven't seen any similar problem on Stack, so I asked, but do not hesitate to tell me if it is a duplicate.

Do you guys could help me please ? Thanks !

EDIT: Here is the full code if you want to give it a try : https://github.com/EvanKoe/stack_tkinter.git

EDIT : I added the main.py file (the """backend""" file that calls ui.py) to the demo repository. This way, you'll be able to run the project (be careful, there are "YOUR TOKEN" and "YOUR ORGANIZATION" strings in main.py you'll have to modify with your own token/organization. I couldn't push mine or Github would've asked me to delete my token)

CodePudding user response:

The following code should do what you expect. Explanation below code

from tkinter import *

repos = ["one","two","three","four"]

class Ui:
  def __init__(self, parent):
    # the list I want to display in Text
    self.repos = repos

    # here is the entry keys are bind to
    self.entry = Entry(parent)
    self.entry.pack()
    self.entry.bind('<Up>', lambda i: self.changeIndex(-1))
    self.entry.bind('<Down>', lambda i: self.changeIndex(1))

    # here is the Text widget
    self.lists = Text(parent, state=NORMAL)
    self.lists.pack()

    
    
    # inits Text value
    for i in self.repos:
      self.lists.insert('insert', i   '\n')
    self.lists['state'] = DISABLED

    # variable I use to navigate with highlight
    self.index = 1
    self.lists.tag_add('curr', str(self.index)   '.0', str(self.index   1)   '.0') # added   '.0' to make it look '0.0' instead of '0'
    self.lists.tag_config('curr', background='#70fffa', foreground='#000000')



  def changeIndex(self, n):
    print(f"Moving {n} to {self.index}")
    self.index = self.index   n
    self.index = min(max(self.index,0),len(self.repos))
    self.lists.tag_delete('curr')
    self.lists.tag_config('curr', background='#70fffa', foreground='#000000')
    self.lists.tag_add('curr', str(self.index)   '.0', str(self.index   1)   '.0')
    # to make it scroll if cannot see :
    self.lists.see(str(self.index)   '.0')

root = Tk()
ui = Ui(root)
root.mainloop()

Few changes made to your code

  • Changed the Ui function to accept the parent tk object as a parameter
  • Changed self.index to be initialised to 1 rather than 0 since the first line on a text box is 1 not 0
  • Bound the Up/Down keys to the entry box. Not sure why this is what you are going for but this seems to be what your comments indicate
  • Added some checking code to limit the index value between 1 and len(repos)
  • Re-created the tag style each time it is set since you delete the tag (this is why it wasn't showing)

I'd suggest that you look to bind the up/down button press to the text box rather than the entry box. Seems a bit strange to have to select a blank entry box to scroll up and down in a list.

Also, why aren't you just using the build in Tkinter list widget?

CodePudding user response:

I finally managed to solve the problem, and it was due to my event bindings. I made the decision (to improve the UX) to bind up/down arrows on the top Entry instead of binding em on the Text widget. I bind 4 events :

  • Up arrow => move highlight up,
  • Down arrow => move highlight down,
  • Return key => calls get_name(), a function that returns the selected option,
  • Any other Key => calls repo_filter(), a function that updates the displayed options in the Text widget, according to what has been typed in the Entry.

The problem was that pressing the up/down arrow was triggering "up/down key" event AND "any other key" event, so the highlight was removed since the Text value was refreshed.

To solve this problem, I just had to verify that the pressed key was neither up nor down arrow in the "any other key" event callback :

def repo_filter(evt):
  if evt.keysym == 'Up' or evt.keysym == 'Down': # verify that pressed key
    return                                       # isn't '<Down>' or '<Up>'
  # filter Text widget

Also, I am sorry I didn't give you all the code at the beginning, because, indeed you couldn't guess about those event bindings.

Thanks to everyone who tried to help me !

  • Related