Correct way to set scrollbar position in python tkinter - python

I am making a basic text editor and I am saving the scroll position in a file on closing the program. Then when opening the program it will read the scroll position from the file and update it so you can continue where you left off.
I can get the position fine from scrolledtext.yview() which returns a tuple with e.g. (0.42, 0.75)
But I cannot figure out how to change the scroll position. I have tried scrolledtext.vbar.set(0.42, 0.75) to try and update it but that doesn't work as in it doesn't do anything and gives no errors. I have also tried scrolledtext.yview(0.42, 0.75) but it says TclError: bad option "0.42": must be moveto or scroll so if anyone knows how to update it that would be greatly appreciated, cheers.
Edit(Code):
import tkinter as tk
root = tk.Tk()
Frame = frame(root)
Frame.pack()
textbox = ScrolledText(Frame)
textbox.pack()
textbox.yview() #this is saved to file, produces tuple of e.g. (0.42, 0.75)
textbox.vbar.set(0.3, 0.7) #this doesn't produce any errors but doesn't change the scroll position
textbox.yview(0.3, 0.7) #this is also something i have tried but produces the error _tkinter.TclError: bad option "0.4243827160493827": must be moveto or scroll
root.mainloop()

You can't expect the saved yview to work in all cases. If the file has been edited, the proportions could be all wrong.
The tuple you get from yview represents the fraction visible at the top and the fraction visible at the bottom. You can call yview_moveto to set the position at the top, and then let tkinter take care of the fraction at the bottom.
For example, if the yview you've saved is (0.42, 0.75), then you just need to call yview_moveto('0.42'). This will cause the view to be adjusted so that the given offset is at the top of the window.

In case of widgets update with change bbox sizes, i use a followed snippet to keep scroll position:
#before repaint store vsb position caclulated in pixels from top
bbox = canvas.bbox(ALL)
self.mem_vsb_pos = canvas.yview()[0] * (bbox[3] - bbox[1])
#after repaint (back calculation):
bbox = canvas.bbox(ALL)
canvas.yview_moveto(self.do_vsb_pos / (bbox[3]-bbox[1]))
#before repaint - if need repaint from top
self.mem_vsb_pos = 0.0

Related

Is there an easy and effective way to see if a tkinter Text widget is full?

I have a program that gets some some text from a website (link) then outputs it into a Text widget.
But I want a way to check if the text exceeds the Text widget so then I can call my clear_Text_Box() function.
Is there any way to do this?
I searched up things like: tkinter how to know if my TEXT box is full, or tkinter Text box methods, but it gives me unrelated things such as how to create full screen window in tkinter and so on.
My Text widget is (width = 30,height = 70).
My clear_Text_Box() function:
def clear_Text_Box(Text_Box): # I have this function, so it clears the Text box if text
# exceeds the Text box.
Text_Box.config(state = 'normal')
Text_Box.delete('0.0','end')
Text_Box.config(state = 'disabled')
Knowing if it's full can be a bit tricky at the very edge (ie: knowing if it's possible to squeeze in one more space or "i" character", but knowing whether or not some text has scrolled out of view is easy.
You can call the yview and xview methods of the widget each will return a tuple of two fractions: the amount above (or to the left) the viewable region and the amount below (or to the right). From the official documentation for the yview method:
The first element gives the position of the first visible pixel of the first character (or image, etc) in the top line in the window, relative to the text as a whole (0.5 means it is halfway through the text, for example). The second element gives the position of the first pixel just after the last visible one in the bottom line of the window, relative to the text as a whole.
So, if you call yview and xview and they return (0.0, 1.0) then everything in the widget is visible.
if Text_Box.xview() == (0.0, 1.0) and Text_Box.yview() == (0.0, 1.0):
# all text is visible
...

Tkinter | How to detect tkinter scrollbar position near the end

