Tkinter Button not registering click - python

I have the following tkinter button class
import tkinter as tk
class Button(GraphicsObject):
"""A button is a labeled rectangle in a window.
It is activated or deactivated with the activate()
and deactivate() methods. The clicked(p) method
returns true if the button is active and p is inside it."""
def __init__(self, p, width, height, label):
""" Creates a rectangular button, eg: qb = Button(myWin, Point(30,25), 20, 10, 'Quit') """
GraphicsObject.__init__(self, [])
self.anchor = p.clone()
w,h = width/2.0, height/2.0
self.x, self.y = p.getX(), p.getY()
self.xmax, self.xmin = self.x+w, self.x-w
self.ymax, self.ymin = self.y+h, self.y-h
p1 = Point(self.xmin, self.ymin)
p2 = Point(self.xmax, self.ymax)
self.width = width
self.height = height
self.rect = Rectangle(p1,p2)
self.label = label
self.fill = "white"
self.color = "black"
self.font = DEFAULT_CONFIG['font']
self.activate()
def _draw(self, canvas, options):
p = self.anchor
x,y = canvas.toScreen(p.x,p.y)
#frm = tk.Frame(canvas.master,height = self.height, width = self.width,)
# frm.pack_propagate(0)
#frm.pack()
self.button = tk.Button(canvas.master,
height = self.height,
width = self.width,
text = self.label,
bg = self.fill,
fg = self.color,
font=self.font)
self.button.place(x = self.x, y = self.y, height =self.height, width = self.width)
#self.setFill(self.fill)
self.button.focus_set()
#return canvas.create_window(x,y,window=self.button)
def clicked(self, p):
""" RETURNS true if button active and p is inside"""
return self.active and \
self.xmin <= p.getX() <= self.xmax and \
self.ymin <= p.getY() <= self.ymax
def getLabel(self):
"""RETURNS the label string of this button."""
return self.label.getText()
def setColor(self, color):
self.color = color
def setFill(self, color):
self.fill = color
def activate(self):
"""Sets this button to 'active'."""
self.color = 'black'
self.rect.setWidth(2)
self.active = 1
def deactivate(self):
"""Sets this button to 'inactive'."""
self.color = 'darkgrey'
self.rect.setWidth(1)
self.active = 0
I first decided to test it out by calling the method clicked in the following loop:
sign_in_button = Button(Point(237.5,300),80,40,'Sign In')
sign_in_button.draw(login_page)
click_point = login_page.getMouse()
clicked = sign_in_button.clicked(click_point)
print(clicked)
while(clicked == False):
click_point = login_page.getMouse()
clicked = sign_in_button.clicked(click_point)
print(clicked)
print("clicked == True")
When I run my program, the button appears correctly on the window and as long as I don't click inside the button it does register a clicked value of False. However, when I click on the button, nothing happens. Both print(clicked) and print(clicked == True) never run when clicked should have a value of True. Do I need to modify my class or did I just forget to include something when I try to create the Button?

Related

Unable to detect mouse clicks for randomly generated buttons

I created a program that allows the user to randomly generate buttons on a grid, but I cannot detect if the user presses them or not. Here is what I have so far:
from graphics import *
from time import *
from random import *
class Button:
def __init__(self, win, center, width, height, label):
w,h = width/2.0, height/2.0
x,y = center.getX(), center.getY()
self.xmax, self.xmin = x+w, x-w
self.ymax, self.ymin = y+h, y-h
p1 = Point(self.xmin, self.ymin)
p2 = Point(self.xmax, self.ymax)
self.rect = Rectangle(p1,p2)
self.rect.setFill("blue")
self.rect.draw(win)
self.label = Text(center, label)
self.label.draw(win)
self.label.setSize(8)
self.activate()
def clicked(self, p):
#print("clicked", p.getX(), p.getY(), self.xmin, self.xmax)
return (self.active and
self.xmin <= p.getX() <= self.xmax and
self.ymin <= p.getY() <= self.ymax)
def getLabel(self):
return self.label.getText()
def activate(self):
self.label.setFill("black")
self.rect.setWidth(2)
self.active = True
def deactivate(self):
self.label.setFill("darkgray")
self.rect.setWidth(1)
self.active = False
def setColor(self, color):
self.rect.setFill(color)
class Grid:
def __init__(self, win, startX, startY, numCols, numRows, squareWidth, squareHeight):
self.ButtonMatrix = []
self.numCols = numCols
self.numRows = numRows
for y in range(startY, numRows):
buttonList = []
for x in range(startX,numCols):
label = str(x) + str(y)
buttonList.append(Button(win,Point(x,y), squareWidth, squareHeight, label))
self.ButtonMatrix.append(buttonList)
sleep(0.03)
def getClickPos(self, clickPt):
for y in range(self.numRows):
for x in range(self.numCols):
if self.ButtonMatrix[y][x].clicked(clickPt):
return y,x
def GenerateRandomColor(self, X,Y, color):
self.ButtonMatrix[X][Y].setColor(color)
#def insideBox(x,y):
def setSquareColor(self,r,c,color):
self.ButtonMatrix[r][c].setColor(color)
def setRowColor(self,rowNum,color):
for c in range(15):
self.ButtonMatrix[rowNum][c].setColor(color)
def main():
SIZE = 15
#application window
win = GraphWin("Memory Game", 600, 600)
win.setBackground(color_rgb(45,59,57))
win.setCoords(-3, -3, SIZE + 2, SIZE + 2)
grid = Grid(win, 0, 1, SIZE, SIZE, 1, 1)
quitButton = Button(win, Point(SIZE, SIZE+1), 2, 1, "Quit")
for i in range(10):
X = randrange(13)
Y = randrange(13)
grid.GenerateRandomColor(X,Y, "white")
Coords = X,Y
sleep(0.1)
print(Coords) #checking to see if each button coord will be printed out
pt = win.getMouse()
if grid.getClickPos(pt) == Coords:
print("pressed random button")
else:
print("did not press a random button")
if __name__ == "__main__":
main()

