Changing variable value in tkinter loop doesn't update - python

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!

Related

Kinematic study application. Python and opencv

Im trying to develop a simple code using opencv and python. My idea is the following:
I have a video with a moving object(free falling or parabolic) and I've managed to separate the video in frames. What I need (and I'm a total newby in this aaaand have little time) is to extract coordinates of the object frame by frame. So the idea is to click on the moving object, and get the (x, y) coordinate and the number of frame and open the next frame so I can do the same. So basically something with clicks over the foto and extracting the data in a csv and showing the next frame. Thus I could study its movement through space with its velocity and acelerations and stuff.
Haven't written any code yet.
Thanks in advance.
Look at docs example with using mouse input in opencv w python:
mouse example - opencv
You can define callback reading the click coordinates:
def get_clic_point(event,x,y,flags,param):
if event == cv.EVENT_LBUTTONDBLCLK: # on left double click
print(x,y) # here goes Your sepcific code, using x, y as click coordinates
In main loop, You need to create window and supply it with callback method
cv.namedWindow('main_win')
cv.setMouseCallback('main_win',get_clic_point)
Then using window name (in this case 'main_win') as Your window handle, You can
show image, calling cv.imshow('main_win',img), where img is loaded image.
You can write simple loop like this:
cv.namedWindow('main_win')
cv.setMouseCallback('main_win',get_clic_point)
images = []
# read images to this list
# ...
i = 0
while(1):
cv.imshow('main_win',images[i])
k = cv.waitKey(1) & 0xFF
if k == ord('n'): # n for next
# change image
i = i + 1
i = i % len(images)
elif k == 27:
break
cv.destroyAllWindows()
then just substitiute call back with desired logic

How could I make a dash in a 2D pygame python program?

If the pygame program is just a basic entity you can move normally with arrow keys, how could i make it so that if space is pressed, based on the arrow key that was being held at the moment of pressing, a player dashes slightly to the specified direction? My idea is that when the space is pressed, program checks if other keys are being held down and based on that it rapidly increases the x and/or y coordinate and then stops at specific time, but I don't know how to make it stop as all of this is happening inside a main game loop. Any insight is highly appreciated.
You can use time.perf_counter.
So for using that function you will need to import module time
import time
And now you can store the value of time.perf_counter in a variable when space is pressed.
import time
jumping = False
start_time = 0
while True:
if ("space is pressed"): # Replace condition with what you want
jumping = True
start_time = time.perf_counter()
# Add your code here
if jumping:
# change of x, y and other jumping mechanism code here
if jumping and time.perf_counter() - start_time > 1: # Replace 1 with the amount you want this if condition to trigger, also add value in seconds
jumping = False
I'm not sure what your code is like, but this is how I'd go about it:
def dash(self):
keys = pygame.keys.get_pressed()
if keys[pygame.K_SPACE] and not self.dashing and self.jumping:
# ^^ Can be any key
if self.facing_right:
self.x += 20
if self.facing_left:
self.x -= 20
self.dashing = True
Make sure that when you hit the ground it does self.dashing = False else you will be able to dash forever.
Just put this function in the player class if you are doing it OOP.
Else if you are not using classes, put it anywhere in the file and take out all self..

How to use mouseclick to change a variable using OpenCV and Python?

I'm a noobie at programming and this question may sound easy and dumb but I really can not do it! The goal here is to change the color between green, blue and red everytime I click. I'm sorry if that's obvious, but I'm for hours stuck at this. That's the code I have, that prints everytime the same color. Now, I want to add something to change colors everytime I click.
import cv2
import numpy as np
def draw_circle(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img,(x,y),100,color=(0,255,0),thickness=10)
cv2.namedWindow(winname='my_drawing')
cv2.setMouseCallback('my_drawing', draw_circle)
img = np.zeros((512,512,3),np.int8)
while True:
cv2.imshow('my_drawing', img)
if cv2.waitKey(20) &0xFF == 27:
break
cv2.destroyAllWindows
I've tried a lot of stuff that I don't even know where to start, but I've tried creating a variable inside the function that each time it enters on the function, it sums, and depending on the value(using if) it goes to a different color, but the variable doesn't seem to change if it enters the loop again, then I've tried returning the variable as well. No success. Adding a Paramater. No Success as well. I believe it's such a simple thing that my head cannot think at this point.
Here's one way... make a list of the colours you want to cycle through. Have a global variable that you use as the index into the list to get the next colour. Each time you use it, add 1 to it and reduce it modulo the length of the list of colours. Colour the circle with value from the colour list as indexed by the index.
#!/usr/bin/env python3
import cv2
import numpy as np
index = 0
colours = [[255,0,0], [0,255,0], [0,0,255]]
def draw_circle(event,x,y,flags,param):
global index
if event == cv2.EVENT_LBUTTONDOWN:
cv2.circle(img,(x,y),100,color=colours[index],thickness=10)
index = (index+1) % len(colours)
cv2.namedWindow(winname='my_drawing')
cv2.setMouseCallback('my_drawing', draw_circle)
img = np.zeros((512,512,3),np.int8)
while True:
cv2.imshow('my_drawing', img)
if cv2.waitKey(20) &0xFF == 27:
break
cv2.destroyAllWindows

Loop for Drawing Lines

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()

Getting Turtle in Python to recognize click events

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

Categories