Applying the getMouse() function to one part of the window - python

I am attempting to apply the getMouse() function to a specific part of the window, instead of the whole thing. I need the block that is clicked in to change color if it is 'rect1'. However if any other block is clicked, nothing should happen. I have attached the portion of my code which I feel relates to this in case anyone can be of any assistance.
#draw the grid
for j in range (6):
for i in range(6):
sq_i = Rectangle(Point(20 + (40*i), 20 + (40*j)),
Point(60 + (40*i),60 + (40*j)))
sq_i.draw(window)
sq_i.setFill('white')
sq_i.setOutline('grey')
#wait for a click
window.getMouse ()
#turn the alotted region red
rect1 = Rectangle(Point(20 + (40*1), 20 + (40*1)),
Point(60 + (40*1), 60 + (40*1)))
rect1.setOutline('black')
rect1.draw(window)
rect1.setFill('brown')
#if the mouse is clicked in rect1, change the block color to black
while window.getMouse() in rect1:
rect1.setFill('black')

First off, you need to understand what window.getMouse() in rect1 does. Python's in operator works by turning a in b into the method call b.__contains__(a). Unfortunately, the Rectangle class doesn't have a __contains__ method. This your immediate problem.
So, you need to use a different test. I suggest using Python's chained comparison operators to do the bounds checking yourself (there doesn't seem to be any library support for this in the graphics module):
mouse = window.getMouse()
if rect1.p1.x < mouse.x < rect1.p2.x and rect1.p1.y < mouse.y < rect1.p2.y
rect1.setFill("black")
Note that I've changed the while loop for an if statement. If you want to repeatedly test mouse clicks until one hits the rectangle, you probably want to loop on while True and then break if the tested condition is true:
while True:
mouse = window.getMouse()
if rect1.p1.x < mouse.x < rect1.p2.x and rect1.p1.y < mouse.y < rect1.p2.y
rect1.setFill("black")
break

Related

Optimising pygame

