How to animate a "movie style" credit reel with tkinter and canvas? - python

I am trying to create a "movie" style credit reel using tkinter's canvas. I'd like to iterate through a list of names and have them scroll across the window. While I've successfully gotten a single name to scroll, I'm struggling to iterate over several names.
I appreciate your help and apologies for any gross oversights on my part.
from tkinter import *
import time
window = Tk()
window.geometry("1920x1080")
window.title("Window 1")
canvas1 = Canvas(window, width=1920, height=1080, bg="green", bd=0, highlightthickness=0, relief='ridge')
canvas1.pack()
class CreditList:
def __init__(self, text):
self.text = text
self.location = 1080
def credit_roll_function(self):
text = canvas1.create_text(960,self.location, text=self.text, anchor=S, fill="black",
font="Time 40")
while True:
canvas1.move(text, 0, -3)
window.update()
time.sleep(.03)
credit_list = ["First Name", "Second Name", "Third Name"]
for credit in credit_list:
item = CreditList(credit)
item.credit_roll_function()
window.mainloop()

Here's a simple way to do it that uses the universal widget after() method instead of calling time.sleep() which interferes with tkinter's mainloop(). Each time the class' roll_credits() method is called, it moves the text upward a little and schedules another call to itself if the text isn't at the top of the window yet.
import tkinter as tk
from tkinter.constants import *
class CreditList:
def __init__(self, lines):
self.location = HEIGHT
self.text = canvas.create_text(0, 0, text='\n'.join(lines), justify=CENTER,
anchor=NW, fill='black', font='Time 40')
xl, yt, xr, yb = canvas.bbox(self.text)
txtwidth = xr - xl
xpos = (WIDTH-txtwidth) // 2 # To center text horizontally.
canvas.move(self.text, xpos, self.location)
def roll_credits(self):
xl, yt, xr, yb = canvas.bbox(self.text)
if yb <= 0: # Completely off top of screen?
canvas.pack_forget()
tk.Button(text='Done', font=('Courier New', 20), relief=GROOVE, bg='orange',
command=window.quit).place(x=WIDTH/2, y=HEIGHT/2)
return # Stop.
canvas.move(self.text, 0, -3)
window.after(DELAY, self.roll_credits) # Keep going.
DELAY = 25 # Millisecs.
window = tk.Tk()
window.attributes('-fullscreen', True)
window.update_idletasks()
WIDTH, HEIGHT = window.winfo_width(), window.winfo_height() # Get screen size.
window.geometry(f'{WIDTH}x{HEIGHT}+0+0')
window.title('Window 1')
canvas = tk.Canvas(window, width=WIDTH, height=HEIGHT, bg='green', bd=0,
highlightthickness=0, relief='ridge')
canvas.pack()
credits = 'First Name', 'Second Name', 'Third Name'
cl = CreditList(credits)
window.after(DELAY, cl.roll_credits) # Start rolling credits "loop".
window.mainloop()

If you give all of your text items the same tag, you can move them all at the same time. Another solution is to create a single text item that is created by joining all of your strings with a newline.
For example, the following code illustrates how to create all of the items with a given tag, then moves them all one pixel at a time every 33 ms until they no longer are visible.
import tkinter as tk
class CreditList(tk.Canvas):
def __init__(self, master, **kwargs):
super().__init__(master, **kwargs)
def roll_credits(self, credit_list):
x = self.winfo_width() // 2
y = self.winfo_height()
temp = self.create_text(0,0,text="Hello")
bbox = self.bbox(temp)
self.delete(temp)
lineheight = bbox[3]-bbox[1]
linespacing = 4
for credit in credit_list:
self.create_text(x, y, text=credit, anchor="n", tags=("credit",))
y += lineheight + linespacing
self._animate()
def _animate(self):
self.move("credit", 0, -1)
x0, y0, x1, y1 = self.bbox("credit")
if y1 > 0:
self.after(33, self._animate)
root = tk.Tk()
credits = CreditList(root)
credits.pack(side="top", fill="both", expand=True)
credit_list = 'First Name', 'Second Name', 'Third Name'
root.after(1000, credits.roll_credits, credit_list)
root.mainloop()

