Tkinter forgetting to finish the function - python-2.7

I am again asking a question on this progressbar project; although this should just be a clarification question.
My code causes for a progressbar to be created to track the creation of a file. The user selects the type of file they want to create and then hits "go" which causes for the file to begin changing and for the progressbar to appear. Progressbar works great. File writing/manipulation works great.
Problem: When the user selects several files to manipulate, despite the progressbars being created correctly, they do NOT update correctly. At first I thought that clicking on a button multiple times causes for tkinter to forget the root.after() function it was doing previously but after playing with a (much simpler) sample code I realized that this is not the case.
Question: How do I make sure tkinter doesn't stop implementing the first function even when the same function is restarted with different parameters?
Below are parts of my code to describe what I am doing.
progbarlock = False # start with the prograssbar marked as not occupied
class guibuild:
def __init__(self):
self.root = root
guibuild.progbarlock = False
global theframe
theframe = Frame(root)
job_name = e.get()
label = Label(theframe,text = job_name).pack(side=LEFT,padx =2)
self.progbar = Meter(theframe) #makes the progressbar object
self.progbar.set(0.0) #sets the initial value to 0
self.progbar.pack(side=LEFT)
self.counter = 0
self.i = float(0) #i is the value set to the progressbar
def stop_progbar(self):
self.progbar.stop()
def begin(self):
self.interval()
self.Status_bar()
theframe.pack(anchor="s")
def interval(self):
if guibuild.progbarlock == False:
guibuild.progbarlock = True
def update(self):
the_file = open('running_file.json')
data = json.load(the_file)
curr = data["current_line"]
total = data["total_lines"]
if self.i == 1.0:
self.stop_progbar
rint "100% - process is done"
self.root.after_cancel(self.interval)
elif curr == self.counter:
self.root.after(5000, self.interval)
elif curr == self.counter+1:
self.i += 1.0/total
self.progbar.set(self.i) #apply the new value of i to the progressbar
self.counter += 1
self.stop_progbar
self.root.after(5000, self.interval)
elif curr > self.counter+1:
self.i += 1.0/total*(curr-self.counter)
self.progbar.set(self.i) #apply the new value of i to the progressbar
self.counter = curr
self.stop_progbar
self.root.after(5000, self.interval)
else:
print "something is wrong - running.json is not available"
self.root.after(5000, self.interval)
guibuild.progbarlock = False
def start_process():
makeRequest() #this is defined much earlier in the code and includes all the file creation and manipulation
guibuild().begin()
button4 = Button(root,text="GO", command = start_process).pack()
NOTE:makeRequest() depends entirely on user input and the user input changes each time "go" is pressed.

Related

Tkinter assign buttons to entries in dynamically created widgets

How can I access an Entry content with pressing the corresponding Button in dynamically created widgets?
Below is the best I come up with so far. Thank you for any help.
from Tkinter import *
class App(object):
def __init__(self, master):
self.master = master
self.mf = Frame(self.master)
self.l = ["white", "red", "blue", "brown"]
self.font = ("Arial", 30)
self.c, self.r = 1, 0
self.cc, self.rr = 0, 0
self.bel = []
for c in self.l:
action = self.print_entry
self.e = Entry(self.mf, bg=c, width=10, font=self.font)
self.e.grid(row=self.r, column=self.c)
self.b = Button(self.mf, bg=c, text=c, font=self.font)
self.b.grid(row=self.rr, column=self.cc)
self.b.config(command=action)
self.bel.append((self.b, self.e))
self.rr += 1
self.r += 1
self.mf.pack()
def print_entry(self): # this function prints the content of the entry
pass
def main():
root = Tk()
display = App(root)
root.mainloop()
if __name__=="__main__":
main()
You can pass a reference to the entry widget into the command, using lambda or functools.partial. For example:
self.b.config(command= lambda entry=self.e: action(entry))
...
def print_entry(self, entry):
print("the entry is '%s'" % entry.get())
By the way, using self.b and self.e is pointless, since those variables will only ever hold references to the last button and last entry. You should either use a local variable, and/or append the values to a list.

Setting and Retrieving Data with TkInter

