Zelle-graphics window — Cloning object instead of moving - python

I'm making a simulation of a traffic light and a car. When the light turns green, the car should move. (I'm aware that actual stoplights don't jump from green to red, or from red to yellow, but...just go with it). The program takes input from the user as to how long the stoplight should loop; it starts by remaining red for 3 seconds and then looping through the other colors.
So my problem is that when I try to move the car (represented by a Rectangle object here) and wheels (two Circle objects), the graphics window appears to be creating an entirely new rectangle, despite the fact that I haven't called the clone() method or anything anywhere in my program. Try running the program for 8+ seconds to see.
Why is the car rectangle making copies of itself instead of just moving the original?
Code below:
import sys
from graphics import *
import time
def drawCar(win):
car = Rectangle(Point(0,510), Point(100,555))
car.setFill(color_rgb(255,0,0))
wheel1 = Circle(Point(15,565),10)
wheel2 = Circle(Point(75,565),10)
wheel1.setFill(color_rgb(0,0,0))
wheel2.setFill(color_rgb(0,0,0))
car.draw(win)
wheel1.draw(win)
wheel2.draw(win)
def drawTrack(win):
rect = Rectangle(Point(0,575),Point(500,600))
rect.setFill(color_rgb(0,0,0))
rect.draw(win)
drawCar(win)
def loop(x):
# opens and titles a graphics window
win = GraphWin("Traffic Light", 500,600)
# creates 3 black circles for the window
red = Circle(Point(250,100),80)
yellow = Circle(Point(250,260),80)
green = Circle(Point(250,420),80)
# draw the car and track
drawTrack(win)
corner1 = Point(0,510)
corner2 = Point(100,555)
car = Rectangle(corner1,corner2)
car.setFill(color_rgb(255,0,0))
wheel1 = Circle(Point(15,565),10)
wheel2 = Circle(Point(75,565),10)
wheel1.setFill(color_rgb(0,0,0))
wheel2.setFill(color_rgb(0,0,0))
car.draw(win)
wheel1.draw(win)
wheel2.draw(win)
# sets default colors of the circles
red.setFill(color_rgb(255,0,0))
yellow.setFill(color_rgb(0,0,0))
green.setFill(color_rgb(0,0,0))
red.draw(win)
yellow.draw(win)
green.draw(win)
redCount = 1 # red light counter is automatically set to 1 because it starts with red by default
yelCount = 0 # yellow and green light counters are set to 0
greenCount = 0
redSet = True
yellowSet = False
greenSet = False
start = time.time()
end = time.time() + x
time.sleep(2) # wait 2 seconds while showing the red light (since the loop waits 1 additional second on the red), then begin the color-changing
while (time.time() - start < end):
if(time.time() + 1 > end): # if the time it will take to rest on the next light will make it go overtime
break # then stop
if redSet:
print("Red, changing to yellow")
red.setFill(color_rgb(0,0,0))
yellow.setFill(color_rgb(255,255,0))
yelCount += 1
redSet = False
yellowSet = True
time.sleep(1) # hold the yellow light for 1 second
elif yellowSet:
yellow.setFill(color_rgb(0,0,0))
green.setFill(color_rgb(0,255,0))
greenCount += 1
yellowSet = False
greenSet = True
time.sleep(1) # hold green light for 1 second
#corner1.move(60,0)
#corner2.move(60,0)
car.move(60,0)
wheel1.move(60,0)
wheel2.move(60,0)
print("Corners moved")
elif greenSet:
green.setFill(color_rgb(0,0,0))
red.setFill(color_rgb(255,0,0))
greenSet = False
redSet = True
time.sleep(1)
redCount += 1
else:
break
print("Red light hit ", redCount)
print("Yellow light hit ", yelCount)
print("Green light hit ", greenCount)
# if the user clicks anywhere in the window
win.getMouse()
# then we close the window
win.close()
def main():
# prompts the user for a time limit
x = float(input("How many seconds do you want the traffic light simulation to last? "))
# prints confirmation
print("Starting the loop for ", x, " seconds:")
# begins the loop
loop(x)
main()

