Python Tkinter preventing button command from executing - python

I'm trying to get the last few lines of the following code to have when one of the original 6 buttons is pressed to call the appropriate function to rename the buttons. I've tried changing the command line to buttons[0].command = Pistols(). I've also tried using a if loop with a variable such as x == 1 to determine that if the button is pressed x with then be 1 and the for loop will call the function Pistols, but with no success. However the button automatically calls the function and renames the first button to ".44 Pistol" rather than what it should be "Pistols". I wan't the command to only be executed and call the function when pressed. I know that tkinter will automatically look to the function being called and run it's code. How can I either delay this or go about this in another way to have the functions code only execute when pressed. Thanks in advance!
from tkinter import *
buttons = []
clm = [1,2,1,2,1,2]
rw = [1,1,2,2,3,3]
btnmain_list = ['Pistol','Rifle','Assult Rifle','Submachine Gun','Heavy Weapon','Plasma Weapons']
btnpistol_list = ['.44 Pistol', '10mm Pistol', 'Pipe Bolt-Action Pistol','Flare Gun', 'Pipe Pistol', 'Pipe Revolver']
btnrifle_list = []
btnasrifle_list = []
btnsubgun_list = []
btnheavy_list = []
btnplasma_list = []
ms = Tk()
ms.title('Fallout 4 weapon mods and needed materials')
ms.geometry('450x400')
placement = Frame(ms)
placement.grid()
class Guns:
def Pistols ():
buttons[0] = Button(placement,height = '5',width = '20', text = btnpistol_list[0])
buttons[0].grid(column = clm[0], row = rw[0])
def Rifles ():
x = 0
def AssultRifles ():
x = 0
def SubmachineGuns ():
x = 0
def HeavyWeapons ():
x = 0
def PlasmaWeapons ():
x = 0
for i in range (6):
b = Button(placement,height = '5',width = '20', text = btnmain_list[i])
b.grid(column = clm[i], row = rw[i])
buttons.append(b)
buttons[0].command = Pistols()

I've found a solution by changing the class to this:
class Guns:
global counter
counter = 0
def pistolCycle():
global counter
buttons[0].config(text=btnpistol_list[counter])
if counter == len(btnpistol_list)-1:
counter=0
counter = counter+1
def Pistols ():
buttons[0] = Button(placement, height = '5',width = '20', text="Pistols", command = lambda: Guns.pistolCycle() )
buttons[0].grid(column = clm[0], row = rw[0])
def Rifles ():
x = 0
def AssultRifles ():
x = 0
def SubmachineGuns ():
x = 0
def HeavyWeapons ():
x = 0
def PlasmaWeapons ():
x = 0
for i in range (6):
b = Button(placement,height = '5',width = '20', text = btnmain_list[i])
b.grid(column = clm[i], row = rw[i])
buttons.append(b)
Pistols()
So, here's a breakdown of what happens:
Once your buttons are defined, the Pistol function is called, which adds all the features to your Pistol button, including changing the text, and adding the function it will call when pressed.
When the button is pressed, it calls to pistolCycle. What pistol cycle does, is takes the "counter" value, and changes the text of the button to the item in the list which is associated to it. EG, when the counter is 0, .44 Pistol is displayed.
The counter increases by one, each time pistolCycle is called, meaning the next time it's called, it will display the next item in the list.
Now, using global variables can get messy. I've given you the basic framework, so you may be able to use your own logic to get the variable "counter" to pass into pistolCycle each time (EG, pistolCycle(counter))
You will need to make a separate counter and cycle function in order for all the buttons to work.
I hope this helped!!
PS: The if statement in the pistolCycle function means that it wont try and get an item when it doesn't exist in the list.

Related

Return value of reference function in button.when_pressed

I'm trying to have the return value of a function stored in a variable. The function is called (by a reference) when a Raspberry Pi gpiozero button is pressed.
from gpiozero import Button
from signal import pause
def fcn(a):
print(a)
b = a + 1
print(b)
return b
btn = Button(26)
i = 1
btn.when_activated = lambda: fcn(i) # returns 2
pause()
When the button is pressed it will print 1 and 2 as expected.
But how can I store the return value of fcn into i so it can increment at each button press?
Edit: The reason I've started with the when_activated reference is that is in my main script I've got multiple buttons and multiple functions which can be pressed in any order and should pass around variables to each other. Something like this:
def fcn(a):
b = a + 1
return b
def fcn2(b):
c = b + 10
return c
btn1 = Button(26)
btn2 = Button(19)
i = 1
btn1.when_activated = lambda: fcn1(i) # returns i + 1
btn2.when_activated = lambda: fcn2(i) # returns i + 10
Actually I'm also passing around datetime objects.
Assign to i the value of fcn(i) every time the button is pressed:
def fcn(a):
print(a)
b = a + 1
print(b)
return b
i = 1
while 1:
input() # Button press
i=fcn(i) # Incrementation
Just a fix up. You can use global in your function to assign to i. see the example code below
from gpiozero import Button
from signal import pause
def fcn():
global i
i = i + 1
check()
return b #No need of returning
def check():
print i
btn = Button(26)
i = 1
btn.when_activated = lambda: fcn() # returns 2
pause()
global tells interpreter that variable, in this case "i" is relating to the variable of outer scope. hen you can to anything with that variable... function check() just prints the value for evaluating, so you can remove it.

