Line repeat in processing with python - python

i wanna loop a line function in processing using python. it works but without any motion. i wanna loop line like this:
https://www.youtube.com/watch?v=0KUg9dcIFtM
but in python this not work. it is static.
This is my code:
def setup():
size(400,600);
background(255);
def draw():
x=0;
num=600;
andx=0;
for x in range(0,600,20):
line(x,0,x,600);
x+=20;
andx+=20;
if andx>400:
andx=0;
why i have no motion?
screen of the sketch

If you were trying to follow that video I can see how it was somewhat confusing (given all of the commented out code). A working Python equivalent is:
andx = 0 #global variable to determine placement of final line
def setup():
size(400,600)
frameRate(5)
def draw():
global andx
background("#EC4DF5")
stroke(50)
for x in xrange(0,andx,20):
line(x,0,x,height)
andx += 20
if andx > width:
andx = 0
draw() is what actually draws in an animation loop. Any drawing function such as line() is buffered with the display only redrawn at the end of the call to draw().
For tutorials, it would be better to see the Python-based ones here rather than Java ones on YouTube.

Related

How can you have multiple things happen during onscreenclick for python? (turtle)

I'm trying as a personal project to create a game in python using turtle, but I've run into an issue
def player_headto(x, y):
player.left(player.towards(x, y) - player.heading())
player.goto(x, y)
window.onscreenclick(player_headto)
def currency(x, y):
c_amount = 0
if x >= 100 and x <= 150:
if y >= 50 and y <= 100:
c_amount = c_amount + 1
print(str(c_amount))
else:
print(str(c_amount))
else:
print(str(c_amount))
window.onscreenclick(currency)
I'm not able to have multiple things happen all at once each click. I've tried a bunch, but it always either returns an error or only does one.
use multi threading, which makes the processes multi task
I'm not able to have multiple things happen all at once each click.
You don't need threading or nor a single click hander that does everything. What you need is the poorly documented add argument to onscreenclick() aka screen.onclick(). Here's a simple example:
from turtle import Screen, Turtle
def draw_circle(x, y):
turtle.circle(100)
def change_background(x, y):
screen.bgcolor('red')
turtle = Turtle()
screen = Screen()
screen.onclick(change_background)
screen.onclick(draw_circle, add=True)
screen.mainloop()
Of course, both won't happen all at once but rather sequencially. Which is what you'd get with a single click hander that does everything, or even threading as all turtle graphic operations have to handled by the main thread.

Python Processing: How to call multiple of the same class

I am a noob at Python and I am having trouble with this problem. I am trying to create a raindrop scene with the 'drop' class. Right now, the code only calls one drop at a time. How can I call multiple drops at once?
Here is my code:
import random
class drop():
def __init__(self):
# where the drop starts
self.x = random.randint(20,480)
self.y = 0
#how fast the drop falls
self.yspeed = 5
def fall(self):
self.y = self.y+self.yspeed
def show(self):
# color of drop
stroke(1,1,1)
fill(1,1,1)
strokeWeight(1)
line(self.x,self.y,self.x,self.y+10)
def setup():
size(500,500)
global d
d = drop()
def draw():
background(255,255,255)
d.fall()
d.show()
You can create as many instances of a class as you want. For example the following code creates two drops.
d1 = drop()
d2 = drop()
You can also create a list of drops like this -
drops = []
drops.append(drop())
drops.append(drop())
drops[0].fall()
drops[1].show()
I'm a bit rusty with Python, But I would suggest changing draw to accept an array of elements.
So for example, in setup and can generate an array of droplets by doing:
def setup():
size(500,500) #Idk what this does, I assume its another function somewhere else in your code
droplets = [] #The array of droplets we want to render
for x in range(6): #6 is the number of droplets we want.
droplets.append(drop())
#Pass droplets into draw.
def draw(entities):
background(255,255,255) #Again, IDK what this does but its in your code. I assume it is another function somewhere.
for droplet in entities: #Goes through ALL the droplets in the list you passed.
droplet.fall() #Calls these methods on every droplet in the array.
droplet.show()
This is untested, but from here you would somehow pass the droplets array into draw and BOOM you have a bunch of droplets being rendered every draw cycle. I would suggest NOT using global variables. Its just bad practice. You should have another function somewhere that runs all of this, I assume on a time for the draw cycle.

How to make a text button which speeds up text animation

