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:
Related
Still pretty new to Python, so my apologies in advance...
I'm trying to use a button to move thru a List one item at a time.
It works the first time the button is clicked and moves to the second item in the List, but subsequent clicks keep returning the same values
from tkinter import *
window=Tk()
window.title('nextArrItem')
window.geometry("300x200+10+10")
options = ["H9Iu49E6Mxs", "YuWZNV4BkkY", "mBf6kJIbXLg", "Hz-xbM6jaRY"]
print("options[0] = " + options[0])
curItemText = options[0]
nextItemText = options[1]
curItem = 0
print('curItemText = '+curItemText)
print('nextItemText = '+nextItemText)
def nextArrItem(curItem=curItem+1):
print("str(curItem) = "+str(curItem))
try:
curItemText = options[curItem]
nextItemText = options[curItem+1]
print('curItemText = '+curItemText)
print('nextItemText = '+nextItemText)
curItem = curItem + 1
except:
print("End of Array Reached")
nextButton = Button(window, text="Next Item", command=nextArrItem)
nextButton.place(x=130, y=110)
window.mainloop()
When the Window opens initially, these values are returned:
options[0] = H9Iu49E6Mxs
curItemText = H9Iu49E6Mxs
nextItemText = YuWZNV4BkkY
The first click returns the following:
str(curItem) = 1
curItemText = YuWZNV4BkkY
nextItemText = mBf6kJIbXLg
Subsequent clicks keep returning the same values, so it only advances the first time and I'm not sure how to fix it. Although it probably doesn't look like it, this is the culmination of a lot of work just to get it to this point but I'm not sure where to go from here. I have the feeling the solution is going to be a true Homer Simpson "D'oh!" moment but I've steered this boat into shallow waters and need someone to help me from running aground...
Thanks in advance!
Paul
You need to increment the parameter each time to the next highest value. Currently your code just feeds the nextArrItem function the same value each time.
You could also try something to put the curItem inside a mutable data type so that it can be updated from within the scope of the function call like this:
...
options = ["H9Iu49E6Mxs", "YuWZNV4BkkY", "mBf6kJIbXLg", "Hz-xbM6jaRY"]
curItem = [0]
...
def nextArrItem(label=label):
try:
option = options[curItem[0]]
print(option)
label["text"] = option # updates label on each call
curItem[0] += 1 # increments index for option text
except IndexError:
print("End of Array Reached")
...
nextButton = Button(window, text="Next Item", command=nextArrItem)
...
Another way of doing it would be to bind the curItem variable to the window itself like this:
from tkinter import *
window=Tk()
window.curItem = 0
window.title('nextArrItem')
window.geometry("300x200+10+10")
options = ["H9Iu49E6Mxs", "YuWZNV4BkkY", "mBf6kJIbXLg", "Hz-xbM6jaRY"]
label = Label(window, text=options[window.curItem])
label.place(x=130, y=50)
def nextArrItem(label=label):
try:
option = options[window.curItem]
print(option)
label["text"] = option # updates label on each call
window.curItem += 1 # increments index for option text
except IndexError:
print("End of Array Reached")
nextButton = Button(window, text="Next Item", command=nextArrItem)
nextButton.place(x=130, y=110)
window.mainloop()
The issue you have is that curItem is both a global variable and a local variable in your callback function. You only ever update the local variable, so the change doesn't persist.
Here's how you're currently setting up the local variable, as an argument with a default value:
def nextArrItem(curItem=curItem+1):
The default value comes from the global variable, but it is only evaluated once, at the time the function is defined. It does not keep checking the global value, nor do changes to the local variable in the function change the global value either.
There's a better way. You can use a global statement to make it so that your function's code can directly read and write the global variable, so that there's only one curItem that everything is accessing the same way.
def nextArrItem():
global curItem
# the rest can be the same
I tried to make a Clicker and I used an infinite loop, so I would raise my Variable every second. But every time I use the Button, my program crashes.
Do you have any advice how I prevent that, because I have no idea what is really happening.
import time
from tkinter import *
class Clicker :
#updates the Label
def AK_CLabel(self):
self.ClickerLabel.configure(text="Du hast " + str(self.Clicks))
#Generates Clicks
def Klicken(self):
self.Clicks += 1
self.AK_CLabel()
#raises price of Helping Elf and raises the clicks per second
def HElf(self) :
if(self.Clicks >= self.priceHElf) :
self.Clicks -= self.priceHElf
self.priceHElf = self.priceHElf * 1.2
self.Elfs += 1
self.Elfhilft()
self.AK_CLabel()
#Should make the Clicks go up by the amount of Elfs, but if I use the Button the Programm shuts down
def Elfhilft(self):
while (not time.sleep(5)):
self.Clicks = self.Bitcoins1 + self.Elfs
time.sleep(1)
def __init__(self, master):
self.master = master
self.master.title = "Der Klicker"
self.Elfs = 0
self.priceHElf = 30
self.Clicks = 30
#Buttons and Label
self.DerKnopf = Button(text = "Clicks", command = self.Klicken)
self.ClickerLabel = Label(text = "You have " +str(self.Clicks))
self.HelferElf = Button(text = "A helping Fairy", command = self.HElf)
self.DerKnopf.pack()
self.ClickerLabel.pack()
self.HelferElf.pack()
root = Tk()
my_gui = Clicker(root)
root.mainloop()
Firstly, in your example bitcoins1 is undeclared. I assume this is just a variable name you forgot to change before posting, so I renamed it to clicks in order to replicate your issue.
Second, you have your Elfhilft() function using sleep(), which is causing issues with your Tkinter app. Tkinter uses its own loop system to handle real-time stuff, and sleep will cause that loop to stall in most cases. I suggest you use an implementation of after (How to create a timer using tkinter?) in order to replicate the autoclicker-esque function I assume you're trying to implement. As an example:
def autoclick(self):
self.clicks = self.clicks + self.Elfs
#In main app / __init__()
root.after(1000, self.autoclick) # updates auto-clicks every second
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.
I'm trying to make a GUI that display lots of checkbuttons, i create them from a list; make a dictionary form the list and assign each checkbutton a variable from the dictionary so i can check it's state later.
Problem is that all the checkbuttons are displayed in an 'alternate' state, even if i set the variable to either 0 or 1, i've also tried changing states, but nothing seems to help.
y = 0
for x in get_dir_names(r'D:\SKL\test\win10'):
drv_check[x] = Variable()
drv_check[x].set(0)
center_window(150, 500, top_child)
drv = ttk.Checkbutton(child_frame, text=x, variable=drv_check[x])
drv.grid(row=y, column=0, sticky=W)
y += 1
for reference
def get_dir_names(dir_path):
"""Get names only of all directories from a given path (none recursive)"""
drv_list = [x for x in os.walk(dir_path).__next__()[1]]
drv_name = dict({})
for y in drv_list:
tmp_ver = dir_path + r'\\' + y
drv_name[y] = (os.walk(tmp_ver).__next__()[1]).pop()
return drv_name
Figured it out, I've made a "toggle all" button and it seemed to fix it, but it's weird that it didn't work before.
here's the function i used:
def toggle_all(*args):
while True:
if toggle_all_var.get() == '1':
for name in drv_check:
drv_check[name].set('1')
elif toggle_all_var.get() == '0':
for name in drv_check:
drv_check[name].set('0')
ttk.Checkbutton(drv_frame, text='Toggle all', variable=toggle_all_var).grid(row=y, column=0, sticky=W)
Also i run the function in a new thread.
I'm writing a program for a calculator but i'm experiencing a small problem.
Whenever I press one of the buttons, it increases numbers by 9 every time, even though it should be i (from the for loop).
Please can someone tell me why its always 9 please?
Code -
import tkinter
plus = True
numbers = 0
def main():
def numButton(i):
global numbers
if plus == False:
numbers-=i
else:
numbers+=i
def quitHandler():
root.destroy()
def entryHandler():
global numbers
numbers+=int(text.get())
text.set("")
def printHandler():
text2.set(numbers)
def restartHandler():
global numbers
root.destroy()
plus = True
numbers = 0
main()
def plusHandler():
global plus
plus = True
def minusHandler():
global plus
plus = False
root = tkinter.Tk()
frame = tkinter.Frame(root).pack(side=tkinter.TOP)
text = tkinter.IntVar()
text2 = tkinter.IntVar()
text.set("")
text2.set("")
tkinter.Entry(frame,bd =8,textvariable=text).pack()
tkinter.Button(frame,padx=8,pady=8,bd=8,text="Enter",command=entryHandler).pack()
tkinter.Button(frame,padx=8,pady=8,bd=8,text="Quit",command=quitHandler).pack(side=tkinter.RIGHT)
tkinter.Button(frame,padx=8,pady=8,bd=8,text="Restart",command=restartHandler).pack(side=tkinter.RIGHT)
tkinter.Button(frame,padx=8,pady=8,bd=8,text="Print",command=printHandler).pack(side=tkinter.LEFT)
tkinter.Entry(frame,bd =8,textvariable=text2).pack(side=tkinter.LEFT)
_padx = 16
_pady = 16
_bd = 8
for i in range (1,10):
tkinter.Button(frame, padx = _padx, pady = _pady, bd = _bd, text = str(i), command = lambda: numButton(i)).pack(side = tkinter.LEFT)
tkinter.Button(frame,padx=8,pady=8,bd=8,text="+",command=plusHandler).pack(side=tkinter.LEFT)
tkinter.Button(frame,padx=8,pady=8,bd=8,text="-",command=minusHandler).pack(side=tkinter.LEFT)
main()
Could someone also tell me how to put all of it inside the code thing on this site, I cant figure it out and the ways the site's help shows me isn't working ( or admin fix please ).
OK thanks guys, someone's emailed me the solution:
tkinter.Button(frame, padx = _padx, pady = _pady, bd = _bd, text = str(i), command = lambda i=i: numButton(i)).pack(side = tkinter.LEFT)
Had to add the i=i after the lamda.
Can someone explain to me what the i=i thing does please?
Thanks
command = lambda: numButton(i)
This doesn't look up the value of i at the time of the lambda's creation and insert it into the function. When the lambda is called, then it looks up i. i is always 9 by that time.
There are several ways to get around the problem, all focused on ensuring that i is looked up at command's creation time instead of execution time. The one I'd use is functools.partial, a tool designed to associate a function with arguments:
from functools import partial
...
command=partial(numButton, i)
You can also use a default argument, which is kind of kludgy:
command=lambda i=i: numButton(i)
Or write a factory function:
def closure_maker(i):
def closure():
numButton(i)
return closure
...
command=closure_maker(i)
The lambda functions used for the commands reference the variable i:
for i in range (1,10):
tkinter.Button(frame, padx = _padx, pady = _pady, bd = _bd, text = str(i),
command = lambda: numButton(i)).pack(side = tkinter.LEFT)
When the command is executed and the lambda function runs, it selects button number i. But at that time, when the lambda function is executed, i contains the value 9 (That's the value i ends up with after the for loop that created the buttons was complete.
To solve this issue, make sure the lambda functions don't all share the same global variable. One way would be to use a function which creates a new local scope:
def numCommand(x):
return (lambda: numButton(x))
for i in range (1,10):
tkinter.Button(frame, padx = _padx, pady = _pady, bd = _bd, text = str(i),
command = numCommand(i)).pack(side = tkinter.LEFT)
Here each lambda function refers to its own x variable from its numCommand() call.