How to put a button image borderless/tansparent

I'm trying to put my button borderless so we can we see the progressbar on the background
I tried all the forum but coundt put the border of the button round or make something like the second picture
this is the part of the code for the button :
bg3 = Image.open("blanc.png")
resized_bg03 = bg3.resize((20, 20),Image.ANTIALIAS)
new_bg03 = ImageTk.PhotoImage(resized_bg03)
s = Button(Fenetre,image=new_bg03,borderwidth=0,highlightthickness=0)
s.place(x=600, y=10,height = 20 , width = 20)
I would like to have something more like this with a circle and no border :
can someone help me :)
Try this:
import tkinter as tk
# Kindly plagiarised from: https://stackoverflow.com/a/17985217/11106801
def _create_circle(self, x, y, r, **kwargs):
return self.create_oval(x-r, y-r, x+r, y+r, **kwargs)
tk.Canvas.create_circle = _create_circle
# Please suggest a better name for the class
class ProgressBar(tk.Canvas):
def __init__(self, height=20, radius=6, width=400, circle_colour="black",
colour="red", bar_width=6, bd=0, highlightthickness=0,
anti_bar_colour="white", **kwargs):
super().__init__(height=height, width=width, bd=bd,
highlightthickness=highlightthickness, **kwargs)
self.radius = radius
self.height = height
self.width = width
self.rectangle = None
self.circle_colour = circle_colour
self.colour = colour
self.bar_width = bar_width
self.circle = None
self.button_1_down = False
self.create_anti_bar(anti_bar_colour)
self.progress = 0
super().bind("<Enter>", self.show_circle)
super().bind("<Leave>", self.hide_circle)
super().bind("<Button-1>", self.mouse_click)
super().bind("<ButtonRelease-1>", self.mouse_release)
super().bind("<B1-Motion>", self.mouse_motion)
def create_anti_bar(self, colour):
start_y = (self.height - self.bar_width)/2
end_y = self.height - start_y
start_x = self.radius
end_x = self.width - self.radius
# Change it to >= if you don't what the bar to appear when the
# progress is at 0
if start_x > end_x:
return None
super().create_rectangle(start_x, start_y, end_x, end_y,fill=colour,
outline=colour)
def mouse_click(self, event):
self.button_1_down = True
self.progress = (event.x - self.radius)/(self.width - 2*self.radius)
def mouse_release(self, event=None):
self.button_1_down = False
def mouse_motion(self, event):
if self.button_1_down:
self.mouse_click(event)
def hide_circle(self, event=None):
if self.circle is not None:
super().delete(self.circle)
self.circle = None
def show_circle(self, event=None):
# Try removing the circle if we can
self.hide_circle()
x = (self.width - 2*self.radius)*self._progress + self.radius
self.circle = super().create_circle(x, self.height//2, self.radius,
fill=self.circle_colour,
outline=self.circle_colour)
def update_bar(self):
# Try removing the progress bar
if self.rectangle is not None:
super().delete(self.rectangle)
start_y = (self.height - self.bar_width)/2
end_y = self.height - start_y
start_x = self.radius
end_x = (self.width - 2*self.radius)*self._progress + self.radius
# Change it to >= if you don't what the bar to appear when the
# progress is at 0
if start_x > end_x:
return None
self.rectangle = super().create_rectangle(start_x, start_y, end_x,
end_y, fill=self.colour,
outline=self.colour)
#property
def progress(self):
return self._progress
#progress.setter
def progress(self, new_value):
# Check if the new_value is in the correct range
if new_value < 0:
new_value = 0
elif new_value > 1:
new_value = 1
# Update self._progress
self._progress = new_value
# Update the progress bar
self.update_bar()
# If the circle was shown update it
if self.circle is not None:
self.show_circle()
if __name__ == "__main__":
root = tk.Tk()
pbar = ProgressBar()
pbar.pack()
pbar.progress = 0
# keep incrementing the progress until the end then stop
def increment_progress():
pbar.progress += 0.001
if pbar.progress >= 1:
return None
pbar.after(10, increment_progress)
increment_progress()
root.mainloop()
Tell me if you don't get what any of the methods do. It is too much code to properly annotate