The problem is because you're drawing the car (and wheels) twice.
Fortunately the fix to prevent that is simple:
def drawTrack(win):
rect = Rectangle(Point(0,575),Point(500,600))
rect.setFill(color_rgb(0,0,0))
rect.draw(win)
# drawCar(win) # Don't do this.
Note: After doing this, there will be no calls at all the function drawCar() so you could remove it. Another alternative would be to leave it in and replace the lines of code in the initialization part of the loop() function that do the same thing with a call to it (thereby making the code more modular).

Related

How to stop button from working until code finishes running?

Using Tkinter and tkmacosx.
I am making a dice roller and when the button is pressed successively multiple times, it would run several times which isn't what I would like the program to do. Here is the code:
def rolled_lbl (event):
global roll_lbl_txt
sleep(0.2)
roll_lbl_txt = "You Rolled a" # show at first
rolling_lbl.place(x=345, y=200)
for x in range(4): # repeating 4 times as the first time the label stays the same
rolling_lbl.config(text = (roll_lbl_txt)) # has to be implemented because of the changing variable
rolling_lbl.update() # update the label on screen
sleep(0.25) # wait for a better effect
roll_lbl_txt += "." # adds the dot
# hides previous dice and rolled label
def hide_previous(event):
rolling_lbl.place_forget() # place.forget is used as i placed the objects in these positions so place.forget removes them
d1.place_forget() # dice image 1
d2.place_forget() # dice image 2
d3.place_forget() # dice image 3
total.place_forget() # total value
window.update() # refreshes window
sleep(0.2) # creates better effect so it isnt rushing
# dice value event
# randomiser uses the number of sides as a maximum value
# this randomised number is then placed into a string text which has the location of the dice images
def roll_dice():
global dice_sides # to get values from dice sides dropdown
global dice_value # to tell the total value codes what the value was
dice_value = random.randint(1,(dice_sides))
dice_img= "Resources/Dice_imgs/dice_img_"+str(dice_value) +".png" #variable with image location must use string as location needs to be in string format
return dice_img #returns the location to the image processing function
#event which updates dice images on screen
def update_dice(event):
global updated_picture
global updated_picture_2
global updated_picture_3
global value_d1 # for totals function
global value_d2 # for totals function
global value_d3 # for totals function
value_d2 = 0 # must be set to zero otherwise when reducing number of dice, value stays
value_d3 = 0 # must be set to zero otherwise when reducing number of dice, value stays
if no_dice == 1: # if there is only 1 dice running - from number of dice dropdown
dice_img = roll_dice() # gets location from randomiser
updated_picture = ImageTk.PhotoImage(Image.open(dice_img)) # opens location and gets image
d1.place(x=312 ,y=255) # puts on screen
d1.configure(image = updated_picture) # ensures image is updated picture
value_d1 = dice_value # tells total the first dice value - must be done because Dice_value is used for dice 2&3 as well
elif no_dice == 2: # if there is 2 dice running from number of dice dropdown - reuses same code from above for the first but changes location, new variables for second
dice_img = roll_dice() # gets location
updated_picture = ImageTk.PhotoImage(Image.open(dice_img)) #runs and opens image
d1.place(x=110 ,y=255) # places location of image on screen
d1.configure(image = updated_picture) # puts image in location
value_d1 = dice_value # tells total value of dice one
dice_img = roll_dice() # gets location for dice two - dice one has already been inputed so variable can be reused
updated_picture_2 = ImageTk.PhotoImage(Image.open(dice_img)) # loads image from location
d2.place(x=525 ,y=255) # places on righthand side of screen
d2.configure(image = updated_picture_2) # updates image
value_d2 = dice_value # tells total value of dice two
elif no_dice == 3: # if there is 2 dice running from number of dice dropdown - reuses same code from above for the first and second but changes location, new variables for third
dice_img = roll_dice() # gets location of dice 1
updated_picture = ImageTk.PhotoImage(Image.open(dice_img)) # loads image
d1.place(x=46 ,y=255) # puts location on far left of screen
d1.configure(image = updated_picture) # updates image to be new loaded one
value_d1 = dice_value # tells total the value of dice one
dice_img = roll_dice() # gets location of dice 2
updated_picture_2 = ImageTk.PhotoImage(Image.open(dice_img)) # loads image
d2.place(x=321 ,y=255) # places in middle of screen
d2.configure(image = updated_picture_2) # updates image
value_d2 = dice_value # tells total the value of dice two
dice_img = roll_dice() # gets location of dice 3
updated_picture_3 = ImageTk.PhotoImage(Image.open(dice_img)) # loads image
d3.place(x=596 ,y=255) # places on right of screen
d3.configure(image = updated_picture_3) # updates image
value_d3 = dice_value # tells total value of dice three
# Event which runs Total at bottom of screen
def total_lbl (event):
total_txt = "The total is: " # original text
dice_total = (value_d1 + value_d2 + value_d3) # add values of dice one two (if available) and 3 (if available)
total_txt += str(dice_total) # adds total of dice to original text
total.config(text=(total_txt)) # updates label to have text
total.update()
total.place(x=350, y=535)
roll=Button(window, text="Roll the Dice", background="red", fg="white", activebackground='#00FF00',font=("Calibri",(30)), width =180, height = 55, borderless=1)
roll.place(x=350, y=120)
# when the button is clicked:
# happens in order shown
roll.bind('<Button-1>', hide_previous, add="+") # commands hide_previous function to hide old objects
roll.bind('<Button-1>', rolled_lbl, add="+") # commands rolled_lbl to display you rolled a
roll.bind('<Button-1>', update_dice, add="+") # commands update_dice to update dice images and display on screen
roll.bind('<Button-1>', total_lbl, add="+") # commands total value to display on screen
Is there a way to stop the button from running the functions whilst it is going? I don't want to use disable, however.
Could I create a variable that will only let the code work once it has begun and leave the button useless until it has finished all of the 4 functions?

