array not saving input - python

hey back again with the same code, well edited so it works better. anyway trying to add the button input into the array and that works. what doesn't work is the fact every time i call the function do() the values reset due to them being local. i tried to fix this by making it global(within the class) using the self.store array. this didn't seem to fix the problem so if someone could help would be much appreciated here is the relevant code
def __init__(self,master):#is the master for the button widgets
self.count=0
self.store=["0"]
frame=Frame(master)
frame.pack()
self.addition = Button(frame, text="+", command=self.add)#when clicked sends a call back for a +
self.addition.pack()
self.subtraction = Button(frame, text="-", command=self.sub)#when clicked sends a call back for a -
self.subtraction.pack()
self.equate = Button(frame, text="=", command=self.equate)#when clicked sends a call back for a =
self.equate.pack()
self.one = Button(frame, text="1", command=self.one)#when clicked sends a call back for a -
self.one.pack()
def add(self):
self.do("+")
self.count=self.count+1
def sub(self):
self.do("-")
self.count=self.count+1
def equate(self):
self.do("=")
def one(self):
self.do("1")
self.count=self.count+1
def do(self, X):#will hopefully colaborate all of the inputs
cont, num = True, 0
strstore="3 + 8"#temporarily used to make sure the calculating works
self.store=["2","1","+","2","3","4"]#holds the numbers used to calculate.
for num in range(1):
if X == "=":
cont = False
self.store[self.count]=X
print(self.store[self.count])
print(self.store[:])#test code
if cont == False:
print(self.eval_binary_expr(*(strstore.split())))

self.store=["2","1","+","2","3","4"]
If you initialize it this way in the do function, self.store will be reset to ["2","1","+","2","3","4"] every time you call do(X)
Initialize it outside of the do function if you don't want it to be overwritten.
But if you want to add a value to a list, use append(), extend, or += operator:
self.store+=["2","1","+","2","3","4"]
(if you want it to be done only once, do it in the constructor, __init__ function!)
also,
self.store[self.count]=X
If you try to append to the END of the list self.store, you should just do:
self.store.append(X)
This way, you won't need to count anything, with the risk of forgetting an incrementation and replacing a value by X instead of appending X.
As said above, range(1), it's 0...
Let's do it another way:
def do(self, X):
cont, num = True, 0
liststore=['3', '+', '8']#splitting your string.
#initialize str.store elsewhere!
if X == "=":
cont = False
self.store.append("X")
print(self.store[-1])#getting the last
print(self.store)#the same as self.store[:]
if cont == False:
print(self.eval_binary_expr(*(liststore)))
Simpler, and probably better.
Last thing: in Python, you are usually working with lists (like self.store), not arrays (which come from the module array)

Related

crate memory game in python

I'm trying to make a memory game and I'm trying to figure out how to call the action hide_text so that the parameter will be the button I clicked.
from tkinter import *
from random import shuffle
root = Tk()
a = list(range(1, 19))
a = list(a) + list(a)
shuffle(a)
def game():
count = 0
for i in a:
if count == 0:
posisonx = 0
posisony = 0
if count == 6:
count = 0
posisonx = 0
posisony += 1
button = Button(root,text=f' {i} ',
command=lambda: hide_text())
button.grid(row=posisony, column=posisonx)
def hide_text(s):
s.config(text='')
posisonx += 1
count += 1
game()
root.mainloop()
There are two problems to solve here. One is that you can't reference a button until it has been created. The other is correctly capturing the reference once it has.
If you want to pass a command involving a reference to the button, you have to split the creation into two lines:
button = Button(root,text=f' {i} ')
button.configure(command=...)
You need to bind button to the lambda that you create. There are a couple of ways to do this. If you just do command=lambda: hide_text(button), all the commands will point to the last button you create in the loop.
One trick is to use the fact that default arguments are bound when the function is created:
button.configure(command=lamda s=button: hide_text(s))
You should take def hide_text outside the loop in this case.
Another way is to capture a closure using a nested function. Put this outside the loop:
def get_hide_text(button):
def hide_text():
button.config(text='')
return hide_text
Then, in the loop, do
button.configure(command=get_hide_text(button))

tkinter radiobutton not updating variable

