graphics.py and python - looking for help on button clicking - bound motion

227 views Asked by At

I am working on a couple ideas here:

  1. hovering over a button changes the colour (using cursor binding) - works fine
  2. when clicked, the button changes colour - issues with this

If the user is hovering over the button, the button changes colour. The first time it works, and then if the user click anywhere OUTSIDE of the button, the button changes colour. Any ideas?

from graphics import *
win = GraphWin("button", 500, 500)
win.master.attributes('-topmost', True)

def motion(event):
    x1, y1 = event.x, event.y
    if (x1 > 10 and x1 < 80) and (y1 > 20 and y1 < 80):
        rectangle1.setFill("red")        
        if win.getMouse():
            rectangle1.setFill("blue")
            print("Clicked")
    else:
        rectangle1.setFill("white")

win.bind('<Motion>', motion)

rectangle1 = Rectangle(Point(10, 20), Point(80, 80))
rectangle1.draw(win)
win.mainloop()

EDIT : I found a slight workaround, but it is doesn't feel like it is workable for a game or even menu. It works as long as the mouse is completely still when you click on it. As I said, I am sure there are ways to make this better so any help is appreciated.

from graphics import *
import time
win = GraphWin("buttons", 500, 500)
win.master.attributes('-topmost', True)

def motion(event):
    x1, y1 = event.x, event.y
    if (x1 > 10 and x1 < 80) and (y1 > 20 and y1 < 80):
        rectangle1.setFill("red")
    else:
        rectangle1.setFill("white")
    click = win.getMouse()
    if (click.getX()>10 and click.getX()<80) and (click.getY()>20 and click.getY()<80):
        rectangle1.setFill("blue")
        print("Clicked")

win.bind('<Motion>', motion)

rectangle1 = Rectangle(Point(10, 20), Point(80, 80))
rectangle1.draw(win)
win.mainloop()
1

There are 1 answers

0
furas On

In motion I would use use global variable

    global hovered

    hovered = (10 < x1 < 80) and (20 < y1 < 80)

And I would use <Button-1> to run function when mouse (left-button) was clicked. And it would check if hovered is True

from graphics import *

# --- functions ---

def motion(event):
    global hovered
    
    hovered = (10 < event.x < 80) and (20 < event.y < 80)
        
    if hovered:
        rectangle1.setFill("red")
        print("Hovered")
    else:
        rectangle1.setFill("white")
        print("UnHovered")
        
def click_left_button(event):
    if hovered:
        rectangle1.setFill("blue")
        print("Clicked Left")

def click_right_button(event):
    if hovered:
        rectangle1.setFill("black")
        print("Clicked Right")

# --- main ---

hovered = False

win = GraphWin("buttons", 500, 500)
#win.master.attributes('-topmost', True)

win.bind('<Motion>', motion)
win.bind('<Button-1>', click_left_button)
#win.bind('<Button-2>', click_middle_button)
win.bind('<Button-3>', click_right_button)

rectangle1 = Rectangle(Point(10, 20), Point(80, 80))
rectangle1.draw(win)

win.mainloop()

But I don't know if this resolve all problems. Because when you keep press button then it sets BLUE but if I move then change color to RED but it should keep this color until I release button. It may need to use <ButtonPress-1> and <ButtonRelease-1>


EDIT:

it seems this works like real button

from graphics import *

# --- functions ---

def motion(event):
    global previous 
    global hovered
    
    hovered = (10 < event.x < 80) and (20 < event.y < 80)
        
    if not clicked:
        if hovered:
            if not previous:
                rectangle1.setFill("red")
                print("Enter")
        else:
            if previous:
                rectangle1.setFill("white")
                print("Leave")
                
    previous = hovered
    
def press_left_button(event):
    global clicked

    if hovered:
        clicked = True
        rectangle1.setFill("blue")
        print("Pressed Left")

def release_left_button(event):
    global clicked

    if clicked:    
        clicked = False
        print("Released left")
    
    if hovered:
        rectangle1.setFill("red")
    else:
        rectangle1.setFill("white")
        
# --- main ---

previous = False
hovered = False
clicked = False

win = GraphWin("buttons", 500, 500)
#win.master.attributes('-topmost', True)

win.bind('<Motion>', motion)
win.bind('<ButtonPress-1>', press_left_button)
win.bind('<ButtonRelease-1>', release_left_button)

rectangle1 = Rectangle(Point(10, 20), Point(80, 80))
rectangle1.setFill("white")
rectangle1.draw(win)

win.mainloop()

EDIT:

If I put code in class Button then I can simply create many buttons

enter image description here

from graphics import *

# --- classes ---

class Button():
    
    def __init__(self, x, y, w, h):
        
        self.x1 = x
        self.y1 = y
        self.x2 = x + w
        self.y2 = y + w
        
        self.w = w
        self.h = h
        
        self.rectangle = Rectangle(Point(x, y), Point(x+w, y+h))
        self.rectangle.setFill("white")
    
        self.previous = False
        self.hovered = False
        self.clicked = False
        
    def draw(self, win):
        self.rectangle.draw(win)
        
    def on_motion(self, event):
        self.hovered = (self.x1 < event.x < self.x2) and (self.y1 < event.y < self.y2)
        
        if not self.clicked:
            if self.hovered:
                if not self.previous:
                    self.rectangle.setFill("red")
                    #print("Enter")
            else:
                if self.previous:
                    self.rectangle.setFill("white")
                    #print("Leave")
                
        self.previous = self.hovered

    def on_press_left_button(self, event):
        self.clicked
    
        if self.hovered:
            self.clicked = True
            self.rectangle.setFill("blue")
            #print("Pressed Left")
    
    def on_release_left_button(self, event):
        self.clicked
    
        if self.clicked:    
            self.clicked = False
            #print("Released left")
        
        if self.hovered:
            self.rectangle.setFill("red")
        else:
            self.rectangle.setFill("white")
            
# --- functions ---

def motion(event):
    for button in all_buttons:
        button.on_motion(event)
    
def press_left_button(event):    
    for button in all_buttons:
        button.on_press_left_button(event)
        
def release_left_button(event):    
    for button in all_buttons:
        button.on_release_left_button(event)

# --- main ---

win = GraphWin("buttons", 500, 500)
#win.master.attributes('-topmost', True)

win.bind('<Motion>', motion)
win.bind('<ButtonPress-1>', press_left_button)
win.bind('<ButtonRelease-1>', release_left_button)

all_buttons = []

for y in range(10, 400, 60):
    for x in range(10, 400, 60):
        button = Button(x, y, 50, 50)
        button.draw(win)
        all_buttons.append( button )

win.mainloop()