I just ran into some strange behavior that has me stumped. I'm writing a simple little GUI for some in-house data processing. I want to allow a user to switch between a few different data-processing modes and input some parameters which define how the data is processed for each mode. The problem is that when the user inputs new parameters, the app ignores requests to switch modes.
The code below replicates the issue. I apologize for the size, this was the shortest code that replicates the problem.
import Tkinter as Tk
class foo(Tk.Frame):
def __init__(self):
self.master = master =Tk.Tk()
Tk.Frame.__init__(self,self.master) #Bootstrap
#Here mode and parameters as key, value pairs
self.data = {'a':'Yay',
'b':'Boo'
}
self.tex = Tk.Text(master=master)
self.tex.grid(row=0,column=0,rowspan=3,columnspan=4)
self.e = Tk.Entry(master=master)
self.e.grid(row=3,column=0,columnspan=4)
self.sv =Tk.StringVar()
self.sv.set('a')
self.b1 = Tk.Radiobutton(master=master,
text = 'a',
indicatoron = 0,
variable = self.sv,
value = 'a')
self.b2 = Tk.Radiobutton(master=master,
text = 'b',
indicatoron = 0,
variable = self.sv,
value = 'b')
self.b3 = Tk.Button(master = master,
text='Apply',command=self.Apply_Func)
self.b4 = Tk.Button(master = master,
text='Print',command=self.Print_Func)
self.b1.grid(row=4,column=0)
self.b2.grid(row=4,column=1)
self.b3.grid(row=4,column=2)
self.b4.grid(row=4,column=3)
def Apply_Func(self):
self.innerdata = self.e.get()
def Print_Func(self):
self.tex.insert(Tk.END,str(self.innerdata)+'\n')
#This is how I'm retrieving the user selected parameters
#property
def innerdata(self):
return self.data[self.sv.get()]
#And how I'm setting the user defined parameters
#innerdata.setter
def innerdata(self,value):
self.data[self.sv.get()] = value
if __name__ == "__main__":
app = foo()
app.mainloop()
Expected behavior:
1) Press button 'a' then 'print' prints:
Yay
2) Press button 'b' then 'print' prints:
Boo
3) Type 'Zep Rocks' into the entry field and press apply
4) Pressing 'print' now yields
Zep Rocks
5) Pressing 'a' then 'print' should yield
Yay
But instead yields
Zep Rocks
Which might be true, but not desired right now. What is going on here?
Edit: I have some new information. Tk.Frame in python 2.7 is not a new-style class. It isn't friendly with descriptors, so rather than interpreting the '=' as a request to use the foo.innerdata's __set__ method, it just assigns the result of self.e.get() to innerdata.
ARGLEBARGLE!!!
Still an open question: how do I get this to do what I want in a clean manner?
So the core problem is that Tk.Frame doesn't subclass from object, so it is not a new-style python class. Which means it doesn't get down with descriptors like I was trying to use. One solution that I found is to subclass my app from object instead.
Code that solves my problem is below:
import Tkinter as Tk
class foo(object):
def __init__(self,master):
self.master = master #Bootstrap
self.mainloop = master.mainloop
self.data = {'a':{'value':7,'metavalue':False},
'b':{'value':'Beeswax','metavalue':True}
}
self.tex = Tk.Text(master=master)
self.tex.grid(row=0,column=0,rowspan=3,columnspan=4)
self.e = Tk.Entry(master=master)
self.e.grid(row=3,column=0,columnspan=4)
self.sv =Tk.StringVar()
self.sv.set('a')
self.b1 = Tk.Radiobutton(master=master,
text = 'a',
indicatoron = 0,
variable = self.sv,
value = 'a')
self.b2 = Tk.Radiobutton(master=master,
text = 'b',
indicatoron = 0,
variable = self.sv,
value = 'b')
self.b3 = Tk.Button(master = master,text='Apply',command=self.Apply_Func)
self.b4 = Tk.Button(master = master,text='Print',command=self.Print_Func)
self.b1.grid(row=4,column=0)
self.b2.grid(row=4,column=1)
self.b3.grid(row=4,column=2)
self.b4.grid(row=4,column=3)
def Apply_Func(self):
self.innerdata = self.e.get()
def Print_Func(self):
self.tex.insert(Tk.END,str(self.innerdata)+'\n')
#property
def innerdata(self):
return self.data[self.sv.get()]
#innerdata.setter
def innerdata(self,value):
self.data[self.sv.get()] = value
if __name__ == "__main__":
master = Tk.Tk()
app = foo(master)
app.mainloop()

Tkinter button doesn't respond (has no mouse over effect)

