This is my code to make a rocket move around in a turtle screen:
import turtle
s = turtle.Screen()
image = "rocket.gif"
s.addshape(image)
turtle.shape(image)
s.bgcolor("black")
def fd():
turtle.fd(5)
def bk():
turtle.bk(10)
def lt():
turtle.lt(5)
def rt():
turtle.rt(5)
turtle.penup()
turtle.speed(0)
turtle.home()
s.listen()
s.onkeypress(fd, "Up")
s.onkeypress(bk, "Down")
s.onkeypress(lt, "Left")
s.onkeypress(rt, "Right")
When I press the up button the rocket moves forward, and when I press the right button, the rocket image does not turn, but when it moves forward again the direction of the movement does change.
I read this at the help() section at Python3 IDLE:
Image shapes do not rotate when turning the turtle, so they do not display the heading of the turtle!
But, I do not understand is this for turtle.rt() or turtle.setheading()?
Even if it is for both, how do I rotate the image?
Note: Surprisingly it works with
trinket.io
We can make a shape appear to rotate by telling a turtle to switch between different related shapes.
use setshape (setsh) and setheading (seth).For more details Visit http://mia.openworldlearning.org/howdoi/ftn.htm
Related
I'm trying to make a program where the user can paint on the screen. So I want to make an invisible canvas window in fullscreen where only the user's pen marks on the canvas will be visible. The closest thing I found is this function: root.attributes("-transparentcolor","color code here"), which will make all the parts of the window that's in the color you give transparent. So if I give the second parameter the background color of the canvas, then only the pen strokes on the canvas will be visible. This is so close to what I want, except for one thing, the transparent areas can't detect or block mouse clicks! Any mouse clicks will just go through to whatever is behind the tkinter window. Is there a way to make it so the transparent areas will still block mouse clicks? I really need help on this!
Here is a much better way to do this using only tkinter. Explanation is in code comments. Basically uses two windows, one for "blocking" the mouse and being transparent using the "-alpha" attribute and the other window for "hosting" canvas and having one completely transparent color while keeping others opaque using "-transparentcolor" attribute. That also means that this is cross-platform solution too (except I think the -transparentcolor attribute differs a little bit on other OS like Linux where I think it is -splash or sth and maybe something different on MacOS):
from tkinter import Tk, Toplevel, Canvas
# setting the starting coordinate of the line so that
# on motion it is possible to immediately draw it
def set_first(event):
points.extend([event.x, event.y])
# on motion append new coordinates to the list and if there are
# 4 (the minimum), create a new line and save the id
# otherwise update the existing line
def append_and_draw(event):
global line
points.extend([event.x, event.y])
if len(points) == 4:
line = canvas.create_line(points, **line_options)
else:
canvas.coords(line, points)
# when released clear the list to not waste space
# and not necessarily but also set "id" to None
def clear_list(event=None):
global line
points.clear()
line = None
line = None # this is a reference to the current line (id)
points = [] # list to keep track of current line coordinates
line_options = {} # dictionary to allow easier change of line options
# just a variable to more easily store the transparent color
transparent_color = 'grey15'
# creating the root window which will help with drawing the line
# because it will "block" mouse because `-alpha` (0.01 seems to be the lowest value)
# attribute is used, however it makes everything transparent on the window
# so need another window to "host" the canvas
root = Tk()
root.attributes('-alpha', 0.01)
root.attributes('-topmost', True)
root.attributes('-fullscreen', True)
# just press Esc key to close the whole thing, otherwise
# it is only doable by pressing Alt + F4 or turning off
# the computer
root.bind('<Escape>', lambda e: root.quit())
# create the host window, because it allows to have only
# one transparent color while keeping the other opaque and
# visible
top = Toplevel(root)
top.attributes('-transparentcolor', transparent_color)
top.attributes('-topmost', True)
top.attributes('-fullscreen', True)
# set the focus to root because that is where events are bound
root.focus_set()
# create the canvas to draw on
canvas = Canvas(top, bg=transparent_color, highlightthickness=0)
canvas.pack(fill='both', expand=True)
# bind all the events to `root` which "blocks" mouse
# but is also almost (because it has a very small alpha value
# it is not entirely invisible but human eye won't notice much)
# invisible
root.bind('<Button-1>', set_first)
root.bind('<B1-Motion>', append_and_draw)
root.bind('<ButtonRelease-1>', clear_list)
root.mainloop()
Here is an improvable example (you may need to pip install pyautogui, ctypes is a built-in library), it is also Windows only as far as I know:
Note: The other answer using two windows, however, is a lot better but I will keep this too just for the information.
from tkinter import Tk, Canvas
import pyautogui as pag
import ctypes
data = {
'draw': True,
'cur_line_points': [],
'cur_line_id': None
}
# function taken mainly from here: https://stackoverflow.com/a/46596592/14531062
def is_pressed(btn: str = 'left') -> bool:
if btn == 'left':
btn = 0x01
elif btn == 'right':
btn = 0x02
else:
raise Warning("incorrect argument, should be 'left' or 'right'")
return ctypes.windll.user32.GetKeyState(btn) not in (0, 1)
def draw_line(canvas_):
if not data['draw']:
root.after(10, draw_line, canvas_)
return
pressed = is_pressed('left')
cur_line_points = data['cur_line_points']
cur_line_id = data['cur_line_id']
if not pressed:
if cur_line_id is not None:
canvas_.coords(cur_line_id, cur_line_points)
data['cur_line_id'] = None
cur_line_points.clear()
else:
mouse_x, mouse_y = pag.position()
cur_line_points.extend((mouse_x, mouse_y))
len_points = len(cur_line_points)
if len_points == 4:
data['cur_line_id'] = canvas_.create_line(cur_line_points)
elif len_points > 4:
canvas_.coords(cur_line_id, cur_line_points)
root.after(10, draw_line, canvas_)
transparent_color = 'grey15'
root = Tk()
root.config(bg=transparent_color)
root.attributes('-transparentcolor', transparent_color)
root.attributes('-topmost', True)
root.attributes('-fullscreen', True)
canvas = Canvas(root, bg=transparent_color, highlightthickness=0)
canvas.pack(fill='both', expand=True)
draw_line(canvas)
root.mainloop()
Basically detects if mouse button is pressed using the built-in library ctypes and if it is adds the current mouse coordinates (does that using pyautogui library which may need be installed) to a list and then draws a line based on that list (it also keeps the reference of the currently drawn line and simply changes its coordinates instead of drawing a new line each time it loops), the only slight issue is that while drawing the mouse is also interacting with the window below, highlighting text and stuff, couldn't really figure out how to remove that yet but at least you get to draw a line.
I am learning python, I have started on a simple pong game. I have the code to draw two paddles on both sides of the screen the paddles don't appear just a blank screen in the output. Here is the code:
import turtle
#creating a window for pong
win = turtle.Screen()
win.title("PONG")
win.bgcolor("white")
win.setup(width=800, height=600)
win.tracer(0)
#creating paddle A
paddle_a = turtle.Turtle()
paddle_a.speed(0)
paddle_a.color("black")
paddle_a.shape("square")
paddle_a.shapesize(stretch_wid=5, stretch_len=1)
paddle_a.penup()
paddle_a.goto(-350,0)
#creating paddle B
paddle_b = turtle.Turtle()
paddle_b.speed(0)
paddle_b.color("black")
paddle_b.shape("square")
paddle_b.shapesize(stretch_wid=5, stretch_len=1)
paddle_b.penup()
paddle_b.goto(350,0)
#ball
ball = turtle.Turtle()
ball.speed(0)
ball.color("black")
ball.shape("square")
ball.penup()
ball.goto(0,0)
win.exitonclick()
PLEASE TELL ME WHERE I AM WRONG!!
If you remove win.tracer(0), the drawn shapes appear. It's unintelligible what you want to achieve with tracer(0), since according to the documentation this means only each zeroth regular screen update is really performed, which is nonsense per se.
Hey I'm pretty bad at coding, but I wanted to automate a task in a game, specifically fishing. Therefore, you have to click a button, another one pops up with a white circle around it. This circle varies its size and when its the size of the button, the circle changes its color and you have to click the button to catch a fish. Therefore I searched the first button (fishing hole), and clicked on its position. Then I look at a specific pixel around the second button, that changes color from white to pale red?. If the color changes, I want to click the button. Everything works besides this last step, it moves there if I use the pyautogui.click(x,y), but it doesn't click. Any way to make it work (bear in mind I'm not an experienced programmer)?. I tried pyautogui.click(x,y), clicking several times and so on. Thank you.
import pyautogui as pa
import time
counter = 0
def main():
global counter
# Countdown to open the game
for i in reversed(range(0,5)):
print(i)
time.sleep(1)
# Finding the fishing hole using a prior screenshot
fishing_hole_location = pa.locateOnScreen("fishing_hole.png",confidence = 0.5)
# Getting the center of the hole
fishing_hole_center = pa.center(fishing_hole_location)
# Clicking the hole
pa.click(fishing_hole_center[0], fishing_hole_center[1])
time.sleep(3)
# Locating the second button (even though it appears in the middle of the screen)
fishing_button_location = pa.locateOnScreen("fishing_button.png",confidence = 0.5)
# Finding the buttons center
fishing_button_center = pa.center(fishing_button_location)
while True:
# Looking at the pixel at the edge of the button
pix = pa.pixel(int(fishing_button_center[0]+24), int(fishing_button_center[1]))
# If the pixel doesn´t have its usual colour 5 times in a row, I want to click the button
if pix != (75,99,118):
counter+= 1
if counter >= 5:
pa.moveTo(fishing_button_center[0], fishing_button_center[1])
time.sleep(2)
pa.click()
break
else:
counter = 0
time.sleep(0.1)
main()
Try making a function like this instead of using the pre-built one -
def click(button):
pyautogui.mouseDown(button=button)
pyautogui.mouseUp(button=button)
It worked for me when I had this same problem
I have an application that displays images. Since people need to read some information out of the image i implemented a zoom functionality.
I set the picture Widget to 600x600. To Preserve the aspect ratio i then scale the picture and draw it to the widget. This works really well.
For the zoom functionality the user should click anywhere on the picture and it should cut out the are 150x150 pixesl around where the cursor clicks. To be precises the click of the cursore should mark the middle of the rectangle i cut out. So if i click on x=300 y=300 the area should be x=225 y=225 width=150 height=150.
To Archive that i scale the coordinates where the user clicks back to the original image resolution, cut out the subimage and scale it back down. Cutting out the scaled image allready loaded in my programm would yield a far worse quality.
The error is simple. The area cut out is not exactly the aerea i would like to cut out. Sometimes it is to far left. Sometimes to far right. Somtes to high sometimes to low... And i fail to see where the problem lies.
I wrote a barebone prototype whith just the functionality needed. After you put in a path to a jpeg picture you should be able to run it.
# -*- coding: utf-8 -*-
"""
Created on Sun Jan 12 12:22:25 2020
#author: Paddy
"""
import wx
class ImageTest(wx.App):
def __init__(self, redirect=False, filename=None):
wx.App.__init__(self, redirect, filename)
self.frame = wx.Frame(None, title='Radsteuer Eintreiber')
self.panelleft = wx.Panel(self.frame)
self.picturepresent=False
self.index=0
self.PhotoMaxSize = 600
self.zoomed=False
#Change path here
self.imagepath='F:\Geolocation\Test\IMG_20191113_174257.jpg'
self.createUI()
self.frame.Centre()
self.frame.Show()
self.frame.Raise()
self.onView()
#Creates UI Elements on Initiation
def createUI(self):
#instructions = 'Bild'
img = wx.Image(self.PhotoMaxSize,self.PhotoMaxSize,clear=True)
self.imageCtrl = wx.StaticBitmap(self.panelleft, wx.ID_ANY,
wx.Bitmap(img),size=(self.PhotoMaxSize,self.PhotoMaxSize))
self.mainSizer = wx.BoxSizer(wx.VERTICAL)
self.imageCtrl.Bind(wx.EVT_LEFT_UP, self.onImageClick)
self.mainSizer.Add(self.imageCtrl, 0, wx.ALL|wx.ALIGN_CENTER, 5)
self.panelleft.SetSizer(self.mainSizer)
self.mainSizer.Fit(self.frame)
self.panelleft.Layout()
def onImageClick(self,event):
if self.zoomed:
self.onView()
self.zoomed=False
else :
#Determin position of mouse
ctrl_pos = event.GetPosition()
print(ctrl_pos)
picturecutof=self.PhotoMaxSize/4
if ctrl_pos[0]-((self.PhotoMaxSize-self.NewW)/2)>0:
xpos=ctrl_pos[0]-((self.PhotoMaxSize-self.NewW)/2)
else:
xpos=0
if ctrl_pos[0]+picturecutof>self.NewW:
xpos=self.NewW-picturecutof
if ctrl_pos[1]-((self.PhotoMaxSize-self.NewW)/2)>0:
ypos=ctrl_pos[1]-((self.PhotoMaxSize-self.NewW)/2)
else:
ypos=0
if ctrl_pos[1]+picturecutof>self.NewH:
ypos=self.NewH-picturecutof
xpos=xpos*self.W/self.NewW
ypos=ypos*self.H/self.NewH
picturecutofx=picturecutof*self.W/self.NewW
picturecutofy=picturecutof*self.H/self.NewH
rectangle=wx.Rect(xpos,ypos,picturecutofx,picturecutofy)
self.img = wx.Image(self.imagepath, wx.BITMAP_TYPE_ANY)
self.img=self.img.GetSubImage(rectangle)
self.img=self.img.Scale(600,600,wx.IMAGE_QUALITY_BICUBIC)
self.imageCtrl.SetBitmap(wx.Bitmap(self.img))
self.imageCtrl.Fit()
self.panelleft.Refresh()
self.zoomed=True
def onView(self,event=None):
self.img = wx.Image(self.imagepath, wx.BITMAP_TYPE_ANY)
# scale the image, preserving the aspect ratio
self.W = self.img.GetWidth()
self.H = self.img.GetHeight()
if self.W > self.H:
self.NewW = self.PhotoMaxSize
self.NewH = self.PhotoMaxSize * self.H / self.W
else:
self.NewH = self.PhotoMaxSize
self.NewW = self.PhotoMaxSize * self.W / self.H
self.img = self.img.Scale(self.NewW,self.NewH,wx.IMAGE_QUALITY_BICUBIC)
self.imageCtrl.SetBitmap(wx.Bitmap(self.img))
self.imageCtrl.Fit()
self.panelleft.Refresh()
if __name__ == '__main__':
app = ImageTest()
app.MainLoop()
There maybe some wird stuff happening in the code that doesnt make entirely sense. Most of it is because the normal programm is much bigger and i removed many features in the prototpy that are having nothing to do with the zooming. It might very well be that i do the scaling wrong. But im out of ideas.
The Functionality for this protopye is simple: Replace the Path to a jpge image of your choiche. Run the programm. Click on the image and it should zoom. Click around your image and you will see zooming is wrong.
Thats it. Thanks for your help.
So i found the answer. But i changed something on the logic also. The picture will now be centered at the position where the user clicked. This is much more intuitive to use. I onle post the onImageClick Function. If you want to use the whole thing feel free to replace it in the original code from the question.
def onImageClick(self,event):
if self.zoomed:
self.onView()
self.zoomed=False
else :
#Determin position of mouse
ctrl_pos = event.GetPosition()
#Set magnification.
scalingfactor=4
#Set Picture size for rectangle
picturecutof=self.PhotoMaxSize/scalingfactor
##Find coordinates by adjusting for picture position
xpos=ctrl_pos[0]-((self.PhotoMaxSize-self.NewW)/2)
ypos=ctrl_pos[1]-((self.PhotoMaxSize-self.NewH)/2)
#if position is out of range adjust
if xpos>self.NewW:
xpos=self.NewW
if xpos<0:
xpos=0
if ypos>self.NewH:
ypos=self.NewH
if ypos<0:
ypos=0
#scale rectangle area to size of the unscaled image
picturecutofx=picturecutof*self.W/self.NewW
picturecutofy=picturecutof*self.H/self.NewH
#scale coordinates to unscaled image
xpos=xpos*self.W/self.NewW
ypos=ypos*self.H/self.NewH
#centeres image onto the coordinates where they were clicked
xpos=xpos-((ctrl_pos[0]*self.W/self.NewW)/scalingfactor)
ypos=ypos-((ctrl_pos[1]*self.H/self.NewH)/scalingfactor)
#if position is out of range adjust
if xpos>self.W-picturecutofx:
xpos=self.W-picturecutofx-5
if xpos<0:
xpos=0
if ypos>self.H-picturecutofy:
ypos=self.H-picturecutofy-5
if ypos<0:
ypos=0
#create rectangle to cut from original image
rectangle=wx.Rect(xpos,ypos,picturecutofx,picturecutofy)
#load original image again
self.img = wx.Image(self.imagepath, wx.BITMAP_TYPE_ANY)
#get subimage
self.img=self.img.GetSubImage(rectangle)
#scale subimage to picture area
self.img=self.img.Scale(self.PhotoMaxSize,self.PhotoMaxSize,wx.IMAGE_QUALITY_BICUBIC)
self.imageCtrl.SetBitmap(wx.Bitmap(self.img))
self.imageCtrl.Fit()
self.panelleft.Refresh()
self.zoomed=True
event.Skip()
I have a class that represents a bit of text that can be drawn to the screen. This class is intended to be used in relation to a Window object that, when it is drawn, passes a subsurface of the surface it was given to the draw() function of the text element.
The window draws, but the text does not. Nor does it draw when I invoke the TextElement's draw() function directly. I've examined the code with a debugger, and the blit is definitely being done. I've also tried switching the font to "Arial" instead of letting Pygame grab the default.
import pygame
#Background and edge color of windows
global bgcolor
global bordercolor
global txtcolor
#Pygame font object that most text will be drawn in
global mainfont
#Defaults; code that imports us can change these.
#Black background with white borders & text
bgcolor=(0,0,0)
txtcolor=(255,255,255)
bordercolor=(255,255,255)
#This will give us whatever font Pygame is set up to use as default
if not __name__== "__main__":
#Font module needs to be initialized for this to work; it should be if we're imported, but won't if
#we're being executed directly.
mainfont=pygame.font.SysFont(None,12)
else:
mainfont=None
class Window:
"""The base UI class. Is more-or-less a way to draw an empty square onto the screen. Other things are derived from this.
Also usable in its own right as a way to manage groups of more complex elements by defining them as children of
a basic window."""
def __init__(self, rect):
self.rect=rect
#'children' of a window are drawn whenever it is drawn, unless their positions are outside the area of the window.
self.children=[]
def draw(self, surface):
"Draw this window to the given surface."
pygame.draw.rect(surface, bgcolor, self.rect)
#Draw non-filled rect with line width 4 as the border
pygame.draw.rect(surface, bordercolor, self.rect, 4)
self._draw_children(surface)
def _draw_children(self,surface):
"Call draw() on each of the children of this window. Intended to be called as part of an element's draw() function."
for thechild in self.children:
#We use a subsurface because we only want our child elements to be able to access the area inside this window.
thechild.draw(surface.subsurface(self.rect))
class TextElement(str):
"""A bit of static text that can be drawn to the screen. Intended to be used inside a Window, but can be drawn
straight to a surface. Immutable; use DynamicTextElement for text that can change and move."""
def __new__(cls,text,font,rect=pygame.Rect((0,0),(0,0))):
self=super().__new__(cls,text)
self.image=font.render(text,True,txtcolor)
self.rect=rect
return self
def __repr__(self):
return str.join("",("TextElement('",self,"')"))
def draw(self,surface):
self.image.blit(surface,self.rect.topleft)
class DynamicTextElement():
"""A bit of text whose text can be changed and that can be moved. Slower than TextElement."""
def __init__(self,text,font,rect):
self.text, self.font, self.rect = text, font, rect
def __repr__(self):
return str.join("",("DynamicTextElement('",self.text,"')"))
def draw(self,surface):
image=self.font.render(self.text,True,txtcolor)
image.blit(surface,self.rect.topleft)
if __name__ == '__main__':
pygame.init()
mainfont=pygame.font.SysFont(None,12)
screen=pygame.display.set_mode((800,600))
mywindow=Window(pygame.Rect(150,150,600,300))
mytext=TextElement("Hello world! This is static, immutable text!",mainfont,pygame.Rect((200,200),(100,100)))
mydyntext=DynamicTextElement("And this is dnyamic, movable text!",mainfont,pygame.Rect((200,230),(100,100)))
print(mytext.image)
mywindow.children.append(mytext)
clock=pygame.time.Clock()
while True:
pygame.event.pump()
clock.tick(60)
screen.fill((55,55,55))
mywindow.draw(screen)
mytext.draw(screen)
pygame.display.flip()
Any ideas?
You use
self.image.blit(surface, self.rect.topleft)
but should be
surface.blit(self.image, self.rect.topleft)
surface and self.image in wrong places.
You tried to draw surface on text.
You have the same problem in DynamicTextElement()
BTW: I use Python 2 so I had to use str
self = str.__new__(cls,text)
in place of super()
self = super().__new__(cls,text)