Trying to bind a function with argument to a button using lambda, but it keeps activating on initialization

31 Views Asked by At

I am making a python version of pong for a class, which is unimportant to the main menu issue I am having. My team and I have gotten buttons to be created using the following function:

def buttonCreate(buttonName, bindingFunction, canvasName, x, y, name):

    buttonName = tk.Button(canvasName.master, text=name,
                       command= bindingFunction,
                       height=2,
                       width=12,
                       font=("Bauhaus", 14),
                       fg="black",
                       activebackground="black",
                       relief="raised",
                       bd=10
                       )


    def onEnter(event):
        buttonName.config(bg='black', fg='green')  # Change background and foreground color when mouse enters

    def onLeave(event):
        buttonName.config(bg='SystemButtonFace', fg='black')

    buttonName.bind("<Enter>", onEnter)
    buttonName.bind("<Leave>", onLeave)
    canvasName.create_window(x, y, window=buttonName)
    

def buttonLambdaCreate(buttonName, bindingFunction, canvasName, x, y, name):

    buttonName = tk.Button(canvasName.master, text=name,
                       command= lambda: bindingFunction,
                       height=2,
                       width=12,
                       font=("Bauhaus", 14),
                       fg="black",
                       activebackground="black",
                       relief="raised",
                       bd=10
                       )


    def onEnter(event):
        buttonName.config(bg='black', fg='green')  # Change background and foreground color when mouse enters

    def onLeave(event):
        buttonName.config(bg='SystemButtonFace', fg='black')

    buttonName.bind("<Enter>", onEnter)
    buttonName.bind("<Leave>", onLeave)
    canvasName.create_window(x, y, window=buttonName)

I added the lambda function because we need to pass an argument to the buttons for the difficulty for singleplayer. Making all binding functions lambda seems to break the code further so I made a separate function. This is the code for the main menu:

from ast import Lambda
from tokenize import Single
import turtle
from turtle import Screen, Turtle
import tkinter as tk
from tkinter import messagebox
import buttonSetting
import singlePlayerPage

def draw_title(text):
    title_turtle = Turtle()
    title_turtle.hideturtle()
    title_turtle.penup()
    title_turtle.goto(0, 200)
    title_turtle.color("green")
    title_turtle.write(text, align="center", font=("Bauhaus 93", 50, "bold"))

def main_menu():
    screen.clear()
    canvas1 = screen.getcanvas()
    screen.bgcolor("black")
    draw_title("Main Menu")

    # def buttonCreate(nameText, bindingFunction, canvasName, x, y, textDisplay):
    buttonSetting.buttonCreate("button1", single_player, canvas1, 0, -110, "Single Player")
    buttonSetting.buttonCreate("button2", multiplayer, canvas1, 0, 0, "Multiplayer")
    buttonSetting.buttonCreate("button3", exit_game, canvas1, 0, 110, "Exit Game")



def single_player():
    screen.clear()
    canvas2 = screen.getcanvas()
    screen.bgcolor("black")
    draw_title("Single Player Mode")
    buttonSetting.buttonCreate("button1", singlePlayerPage.startEasy("Easy"), canvas2, 0, -110, "Easy")
    # buttonSetting.buttonCreate("button2", needs a function for normal, canvas2, 0, 0, "Normal")
    # buttonSetting.buttonCreate("button3", Needs a function for hard, canvas2, 0, 110, "Hard")
    #buttonSetting.buttonCreate("button4", main_menu, canvas2, 0, 210, "Main Menu")


def multiplayer():
    screen.clear()
    canvas3 = screen.getcanvas()
    screen.bgcolor("black")
    draw_title("Multiplayer Mode")
    # screen.update()
    buttonSetting.buttonCreate("button4", main_menu, canvas3, 0, 210, "Main Menu")
# THese are mocks levels of functions
# only made to test functionality of buttons
def hardLevel():
    screen.clear()
    # canvas = screen.getcanvas()
    screen.bgcolor("black")
    draw_title("Hard")

def normalLevel():
    screen.clear()
    # canvas = screen.getcanvas()
    screen.bgcolor("black")
    draw_title("Normal")

def easyLevel():
    screen.clear()
    # canvas = screen.getcanvas()
    screen.bgcolor("black")
    draw_title("Easy")


def exit_game():
    root = tk.Tk()
    root.withdraw()
    if messagebox.askokcancel("Exit", "Are you sure you want to quit?"):
        root.quit()


#
screen = Screen()
main_menu()
turtle.mainloop()

My issue here is that we could simply create multiple functions for each difficulty/mode, but that gives us a lot of extra work when we want to change bits and it is very inefficient. Passing 1 or 2 strings could bypass all of that. This, of course, is why I want to use lambda but it seems to break the buttons by making them activate when they are drawn on the screen. I suspect this might be because we are asking the program to run the function instead of binding it to a button to be called later. To be honest I just don't know a lot about python/Tkinter, and the forums I have seen only mention to just use command= lambda: to solve the launch on initialization issue. Any help would be greatly appreciated, thanks!

1

There are 1 best solutions below

0
acw1668 On

Note that the following line:

buttonSetting.buttonCreate("button1", singlePlayerPage.startEasy("Easy"), canvas2, 0, -110, "Easy")

will execute singlePlayerPage.startEasy("Easy") immediately and pass the result as the value of the argument bindingFunction of .buttonCreate().

You can use lambda to fix the issue:

buttonSetting.buttonCreate("button1", lambda: singlePlayerPage.startEasy("Easy"), canvas2, 0, -110, "Easy")

Or use functools.partial():

from functools import partial
...

buttonSetting.buttonCreate("button1", partial(singlePlayerPage.startEasy, "Easy"), canvas2, 0, -110, "Easy")