So basically, I have text which is typed out character by character. with the code:
text = "test"
delta = 40
delay = 0
for i in range(len(text) + 1):
s = test_string[:i]
update_text = lambda s=s: canvas.itemconfigure(variable, text=s)
canvas.after(delay, update_text)
delay += delta
This is all inside of a function, lets call: def NewEvent(). What I want to do is create a text button with the text "Skip" which changes delta to a lower number, thus speeding up the animation upon click. I cant seem to figure it out, normally when you make text clickable, it has something along the lines of:
skipbutton = canvas.create_text((400,100), activefill="Medium Turquoise", text="Skip", fill="White", font=('Arial', 30), tags="skip")
canvas.tag_bind('skip', '<ButtonPress-1>', function)
The problem is, it needs to stay within the same function. So I thought of creating an if statement similar like this:
if delta is 40 and skip is ____:
delta = 10
However, I dont know what would come after- (skip is) for it to work, or even if this would work at all... Any help would be appreciated.
You are doing animation in a way that makes your problem very difficult to solve. The problem is that you are scheduling all of the frames of animation before you display the first frame. In order to change the speed you would have to cancel all of the pending jobs and recreate new jobs. This is not the proper way to do animation in Tkinter.
A better solution is to only have a single job active at one time. You do this by having a function that displays one frame of animation and then schedules itself to run again in the future.
The general structure looks like this:
def animate():
<draw one frame of animation>
if <there are more frames>:
root.after(delay, animate)
In your case, each "frame" is simply adding one character to a character string, and your condition at the end is to simply check to see if there are more characters.
A simple implementation is to pass a string into the animate function, have it pull the first character off of the string and append it to the display.
For example:
def update_text(text):
char = text[0]
remainder = text[1:]
current_string = canvas.itemcget(variable, "text")
new_string = current_string + char
canvas.itemconfigure(variable, text=new_string)
if len(remainder) > 0:
canvas.after(delay, update_text, remainder)
To start the animation, give it the string you want to display:
update_text("Hello, world")
This this function depends on a global variable for the delay, writing a function to speed the animation up or slow it down only requires that you modify this delay:
def speedup():
global delay
delay = int(delay/2)
You can call it from a button quite easily:
tk.Button(root, text="Speed up!", command=speedup)

How to use a while loop within a class in pygame?