--UPDATE:
I changed
variable=self.optionVal.get()
to
variable=self.optionVal
But nothing changed.Also I wonder why it automatically call self.selected while compiling?
----Original:
I'm trying to get familiar with radiobutton, but I don't think I understand how radiobutton works. Here's a brief code for demonstration:
self.optionVal = StringVar()
for text, val in OPTIONS:
print(text,val)
radioButton = Radiobutton(self,
text=text,
value=val,
variable=self.optionVal.get(),
command = self.selected())
radioButton.pack(anchor=W)
def selected(self):
print("this option is :"+self.optionVal.get())
In my opinion this should work like once I choose certain button, and it prints out "this option is *the value*", however now what it does is once compiled, it prints out everything, and the self.optionVal.get() is blankspace, as if value wasn't set to that variable.
I wonder what happens to my code,
Many thanks in advance.
AHA! I beleive I've figured it out. I had the exact same issue. make sure you are assigning a master to the IntVar like self.rbv=tk.IntVar(master) #or 'root' or whatever you are using):
import Tkinter as tk
import ttk
class My_GUI:
def __init__(self,master):
self.master=master
master.title("TestRadio")
self.rbv=tk.IntVar(master)#<--- HERE! notice I specify 'master'
self.rb1=tk.Radiobutton(master,text="Radio1",variable=self.rbv,value=0,indicatoron=False,command=self.onRadioChange)
self.rb1.pack(side='left')
self.rb2=tk.Radiobutton(master,text="Radio2",variable=self.rbv,value=1,indicatoron=False,command=self.onRadioChange)
self.rb2.pack(side='left')
self.rb3=tk.Radiobutton(master,text="Radio3",variable=self.rbv,value=2,indicatoron=False,command=self.onRadioChange)
self.rb3.pack(side='left')
def onRadioChange(self,event=None):
print self.rbv.get()
root=tk.Tk()
gui=My_GUI(root)
root.mainloop()
try running that, click the different buttons (they are radiobuttons but with indicatoron=False) and you will see it prints correctly changed values!
You're very close. Just take out the .get() from self.optionVal.get(). The Radiobutton constructor is expecting a traced variable, you're giving it the result of evaluating that variable instead.
You need to:
Remove the .get() from the variable=self.optionVal argument in the constructor the button. You want to pass the variable, not the evaluated value of the variable; and
Remove the parenthesis from command=self.selected() and use command=self.selected instead. The parenthesis says "call this function now and use the return value as the callback". Instead, you want to use the function itself as the callback. To better understand this, you need to study closures: a function can return a function (and, if that was the case, that would be used as your callback).
EDIT: A quick reminder, also: Python is not compiled, but interpreted. Your callback is being called while the script is being interpreted.
def view(interface):
choice = interface.v.get()
if choice == 0:
output = "0"
elif choice == 1:
output = "1"
elif choice == 2:
output = "2"
elif choice == 3:
output = "3"
else:
output = "Invalid selection"
return tk.messagebox.showinfo('PythonGuides', f'You Selected {output}.')
class Start:
def __init__(self):
self.root = tk.Tk()
self.root.geometry('500x500')
self.root.resizable(False, False)
self.root.title('find out the degree of severity')
self.v = tk.IntVar()
dolori_ossa = {"nessun dolore": 0,
"dolori articolari": 1,
"frattura composta": 2,
"frattura scomposta": 3}
for (txt, val) in dolori_ossa.items():
tk.Radiobutton(self.root,
text=txt,
variable=self.v,
value=val,
command=lambda:view(self)
).pack()

capturing tkinter checkbox input

I am running a script with tkinter that captures user input and then opens a second and possibly a third window based on the input. The issue I am having is capturing user input from the third and final window. Each window is divided up into it's own python class on execution.
Here is the code that calls the third window, which executes properly:
test_assign = TestAssign(mylist)
Here is the third window code:
class TestAssign:
def __init__(self, mylist):
self.mylist = mylist
self.selected_values = []
self.master = Tk()
for i in range(len(mylist)):
setattr(self, 'item'+mylist[i], IntVar())
ch = Checkbutton(master, text='item'+mylist[i], variable=getattr(self, 'item'+mylist[i])
ch.pack()
b = Button(master, text='Next', command=self.get_selected_values)
b.pack()
mainloop()
def get_selected_values(self):
for i in range(len(self.mylist)):
if getattr(self, 'item'+self.mylist[i]) == 1:
self.selected_values.append(self.mylist[i])
self.master.destroy()
Control then returns to the call point (at least I believe it does). Where I attempt to print the selected values:
test_assign = TestAssign(mylist)
while not test_assign.selected_values:
pass
print test_assign.selected_values
Everytime execution gets to the print statement it prints an empty list whether there are boxes checked or not. If I call dir(test_assign) for testing purposes, the checkbox attrs are there. Not sure why I am not able to capture it like this.
Can anyone see the flaw in my code?
Two things:
1)
ch = Checkbutton(master, text='item'+mylist[i], variable=getattr(self, 'item'+mylist[i])
and
b = Button(master, text='Next', command=self.get_selected_values)
I think master should be self.master (but honestly, that almost certainly just a copy/pasting error.)
2) The important one:
if getattr(self, 'item'+self.mylist[i]) == 1:
should be
if getattr(self, 'item'+self.mylist[i]).get() == 1:
(you need to call get on your IntVars to read the value.)

