How to pass id of a Tkinter Scale through command - python-2.7

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.

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.

Using Python Tkinter .config() method

I am trying to use the Python Tkinter .config() method to update some message text. I can't get it to work. What might I be doing wrong (see the update_message method):
#!/usr/bin/python
import alsaaudio as aa
import audioop
import Tkinter as tk
import tkFont
import threading
import Queue
# styles
BACKROUND_COLOR = '#000000'
TYPEFACE = 'Unit-Bold'
FONT_SIZE = 50
TEXT_COLOR = '#777777'
TEXTBOX_WIDTH = 400
# text
TITLE = 'listen closely'
SCORE_MESSAGE = 'your score:\n '
END_MESSAGE = 'too loud!\ntry again'
# configuration
DEVICE = 'hw:1' # hardware sound card index
CHANNELS = 1
SAMPLE_RATE = 8000 # Hz // 44100
PERIOD = 256 # Frames // 256
FORMAT = aa.PCM_FORMAT_S8 # Sound format
NOISE_THRESHOLD = 3
class Display(object):
def __init__(self, parent, queue):
self.parent = parent
self.queue = queue
self._geom = '200x200+0+0'
parent.geometry("{0}x{1}+0+0".format(
parent.winfo_screenwidth(), parent.winfo_screenheight()))
parent.overrideredirect(1)
parent.title(TITLE)
parent.configure(background=BACKROUND_COLOR)
parent.displayFont = tkFont.Font(family=TYPEFACE, size=FONT_SIZE)
self.process_queue()
def process_queue(self):
try:
score = self.queue.get(0)
self.print_message(score)
except Queue.Empty:
pass
self.parent.after(100, self.update_queue)
def update_queue(self):
try:
score = self.queue.get(0)
self.update_message(score)
except Queue.Empty:
pass
self.parent.after(100, self.update_queue)
def print_message(self, messageString):
print 'message', messageString
displayString = SCORE_MESSAGE + str(messageString)
self.message = tk.Message(
self.parent, text=displayString, bg=BACKROUND_COLOR,
font=self.parent.displayFont, fg=TEXT_COLOR, width=TEXTBOX_WIDTH, justify="c")
self.message.place(relx=.5, rely=.5, anchor="c")
def update_message(self, messageString):
print 'message', messageString
displayString = SCORE_MESSAGE + str(messageString)
self.message.config(text=displayString)
def setup_audio(queue, stop_event):
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NONBLOCK, 'hw:1')
data_in.setchannels(2)
data_in.setrate(44100)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(256)
while not stop_event.is_set():
# Read data from device
l, data = data_in.read()
if l:
# catch frame error
try:
max_vol = audioop.rms(data, 2)
scaled_vol = max_vol // 4680
print scaled_vol
if scaled_vol <= 3:
# Too quiet, ignore
continue
queue.put(scaled_vol)
except audioop.error, e:
if e.message != "not a whole number of frames":
raise e
def main():
root = tk.Tk()
queue = Queue.Queue()
window = Display(root, queue)
stop_event = threading.Event()
audio_thread = threading.Thread(target=setup_audio,
args=[queue, stop_event])
audio_thread.start()
try:
root.mainloop()
finally:
stop_event.set()
audio_thread.join()
pass
if __name__ == '__main__':
main()
I don't want to be laying down a new message every time I update. If the .config() doesn't work, is there another method to update the text configuration of the message?
I would use string variables, first create your string variable then set it to want you want it to display at the start next make your object and in text put the sting variable then when you want to change the text in the object change the string variable.
self.messaget = StringVar()
self.messaget.set("")
self.message = tk.Message(
self.parent, textvariable=self.messaget, bg=BACKROUND_COLOR,
font=self.parent.displayFont, fg=TEXT_COLOR,
width=TEXTBOX_WIDTH, justify="c").grid()
#note renember to palce the object after you have created it either using
#.grid(row = , column =) or .pack()
#note that it is textvariable instead of text if you put text instead it will run but
#but will show PY_Var instead of the value of the variable
edit
to change the text without recreating the object you do the name of the string variable you have used and .set
self.messaget.set("hi")

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

How to invoke a method from combo box event