I'm writing a game that has info that is communicated from client to server and from server to client. One specific (non-playing) client is the monitor, which only displays the game board and players. This works fine, the only thing that doesn't work is the quit button on the GUI. A minor thing, but I would like it to work. :) Plus I think that there might be something pretty wrong with the code, even though it works.
I tried all kind of different commands (sys.exit, quit...) and nothing fixed it.
There's no error message, nothing happens with the button at all. No mouse over effect, nothing if I click it. Relevant code (I removed matrix and server logic because I think it's irrelevant - if it isn't I'll post it):
class Main():
def __init__(self, master):
self.frame = Frame(master)
self.frame.pack()
# Has to be counted up by server class
rounds = 0
# Has to be communicated by server class. If numberwin == numberrobots,
# game is won
numberwin = 0
numberrobots = 2
def draw(self):
if hasattr(self, 'info'):
self.info.destroy()
if hasattr(self, 'quit'):
self.quit.destroy()
print "Main should draw this matrix %s" % self.matrix
[...] lots of matrix stuff [...]
# Pop-Up if game was won
# TODO: Make GUI quittable
if self.numberwin == self.numberrobots:
self.top = Toplevel()
self.msg = Message(self.top, text="This game was won!")
self.msg.pack(side=LEFT)
self.quittop = Button(
self.top, text="Yay", command=self.frame.destroy)
self.quittop.pack(side=BOTTOM)
# TODO: Quit GUI
self.quit = Button(self.frame, text="Quit", command=self.frame.destroy)
self.quit.pack(side=BOTTOM)
# Information on the game
self.info = Label(
self.frame, text="Rounds played: {}, Numbers of robots in win condition: {}".format(self.rounds, self.numberwin))
self.info.pack(side=TOP)
def canvasCreator(self, numberrows, numbercolumns):
# Game board
self.canvas = Canvas(
self.frame, width=numbercolumns * 100 + 10, height=numberrows * 100 + 10)
self.canvas.pack()
class Agent(Protocol, basic.LineReceiver):
master = Tk()
main = Main(master)
# So first matrix is treated differently from later matrixes
flagFirstMatrix = 1
def connectionMade(self):
msg = dumps({"type": "monitor"})
self.sendLine(msg)
print "Sent message:", msg
def dataReceived(self, data):
# Decode the json dump
print "Data received: %s" % data
data = loads(data)
self.main.matrix = np.matrix(data["positions"])
self.main.goals = np.matrix(data["goals"])
self.main.next_move_by_agent = data["next_move"]
self.main.rounds = data["rounds"]
self.main.numberwin = data["win_states"]
if self.flagFirstMatrix == 1:
self.main.numberrows, self.main.numbercolumns = self.main.matrix.shape
self.main.canvasCreator(
self.main.numberrows, self.main.numbercolumns)
self.main.canvas.pack()
self.flagFirstMatrix = 0
self.main.canvas.delete(ALL)
self.main.draw()
self.master.update_idletasks()
self.master.update()
First there is no indentation for class Agent, second for the quit button's "call back" self.frame.destroy is never defined so it doesn't do anything. If you meant tkinter destroy method try self.frame.destroy() or try explicitly defining it. You can also try calling either fram.pack_forget() or fram.grid_forget()
Add master.mainloop() to your last line in terms of the entire lines of code

value of radio button and option menu value in Tkinter

def fresh():
t = Toplevel()
t.geometry("%dx%d%+d%+d" % (600, 400, 0, 0))
rad1 = StringVar(t)
opt1 = StringVar(t)
opt1.set("School Board")
opt2 = StringVar(t)
opt2.set("Department")
form = Frame(t)
form.pack()
for (ix, label) in enumerate(fieldnames):
lab = Label(form, text=label)
ent = Entry(form)
lab.grid(row=ix, column=0)
ent.grid(row=ix, column=1)
entries[label] = ent
r1=Radiobutton(form, text="M", variable=rad1, value="M")
r1.grid(row=5,column=0)
r2 = Radiobutton(form, text="F", variable=rad1, value="F")
r2.grid(row=5, column=1)
student.gender = rad1.get()
l = OptionMenu(form, opt1, "CBSE", "State Board","SB-TN","SB-KA","SB-KL","SB-AP","SB-RJ","SB-MH" )
l.grid(row=6, column=1)
student.SB = opt1.get()
l2 = OptionMenu(form, opt2,"AE","CH","CI","CSE","ECE","EEE","EIE","ME" )
l2.grid(row=7, column=1)
student.dept = opt2.get()
The value for radio button remains NULL value.
opt1 and opt2 value is also not getting changed other than default value.
kindly help.
They're changing, you're just not tracing the change. When you're setting them with these lines:
student.gender = rad1.get()
it's only getting set when the control flow passes over them. So, those values are going to be the values on initiation of the code.
You can trace a variable class instance with the trace method. Here's a small example:
root = Tk()
def callback(*args): # function called when var changes
print var.get() # this is where you'd set another variable to var.get()
var = StringVar()
Radiobutton(root, text='M', variable=var, value='M').pack()
Radiobutton(root, text='F', variable=var, value='F').pack()
var.trace('w', callback) # this will call a function when var changes
mainloop()
Alternatively, you can set a command to a callback in the Radiobutton options. Or, if you don't need the values updated in real-time, you could make a button that handles all of the processing of the fields at once.

