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.
Related
I need to find the position of a widget relative to the window.
winfo_x returns the position relative to the widgets parent, this is a problem if the widget is in a frame.
winfo_rootx returns the position relative to the screen, not the window.
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)
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
Is it actually possible to place widgets at specific coordinates in a Tkinter window? For example, if i set up a window like so...
class LogInWindow(object):
def __init__(self):
#create variables
self.currentUser = StringVar()
#create the window and frame
self.LW = Toplevel()
self.LW.title('Login')
self.LW.geometry('310x100-500+300')
self.LW.resizable(width=False, height=False)
self.LWFrame = ttk.Frame(self.LW)
Creating a fixed window 310 pixels wide and 100 pixels high. How would I then place a button at say x=120,y=62?
I've explored the pack and grid documentation but cannot seem to find anything useful.
There's the less well known place geometry manager.
In your case you'd simply create the button and place it at the coordinates you want.
b = tk.Button(self.LW,text='Button')
b.place(x=120,y=62)
The reason people typical avoid 'place' is that it isn't automatically responsive to things like design changes or window resizes in the same way that pack and grid are.
You might be better off using relx and rely and the anchor options to express the position in terms of fractions of the window rather than specifying an absolute position to avoid some of these disadvantages.
The title says it all really, I have a piece of code in which I would like to move the top-level in relation to its old position.
To do this I need to fetch the current position of the window, then set the new position using root.geometry('+x+y'). How can I find the current position of a TopLevel?
You can get the window's position through the winfo_x and winfo_y methods.
Below is a simple script to demonstrate*:
from tkinter import Tk, Button
root = Tk()
def click():
print(root.winfo_x(), root.winfo_y())
Button(text="Get position", command=click).grid()
root.mainloop()
*Note: I used a Tk window here instead of a Toplevel for the sake of simplicity. However, the same principle applies to the Toplevel.