For my project in AH Computing I'm recreating my version of Nidhogg. Everything runs smoothly until the blood starts spraying. Im not too sure how i can make the code more efficient as i am still fairly new to python.
This is the class for the the spraying blood:
class bloodmaker():
def __init__(self,xv,yv,colour,x,y):
self.gravity = 2
self.air_resistance = 0.25
self.xv = xv
self.yv = yv
self.x = x
self.y = y
self.pastx = 0
self.pasty = 0
self.colour = colour
def move(self):
if self.y < 400:
self.pastx = self.x
self.pasty = self.y
#so that it doesnt curve backwards
if self.xv > 0:
self.xv -= self.air_resistance
self.yv += self.gravity
self.x += self.xv
self.y += self.yv
#so that the drawn line doesnt go over the edge
if self.y > 400:
self.y = 400
if self.colour is "o":
py.draw.line(screen, (255, 165, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
py.draw.line(screen, (255, 255, 0), (self.pastx-backgroundx, self.pasty), (self.x-backgroundx, self.y),5)
else:
global bloodgrid
try:
#the bloodgrid are squares 5 pixels wide, covering the bottom section, so we we divide by 5 to find where to put the blood
bloodgrid[int(self.x/5)].insert(0,self.colour)
except:
pass
#deleting this object as it is no longer required
return True
[Here is an image of the blood spraying][1]
(excuse the incomplete sprite)
[1]: https://i.stack.imgur.com/hXiAa.png
Underneath there is a floor of blood that works using an array of stacks, which is added above code when it the blood reaches the floor.
bloodgrid = [[] for x in range(512)]
Here is the code for destroying the flying blood object and blitzing the blood floor to the screen.
def blood():
for i in range(len(bloodarray)):
killyourself = bloodarray[i].move()
if killyourself is True:
kill.append(i)
#appends to a kill list as if i popped here the list would get shorter while the for loop stays the same giving an out of index error
for i in range(len(kill)):
bloodarray.pop(kill[0]-i)
kill.pop(0)
#printing the entire bloodgrid
for i in range(512):
for ii in range(len(bloodgrid[i])):
try:
if bloodgrid[i][ii] is "o":
py.draw.rect(screen, (255, 165, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
else:
py.draw.rect(screen, (255, 255, 0), ((i*5)-backgroundx, ii*5+400, 5, 5))
except:
pass
I don't think it is possible to only update parts of the screen as the camera moves about and the blood on the floor moves too.
As more blood accumulates on the floor the game framerate starts to drop and it gets especially choppy when the blood sprays. Is there any ways I could make this more efficient? I don't want to hand in a choppy game but I really like how the blood looks. Thanks.
A couple of points that are hopefully helpful.
(This isn't directly related to speeding up your code, but...) Try to get away from using global variables. Just pass them into your functions. Several of the ones you posted are difficult to figure out because you are accessing variables that are outside the scope of the function.
First reason things are bogging down here: You are using insert() to insert new elements at the start of a list, and you are doing that inside of a loop where you call the move() function. Inserting anywhere into a list (except the end) is horribly slow for reasons beyond the scope of this post. You should consider putting a deque from collections in there, which can be inserted front or back efficiently. If that is daunting, you could also "do a little math" and insert at the end of your list and draw it backwards by counting down from the last element, which is also efficient.
Also in the same notion, as mentioned in the comments, poping things out of the middle of lists or arrays is also horribly slow. Figure out a different way or look at a different data structure. It is tough to give advice on this point because bloodarray is not defined in your code.
Basically: things are bogging down because you are choosing the wrong data structures. tweak a couple of them and you'll get better results.

pyautogui.moveTo() failing sometimes and I have no idea why

I included the whole loop, but the part I'm talking about is the third paragraph. I have this loop, where it clicks on something, and then it moves the mouse back to wherever it was, to not interrupt the user while this thing is running. However, the pyautogui.MoveTo() call seems to be getting skipped sometimes?
while unlim or intFTC > 0:
# switch to program, do thing, and switch back to current window
current_win = gw.getActiveWindow().title
win = gw.getWindowsWithTitle('World of Warcraft')[0]
win.activate()
pyautogui.press(input_key)
win = gw.getWindowsWithTitle(current_win)[0]
win.activate()
# find and click thing
bx, by = thing_finder(x, y, x1, x2, y1, y2)
time.sleep(random.uniform(0.2, 0.6))
current_x, current_y = pyautogui.position()
current_win = gw.getActiveWindow().title
pyautogui.moveTo(bx, by)
pyautogui.rightClick()
# send mouse back to original position
while pyautogui.position() != (current_x, current_y):
pyautogui.moveTo(current_x, current_y)
print('::', pyautogui.position())
# return to og window
win = gw.getWindowsWithTitle(current_win)[0]
win.activate()
window.refresh()
stop()
time.sleep(random.uniform(1.5, 2))
runs += 1
So in the third chunk commented "send mouse back to original position," it doesn't always work. The current_x, and current_y are always correct, but it won't move the mouse to those coordinates. I thought the while loop would fix it, but it still doesn't always work which boggles my mind, because how does it get out of the while loop without moving the mouse? Thank you for reading.
Is it simply a case of not enough pause time? I have the time between pyautogui calls set to 0.001, but I figured it would slow down if it had to, not skip commands.
The loop does not make sense, you should send the command pyautogui.moveTo(current_x, current_y) and it should work. That condition can lead to some errors.

Python - get raw mouse input

I want to use Python to read raw data from the mouse (e.g. left signal, right signal) independent of the mouse pointer. I don't want to read the position of the cursor, I want to read the raw data, akin to how a game would. How can I do that? I can't find any libraries that support it.
I'm using Windows.
Edit: To clarify, when I said "left signal and right signal", I meant mouse movement, not mouse clicks,
Edit 2: I believe the terminology is that I want the "mouse delta." This is how people did it in autohotkey - https://www.autohotkey.com/boards/viewtopic.php?t=10159 - but I want it in Python.
Edit 3: I should mention that this is in a game that constantly resets pointer position, so I can't use the difference in pointer position. That's why I want a lower level API.
Get the mouse position and compare two events. If the x-axis value increases, the mouse moved right, if the x-axis value decreases, the mouse moved left:
from pynput.mouse import Listener
last_position = None
def on_move(x, y):
global last_position
if last_position:
if x > last_position:
print('mouse moved right')
elif x < last_position:
print('mouse moved left')
last_position = x
with Listener(on_move=on_move) as listener:
listener.join()
You can always compare the position constantly that would have easily given you a delta. Any lower level API you will have to code a mouse driver yourself, which is what game developers do to prevent people from scripting, which isn't what python made for.
import pyautogui
import time
while True:
prev_x, prev_y = pyautogui.position()
time.sleep(0.3)
curr_x, curr_y = pyautogui.position()
if (curr_x - prev_x) > 0:
print("move right")
else:
print("move left")

Python/Pygame.. Trouble w/ classes

Hey guys I have some issues while I was making my game.... I want to move my character while the image changes every "step" that it looks like an animation...
I haven been working on this for hours but the only thing I have done so far is to move the char but all images are drawn at once while moving
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
currentimageleft = 1
gamedisplay.blit(background, (0,0))
if currentimageleft == 1:
gamedisplay.blit(temp1, (template_x, template_y), (g1x, g1y, box, box))
template_x -= moveit
currentimageleft += 1
if currentimageleft == 2:
gamedisplay.blit(temp1, (template_x, template_y), (g2x, g2y, box, box))
template_x -= moveit
currentimageleft += 1
if currentimageleft == 3:
gamedisplay.blit(temp1, (template_x, template_y), (g3x, g3y, box, box))
template_x -= moveit
currentimageleft += 1
if currentimageleft == 4:
gamedisplay.blit(temp1, (template_x, template_y), (g4x, g4y, box, box))
template_x -= moveit
currentimageleft = 1
pygame.display.update()
Use elif for the subsequent comparisons, so that only one is done per iteration.
Since you are incrementing currentimgeleft in each of your if blocks, it then triggers the next if block down the line. You only want one to trigger each frame, so switch the if to elif for all but the first one and then you'll have only one triggering each time the K_LEFT button is pressed on the keyboard. Like so
if currentimageleft == 1:
# code
elif currentimageleft == 2:
# code
...
However, notice that you are setting currentimageleft each time you enter the bigger if statement for the keypress. This will ensure that you always end up using the very first sprite of your animation. You may want to associate currentimageleft with a class variable by declaring it in __init__ to be something like
def __init__(self, ...):
# other code, including possible parent constructors
self.currentleftimage = 1
# more code
You would then access your variable with self.currentimageleft instead of currentimageleft. This would then allow you to use else or elif clauses on your button press detection (the outer if in your code snippet) to reset the self.currentleftimage variable back to 1 allowing you to track motion that way instead.
You may also find this other answer on the equivalent form of switch in Python interesting, though several calls to elif clauses is also perfectly normal and may lead to more legible code in many cases.

Single mouse click within While loop (PsychoPy)

I am using PsychoPy and I would like to print the position of my mouse when it has been clicked.
The actual printing of the position needs to be placed inside a while loop. Using the code below, when I click I get more than one output lines, which print the same positions. I would like to have only one output printing for each click.
This is the code I am using:
#!/usr/bin/env python2
from psychopy import visual, core, event
from pyglet.gl import *
width = 600
height = 600
myWin = visual.Window([width,height], color='white',units='pix',monitor='testMonitor')
#This will set the windows units (pixels) to GL units
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glOrtho(0, width, 0, height, -1, 1)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glEnable(GL_BLEND)
glBlendFunc(GL_ZERO, GL_SRC_COLOR)
myMouse = event.Mouse() # will use myWin by default
while True:
#Triangle left
glColor3f(1.0, 0.0, 0.0)
glBegin(GL_TRIANGLES)
glVertex3f(150, 550, 1)
glVertex3f(50, 350, 1)
glVertex3f(250, 350, -1)
glEnd()
if myMouse.getPressed()[0]:
myMouse.clickReset()
print myMouse.getPos()
myWin.flip()
core.quit()
Is there something wrong I am doing? Should the 'frame rate' of the while loop be changed?
I've not used the module, but it seems like mouse events are thrown for mouse raises as well.
You'll need to
Store the mouse state for next time
Each iteration, test whether the mouse state for button 0 has gone up → down
The mouse state is returned by myMouse.getPressed.
So something like:
oldMouseIsDown = False
while True:
...
mouseIsDown = myMouse.getPressed()[0]
myMouse.clickReset()
if mouseIsDown and not oldMouseIsDown:
print myMouse.getPos()
oldMouseIsDown = mouseIsDown
Veedrac's answer is correct. Your code is using the typical PsychoPy pattern of checking for events once every time the window is redrawn. This will typically be happening at least at 60 Hz. So unless you manage to press the mouse button for less than 16.7 ms (or less for a faster screen), you will detect it multiple times as being pressed, as each time you check on successive window redraws, the mouse button remains down. Even though it was pushed only once, the duration of the push is not instantaneous.
As Veedrac suggests, you therefore need to maintain the previous state of the mouse button in a variable so that you can choose to only print the position once. Mouseup events are not relevant here: you are purely testing for whether the button is currently pressed.
I had a very similar problem and fixed it in a slightly different way from the accepted answer. The advantage of this one is that you can set how long you want to 'desensitize' the mouse after a click (i.e. to prevent longer clicks from triggering your if loop multiple times). Depending on your mouse or the user's click release speed you can change minFramesAfterClick:
minFramesAfterClick = 10 # to prevent re-entering the if loop too early
myMouse.clickReset()
timeAfterClick = 0
while True:
timeAfterClick += 1
if myMouse.getPressed()[0] and timeAfterClick >= minFramesAfterClick:
print myMouse.getPos()
myMouse.clickReset()
timeAfterClick = 0
myWin.flip()
By the way, the reason why the OP couldn't get Veedrac's answer work is because line oldMouseIsDown = mouseIsDown should be placed it inside the if loop rather than after it (not enough reputation to comment there).

Categories

Resources