I'm trying to make an instance class (Block) spawn when the mouse is clicked and fade out slowly, and i want to be able to spawn an infinite number of blocks that fade out provided that the mouse is clicked fast enough.
To do this, I want to initiate the fade() function when the instance is first spawned and set ticks to 255 and set the alpha to ticks.
However, when using a while loop, the function completes itself without updating the display because the program gets constrained to the while loop in the fade() function.
Can someone help me with calling the fade function 255 times per instance?
import pygame,sys,time,Blockm
from pygame.locals import *
black,white=(0,0,0),(255,255,255)
size=w,h=1400,800
screen=pygame.display.set_mode(size)
pygame.init()
class Block(object):
sprite = None
def __init__(self, x, y):
if not Block.sprite:
Block.sprite = pygame.image.load("Block.png").convert_alpha()
self.rect = Block.sprite.get_rect(top=y, left=x)
self.fade()
def fade(self):
ticks=255
Block.sprite.set_alpha(ticks)
while ticks!=0:
ticks-=1
print(ticks)
while True:
blocks = []
mmraw=pygame.mouse.get_pos()
mmx,mmy=mmraw[0]-(pygame.image.load("Block.png").get_width())/2,mmraw[1]-(pygame.image.load("Block.png").get_width())/2
for event in pygame.event.get():
if event.type== pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
print('click')
blocks.append(Block(mmx,mmy))
for block in blocks:
screen.blit(block.sprite, block.rect)
print('trying to blit')
print(Block.sprite.get_offset())
pygame.display.update()
I have one solution for you that ought to accomplish what you want it to, but I concede that it does not answer the question exactly as asked.
The first and most hindering problems you may be having are that you need to use pygame.image.load("Block.png").convert() and not pygame.image.load("Block").convert_alpha(), since .convert_alpha() doesn't work with .set_alpha(..).
It's also possible that you aren't noticing when blocks fade in any solution because screen isn't being refreshed prior to update. New, faded blocks are being drawn over 'brighter' ones, producing no difference (overlapping blocks notwithstanding). I've added a stopgap to my solution code below that fills the screen with blue, but I imagine you'll want something different. It's marked with a comment.
What I would suggest is having each Block process its alpha locally during a call from the main loop in your for block in blocks: block before you blit it. This version of your code should give you the result you want, although it does it using just the main loop, rather than parallel loops like you were asking about...
import pygame,sys,time,Blockm
from pygame.locals import *
black,white=(0,0,0),(255,255,255)
size=w,h=1400,800
screen=pygame.display.set_mode(size)
pygame.init()
class Block(object):
sprite = None
def __init__(self, x, y):
if not Block.sprite:
Block.sprite = pygame.image.load("Block.png").convert()
# ^ MODIFIED: .convert_alpha() won't recognize the effects of .set_alpha(), so use regular .convert() instead.
self.rect = Block.sprite.get_rect(top=y, left=x)
self.ticks = 255 # NEW: Set a local tick count.
# REMOVED `self.fade()`: This runs on main now.
def fade(self):
## REPURPOSED METHOD: Runs each frame that the Block is active. Called on main loop.
# MODIFIED BLOCK: Update local tick count before setting the class's sprite alpha.
self.ticks -= 1
Block.sprite.set_alpha(self.ticks) # MOVED, MODIFIED: uses local tick count for alpha.
print(self.ticks) # UPDATED: Using local ticks.
blocks = [] # MOVED: Make a list for the Blocks, but don't clear it on frame.
while True:
mmraw=pygame.mouse.get_pos()
mmx,mmy=mmraw[0]-(pygame.image.load("Block.png").get_width())/2,mmraw[1]-(pygame.image.load("Block.png").get_width())/2
# ^ There may be a tidier way of doing this. Described in solution body...
for event in pygame.event.get():
if event.type== pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
print('click')
blocks.append(Block(mmx,mmy))
screen.fill((22,22,222))
# ^ NEW: Fill the screen with some backdrop so that the erasure is obvious!
# If you don't do this, you'll be blitting faded images over brighter ones and there will be no real change!!
for block in blocks:
block.fade() # NEW: Update this block.
if block.ticks < 1: # NEW BLOCK: If the block has become invisible...
blocks.remove(block) # NEW: Expunge this invisible block.
continue # NEW: Immediately move on to the next block.
screen.blit(Block.sprite, block.rect)
print('trying to blit')
print(Block.sprite.get_offset())
pygame.display.update()
There's a small problem with it, which is that vanishing Blocks trigger a 'flicker' in other remaining Blocks (or at least the next one in blocks) and I'm not sure why or how.
While I was looking, I found a few other things you might want to consider:
...in class Block::
-Consider using sprite = pygame.image.load("Block.png").convert() instead of sprite = None. This way, you can use something like mmx,mmy = mmraw[0] - Block.sprite.get_rect.centerx, mmraw[1] - Block.sprite.get_rect().centery instead of loading the image for a moment, just to know its size. Since all Blocks use the same graphic, it shouldn't make a difference, and this way, you won't have to reacquire the offset if you change Block.sprite during runtime!
-Consider assigning a copy of Block's sprite to each instance instead of using the class's Surface. This will take up more processing power, but only momentarily if you use it as a return instead. For instance:
class Block(object):
...
def fade(self):
sprite = Block.sprite.copy()
sprite.set_alpha(self.ticks)
self.ticks -= 1
return sprite
...
while True: # main loop
...
for block in blocks:
screen.blit(block.fade(), block.rect) # Although there are more Pythonic ways of writing this.
Alternatively, you could use screen.blit(sprite, self.rect) in Block.fade() rather than on main and forego the return entirely. Because the alpha is set locally, it won't have to be reset every time fade runs, and Block's unbound sprite can stay fresh!
Anyway, I hope this solves your problem, even if it does not (exactly) answer your question!
You probably want to use threads. These allow you to run multiple things at once. For your code, change it like this:
from threading import Thread
import pygame, sys, time, Blockm
from pygame.locals import *
black = (0,) * 3
white = (255,) * 3
size = w, h = 1400, 800
screen = pygame.display.set_mode(size)
pygame.init()
class Block(object):
sprite = None
def __init__(self, x, y):
if not Block.sprite:
Block.sprite = pygame.image.load("Block.png").convert_alpha()
self.rect = Block.sprite.get_rect(top=y, left=x)
Thread(target=self.fade) # Made this a Thread so it runs separately.
def fade(self):
for tick in range(255, 0, -1):
Block.sprite.set_alpha(tick)
print(tick)
def game():
while True:
# The contents of that while True loop at the end
def main():
Thread(target=Main)
if __name__ == "__main__":
main()
This also adds an entry point to your program, which was lacking. Also, Block.fade wouldn't actually do what you want, as you only set_alpha once. I fixed that with a for loop instead. Also, note that you can now just return to break out of the whole game.

Having a certain piece of code run for a specified amount of time

I'm working on a galactica type of game using pygame and livewires. However, in this game, instead of enemy's, there are balloons that you fire at. Every 25 mouse clicks, I have the balloons move down a row using the dy property set to a value of 1. If a balloon reaches the bottom, the game is over. However, I'm having some trouble figuring out how to get this to run only for, say, 1 second, or 2 seconds. Because I don't have a way to "time" the results, the dy value just indefinitely gets set to 1. Therefore, after the first 25 clicks, the row just keeps moving down. This is ok, but like I said, it's not my intended result.
Here is the code I have so far for this action:
if games.mouse.is_pressed(0):
new_missile = missile(self.left + 6, self.top)
games.screen.add(new_missile)
MISSILE_WAIT = 0 #25
CLICKS += 1
if CLICKS == 25:
SPEED = 1
CLICKS = 0
CLICKS, and MISSILE_WAIT are global variables that are created and set to an initial value of 0 before this block of code. What I'm trying to figure out is the algorithim to put underneath the if CLICKS statement. I've looked through the python documentation on the time module, but just can't seem to find anything that would suit this purpose. Also, I don't think using a while loop would work here, because the computer checks those results instantly, while I need an actual timer.
I'm not sure if I got your question but what I can suggest is that:
class Foo():
def __init__(self):
self.start_time = time.time()
self.time_delay = 25 # seconds
def my_balloon_func(self):
if(time.time() - self.start_time) > self.time_delay:
self.start_time = time.time()
else:
# do something

Categories

Resources