Python: Tkinter access variable from another module - python

Somewhere in my code I import this module:
import sModule as s
And initialize my main Tkinter window like this:
base = tk.Tk()
mw = MainWindow(base).grid()
s.parent = sys.modules[__name__]
base.mainloop()
And MainWindow class is something like this:
class MainWindow(tk.Frame):
def __init__(self, parent):
self.info1 = tk.StringVar()
self.info2 = tk.StringVar()
What I'm trying to do is accessing info1 and info2 in sModule like this:
parent.mw.info1.set(str1)
And I'm getting this error:
AttributeError: 'NoneType' object has no attribute 'info1'
Which part is wrong?

Replace following line:
mw = MainWindow(base).grid()
with:
mw = MainWindow(base)
mv.grid()
Why? grid() does not return anything; implicitly return None.

Related

Passing user entry to another script

I am trying to use a variable that I get from an entry field in tkinter to another script.
In short:
I want to use the user's input in an entry field in another script. This does not work at all.
Any help highly appreciated!
I tried so far for Script2:
from Script1 import App
test = App()
print(test.write_slogan(self))
TypeError: init() missing 1 required positional argument: 'master'
and
from Script1 import App
print(App.write_slogan())
write_slogan() missing 1 required positional argument: 'self'
and
from Script1 import App
print(App.write_slogan(self))
NameError: name 'self' is not defined
and
import Script1
print(Script1.App.a)
AttributeError: type object 'App' has no attribute 'a'
Script1:
from tkinter import *
class App:
a = 0
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.slogan = Button(frame,
text="Hello",
command=self.write_slogan)
self.slogan.pack(side=LEFT)
self.entry1 = Entry(root, width=15)
self.entry1.pack(side=LEFT)
self.importbutton = Button(frame,
text="import",
command=self.importing)
self.importbutton.pack(side=LEFT)
def write_slogan(self):
print ("Test!")
App.a = self.entry1.get()
print(App.a)
return App.a
def importing(self):
print('Import')
import Script2
if __name__ == '__main__':
root = Tk()
app = App(root)
root.mainloop()
Script2:
import Script1
You've since fixed this I believe, but I'm remarking the information for others that may find their way here: The self within a class function is the instance python will automatically pass when calling the defs. (Unless you're using classmethod or staticmethod for which are not useful in your use case and are a slightly more advanced topic)
# -- Script1 \/
class App:
def __init__(self, master):
# self here is automatically passed
# but you need to pass the "master" arg.
frame = Frame(master)
# ... Example:
self._value = 0
def set_value(self, val):
self._value = val
def get_value(self):
return self._value
# -- Use (probably in Scipt2)
master = Tk()
app = App(master)
print (app.get_value()) # Notice how we don't pass self
>>> 0
app.set_value("my new value") # This string is the "val" arg
print (app.get_value())
>>> my new value
Scipt1.App.a issue
The main issue you're having with is most likely to do with the way python manages modules. The class is writing to App.a in Script1.App but not Script2.Script1.App.a. This is expected behavior so I recommend instead trying to work with something like:
class App:
def __init__(self, master):
# ... Make your tk widgets as you already have
def set_entry_value(self, val):
self.entry1.set(val)
def get_entry_value(self):
self._last_recieved_entry_value = self.entry1.get()
# -- Script2 \/
# The if __name__ == '__main__' is not run on imported
# scripts, only the script python starts execution on
# ~$> python Script2.py
import Script1
if __name__ == '__main__':
root = Tk()
app = Script1.App(root)
# Possibly set a default?
app.set_entry_value("Default value")
root.mainloop()
# - Some time after the mainloop is over
my_lastest_value = root.get_entry_value()
This way, you're letting the local instance of objects handle their internal values. If you're looking to set a class member of an alternate module, then doing so in Script2 may work.
if __name__ == '__main__':
root = Tk()
app = Script1.App(root)
# Do something to set the entry
Script1.App.a = app.get_entry_value()
But be warned, that may not scale across multiple modules.

How do I use tklib widgets in tkinter?

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()

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

Changing the names of tkintertable rows

So I'm working on an embedded user editable table in tkinter. I'd like to give the PlateLayout class a custom set of rows instead of the default 1,2,3...
import Tkinter as tk
from tkintertable.Tables import TableCanvas
class PlateLayout:
def __init__(self, parent):
self.parent = parent
def make_frame(self):
self.the_frame = tk.Frame(self.parent)
self.the_frame.pack()
def make_table(self):
self.the_table = TableCanvas(self.the_frame, rows=8, cols=12)
self.the_table.createTableFrame()
def make_all(self):
self.make_frame()
self.make_table()
root_win = tk.Tk()
app = PlateLayout(root_win)
app.make_all()
root_win.mainloop()
I've seen screen shots of renamed columns, but haven't found a reference as to how to do this programmatically.
This is referenced from https://code.google.com/p/tkintertable/wiki/Usage#Change_column_labels
A quick change to your code will let you set custom labels;
....
def make_table(self):
self.the_table = TableCanvas(self.the_frame, rows=8, cols=12)
# Lets peek at the current labels, delete in production
print self.the_table.model.columnlabels
self.the_table.model.columnlabels['1'] = "Custom Col"
self.the_table.createTableFrame()
....

why I can't update a variable value and return self.func(*args)?

I'm sending a variable value from programa1 for a new object using :
def send_price(self):
self.pricesend = float(self.text1.get()) #this take a value from a tkinker.Entry
print(self.pricesend)
objetoprograma1.Object(self.pricesend)
the object "objetoprograma1" return a new value using:
class Object():
def __init__(self, price):
self.price_recibe = float(price)
print(self.price_recibe)
self.new_price = self.price_recibe + 10
print(self.new_price)
programa1.Aplication.recibe_newprice(self, float(self.new_price))
now I want to update the value in the principal1 tkinter.Entry called self.text1:
def recibe_newprice(self, new_price):
self.new_price = new_price
print("price new recibe" , self.new_price)
## this don't work.. this don't update or change the value in the tkinter.Entry
self.text1.delete(0, len(self.text1.get()))
self.text1.insert(self.my_main, str(self.new_price))
I have the following exception:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python34\lib\tkinter\__init__.py", line 1482, in __call__
return self.func(*args)
File "B:\MAESTRIA\PYTHON\trabajos\hello\programa1.py", line 38, in send_price
objetoprograma1.Object(self.pricesend)
File "B:\MAESTRIA\PYTHON\trabajos\hello\objetoprograma1.py", line 19, in __init__
programa1.Aplication.recibe_newprice(self, float(self.new_price))
File "B:\MAESTRIA\PYTHON\trabajos\hello\programa1.py", line 51, in recibe_newprice
self.text1.delete(self.my_main, len(self.text1.get()))
AttributeError: 'Object' object has no attribute 'text1'
the full programa1.py
# -*- coding: latin-1 -*-
import tkinter
import objetoprograma1
import time
class Aplication():
def __init__(self,my_main):
self.my_main = my_main
self.variables()
self.GUI()
def variables (self):
self.price = None
self.list = []
def GUI(self):
self.text1 = tkinter.Entry()
self.text1.insert(0, "1000")
self.text1.grid(column = 0, row = 0)
self.boton1 = tkinter.Button(self.my_main, text = "sendprice", command = self.send_price )
self.boton1.grid(column=1, row = 0)
def send_price(self):
self.pricesend = float(self.text1.get())
print(self.pricesend)
objetoprograma1.Object(self.pricesend)
def recibe_newprice(self, new_price):
self.new_price = new_price
print("price new recibe" , self.new_price)
## this don't work
self.text1.delete(0, len(self.text1.get()))
self.text1.insert(self.my_main, str(self.new_price))
if __name__ == "__main__":
root = tkinter.Tk()
#root.geometry("800x500+0+0")
root.title("titulo")
app = Aplication(my_main=root)
root.mainloop()
and objetoprograma1.py
# -*- coding: latin-1 -*-
import programa1
import tkinter
import time
class Object():
def __init__(self, price):
self.price_recibe = float(price)
print(self.price_recibe)
self.new_price = self.price_recibe + 10
print(self.new_price)
programa1.Aplication.recibe_newprice(self, float(self.new_price))
Look at your Object class and look at the exception message. You are calling the recibe_newprice method, but passing it the Object instance (Object has no text1 attribute). The recibe_newprice is written for the Aplication class and as such expects self to be an instance of the Aplication class. You seem to be mixing up what classes are for or how the self argument works.
My first tip is to name things with more descriptive names. Names like Object, Application, and Program1 don't tell the reader anything about what the purpose of those objects are.
Second, do you know the difference between classes and functions? Maybe this will help. I would code the send_price method this way:
def send_price(self, price_recibe):
pricesend = float(self.text1.get())
print(pricesend)
print(price_recibe)
new_price = price_recibe + 10
print(new_price)
self.recibe_newprice(new_price)
If this doesn't make sense why I'm doing things this way or why this might be considered better/easier than the way you did it then I suggest researching how python classes, attribute assignment, and argument passing works.

Categories

Resources