I appreciate anyone's help on this as I am new to Python. The script below will draw a triangle with 3 mouse clicks. I want to alter this script with a loop to allow unlimited mouse clicks. Can someone help me on the next steps? A picture of what I want it to do is provided below the script.
import graphics as g
def main():
win=g.GraphWin("Draw a Triangle")
win.setCoords(0.0,0.0,100.0,100.0)
message=g.Text(g.Point(50,50),"Click on three points")
message.draw(win)
p1=win.getMouse()
p1.draw(win)
p2=win.getMouse()
p2.draw(win)
p3=win.getMouse()
p3.draw(win)
triangle=g.Polygon(p1,p2,p3)
triangle.setFill("Red")
triangle.setOutline("cyan")
triangle.draw(win)
message.setText("click anywhere to quit.")
win.getMouse()
main()
print(main)
Below is what I would like it to do. On my 2nd mouse click it will automatically draw the point and a line between the first and second point. Then the same for point 3, point 4, etc.with the option for unlimited points.
The main idea is to keep track of the points the user has clicked, and to clear the canvas when re-drawing the objects.
The graphics.py file you are using seems to be from here. The code in turn is just a wrapper around TkInter.
Therefore, just familiarise yourself with how TkInter handles mouse and keypresses and you should be able to do what you want. For e.g., rather than use win.getMouse(), the preferred way to use a GUI framework is to bind functions (event handlers) to specific events as I demonstrate below.
import graphics as g
def main():
win = g.GraphWin("Draw a polygon")
win.setCoords(0.0, 0.0, 100.0, 100.0)
message = g.Text(g.Point(50, 50), "Click to add point to polygon")
message.draw(win)
# Set of points clicked so far
points = []
def onClick(pt):
x, y = win.toWorld(pt.x, pt.y)
points.append(g.Point(x, y))
poly = g.Polygon(*points)
# Clear all objects on canvas.
# You can choose to delete only current polygon by associating a label with it.
# See Tkinter's documentation for details
win.delete("all")
poly.setFill("Red")
poly.setOutline("Cyan")
poly.draw(win)
win.setMouseHandler(onClick)
# This is not idea as we are wasting cycles doing polling
# I believe Tkinter should have a better approach to avoid this.
while not win.checkKey() == 'q':
pass
if __name__ == "__main__":
main()
Related
I'm working on a program and I ran into a problem I'm not sure how to fix.
I'm gonna try to give a simplified example below.
The purpose of the code is to read data from a device and display the stream live. However in the GUI You can select what stream of data You wish to display.
import tkinter
import datastream.py
dataselector = 3
def ReDraw(dataselector):
if dataselector == 0:
#draw a certain stream
if dataselector == 1:
#draw another stream
#draw a bunch of other streams in other displays
canvas.after(10,ReDraw,dataselector)
def SelectData(mouseevent):
if event.clickedbutton == 0:
#thatbuttonbecomesred
dataselector = 0
if event.clickedbutton == 1:
#thatotherbuttonbecomesred
dataselector = 1
return dataselector
ReDraw(dataselector)
SelectData()
Sorry for the pseudo-code, but it's the simplest way to explain the problem.
The behavior I get is that everything draws and redraw correctly, the buttons do interact and become red BUT the ReDraw function only takes the original dataselector value and doesn't take the new one given by the SelectData function even if, testing with some prints, it indeed changes it.
It's like the ReDraw function takes the original value and store it secretly, ignoring any changes to that value!
I also tried using a global dataselector in the SelectData function instead, but it doesn't change anything.
Any suggestions how to fix this?
Just for clarifications, #PaulM. solved it in the comments above.
Here is a version of the pseudo-code that would work correctly.
import tkinter
import datastream.py
dataselector = 3
def ReDraw():
if dataselector == 0:
#draw a certain stream
if dataselector == 1:
#draw another stream
#draw a bunch of other streams in other displays
canvas.after(10,ReDraw)
def SelectData(mouseevent):
global dataselector
if event.clickedbutton == 0:
#thatbuttonbecomesred
dataselector = 0
if event.clickedbutton == 1:
#thatotherbuttonbecomesred
dataselector = 1
ReDraw()
SelectData()
Not sure why I didn't see it before, but thanks for the help!
Hey I've been stuck on this issue for quite a while and was hoping someone could help me out:
I'm using pyglet and have got all of the code working in my project (even what I was having the issue with) then I restarted my computer and suddenly it didn't work...
This is the loop that is instantiating my 'Letter' objects:
main_st = ut.makeString("EXNXYXAXDAADUXMDXLGEQTAQXDDQSVXUTSXKHXHRXYFUXLXJUTHXYVADSUXKHUQUIXSJHXHDPKXFQUXILNXORMXRPL")
letter_list = []
for i in range(len(main_st)):
letter_list.append(l.Letter(pyglet.resource.image("Letters/" + main_st[i] + ".png"),main_st[i],10,10))
And this is the Letter class constructor Letter is a subclass of pyglet.sprite.Sprite:
def __init__(self,im,iden,xx,yy):
super(Letter,self).__init__(img=im,x=xx,y=yy)
At no point in the program do I modify the x and y coordinates of sprite but when I go to draw them, no matter what I put in for xx and yy they're always drawn in the same place on the window UNLESS I do a very large number for yy, and in those cases it simply disappears (I assume it's outside of the window).
I'm having each letter flash on the screen for 1 second and in order to do that here's my on_draw method
def on_draw():
background.draw()
if not key_manager.cur_letter == None:
key_manager.cur_letter.draw()
(only key_manager.cur_letter gets drawn and that switches every second).
The problem might be related to older versions.
But after calling super(Letter, self)... you could do:
def __init__(self,im,iden,xx,yy):
super(Letter,self).__init__(img=im,x=xx,y=yy)
self.x = xx
self.y = yy
And that should do the trick.
In this code I can't see why it isn't printing a hexagon 24 times. I tell it to make a 6 sided shape with 60 degrees between lines ( a hexagon) and tell it do turn 15 degrees each time. This ends up being a even 24 for the picture I'm trying to draw.
import turtle
Hex_Count = 0
x = turtle.Turtle()
x.speed(.25)
def Hexagon():
for i in range(24):
for i in range(6):
x.forward(100)
x.left(60)
Hex_Count = Hex_Count + 1
x.left(15)
print(Hex_Count)
Hexagon
But, for some reason, when I run this code the turtle screen pops up for about a half second then closes. How do I get it to perform in the way I want it to?
You have several errors that I corrected for you; I added the explanation in the comments:
import turtle
hexagons_count = 0
my_turtle = turtle.Turtle() # x is not a good name for a Turtle object
# my_turtle.speed(.25) # see #cdlane comment reported in a note under.
def draw_hexagon(): # use explicit names respecting python conventions (no camel case)
global hexagons_count # need global to modify the variable in the function scope
for idx in range(24): # use different dummy variable names in your loops
for jdx in range(6): # use different dummy variable names in your loops
my_turtle.forward(100)
my_turtle.left(60)
hexagons_count += 1
my_turtle.left(15)
print(hexagons_count)
draw_hexagon() # need parenthesis to call the function
turtle.exitonclick() # this to exit cleanly
Note: I know you simply copied it from the OP but my_turtle.speed(.25)
doesn't make sense as the argument should be an int from 0 to 10 or a
string like 'slow', 'fastest', etc. I especially don't understand why
beginners with turtle code that isn't working call turtle.speed() at
all -- it seems to me a function to be tweaked after everything is
working. #cdlane
You have some reference issue, you just need to put the variable hex_count where it needs to be so you don't have error accessing it.
import turtle
x = turtle.Turtle()
x.speed(.25)
def Hexagon():
Hex_Count = 0
for i in range(24):
for i in range(6):
x.forward(100)
x.left(60)
Hex_Count += 1
x.left(15)
print(Hex_Count)
Hexagon()
prints 24
You have several problems with your program. One is that it will when after running through the program, closing the window it created. You can add turtle.exitonclick() to the end of your script which tells python to wait for a click in the graphics window, after which it will exit.
The second problem is that you don't call the Hexagon function because you're missing the parentheses. Even if a function takes no arguments, you still need to call it like:
Hexagon()
The final problem is that you need to define Hex_Count before you try to increment it. Hex_Count + 1 will thrown an error if Hex_Count wasn't already assigned to. You can fix this by putting
Hex_Count = 0
before your for loop in Hexagon.
An approach different in a lot of the details but primarily in its use of circle() to more rapidly draw the hexagons:
from turtle import Turtle, Screen # force object-oriented turtle
hex_count = 0 # global to count all hexagons drawn by all routines
def hexagons(turtle):
global hex_count # needed as this function *changes* hex_count
for _ in range(24): # don't need explicit iteration variable
turtle.circle(100, steps=6) # use circle() to draw hexagons
turtle.left(15) # 24 hexagons offset by 15 degrees = 360
hex_count += 1 # increment global hexagon count
print(hex_count)
screen = Screen()
yertle = Turtle(visible=False) # keep turtle out of the drawing
yertle.speed('fastest') # ask turtle to draw as fast as it can
hexagons(yertle)
screen.exitonclick() # allow dismiss of window by clicking on it
I'm trying to make Connect 4 in python, but I can't figure out how to get the coordinates of the screen click so I can use them. Right now, I want to draw the board, then have someone click, draw a dot, then go back to the top of the while loop, wipe the screen and try again. I've tried a couple different options but none have seemed to work for me.
def play_game():
"""
When this function runs, allows the user to play a game of Connect 4
against another person
"""
turn = 1
is_winner = False
while is_winner == False:
# Clears screen
clear()
# Draws empty board
centers = draw_board()
# Decides whose turn it is, change color appropriately
if turn % 2 == 0:
color = RED
else:
color = BLACK
# Gets coordinates of click
penup()
onscreenclick(goto)
dot(HOLE_SIZE, color)
turn += 1
As well intentioned as the other answers are, I don't believe either addresses the actual problem. You've locked out events by introducing an infinite loop in your code:
is_winner = False
while is_winner == False:
You can't do this with turtle graphics -- you set up the event handlers and initialization code but turn control over to the main loop event handler. My following rework show how you might do so:
import turtle
colors = ["red", "black"]
HOLE_SIZE = 2
turn = 0
is_winner = False
def draw_board():
pass
return (0, 0)
def dot(color):
turtle.color(color, color)
turtle.stamp()
def goto(x, y):
global turn, is_winner
# add code to determine if we have a winner
if not is_winner:
# Clears screen
turtle.clear()
turtle.penup()
# Draws empty board
centers = draw_board()
turtle.goto(x, y)
# Decides whose turn it is, change color appropriately
color = colors[turn % 2 == 0]
dot(color)
turn += 1
else:
pass
def start_game():
"""
When this function runs, sets up a new
game of Connect 4 against another person
"""
global turn, is_winner
turn = 1
is_winner = False
turtle.shape("circle")
turtle.shapesize(HOLE_SIZE)
# Gets coordinates of click
turtle.onscreenclick(goto)
start_game()
turtle.mainloop()
Run it and you'll see the desired behavior you described.
I'm assuming that your using Turtle in python(hence the name.)
If that's the case, Here's a link to a helpful post: Turtle in python- Trying to get the turtle to move to the mouse click position and print its coordinates
I know, i know. I hate just link answers as much as the next guy. But The post I gave a link to can probably do a much better job of answering your question than I can.
~Mr.Python
Assuming you're using turtle as mentioned in your title:
>>> import turtle
>>> help(turtle.onscreenclick)
Help on function onscreenclick in module turtle:
onscreenclick(fun, btn=1, add=None)
Bind fun to mouse-click event on canvas.
Arguments:
fun -- a function with two arguments, the coordinates of the
clicked point on the canvas.
num -- the number of the mouse-button, defaults to 1
Example (for a TurtleScreen instance named screen)
>>> onclick(goto)
>>> # Subsequently clicking into the TurtleScreen will
>>> # make the turtle move to the clicked point.
>>> onclick(None)
That means that your callback function, which you have apparently named goto, will take two parameters, an X and Y location.
import turtle
def goto(x, y):
print('Moving to {}, {}'.format(x,y))
turtle.goto(x, y)
turtle.onscreenclick(goto)
turtle.goto(0,0)
Each click that you make will move the turtle to a different position. Note that turtle already has an event loop - you don't need one of your own. Just respond to the clicks.
basically, you need to add an 'x' and 'y' parameter for the onclick and onscreenclick functions. You don't need to use them, they're just dummy params. After filling those out the clicks will work no problem:
window = turtle.Screen()
This function uses the x, y params because i'm saving the clicks in order to specify an area to fill with turtles
def on_left_click_save_coordinates(x, y):
global counter, Fill_COORS1, Fill_COORS2
counter += 1
print(x, y)
if counter == 1:
Fill_COORS1 = (x, y)
elif counter == 2:
Fill_COORS2 = (x, y)
counter = 0
This one doesn't use the x,y params because they are dummies, this one is used to allow multiple options, one of which exits, another tells the turtle to fill in the specified area saved in clicks above.
def on_right_click_open_options(x, y):
global going
last_color = options(window, filler, Fill_COORS1, Fill_COORS2, LAST_BLOCK_USED)
if type(Last_COLOR) == type(bool):
going = True
window.onscreenclick(on_click, btn=1)
window.onscreenclick(open_options, btn=3)
This is an example of a snippet of my code. hope this helps.
btn 3 refers to the right click
btn 1 is the default and isn't necessary to specify and refers to left click
btn 2 is the scroll wheel click, not scroll.
and sorry if this isn't formatted the best, it's my first time posting to stackoverflow. Hope it helps nonetheless
For those who don't know, repeat delay is the slight pause between, when holding down a key, the letter first appearing and it repeating. This might be a useful feature when typing, however when you start to write games, it becomes very annoying. An example is when you need to move a character; it will move a tiny bit, pause, and then start to move again. Tkinter code:
ball = canvas.create_rectangle(50, 50, 100, 100)
def move():
canvas.move(ball, 0, 3)
canvas.bind_all("<space>", move)
If space is pressed, the ball will move down 3 pixels, pause, and then start moving normally. I was wondering if there is any way to avoid that pause, for example a module that reads directly from the keyboard, and not the windows-processed keyboard. I know that it is possible to "cheat" by, for example, automatically running the function when you expect the delay to occur; sadly that is inaccurate and can result in choppy movement. Thanks in advance
Make a recursion loop for while it is pressed. Or that's how I do it, at least.
moving = False
def move():
global moving
moving = True
def stop_moving():
global moving
moving = False
def myloop():
global moving
if moving == True:
canvas.move(ball, 0, 3)
root.after(1, myloop)
root.bind('<space>', lambda e: move())
root.bind('<KeyRelease-space>', lambda e: stop_moving())
just make sure you call your loop once before root.mainloop()
#like this
root.after(1, myloop)
root.mainloop()