Related

Why am I getting a value error in imagegrab pil?

So I am making an Tkinter painting application but I keep getting a Value:Error which is blocking me from proceeding and I don't know how to solve it.
here is my code
from tkinter import *
from tkinter import Scale
from tkinter import colorchooser, filedialog, messagebox
import PIL.ImageGrab as ImageGrab
class Paint(): # the class adds the screen for all the widgets to be added on
def __init__(self,root):
global color
self.root = root
self.root.title("Paint")
self.root.geometry("800x520")
self.root.configure(background='white')
self.root.resizable(100,100)
self.pen_color ="black"
self.eraser_color = "white"
# Adding the widgets
self.color_frame = LabelFrame(self.root, text='Color Palette', font=("Lucida's Caligraphy",15),bd=10, relief=RIDGE, bg='pink')
self.color_frame.place(x=0,y=0,width=145,height=190)
colors = ["#ff00ff", "#ff0000", "#ffa600", "#ffff00", "#80ff80", "#00ff00", "#09ff90", "#0ff8c0", "#00d0ff", "#00ffff", "#ffffff", "#fff3d4", "#808080", "#404040", "#202020", "#000000"]
i=j=0
for color in colors: # this is the creation for the positioning of the colors
Button(self.color_frame,bg=color,bd=2,relief=RIDGE,width=3,command=lambda col=color:self.select_color(col)).grid(row=i,column=j) # this is the creation for the color buttons
i+=1
if i==4:
i=0
j+=1
self.eraser_button = Button(self.root,text='Eraser',bg='violet',bd=2,relief=GROOVE,width=3,command=self.eraser) # this is the creation for the eraser button
self.eraser_button.place(x=0, y=187, width=60, height=30)
self.clear_button = Button(self.root,text='Clear',bg='light blue',bd=2,relief=GROOVE,width=3,command=lambda : self.canvas.delete("all")) # this is the creation for the clear button
self.clear_button.place(x=0, y=217, width=60, height=30)
self.save_button = Button(self.root,text='Save',bg='light green',bd=2,relief=GROOVE,width=3,command=self.save_paint) # this is the creation for the save button
self.save_button.place(x=0, y=247, width=60, height=30)
self.canvas_button = Button(self.root,text="Canvas",bg='light cyan', bd=4, width=8,relief=GROOVE,command=self.canvas_color) # this is the creation for the canvas button
self.canvas_button.place(x=0, y=277)
self.pen_scale_frame = LabelFrame(self.root,text='size', bg='white', bd=5, font=('arial', 15), relief=RIDGE,) # this is the creation for the box that contains the colors
self.pen_scale_frame.place(x=0, y=310, width=70, height=200)
self.pen_scale = Scale(self.pen_scale_frame, orient=VERTICAL,from_=100, to=0,length=150) # this is the creation for the scale
self.pen_scale.set(1)
self.pen_scale.place(x=5,y=10)
self.canvas = Canvas(self.root, bg='light cyan', bd=4, relief=GROOVE, width=1105, height=630) # this is the creation for the canvas
self.canvas.place(x=150, y=0)
self.canvas.bind("<B1-Motion>",self.paint) # this binds the mouse motion with the canvas
def paint(self,event): # this is the function for the ink to be shown on the canvas
x1,y1 = (event.x-0),(event.y-0)
x2,y2 = (event.x+0), (event.y+0)
self.canvas.create_oval(x1,y1,x2,y2,fill=self.pen_color,outline=self.pen_color,width=self.pen_scale.get())
def select_color(self, col): # this is the function for selecting colors for my pen
self.pen_color = col
def eraser(self): # this is the function for copying the colors that my canvas has for my eraser
self.pen_color = self.eraser_color
def canvas_color(self): # this is the function for selecting colors for my canvas
color = colorchooser.askcolor()
self.canvas.configure(background=color[1])
self.eraser_color = color[1]
self.pen_color = color[1]
def save_paint(self): # this is the function for screenshotting whatever is on the canvas
global filename
filename = filedialog.asksaveasfilename(initialdir='C:/',filetypes=[('All files','.*'), ('Picture Files','.png*')])
print(filename)
cx = self.root.winfo_rootx() + self.canvas.winfo_x()
print(cx, self.root.winfo_rootx())
cy = self.root.winfo_rooty() + self.canvas.winfo_y()
print(cy)
cx1 = cx + self.canvas.winfo_width()
print(cx1)
cy1 = cy + self.canvas.winfo_height()
print(cy1)
filename = filename + '.jpeg'
ImageGrab.grab().crop((cx1, cy1, cx, cy)).save(filename) # <-- The value:Error redirects me here
messagebox.showinfo('paint sys','image is saved as '+ str(filename))
if __name__ == "__main__":
root = Tk()
p = Paint(root)
root.mainloop()
the error
**ImageGrab.grab().crop((cx1, cy1, cx, cy)).save(filename) line 1171, in crop
raise ValueError("Coordinate 'right' is less than 'left'")
ValueError: Coordinate 'right' is less than 'left'
**
Whats wrong with my code?

