Tkinter draws in two canvases - python

I'm writing 3 functions in Tkinter. Each function is in the form ObjectName(c,x,y) where c is the name of the canvas. I want each function to draw shape in any given canvas.
Example:
from Tkinter import *
root = Tk()
def line(c,x,y):
root = Tk()
c = Canvas(root, width=600, height=800)
c.pack()
c.create_line(x-160,y,x+300,y)
drawLine(c,200,300)
root.mainloop()
Problem:
when I call the same function to draw two shapes on the same canvas it draws on two different canvases :(

Your code looks to be creating a new canvas object each time you call line (or drawLine, as your function name and usage appears to be inconsistent.) You shouldn't create a new root object and Canvas object in your function.
Try something like this:
from Tkinter import *
def drawLine(c, x, y):
c.create_line(x - 160, y, x + 300, y)
root = Tk()
c = Canvas(root, width=600, height=800)
c.pack()
drawLine(c, 200, 300)
drawLine(c, 300, 400)
drawLine(c, 350, 450)
root.mainloop()

Related

Canvas.Tag_bind not working with OOP | Python 3 [duplicate]

In my simple code, a red ball is falling down in a straight line (that's working). When I push the right arrow key, I want the ball to also move in right direction. This is not working, however. What am I doing wrong?
from tkinter import *
root = Tk()
canvas = Canvas(root, height=400, width=500, background='black')
canvas.pack()
class Bird:
def __init__(self, canvas, coords):
self.canvas = canvas
self.coords = coords
self.bird = canvas.create_rectangle(coords, fill='red')
def gravity(self):
self.canvas.move(self.bird, 0, 10)
self.canvas.after(200, self.gravity)
def moveRight(self, event):
self.canvas.move(self.bird, 10, 0)
self.canvas.after(200, self.moveRight)
bird = Bird(canvas, (100, 100, 110, 110))
bird.gravity()
canvas.bind('<Right>', bird.moveRight)
root.mainloop()
I have another additional question:
Is it possible to call this "after"-function or a similar function for the whole canvas instead of the two methods separately?
If you see any other flaws with my code plz let me know!
Thanks!
You must bind the right key to the canvas inside the class, and set the focus on the canvas:
from tkinter import *
root = Tk()
canvas = Canvas(root, height=400, width=500, background='black')
canvas.pack()
class Bird:
def __init__(self, canvas, coords):
self.canvas = canvas
self.coords = coords
self.bird = canvas.create_rectangle(coords, fill='red')
self.canvas.bind('<Right>', self.moveRight)
self.canvas.focus_set()
def gravity(self):
self.canvas.move(self.bird, 0, 10)
self.canvas.after(200, self.gravity)
def moveRight(self, event=None):
self.canvas.move(self.bird, 10, 0)
self.canvas.after(200, self.moveRight)
bird = Bird(canvas, (100, 100, 110, 110))
bird.gravity()
root.mainloop()
The problem you are facing is that you are binding keyboard events, but the events can only work if the widget with the bindings has the keyboard focus. You can give the canvas the keyboard focus with focus_set():
canvas = Canvas(root, height=400, width=500, background='black')
canvas.focus_set()
Is it possible to call this "after"-function or a similar function for the whole canvas instead of the two methods separately?
Yes. Your binding can call any function you want. If you expect to have more than one object and you want them all to move at the same time, you can move them all from a function.
First, remove the call to after from moveRight. Next, define a global function that calls moveRight for every object. For example:
def move_them_all():
bird1.moveRight()
bird2.moveRight()
something_else.moveRight()
self.canvas.after(1000, move_them_all)
...
canvas = Canvas(root, height=400, width=500, background='black')
...
canvas.bind('<right>', move_them_all)

How can I set the size of a button in pixels - python [duplicate]

This question already has answers here:
How do I resize buttons in pixels? (Tkinter)
(5 answers)
Closed 4 years ago.
I'm using Python 3, and I want to set the size of a button in pixels.
I wanted to make it width = 100 pixels, height = 30 pixels, but it didn't work.
It was much bigger than I expected.
Here's My code:
from tkinter import *
def background():
root = Tk()
root.geometry('1160x640')
btn_easy = Button(root, text = 'Easy', width = 100, height = 50)
btn_easy.place(x = 100, y = 450)
root.mainloop()
background()
How can I make it?
http://effbot.org/tkinterbook/button.htm
You can also use the height and width options to explicitly set the
size. If you display text in the button, these options define the size
of the button in text units. If you display bitmaps or images instead,
they define the size in pixels (or other screen units). You can
specify the size in pixels even for text buttons, but that requires
some magic. Here’s one way to do it (there are others):
f = Frame(master, height=32, width=32)
f.pack_propagate(0) # don't shrink
f.pack()
b = Button(f, text="Sure!")
b.pack(fill=BOTH, expand=1)
from tkinter import *
def background():
root = Tk()
root.geometry('1160x640')
f = Frame(root, height=50, width=50)
f.pack_propagate(0) # don't shrink
f.place(x = 100, y = 450)
btn_easy = Button(f, text = 'Easy')
btn_easy.pack(fill=BOTH, expand=1)
root.mainloop()
background()
Bonus: many buttons (just to get the idea)
from tkinter import *
def sizedButton(root, x,y):
f = Frame(root, height=50, width=50)
f.pack_propagate(0) # don't shrink
f.place(x = x, y = y)
btn_easy = Button(f, text = 'Easy')
btn_easy.pack(fill=BOTH, expand=1)
def background():
root = Tk()
root.geometry('1160x640')
for x in range(50,350,100):
for y in range(50,350,100):
sizedButton(root, x,y)
root.mainloop()
background()

tkinter canvas not opening when while loop is used

I was experimenting with tkinter and was using the following code as a tester:
import keyboard
from time import sleep
from tkinter import *
tk = Tk()
canvas = Canvas(tk, width=400, height=400)
canvas.pack()
a = 0
if keyboard.is_pressed('q'):
a = 1
if a == 1:
canvas.create_line(0,0,400,400)
tk.mainloop()
No canvas appears when I run this code. I have tried using a debug message instead of creating a line, as well as shifting "tk.mainloop" but the canvas does not show up. However, when I do not use a while loop but a for loop the canvas appears. The program that I plan on making needs an infinite loop. Is there a way to do this in a way that works with tkinter?
Thanks in advance and my apologies for a question that is probably down to some simple error of mine.
Tkinter has bind() to assign function to key/mouse/event so you don't need keyboard module and while loop.
import tkinter as tk
def myfunction(event):
canvas.create_line(0, 0, 400, 400)
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=400)
canvas.pack()
root.bind('q', myfunction)
root.mainloop()
EDIT: something more funny - every q create random line
import tkinter as tk
import random
def myfunction(event):
x = random.randint(0, 400)
y = random.randint(0, 400)
canvas.create_line(x, y, 400, 400)
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=400)
canvas.pack()
root.bind('q', myfunction)
root.mainloop()
Or like Picasso
import tkinter as tk
import random
def myfunction(event):
x1 = random.randint(0, 400)
y1 = random.randint(0, 400)
x2 = random.randint(0, 400)
y2 = random.randint(0, 400)
canvas.create_line(x1, y1, x2, y2)
root = tk.Tk()
root.title('Picasso')
canvas = tk.Canvas(root, width=400, height=400)
canvas.pack()
root.bind('q', myfunction)
root.mainloop()

