How do I use tklib widgets in tkinter? - python

I was looking for an IP address entry widget for tkinter for use with python3.7 and came across ipentry in the tklib.
https://core.tcl-lang.org/tklib/doc/trunk/embedded/www/tklib/files/modules/ipentry/ipentry.html#section2
This is not a terribly complicated need to 'recreate the wheel' for and there are several examples on stackoverflow for IP entry boxes but I'd really like to understand how to use the modules and widgets in the tklib with tkinter. Documentation or examples appear to be few and far between. Here's how far I've gotten:
First of all, I have to tell Python that this widget exists.
import tkinter as tk
root = tk.Tk()
root.tk.call(‘package’,’require’,’ipentry’)
Then I created a class for the widget.
class iPentry(tk.Widget):
def __init__(self, master):
tk.Widget.__init__(self, master, '::ipentry::ipentry’)
Then I create an instance of it and pack it in my window.
enterIp = iPentry(root)
enterIp.pack()
So far so good. I get a window with a familiar looking input box for an IPV4 address.
The problem is that I haven't figured out how to use the get or complete or insert widget commands. When I try to get from the enterIp widget that I created, I get an error.
myip = enterIp.get()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'iPentry' object has no attribute 'get'
I suspect that I'm missing some syntax concepts. Any suggestions for how to do this?

You need to define get() function in your wrapper class iPentry:
def get(self):
return self.tk.call(self._w, 'get')
Actually you need to define every functions that ipentry provides like above if you want to call them.

Thanks to acw1668 I was able to work through some bone headed mistakes and figure this out a bit. Here is some sample code for someone else who might like to work with these in Python. They are pretty limited and I found a few things that didn't work even though they are documented on the lib page. But they might be useful for some situations. The rdial in particular was surprising because it is visually a "thumbwheel" rather than a typical round dial. I have included a screenshot of what some of these look like in Macos.
This code displays an iPentry with a label below that will populate with the address when you press enter. Then there is a bank of 3 sliders whose values will show in a label below. Then an rdial whose value shows in a label below and finally a "voltmeter" that bounces around based on a random number. Enjoy.
# This Python file uses the following encoding: utf-8
# Python 3.7 and Tk version 8.6
import sys
import tkinter as tk
import random
class iPentry(tk.Widget):
def __init__(self, master):
tk.Widget.__init__(self, master, '::ipentry::ipentry')
def get(self):
return self.tk.call(self._w, 'get')
def complete(self):
return self.tk.call(self._w, 'complete')
class CWslider(tk.Widget):
def __init__(self, master, placeholder):
tk.Widget.__init__(self, master, '::controlwidget::slider',
{'variable':placeholder, 'from_':0, 'to':20, 'number':3,
'width':55, 'background':'yellow'})
def get(self):
getvalue = self.tk.call(self._w, 'get')
getvalue = [int(x) for x in getvalue]
return getvalue
def set(self, value):
self.tk.call(self._w, 'set', value)
def complete(self):
return self.tk.call(self._w, 'complete')
class CWrdial(tk.Widget):
def __init__(self, master):
tk.Widget.__init__(self, master, '::controlwidget::rdial',
{'width':50, 'orient':'vertical', 'height':100, 'background':'green'})
def get(self):
return self.tk.call(self._w, 'get')
def complete(self):
return self.tk.call(self._w, 'complete')
class CWvoltmeter(tk.Widget):
def __init__(self, master, variable):
tk.Widget.__init__(self, master, '::controlwidget::voltmeter',
{'min':0, 'max':100, 'variable':variable})
def getIP(event):
myip = enterIp.get()
labelvar.set(myip)
print(f"myip is {myip}")
def updating(master, myValuesvar, myvoltvar, interval):
#we can't get value from placeholder because slider corrupts the IntVar?
slidervalues = slider.get() #so we use the get method
myValuesvar.set(slidervalues)
mydialvalue.set(mydial.get())
myvoltvar.set( random.randrange(0, 100, 1))
root.after(interval, updating, root, myValuesvar, myvoltvar, interval)
root = tk.Tk()
root.geometry("300x550+280+0")
root.tk.call('package','require','ipentry')
root.tk.call('package','require','controlwidget')
enterIp = iPentry(root)
enterIp.pack()
labelIP = tk.Label(root, text="Show The IP")
labelIP.pack()
labelvar = tk.StringVar()
label2 = tk.Label(root, textvariable=labelvar)
label2.pack()
root.bind('<Return>', getIP)
myvalues = [5,15,3]
myValuesvar = tk.IntVar()
placeholder = tk.IntVar() #necessary for slider to change values
slider = CWslider(root, placeholder)
slider.pack()
slider.set(myvalues)
labelSlider = tk.Label(root, textvariable=myValuesvar)
labelSlider.pack()
mydialvalue = tk.StringVar()
mydial = CWrdial(root)
mydial.pack()
labeldial = tk.Label(root, textvariable=mydialvalue)
labeldial.pack()
myvoltvar = tk.IntVar()
myvolt = CWvoltmeter(root, myvoltvar)
myvolt.pack()
interval = 300 #milliseconds for GUI
updating(root, myValuesvar, myvoltvar, interval)
root.mainloop()
sys.exit()

Related

My Button Functions Are Not Working in my Python code

Good Day,
I'm designing a simple eatery Gui in Python with the Tkinter library but my "Reset" and "Calculate Price" buttons don't work as they should. Please what could the problem be?
from tkinter import *
class Application (Frame):
def __init__(self,anjie):
super(Application,self).__init__(anjie)
#getting the init() function in frame,the superclass
#super is a function that takes two parameters: the name of your subclass i.e. Application and self
self.grid() #allows grid function to work
self.toppings=["sausage","pepperoni","chicken","mushroom","black olive","green pepper","red pepper","onion"]
self.prices={"medium":9.99, "large":12.99, "x-large":14.99, "mid-topping":1.0, "large-topping":1.4, "xl-topping":1.8}
self.checkbox=list(self.toppings)
self.checkboxVar=list(self.toppings)
self.create_widgets()
def create_widgets(self):
menubar=Menu(self)
filemenu=Menu(menubar)
menubar.add_cascade(label="File", menu=filemenu)
menubar.add_cascade(label="Quit",command=window.quit)
filemenu.add_command(label="Submit",command=self.process)
self.label1=Label(self,text="Select size: ")
self.label1.grid(row=0,column=0,sticky=W)
self.size=StringVar() #similar to name in html, groups similar elements together with variable self.size
print(StringVar())
self.radio1=Radiobutton(self,text="Medium",variable=self.size,value="Medium")
self.radio1.grid(row=1,column=0,sticky=W)
self.radio1.select() #makes radio1 the default selected radio button
self.radio2=Radiobutton(self,text="Large",variable=self.size,value="Large")
self.radio2.grid(row=1,column=1,sticky=W)
self.radio2.select()
self.radio3=Radiobutton(self,text="Extra Large",variable=self.size,value="X-Large")
self.radio3.grid(row=1,column=2,sticky=W)
self.radio3.select()
self.label2=Label(self,text="Select size: ")
self.label2.grid(row=2,column=0,sticky=W)
line=2#last row number
for i in range(len(self.toppings)):
line+=1
self.checkboxVar[i]=BooleanVar() #default value added is false
self.checkboxVar[i]=Checkbutton(self,text=self.toppings[i], variable=self.checkboxVar[i])
self.checkboxVar[i].grid(row=line,column=0,sticky=W)
line+=1
self.button1=Button(self,text="Reset",command=self.reset)
self.button1.grid(row=line,column=0,sticky=E)
self.button2=Button(self,text="Calculate Price",command=self.calculate)
self.button2.grid(row=line,column=2)
line+=1
self.label3=Label(self,text="")
self.label3.grid(row=line,column=0)
line+=1
self.label4=Label(self,text="Total: ")
self.label4.grid(row=line,column=0,sticky=E)
self.result=Entry(self,width=10)
self.result.grid(row=line,column=1,sticky=W)
window.config(menu=menubar)
def process(self):
print("This is the process to submit: ")
def reset(self):
self.radio1.select()
for i in range(len(self.toppings)):
self.checkbox[i].deselect()
self.result.delete(0,END)
def calculate(self):
self.totalToppings=0
for i in range(len(self.toppings)):
if self.checkboxVar[i].get():
self.totalToppings+=1
if self.size.get()=="Medium":
self.price=self.prices["medium"]+(self.totalToppings * self.prices["mid-topping"])
if self.size.get()=="Large":
self.price=self.prices["large"]+(self.totalToppings*self.prices["large-topping"])
if self.size.get()=="X-Large":
self.price=self.prices["x-large"]+(self.totalToppings*self.prices["xl-topping"])
str_price="{.2f}".format(self.price)
self.result.delete(0,END)
self.result.insert(END,str_price)
window=Tk()
window.title("Anjie\'s Pizza")
window.geometry("800x500") #WIDTH BY HEIGHT
app=Application(window)
app.mainloop()
I would appreciate any help regarding this. I did this with the Python tkinter library and my problem is that no result appears in the total entry.
There are few issues in your code:
Call .select() on all radio buttons, should only call self.radio1.select() initially
Used self.checkboxVar[i] instead of self.checkbox[i]
def create_widgets(self):
...
#self.radio2.select() # should not call
...
#self.radio3.select() # should not call
...
line=2#last row number
for i in range(len(self.toppings)):
line+=1
self.checkboxVar[i]=BooleanVar() #default value added is false
### should use self.checkbox[i]=... instead of self.checkboxVar[i]=...
self.checkbox[i]=Checkbutton(self,text=self.toppings[i], variable=self.checkboxVar[i])
self.checkbox[i].grid(row=line,column=0,sticky=W)
...
Used invalid float format {.2f}, should be {:.2f} instead:
def calculate(self):
...
str_price="{:.2f}".format(self.price)
...

How to make a subclass that fills *args and **kwargs of the parent?

I am trying to make a button subclass in tkinter so that it fills some of the args and kwargs automatically. I am not sure if it is a python issue or a tkinter issue.
from tkinter import *
root = Tk()
class MyButton(Button):
def __init__(self, *args, **kwargs):
super(Button).__init__(*args, **kwargs)
kwargs = {"padx": 40, "pady": 20}
args = (root)
test = Numbutton(text = "48")
test.pack()
root.mainloop()
And the error is:
Traceback (most recent call last):
File "my file directory", line 18, in <module>
test = Numbutton(text = "xyz", *args, **kwargs)
File "my tkinter directory", line 1489, in cget
return self.tk.call(self._w, 'cget', '-' + key)
TypeError: can only concatenate str (not "int") to str
Any help is appreciated :)
Here's a first stab:
from tkinter import *
root = Tk()
class MyButton(Button):
def __init__(self, *args, **kwargs):
super().__init__(root, padx=40, pady=20,**kwargs)
test = MyButton(text = "48")
test.pack()
root.mainloop()
EDIT:
here's a better way that can be used to used to make a subclass using the initial kwargs instead of hard coding initial kwargs:
from tkinter import Tk, Button
def set_button_initial(*initial_args, **initial_kwargs):
class ButtonClassWithInitialStuff(Button):
def __init__(self, *args, **kwargs):
super().__init__(*initial_args, **initial_kwargs, **kwargs)
return ButtonClassWithInitialStuff
root = Tk()
ButtonClass1 = set_button_initial(root, {"padx": 40, "pady": 20})
ButtonClass2 = set_button_initial(root, {"padx": 10, "pady": 50})
ButtonObject1a = ButtonClass1(text="48")
ButtonObject1b = ButtonClass1(text="49")
ButtonObject2 = ButtonClass2(text='second')
ButtonObject1c = ButtonClass1(text="50")
ButtonObject1a.pack()
ButtonObject1b.pack()
ButtonObject2.pack()
ButtonObject1c.pack()
root.mainloop()
Note, for the function set_button_initial we return the class ButtonClassWithInitialStuffinstead of an object ButtonClassWithInitialStuff(), that is, we set up the constructor (init), but we don't use it yet.
Using the function, we create classes ButtonClass1 and ButtonClass2.
Then, we instantiate obejcts ButtonObject1a-c and ButtonObject1 of our classes we just created. Note: we can reuse the classes to make as many objects as we want without re-calling set_button_initial
(edited to explain questions raised by commentors)
As far as I can see #MisterMiyagi suggests to use
from functools import partial
def set_button_initial(*initial_args, **initial_kwargs):
return partial(Button, *initial_args, **initial_kwargs)
which appears to behave identically, but is nicer-looking without losing information, and is therefore better

