Is there a Python tkinter function that makes a drawing’s coords on a certain ratio on the canvas widget? - python

I’m a first timer at tkinter(python) and what I want to do is to make a line of text stay on the same coords ratio on the canvas. For example, I want a line of text to stay in the middle. Is there any tkinter text parameters that make it stay in a certain ratio without running a while loop? I want minimal time complexity.

Your GUI can have a function bound to the Canvas <Configure> event that fires whenever the Canvas changes size. A simple example below.
There is also a Canvas.scale method which will change the location and size of canvas objects. Text may move but will not change size.
import tkinter as tk
root = tk.Tk()
# Create a canvas that will change size with the root window.
canvas = tk.Canvas( root, width = 600, height = 400, borderwidth = 2,
relief = tk.SOLID )
canvas.grid( sticky = 'nsew', padx = 5, pady = 5 )
root.grid_columnconfigure( 0, weight = 1 )
root.grid_rowconfigure( 0, weight = 1 )
text = canvas.create_text( 50, 50, text = 'Test Text' )
def on_canvas_config( event ):
""" This is bound to the canvas <Configure> event.
It executes when the canvas changes size.
The event is an object that contains the new width and height.
"""
x = max( 25, event.width // 2 ) # x >= 25
y = max( 12, event.height // 8 ) # y >= 12
canvas.coords( text, x, y ) # Set the canvas coordinates
canvas.bind( '<Configure>', on_canvas_config )
root.mainloop()
The on_canvas_configure function can be written to amend the coords of any objects in the Canvas to meet your requirements. I've never tried to use the Canvas.scale method but this may be worth exploring if there are many objects to reposition on the canvas.

Related

Why does the text on my button not appear?

I'm making a text editor and I'm adding a navbar but the text on FileBtn doesn't appear.
from tkinter import *
##############################################
# Constants
width = 800
height = 450
##############################################
# Window Creation
Window = Tk()
Window.geometry(F'{width}x{height}')
Window.title("TextWrite")
##############################################
# Navbar Creation
Navbar = Frame(Window, width = width, height = 25, bg = "#141414")
Navbar.place(x = 0, y = 0)
# File Options Creation
def OpenFileOptions():
FileOptions.place(x = 0, y = 25)
FileBtn = Button(Window, width = 10, height = 25, text = "File", bg = "#171717", fg = "white", command = OpenFileOptions)
FileBtn.place(x = 0, y = 0)
FileOptions = Frame(Window, width = 10, height = 50)
FileOptions.place(x = -1000, y = 0)
##############################################
# Text Input Creation
Input = Text(Window, width = width, height = 425, bg = "#202020", fg = "white")
Input.place(x = 0, y = 25)
Window.mainloop()
I searched for my problem but nothing I found seemed to fix it.
This is the first time I have encountered this error, and I have no clue why it happens.
The main problem is that you've set the height of the button to 25 lines tall. The width and height for some widgets -- including buttons -- is in number of characters, not pixels. Tkinter will center the text in the widget, so the text was very far down and out of view.
You can actually see this if you remove the text widget. You'll see that the button is very tall and the button label is centered in the button but roughly in the middle of the widget. (note: the following screenshot was taken on a Mac, which doesn't support changing the background color of buttons)
If you remove the height attribute altogether or set height to 1, you'll see the text, though you might have to also change the colors.
If you're just now learning tkinter, I strongly encourage you to use pack and/or grid rather than place. There's a slight learning curve, but it's much easier to make GUIs that are responsive and that make optimal use of the window size. place is best for very unique circumstances rather than as a general purpose tool.
but the text on FileBtn doesn't appear
No need to change value. In line 37, change this y=25 to y=400. Should be like this: Input.place(x = 0, y = 400)
Screenshot:

How to make a floating Frame in tkinter

For my tkinter app I want to make a frame that would on top of other widgets, not taking any space (like html position fixed).
The Frame will have to contain widgets, if Frame is not possible labels or buttons will do.
I have no idea how to do it so haven't tried anything yet. Please help!
Here is a demonstration of place manager.
Remarks in the code explain behaviour.
import tkinter as tk
root = tk.Tk()
root.geometry("868x131")
button = tk.Button(root, text = "A Button")
button.place(x = 2, y = 2)
Frame = tk.LabelFrame(root, text = "A LabelFrame using place", bg="cyan")
# Frame using place with x, y, width, height in absolute coordinates
Frame.place(x = 250, y = 20, width = 600, height = 70)
ButtonInFrame = tk.Button(Frame, text = "A Button IN LabelFrame", bg="white")
# Note: place x,y is referenced to the container (Frame)
# Note: place uses just x,y and allows Button to determine width and height
ButtonInFrame.place(x = 2, y = 2)
Label = tk.Label(root, text = "A Label on LabelFrame\nWith multiple lines\nOf Text.", bg="light green")
# Label sits on top of buttonFrame and Frame
# Note place uses just x,y and allows Label to determine width and height
Label.place(x = 330, y = 60)
root.mainloop()

Python tkinter - place function not working with multiple widgets

When making a program for a game database, I want to have the main menu to have multiple buttons leading to multiple topics. The place function isn't working for me. Here is my code:
windowFU = tk.Tk()
windowFU.title("MHFU Database")
windowFU.geometry("255x200+300+180")
frame = tk.Frame(master = windowFU, width = 255, height = 200)
frame.pack()
icon = tk.PhotoImage(file = "images/icon.png")
windowFU.iconphoto(False, icon)
welcome = tk.Label(
master = frame,
text = "What would you like to view?",
width = 30,
height = 2
)
searchEntry = tk.Entry(
master = frame,
width = 30
)
buttonMonstersFU = tk.Button(
master = frame,
text = "Monsters",
width = 12,
height = 2
)
# Here is the place function
buttonMonstersFU.place(x = 100, y = 100)
welcome.pack()
searchEntry.pack()
buttonMonstersFU.pack()
searchEntry.bind('<Return>', getEntry)
windowFU.mainloop()
Note: Currently I just have the place function set to x = 100, y = 100 to test, it is not my final location.
Here is an image of what I get:
Result
What should I do?
You call buttonMonstersFU.pack() a few lines after you call buttonMonsersFU.place(...). Only one of pack, place, or grid can be responsible for a widget. When you call pack, any work done by place will be thrown away.
If you want to use place, then remove the line that calls buttonMonstersFU.pack().

Canvas resizing with window - good way to do it?

i created a window with a canvas inside. The canvas contains an rectangle. Both change their size through a callback together with the window size.
So my beginner question is: It works fine, but is this a good or common way to do this? Or is there a more efficient/common way?
from tkinter import* #
#creating instance of tkinter
obj = Tk()
#Set title of our window form
obj.title("MyFirst Window - WOW")
#Set dimension of form
x_size = 1200
y_size = 600
obj.geometry(str(x_size)+"x"+str(y_size))
obj.update()
w = Canvas(obj, width=x_size, height=y_size)
w.place(x=0,y=obj.winfo_height()-100)
w.create_rectangle(0, 0, obj.winfo_width(), 100, fill="#476042")
def callback(event):
print(str(obj.winfo_width())+'x'+str(obj.winfo_height()))
w.config(width=obj.winfo_width(),height=obj.winfo_height())
w.place(x=0,y=obj.winfo_height()-100)
w.create_rectangle(0, 0, obj.winfo_width(), 100, fill="#476042")
window = obj
window.bind("<Configure>", callback)
obj.mainloop()
No, this is not a good way to have the canvas resize. You should almost never use place. grid and pack make it much easier to create widgets that automatically resize.
For example, if you want the canvas to always be 100 pixels tall and fill the full width of the window, you can add it to obj like this:
w = Canvas(obj, width=x_size, height=100)
w.pack(side="bottom", fill="x")
As for the green rectangle, you have no choice but to use a binding on <Configure> if you want the rectangle to grow and shrink along with the canvas.
However, your callback creates a new rectangle every time it's called instead of modifying the coordinates of the existing rectangle. This is a memory leak, because your program will use more and more memory the longer it runs and the more often the window is resized. w.create_rectangle will return an identifier; you can use that identifier to later modify the rectangle.
Here's a simplified version of your code. I've changed the variable names to make it a bit easier to comprehend.
from tkinter import *
window = Tk()
window.title("MyFirst Window - WOW")
x_size = 1200
y_size = 600
window.geometry(str(x_size)+"x"+str(y_size))
window.update_idletasks()
canvas = Canvas(window, width=x_size, height=100, background="bisque")
canvas.pack(side="bottom", fill="x")
rect = canvas.create_rectangle(0, 0, window.winfo_width(), 100, fill="#476042")
def callback(event):
canvas.coords(rect, 0, 0, canvas.winfo_width(), 100)
canvas.bind("<Configure>", callback)
window.mainloop()

Tkinter -- how to horizontally center canvas text?

I'm working on a function in a UI class that is a config window, it displays the logo of the program and has an updating text at the bottom telling you what it's loading, etc. This is what I have so far:
self.window = "config"
self.windowWidth = 340
self.windowHeight = 270
infoText = "Configuring Kh..."
self.root = tk.Tk()
self.root.geometry("%dx%d+400+400" % (self.windowWidth, self.windowHeight))
self.root.title("Kh Control v1.1 starting...")
logo = tk.PhotoImage(file="KhLogo.gif")
mainPanel = tk.Canvas(self.root, width=self.windowWidth, height=self.windowHeight)
mainPanel.image = logo
mainPanel.pack()
mainPanel.create_image(0, 0, image=logo, anchor="nw")
mainPanel.create_text(0,200, text=infoText, anchor="nw", fill="yellow")
return
I'd like the text in infoText to be centered horizontally and offset vertically about 200px down. The vertical offset works fine, but I can't figure out how to center the text horizontally.
I started by trying the age old ((width / 2) - (str length / 2)) but then realized that each letter isn't 1px. And anchor = "center" seems to only put half the text off the left side of the screen.
I'm very new to Python (only a few days now) so if I'm missing something obvious, that's why.
EDIT: and in case it wasn't obvious, this text will change so I can't just make an absolute decision on the offsets, it has to change with the text
I figured it out after scrounging through the canvas reference.
There is a method for a canvas called bbox that returns a tuple containing (x1, y1, x2, y2) of the area an item takes up. I got those coords, drew up a function to find the px length of it, divided it by 2 and subtracted it from the window width. Then I used canvas.move to change the x offset using the number the function returned.
def findXCenter(self, canvas, item):
coords = canvas.bbox(item)
xOffset = (self.windowWidth / 2) - ((coords[2] - coords[0]) / 2)
return xOffset
The main part is here:
textID = mainPanel.create_text(0,0, text=infoText, anchor="nw", fill="yellow")
xOffset = self.findXCenter(mainPanel, textID)
mainPanel.move(textID, xOffset, 0)
Hopefully my hours of searching for this answer will help someone later on.
Have you tried using the justify parameter?
Often center is used with non-text objects.
https://stackoverflow.com/a/15016161/3900967 might provide some insight.
You can set the position of the text using the first part of the .create_text() method.
see http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/create_text.html
To make this position horizontialy center to the window use self.windowWidth / 2 as the x cordinate
By default the text is anchored to center around the given position.
(.create_text will default to anchor="CENTER")
You will also want to remove the anchor="nw" as this will make your text display down and to the right of the position given
As such your updated code should be.
self.window = "config"
self.windowWidth = 340
self.windowHeight = 270
infoText = "Configuring Kh..."
self.root = tk.Tk()
self.root.geometry("%dx%d+400+400" % (self.windowWidth, self.windowHeight))
self.root.title("Kh Control v1.1 starting...")
logo = tk.PhotoImage(file="KhLogo.gif")
mainPanel = tk.Canvas(self.root, width=self.windowWidth, height=self.windowHeight)
mainPanel.image = logo
mainPanel.pack()
mainPanel.create_image(0, 0, image=logo, anchor="nw")
mainPanel.create_text(self.windowWidth/2,200, text=infoText, fill="yellow")
return
You can use the anchor="center" method to align your text that created inside the canvas using canvas.create_text method
try following example code:
from tkinter import *
root = Tk()
canvas = Canvas(root, width=400, height=300)
canvas.pack()
text = canvas.create_text(200, 150, text="Hello World!", anchor="center")
root.mainloop()

Categories