Python - Tkinter event_generate not detected by bind

I have two classes (one called 'Point' one called 'PointConector'), both of which inherits 'Frame' (Im not sure If I need to but was seeing if it helped)
When I move the Point (mouse Enter/Leave) and drag the point around, I want to fire off an event for 'PointConnector' (which is a line thats joins the twp points) to pick up the event and redraw the line with the new position of the point.
If I use the event_generate from the canvas object and bind also from the canvas object then the event is picked up, but the object that is passed is the canvas object, not the Point object.
If I fire the event_generate from the Point object (which is inherited from Frame) then the event is ignored
I need the bind event to have the Widget field set to the 'Point' that generated the event. ie I need to have a handle to the point that fired the event. So the Field 'Widget' in the event should be the Point object that is being handled, not the canvas.
from tkinter import Tk, Canvas,Frame
class pointConnector(Frame):
point1 = None
point2 = None
connector = None
def __init__(self,canvas, point1,point2):
super(pointConnector,self).__init__(canvas)
self.point1 = point1
self.point2 = point2
self.canvas = canvas
p1 = self.canvas.translatePoint((self.point1.x,self.point1.y))
p2 = self.canvas.translatePoint((self.point2.x,self.point2.y))
self.connector = canvas.create_line(p1[0], p1[1], p2[0], p2[1],fill='red',state="hidden", width =2)
self.canvas.bind("<<foo>>",self.dummy)
def dummy(self,event):
print("Virtual Event Data: {}".format(event))
def show(self):
self.canvas.itemconfig(self.connector,state="normal")
class screen(Canvas):
width =0
height=0
cY = 0
cX = 0
items =[]
dragItem = None
mouseOverItem = None
def setScreenSize(self,width,height):
self.height = height
self.width = width
self.cX = int(width/2)
self.cY = int(height/2)
def __init__(self,root, unit,width,height):
super(screen,self).__init__(root,width=width,height=height)
self.root = root
self.width = width
self.height = height
self.setScreenSize(width,height)
self.tag_bind('DRAG_OBJECT','<ButtonPress-1>',self.onStartDrag)
self.tag_bind('DRAG_OBJECT','<ButtonRelease-1>',self.onEndDrag)
self.tag_bind('DRAG_OBJECT','<B1-Motion>',self.onDrag)
self.bind("<Configure>", self.configure)
def addObject(self,object):
self.items.append(object)
def findObject(self,tags):
for item in self.items:
if str(item.id) in tags:
return item
return None
def onStartDrag(self,event):
self.dragItem= self.find_closest(event.x,event.y)
wt = self.gettags(self.dragItem)
self.dragItem = self.findObject(wt)
def onEndDrag(self,event):
self.dragItem = None
def onDrag(self,event):
self.dragItem.move(event.x,event.y)
def draw(self):
# Draw array of items
for o in self.items:
o.show()
def translateX(self,x):
return (x + self.cX)
def translateY(self,y):
return (y + self.cY)
def translatePoint(self,pointxy):
return (self.translateX(pointxy[0]),self.translateY(pointxy[1]))
class point(Frame):
state = "normal"
x : int
y : int
def setPoint(self,x,y):
self.x = x
self.y = y
def __init__(self,canvas,x,y,radius,ObjectName):
super(point,self).__init__(canvas)
self.x = x
self.y = y
self.radius = radius
self.canvas = canvas
self.ObjectName= ObjectName
self.id = None
self.create_point()
def create_point(self):
x = self.canvas.translateX(self.x)
y = self.canvas.translateY(self.y)
self.circle = self.canvas.create_oval(x-self.radius,y-self.radius,x+self.radius,y+self.radius,state=self.state,fill='yellow')
self.text = self.canvas.create_text(x,y,text=self.ObjectName,state=self.state )
self.canvas.itemconfig(self.circle, tags=({"type":"POINT", "name":self.ObjectName},"DRAG_OBJECT"))
self.canvas.itemconfig(self.text, tags=({"type":"POINT", "name":self.ObjectName},"DRAG_OBJECT"))
self.id = {"type":"POINT", "name":self.ObjectName}
def show(self):
newX = self.canvas.translateX(self.x)
newY = self.canvas.translateY(self.y)
self.canvas.itemconfig(self.circle,state='normal')
self.canvas.coords(self.circle,(newX-self.radius,newY-self.radius,newX + self.radius, newY + self.radius))
self.canvas.coords(self.text,(newX,newY))
self.canvas.itemconfig(self.text,state='normal')
self.canvas.tag_raise(self.circle)
self.canvas.tag_raise(self.text)
def hide(self):
self.canvas.itemconfig(self.circle,state='hidden')
self.canvas.itemconfig(self.text,state='hidden')
def move (self,x,y):
delta_x = x- (self.x + self.canvas.cX)
delta_y = y- (self.y + self.canvas.cY)
self.canvas.move(self.circle,delta_x,delta_y)
self.canvas.move(self.text,delta_x,delta_y)
self.x = x - self.canvas.cX
self.y = y - self.canvas.cY
## If I use the canvas object (that is passed in) then the Point object picks up the event, but then I cant tell which 'Point' is begin dragged
#self.canvas.event_generate("<<foo>>",x=self.x,y=self.y,when='now')
## If I generate the event from the Point Object then the Event is lost
self.event_generate("<<foo>>",x=self.x,y=self.y,when='tail')
self.update()
# Start of program
print("Start --->")
tk = Tk()
mainScreen = screen(tk,20,700,700)
mainScreen.pack(fill="both",expand=1)
# Add Point A
pointA = point(mainScreen,0,0,10,"A",)
mainScreen.addObject(pointA)
# Add Point B
pointB = point(mainScreen,20,20,10,"B")
mainScreen.addObject(pointB)
LineA = pointConnector(mainScreen,pointA,pointB)
mainScreen.addObject(LineA)
mainScreen.draw()
tk.mainloop()