tkinter new class can't use .get()/float()

I have defined a new Entry subclass: NewEntry, but it can't get the numbers which are put in it. How can I fix this?
When I click the button, the error message is showed:
ValueError: could not convert string to float:
from Tkinter import *
root = Tk()
class NewEntry(Entry):
def __init__(self,parent,cusdef='1'): #Initiation default number is '1'
Entry.__init__(self,parent)
self.cusdef = cusdef
v=StringVar()
v.set(self.cusdef)
self = Entry(self,textvariable=v)
self.pack()
return
def GetNum():
a=e.get()
print float(a)
return
e = NewEntry(root)
e.pack(fill='x')
button = Button(root,command=GetNum)
button.pack(fill='x')
root.mainloop()
You seem to be trying to initialize your Entry subclass here:
self = Entry(self,textvariable=v)
self.pack()
But instead, you're merely overwriting the variable called self and creating a new Entry which gets discarded.
Instead you need to do the Entry.__init__ call once, with the correct arguments:
class NewEntry(Entry):
def __init__(self,parent,cusdef='1'):
self.cusdef = cusdef
v=StringVar()
v.set(self.cusdef)
Entry.__init__(self,parent, textvariable=v)
self.pack()
return

button command option in tkinter

In the little GUI app below. When I use button's command option to call a function. It doesn't work like this: self.update() rather it works like this: self.update. Why so? Is is some special way that command option of a button works? I think a method or a function should be called with those braces (), unless it's a property:
i.e.
#name.setter:
def setter(self, name):
self.name = name
#main
object.name = "New_obj"
Note: The above is just a template so you might get my point. I didn't write the complete valid code. Including class and everything.
from tkinter import *
class MuchMore(Frame):
def __init__(self, master):
super(MuchMore,self).__init__(master)
self.count =0
self.grid()
self.widgets()
def widgets(self):
self.bttn1 = Button(self, text = "OK")
self.bttn1.configure(text = "Total clicks: 0")
self.bttn1["command"] = self.update # This is what I am taking about
self.bttn1.grid()
def update(self):
self.count += 1
self.bttn1["text"] = "Total clicks" + str(self.count)
#main
root = Tk()
root.title("Much More")
root.geometry("324x454")
app = MuchMore(root)
It is a high order function, meaning you are referencing a function as an object. You are not calling the function and assigning the command to the return value of the function. See here for more information.
The command parameter takes a reference to a function -- ie: the name of the function. If you add parenthesis, you're asking python to execute the function and give the result of the function to the command parameter.