Canvas line jump

I'm learning new python. I'm trying to design a chatbot. I've added the codes I've made so far. I want the distance between the bubbles to remain constant in long articles. Can you help with this? I'm sorry about my bad English.
from tkinter import *
from datetime import datetime
import random
import re
from tkinter import messagebox
from tkinter.font import Font
import textwrap
root = Tk()
root.config(bg="lightblue")
root.geometry('410x600+400+100')
#ana ekran
canvas = Canvas(root, width=200, height=200,bg="white")
canvas.grid(row=0,column=0,columnspan=2)
canvas.place(x=10, y=10, width=390, height=530)
balon = []
class balon1:
def __init__(self,master,message=""):
self.master = master
self.frame = Frame(master,bg="light green")
self.i = self.master.create_window(30,490,window=self.frame, anchor="sw")
Label(self.frame,text=datetime.now().strftime("%d-%m-%Y %X"),font=("Helvetica", 7),bg="light green").grid(row=0,column=0,sticky="w",padx=5) #tarih saat
Label(self.frame, text=textwrap.fill(message, 35), font=("Helvetica", 9),bg="light green").grid(row=1,column=0,sticky="w",padx=5,pady=3)
root.update_idletasks()
self.master.create_polygon(self.draw_triangle(self.i), fill="light green", outline="light green")
def draw_triangle(self,widget):
x1, y1, x2, y2 = self.master.bbox(widget)
return x1, y2 - 10, x1 - 15, y2 + 10, x1, y2
def send_message():
if balon:
canvas.move(ALL, 0, -70)
a = balon1(canvas,message=entry.get())
balon.append(a)
#mesaj yazma alanı
entry = Entry(root,width=26, font=("Helvetica", 10))
entry.place(x=10, y=550, width=290, height=40)
#buton
buton = Button(root, width=10, height=2, relief='raised',state='active',command=send_message)
buton.config(text='GÖNDER', bg='lightblue', font='Verdana 8 bold')
buton.place(x=310, y=550)
root.mainloop()
I think the easiest thing would be to start by creating the bubble and then getting the height of the bubble. Then, move everything up by that amount plus a margin, and then move the bubble to where it belongs.
Even easier might be to simply add the new bubble under the current bubble and then scrolling everything up so that it is in view.

Python Tkinter, Display Live Data

