For example I want to do something like this:
textSprite=new object(text("Hello"))
turtle.display(textSprite)
-Then do
time.sleep(10)
textSprite.hide()
-Or
while i<100:
time.sleep(10)
textsprite.size=i
-Or
textSprite.x=10
textSprite.y=100
time.sleep(100)
textSprite.x=20
textSprite.y=200
I tried searching google but I got no answers.
update: some question changes have been made
Since Python is generally object-oriented, and turtle specifically is, you can create your own TextSprite class using turtle as underpinning:
from turtle import Turtle
class TextSprite(Turtle):
DEFUALT_FONT = ('Arial', 18, 'normal')
def __init__(self, text):
super().__init__(visible=False)
self.text = text
self.font = self.DEFUALT_FONT
self.penup()
def setfont(self, font):
self.font = font
def showtext(self):
self.write(self.text, align='center', font=self.font)
def hidetext(self):
self.clear()
if __name__ == "__main__":
from turtle import Screen
screen = Screen()
sprite = TextSprite("Hello")
sprite.goto(100, 100)
sprite.color('blue')
sprite.setfont(('Times Roman', 24, 'bold'))
sprite.showtext()
screen.ontimer(sprite.hidetext, 10_000) # ten seconds
screen.exitonclick()
Related
I am trying to use the code at Python Tkinter rotate image animation with the following change:
Instead of rotating the canvas endlessly, I want a rotation of "turn" degrees which is randomly decided using randint() function. However, after turning by this angle, the tkinter window disappears and an error is raised. How can I make the following code work.
From my intermediate level knowledge of Python, I can see that the "yield" statement is putting a generator to work.
You can use any image in place of "0.png" in my code. I am using Python 3.9.6. Thanks in advance. The following is the code I am trying to get to work.
from tkinter import *
from PIL import ImageTk, Image
from time import sleep
from random import randint
class SimpleApp(object):
def __init__(self, master, filename):
self.master = master
self.filename = filename
self.canvas = Canvas(master, bg="black", width=500, height=500)
self.canvas.pack()
self.update = self.draw().__next__
master.after(100, self.update)
def draw(self):
image = Image.open(self.filename)
angle = 0
turn = randint(30, 390)
for i in range(turn):
tkimage = ImageTk.PhotoImage(image.rotate(angle))
canvas_obj = self.canvas.create_image(250, 250, image=tkimage)
self.master.after_idle(self.update)
yield
self.canvas.delete(canvas_obj)
angle = (angle - 1) % 360
sleep(.01)
win = Tk()
app = SimpleApp(win, '0.png')
win.mainloop()
After last yield it exits function draw in normal way and then __next__() can't run it again and it raises StopIteration and this makes problem. Normally when it is used in for-loop then it catchs StopIteration. Or if you run it with next() then you can also catch StopIteration but in this example it is problem.
I would do it without yield. I would split it in two functions: draw() to set default values at start, and rotate() to update image.
import tkinter as tk
from PIL import ImageTk, Image
from time import sleep
from random import randint
class SimpleApp(object):
def __init__(self, master, filename):
self.master = master
self.filename = filename
self.canvas = tk.Canvas(master, bg="black", width=500, height=500)
self.canvas.pack()
self.draw()
def draw(self):
self.image = Image.open(self.filename)
self.angle = 0
self.turn = randint(30, 360)
self.canvas_obj = None
self.master.after(100, self.rotate)
def rotate(self):
# it will remove image after last move
#if self.canvas_obj:
# self.canvas.delete(self.canvas_obj)
if self.turn > 0:
# it will NOT remove image after last move
if self.canvas_obj:
self.canvas.delete(self.canvas_obj)
self.tkimage = ImageTk.PhotoImage(self.image.rotate(self.angle))
self.canvas_obj = self.canvas.create_image(250, 250, image=self.tkimage)
self.angle = (self.angle - 1) % 360
self.turn -= 1
self.master.after_idle(self.rotate)
win = tk.Tk()
app = SimpleApp(win, 'lenna.png')
win.mainloop()
lenna.png - (Wikipedia Lenna)
I'm making a chess game.
I've created a main file with the Tkinter code in a class "Window". In this class, I created a canvas.
Then I've created a second file with the name "pieces", where I put the behaviour of the different pieces. In this one, I have a superclass "Pieces", and a subclass "Bishop" (because I haven't created the classes for the other pieces yet)
What I tried first to do, is to create a bishop's icon in the constructor of the class "Bishop".
My class "Bishop" has the argument "color", so that, when we create an object "Bishop", we can choose if he's black or white.
So I wrote :
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
main.Window.canvas.create_image(x_position, y_position, self.icon)
The problem is, when I create a new object of the bishop, it makes a loop.
I don't really know how to fix this, a solution would be to put the whole code in the same file, but I don't like it because it's not clean.
If you need the whole code, I can give it to you.
that's the complete error:
Traceback (most recent call last):
File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 2, in <module>
import pieces
File "C:\Users\CSP\PycharmProjects\chess_game\pieces.py", line 3, in <module>
import main
File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 28, in <module>
fen = Window()
File "C:\Users\CSP\PycharmProjects\chess_game\main.py", line 20, in __init__
pieces.Bishop("black", 5, 5)
AttributeError: partially initialized module 'pieces' has no attribute 'Bishop' (most likely due to a circular import)
I've written import main in the file pieces, because I want to create the image in the constructor of the class Bishop, I thought it was cleaner than do it in the class Windows, because I would have to do I 32 times (one per piece) and it would be very heavy.
And to create the image in the class bishop, I need to import the module main (because I use canvas.create_image(), and the canvas is in the class Windows)
But if I write import main it makes a loop, so do you have an idea to fix this?
Here's the code, very simplified
main.py
from tkinter import *
import pieces
#GUI class
class Window:
def __init__(self):
self.window = Tk()
self.window.title("My chess game")
self.frame = Frame(self.window, bg='#41B77F')
self.canvas = Canvas(self.frame, width=500, height=500, bg="skyblue", bd=0, highlightthickness=0)
self.canvas.pack()
bishop = Bishop("black",5 , 5)
self.frame.pack(expand=YES)
win = Window()
win.window.mainloop()
pieces.py
from tkinter import PhotoImage
import main
#superclass
class Pieces:
def __init__(self, color, x_position, y_position):
self.color = color
self.x_position = x_position
self.y_position = y_position
#subclass
class Bishop(Pieces):
def __init__(self, color, x_position, y_position):
super().__init__(color, x_position, y_position)
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
main.Window.canvas.create_image(x_position, y_position, image=self.icon)
If you are familiar with Model/View approach of writing code, it will help you find your way around tkinter applications. In such a case you would place all the code relating to views in one class and all the data is managed in the Model class(es).
In your case, you could start with the structure illustrated below and grow from it:
# model.py
from tkinter import *
class GameModel():
# maintains game state/data
# may include positions of pieces on board
# number of moves made by each player
pass
#superclass, inherits from Tk().Canvas
class Pieces(Canvas):
def __init__(self, color, x_position, y_position):
self.color = color
self.x_position = x_position
self.y_position = y_position
#subclass
class Bishop(Pieces):
def __init__(self, color, x_position, y_position):
super().__init__(color, x_position, y_position)
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
# main.Fenetre.canvas.create_image(x_position, y_position, image=self.icon)
self.create_image(x_position, y_position, image=self.icon)
# because bishop inherits from pieces which inherits from Canvas, its possible
# to call .create_image() from within Bishop class using self.create_image()
# gameinterface.py
from tkinter import *
#GUI/View class
class GameInterface():
def __init__(self, window: Tk()):
self.window = window
self.frame = Frame(self.window, bg='#41B77F')
self.canvas = Canvas(self.frame, width=500, height=500, bg="skyblue", bd=0, highlightthickness=0)
self.canvas.pack()
self.frame.pack(expand=YES)
def play(self, game: GameModel):
# the game objects give access to state of game and all data
self.window.mainloop()
# main.py
from tkinter import *
def main() -> None:
"""Entry point to gameplay."""
window = Tk()
# window = Tk()
#set title and position at center of screen
window.title("My chess game")
# game object gives you access to all relevant data
game = GameModel()
# app object gives you access to view
app = GameInterface(window)
# app.play combines the model and the view
app.play(game)
if __name__ == '__main__':
main()
If you need a reference to the window in Bishop, you have to pass it as an argument:
from tkinter import PhotoImage
class Pieces:
def __init__(self, color, x_position, y_position):
self.color = color
self.x_position = x_position
self.y_position = y_position
#subclass
class Bishop(Pieces):
def __init__(self, win, color, x_position, y_position):
super().__init__(color, x_position, y_position)
if self.color == "black":
icon_path = 'black_bishop.png'
elif self.color == "white":
icon_path = 'white_bishop.png'
self.icon = PhotoImage(file=icon_path)
win.canvas.create_image(x_position, y_position, image=self.icon)
I'm looking for a way to change the rendered text while the game is running.
I found someone who says that simply change the text variable would work.
So I try this:
import arcade
WINDOW = {"width":800, "height": 600, "title": ""}
class MyGame(arcade.Window):
"""
Main application class.
"""
def __init__(self):
# Call the parent class and set up the window
super().__init__(WINDOW['width'], WINDOW['height'], WINDOW['title'])
arcade.set_background_color(arcade.csscolor.CORNFLOWER_BLUE)
def setup(self):
""" Set up the game here. Call this function to restart the game. """
pass
def on_draw(self):
""" Render the screen. """
arcade.start_render()
# Code to draw the screen goes here
self.text = "Hello world!"
arcade.draw_text(self.text, WINDOW['width'] / 3 + (WINDOW['width'] / 3 / 3) - 20, WINDOW['height'] / 2, arcade.csscolor.WHITE, 18)
def on_mouse_release(self, x, y, button, key_modifiers):
print("Clicked!")
self.text = "Clicked!"
def main():
""" Main method """
window = MyGame()
window.setup()
arcade.run()
if __name__ == "__main__":
main()
But still, The text didn't change but It can detect clicks.
arcade.run() runs loop which executes on_draw() in this loop. If code runs with speed 25 FPS (Frames Per Second) then it executes on_draw() 25 times in every second. So when you click mouse then on_mouse_release() changes text to "Clicked!" and later on_draw() changes it back to "Hello world!" and finally it displays "Hello world!"
You should use self.text = "Hello world!" in __init__() or (better) in setup() to set it only once.
import arcade
WINDOW = {"width":800, "height": 600, "title": ""}
class MyGame(arcade.Window):
"""
Main application class.
"""
def __init__(self):
# Call the parent class and set up the window
super().__init__(WINDOW['width'], WINDOW['height'], WINDOW['title'])
arcade.set_background_color(arcade.csscolor.CORNFLOWER_BLUE)
def setup(self):
""" Set up the game here. Call this function to restart the game. """
self.text = "Hello world!"
def on_draw(self):
""" Render the screen. """
arcade.start_render()
# Code to draw the screen goes here
arcade.draw_text(self.text, WINDOW['width'] / 3 + (WINDOW['width'] / 3 / 3) - 20, WINDOW['height'] / 2, arcade.csscolor.WHITE, 18)
def on_mouse_release(self, x, y, button, key_modifiers):
print("Clicked!")
self.text = "Clicked!"
def main():
""" Main method """
window = MyGame()
window.setup()
arcade.run()
if __name__ == "__main__":
main()
Here is the short example of changing text on mouse click:
import arcade
class Game(arcade.Window):
def __init__(self):
super().__init__(400, 300)
self.text = 'Waiting for click!'
def on_draw(self):
arcade.start_render()
arcade.draw_text(self.text, 200, 150, arcade.color.RED, 18, anchor_x='center')
def on_mouse_release(self, x, y, button, key_modifiers):
self.text = 'Clicked!'
Game()
arcade.run()
Output:
I'm making a tkinter gui and I want it to work like this:
I have a frame, inside the frame there's a canvas and inside the canvas there are multiple rectangles
I want to make it that once I hover over a rectangle it's color will change from white, to green
simple, right?
so help me figure out what's wrong
Here's the class:
class guiSong:
def __init__(self, master: tkinter.Canvas, songobject: SongFile, x, y, rect=None):
self.master = master
self.songobject = songobject
self.x = x
self.y = y
self.rect = rect
def on_enter(self, event):
self.master.itemconfig(self.rect, fill='green')
print("Should change to green rect ", str(self.rect))
def on_leave(self, enter):
self.master.itemconfig(self.rect, fill='white')
def display(self):
self.rect = self.master.create_rectangle(self.x, self.y, self.x + 1150, self.y + 150, fill='white', tags = ['playbutton',self.songobject])
print("Self Rect is "+str(self.rect)+"!!!!!!!!!!!!!!!!!!!!!!!")
self.master.tag_bind('playbutton',"<Enter>", self.on_enter)
self.master.tag_bind('playbutton',"<Leave>", self.on_leave)
self.albumimg = Image.open(BytesIO(self.songobject.albumimage))
self.albumimg = ImageOps.expand(self.albumimg,border=5)
self.albumimg = self.albumimg.resize((120, 120), Image.ANTIALIAS)
self.img = ImageTk.PhotoImage(self.albumimg)
make_image(self.img, self.x + 25, self.y + 15, self.master)
print(f"Creating image {str(self.img)} at x",self.x+25, " y ",self.y+15 )
return self.img
#self.master.create_image(self.x + 25, self.y + 15, anchor = tkinter.W,image=img)
Don't bother the whole songobject stuff that's unrelated
I made a list of those objects and displayed them all inside a canvas one after another
The expected output is that once I hover over a rectangle it'll turn green
what happens in reality is that only the last rectangle created is colored once hovering over any rectangle.
Maybe this can help you, it's a rectangle that becomes green when you hover it with mouse, red when you leave it, and blue if you click on it.
Note : for blue color, I made an example with an argument in the callback function.
import tkinter as tk
class GUI(tk.Tk):
def __init__(self):
super().__init__()
self.can = tk.Canvas(self, width=200, height=200)
self.can.pack()
self.rect = self.can.create_rectangle(50, 50, 100, 100, fill="gray")
self.can.tag_bind(self.rect, '<Enter>', self.green_rect)
self.can.tag_bind(self.rect, '<Leave>', self.red_rect)
self.can.tag_bind(self.rect, '<Button-1>', lambda x:self.color_rect("blue"))
def color_rect(self, color):
self.can.itemconfigure(self.rect, fill=color)
def green_rect(self, event=None):
self.can.itemconfigure(self.rect, fill="green")
def red_rect(self, event=None):
self.can.itemconfigure(self.rect, fill="red")
gui = GUI()
gui.mainloop()
Since each rectangle is an instance of guiSong, you can directly bind to the canvas item rather than to a tag.
Here's a simplified version of your class:
class guiSong:
def __init__(self, master, songobject, x, y):
self.master = master
tags = ("playbutton", songobject)
self.rect = master.create_rectangle(x,y,x+100, y+100, tags=tags, fill="white")
self.master.tag_bind(self.rect, "<Enter>", self.on_enter)
self.master.tag_bind(self.rect, "<Leave>", self.on_leave)
def on_enter(self, event):
self.master.itemconfigure(self.rect, fill="red")
def on_leave(self, event):
self.master.itemconfigure(self.rect, fill="white")
If you wish to bind to the tag, you can use the tag "current" to refer to the object that received the event.
class guiSong:
...
def on_enter(self, event):
self.master.itemconfigure("current", fill="red")
def on_leave(self, event):
self.master.itemconfigure("current", fill="white")
The objective of my exercise is to create a game in which a falling ball needs to be caught by a bar at the bottom of the screen. Below code does not make the ball to fall automatically. I referred to below posts, but could not find a solution:
Tkinter bind to arc, Automatically Moving Shape? Python 3.5 Tkinter
import tkinter as tk
class Game(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.can = tk.Canvas(self, width=400, height=400)
self.can.pack(fill="both", expand=True)
self.ball = self.can.create_oval(40, 40, 60, 60, fill="red", tag="ball")
self.player = self.can.create_rectangle(300,345,350,360, fill="red")
self.bind("<Key>", self.move_player)
self.can.tag_bind("ball",self.move_b)
self.mainloop()
def move_b(self,event=None):
self.can.move(self.ball, 1, 0)
print(self.ball)
# move again after 25ms (0.025s)
self.can.after(25, self.move_b)
def move_player(self, event):
key = event.keysym
if key == "Left":
self.can.move(self.player, -20, 0)
elif key == "Right":
self.can.move(self.player, 20, 0)
if __name__ == '__main__':
Game()
2nd positional argument to tag_bind is an event, whereas in your code it's passed as the actual callback, self.move_b. First, add an event:
self.can.tag_bind("ball", "<ButtonRelease-1>", self.move_b)
If you don't want it to have event, simply pass None:
self.can.tag_bind("ball", None, self.move_b)
or don't use tag_bind at all, and simply call:
self.move_b()
Whenever you want the animation to start.