Tkinter centering ctktoplevel window in middle of main window - python

There are tons of results on centering a top level window comparative to your screen size. However, to my knowledge, there is no information out there on how to center the top level window in the center of the main window. I could hard code it but it's ugly to do and would of course not work anymore once im moving the main window to a seperate screen. So my question is, how do i center a top level window in the main window regardless of the position of the main window?

from tkinter import Toplevel, Button, Tk
root = Tk()
width = 960
height = 540
root.geometry("%dx%d" % (width, height))
def new_window() :
win = Toplevel()
win.geometry("%dx%d+%d+%d" % (480, 270, root.winfo_x() + width/4, root.winfo_y() + height/4))
#the geometry method can take four numbers as a string argument
#first two numbers for dimensions
#second two numbers for the position of the opened window
#the position is always the top left of your window
#winfo_x and winfo_y are two methods
#they determine the current position of your window
Button(root, text = "open", command = new_window).pack()
root.mainloop()
You can test the code and make it satisfy your needs.
I hope that helps !

Get location of main window along with dimensions
x, y, w_main, h_main
get dimensions of your top level window
w_top, h_top
location of toplevel is in center of main window, so its relative position is:
x_rel = round((w_main - w_top)/2)
y_rel = round((h_main - h_top)/2)
now add these relative coordinated to absolute location of main window:
x += x_rel
y += y_rel
Place the toplevel at these coordinates
x, y
repeat the whole process every time the location of main window changes, except if dimensions are fixed they can be separated out (no need to check them repeatedly)

Related

How to change tkinter geometry values in regard to (for example) a scrolled text height?

I was wondering how do the geometry() function values in tkinter come to play with the height value of for example a scrolled text? How can I convert the units to work with each other?
For example: If I want to open a scrolled text box underneath my tkinter window with a click of a button, I know I need then to change my window's geometry() (height) value, but I don't know by how much exactly.
(In the following example I randomly added 100 to the geometry value in the function open. But I want a more specific value that translates to the 7 of the height value of the scrolled text)
from tkinter import *
from tkinter import scrolledtext
def open():
root.geometry(f'{frame_width}x{frame_height+100}')
st.pack(side="bottom")
frame_width = 900
frame_height = 500
root = Tk()
root.geometry(f'{frame_width}x{frame_height}')
root.configure(bg='white')
root.resizable(False, False)
open_st = Button(root, text="OPEN SCROLLED TEXT", command= open)
open_st.pack()
st = scrolledtext.ScrolledText(root, width=frame_width, height=7)
root.mainloop()
Widgets that contains text usually provide their width and height in character space. This means tkinter takes the average width and height of your font and multiplies it with the given number in width and height.
There are mainly two ways to deal with that, either tk.Font.measure or metrics, if you want to convert characters to pixel or the much more comfortable way by just asking the widget for it's size via winfo. Happily your case fits for the latter.
The alternate code would looks like this:
def open_text():
st.pack(side="bottom")
width, window_height = root.winfo_width(), root.winfo_height()
requested_text_height = st.winfo_reqheight()
new_height = window_height + requested_text_height
root.geometry(f'{width}x{new_height}')
Please note that I have named your function differently. Cause you redefined open in the global namespace of your module and this could lead to unintended behavior. In addition I wonder why you want to do it like this and not just let the geometry managers do their job?

how to measure the width of a tkinter canvas text

