I wrote a code using tkinter to switch between frames after a few seconds.
The problem is: All the application must run in fullscreen.
Here is the code.
from tkinter import *
N_TRIALS = 3
COUNT=-2
N_INTERVALS = 2*N_TRIALS + 1
RELAXING_TIME = 2000
RESTING_TIME = 2000
TASKING_TIME = 2000
start_btn_state = False
def raise_frame(frame):
frame.tkraise()
global COUNT
COUNT+=1
print(COUNT)
if (frame == f3 and COUNT==N_INTERVALS):
frame.after(RESTING_TIME,lambda:raise_frame(f5))
elif(frame == f5):
frame.after(RELAXING_TIME,lambda:raise_frame(f6))
elif frame == f2:
frame.after(RELAXING_TIME, lambda:raise_frame(f3))
elif frame == f3:
frame.after(RESTING_TIME, lambda:raise_frame(f4))
elif frame == f4:
frame.after(TASKING_TIME, lambda:raise_frame(f3))
elif frame==f6:
root.destroy()
def finish():
root.destroy()
def start():
start_btn_state=True
raise_frame(f2)
root = Tk()
f1 = Frame(root)
f2 = Frame(root)
f3 = Frame(root)
f4 = Frame(root)
f5 = Frame(root)
f6 = Frame(root)
for frame in (f1, f2, f3, f4, f5,f6):
frame.grid(row = 0, column = 0, sticky='nsew')
lb0=Label(f1, text="blablabla",font=("Arial Bold",10))
lb0.pack(padx = 10, pady=10)
lb1=Label(f1, text="blablabla",font = ("Arial Bold",10),fg = "black")
lb1.pack(padx = 10, pady=10)
lb2 = Label(f1, text="blablabla",font = ("Arial Bold",10),fg = "green")
lb2.pack(padx = 10, pady=10)
lb3 = Label(f1, text="blablabla",font = ("Arial Bold",10),fg = "blue")
lb3.pack(padx = 10, pady=10)
lb4 = Label(f1, text="blablabla",font = ("Arial Bold",10),fg = "red")
lb4.pack(padx = 10, pady=10)
start_value=BooleanVar()
btn_start=Button(f1, text="Start",font = ("Arial Bold",20), command = start)
btn_start.pack(padx=10,pady=30)
close_value=BooleanVar()
btn_close=Button(f1, text="CLOSE",font = ("Arial Bold",20), command=finish)
btn_close.pack(padx=10,pady=30)
Label(f2, text='ACTION 1').pack()
#Button(f2, text='Go to frame 3', command=lambda:raise_frame(f1)).pack()
f2.config(bg='red')
Label(f3, text='ACTION2').pack(side='left')
f3.config(bg='green')
Label(f4, text='ACTION3').pack()
f4.config(bg='blue')
Label(f5, text='RELAXING FINAL').pack()
f5.config(bg='black')
raise_frame(f1)
root.mainloop()
I don't know how to expand the frames to fill all the window. I tried everything: geometry, width and height, etc. It seems probably (100% sure) that I am doing something wrong to put those frames on fullscreen.
could anyone help me?
Sorry about the code that is not in OOP. I am learning about it.
I appreciate the help!
Best Regards
The placement and geometry of a window on screen is controlled by the window manager. Your application provides information about its size but the window manager is what decides which windows are visible and where they get placed. For this reason the flag that controls if a Tk window is fullscreen or not is one of the wm_attributes flags.
You can set your toplevel window to fullscreen using root.wm_attributes('-fullscreen', 1). If your frames are then managed such that they expand to fill the parent toplevel then they will fill the screen. For that with your grid usage you should configure the root grid geometry manager to allow (0,0) to expand to fill the area:
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
Or you could place each frame here at 0,0 and fill the available space using:
frame.place(x=0, y=0, relwidth=1.0, relheight=1.0)
This is one situation where place might be appropriate over grid.
Related
import tkinter as tk
from tkinter import *
HEIGHT = 600
WIDTH = 600
root = tk.Tk()
def button_event1():
import ThreePlayers
print(ThreePlayers)
def button_event2():
import TwoPlayersGame
print(TwoPlayersGame)
def button_event3():
print("")
def button_event4():
print("")
def button_event5():
quit()
root = Tk()
root.title('Connect 4 Game')
canvas = tk.Canvas(root, height=HEIGHT, width=WIDTH)
canvas.pack()
L = Label(root, text="Welcome to 3P Connect 4!!!",font=("Ariel",20,"bold", "underline"))
L.config(anchor=CENTER)
L.pack()
button = tk.Button(root, text="3 Player Game", command=button_event1)
button.pack()
button = tk.Button(root, text="2 Player Game", command=button_event2)
button.pack()
button = tk.Button(root, text="1 Player Game", command=button_event3)
button.pack()
button = tk.Button(root, text="Options", command=button_event4)
button.pack()
button = tk.Button(root, text="QUIT", command=button_event5)
button.pack()
root.mainloop()
Above is my Code for a Tkinter GUI but I want the have the label at the center of the root/window how do I do that? Currently its sitting on top of the buttons everything else is fine the button events and such works
In my opinion, you should have used place or grid instead of pack. Because pack only gives few alignment options.
otherwise, maybe divide the main window into two frames then pack the label at the top of the lower frame
frame = Frame(root)
frame.pack()
bottomframe = Frame(root)
bottomframe.pack( side = BOTTOM )
L = Label(root, text="Welcome to 3P Connect 4!!!",font=("Ariel",20,"bold", "underline"))
L.pack(side = TOP)
I wish this helps. but you should use grid for better alignment or place.
You can use the following commands to place the label in the center
L = Label(root, text="Welcome to 3P Connect 4!!!",font=("Ariel",20,"bold", "underline"))
# L.config(anchor=CENTER)
# L.pack()
L.place(x=HEIGHT/2, y=WIDTH/2, anchor="center")
Similarly, you can also use button.place(x=100, y=25) for buttons
REF: Tkinter: Center label in frame of fixed size?
I'm trying to control multiple canvases widths with the mouse wheel. What I have so far is this:
import tkinter as tk
class App(tk.Frame):
row_amount = 3
def __init__(self, root):
super(App, self).__init__(root)
self.root = root
self.main_frame = tk.Frame(root)
self.main_frame.pack(expand=True, fill=tk.BOTH)
self.row_collection = RowCollection(root, self.main_frame)
for i in range(App.row_amount): self.row_collection.row()
window_height = App.row_amount * 100
window_width = root.winfo_screenwidth() - 30
root.geometry(f'{window_width}x{window_height}+0+0')
self.row_collection.right_frame.grid_columnconfigure(0, weight=1)
self.row_collection.left_frame.grid_columnconfigure(0, weight=1)
self.pack()
class RowCollection:
"""Collection of rows"""
def __init__(self, root, frame):
self.row_list = []
self.root = root
self.frame = frame
self.right_frame = tk.Frame(self.frame, bg='red')
self.right_frame.pack(side=tk.RIGHT, expand=tk.YES, fill=tk.BOTH)
self.left_frame = tk.Frame(self.frame)
self.left_frame.pack(side=tk.LEFT, fill=tk.Y)
self.scrollbar = tk.Scrollbar(self.right_frame, orient=tk.HORIZONTAL)
self.scrollbar.config(command=self.scroll_x)
def row(self):
row = Row(self)
self.row_list.append(row)
return row
def scroll_x(self, *args):
for row in self.row_list:
row.canvas.xview(*args)
def zoomer(self, event=None):
print('zooming')
for row in self.row_list:
scale_factor = 0.1
curr_width = row.canvas.winfo_reqwidth()
print(f'event delta={event.delta}')
if event.delta > 0:
row.canvas.config(width=curr_width * (1 + scale_factor))
elif event.delta < 0:
row.canvas.config(width=curr_width * (1 - scale_factor))
row.canvas.configure(scrollregion=row.canvas.bbox('all'))
class Row:
"""Every row consists of a label on the left side and a canvas with a line on the right side"""
row_count = 0
label_width = 15
line_weight = 3
line_yoffset = 3
padx = 20
def __init__(self, collection):
self.frame = collection.frame
self.root = collection.root
self.collection = collection
self.canvas = None
self.label = None
self.text = f'Canvas {Row.row_count}'
self.height = 100
self.root.update()
self.label = tk.Label(self.collection.left_frame,
text=self.text,
height=1,
width=Row.label_width,
relief='raised')
self.label.grid(row=Row.row_count, column=0, sticky='ns')
# configure row size to match future canvas height
self.collection.left_frame.grid_rowconfigure(Row.row_count, minsize=self.height)
self.root.update()
self.canvas = tk.Canvas(self.collection.right_frame,
width=10000,
height=self.height,
bg='white',
highlightthickness=0)
self.canvas.grid(row=Row.row_count, column=0, sticky=tk.W)
self.root.update()
# draw line
self.line = self.canvas.create_rectangle(self.padx,
self.canvas.winfo_height() - Row.line_yoffset,
self.canvas.winfo_width() - self.padx,
self.canvas.winfo_height() - Row.line_yoffset + Row.line_weight,
fill='#000000', width=0, tags='line')
# config canvas
self.canvas.config(scrollregion=self.canvas.bbox('all'))
self.canvas.config(xscrollcommand=self.collection.scrollbar.set)
self.canvas.bind('<Configure>', lambda event: self.canvas.configure(scrollregion=self.canvas.bbox('all')))
self.canvas.bind('<MouseWheel>', self.collection.zoomer)
# Create point at canvas edge to prevent scrolling from removing padding
self.bounding_point = self.canvas.create_rectangle(0, 0, 0, 0, width=0)
self.bounding_point = self.canvas.create_rectangle(self.canvas.winfo_width(), self.canvas.winfo_width(),
self.canvas.winfo_width(), self.canvas.winfo_width(),
width=0)
Row.row_count += 1
self.collection.scrollbar.grid(row=Row.row_count, column=0, sticky='ew')
if __name__ == '__main__':
root = tk.Tk()
app = App(root)
root.mainloop()
The canvases themselves are inside right_frame, and the number of canvases is given by row_amount. The left_frame contains labels for each of the canvases. The canvases should be allowed to be pretty wide, so I initially set a width value of 10000. Because of that, they start partially visible, with the rest being accessible via a scrollbar.
What I would like is for the mouse wheel to control the size of the canvas as a whole (that is, both what is currently visible and what could be viewed using the scrollbar), similar to what would happen in an audio or video editing software timeline.
Right now, when I use the mouse wheel, what seems to get resized is not the whole canvas, but only the 'visible' portion. Resize it to be small enough and you can start to see it's frame background on the right portion of the window.
What am I missing here?
What am I missing here?
I think what you're missing is that the drawable area of the canvas is not at all related to the physical size of the canvas widget. You do not need to resize the canvas once it has been created. You can draw well past the borders of the widget.
If you want to be able to scroll elements into view that are not part of the visible canvas, you must configure the scrollregion to define the area of the virtual canvas that should be visible.
You said in a comment you're trying to create a timeline. Here's an example of a canvas widget that "grows" by adding a tickmark every second. Notice that the canvas is only 500,100, but the drawable area gets extended every second.
import tkinter as tk
root = tk.Tk()
canvas = tk.Canvas(root, width=500, height=100, bg="black")
vsb = tk.Scrollbar(root, orient="vertical", command=canvas.yview)
hsb = tk.Scrollbar(root, orient="horizontal", command=canvas.xview)
canvas.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
canvas.grid(row=0, column=0, sticky="nsew")
vsb.grid(row=0, column=1, sticky="ns")
hsb.grid(row=1, column=0, sticky="ew")
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
counter = 0
def add_tick():
global counter
# get the current state of the scrollbar. We'll use this later
# to determine if we should auto-scroll
xview = canvas.xview()
# draw a new tickmark
counter += 1
x = counter * 50
canvas.create_text(x, 52, anchor="n", text=counter, fill="white")
canvas.create_line(x, 40, x, 50, width=3, fill="red")
# update the scrollable region to include the new tickmark
canvas.configure(scrollregion=canvas.bbox("all"))
# autoscroll, only if the widget was already scrolled
# as far to the right as possible
if int(xview[1]) == 1:
canvas.xview_moveto(1.0)
canvas.after(1000, add_tick)
add_tick()
root.mainloop()
I want this entry bar and other contents I'll add to the frame later to be centred correctly, I received this code that supposedly should work but it isn't.
import tkinter as tk
import math
import time
root = tk.Tk()
root.geometry()
root.attributes("-fullscreen", True)
exit_button = tk.Button(root, text = "Exit", command = root.destroy)
exit_button.place(x=1506, y=0)
frame = tk.Frame(root)
main_entry = tk.Entry(root, width = 100, fg = "black")
main_entry.place(x=50, y=50)
frame.place(relx=.5,rely=.5, anchor='center')
root.mainloop()
As you can see the frame isn't centred so how can I fix this?
In order to achieve widget centering on a fullscreen I've had to use grid manager.
The code below works but the exact positioning requires some fiddling with frame padding.
frame padx = w/2-300 and pady = h/2-45 are arbitrary values found using a bit of trial and error.
import tkinter as tk
root = tk.Tk()
root.attributes( '-fullscreen', True )
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
frame = tk.Frame( root )
main_entry = tk.Entry( frame, width = 100 )
main_entry.grid( row = 0, column = 0, sticky = tk.NSEW )
frame.grid( row = 0, column = 0, padx = w/2-300, pady = h/2-45, sticky = tk.NSEW )
exit_button = tk.Button( frame, text = 'Exit', command = root.destroy )
exit_button.grid( row = 1, column = 0, sticky = tk.NSEW )
tk.mainloop()
Frame automatically changes size to size of objects inside Frame (when you use pack()) but you have nothing inside Frame. You put all widgets directly in root - so Frame has no size (width zero, height zero) and it is not visible.
When I use tk.Frame(root, bg='red', width=100, height=100) then I see small red frame in the center.
You have two problems:
(1) you put Entry in wrong parent - it has to be frame instead of root,
(2) you use place() which doesn't resize Frame to its children and it has size zero - so you don't see it. You would have to set size of Frame manully (ie. tk.Frame(..., width=100, height=100)) or you could use pack() and it will resize it automatically.
I add colors for backgrounds to see widgets. blue for window and red for frame.
import tkinter as tk
root = tk.Tk()
root['bg'] = 'blue'
root.attributes("-fullscreen", True)
exit_button = tk.Button(root, text="Exit", command=root.destroy)
exit_button.place(x=1506, y=0)
frame = tk.Frame(root, bg='red')
frame.place(relx=.5, rely=.5, anchor='center')
main_entry = tk.Entry(frame, width=100, fg="black")
main_entry.pack(padx=50, pady=50) # with external margins 50
root.mainloop()
My objective is to solve the problem of the grid exceeding the window(shown as figure.1)
enter image description here
My program function is creating a grid that number of columns defined by user.
I tried using canvas to solve this problem, but it still doesn't work successfully.
It doesn't show the full grid in the canvas.(shown as figure.2)
enter image description here
Below is my code, could you please help solve the problems or give me some advice.
Thanks a lot.
Code:
import tkinter as tk
import tkinter.messagebox
import tkinter.filedialog
MainWindow = tk.Tk()
MainWindow.title('Helloworld')
MainWindow.geometry('1000x800')
def btn_generate():
global EntryNamelist
global Entrycoordinatelist
global EntryLabellist
con_num = en_condition_num.get()
if con_num != '':
#### Grid Body
for i in range(1,int(con_num) +1 ):
lb_name = tk.Label(fm_grid, text="Condition" + str(i) )
lb_name.grid(row=i, column=0, padx=2, pady=1, ipadx=20, ipady=5)
En_name = tk.Entry(fm_grid, bd = 2,width = 10,font=('Ubuntu', 10))
En_name.grid(row=i, column=1, padx=2, pady=1, ipadx=35, ipady=5)
En_coor = tk.Entry(fm_grid, bd = 2,width = 10,font=('Ubuntu', 10))
En_coor.grid(row=i, column=2, padx=2, pady=1, ipadx=200, ipady=5)
else:
tk.messagebox.showerror("Error", "Please input a num of conditions")
fm_main = tk.Frame()
fm3 = tk.Frame(fm_main)
lb_condition = tk.Label(fm3,text = 'Please input the number of condition')
lb_condition.pack(side="left")
en_condition_num = tk.Entry(fm3, bd = 2,width = 5)
en_condition_num.pack()
fm3.pack()
btn_generate = tk.Button(fm_main,text="Generate Grid",command=btn_generate)
btn_generate.pack()
lb_en = tk.Label(fm_main,text = '')
lb_en.pack()
def myfunction(event):
canvas.configure(scrollregion=canvas.bbox("all"),width=200,height=200)
canvas=tk.Canvas(fm_main)
fm_grid = tk.Frame(canvas)
fm_grid.pack()
myscrollbar=tk.Scrollbar(fm_main,orient="vertical",command=canvas.yview)
canvas.configure(yscrollcommand=myscrollbar.set)
myscrollbar.pack(side="right",fill="y")
canvas.pack(side="left")
canvas.create_window((4,4),window=fm_grid,anchor='nw')
fm_grid.bind("<Configure>",myfunction)
fm_main.pack()
MainWindow.mainloop()
Question: It doesn't show the full grid in the canvas
You have to sync, the width of the Canvas with the width of the Frame inside.
Note: fm_grid = tk.Frame(canvas, bg='blue') is shown in 'blue'.
Dont's:
Remove fm_grid.pack(), you layout with: canvas.create_window(....
Also, i recommend not to use a offset (4, 4), because you have to calculate with this offset on every canvas.configure(..., width=width + 4. Use (0, 0) instead.
# fm_grid.pack()
...
canvas.create_window((4,4),window=fm_grid,anchor='nw')
Useless, to create dynamically window:
Your usage of canvas.bbox is useless, because it's the dimension you want to layout this widget.
Using a fixed width=200, smaller than fm_grid.width will allways cut the fm_grid content, it's not dynamically either.
canvas.configure(scrollregion=canvas.bbox("all"),width=200,height=200)
How to sync the width of the Canvas with the width of the Frame inside?
You bound fm_grid.bind("<Configure>", therefore the event.widget is fm_grid, the Frame inside.
Get the dimensions of the event.widget from there w.winfo_... and build a bbox tuple to set scrollregion.
Use width to set canvas.width, to be in sync with the event.widget.winfo_width().
class ScrollCanvas(tk.Canvas):
def __init__(self, parent, **kwargs):
super().__init__(parent, **kwargs)
def create_window(self, child):
super().create_window((0, 0), window=child, anchor='nw')
child.bind("<Configure>", self.on_configure)
def on_configure(self, event):
w = event.widget
bbox = x, y, width, height = 0, 0, w.winfo_width(), w.winfo_height()
self.configure(scrollregion=bbox, width=width)
Tested with Python: 3.5 - 'TclVersion': 8.6 'TkVersion': 8.6
I am writing a program to generate draft emails, and I am trying to set it up with a tkinter GUI. I'm on Python 3.5 with tkinter 8.6.
My issue is that I cannot get rowspan to work. I want to have the first row span a few rows, but when I add 'rowspan' the row doesn't change size. If I add 'sticky=tK.N+tK.S', I get errors that those mean nothing. So then I started messing with how I was importing tkinter, trying it as importing from, importing as tk, importing *, but everything I change breaks soemthing else, and still doesn't get the north-south stickiness that I am looking for so that the row will stretch out and fill the space.
I am pretty sure that this is an issue with how I am importing tkinter, and any advice would be tremendously appreciated. I also can't get tkinter to work if I use 'import tkinter as tk" because then I get errors like name 'StringVar' is not defined". I tried fixing that by moving where my root was declared, but that created issues with GUI.
Help! Thanks :)
import os
# pull in GUI stuff
import tkinter as tk #import Tk, Label, Button, W, E, N, S, StringVar, OptionMenu, Entry, Text, END, WORD
from PIL import Image, ImageTk
path = os.getcwd()
# init GUI
class GUI:
def __init__(self, master):
self.master = master
master.title("Email Draft Builder")
# set logo image
f = os.getcwd() + "\\cgs_logo.gif"
# lock in image for use outside this section
im = Image.open(f)
ph = ImageTk.PhotoImage(im)
# vars to show dynamic text for displays 1 and 2
self.box1_titleText = tk.StringVar()
self.box2_titleText = tk.StringVar()
self.box1_content = tk.StringVar()
self.box2_content = tk.StringVar()
# main/container pane info
self.label = tk.Label(master, text="Email Draft Composer", image = ph, bg = "#ffffff")
self.label.image = ph
self.label.grid(columnspan = 3, row = 0, rowspan = 2, sticky=tk.N+
tk.S, column=1)
# ROW 1
self.readLast_button = tk.Button(master, text="Read Training File", command=self.dataOps)
self.readLast_button.grid(row=3, column=0,sticky=tk.W)
# ROW 2
self.file_button = tk.Button(master, text="Unused", command=self.chooseSite)
self.file_button.grid(row=4, column=0,sticky=tk.W)
# ROW 3
self.pullSite_button = tk.Button(master, text="Show Site Info:", command=self.pullSite)
self.pullSite_button.grid(row=5, column=0,sticky=tk.W)
self.getSite = tk.Entry(master)
self.getSite.grid(row=5, column=1,sticky=tk.W)
# ROW 4
self.sendEmail_button = tk.Button(master, text="Build Email", command=self.sendEmail)
self.sendEmail_button.grid(row=6, column=0,sticky=tk.W)
recipients = ["mgancsos#cogstate.com","mgancsos#gmail.com","kkiernan#cogstate.com;mchabon#cogstate.com","ashortland#cogstate.com"]
self.receiver = tk.StringVar()
self.receiver.set(recipients[0])
self.menu1 = tk.OptionMenu(master, self.receiver, *recipients)
self.menu1.grid(row=6, column=1,sticky=tk.W)
# ROW 5
self.close_button = tk.Button(master, text="Close", command=root.destroy)
self.close_button.grid(row=7, column=0,sticky=tk.W)
# ROW 6
self.box1_title = tk.Label(master, textvariable=self.box1_titleText, bg = "#fffff0", borderwidth=1, relief = "groove", width = 15)
self.box1_title.grid(columnspan=1, row = 8, column=0, sticky=tk.W)
self.box1_pane = tk.Label(master, textvariable=self.box1_content, bg = "#fffff0", borderwidth=1, relief = "groove", width = 55)
self.box1_pane.grid(columnspan=1, row = 8, column=1, sticky=tk.W)
# ROW 7
self.box2_title = tk.Label(master, textvariable=self.box2_titleText, bg = "#ffff00", borderwidth=1, relief = "groove", width = 15)
self.box2_title.grid(columnspan=1, row = 9, column=0, sticky='nw')
self.box2_pane = tk.Label(master, textvariable=self.box2_content, bg = "#ffffff", borderwidth=1, relief = "groove", width = 55)
self.box2_pane.grid(columnspan=1, row = 9, column=1, sticky='NW')
# ROW 8
self.display1 = tk.Text(master, wrap=tk.WORD)
self.display1.grid(columnspan=1, row = 10, column=1, sticky=tk.W)
def dataOps(self):
return(1)
def chooseSite(self):
return(1)
def pullSite(self):
return(1)
def sendEmail(self):
return(1)
root = tk.Tk()
root.geometry('800x800')
root["bg"] = "#ffffff"
my_gui = GUI(root)
root.mainloop()