How do I run an if loop constantly?

I want to constantly run an if loop outside of my functions, but I can't figure out how to do it. I made a mock up of the code::
import tkinter
from tkinter import *
code = Tk()
T = 1
X = 0
def printx():
global X
print(X);
def teez():
global T
T = 0;
def teeo():
global T
T = 1;
if T == 1:
X = 5
else:
X = 6
button1 = Button(code, text = "Print X", command = printx)
button1.pack()
button2 = Button(code, text = "T = 0", command = teez)
button2.pack()
button2 = Button(code, text = "T = 1", command = teeo)
button2.pack()
code.mainloop()
I want the if loop to change X based on what my functions do, but the if loop seems to not run.
There is no such thing as an "if loop". if is a conditional statement that executes one of two branches of code, once only. The looping statements at this level are for and while; see your favorite Python tutorials to become familiar with their usage.
Running a constant monitor loop is the wrong control flow for this. Rather, you need only to check when the button is pressed. Put the functionality inside your press-handling routines:
def teez():
global T, X
T = 0
X = 6
def teeo():
global T, X
T = 1
X = 5
I strongly question setting global variables within the code. Instead, consider making an object with attributes T and X, and setting those as needed, passing the object to the routines that have to manipulate them.
the if structure is not a loop, but a conditional statement. If you want to do something iteratively, try with while True:

Python - returning from a While Loop after its end

I am trying to code a menu routine for an alarm-clock in python, which displays the relevant information on a 7-segment display according to some inputs via pushbuttons.
I managed to create a loop which displays the current time, and whe the button "debMenu" is clicked 3 menu options are displayed. This works well only for the first time untill the 3rd. menu option is reached. When I push the button again the routine does not work - so the function "main_menu" is not called again.
What I am doing wrong...? Thanks !!
stop_loop = [False]
def main_menu(stop_loop, n=[0]):
stop_loop[0] = True
debSelect = DebouncedSwitch(butSelect, cbButSelect, "butSelect")
menuList = ['HO ', 'AL ', 'UP ']
if n[0] < 3:
display.scroll(menuList[n[0]], 200) #display menu on 7-seg display
display.show(menuList[n[0]])
n[0] += 1
elif n[0] == 3:
n=[0]
stop_loop[0] = False
main()
def main():
stop_loop[0] = False
debMenu = DebouncedSwitch(butMenu, main_menu, stop_loop)
while not stop_loop[0]: # display current time on 7-seg display
curTime = rtc.datetime()
display.numbers(curTime.hour, curTime.minute, False)
time.sleep_ms(500)
display.numbers(curTime.hour, curTime.minute)
time.sleep_ms(500)
main()
I guess the default value mutable variable is the one killing you. Check here: http://docs.python-guide.org/en/latest/writing/gotchas/#mutable-default-arguments.
I don't even understand why you need a list variable in this case, why not just n=0 without having to use a list?.
maybe make a new global variable or class to encapsulate both state-related variables (stop_loop and n). Like:
loop = dict(stop=False, n=0)
now you pass this instead of stop_loop to main_menu.
Or use a class that encapsulates a circular array for menu like:
class LoopMenu:
""" circular array menu list with pause/continue/next"""
def __init__(self, menu):
self.menu = menu
self.stop = False
self.n = 0
def next(self):
menu = self.menu[self.n]
self.n += 1
if self.n > len(self.menu):
self.n = 0
return menu
def pause(self):
self.stop = True
def play(self):
self.stop = False
then you main_menu method could be:
my_menu_loop = LoopMenu(['HO ', 'AL ', 'UP '])
def main_menu(menu_loop):
menu_loop.pause()
debSelect = DebouncedSwitch(butSelect, cbButSelect, "butSelect")
menu = menu_loop.next()
display.scroll(menu, 200) #display menu on 7-seg display
display.show(menu)
main()
def main():
my_menu_loop.play()
debMenu = DebouncedSwitch(butMenu, main_menu, my_menu_loop)
while not loop_menu.stop: # display current time on 7-seg display
curTime = rtc.datetime()
display.numbers(curTime.hour, curTime.minute, False)
time.sleep_ms(500)
display.numbers(curTime.hour, curTime.minute)
time.sleep_ms(500)
main()

Passing variable from one function to another in same class?

I'm trying to create a button that changes colors on click.
After digging around in an old Python book I haven't looked at in years, I've gotten to the point where I make the the button, but I can't figure out how to pass i into the second function so it increments and is then is reset to 0.
I suppose I could just increment I on click in the first function, but now I'm annoyed and want to figure it out.
Instead of self.change_color I tried change_color(i). That threw an error. Same with trying self.change_color(i).
I'm not sure what to do at this point.
import tkinter
class joeGUI:
def __init__(self):
i = 0
colorArray = ['blue','DarkGreen','red','yellow']
self.main_window = tkinter.Tk()
self.color_button = tkinter.Button(self.main_window,
text = 'Click to Change Color',
command = self.change_color,
bg = colorArray[i])
self.color_button.pack()
tkinter.mainloop()
def change_color(self):
if (count < 3):
count += 1
else:
count = 0
return count;
joe_gui = joeGUI()
Store i as a class attribute (self.i = 0) and change the references of count to self.i.

