How stop sketch in tkinter canvas - python-3.x

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()

Related

How do I capture keystrokes directly on the tkinter canvas?

I'm having trouble capturing keystrokes from the tkinter canvas. The keystroke is captured properly if I bind it to a label widget but not to the canvas. My code is here:
from tkinter import *
root = Tk()
class keyPresser():
# For test purposes, move the rectangle and print a message.
# Nothing is happening
def handle_down_key(self,event):
self.canvas.focus_set()
self.canvas.move(self.rectangle,10,10)
print('Down arrow key pressed')
def __init__(self):
self.canvas = Canvas(root, width=400, height=400)
self.rectangle = self.canvas.create_rectangle(
0, 0, 20, 20, fill = "black")
self.canvas.bind("<Down>", self.handle_down_key)
self.canvas.focus()
self.canvas.pack()
mm = keyPresser()
root.mainloop()
Thanks for any suggestions.
Here you go !
from tkinter import *
root = Tk()
class keyPresser():
# For test purposes, move the rectangle and print a message.
# Nothing is happening
def handle_down_key(self, event):
self.canvas.focus_set()
self.canvas.move(self.rectangle, 10, 10)
print('Down arrow key pressed')
def __init__(self):
self.canvas = Canvas(root, width=400, height=400)
self.rectangle = self.canvas.create_rectangle(
0, 0, 20, 20, fill="black")
root.bind("<Down>", self.handle_down_key)
self.canvas.focus()
self.canvas.pack()
mm = keyPresser()
root.mainloop()

How do I overlay these buttons on top of an image for a background in Python Tkinter

Currently, I'm attempting to create a simple main menu for a game using Tkinter as a simple GUI since it's a simple RPG game and Python however the image overlays the buttons in all circumstances.
I've tried using some other solutions like placing them or creating a window but I can't find a straight answer of how to make that either.
import tkinter
from tkinter import *
from PIL import ImageTk, Image
(PIL is from when I was using a JPG before.)
root = Tk()
content = ttk.Frame(root)
root.geometry("600x600")
background = ImageTk.PhotoImage(Image.open("bred.png"))
canvas = tkinter.Canvas(root, width=580, height=600)
content.grid(column=0, row=0)
Btn1 = Button(content, text="Play", width=5, height=1)
Btn2 = Button(content, text="Kill me", width=7, height=1, command =
root.quit)
backgroundlabel = tkinter.Label(root, image=background)
backgroundlabel.image = background
backgroundlabel.place(x=0, y=0, relwidth=1, relheight=1)
Btn1.grid(row=1, column=2, padx=(130))
Btn1.columnconfigure(1, weight=1)
Btn1.rowconfigure(1, weight=1)
Btn2.grid(row=1, column=3, pady=(130))
Btn2.columnconfigure(3, weight=1)
Btn2.rowconfigure(1, weight=1)
root.mainloop()
Currently your background's master is set to root while your buttons are set to a frame. The first thing you need to do is set both to the same master, i.e. changing background master to content:
backgroundlabel = tk.Label(content, image=background)
Next you need to deal with the stacking order. You can call widget.lift() to raise the buttons to top:
Btn1.grid(row=1, column=2, padx=(130))
...
Btn1.lift()
Btn2.grid(row=1, column=3, pady=(130))
...
Btn2.lift()

Can I stack a Frame on top of a canvas? (Tkinter)

