I have a tkinter UI which has a frame, the frame has 2 canvases, 1 canvas has a button that creates a top-level window (named top). That top-level window has a CLOSE button that closes the top window (which is easily done by top.destoy). But, I need the CLOSE button to also call a function that does something. So, since CLOSE button cannot be configured to call something() and destroy(), I set the button to call sequence() which calls something() and top.destroy().
When I run this and hit the CLOSE button, I get the error name 'top' is not defined. I know why it's happening, but I don't know how to fix this. Any ideas?
import time
import tkinter as tk
import tkinter.font
from tkinter import*
window = Tk()
window.geometry("1920x1080")
window.title("HOME")
f1 = Frame (window, bg="white")
f1.pack()
c1 = Canvas(f1, height=200, width=1960, bg="white")
label = Label(f1, text="Running Apps", font= "Cambria 30 bold").pack()
c1.pack(anchor=N)
r = c1.create_rectangle(400, 50, 550, 150, fill="white", activefill="black")
r2 = c1.create_rectangle(550, 50, 700, 150, fill="white", activefill="black")
c2 = tk.Canvas(f1, height=800, width=1960, bg="white")
c2.pack(side="bottom")
def sequence():
top.destroy()
c1.itemconfig(r, fill="white") #something()
def openApp1():
c1.itemconfig(r, fill="red")
top = Toplevel()
top.geometry("1920x1080")
top.title("App 1")
cvs1 = tk.Canvas(top, height="880", width="800", bg="red")
Closebutton = Button(cvs1, text="CLOSE", command=sequence, padx="20", pady="0", justify="center", height="1", width="6", font="Cambria 20 bold", borderwidth="7")
cvs1.create_window(400, 600, window=Closebutton)
label1 = Label(top, text="I am App 1", font= "Cambria 50 bold")
label1.place(x=630, y=100)
cvs1.pack()
button1= Button(c2, bg="red", text="App 1", command=openApp1, padx="20", pady="10", justify="center", height="3", width="10", font="Cambria 30 bold", borderwidth="10")
c2.create_window(100, 200, anchor=NW, window=button1)
window.mainloop()
Use global top inside openApp1 and it will assign TopLevel to global variable (instead of local variable) and it will resolve problem in sequence
def openApp1():
global top
c1.itemconfig(r, fill="red")
top = Toplevel()
Related
So I'm trying to create a program which uses multiple tk.Toplevel windows. The problem with this is, that all windows show up seperated as their "own App", so when you alt tab, you switch between the toplevel windows.
The pseudocode would look something like this:
import tkinter as tk
top_levels = {}
def open_toplevel():
top_level = tk.Toplevel(root)
top_level.geometry("300x200+0+0")
top_levels.update({f"toplevel{len(top_levels.keys())}" : top_level})
root = tk.Tk()
button = tk.Button(root, command= open_toplevel)
button.place(x=0, y=0)
root.mainloop()
So my question, is: is there a way to unify them into "one window"?
If you want all of them to unify into one window then tk.Frame is a better widget to use instead of tk.Toplevel
The purpose of tk.Toplevel is to create a new temporary window, not an extra part of the window. But frames are a really good way to organise stuff.
This code below creates new frame every time you click the button. This is just a simple example. You can also use grid for widgets in a frame. I also put a border so you can see where the frames are located.
from tkinter import *
def open_frame():
frame = Frame(root, highlightbackground="black", highlightthickness=2)
lbl1 = Label(frame, text=f"Frame {len(frames) + 1} label 1")
lbl2 = Label(frame, text=f"Frame {len(frames) + 1} label 2")
lbl1.pack()
lbl2.pack()
frame.pack(padx=5, pady=5)
frames.append(frame)
root = Tk()
frames = []
btn = Button(root, text="Open Frame", command=open_frame)
btn.pack()
root.mainloop()
I hope this solution is helpful
EDIT
Use this code here to move the frames:
from tkinter import *
def open_frame():
global frame, frames
frame = Frame(root, highlightbackground="black", highlightthickness=2)
lbl1 = Label(frame, text=f"Frame {len(frames) + 1} label 1")
lbl2 = Label(frame, text=f"Frame {len(frames) + 1} label 2")
lbl1.pack()
lbl2.pack()
frame.pack(padx=5, pady=5)
frame_number = len(frames)
lbl1.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
lbl2.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
frame.bind('<B1-Motion>', lambda event: MoveWindow(event, frame_number))
frames.append(frame)
labels.append(lbl1)
labels.append(lbl2)
def MoveWindow(event, frame_number):
global root, frames
root.update_idletasks()
f = frames[frame_number]
x = f.winfo_width()/2
y = f.winfo_height()*1.5
f.place(x=event.x_root-x, y=event.y_root-y)
root = Tk()
root.geometry("500x500")
frames = []
labels = []
btn = Button(root, text="Open Frame", command=open_frame)
btn.pack()
root.mainloop()
I have researched for 3 weeks and could find no solution.
I have read numerous tags and have also tried modifying code
Most of the tags refer to Java or some other programming language other than Python
My file has upwards of 80 frame which will all have 3 buttons and 1 textbox.
I sent over 1 frame with 3 radio buttons and a textbox.
Would this work or should I go in a different direction.
Any advice would be greatly appreciated.
from tkinter import *
from tkinter import Tk
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("Dental Milling Machines")
root.geometry("1000x900")
def func1(event):
insert("")
textbox1.insert('1.5')
def onclick1():
textbox1.insert('<Return>', func1)
button_var1 = tk.IntVar()
frame1 = Frame(root, height = 150, width= 150, relief= RAISED, bd=8, bg="blue")
frame1.grid(row=1, column=0, pady=2,sticky="NW")
frame2 = Frame(frame1, height = 150, width= 150, relief= RAISED, bd=8, bg="lightblue")
frame2.grid(row=1, column=0, pady=2,sticky="NW")
label = Label(frame2, text="Select # Of Units", fg="red")
label.grid(row=0, column=0, pady= 1, padx=3, sticky= "W")
textbox1 = Text(frame2, borderwidth=1, wrap="none", width=10, height=2)
textbox1.grid(row=0, column=1, sticky="w")
button1=Radiobutton(frame2, text="1 Unit ", variable=button_var1, command=onclick1)
button1.grid(row=1, column=0, pady= 1, padx= 5, sticky= "W")
root.mainloop()
If you want to insert something into the text box whenever the radio button is click, just modify your code as below:
def onclick1():
textbox1.delete('1.0', 'end') # clear the text box
textbox1.insert('end', '1.5') # insert whatever you want into text box
...
button1 = Radiobutton(frame2, text="1 Unit", variable=button_var1, command=onclick1)
...
I have a program calculator, the button code opens a new window, but I want the button calculator run in the same window but not two separate, how do I instead run my code in the same windows?
P.S. the code is not mine, it is as an example
from tkinter import *
root = Tk()
root.title("Math Lab")
root.geometry("1400x1000")
heading = Label(root, text = "Welcome to the MATH Lab", font=("Berlin Sans FB", 40, "bold"), fg= "steelblue").pack()
root.configure(background= "powder blue")
def calculator():
w = Tk()
w.geometry("1400x1000")
def evaluate1():
res.configure(text="Answer: " + str(eval(entry.get())))
def evaluate(event):
res.configure(text="Answer: " + str(eval(entry.get())))
but1 = Button(w, text="Enter", width=10, height=3)
but1.place(x=650, y=100)
but1.config(command=evaluate1)
Label(w, text="Your Expression:").pack()
entry = Entry(w)
entry.bind("<Return>", evaluate)
entry.pack()
res = Label(w)
res.pack()
w.mainloop()
but1=Button(root,text="Calculator",width = 10, height = 3)
but1.place(x=100, y=100)
but1.config(command = calculator)
root.mainloop()
Any suggestions?
Here you are requesting a new window:
w = Tk()
Replace it with
w = root
and you are good to go.
(Well, you'll want to tidy up some x,y offsets, for aesthetics.)
I have few line of code here which draw circle in tkinter canvas but want i want to do is to stop the sketch to the point where it started so i use sleep but i stop the sketch where it has gotten to then it continue.
So i wrote this function
def stop_extent():
canvas.create_arc(0, 0, 0, 0, extent=0, outline="red", tags=("arc",))
extent = float(canvas.itemcget("arc", "extent"))
extent = extent + 5.0
canvas.itemconfigure("arc", extent=extent)
canvas.after(100, stop_extent)
and set all the values to 0 but still it doesn't stopped it and the label doesn't configure too.
import tkinter as tk
import time
def change_extent():
l.config(text="sketch in progress...")
canvas.create_arc(350, 100, 220, 220, extent=0, outline="yellow", tags=("arc",))
extent = float(canvas.itemcget("arc", "extent"))
extent = extent + 5.0
canvas.itemconfigure("arc", extent=extent)
canvas.after(100, change_extent)
def stop_extent():
l.config(text="sketch stopped...")
time.sleep(10)
l.config(text="sketch stopped...")
root = tk.Tk()
root.geometry("600x400")
canvas = tk.Canvas(root, width=200, height=200, background="dodger blue")
canvas.pack(fill="both", expand=True)
l = tk.Label(canvas, bg="dodger blue", fg="white")
l.place(x=250, y=20)
b= tk.Button(canvas, text="Start sketch", command=change_extent, width=25, fg='dodger blue')
b.place(x=220, y=330)
b= tk.Button(canvas, text="stop sketch", command=stop_extent, width=25, fg='dodger blue')
b.place(x=220, y=370)
root.mainloop()
You can keep track of the call back generated by canvas.after in the change_extent method, and cancel it with canvas.after_cancel in the stop_extent method; in the following example, I use a global variable stop to demonstrate it.
Using time.sleep in a GUI application is generally a bad idea.
You also do not need to recreate the arc each time you want to modify its extent, but that is outside the scope of your question.
import tkinter as tk
import time
def change_extent():
global stop
l.config(text="sketch in progress...")
extent = float(canvas.itemcget(arc_item, "extent"))
canvas.itemconfigure("arc", extent=extent+5)
stop = canvas.after(100, change_extent)
def stop_extent():
l.config(text="sketch stopped...")
print(stop)
canvas.after_cancel(stop)
canvas.itemconfigure("arc", extent=0) # <- reset to straight line start
stop = None
root = tk.Tk()
root.geometry("600x400")
canvas = tk.Canvas(root, width=200, height=200, background="dodger blue")
canvas.pack(fill="both", expand=True)
arc_item = canvas.create_arc(350, 100, 220, 220, extent=0, outline="yellow", tags=("arc",))
l = tk.Label(canvas, bg="dodger blue", fg="white")
l.place(x=250, y=20)
b= tk.Button(canvas, text="Start sketch", command=change_extent, width=25, fg='dodger blue')
b.place(x=220, y=330)
b= tk.Button(canvas, text="stop sketch", command=stop_extent, width=25, fg='dodger blue')
b.place(x=220, y=370)
root.mainloop()
I would like to create a zoom button. On clicking on that zoom button, the image would be zoomed in by a factor represented by an integer (1,2,3,4,5...). With this piece of code, by clicking on the zoom button, another panel is created underneath the already loaded picture. Inside it is blank. What would be needed is to:
1. kill the first (non-zoomed window) and 2. load the zoomed image on the updated panel
from tkinter import *
from tkinter.filedialog import askopenfilename
import tkinter as tk
event2canvas = lambda e, c: (c.canvasx(e.x), c.canvasy(e.y))
root = Tk()
#setting up a tkinter canvas with scrollbars
frame = Frame(root, bd=2, relief=SUNKEN)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
xscroll = Scrollbar(frame, orient=HORIZONTAL)
xscroll.grid(row=1, column=0, sticky=E+W)
yscroll = Scrollbar(frame)
yscroll.grid(row=0, column=1, sticky=N+S)
canvas = Canvas(frame, bd=0, xscrollcommand=xscroll.set,yscrollcommand=yscroll.set)
canvas.grid(row=0, column=0, sticky=N+S+E+W)
xscroll.config(command=canvas.xview)
yscroll.config(command=canvas.yview)
frame.pack(fill=BOTH,expand=1)
#adding the image
image_str="image.png"
image = tk.PhotoImage(file=image_str)
image = image.zoom(1,1)
canvas.create_image(0,0,image=image,anchor="nw")
canvas.config(scrollregion=canvas.bbox(ALL))
def zoomin():
root = Tk()
frame = Frame(root, bd=2, relief=SUNKEN)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
xscroll = Scrollbar(frame, orient=HORIZONTAL)
xscroll.grid(row=1, column=0, sticky=E+W)
yscroll = Scrollbar(frame)
yscroll.grid(row=0, column=1, sticky=N+S)
canvas = Canvas(frame, bd=0, xscrollcommand = xscroll.set, yscrollcommand = yscroll.set)
canvas.grid(row=0, column=0, sticky=N+S+E+W)
xscroll.config(command=canvas.xview)
yscroll.config(command=canvas.yview)
frame.pack(fill=BOTH,expand=1)
image = tk.PhotoImage(file=image_str)
image = image.zoom(1,1)
canvas.create_image(0,0,image=large_img,anchor="nw")
canvas.config(scrollregion=canvas.bbox(ALL))
toolbar = Frame(root, bg="blue")
insertButt = Button(toolbar, text="zoomin", command=lambda:zoomin())
insertButt.pack(side = LEFT, padx=2, pady=2)
toolbar.pack(side=TOP, fill = X)
#function to be called when mouse is clicked
def printcoords(event):
#outputting x and y coords to console
print (event.x,event.y)
#mouseclick event
canvas.bind("<Button 1>",printcoords)
#mouseclick event
canvas.bind("<ButtonPress-1>",printcoords)
canvas.bind("<ButtonRelease-1>",printcoords)
root.mainloop()
I would like to thank #Symon for his stackoverflow question. I largely inspired myself from his code
Well, the reason that the function zoomin(img) does not work properly is that it returns in the first line:
def zoomin(img):
return # Function returns here
... rest of code is never executed
I suspect this is due to the function being run when you create the button, not when you press it. Try cretating the button in this way instead:
insertButt = Button(toolbar, text="zoomin", command=lambda:zoomin(img))
Now the button will call zoomin(img) when it's pressed and not when the button is created.
Zooming with Tkinter
PhotoImage zoom only allows integer values, wich makes it a bit limited. But here's an example:
from tkinter import *
root = Tk()
root.geometry('300x200')
field = Canvas(root, bg='tan1', highlightthickness=0)
field.grid(sticky='news')
photo = PhotoImage(file='test.gif')
field.create_image(0, 0, image=photo, anchor='nw')
scale = 1
def zoom(event=None):
global scale, photo
scale = scale * 2
field.delete('all')
photo = photo.zoom(x=scale, y=scale)
field.create_image(0, 0, image=photo, anchor='nw')
field.image = photo
root.bind('z', zoom) # Bind "z" to zoom function
root.mainloop()
If you want to zoom by float you'll have to import a module for that. Pillow seems popular. But I haven't worked with any of them so you'll have to research them yourself.