I was wondering how to measure the width of a text in tkinter canvas. I have a text displayed somewhere:
myCanvas.create_text(400,410, text="This is my text", tags="my_tag")
and at some point there is a shape (polygon line) that may overlap when the text gets longer (text will be changed with .itemconfigure()). In this case I wanted to break the text in lines, which works well with the width option.
To figure out if objects overlap there are a couple of possible methods: find_overlapping() or find_enclosed(), however, they do require 4 coordinates and myCanvas.coords("my_tag") only returns 2 coordinates. How could I figure out the other x2, y2?
An alternative was to use find_closest(), however, there are a bunch of shapes closer than the overlapping one, so that is not accurate.
Another alternative was that I could figure out the intersecting coordinate and then just take the distance to the x coordinate of the text and double that as a width (since text gets drawn from the center), for example:
intersection point at (350,405) => width = 2 * (400-350)
However, since the shape is a line (part of a polygon) that goes diagonal, I'm not sure how get the intersection point other than having a loop that creates a line that slowly increases till it overlaps with an object, for example:
text_center = (400,410)
start_x = text_center[0]
start_y = text_center[1]
extender = 1
while(myCanvas.find_overlapping(start_x, start_y, start_x+entender, start_y == ())
myCanvas.delete("mytestline")
extender += 1
myCanvas.create_line(start_x, start_y, start_x+extender, start_y, tags="mytestline")
overlapping_point = (start_x+extender, start_y)
Is there an easier way?
The bbox method will return the bounding box of any item or group of items on the canvas. The bounding box is the smallest rectangle that contains the elements, give or take a pixel or two. The returned value is a tuple of the two coordinates.
The following example creates a text item, gets and displays the bounding box, and also uses the coordinates to draw a rectangle around the text so you can visually see that the numbers are correct.
import tkinter as tk
root = tk.Tk()
myCanvas = tk.Canvas(root)
myCanvas.pack(fill="both", expand=True)
item_id = myCanvas.create_text(100,50, text="This is my text", tags="my_tag")
bbox = myCanvas.bbox(item_id)
myCanvas.create_rectangle(bbox, outline="red")
myCanvas.create_text(100, 70, text=f"bbox: {bbox}")
root.mainloop()

How do you specify or read the position of the main app window with guizero?

I need to specify, or just read, the position (x and y coordinates of the top left corner) of the main app window in guizero.
Apparently the App object does not have x and y positions properties that I can set or read.
I found on this forum a solution for this issue with TkInter but not guizero.
My goal is actually to be able to tell the mouse position within the app window. I can tell the mouse position with respect of the entire screen with the event data display_x and display_y and those change if I move the window around but I could use these values if I could account for the position of the window on the screen so, how do I find that out?
The event data x and y, which are meant precisely to return the relative x and y position of the mouse within a widget, will NOT work IF that particular region of the window is "covered" by a widget like a text or a button.
I am using Windows 10 and Python 3
event_data.display_x = x position of the mouse on the entire display
event_data.x = x position of the mouse on a widget.
Cannot use for the app window if that position is "covered" by a widget
I found the answer!
Apparently you can use tkinter methods and properties directly on guizero objects.
As it happens, the geometry property returns a string that contains the window coordinates.
so this is an example:
from guizero import App, PushButton
#___________________________________
def appos(event_data):
""" Print the coordinates of the Window position,
of the mouse position on the screen
and of the mouse position within the window"""
# Absolute mouse position on display
mouse_x, mouse_y = (event_data.display_x), (event_data.display_y)
# tk.geometry() returns the size and coordinates of a window in a string
wincord = (app.tk.geometry()).split("+")[1:] # Discards size
# Coordinates of (top left corner) of window
wincord_x, wincord_y = int(wincord[0]), int (wincord[1])
# Relative mouse position within the window
mouse_rel_x = mouse_x - wincord_x
mouse_rel_y = mouse_y - wincord_y
print ("Window position on display: ", wincord_x, wincord_y)
print ("Mouse position on entire display: ", mouse_x, mouse_y)
print ("Mouse position in window: ", mouse_rel_x, mouse_rel_y)
print()
#_________________________________
app = App(layout="grid")
app.when_clicked = appos # Call the function that prints the positions
# Creates a list of lists (kinda a 2-dimension array) with
# placeholders to be filled with buttons
bt = [[0,1,2,3,4,5,6,7],
[0,1,2,3,4,5,6,7],
[0,1,2,3,4,5,6,7],
[0,1,2,3,4,5,6,7],
[0,1,2,3,4,5,6,7],
[0,1,2,3,4,5,6,7],
[0,1,2,3,4,5,6,7],
[0,1,2,3,4,5,6,7]]
# creates an array of 8x8 buttons and arrange them on the window
for x in range (8):
for y in range (8):
bt[x][y] = PushButton(app, text= str(x)+str(y), grid= [x, y])
# bt[x][y].when_clicked = clicked # For future use
app.display()
As you can see, by clicking anywhere on the window, you get the correct mouse position within the window even if it is covered by buttons!

Change the on-screen position of the Turtle Graphics window?

Is it possible to change the position of the turtle console on screen?
My main objective is to write code that can move the window, that's all.
I'm using Python 3.4.0 under Windows 10.
If any extra information is needed please ask.
Why do folks always jump into tkinter before reading the turtle documentation?
Yes, you can set the screen position of the turtle graphics window using the same setup() method you use to size it:
from turtle import Turtle, Screen
def animate():
global offset
screen.setup(width=0.333, height=0.333, startx=offset, starty=offset)
turtle.dot(offset)
offset += 10
if offset < 300:
screen.ontimer(animate, 100)
screen = Screen()
turtle = Turtle()
offset = 30
animate()
screen.exitonclick()
startx, if positive, is the starting position in pixels from the left edge of the screen, or from the right edge if negative. Similarly, starty, if positive, is the starting position from the top edge of the screen, or from the bottom edge if negative. By default, the window is centered on the screen.
Your title asks about the position of the Turtle Graphics window on the screen but the body of your question asks about the Turtle Console. These might be considered two different windows.
My main objective is to write code that can move the window
I can't tell if you just want to set the initial position of the window or actually move the window around the screen so I rewrote my example to demonstrate the later.
Yes. You need to get the root window that contains the Tkinter Canvas that the turtle is using as its TurtleScreen. Once you have that window you can change its geometry.
Here's a simple demo.
import turtle
turtle.setup(width=0.5, height=0.5)
screen = turtle.Screen()
width, height = screen.window_width(), screen.window_height()
canvas = screen.getcanvas()
left, top = 30, 100
geom = '{}x{}+{}+{}'.format(width, height, left, top)
canvas.master.geometry(geom)
t = turtle.Turtle()
turtle.exitonclick()

how to set the position of a tkinter window without setting the dimensions

I know a way to put a tkinter window lets say in the middle
from tkinter import *
root=Tk()
w=350
h=285
ws=root.winfo_screenwidth()
hs=root.winfo_screenheight()
x=(ws/2)-(w/2)
y=(hs/2)-(h/2)
root.geometry('%dx%d+%d+%d'%(w,h,x,y))
In the above code the tkinter window will pop up in the middle with a height of 285 and a width of 350. but if i added too many widget the height 285 may not be enough. fort example lets say we added this code
for x in range(50):
Label(root,text=x).pack()
only the first 13 numbers will be in the window since i set the height to a fixed number 285.
so my question is would there be a way to put a tkinter window in the middle of the screen without setting the height and width, i mean if the height and the width are not set to a fixed number the window will continue to expand automatically.for example in the code below it expands automatically except the window is not in the middle.
from tkinter import *
win=Tk()
for x in range(50):
Label(win,text=x).pack()
You can omit the width and height from the geometry specification. For example:
root.geometry('+%d+%d'%(x,y))

Categories