In python turtle, when I want to show random motion of balls ,they are not bouncing off the edge of my screen

import turtle
import random
import time
we confirm ball is on left side of screen
def atLeftEdge(ball,screen_width):
if ball.xcor()<screen_width/2:
return True
else:
return False
we confirm ball is on right side
def atRightEdge(ball,screen_width):
if ball.xcor()>screen_width/2:
return True
else:
return False
we confirm ball is on top edge
def atTopEdge(balls,screen_height):
if ball.ycor()>screen_height/2:
return True
else:
return False
we confirm ball is on bottom edge
def atBottomEdge(balls,screen_height):
if ball.ycor()<-screen_height/2:
return True
else:
return False
Now ball must bounce when it reaches edge of screen
def bounceBall(ball,new_direction):
if new_direction=='left'or new_direction=='right':
new_heading=180-ball.heading()
elif new_direction=='up'or new_direction=='down':
new_heading=360-ball.heading()
return new_heading
def createBalls(num_balls):
balls=[]
for x in range(0,num_balls):
new_ball=turtle.Turtle()
new_ball.shape('circle')
new_ball.fillcolor('black')
new_ball.speed(0)
new_ball.penup()
new_ball.setheading(random.randint(1,359)) #random angle between 1 to 359
balls.append(new_ball)
return balls
program starts here ,this is the main part of program where we take input from user and call all functions
#------------------MAIN-------------------------------------------------
print("The program stimulates bouncing balls in a turtle screen"\
"for a specified number of seconds")
#TODO:create turtle graphics window object
#set up screen
screen_width=800
screen_height=600
turtle.setup(screen_width,screen_height)
#get reference to turtle window by calling Screen method
window=turtle.Screen()
window.title('Random balls on screen')
window.bgcolor('violet')
ask user to enter execution time and number of balls
num_sec=int(input("enter no of seconds to run"))
num_balls=int(input("enter no of balls in sim"))
create balls
balls=createBalls(num_balls)
set start time
start_time=time.time()
begin simulation
terminate=False
while not terminate:
for x in range(0,len(balls)):
balls[x].forward(40)
if atLeftEdge(balls[x],screen_width):
balls[x].setheading(bounceBall(balls[x],'right'))
elif atRightEdge(balls[x],screen_width):
balls[x].setheading(bounceBall(balls[x],'left'))
elif atTopEdge(balls[x],screen_height):
balls[x].setheading(bounceBall(balls[x],'down'))
elif atBottomEdge(balls[x],screen_height):
balls[x].setheading(bounceBall(balls[x],'up'))
if time.time()-start_time>num_sec:
terminate =True
#exit on close window
turtle.exitonclick()
I've change the elifs to ifs as these are independent events.
if atLeftEdge(balls[x],screen_width):
balls[x].setheading(bounceBall(balls[x],'right'))
if atRightEdge(balls[x],screen_width):
balls[x].setheading(bounceBall(balls[x],'left'))
if atTopEdge(balls[x],screen_height):
balls[x].setheading(bounceBall(balls[x],'down'))
if atBottomEdge(balls[x],screen_height):
balls[x].setheading(bounceBall(balls[x],'up'))
also in atLeftEdge function you need to write your if this way :
if ball.xcor()<-screen_width/2:
also in atTopEdge and atBottomEdge I think you meant
def atTopEdge (ball , screen_height) :
and
def atBottomEdge(ball , screen_height):