I know that you can get the scrollbar position if I bind the frame the scrollbar is controlling to a function (onFrameConfigure) like this: self.calendar_frame.bind("<Configure>", self.onFrameConfigure), and from the event in the onFrameConfigure(self, event): I can get some x value of the scroll bar event.x. I thought the value was showing the location of the scrollbar in px so with that logic if I scroll to the maximum right side, I should get the same value (or similar value) as the width of the scrollbar but from the test below I got otherwise:
event.x: -640 | scrollbar.winfo_width(): 382
event.x: -415 | scrollbar.winfo_width(): 607
event.x: -245 | scrollbar.winfo_width(): 777
event.x: -713 | scrollbar.winfo_width(): 309
(the result above are only when the scrollbar is to the maximum right)
Don't see any way to use those value to determine whether the scrollbar is at the right end.
My question is: how can I detect when the scrollbar reached the end of one of the end-of-the-scrollbar?
Even better would be to detect when the scrollbar is near the end because for my purpose (read the purpose of my project here: here) I need something to trigger right before the scrollbar reach it maximum end.
There is a simple get method for tkinter scrollbar (more info) which can be used like this: scrollbar.get(). Whenever the scrollbar is moved by the user, I would just check with if scrollbar.get()[1] > 0.9:. With this method, I can execute my stuff when the scrollbar is closing in to the rightmost position.
If you're wanting to know when the scrolled widget is scrolled toward the edge, the simplest solution is to call a custom command rather than calling the set method of the associated scrollbar. This command can itself call the set method, and then do whatever else you want it to do.
This command will be passed two parameters which represent the range of the viewport into the scrolled widget. The numbers are floating point numbers between 0 and 1, though they are passed as strings rather than floats. The first number represents the first visible part of the widget, and the second number represents the last part of the visible widget.
For example, normally you would do something like this to wire a canvas and scrollbar together:
canvas = tk.Canvas(...)
scrollbar = tk.Scrollbar(..., command=canvas.xview)
canvas.configure(xscrollcommand=scrollbar.set)
Instead, create your own command which calls the scrollbar.set method and then does whatever else you want it to do. It would look something like this:
def handle_scroll(x0, x1):
hsb.set(x0, x1)
if float(x1) > .9:
...
canvas = tk.Canvas(...)
scrollbar = tk.Scrollbar(..., command=canvas.xview)
canvas.configure(xscrollcommand=handle_scroll)

Tkinter - change location of treeview

I use tkinter for an assigment with a treeview:
tree = ttk.Treeview(root)
tree["columns"]=("one","two","three")
tree.column("one", width=100 )
tree.column("two", width=100)
tree.column("three", width=120)
tree.heading("one", text="3")
tree.heading("two", text="2")
tree.heading("three", text="1")
tree.place(x=0,y=0)
then it places it at the top left corner where it should be.
But whenever I start writing using this
tree.insert("", 0, values=(1, 2, 3))
it goes to a y axes of 0 and a x axes that is in the middle of screen.
Is there any way to fix it so that it stays on the top left corner?
To explain the query you had in the comments above.
.pack() differs from .place() when it comes to positioning of the widgets. For a start .pack()'s default position for displaying widgets is the top, middle of it's parent if it is the first widget to be default packed in the parent, else it's the middle of the bottom edge of the last widget to be default packed in the parent. Where calling .place() without any attributes will not actually draw the widget visibly on the screen, instead you must clarify a position for the widget (there are several sets of attributes which can be used for this with .place()).
This is why using .pack() on the widget instead of .place() caused it to move to the top middle of the screen.
If there are any others out there that are having troubles when you write .place and it just stays in the same location, it might be because later on in the code you have another .place referring to the same object that overrides the original placement. Just do a control + f and type in object_name.place( and see if any other lines appear.

Any help on placing buttons with Tkinter in Python?

UPDATE - I got it to work by using the .grid() function in Tkinter. Thanks for all the help!
I would like to make a button that will center itself in the middle of the GUI in Tkinter, but I have tried using the place() function and also, the pack() function will not work. Any tips or advice?
A section of my code:
restart = Button(tk, text = "Restart", command = restartGame)
restart.pack()
#The code to place the button in the middle goes here
I rarely ever recommend using place, but if you literally only have a single widget that you want to put in the center of some other widget, place is a really good choice:
restart.place(relx=.5, rely=.5, anchor="center")
relx sets the relative x coordinate to be the middle (it is a floating point value between 0.0 and 1.0)
rely sets the relative y coordinate to be the middle
anchor specifies that the center of the widget should be at the x/y coordinate
I assume you mean centering the button at the middle of the top/bottom.
You can use .pack(side = "bottom") to place the button at the bottom (middle) of the Tk window.
Using side =, you can define it as top, bottom, left, or right.
So this means that your code would look like:
restart = Button(tk, text = "Restart", command = restartGame)
restart.pack(side = "bottom")

Tkinter: Determine Widget Position relative to Root Window

The tkinter MouseWheel event can only be bound to the root window. Thus the event position is also relative to the root position.
For a canvas zooming operation, I would like to get the MouseWheel-event with information about the cursor-position within the canvas. To calculate this I thought I would simply subtract the position of the canvas within the root window. Problem now is, that I can not find out the canvas position in the window.
I tried:
can.grid_bbox --> (0,0,0,0) ?
can.grid_info
can.grid_location
cget("offset")
pointerx
and some others I can't remember. Somehow I keep on missing it could someone give me a hint?
---edit---
To get the position of the mouseWheel event relative to the canvas, this approach seems to work:
def on_mouse_wheel(self, event):
xCan = event.x_root - self.can.winfo_rootx()
yCan = event.y_root - self.can.winfo_rooty()
You want to use the winfo_x and winfo_y methods to get the x/y position relative to the parent.

Categories

Resources