I want to display live data in a GUI, in tkinter. The data I am getting contains a list of two integers [current, voltage]. I am getting new data every second.
I managed to create a GUI, now I want to know how to display data in GUI Label widgets (python tkinter) and update labels dynamically. Any suggestions please
Here is my code so far:
#data getting is a list eg. [10, 12]
from tkinter import *
import tkinter.font
#main Window using Tk
win = Tk()
win.title("v1.0")
win.geometry('800x480')
win.configure(background='#CD5C5C')
#Labels
voltage = Label(win, text = "voltage")
voltage.place(x=15, y=100)
current = Label(win, text = "current")
current.place(x=15, y=200)
#display measured values
#how to display here !!!
currentValues = Label(win, text = "want to display somewhere like this")
currentValues.place(x=200, y=100)
voltageValues = Label(win, text = "want to display somewhere like this")
voltageValues.place(x=200, y=200)
mainloop()
If you want to graph your live data and want to avoid using other libraries to do that for you, you might find the following to be an enlightening starting point for creating your own graphs. The sample draws a full circle of values when evaluating the math.sin function that comes in the standard library. The code takes into account automatic sampling, resizing, and updating as needed and should be fairly responsive.
#! /usr/bin/env python3
import math
import threading
import time
import tkinter.ttk
import uuid
from tkinter.constants import EW, NSEW, SE
class Application(tkinter.ttk.Frame):
FPS = 10 # frames per second used to update the graph
MARGINS = 10, 10, 10, 10 # internal spacing around the graph
#classmethod
def main(cls):
tkinter.NoDefaultRoot()
root = tkinter.Tk()
root.title('Tkinter Graphing')
# noinspection SpellCheckingInspection
root.minsize(640, 480) # VGA (NTSC)
cls(root).grid(sticky=NSEW)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
root.mainloop()
def __init__(self, master=None, **kw):
super().__init__(master, **kw)
self.display = tkinter.Canvas(self, background='white')
self.display.bind('<Configure>', self.draw)
self.start = StatefulButton(self, 'Start Graphing', self.start_graph)
self.grip = tkinter.ttk.Sizegrip(self)
self.grid_widgets(padx=5, pady=5)
self.data_source = DataSource()
self.after_idle(self.update_graph, round(1000 / self.FPS))
self.run_graph = None
def grid_widgets(self, **kw):
self.display.grid(row=0, column=0, columnspan=2, sticky=NSEW, **kw)
self.start.grid(row=1, column=0, sticky=EW, **kw)
self.grip.grid(row=1, column=1, sticky=SE)
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
def start_graph(self):
self.run_graph = True
threading.Thread(target=self.__simulate, daemon=True).start()
return 'Stop Graphing', self.stop_graph
def stop_graph(self):
self.run_graph = False
return 'Clear Graph', self.clear_graph
def clear_graph(self):
self.data_source.clear()
self.reset_display()
return 'Start Graphing', self.start_graph
# def __simulate(self):
# # simulate changing populations
# for population in itertools.count():
# if not self.run_graph:
# break
# self.data_source.append(population, get_max_age(population, 200))
# def __simulate(self):
# # simulate changing ages
# for age in itertools.count(1):
# if not self.run_graph:
# break
# self.data_source.append(age, get_max_age(250_000_000, age))
def __simulate(self):
# draw a sine curve
for x in range(800):
time.sleep(0.01)
if not self.run_graph:
break
self.data_source.append(x, math.sin(x * math.pi / 400))
def update_graph(self, rate, previous_version=None):
if previous_version is None:
self.reset_display()
current_version = self.data_source.version
if current_version != previous_version:
data_source = self.data_source.copy()
self.draw(data_source)
self.after(rate, self.update_graph, rate, current_version)
def reset_display(self):
self.display.delete('data')
self.display.create_line((0, 0, 0, 0), tag='data', fill='black')
def draw(self, data_source):
if not isinstance(data_source, DataSource):
data_source = self.data_source.copy()
if data_source:
self.display.coords('data', *data_source.frame(
self.MARGINS,
self.display.winfo_width(),
self.display.winfo_height(),
True
))
class StatefulButton(tkinter.ttk.Button):
def __init__(self, master, text, command, **kw):
kw.update(text=text, command=self.__do_command)
super().__init__(master, **kw)
self.__command = command
def __do_command(self):
self['text'], self.__command = self.__command()
def new(obj):
kind = type(obj)
return kind.__new__(kind)
def interpolate(x, y, z):
return x * (1 - z) + y * z
def interpolate_array(array, z):
if z <= 0:
return array[0]
if z >= 1:
return array[-1]
share = 1 / (len(array) - 1)
index = int(z / share)
x, y = array[index:index + 2]
return interpolate(x, y, z % share / share)
def sample(array, count):
scale = count - 1
return tuple(interpolate_array(array, z / scale) for z in range(count))
class DataSource:
EMPTY = uuid.uuid4()
def __init__(self):
self.__x = []
self.__y = []
self.__version = self.EMPTY
self.__mutex = threading.Lock()
#property
def version(self):
return self.__version
def copy(self):
instance = new(self)
with self.__mutex:
instance.__x = self.__x.copy()
instance.__y = self.__y.copy()
instance.__version = self.__version
instance.__mutex = threading.Lock()
return instance
def __bool__(self):
return bool(self.__x or self.__y)
def frame(self, margins, width, height, auto_sample=False, timing=False):
if timing:
start = time.perf_counter()
x1, y1, x2, y2 = margins
drawing_width = width - x1 - x2
drawing_height = height - y1 - y2
with self.__mutex:
x_tuple = tuple(self.__x)
y_tuple = tuple(self.__y)
if auto_sample and len(x_tuple) > drawing_width:
x_tuple = sample(x_tuple, drawing_width)
y_tuple = sample(y_tuple, drawing_width)
max_y = max(y_tuple)
x_scaling_factor = max(x_tuple) - min(x_tuple)
y_scaling_factor = max_y - min(y_tuple)
coords = tuple(
coord
for x, y in zip(x_tuple, y_tuple)
for coord in (
round(x1 + drawing_width * x / x_scaling_factor),
round(y1 + drawing_height * (max_y - y) / y_scaling_factor)))
if timing:
# noinspection PyUnboundLocalVariable
print(f'len = {len(coords) >> 1}; '
f'sec = {time.perf_counter() - start:.6f}')
return coords
def append(self, x, y):
with self.__mutex:
self.__x.append(x)
self.__y.append(y)
self.__version = uuid.uuid4()
def clear(self):
with self.__mutex:
self.__x.clear()
self.__y.clear()
self.__version = self.EMPTY
def extend(self, iterable):
with self.__mutex:
for x, y in iterable:
self.__x.append(x)
self.__y.append(y)
self.__version = uuid.uuid4()
if __name__ == '__main__':
Application.main()
You can change label text dynamically:
This is a way using textvariable option with StringVar and .set() method
str_var = tk.StringVar(value="Default")
currentValues= Label(win, textvariable=my_string_var)
currentValues.place(x=200, y=100)
str_var.set("New value")
Another way using simply .configure() method
currentValues = Label(win, text = "default")
currentValues.configure(text="New value")
Finally, to make the UI update without waiting the rest of the loop do an update
win.update()
I figured out a liveplot inspired by a demo, where I used this to plot the realtime current-voltage scan in Keithley2410.
The whole script is below:
import matplotlib.pyplot as plt
import numpy as np
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from tkinter import ttk
x_data, y_data = [], []
class Win(tk.Tk):
def __init__(self):
super().__init__()
self.title('I-V liveplot')
self.geometry('500x450')
# Frame that holds wigets on the left side
left_frame = ttk.Frame(self)
left_frame.pack(side= "left", padx =10, pady = 10) #, fill="y", expand=True
self.fig = plt.figure(figsize=(4, 3.5), dpi=100)
self.ax = self.fig.add_subplot(1,1,1)
self.line, = self.ax.plot([0], [0])
self.ax.set_xlabel('Voltage / V', fontsize = 12)
self.ax.set_ylabel('Current / A', fontsize = 12)
self.fig.tight_layout()
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.toolbar = NavigationToolbar2Tk(self.canvas, self)
self.canvas.get_tk_widget().pack(side= tk.BOTTOM)
voltage_range_label = tk.Label(left_frame, text = "Voltage range")
voltage_range_label.pack(side = "top", padx =10, pady =2)
self.voltage_range = tk.IntVar()
self.voltage_range.set(10)
voltage_range_spinbox = ttk.Spinbox(left_frame, from_=-3e2, to = 5e2, textvariable = self.voltage_range, width=8)
voltage_range_spinbox.pack(side="top", padx =10, pady =5)
voltage_step_label = tk.Label(left_frame, text = "Step")
voltage_step_label.pack(side = "top", padx =10, pady =2)
self.step = tk.IntVar()
self.step.set(1)
step_spinbox = ttk.Spinbox(left_frame, from_=-3e2, to = 5e2, textvariable = self.step, width =9)
step_spinbox.pack(side="top", padx =10, pady =5)
self.start = tk.BooleanVar(value = False)
start_butt = ttk.Button(left_frame, text="Start", command= lambda: self.start.set(True))
start_butt.pack(side='top', padx =10, pady =10)
stop_butt = ttk.Button(left_frame, text="Resume", command=lambda: self.is_paused.set(False))
stop_butt.pack(side="top", padx =10, pady =10)
self.is_paused = tk.BooleanVar() # variable to hold the pause/resume state
restart_butt = ttk.Button(left_frame, text="Pause", command=lambda: self.is_paused.set(True))
restart_butt.pack(side="top", padx =10, pady =10)
def update(self, k=1):
if self.start.get() and not self.is_paused.get():
# quasi For Loop
idx = [i for i in range(0, k, self.step.get())][-1]
x_data.append(idx)
y_data.append(np.sin(idx/5))
self.line.set_data(x_data, y_data)
self.fig.gca().relim()
self.fig.gca().autoscale_view()
self.canvas.draw()
#self.canvas.flush_events()
k += self.step.get()[![enter image description here][2]][2]
if k <= self.voltage_range.get():
self.after(1000, self.update, k)
if __name__ == "__main__":
app = Win()
app.after(1000, app.update)
app.mainloop()
This code works properly and results in an output shown in Graph. I hope it would be helpful.
.
I want to display some live data in a GUI.
I think what you want to do is use the .after() method. The .after() method queues tkinter to run some code after a set time.
For example:
currentValues = Label(win, text = "want to display somewhere like this")
currentValues.place(x=200, y=100)
voltageValues = Label(win, text = "want to display somewhere like this")
voltageValues.place(x=200, y=200)
def live_update():
currentValues['text'] = updated_value
voltageValues['text'] = updated_value
win.after(1000, live_update) # 1000 is equivalent to 1 second (closest you'll get)
live_update() # to start the update loop
1000 units in the after method is the closest you'll get to 1 second exactly.