Issue with python skipping code in simple chess program

I am creating a simple program for chess, and I ran to an issue of supposedly python skipping code.
Program entry is: find_side()
Console output:
Enter your team(1-black 2-white):1
<PlayResult at 0x3ec1dc0 (move=e2e4, ponder=d7d5, info={}, draw_offered=False, resigned=False)>
Enter your enemies move:
According to the console output, engine randomly generated move for the White player and made a counter-response in ponder. But I have input for that, looks like, that python is executing result_engine sooner than user input.
Also, there's one more problem. Engine completely ignores the chess.engine.turn = turn line.
I am using stockfish 11 as an engine and import chess as an link between python code and engine with universal chess engine
Code:
import chess
import chess.engine
import time
import os
def logic_white():
engine = chess.engine.SimpleEngine.popen_uci("C:\\Users\\Admin\\Desktop\\sf.exe")
board = chess.Board()
turn = True # True - white False - black
while True:
chess.engine.turn = turn # This isn't working
result_engine = engine.play(board,chess.engine.Limit(time=0.1))
print(result_engine)
res = input("Enter your enemie's move: ")
move = chess.Move.from_uci(res)
board.push(move)
turn = not turn
time.sleep(0.5)
def logic_black():
engine = chess.engine.SimpleEngine.popen_uci("C:\\Users\\Admin\\Desktop\\sf.exe")
board = chess.Board()
turn = True # True - white False - black
while True:
chess.engine.turn = turn # This isn't working
res = input("Enter your enemie's move: ")
move = chess.Move.from_uci(res) #Inputting the enemy move and putting in on the virtual board
board.push(move)
result_engine = engine.play(board,chess.engine.Limit(time=0.1)) #Calculating best move to respond
print(result_engine)
board.push(result_engine) #Push the engine's move to the virtual board
turn = not turn # Inverting turn, so turns can change from black to white, etc.
time.sleep(0.5)
def find_side():
if(input("Enter your team(1-black 2-white):")) == 1:
logic_black()
else:
logic_white()
Python's input function returns a string, so it will never be equal to the integer 1. As such, the code will always go into the else block. To fix this, either convert the input to an integer or compare it to '1'.
def find_side():
if int(input("Enter your team(1-black 2-white):")) == 1:
logic_black()
else:
logic_white()

Pygame + python: 1 part of code has pygame.wait while rest of code runs

I am making a game in which u have to carry move objects from one place to another. I can move my character to the zone in which I need to put something. I want the player to wait in the zone for 5 secs before the object is placed there, however, if i do this you cannot move anymore if u decide u dont want to place the object in the zone as the whole script would be paused.
Is there a way to make one part of the script wait while the rest of it runs?
Every game needs one clock to keep the game loop in sync and to control timing. Pygame has a pygame.time.Clock object with a tick() method. Here's what a game loop could look like to get the behaviour you want (not complete code, just an example).
clock = pygame.time.Clock()
wait_time = 0
have_visited_zone = False
waiting_for_block_placement = False
# Game loop.
while True:
# Get the time (in milliseconds) since last loop (and lock framerate at 60 FPS).
dt = clock.tick(60)
# Move the player.
player.position += player.velocity * dt
# Player enters the zone for the first time.
if player.rect.colliderect(zone.rect) and not have_visited_zone:
have_visited_zone = True # Remember to set this to True!
waiting_for_block_placement = True # We're now waiting.
wait_time = 5000 # We'll wait 5000 milliseconds.
# Check if we're currently waiting for the block-placing action.
if waiting_for_block_placement:
wait_time -= dt # Decrease the time if we're waiting.
if wait_time <= 0: # If the time has gone to 0 (or past 0)
waiting_for_block_placement = False # stop waiting
place_block() # and place the block.
Example with threading:
from threading import Thread
def threaded_function(arg):
# check if it's been 5 seconds or user has left
thread = Thread(target = threaded_function, args = (10, ))
if user is in zone:
thread.start()
# continue normal code
Another potential solution is to check the time the user went into the zone and continuously check the current time to see if it's been 5 seconds
Time check example:
import time
entered = false
while true:
if user has entered zone:
entered_time = time.time()
entered = true
if entered and time.time() - entered_time >= 5: # i believe time.time() is in seconds not milliseconds
# it has been 5 seconds
if user has left:
entered=false
#other game code