I'm new to coding and am attempting to make some sort of EPOS type system just as a project for the shop I work in. I want to stack a frame to have a keypad for a log in code on top of a background image, just for the start of the program. Essentially no matter how I try to stack the different Tkinter widgets, it never seems to work.
I've tried placing the canvas which holds the image in the main Tk() and then place the frame on top of that, to then use a grid structure to build the keypad put that didn't work.
I tried different combinations of which widget parents which other widget etc, and couldn't get anything to work. It usually ended up with no frame visible, and the 1920x1080 image being pushed to the bottom right of the screen.
screen_width = 1920
screen_height = 1080
screen_geometry = '{}x{}'.format(screen_width, screen_height)
main_window = Tk()
main_window.title('Shop')
main_window.resizable(0,0)
main_window.geometry(screen_geometry)
background_image = PhotoImage(master=C, file='logo.png')
C = Canvas(main_window, bg="blue", height=screen_height, width=screen_width)
background_label = Label(C, image=background_image)
background_label.place(x=0, y=0, relwidth=1, relheight=1)
C.place(x=0, y=0, relwidth=1, relheight=1)
login_window = Frame(main_window, borderwidth=5, relief=GROOVE)
login_window.config(width=10, height=10)
login_window.place(x=0, y=0, relwidth=1, relheight=1)
test_button = Button(login_window, text="test")
test_button.grid(column=0, row=0)
main_window.mainloop()
I expected the logo to be placed underneath the frame, and then I'd be able to use the frame with a normal grid structure, but it didn't seem to work at all.
This code is messy and poor, so some constructive criticism and help overall would be lovely.
Thank you.
From my understanding, you want to have a login window with a background image and a keypad at the center of the window. Below is a sample code:
from tkinter import *
screen_width = 1920 // 2
screen_height = 1080 // 2
screen_geometry = '{}x{}'.format(screen_width, screen_height)
main_window = Tk()
main_window.title('Shop')
main_window.resizable(0, 0)
main_window.geometry(screen_geometry)
# background image
background_image = PhotoImage(file='logo.png')
background_label = Label(main_window, image=background_image)
background_label.place(x=0, y=0, relwidth=1, relheight=1)
# keypad at the center of window
login_frame = Frame(main_window)
login_frame.place(relx=0.5, rely=0.5, anchor=CENTER)
display = Label(login_frame, bg='black', font=('', 20))
display.grid(row=0, column=0, columnspan=3, sticky='ew')
def input_digit(n):
print(n)
font = ('', 16, 'bold')
numpad = []
for number in range(9):
row = number // 3
col = number % 3
btn = Button(login_frame, text=number+1, font=font, width=5, height=2)
btn.grid(row=row+1, column=col)
btn.config(command=lambda n=number+1:input_digit(n))
numpad.append(btn)
main_window.mainloop()

Adding padding query insert to Listbox