Getting current value when dragging canvas

I am trying to make a simple card game, something like Solitaire.
I am not experienced in coding, so forgive me if it's a simple question.
I want to move some canvas objects. New objects have the right value, but when i am dragging an already existing card it shows the wrong value (waarde in Dutch). I would like to bind the value (waarde) to a card, but don't know how to do that...
Thought about tags, binding, ID....
from tkinter import *
from random import randint
window = Tk()
deck = [1,2,3,4,5,6]
def pakkaart():
rand_card = randint(0,len(deck)-1)
global waarde
waarde = deck[rand_card]
deck.pop(rand_card)
global kaart
kaart = Canvas(window, width = 40, height = 40, bg='yellow')
kaart.place(x=50, y=50, anchor=CENTER)
kaart.create_text(20,20,text=(waarde))
kaart.bind("<B1-Motion>", drag)
def drag(event):
event.widget.place(x=event.x_root, y=event.y_root,anchor=CENTER)
print(waarde)
button1 = Button(window, text="Nieuwe Kaart", command=pakkaart)
button1.pack()
window.mainloop()
So essentially looking for a way to bind a value to a canvas.
Your above code works fine, it shows the correct value, but if you want you can try this
from tkinter import *
from random import randint
window = Tk()
ws = window.winfo_screenwidth()
hs = window.winfo_screenheight()
w = 500 # width for the Tk root
h = 300 # height for the Tk root
x = (ws / 2) - (w / 2)
y = (hs / 2) - (h / 2)
window.geometry('%dx%d+%d+%d' % (w, h, x, y))
deck = [1, 2, 3, 4, 5, 6]
def pick_card():
global waarde, kaart
rand_card = randint(0, len(deck)-1)
card_number = deck[rand_card]
deck.remove(card_number)
card = Canvas(window, width=40, height=40, bg='yellow')
card.place(x=50, y=50, anchor=CENTER)
card_number_text = card.create_text(20, 20, text=card_number, tags=card_number)
card.bind("<Button-1>", lambda event: get_number(event, card_number_text)) # or you can use: card.bind("<Button-1>", lambda event: print(card_number))
card.bind("<B1-Motion>", drag)
def drag(event):
# This is better for move a widget
cx = window.winfo_pointerx() - window.winfo_rootx()
cy = window.winfo_pointery() - window.winfo_rooty()
event.widget.place(x=cx, y=cy)
def get_number(event, number):
print(event.widget.itemcget(number, "text"))
button1 = Button(window, text="Generate Card", command=pick_card)
button1.pack()
window.mainloop()
I've modified the drag(event) function and wrote two ways for get the current card value, for store it you can use some global varibles or create a class, the second would be better

