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
Related
The objective is to move alien1, atarts from 0,0 then moves all the way to the right, goes down and then all the way to the left, and then down.
from tkinter import *
import random
def enemigos():
global Enemigos #Enemigos downloads the image for alien1
n = random.randint(1,3)
if n == 1:
def movalien1():
alien1 = CanvasJuego.create_image(0,0, anchor = NW, image = Enemigos[0], tags= ('alien1'))
RIGHT1 = True
CoordsAlien1 = CanvasJuego.coords(alien1)
if (CoordsAlien1[0] < 1000 and RIGHT1==True):
CanvasJuego.coords(alien1, CoordsAlien1[0]+5, CoordsAlien1[1])
if ((CoordsAlien1[0]+5)==1000):
RIGHT1 = False
CanvasJuego.coords(alien1, CoordsAlien1[0], CoordsAlien1[1]+50)
elif (CoordsAlien1[0]>0 and RIGHT1==False):
CanvasJuego.coords(alien1, CoordsAlien1[0]-5, CoordsAlien1[1])
if ((CoordsAlien1[0]-5)==0):
RIGHT1 = True
CanvasJuego.coords(alien1, CoordsAlien1[0], CoordsAlien1[1]+50)
def rec():
movalien1()
root.after(20,rec)
root.after(20,movalien1())
Alien1 does appear at (0,0), but it won't move.
The problem is that you create a new "alien" every 20 milliseconds. You should be creating alien1 exactly once outside of movalien1. What is happening is that you create it at 0,0, then move it to 5.0. The alien is at 5,0. The next time through the loop you create a new alien at 0,0, and then move it to 5,0. You keep creating new aliens over and over and moving the new alien to 5,0.
Also, you can use the move method to move an item instead of adjusting its coordinates.
Finally, even though it doesn't actually matter in this code, you are calling after incorrectly here: root.after(20, movealien1()). It needs to be either root.after(20, movealien1) or just directly call movealien1() without using after.
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()
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 simply want to use the turtle method onscreenclick to find the coordinates of a mouse click. Currently, I have a grid on which I am playing Othello. I already have the algorithm to convert the raw coordinates to specific grid coordinates that can be interpreted by the game. I cannot seem to get the onscreenclick method working. On the docs, it says to use a 'fun' function with two arguments. I believe I have this, but it is not working. I am a beginner with python and turtle so any help would be appreciated :)
import turtle
xclick = 0
yclick = 0
def getcoordinates():
turtle.onscreenclick(modifyglobalvariables())
def modifyglobalvariables(rawx,rawy):
global xclick
global yclick
xclick = int(rawx//1)
yclick = int(rawy//1)
print(xclick)
print(yclick)
getcoordinates()
You got so close!
import turtle
xclick = 0
yclick = 0
def getcoordinates():
turtle.onscreenclick(modifyglobalvariables) # Here's the change!
def modifyglobalvariables(rawx,rawy):
global xclick
global yclick
xclick = int(rawx//1)
yclick = int(rawy//1)
print(xclick)
print(yclick)
getcoordinates()
Catch the change? Syntactically, remove the parentheses after modfiyglobalvariables. What you want is to pass the function, what you are doing is passing the output of the function.
If you ran the code, you would get an exception (TypeError) saying you haven't passed the correct arguments; that's because it's trying to actually call modifyglobalvariables. Reduced, what you wanted was
bind_to_mouseclick( my_function )
In which case, at each mouse click, my_function will be called. At that point, it may or may not have the correct arguments supplied. Instead you said
bind_to_mouseclick( my_function() )
Python evaluates my_function and binds the result of the call to the mouse click. If my_function happens to return a function, that's great (maybe what we intended). If it returns an integer or a string, no good. The key is the exception, as noted above; if the function had required no arguments, this may have been subtler to detect
It's actually alot easier to detect where you clicked it. Here's the code:
from turtle import *
mouseclickx = 0
mouseclicky = 0
def findcoords(x,y):
print(x)
print(y)
mouseclickx = x
mouseclicky = y
onscreenclick(findcoords,1)
EDIT:
That doesn't work... well idk but it should be something like that :/ if you want a thing so it goes where you click, its one line;onscreenclick(goto,1)
from turtle import Turtle, Screen
screen = Screen()
def get_mouse_click(x, y):
print(x, Y)
screen.onscreenclick(get_mouse_click)
screen.mailoop()
Currently, I know only one way to show strings using ncurses library, like as below:
import curses
stdscr = curses.initscr()
stdscr.addch(0,0,'x')
stdscr.getch()
But I've met a problem when I want to make a falling function of string.
import curses
import time
stdscr = curses.initscr()
y=1
def fall():
global y
stdscr.addstr(y,0,'x')
stdscr.move(y-1,0)
stdscr.clrtoeol()
y += 1
stdscr.getch()
while True:
time.sleep(0.2)
fall()
If I remove this getch() function, I can't see the ncurses screen. But if I put it in. I have to touch some key on my keyboard then the string could fall.
Is there a way I can make the string automatically falling without hit keyboard or mouse?
Refresh at the point you wants to reflect changes on your screen.
I am not rectifying but modifying my draw square code in previous answer, below my own code using curses library(added comments so that it can be helpful for someone new):
from curses import *
import random, time
def main(stdscr):
start_color() # call after initscr(), to use color, not needed with wrapper
stdscr.clear() # clear above line.
stdscr.addstr(1, 3, "Fig: RAINING", A_UNDERLINE|A_BOLD)
# init some color pairs:
init_pair(10, COLOR_WHITE, COLOR_WHITE) # BG color
init_pair(1, COLOR_RED, COLOR_WHITE)
init_pair(2, COLOR_BLUE, COLOR_WHITE)
init_pair(3, COLOR_YELLOW, COLOR_WHITE)
init_pair(4, COLOR_MAGENTA, COLOR_WHITE)
init_pair(5, COLOR_CYAN, COLOR_WHITE)
# First draw a white square as 'background'
bg = ' ' # background is blank
for x in range(3, 3 + 75): # horizontal c: x-axis
for y in range(4, 4 + 20): # vertical r: y-axis
stdscr.addstr(y, x, bg, color_pair(10))
stdscr.refresh() # refresh screen to reflect
stdscr.addstr(28, 0, 'Press Key to exit: ')
# Raining
drop = '#' # drop is #
while True: # runs infinitely
xl = random.sample(range(3, 3+75), 25) # generate 25 random x-positions
for y in range(5, 4 + 20): # vertical
for x in xl:
stdscr.addstr(y-1, x, bg, color_pair(10)) #clear drops #previous row
stdscr.addstr(y, x, drop, color_pair(random.randint(1, 5)))
stdscr.refresh() # refresh each time, # ^^ add drops at next row
time.sleep(0.5) #sleep for moving..
for x in xl: # clear last row, make blank
stdscr.addstr(23, x, ' ', color_pair(10))
stdscr.getkey() # it doesn't work in this code
wrapper(main) #Initialize curses and call another callable object, func,
Snap-sort of one iteration:
two iterations: http://s1.postimg.org/ehnvucp1p/rain.gif
You have to explicitly update the screen, either by calling the refresh() method on the window (stdscr in your example) or by calling curses.doupdate().
This is due to the fact that curses was written years ago, when terminal where pretty slow and it was really important to make modifications efficiently. With an explicit update you can first change the screen how you want and then update it in a single operation, instead of doing an update for every single operation.