Python 3.5: Print Canvas Text

Could anyone share with me how to print the text of the text widget added to a Canvas object? In the code below, I want the system return the value of "hello" when mouse on the text, however, it turns out giving me "1". Don't know why. Could anyone help me?
Many many thanks!!!
import tkinter
from tkinter import *
def show_text(event):
print (canvas.text)
master = tkinter.Tk()
canvas = tkinter.Canvas(master, width = 200, height = 100)
canvas.pack()
canvas.bind('<Enter>',show_text)
canvas.text = canvas.create_text(20, 30, text="hello")
mainloop()
According to the canvas docs:
You can display one or more lines of text on a canvas C by creating a
text object:
id = C.create_text(x, y, option, ...)
This returns the object ID of the text object on canvas C.
Now, you gotta modify the code something like this:
import tkinter
from tkinter import *
def show_text(event):
print (canvas.itemcget(obj_id, 'text'))
master = tkinter.Tk()
canvas = tkinter.Canvas(master, width = 200, height = 100)
canvas.pack()
canvas.bind('<Enter>',show_text)
obj_id = canvas.create_text(20, 30, text="hello")
mainloop()
Follow up (see the documentation for Label.config:
import tkinter
from tkinter import *
from tkinter import ttk
def show_text(event):
print (canvas.itemcget(canvas.text, 'text'))
#The command of writing text 'hello' in sch_Label to replace the text 'the info shows here'
sch_Label.config(text = 'hello!')
master = tkinter.Tk()
canvas = tkinter.Canvas(master, width = 200, height = 100)
canvas.pack()
canvas.bind('<Enter>',show_text)
canvas.text = canvas.create_text(20, 30, text="hello")
pad1 = ttk.Notebook(master)
pad1.pack(side=RIGHT, expand=1, fill="both")
tab1 = Frame(pad1)
pad1.add(tab1, text = "Schedule")
pad1.pack(side=RIGHT)
sch_Label = ttk.Label(tab1, text='The info shows here')
sch_Label.pack(side="top", anchor="w")
mainloop()

Tkinter GUI canvas

I am working on a Lego Mindstorms project where we create a GUI that can be used to control the robot. The thing I need to do is to create something that shows the robots position after every move. I am using a canvas where I draw a rectangle and then a dot that shows the current position of the robot. I have a whole bunch of code but I am just showing you a small piece of it relevant to my problem.
from Tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame, text="Move", command=lambda: do_move())
self.button.pack(side=TOP)
self.canvas = Canvas(master, width=300, height=450)
self.canvas.place(x=250, y=550)
self.canvas.create_rectangle(0, 0, 300, 450, fill="white")
self.canvas.create_oval(150, 300, 160, 310, fill="blue", tags="Position")
x, y = self.canvas.coords("Position")
x = int(x)
y = int(y)
x2 = self.canvas.canvasx(self.x)
y2 = self.canvas.canvasy(self.y)
x2 = int(x2)
y2 = int(y2)
def move_forward():
self.canvas.move(Position, x2, y2)
def move_backwards():
self.canvas.move(Position, , )
root = Tk()
app = App(root)
root.title("Mindstorms GUI")
root.geometry("800x1200")
root.mainloop()
root.destroy()
For the move function that I have a button for, I choose a value and that value will move the robot forward/backward. When the robot has moved, I want to also move my blue circle on my canvas. X and Y are the coordinates for the circles current position, and the rest about X2 and Y2 are taken from another site. I am not really sure why you have to write x=int(x) and I dont really understand the parts for X2 and Y2. Any explanations and suggestions about how I can write the rest of my code?
The first new function that I define at the end will be used with my move button so that I have two commands for the button. When I click the button, the Position-circle will also be moved to the new coordinates. I will also need to write somewhere that a specific value of the unit I use for my move function equals for example a move of 5 coordinates in my canvas. Any tips on how to do that?
I hope you understand the task and my formulations. Any help is appreciated!
You have a couple issues with your sample code, hopefully this minimal example will help you get on track:
from Tkinter import *
import random
class App(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.move_button = Button(self, text="Move", command=self.do_move)
self.move_button.pack()
self.random_button = Button(
self, text="Random!",
command=self.random_move)
self.random_button.pack()
self.canvas = Canvas(self, width=300, height=450)
self.canvas.config(
highlightbackground="grey",
borderwidth=2,
relief="flat")
self.canvas.pack()
self.canvas.create_oval(
150, 300, 160, 310, fill="blue", tag="Oval")
self.pack()
def do_move(self):
self.canvas.move("Oval", 10, 10)
def random_move(self):
x = random.randint(1, 290)
y = random.randint(1, 440)
self.canvas.coords("Oval", x, y, x+10, y+10)
root = Tk()
root.title("Mindstorms GUI")
root.geometry("400x600")
app = App(root)
root.mainloop()
Notice that the Canvas move method takes an offset. Alternatively, you could use the coords method with coordinates as arguments if you know the new location of the oval, however note that the coordinates should be a list of coordinate pairs. I've added a random button to show how to use coords.
Sounds like a cool project, have fun!

Categories