I am trying to invoke a method from combo box selected change event
with lambda expression but I am stuck with following error
TypeError: () takes no arguments (1 given)
I think I have passed 1 argument as per the method definition, could somebody please help me where I am wrong
or any other combobox selected change event code will be great help!
please note my code
self.boxWidget[boxName].bind("<<ComboboxSelected>>", lambda:invoke_Setting_Group(self))
def invoke_My_method1(self):
print "expand another window"
I am trying to pass the first class object to the second python script file for variable value assigning easeness.I tried to use this combox event change code without lambda then I noticed that this method is getting called automatically so I used lambda to prevent this automatic method calling
Sorry I am not having the knowledge on lambda expression usage; here I used only to prevent the automatic method execution. Without lambda expression I noticed my combo box function starts automatically, I did not understand why it happens so?
I am using TKinter python 2.6
More Detailed Code of above:
#Main_GUI_Class.py
##----------------------
import sys
class App():
def __init__ (self,master,geometry=None,root=None):
try:
self.master=master
if not root:
self.root=Tkinter.Toplevel(master)
def initUI(self):
try:
self.master.title("GUI")
menubar = Menu(self.master)
self.root.config(menu=menubar)
fileMenu.add_command(label='Open')
submenu_ncss.add_command(label='Model Setting',command=lambda:Combo_Expand_Script.Call_Model_Setting(self))
##----------------------
def main():
r = Tkinter.Tk()
r.withdraw()
r.title("GUI Sample")
r.wm_iconbitmap(Pic1)
v = App(r)
r.mainloop()
if __name__ == "__main__":
main()
##Combo_Expand_Script.py
##-----------------------
import sys
import Tkinter
import Main_GUI_Class
def Call_Model_Setting(self):
try:
self.PopUpWin = Toplevel(bg='#54596d',height=500, width=365)
self.PopUpWin.title("POP UP SETTING")
#Combo Boxs in Pop Up
boxNameGroup="boxSetting"
boxPlaceY=0
for Y in range(4):
boxName=boxNameGroup+str(Y)
if Y == 0:
boxPlaceY=50
else:
boxPlaceY=boxPlaceY+40
self.box_value = StringVar()
self.boxWidget[boxName] = ttk.Combobox(self.PopUpWin, height=1, width=20)
if Y== 0:
self.boxWidget[boxName]['values'] = ('A', 'B')
self.boxWidget[boxName].current(1)
if Y== 1:
self.boxWidget[boxName]['values'] = ('X', 'Y')
self.boxWidget[boxName].bind("<<ComboboxSelected>>",lambda:invoke_Setting_Group(self))
self.boxWidget[boxName].place(x=180, y = boxPlaceY)
#Buttons in Pop Up
self.btnApply = tk.Button(self.PopUpWin,width=10, height=1,text="Apply",relief=FLAT,bg=btn_Bg_Color,command=lambda: treeDataTransfer(self,0))
self.btnApply.pack()
self.btnApply.place(x=75, y = 460)
self.btnCancel = tk.Button(self.PopUpWin,width=10, height=1,text="Cancel",relief=FLAT,command=lambda: deleteTreeNodes(self))
self.btnCancel.pack()
self.btnCancel.place(x=170, y = 460)
except IOError:
print "Error: data error"
def invoke_Setting_Group(self):#, event=None
try:
#self.boxName.current(0)
self.boxWidget["boxSetting3"].current(0)
self.PopUpWin['width']=1050
self.PopUpWin['height']=700
self.btnApply.place(x=500, y = 550)
self.btnCancel.place(x=600, y = 550)
self.txtWidget={}
lsttxtSetting = ['1', '2','3 ','4','5 ','6','7','8','9','10']
for t in range(10):
txtName=txtNameGroupTS+str(t)
if t == 0:
txtPlaceY=120
else:
txtPlaceY=txtPlaceY+30
self.txtWidget[txtName] = Text(self.groupSettingFrame,height=1, width=10,borderwidth = 2)
self.txtWidget[txtName].insert(INSERT, lsttxtSetting[t])
self.txtWidget[txtName].pack()
self.txtWidget[txtName].place(x=200, y = txtPlaceY)
except IOError:
print "Error: Group Settings Popup error"
def turbDataTransferBind(self):
for P in range(0,3):
boxName="boxSetting"+str(X)
dataSettingbox=self.lstTurb[X]+" "+self.boxWidget[boxName].get()
self.root_node_Setting = self.tree.insert( self.root_node_ChildSetting["ChildSettingNode"], 'end', text=dataSettingbox, open=True)
def treeDataTransfer(self,dlgTurbFlag):
self.treeDataTransferBind()
print "data tranfer sucess"
def deleteTreeNodes(self):
print "delete nodes"
command= and bind expect function name - without () and arguments - so in place of
If you use
.bind("<<ComboboxSelected>>", invoke_Setting_Group(self) )
then you use result from invoke_Setting_Group(self) as second argument in .bind(). This way you could dynamicly generate function used as argument in bind
TypeError: () takes no arguments (1 given)
This means you have function function() but python run it as function(arg1)
You run lambda:invoke_Setting_Group(self) but python expects lambda arg1:self.invoke_Setting_Group(self)
You could create function with extra argument
def invoke_My_method1(self, event):
print "expand another window"
print "event:", event, event.widget, event.x, event.y
And then you could use it
.bind("<<ComboboxSelected>>", lambda event:invoke_Setting_Group(self, event))
BTW: it looks strange - you have class App() but in second file you use only functions instead of some class too.