I am new to tkinter and trying to make a basic drawing app. However, when I move my cursor around, it will sometimes suddenly stop drawing, and then only show the finished line several seconds later. This is shown here.
https://www.youtube.com/edit?video_referrer=watch&video_id=_g8n55V6qPQ
Is this lag? My laptop runs fine otherwise, and it can even 'lag' on the first go (i.e. before there are any other objects on the canvas). If it is just lag, what sort of workarounds do I have in making my python drawing app?
This is my code:
from tkinter import *
root = Tk()
root.title("Note Taking")
can_width = 800
can_height = 800
canvas = Canvas(root, width=can_width, height=can_height, bg='white')
canvas.pack(padx=20, pady=20)
class g():
points = []
user_line = None
drawing = False
t = 0
def leftClick(event):
g.points = []
g.user_line = None
g.drawing = True
g.points.append(event.x)
g.points.append(event.y)
def leftMove(event):
# Print out an increasing number: t, so I can see it in the output
print(g.t)
g.t+=1
if g.drawing:
g.points.append(event.x)
g.points.append(event.y)
if g.user_line == None:
g.user_line = canvas.create_line(g.points, width=4, smooth=1)
else:
canvas.coords(g.user_line, g.points)
def leftRelease(event):
g.points = []
g.user_line = None
canvas.bind('<Button-1>', leftClick)
canvas.bind('<B1-Motion>', leftMove)
canvas.bind('<ButtonRelease-1>', leftRelease)
root.mainloop()
Related
I have made a tkinter country guessing game witch works fine however takes along time to run. So i made a loading screen for it with a looping animation on in a separate file. I cant find a way to run the loading screen first and then run the game whilst the animation on the loading screen is still running.
Loading screen code:
from tkinter import *
from time import *
import os
import random
run = 0
loads = True
dotnum = 0
def task():
sleep(2)
root.destroy()
root = Tk()
root.title("Loading...")
root.geometry("1280x720")
Background = PhotoImage(file = "Images\Loadscreen.png")
Loaders = PhotoImage(file = "Images\Loader.gif")
image = Label(root,width=1000,height=500,image=Background)
image.place(x=0, y=0, relwidth=1, relheight=1)
frameCnt = 16
frames = [PhotoImage(file='Images\Loader.gif',format = 'gif -index %i' %(i)) for i in range(frameCnt)]
def update(ind):
frame = frames[ind]
ind += 1
if ind == frameCnt:
ind = 0
loadanim.configure(image=frame)
root.after(100, update, ind)
loadanim = Label(root, bg = "black")
loadanim.place(x = 450, y = 450)
root.after(0, update, 0)
root.mainloop()
To run the loading screen first, you can call the loading screen code before calling the game code.
You can modify the code to pass a function as an argument to the loading screen, and then call this function after the loading screen is destroyed. This will allow you to run the game code after the loading screen is done.
For example:
def run_game():
# game code here
pass
def run_loading_screen(callback):
# loading screen code here
root.after(2000, callback)
root.mainloop()
run_loading_screen(run_game)
Here, run_game is the function containing the game code, and run_loading_screen is the function containing the loading screen code. The run_loading_screen function takes a callback argument, which is the function to be called after the loading screen is destroyed. In the loading screen code, the root.after method is used to call the callback function after 2000 milliseconds (2 seconds).
I'm working on a checkers game GUI, and i can't seem to figure out why it keeps opening two windows instead of the one i initialize.
Here's my code: https://pastebin.com/s7XWLdfY
And here's a screenshot:link
Any help would be really appreciated, i'm fairly new to tkinter.
def init():
global root
global extraref #creates a reference to the pieces images
#so they don't get garbage collected
extraref = np.array([tk.Label() for item in range(64)]).reshape(8, 8)
def switch(board, val):
switcher = {
Board.E: "imgs/E.gif",
Board.W: "imgs/W.gif",
Board.B: "imgs/B.gif",
Board.DW: "imgs/DW.gif",
Board.DB: "imgs/DB.gif",
}
return switcher.get(val)
def refresh(board): #refreshes the board after a turn
mat = board.getMat()
#print(mat)
for i in range(8):
for j in range(8):
path = switch(board, mat[i][j])
#path = "imgs/B.gif"
tmp_img = Image.open(path)
img = itk.PhotoImage(tmp_img, master=root)
extraref[i][j] = tk.Label(root)
extraref[i][j].img = img
extraref[i][j].config(image = extraref[i][j].img)
butts[i*8+j].config(image = extraref[i][j].img)
extraref[i][j].pack()
init()
root = tk.Tk()
frame = tk.Frame(root, width=800, height=800, background="white")
frame.pack_propagate(0)
frame.pack()
lab = tk.Label(frame)
lab.pack()
butts = list()
for i in range (8):
for j in range (8):
lab.grid(row=i, column=j)
butt = tk.Button(lab, bg=("black" if i%2 != j%2 else "white"))
#butt.config(height=5, width=8)
#butt.bind("<Enter>", on_enter)
#butt.bind("<Leave>", on_leave)
butts.append(butt)
butt.grid(row=i, column=j) #creating button grid
board = Board()
refresh(board) #places pieces on the grid
root.mainloop()
P.S: for the complete code please check pastebin, i had to cut some functions to be able to post it
You are calling tk.Label() inside init() function which will create the first window. Then root = tk.Tk() will create the second window.
You should call init() after root = tk.Tk():
root = tk.Tk()
init()
frame = ...
I am trying to update some buttons at the for loop in the end, which buttons I don't want to create individually.
I tried using:
While True:
root.update()
and the other type of update and also each one individually but no luck.
This is the code:
from tkinter import *
from tkinter.ttk import *
from PIL import Image
from functools import partial
import random
#define variables
#main_window_width = 500
#main_window_height = 500
#def functions
buttons_list = []
def InitButtons(n, k):
if k == 0:
k = n
for i in range(n):
InitButtons_column = i
if i//k > 0:
InitButtons_column = i%k
buttons_list.append(Label(main_window, image=button_img))
buttons_list[i].grid(row=(i//k), column=InitButtons_column)
def Hovering(e):
button_img = PhotoImage(file = '/home/klet/Desktop/projects/Python/GUI/button2.png')
buttons_list_button.config(image = button_img)
buttons_list_button.image = button_img
def Clicking(e):
button_img = PhotoImage(file = '/home/klet/Desktop/projects/Python/GUI/button3.png')
buttons_list_button.config(image = button_img)
buttons_list_button.image = button_img
def NotHovering(e):
button_img = PhotoImage(file = '/home/klet/Desktop/projects/Python/GUI/button1.png')
buttons_list_button.config(image = button_img)
buttons_list_button.image = button_img
def UpdatingButtons(n):
n.bind("<Enter>", Hovering)
n.bind("<Leave>", NotHovering)
n.bind("<Button-1>", Clicking)
n.bind("<ButtonRelease-1>", Hovering)
root = Tk()
button_img = PhotoImage(file = "/home/klet/Desktop/projects/Python/GUI/button1.png")
main_window = Frame(root)
main_window.place(relx=0.1, rely=0.1, relwidth=0.8, relheight=0.8)
InitButtons(10, 5)
#buttons_list_tmp1 = 0
#buttons_list_tmp1 += 1
#buttons_list_button =
#UpdatingButtons(buttons_list_tmp1)
#print(buttons_list_tmp1)
for i in range(len(buttons_list)):
buttons_list_button = buttons_list[i]
UpdatingButtons(buttons_list_button)
print(i)
root.mainloop()
The for loop above is only repeating once and i want it to be constantly updating, I also tried putting the whole code (within root and root.mainloop()) in a while loop but didnt work. I also searched some questions asked but they didnt seem to help.
I want to update the buttons(labels in this case) when I enter the button and when i click. Heres an example of what currently happening:
Link to vid
I made this account just to ask this question because I really dont like asking questions.
I am trying to make a ball go to one side of the screen turn around, then come back. Whenever I try running this program the tkinter window doesn't show up, but when i get rid of the sleep(0.5) part it shows up when the ball is already off the screen. Can someone tell my what I did wrong?
from tkinter import *
from time import sleep
window = Tk()
cWidth = 800
cHeight = 500
c = Canvas(window, width = cWidth, height = cHeight, bg='black')
c.pack()
x = 400
y = 250
ball = c.create_polygon(x, y, x, y+25, x+25, y+25, x+25,y, fill='yellow')
Ball_move = 10
for i in range(200):
c.move(ball, Ball_move, 0)
window.update
x += Ball_move
if x == cWidth:
Ball_move = -Ball_move
sleep(0.5)
window.mainloop()
In windows, Tkinter frame shows up only after you call mainloop(). In your case, the for loop might be blocking it. Keep the for loop in a function and then call that function using threads so that it won't block the main loop.
from Tkinter import *
import tkFileDialog
import tkMessageBox
import os
import ttk
import serial
import timeit
import time
######################################################################################
class MyApp:
def __init__(self, parent):
########################################################
#Setup Frames
self.MiddleFrame = Frame(parent) #Middle Frame
self.MiddleFrame.pack()
#GLOBAL VARIABLES
self.chip_number = 0 #number of chip testing
###########################################
#Middle Frame setup
Label(self.MiddleFrame, text='Done').grid(row=8, column=1, sticky = E)
self.Done = Canvas(self.MiddleFrame, bg="yellow", width=10, height=10)
self.Done.grid(row=8, column=2)
Label(self.MiddleFrame, text='Chip Number:').grid(row=9, column=1, sticky = E)
#start button
self.button1 = Button(self.MiddleFrame,state=NORMAL, command= self.start_pre)
self.button1["text"]= "START"
self.button1.grid(row=1, column=2, sticky = E)
###########################################
#Action of Start Button
def start_pre(self):
x = 0
while x<10000:
self.start_button()
x=x+1
#Talking to Board
def start_button(self):
#increase chip count number and update
self.chip_number += 1
Label(self.MiddleFrame, text=str(self.chip_number)).grid(row=9, column=2, sticky = E)
#reset-yellow
self.reset_color()
print "Still Working", self.chip_number
self.Done.configure(background="green")
self.Done.update_idletasks()
###############################################################
#Color Boxes
#Reset
def reset_color(self):
self.Done.configure(background="yellow")
self.Done.update_idletasks()
###############################################################################################################
#Start Programs
root = Tk() #makes window
root.title("Interface")
myapp = MyApp(root) #this really runs program
root.mainloop() #keep window open
With my program, i first push the start button.
I will print "still working" and the GUi will update chip number and blink done light over and over. The start button go to function that will execute 10000 times. However after 3000 iterations, the gui freeze, but the program is still print "still working". How do I keep the gui from crashing?
There are many problems with your code. For one, this is fundamentally flawed:
while self.stop == True:
self.start_button()
time.sleep(0.5)
You simply can't expect a GUI to behave properly with code like that. As a general rule of thumb you should never have the main thread of a GUI call sleep. Causing sleep prevents the event loop from processing any events, including low level events such as requests to refresh the screen.
The use of sleep has been asked and answered many times on stackoverflow. You might find some of those questions useful. For example,
windows thinks tkinter is not responding
Python Tkinter coords function not moving canvas objects inside loop
How do widgets update in Tkinter?
Tkinter multiple operations
Python Tkinter Stopwatch Error
You have another problem that falls into the category of a memory leak. From that while loop, you call self.start_button() indefinitely. This happens about once a second, due to sleep being called for half a second in the loop, and another half a second in start_button.
Each time you call start_button, you create another label widget that you stack on top of all previous widgets in row 9, column 2. Eventually this will cause your program to crash. I'm surprised that it causes your program to fail so quickly, but that's beside the point.
My recommendation is to start over with a simple example that does nothing but update a label every second. Get that working so that you understand the basic mechanism. Then, once it's working, you can add in your code that reads from the serial port.
May I suggest that you start over with the following code? You can port in back to Python 2 if needed, but your program has been rewritten to use Python 3 and has been designed to use tkinter's ability to schedule future events with the after methods. Hopefully, you will find the code easier to follow.
import collections
import timeit
import tkinter
def main():
root = Application()
root.setup()
root.mainloop()
class Application(tkinter.Tk):
def setup(self):
mf = self.__middle_frame = tkinter.Frame(self)
self.__middle_frame.grid()
bf = self.__bot_frame = tkinter.Frame(self)
self.__bot_frame.grid()
self.__port_set = False
self.__chip_number = 0
self.__chip_pass_num = 0
self.__chip_fail_num = 0
self.__chip_yield_num = 0
self.__stop = True
self.__widgets = collections.OrderedDict((
('COT', 'Continuity Test'), ('CHE', 'Chip Erase'),
('ERT', 'Erase Test'), ('WRT', 'Write Test'),
('WIRT', 'Wire Reading Test'), ('WIT', 'Wire Reading Test'),
('WRAT', 'Write All Test'), ('DO', 'Done')))
for row, (key, value) in enumerate(self.__widgets.items()):
label = tkinter.Label(mf, text=value+':')
label.grid(row=row, column=0, sticky=tkinter.E)
canvas = tkinter.Canvas(mf, bg='yellow', width=10, height=10)
canvas.grid(row=row, column=1)
self.__widgets[key] = label, canvas
self.__cn = tkinter.Label(mf, text='Chip Number:')
self.__cn.grid(row=8, column=0, sticky=tkinter.E)
self.__display = tkinter.Label(mf)
self.__display.grid(row=8, column=1, sticky=tkinter.E)
self.__button = tkinter.Button(bf, text='START',
command=self.__start_pre)
self.__button.grid(sticky=tkinter.E)
def __start_pre(self):
self.__button['state'] = tkinter.DISABLED
self.__start_button(0)
def __start_button(self, count):
if count < 100:
self.__chip_number += 1
self.__display['text'] = str(self.__chip_number)
self.__widgets['DO'][1]['bg'] = 'yellow'
start_time = timeit.default_timer()
print('Still Working:', self.__chip_number)
self.after(500, self.__end_button, count)
else:
self.__button['state'] = tkinter.NORMAL
def __end_button(self, count):
self.__widgets['DO'][1]['bg'] = 'green'
self.after(500, self.__start_button, count + 1)
if __name__ == '__main__':
main()