I have read a number of threads and other resources to try to find the correct way to handle this but I have not found anything that works with my application.
Here is what I am trying to accomplish.
When a query is completed and the insert of the data to a Listbox is done I cannot seem to get it to margin the data insert by 1 character space.
I am using pack() and I have read the tkinter manual for this and have tried each example available along with others found on various threads here.
The widget:
output = tkinter.Listbox(window_2, height = 20, font='Times 10',
width=42, bd=1, bg = '#FFD599', fg = '#9A0615', selectmode=SINGLE)
output.pack()
output.place(x=210, y=195)
I have tried padx and pady with pack() without success, although this works successfully with the Text widget. I have also attempted to use a few alternatives that I have found here on the site but all without success in margining the Listbox when the data is inserted.
Any advice?
pack's padx/pady and ipadx/ipady options don't affect the data that is inside the listbox. The listbox itself doesn't have any options to add an internal margin.
To get a margin around the inside of the listbox, what I normally do is give it a zero borderwidth and highlightthickness, and then place it in a frame with the same background color and let the frame be the border. You can then add any padding you want between the border and the listbox.
This is also convenient because you can put a scrollbar inside the frame, giving it the appearance that it is inside the listbox without actually being inside the listbox.
Example:
import tkinter as tk
root = tk.Tk()
root.configure(background="gray")
listbox_border = tk.Frame(root, bd=1, relief="sunken", background="white")
listbox_border.pack(padx=10, pady=10, fill=None, expand=False)
listbox = tk.Listbox(listbox_border, width=20, height=10,
borderwidth=0, highlightthickness=0,
background=listbox_border.cget("background"),
)
vsb = tk.Scrollbar(listbox_border, orient="vertical", command=listbox.yview)
listbox.configure(yscrollcommand=vsb)
vsb.pack(side="right", fill="y")
listbox.pack(padx=10, pady=10, fill="both", expand=True)
for i in range(100):
listbox.insert("end", "Item #{}".format(i))
root.mainloop()
here is a variation on the much appreciated answer by Bryan Oakley.
it uses ttk widgets instead of tk widgets
the scrollbar tracks your position in the list box when you scroll with the mouse
uses the oStyle.theme_use("clam") because it may look more modern...this can be commented out
'
import tkinter as tk
from tkinter import ttk
try: # allows the text to be more crisp on a high dpi display
from ctypes import windll
windll.shcore.SetProcessDpiAwareness(1)
except:
pass
root = tk.Tk()
oStyle = ttk.Style()
oStyle.theme_use("clam")
oStyle.configure('LB.TFrame', bd=1, relief="sunken", background="white")
listbox_border = ttk.Frame(root, style='LB.TFrame')
listbox_border.pack(padx=4, pady=4, fill=None, expand=False)
vsb = ttk.Scrollbar(listbox_border)
vsb.pack(side="right", fill="y")
listbox = tk.Listbox(listbox_border, width=20, height=10, borderwidth=0,
highlightthickness=0, selectmode=tk.SINGLE,
activestyle=tk.NONE)
listbox.pack(padx=6, pady=6, fill="y", expand=True)
listbox.config(yscrollcommand=vsb.set)
vsb.config(command=listbox.yview)
for i in range(100):
listbox.insert("end", "Item #{}".format(i))
root.mainloop()
'
first of all to format chars in a tkinter listbox you need to use a fixed font and .format python funcion....;
So you can do something this
Press Load to load data in the listbox and pay attention to this line code
s = '{0:>8}{1:5}'.format(i[0],i[1])
self.list.insert(tk.END, s)
import tkinter as tk
RS = (('Apple',10),
('Banana',20),
('Peack',8),
('Lemon',6),)
class App(tk.Frame):
def __init__(self,):
super().__init__()
self.master.title("Hello World")
self.init_ui()
def init_ui(self):
self.pack(fill=tk.BOTH, expand=1,)
f = tk.Frame()
sb = tk.Scrollbar(f,orient=tk.VERTICAL)
self.list = tk.Listbox(f,
relief=tk.GROOVE,
selectmode=tk.BROWSE,
exportselection=0,
background = 'white',
font='TkFixedFont',
yscrollcommand=sb.set,)
sb.config(command=self.list.yview)
self.list.pack(side=tk.LEFT,fill=tk.BOTH, expand =1)
sb.pack(fill=tk.Y, expand=1)
w = tk.Frame()
tk.Button(w, text="Load", command=self.on_callback).pack()
tk.Button(w, text="Close", command=self.on_close).pack()
f.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=0)
def on_callback(self,):
for i in RS:
s = '{0:>8}{1:5}'.format(i[0],i[1])
self.list.insert(tk.END, s)
def on_close(self):
self.master.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()

Getting a pop up canvas to take focus

How do I transfer keyboard control from one canvas to another
self.canvas.bind('<FocusOut>')
self.canvas2.bind('<FocusIn>')
doesn't work, the focus is still on canvas one and the keyboard still acts on canvas one not canvas two.
As well
self.canvas2.focus_set()
still keeps the focus on self.canvas and not on self.canvas2.
focus_set is how you give focus to a widget.
Here's an example that shows two canvases, and sets the focus when you click one of the canvases. When you press the "s" key, a square will be drawn on whichever canvas has the focus.
import tkinter as tk
import random
def draw_square(event):
x0 = random.randint(30, 370)
y0 = random.randint(30, 170)
size = random.randint(10, 30)
event.widget.create_rectangle(x0, y0, x0+size, y0+size, fill="red")
def give_focus(event):
event.widget.focus_set()
event.widget.configure(background="bisque")
def lose_focus(event):
event.widget.configure(background="white")
root = tk.Tk()
label = tk.Label(root, text="Click to focus a canvas, press 's' to draw a square")
canvas1 = tk.Canvas(root, width=400, height=200, background="white",
borderwidth=1, relief="raised")
canvas2 = tk.Canvas(root, width=400, height=200, background="white",
borderwidth=1, relief="raised")
label.pack(side="top", fill="x")
canvas1.pack(fill="both", expand=True)
canvas2.pack(fill="both", expand=True)
for canvas in (canvas1, canvas2):
canvas.bind("<FocusOut>",lose_focus)
canvas.bind("<1>", give_focus)
canvas.bind("<s>", draw_square)
root.mainloop()

Resources