When clearing another class' attribute in another class why would it not work?

I am writing a function to load a previously saved game in chess using Pygame. I have six classes: main2, GUI2, navigation2, calc, board and pieces. I store the attribute that stores where each chess piece is on the board class like so:
class board:
def __init__(self,main):
self.main = main
self.pieces = self.main.pieces
self.prv_pos = []
self.dict = self.pieces.dict
A = ["bRook","bKnight","bBishop","bQueen","bKing","bBishop","bKnight","bRook"]
B = ["bPawn","bPawn","bPawn","bPawn","bPawn","bPawn","bPawn","bPawn"]
C = [0,0,0,0,0,0,0,"wKing"]
D = [0,0,0,0,0,0,0,0]
E = [0,0,0,0,"wQueen",0,0,0]
F = [0,0,0,0,0,0,0,0]
G = ["wPawn","wPawn","wPawn","wPawn","wPawn","wPawn","wPawn","wPawn"]
H = ["wRook","wKnight","wBishop","wQueen",0,"wBishop","wKnight","wRook"]
self.piece_pos= [A,B,C,D,E,F,G,H]
I also have a class called main that is passed an instance on each class possible thus all my objects interact with each other through the main class. Thus in my navigation class that hosts the functions to save and load the game I wrote this:
class navigation:
def __init__(self,GUI):
self.GUI = GUI
self.main = self.GUI.main
self.piece_pos = self.GUI.main.board.piece_pos
self.GUI.draw_button("save",self.GUI.SQ_DIM*8,self.GUI.SQ_DIM)
self.GUI.draw_button("load",self.GUI.SQ_DIM*8,self.GUI.SQ_DIM*3)
self.GUI.draw_button("exit",self.GUI.SQ_DIM*8,self.GUI.SQ_DIM*5)
def game_save(self):
file = open("Save file.txt","w")
for line in self.piece_pos:
for item in line:
file.write(str(item)+",")
file.write("\n")
file.close()
def game_load(self): #WIP
self.piece_pos = []
self.GUI.draw_board()
As you may notice that the game_load function is pretty empty; that is because I wanted to check that in the self.piece_pos = [] line that the attribute is actually cleared throughout the classes. But when I call the self.GUI.draw_board() function which just draws the current board positions which are stored in piece_pos in the class board the board's appearance in the GUI is the same. I expect an error message in the python shell telling me there is no self.piece_pos[i][j] but it seems to me that the attribute hasn't changed whatsoever.
The function for draw_board() is stored in the class 'GUI'.
def draw_board(self):
X = 0
Y = 0
Ycounter = False
sqcounter = False
#Draw the board
print("GUI update")
for i in range(0,8):
for j in range(0,8):
print()
print("Piece at:")
print(i,j)
print(self.main.piece_at(i,j))
pg.draw.rect(self.window, self.sq_colour[i][j],(X,Y,self.SQ_DIM,self.SQ_DIM),0)
if self.main.piece_at(i,j) == 0:
print("square empty")
pass
else:
print("square occupied")
self.window.blit(self.dict[self.main.piece_at(i,j)],(X,Y))
#Change of X coord
if X >(self.SQ_DIM*6):
X = 0
Ycounter = True
sqcounter = True
else:
X +=(self.SQ_DIM)
#Change of Y coord
if Ycounter == True:
Y +=self.SQ_DIM
Ycounter = False
else:
pass
So I have come to the conclusions that I am not understanding something about how to globalize the piece_pos attribute. But I don't know how to solve this conundrum? Any ideas why?
This is because in game_load(), self.piece_pos refers to navigation.piece_pos, while draw_board() uses the object referenced by GUI.main.board.piece_pos.
You're effectively doing this
a = [5]
b = a
print(a, b) # prints [5] [5]
# By reassigning `b` you only change what it refers to,
# it doesn't affect what it used to refer to before.
b = []
print(a, b) # prints [5] []
# Instead, if you make an *in-place* modification, then the underlying
# object will indeed change, as both names refer to the same object.
a = b = [5]
b.append(3)
print(a, b) # prints [5, 3] [5, 3]
If you really want to make a change, it should be
def game_load(self): #WIP
self.main.board.piece_pos = []
self.GUI.draw_board()
Since the navigation class is a wrapper of GUI, it might be best to get rid of self.piece_pos and use function calls to get/set the piece positions, e.g.,
def get_piece_pos(self):
return self.main.board.piece_pos
def set_piece_pos(self, new_pos):
self.main.board.piece_pos = new_pos
Overall though, I don't see the point of this class. The save/load/exit buttons belong to the GUI, as should the respective save/load functions. I would just integrate this functionality in the GUI class, as this is the object the user interacts with.

Categories