The item configure method didn't work in Tkinter

I tried to use the Tkinter library for my small project in python. I create a 500 by 500 square with 10000 small square in it.
And I want each small square turns black when user click on it. Can someone please tell me why, I would really appreciate it. Here is the graphics code:
from Tkinter import *
from button import *
class AppFrame(Frame):
def __init__(self):
self.root = Tk()
self.root.geometry = ("1000x1000")
self.f = Frame(self.root, relief = 'sunken', width = 600, height = 600)
self.w = Canvas(self.f,width = 505, height =505)
##get the x, y value whenever the user make a mouse click
self.w.bind("<Button-1>", self.xy)
self.bolist = []
for k in range(1,101):
for i in range(1, 101):
button = Buttons(self.w, i * 5, k * 5, i * 5 + 5, k * 5 + 5)
self.bolist.append(button)
self.f.grid(column =0, columnspan = 4)
self.w.grid(column = 0)
self.root.mainloop()
def xy (self, event):
self.x, self.y = event.x, event.y
print (self.x, self.y)
##check each button if it's clicked
for hb in self.bolist:
if hb.clicked(self.x, self.y):
print ("hurry")
hb.activate()
And
##button.py
from Tkinter import *
class Buttons:
def __init__(self,canvas,bx,by,tx,ty):
self.canvas = canvas
self.rec = canvas.create_rectangle((bx,by,tx,ty),fill = "lightgray",
activefill= 'black', outline = 'lightgray')
self.xmin = bx
self.xmax = tx
self.ymin = by
self.ymax = ty
##print (bx, by, tx, ty)
def clicked(self, px, py):
return (self.active and self.xmin <= px <= self.xmax and
self.ymin <= py <= self.ymax)
def activate(self):
self.canvas.itemconfigure(slef.rec, fill = 'black')
self.active = True
The problem is that you don't initialize the active attribute, so it doesn't exist until the cell becomes active. To fix that, add self.active = False inside the __init__ method of Buttons.
You also have a typo in this line (notice you use slef rather than self):
self.canvas.itemconfigure(slef.rec, fill = 'black')
Instead of a global binding on the canvas, it would be more efficient to set a binding on each individual rectangle. You can then use the binding to pass the instance of the Buttons class to the callback. This way you don't have to iterate over several thousand widgets looking for the one that was clicked on.
To do this, use the tag_bind method of the canvas. You can make it so that your main program passes in a reference to a function to call when the rectangle is clicked, then the binding can call that method and pass it a reference to itself.
For example:
class Buttons:
def __init__(self,canvas,bx,by,tx,ty, callback):
...
self.rec = canvas.create_rectangle(...)
self.canvas.tag_bind(self.rec, "<1>",
lambda event: callback(self))
...
class AppFrame(Frame):
def __init__(...):
...
button = Buttons(..., self.callback)
...
def callback(self, b):
b.activate()
Here, I looked at your code, debugged it, and made some adjustments. It works now.
Just keep both the scripts in one folder and run your AppFrame script (the second one in this answer)
##button.py
from Tkinter import *
class Buttons:
def __init__(self,canvas,bx,by,tx,ty):
self.canvas = canvas
self.rec = canvas.create_rectangle((bx,by,tx,ty),fill = "lightgray", activefill= 'black', outline = 'lightgray')
self.xmin = bx
self.xmax = tx
self.ymin = by
self.ymax = ty
##print (bx, by, tx, ty)
def clicked(self, px, py):
return (self.xmin <= px <= self.xmax and
self.ymin <= py <= self.ymax)
def activate(self):
self.canvas.itemconfigure(self.rec, fill = 'black')
AND
from Tkinter import *
from button import *
class AppFrame(Frame):
def __init__(self):
self.root = Tk()
self.root.geometry = ("1000x1000")
self.f = Frame(self.root, relief = 'sunken', width = 600, height = 600)
self.w = Canvas(self.f,width = 505, height =505)
##get the x, y value whenever the user make a mouse click
self.w.bind("<Button-1>", self.xy)
self.bolist = []
for k in range(1,101):
for i in range(1, 101):
button = Buttons(self.w, i * 5, k * 5, i * 5 + 5, k * 5 + 5)
self.bolist.append(button)
self.f.grid(column =0, columnspan = 4)
self.w.grid(column = 0)
self.root.mainloop()
def xy (self, event):
self.x, self.y = event.x, event.y
print (self.x, self.y)
##check each button if it's clicked
for hb in self.bolist:
if hb.clicked(self.x, self.y):
print ("hurry")
hb.activate()
newApp = AppFrame()