Global variable is not defined

I'm trying to read a file with python, by posting the address in input line. In my plan, when I press the button, program will read the file, make all needed work with the text inside the first file, and write the result into a second one:
import Tkinter
class Generator(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent=parent
self.initialize()
def initialize(self):
self.grid()
self.addressLink = Tkinter.StringVar()
self.entry=Tkinter.Entry(self,textvariable=self.addressLink)
self.entry.grid(column=0,row=0,sticky='EW')
self.entry.bind("<Return>", self.OnPressEnter)
self.entry.bind(".", self.OnPressDot) # verify that address was accepted
self.addressLink.set(u"Enter your input file's address here!")
button=Tkinter.Button(self,text=u'Generate the list!',command=self.OnButtonClick)
button.grid(column=1,row=0)
self.labelVariable = Tkinter.StringVar()
label = Tkinter.Label(self, textvariable=self.labelVariable,
anchor="w",fg="white",bg="blue")
label.grid(column=0,row=1,columnspan=2,sticky='EW')
self.labelVariable.set(u"Enter Address !")
self.grid_columnconfigure(0,weight=1)
self.resizable(True,False)
def ProgramBody(readlink):
excelWrite=open('/Users/BUR/Desktop/final_TK.txt','w')
z=0
for index, line in enumerate(readlink, start=0):
keywrds=[]
title=line.split("+")
title=[lines.strip()for lines in title]
print title[0]
print index
header="Title"+"\t"+"Price equal to title:"+"\t"+"keyword1"+"\t"+"keyword2"+" \t"+"keyword3"+"\t"+"keyword4"+"\t"+"keyword5\t"+"Manufacturer Part Number\n"
exclWrt(header)
excelWrite.close()
def FileRead(tsink):
excelRead=open(tsink,'r')
print tsink
ProgramBody(tsink)
def OnButtonClick(self):
link=(self.addressLink.get())
# print link
self.labelVariable.set(link+" (Here is your button press!) ")
FileRead(link)
def OnPressEnter(self,event):
self.labelVariable.set(self.addressLink.get()+" (Here is your address!)")
def OnPressDot(self,event):
self.labelVariable.set(self.addressLink.get()+" (Here is your address!!!)")
if __name__=="__main__":
app=Generator(None)
app.title('Converter')
app.mainloop()
#excelRead=open('/Users/BUR/Desktop/listings/data.txt','r')
def exclWrt(typo):
excelWrite.write(typo)
Program runs, but when I press button it gives me:
> Exception in Tkinter callback Traceback (most recent call last):
> File
> "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py",
> line 1470, in __call__
> return self.func(*args) File "/Users/BUR/Documents/Python/Temp+price+keyw.1", line 114, in
> OnButtonClick
> FileRead(link) NameError: global name 'FileRead' is not defined
What did I miss?
You're using a class. First, you'll have an instance of the class passed to every function. Usually it's named self:
class A:
def something(self, my_arguments)
And, to call something from the class, you do this:
def something_else(self, another_arguments):
self.something(another_arguments)
The first argument will automatically be passed. Also, __init__ is called when you create an instance of your class, so you have no need of a separate initialize function.
I suggest you read more about classes here. This is just a very short solution for your problem.

Categories

Resources