How do you create a grid of buttons in a frame using .pack() or .grid()?

from tkinter import *
from random import *
class Game:
def __init__(self):
self.root = Tk()
self.frame1 = Frame(self.root, width = 1055, height = 30)
self.frame1.pack()
self.frame_lvl = Frame(self.root, width = 1055, height = 1055)
self.frame_lvl.pack()
for frame_lvl in range(0,31):
self.frame_lvl = Frame(self.root)
self.frame_lvl.pack(side = BOTTOM)
for i in range(0,31):
for j in range(0,31):
button = Button(self.i, width = 30, height = 30, padx = 2, pady = 2)
button.pack(side = LEFT)
self.root.mainloop()
app = Game()
So I try to create a new frame level so the buttons won't keep printing on the same line but I'm not sure if the frame level will be saved as self.0, self.1, self.2, etc...
When I tried making the frame a grid and adjusting the width, height, rowspan, and column span, I got the error ("cannot use geometry manager grid inside . which already has slaves managed by pack") The error comes from these lines:
self.frame2 = Frame(width = 1055, height = 1055)
self.frame2.grid(columnspan = 30, rowspan = 30)
Any suggestions.
Note that there is only one self.frame_lvl so each time you assign it a new value you lose the existing value. In the end it will only contain the last value assigned to it. Also
for i in range(31):
for j in range(31):
creates 31*31 buttons(which is over 900 buttons). While you are learning, stick with all grid() or all pack() in a program until you learn how to mix the two. To create 3 rows of 5 buttons using grid()
from tkinter import *
from functools import partial
class Game:
def __init__(self):
self.root = Tk()
self.frame1 = Frame(self.root, width = 900, height = 30)
self.frame1.grid()
self.label=Label(self.frame1, text="")
self.label.grid(row=0, column=0, columnspan=5)
## save each button id in a list
self.list_of_button_ids=[]
for ctr in range(15):
## use partial to pass the button number to the
## function each time the button is pressed
button = Button(self.frame1, text=str(ctr), width = 20,
height = 20, padx = 2, pady = 2,
command=partial(self.button_callback, ctr))
this_row, this_col=divmod(ctr, 5)
## the label is in the top row so add one to each row
button.grid(row=this_row+1, column=this_col)
## add button's id to list
self.list_of_button_ids.append(button)
self.root.mainloop()
def button_callback(self, button_num):
""" display button number pressed in the label
"""
self.label.config(text="Button number %s and it's id=%s"
% (button_num, self.list_of_button_ids[button_num]))
app = Game()

Categories