Quick debugging question [Python, pygame]

It's still an incomplete program, but for some reason the value of the textbox doesn't increase when it should... Why is this??
When the Pizza sprite overlaps with the Pan sprite, the score in the textbox is supposed to increase in value by 10. Why does this not occur?
Thanks!
'''
Created on Jul 1, 2011
#author: ******* Louis
'''
#Watch me do.
from livewires import games, color
import random
games.init (screen_width = 640, screen_height = 480, fps = 50)
#Pizza Class
class Pizza (games.Sprite):
pizzaimage = games.load_image ("pizza.bmp", transparent = True)
def __init__(self, x = random.randrange(640), y = 90, dy = 4):
super (Pizza, self).__init__(x = x,
y = y,
image = Pizza.pizzaimage,
dy = dy)
def handle_caught (self):
self.destroy()
class Pan (games.Sprite):
panimage = games.load_image ("pan.bmp", transparent = True)
def __init__ (self, x = games.mouse.x, y = games.mouse.y):
super (Pan, self).__init__(x = x,
y = y,
image = Pan.panimage)
self.score = 0
self.textbox = games.Text (value = str(self.score),
size = 20,
color = color.black,
x = 550,
y = 50)
games.screen.add(self.textbox)
def update (self): #WWWWOW There is actually an *update* method
self.x = games.mouse.x
self.y = games.mouse.y
if self.left < 0:
self.left = 0
if self.right >640:
self.right = 640
if self.top < 0:
self.top = 0
if self.bottom > 480:
self.bottom = 480
self.check_collision()
def check_collision (self):
for Pizza in self.overlapping_sprites:
self.score = self.score + 10
Pizza.handle_caught()
#main
def main():
wallbackground = games.load_image ("wall.jpg", transparent = False)
games.screen.background = wallbackground
games.screen.add(Pizza())
games.screen.add(Pan())
games.mouse.is_visible = False
games.screen.event_grab = True
games.screen.mainloop()
main()
The textbox takes a value that is a string. When you create the textbox, you create a string from the current value of score, and set the text to that string. No lasting connection between score and textbox is made.
The textbox probably has a method available to update its text; call that method with the value str(self.score) after you increment the score.

Categories