How to pass id of a Tkinter Scale through command

I am using Tkinter to create a GUI for a program I am writing that will adjust some Zigbee controlled LED lights that I have. I am using a loop to create multiple copies of a Scale that I'm going to use as a brightness slider. I manage to create the sliders properly, but I am having difficulties actually adjust the sliders correctly. Here's my code:
import simplejson as json
import requests # submits http requests
from Tkinter import *
from ttk import Frame, Button, Label, Style, Notebook
# MD5 hash from http://www.miraclesalad.com/webtools/md5.php
myhash = "d9ffaca46d5990ec39501bcdf22ee7a1"
appname = "dddd" # name content isnt relevant
num_lights = int(3)
class hueApp(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self, *args, **kwds):
# title the app window
self.parent.title("Hue controller")
self.style = Style()
# create grid layout
self.columnconfigure(0, pad=3)
self.columnconfigure(1, pad=3)
self.columnconfigure(2, pad=3)
self.rowconfigure(0, pad=3)
self.scale=[]
self.val=[]
for i in range(num_lights):
print 'i=', i, type(i)
self.val.append(i+1)
print 'val=', self.val, type(self.val)
self.scale.append(Scale(self, from_=255, to_=0, command=lambda i=self.val: self.brightness_adj(i,light_id=i)))
print self.scale[i]
print 'i = ', i, type(i), '\n\n'
self.scale[i].set(150)
self.scale[i].grid(row=1, column=i)
if i == 2:
print '\n', self.scale, '\n'
print self.val, '\n'
self.scale[i].set(200)
self.centerWindow
self.pack()
def brightness_adj(self,light_val, light_id):
#global bri_val
print 'light_id:', light_id, type(light_id)
print 'light_val:', light_val, type(light_val)
print self.val[int(light_id)]
#print int(light_id)
bri_val = self.scale[light_id-1].get()
print bri_val
light = light_id
global huehub
huehub = "http://192.168.0.100/api/"+ myhash + "/lights/" + str(light)
#brightness_logic()
reply = requests.get(huehub)
a=json.loads(reply.text)
#print bri_val
payload = json.dumps({"bri":bri_val})
sethuehub = huehub + "/state"
reply = requests.put(sethuehub, data=payload)
def centerWindow(self):
w = 250
h = 150
sw = self.parent.winfo_screenwidth()
sh = self.parent.winfo_screenheight()
x = (sw-w)/2
y = (sh-h)/2
self.parent.geometry('%dx%d+%d+%d' % (w, h, x, y))
def main():
root=Tk() #the root window is created
app=hueApp(root) #create an instance of the application class
root.mainloop()
if __name__ == '__main__':
main()
I realize that this code probably gives an error when you try to run it. Basically my problem is that the command for each scale is only send brightness_adj the value of the scale, but I can't get it to pass through the id of the light. I was trying to do this by sending through the index of the self.scale list that it is appended into when it is created. I need to know which light is being adjusted so that I can send a new brightness to the corresponding light bulb. I hope I was clear enough. Thanks in advance!
I'm a little confused about what you're trying to do with the line that assigns callback functions to the scale widgets:
self.scale.append(Scale(self, from_=255, to_=0, command=lambda i=self.val: self.brightness_adj(i,light_id=i)))
since self.val is a list, and you're sending it as both the light_val and the light_id arguments, which I would think should be integers.
Possible fix:
I'm guessing that you want each callback to send a different ID to the brightness_adj function depending on which scale it's assigned to. Here's how I would fix this up:
Add this function to your hueApp class namespace:
def brightnessCallbackFactory(self, id):
return lambda light_val:self.brightness_adj(light_val, id)
Then change the callback assignment line from the above to this:
self.scale.append(Scale(self, from_=255, to_=0, command=self.brightnessCallbackFactory(i)))
That should create callback functions that retain the ID value in their internal namespace and assign them to the corresponding scale widget.