i am building a python tkinter editor / IDE and want to create a widget that can be clicked on to collapse / expand elided text.
i am not sure what widget is best to use (button, label, image).
think of a method similar to visual studio code. they have a widget to the left of the text box line that is clickable. i want something similar using python tkinter.
i currently have 2 text boxes side by side. the narrow left one is for the line number and the larger right one is for the code text that you can edit.
when i type a keyword like 'Function' i want a widget to be dynamically created that will control elided text so i can collapse / expand the text inside this function.
i already know how to set tags for the area to be elided and know how to check for key words. i just don't know where to put the widget or what type it should be.
here is a small example of trying to use buttons as control:
import tkinter as tk
root = tk.Tk()
root.title("Testing widgets for Elide")
# create 'line number' text box
line_text = tk.Text(root, wrap="none", width=5, insertwidth=0) # don't want the cursor to appear here
line_text.pack(fill="y", side="left")
# create 'code' text box
text_box = tk.Text(root, wrap="none")
text_box.pack(fill="both", expand=True, side="left")
# add a tag to line number text box (need text to be at the right side)
line_text.tag_configure("right", justify="right")
# add some text into the text boxes
for i in range(13):
line_text.insert("end", "%s \n" % (i+1))
line_text.tag_add("right", "1.0", "end")
for i in range(13):
text_box.insert("end", "%s \n" % ("some text here at line number #" + str(i+1)))
# add button to use as elide +/- (inside text boxes? _ not sure which widget is correct (button, label, image)? _ also can't work out spacing so button doesn't interfere with text)
elide_button = tk.Button(text_box, text="-")
text_box.window_create("11.0", window=elide_button) # *** test ***
elide_button2 = tk.Button(line_text, text="-")
line_text.window_create("11.0" + "lineend-1c", window=elide_button2) # *** test ***

screen shot of testing buttons
i have tried using a button inside the main text box with a "-" character but it was too big and pushed the main text line over to fit in. i don't want this.
i tried using a button in the narrow line number text box (at the line end) but it also pushed the text over the other way. i don't want this.
basically i don't want the text interfered with by the button. is there some kind of black magic or spacing i need to know about, lol?
i am still newish to python & newish to tkinter. i tried searching for this but couldn't find what i wanted.
this is an update of the original code above. the original was a left side text box and right side text box. this update added an extra text box in-between these for the elide widget.
this way the new elide widget won't interfere with the text in the original 2 text boxes.
line_textR = tk.Text(root, wrap="none", width=2, insertwidth=0)
line_textR.pack(fill="y", side="left")
notice the order in which this line is added in code below because it is using pack. first pack line text box, then pack elide widget text box, then pack code text box. i also added insertwidth=0 to prevent the cursor appearing in this elide widget text box.
then i filled the elide text box with blank spaces so when the widget is placed in position it can use line numbers + character for it's position.
for i in range(13):
line_textR.insert("end", " \n")
then i added a button to be used as a control widget for the elided text (the button is non functional here. it would normally be dynamically added in code for when it is needed).
elide_button = tk.Button(line_textR, text="-")
line_textR.window_create("11.0", window=elide_button)
the button is just a test to have a control for the elided text. it can also be a label or even an image (made clickable) or even clickable text.
below is the updated code:
import tkinter as tk
root = tk.Tk()
root.title("Testing widgets for Elide")
# create 'line number' text box
line_text = tk.Text(root, wrap="none", width=5, insertwidth=0) # don't want the cursor to appear here
line_text.pack(fill="y", side="left")
# added > create elide button text box
line_textR = tk.Text(root, wrap="none", width=2, insertwidth=0) # don't want the cursor to appear here
line_textR.pack(fill="y", side="left")
# create 'code' text box
text_box = tk.Text(root, wrap="none")
text_box.pack(fill="both", expand=True, side="left")
# add a tag to line number text box (need text to be at the right side)
line_text.tag_configure("right", justify="right")
# add some text into the text boxes
for i in range(13):
line_text.insert("end", "%s \n" % (i+1)) # add line numbers into line text box (now on the left side)
line_text.tag_add("right", "1.0", "end")
for i in range(13):
text_box.insert("end", "%s \n" % ("some text here at line number #" + str(i+1))) # add some text int the main text box (now on the right side)
for i in range(13):
line_textR.insert("end", " \n") # add blank space on each line for the elide widget text box _ this allows for widget placement by line number (now in the middle)
# add button to use as elide +/- (inside text boxes? _ not sure which widget is correct (button, label, image)?
elide_button = tk.Button(line_textR, text="-")
line_textR.window_create("11.0", window=elide_button) # *** test ***
root.mainloop()

My suggestion is to use the unicode character \u25b6 which renders as ▶, and \u25bc which renders as ▼. If you are on a line that needs the character, insert one of those otherwise insert a space. If you use a fixed-width font for the line numbers, everything will line up nicely.
When adding it to a text widget you can give it a tag, and then bind to the tag so that when you click on the character it calls a function. You can then use the x,y coordinate of the event to know which line was clicked on.
In the click method you can replace ▶ with
It looks something like this:
def elide_button(event):
lineno = line_text.index(f"@{event.x},{event.y}").split(".")[0]
print(f"elide button was clicked on line {lineno}")
line_text.tag_bind("elide_btn", "<1>", elide_button)
line_text.insert("11.0 lineend", "\u25b6", "elide_btn")
Note: you only have to call tag_bind once per unique tag.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With