Pausing from within a tkinter function

I'm trying to create a couple of functions which do things in a sequential order. First they need to open a new window and display a label, then they need to wait for some seconds, then they need to call another function. However, I'm struggling to get the functions to wait, all the methods I've tried (.after, .sleep, .wait_visibility) seem to be ignored and it just skips to the next function call without pausing.
Here's what I have (sorry if it's messy, I'm new to python):
from tkinter import *
import time
root =Tk()
root.geometry('600x600')
def scale_screen(event = None):
global s_screen
s_screen = Toplevel(root)
s_screen.title('Residual Inhibition Tester')
s_screen.geometry('600x600')
s_screen.transient(root)
s_screen.bind('<Return>', sel)
global var
var = IntVar()
scale = Scale(s_screen, variable = var, orient = HORIZONTAL, length = 1000)
scale.focus_set()
scale.pack(anchor=CENTER)
button = Button(s_screen, text="Select", command=sel)
button.pack(anchor=CENTER)
def sel(event = None):
label = Label(s_screen)
selection = "Value = " + str(var.get())
label.config(text = selection)
interval_screen()
def interval_screen():
global i_screen
i_screen = Toplevel(root)
i_screen.geometry('600x600')
i_screen.transient(root)
i_label = Label(i_screen, text = "Please Wait")
i_label.pack(anchor = CENTER)
s_screen.destroy()
i_screen.after(3000, masker_screen)
#time.sleep(3)
#i_screen.after(300,i_label.configure(text="Playing New Masker Noise"))
#root.wait_visibility(window = i_screen)
def masker_screen():
global m_screen
m_screen = Toplevel(root)
m_screen.geometry('600x600')
m_screen.transient(root)
m_label = Label(m_screen, text = "Playing New Masker Noise").pack(anchor = CENTER)
m_screen.after(3000, lambda: scale_screen(event = None))
i_screen.destroy()
b1 = Button(root, command = scale_screen).pack(anchor=CENTER)
root.bind('<Return>', scale_screen)
root.mainloop()
In this example, the program will run but just skip the interval_screen entirely and just do the masker_screen. I'm also not averse to just using one screen and using the .configure methods to change the label text if that's easier.
Thanks!
Without seeing all the ways you tried it, it's impossible to know what you did wrong. In general you should never call time.sleep and you should never call after with just a single argument. Also, when you use after with two arguments, the second argument must be a reference to a function.
The proper way to do this is to have your first function call your second function via after:
def interval_screen():
...
i_screen.after(3000, maker_screen)
def masker_screen():
...
m_screen.after(3000, lambda: scale_screen(event = None))
Note that in your updated question you're using after incorrectly:
m_screen.after(3000, scale_screen(event = None))
You're calling the function scale_screen(...) immediately, and giving the result of that to the after function. If you need to pass arguments to your function you must create another function that does not require arguments. The simplest way to do this is with lambda, though you can also use functools.partial or you can create your own function.

tkinter button commands with lambda in Python

ive tried searching for a solution but couldn't find one that works. I have a 2d list of tkinter Buttons, and i want to change their Text when it is clicked by the mouse. I tried doing this:
def create_board(number):
print(number)
for i in range (0,number):
buttonList.append([])
for j in range(0,number):
print(i,j)
buttonList[i].append(Button(root, text = " ", command = lambda: update_binary_text(i,j)))
buttonList[i][j].pack()
Then when it is clicked it calls this function:
def update_binary_text(first,second):
print(first,second)
buttonList[first][second]["text"] = "1"
When i click a button, it simply does nothing, i had the program display the indexes of the button that was clicked, and they ALL show 4, 4 (this is when the variable number=5) Is there an solution to this?
this is my first python attempt for a class.
Thanks
You can fix this problem by creating a closure for i and j with the creation of each lambda:
command = lambda i=i, j=j: update_binary_text(i, j)
You could also create a callback factory with references to the button objects themselves:
def callback_factory(button):
return lambda: button["text"] = "1"
And then in your initialization code:
for j in range(0, number):
new_button = Button(root, text=" ")
new_button.configure(command=callback_factory(new_button))
new_button.pack()
buttonList.append(new_button)
Whenever I need a collection of similar widgets, I find it's simplest to enclose them in an object and pass a bound-method as callback rather than playing tricks with lambda. So, instead of having a list like buttonList[] with widgets, create an object:
class MyButton(object):
def __init__(self, i, j):
self.i = i
self.j = j
self.button = Button(..., command = self.callback)
def callback(self):
. . .
Now, you have a list buttonList[] of these objects, rather than the widgets themselves. To update the text, either provide a method for that, or else access the member directly: buttonList[i].button.configure(. . .) And when the callback is activated, it has the entire object and whatever attributes you might need in self.

Categories

Resources