Processing updating text and function parameters

This is my very first post on this website, and hopefully I can get some valuable insight and hints in regards to my problem as I'm a relative noob when it comes to programming. I am using Python mode in the Processing environment.
I was given a lecture problem (that the teacher will eventually go over) but I wanted to be able to solve it beforehand. Unfortunately I am not sure how to. I'm supposed to create a program that displays the amount of buns, franks and hotdogs that can be made from the amount of buns and franks, and I have keyboard inputs to increase the amount that is displayed for buns and franks.
Currently, I can't figure out how to get my text to update when I enter keyboard inputs nor can I figure out how to automatically update the number of hotdogs based on the amount of buns and franks I have.
I have attached two pictures of the question that will clear up any confusion if my explanation was not clear. I have also attached the current code I have.
picture 1
picture 2
Code:
#Variables/Model
meat = ""
buns = ""
dogs = min(12 * meat, 8 * buns)
def setup():
size(400,400)
def draw():
global meat, buns, dogs
background(255)
meat = 0
buns = 0
dogs = min(12 * meat, 8 * buns)
fill(0)
text("Packages of meat:" + str(meat), 50, 100)
text("Packages of buns:" + str(buns), 250, 100)
text("Dogs possibly made:" + str(dogs), 150, 200)
def make_hotdogs(totalMeat, totalBuns):
global meat, buns, dogs
if keyPressed == "f":
meat += 1
elif keyPressed == "g":
meat -= 1
elif keyPressed == "b":
buns += 1
elif keyPressed == "n":
buns -= 1
else:
print("Type b, n, f or g")
You're never calling your make_hotdogs() function, so you're never hitting the if statements that check which key is pressed.
You might want to look into the keyPressed() function. You can find more info in the reference.
But honestly, if you're confused, maybe you should just wait for the teacher to explain everything in the lecture.
It's been a week so let's assume that lecture has taken place and let's solve this problem. The issues I see with the OP's code is a lack of understanding of basic Python and the Processing environment. And a failure to take advantange of the problem terminology that's been provided.
Reworking the code to address the above issues and generate a viable picnic planner that runs in the Processing environment:
# Variables/Model
FRANKS_PER_PACKAGE = 12
BUNS_PER_PACKAGE = 8
frank_packages = 0
bun_packages = 0
def setup():
size(400, 400)
fill(0) # black text on a
background(255) # white background
def draw():
hotdogs = make_hotdogs(frank_packages, bun_packages)
text("Packages of franks: " + str(frank_packages), 50, 100)
text("Packages of buns: " + str(bun_packages), 250, 100)
text("Hotdogs possibly made: " + str(hotdogs), 150, 200)
def keyPressed():
global frank_packages, bun_packages
if key == "f":
frank_packages += 1
elif key == "g":
if frank_packages > 0:
frank_packages -= 1
elif key == "b":
bun_packages += 1
elif key == "n":
if bun_packages > 0:
bun_packages -= 1
background(255) # white out old calculations
def make_hotdogs(p_franks, p_buns):
return min(p_franks * FRANKS_PER_PACKAGE, p_buns * BUNS_PER_PACKAGE)
With the exception of make_hotdocs(), the Processing environment calls these functions for us. It calls setup() once at the start of the program; it calls draw() continuously over and over again; it calls keyPressed() whenever the user types on the keyboard, leaving the letter pressed in the key variable.
In Python, we only need to declare global variables where we plan to change their values, not where we simply intend to use their values.

Categories

Resources