Python Tkinter Text Widget .get method error - python

I'm very new to Python, sort of following Dive into Python 2 and wanted to dabble with some Tkinter programming. I've tried to make a little program that takes 3 sets of words and makes combinations of each word in the 3 sets to make keywords for websites. When I run the script, the GUI appears as expected, but I get the following error when I click on the Create Combinations button
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python2.6/lib-tk/Tkinter.py", line 1413, in __call__
return self.func(*args)
File "combomaker.py", line 34, in makeCombinations
primaryraw = primaryKeyWordsBox.get()
AttributeError: 'NoneType' object has no attribute 'get'
The code I'm trying to fix
#!/usr/bin/env python
from Tkinter import *
primaryKeyWordsLabel = None
primaryKeyWordsBox = None
secondaryKeyWordsLabel = None
secondaryKeyWordsBox = None
tertiaryKeyWordsLabel = None
tertiaryKeyWordsBox = None
class Application(Frame):
def __init__(self, master=None, padx = 10, pady= 10):
Frame.__init__(self, master)
self.grid()
self.createWidgets()
def createWidgets(self):
self.primaryKeyWordsLabel = LabelFrame(text="Primary Key Words", padx=10, pady=10)
self.primaryKeyWordsLabel.grid()
self.primaryKeyWordsBox = Text(primaryKeyWordsLabel, autoseparators=True, height=5, undo=True)
self.primaryKeyWordsBox.grid()
self.secondaryKeyWordsLabel = LabelFrame(text="Secondary Key Words", padx=10, pady=10)
self.secondaryKeyWordsLabel.grid()
self.secondaryKeyWordsBox = Text(secondaryKeyWordsLabel, autoseparators=True, height=5, undo=True)
self.secondaryKeyWordsBox.grid()
self.tertiaryKeyWordsLabel = LabelFrame(text="Tertiary Key Words", padx=10, pady=10)
self.tertiaryKeyWordsLabel.grid()
self.tertiaryKeyWordsBox = Text(tertiaryKeyWordsLabel, autoseparators=True, height=5, undo=True)
self.tertiaryKeyWordsBox.grid()
self.goButton = Button(text="Create Combinations", command=makeCombinations)
self.goButton.grid()
def makeCombinations():
primaryraw = primaryKeyWordsBox.get()
primary = primaryraw.split(', ')
secondaryraw = secondaryKeyWordsBox.get()
secondary = secondaryraw.split(', ')
tertiaryraw = tertiaryKeyWordsBox.get()
tertiary = tertiaryraw.split(', ')
output=[]
filename = "output.txt"
for i in range(len(primary)):
for j in range(len(secondary)):
for k in range(len(tertiary)):
rawcombo=str(primary[i])+" "+str(secondary[j])+" "+str(tertiary[k])
output.append(rawcombo)
FILE = open(filename, w)
for combo in output:
FILE.write(combo+",\n")
FILE.close()
app = Application()
app.master.title("Keyword Generator")
app.mainloop()
I may have thrown myself into GUI programming too fast, this is my first attempt at any GUI work but not my first time programming.
Many thanks in advance :)

You're trying to access
primaryKeyWordsBox
outside the class Application in the (free) function makeCombinations(..).
You could make makeCombinations(..) a member of Application by indenting it like the other member functions and add the self argument:
def makeCombinations(self):
You should modify the binding of the makeCombinations(..) to the button:
...,command = self.makeCombinations)
Then you'll have to add self. when you're trying to access the members of this class:
primaryraw = self.primaryKeyWordsBox.get(1.0,END)
...
secondaryraw = self.secondaryKeyWordsBox.get(1.0,END)
...
tertiaryraw = self.tertiaryKeyWordsBox.get(1.0,END)
(I found the examples how to use get here).
If you want to open a file for writing, you should do:
FILE = open(filename, "w")
instead of
FILE = open(filename, w)

Related

Why can't the Tkinter Button configuration of a Class be modified?

I am constructing a simple hex editor in Python using Tkinter. I want to be able to indicate selection of a value (the "0000"s) in the GUI by changing the colors of a pressed button.
To implement this, I instantiated each button as a class inside a for loop and put each instance into a list to reference them later in the code. When I attempt to alter the Button API properties of the classes in the while loop, the IDLE prints an error when the hex editor is closed:
Traceback (most recent call last):
File "C:/Users/Administrator/Desktop/Files/Python/hex editor.py", line 64, in <module>
ml.button.configure(bg='#00FF00', fg='#000000')
File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 1479, in configure
return self._configure('configure', cnf, kw)
File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 1470, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".!frame.!frame.!button"
The intended behavior of the button is that when any "0000" is clicked, the selected button will become green and remain so until another button is pressed. (i.e the pressed button will turn green and the previously selected button will turn black)
Source Code:
from tkinter import *
selected_i = 0
selected_j = 0
data_w = 16
address_w = 8
class mem_location:
def __init__(self, row, column, data):
self.row = row
self.column = column
self.data = data
self.button = Button(subframe,
text=self.data,
font=('Consolas',9),
bd=0,
command=self.select)
self.button.pack(side=LEFT)
self.button.configure(bg='#000000', fg='#00FF00')
def select(self):
selected_i = self.row
selected_j = self.column
root = Tk()
root.configure(bg="black")
root.title('Hex Editor')
frame = Frame(root,
padx=30,
pady=10,
bg='black')
frame.pack()
ml_list = []
for i in range((1<<address_w)>>4):
subframe = Frame(frame,
padx=10,
bg='black')
subframe.pack()
addr_label = Label(subframe,
text=hex(i<<4)+" ",
bg='black',
fg='#00FF00',
width=5)
addr_label.pack(side = LEFT)
for j in range(16):
ml_list.append(mem_location(i, j, '0000'))
root.mainloop()
while True:
for ml in ml_list:
if selected_i == ml.row and selected_j == ml.column:
ml.button.configure(bg='#00FF00', fg='#000000')
else:
ml.button.configure(bg='#000000', fg='#00FF00')
I am currently in the process of learning about Tkinter. Why can't I modify the class's Button configuration and how can this be fixed?
The code after root.mainloop will not run until the window is not closed, so you are trying to modify the buttons after the window has been closed.
Instead, you could move the code in the submit method like this:
from tkinter import *
# You don’t need selected_i and selected_j anymore
data_w = 16
address_w = 8
class MemLocation:
def __init__(self, data):
# Same code without self.row and self.column
def select(self):
for ml in [ml_ for ml_ in ml_list if ml_ is not self]: # All elements except this one
ml.button.config(bg='#000', fg='#0F0')
self.button.config(bg='#0F0', fg='#000')
# Same code here
root.mainloop()
#END
Note that I renamed the class according to PEP 8 but there are many other improvement possible like making MemLocation inherit from Frame or Button, or adding event listeners to make the code cleaner.

Tkinter TreeView binding left click to the current tree and selected item

I am trying to bind this function self.copyTextToClipboard(self,t) to multiple different trees to make it more flexible (please see binding below).
from tkinter.ttk import Treeview
from tkinter import *
class App:
def __init__(self, master):
self.master = master
frame = Frame(master)
master.geometry("{}x{}".format(master.winfo_screenwidth() - 100, master.winfo_screenheight() - 100))
master.resizable(False, False)
self.leftFrame = Frame(master, bg="#DADADA", width=375, relief=SUNKEN)
self.leftFrame.pack_propagate(0)
self.leftFrame.pack(side=LEFT, fill=Y, padx=1)
# This table (TreeView) will display the partitions in the tab
self.partitionsOpenDiskTree = Treeview(self.leftFrame, columns=("#"), show="headings", selectmode="browse", height=23)
yscrollB = Scrollbar(self.leftFrame)
yscrollB.pack(side=RIGHT, fill=Y)
self.partitionsOpenDiskTree.column("#", width=50)
self.partitionsOpenDiskTree.heading("#", text="#")
self.partitionsOpenDiskTree.configure(yscrollcommand=yscrollB.set)
# Bind left click on text widget to copy_text_to_clipboard() function
self.partitionsOpenDiskTree.bind("<ButtonRelease-1>", lambda t=self.partitionsOpenDiskTree: self.copyTextToClipboard(self,t))
# Adding the entries to the TreeView
for i in range(3):
self.partitionsOpenDiskTree.insert("", "end", i, values=(i), tags=str(i))
self.partitionsOpenDiskTree.pack(anchor=NW, fill=Y)
#todo: figure out where this is getting called and put in tree
def copyTextToClipboard(self, tree, event=None):
print(type(tree))
# triggered off left button click on text_field
root.clipboard_clear() # clear clipboard contents
textList = tree.item(tree.focus())["values"]
line = ""
for text in textList:
if line != "":
line += ", " + str(text)
else:
line += str(text)
root.clipboard_append(line) # append new value to clipbaord
root = Tk()
app = App(root)
root.mainloop()
However, I am unable to bind it to a TreeView object it seems; when I run the code, I get:
Exception in Tkinter callback
<class '__main__.App'>
Traceback (most recent call last):
File "C:\Users\user1\Anaconda3\lib\tkinter\__init__.py", line 1699, in __call__
return self.func(*args)
File "C:/Users/user1/main_merged.py", line 56, in <lambda>
lambda t=self.partitionsOpenDiskTree: self.copyTextToClipboard(self,t))
File "C:/Users/user1/main_merged.py", line 70, in copyTextToClipboard
textList = tree.item(tree.focus())["values"]
AttributeError: 'App' object has no attribute 'item'
If I try to print out tree type, I get that it's a not a TreeView object. Any ideas on how I can get a TreeView object, so that I can figure out which item was selected?
Thanks!
-FF
So, apparently, taking out the self call seemed to work:
from tkinter.ttk import Treeview
from tkinter import *
class App:
def __init__(self, master):
self.master = master
frame = Frame(master)
master.geometry("{}x{}".format(master.winfo_screenwidth() - 100, master.winfo_screenheight() - 100))
master.resizable(False, False)
self.leftFrame = Frame(master, bg="#DADADA", width=375, relief=SUNKEN)
self.leftFrame.pack_propagate(0)
self.leftFrame.pack(side=LEFT, fill=Y, padx=1)
# This table (TreeView) will display the partitions in the tab
self.partitionsOpenDiskTree = Treeview(self.leftFrame, columns=("#"), show="headings", selectmode="browse", height=23)
yscrollB = Scrollbar(self.leftFrame)
yscrollB.pack(side=RIGHT, fill=Y)
self.partitionsOpenDiskTree.column("#", width=50)
self.partitionsOpenDiskTree.heading("#", text="#")
self.partitionsOpenDiskTree.configure(yscrollcommand=yscrollB.set)
# Bind left click on text widget to copy_text_to_clipboard() function
self.partitionsOpenDiskTree.bind("<ButtonRelease-1>", lambda event, t=self.partitionsOpenDiskTree: self.copyTextToClipboard(t))
# Adding the entries to the TreeView
for i in range(3):
self.partitionsOpenDiskTree.insert("", "end", i, values=(i), tags=str(i))
self.partitionsOpenDiskTree.pack(anchor=NW, fill=Y)
#todo: figure out where this is getting called and put in tree
def copyTextToClipboard(self, tree, event=None):
print(type(tree))
# print(type(tree.partitionsOpenDiskTree))
# triggered off left button click on text_field
root.clipboard_clear() # clear clipboard contents
textList = tree.item(tree.focus())["values"]
line = ""
for text in textList:
if line != "":
line += ", " + str(text)
else:
line += str(text)
root.clipboard_append(line) # append new value to clipbaord
print(line)
root = Tk()
app = App(root)
root.mainloop()
Output:
0
When you use bind, the callback function must have an event as its first argument, custom arguments should be put after. But as your callback does not need the event parameters, you may mask it with your lambda. So you have to change both the binding and the def of your callback:
self.partitionsOpenDiskTree.bind("<ButtonRelease-1>", lambda event, t=self.partitionsOpenDiskTree: self.copyTextToClipboard(t))
...
def copyTextToClipboard(self, tree):
should solve the problem

Why will math.floor not work?

The goal of this program is to generate a sine wave using a 12 bit DAC. The code is as follows:
from Tkinter import *
import smbus, time, math, random
bus = smbus.SMBus(1)
address = 0x60
t=time.time()
class RPiRFSigGen:
# Build Graphical User Interface
def __init__(self, master):
self.start
frame = Frame(master, bd=10)
frame.pack(fill=BOTH,expand=1)
# set output frequency
frequencylabel = Label(frame, text='Frequency (Hz)', pady=10)
frequencylabel.grid(row=0, column=0)
self.frequency = StringVar()
frequencyentry = Entry(frame, textvariable=self.frequency, width=10)
frequencyentry.grid(row=0, column=1)
# Start button
startbutton = Button(frame, text='Enter', command=self.start)
startbutton.grid(row=1, column=0)
def start(self):
#self.low_freq=IntVar
low_freq = float(self.frequency.get())
out = 4095/2 + (math.sin(2*math.pi*low_freq*t))
#out = math.floor(out)
int(math.floor(out))
print (out)
bus.write_byte_data(address,0,out)
sendFrequency(low_freq)
# Assign TK to root
root = Tk()
# Set main window title
root.wm_title('DAC Controller')
root.geometry('250x150+650+250')
# Create instance of class RPiRFSigGen
app = RPiRFSigGen(root)
# Start main loop and wait for input from GUI
root.mainloop()
When I run the program I receive the following error after the value "out" is printed:
2046.18787764
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1437, in __call__
return self.func(*args)
File "/home/pi/DAC Controller.py", line 40, in start
bus.write_byte_data(address,0,out)
TypeError: integer argument expected, got float
It would appear that int(math.floor(out)) is not converting out to an integer because "out" is being printed as float still. Any suggestions?
int(math.floor(out))
This will create an integer version of out, but you aren't assigning it to anything, so it just gets discarded. If you want the changes to be reflected in the value of out, try:
out = int(math.floor(out))

Global Variable 'getInfo' is undefined

I'm working on developing a GUI for a project and once I put all of this into a class, it is returning saying
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter\__init__.py", line 1475, in __call__
return self.func(*args)
File "c:\users\ryan\documents\visual studio 2015\Projects\Group_3_Project\Group_3_Project\Group_3_Project.py", line 30, in <lambda>
b1 = Button(root, text = 'Submit', command = (lambda e = ents: getInfo(e)))
NameError: global name 'getInfo' is not defined
Here is my code so far:
from tkinter import*
class GUI:
fields = 'Last Name', 'First Name', 'Field', 'Phone Number', 'Office number'
def getInfo(entries):
for entry in entries:
field = entry[0]
text = entry[1].get()
print('%s: "%s"' % (field, text))
def makeForm(root, fields):
entries = []
for field in fields:
row = Frame(root)
lab = Label(row, width = 15, text = field, anchor = 'w')
ent = Entry(row)
row.pack(side = TOP, fill = X, padx = 5, pady = 5)
lab.pack(side = LEFT)
ent.pack(side = RIGHT, expand = YES, fill = X)
entries.append((field, ent))
return entries
if __name__ == '__main__':
root = Tk()
root.wm_title("HoursWizard")
ents = makeForm(root, fields)
root.bind('<Return>', (lambda event, e = ents: getInfo(e)))
b1 = Button(root, text = 'Submit', command = (lambda e = ents: getInfo(e)))
b2 = Button(root, text = 'Quit', command = root.quit)
b1.pack(side = LEFT, padx = 5, pady = 5)
b2.pack(side = LEFT, padx = 5, pady = 5)
root.mainloop()
I have no idea what is going on and why it isn't working correctly. I'm sure it is an easy fix and I'm just missing something. Any help is appreciated. Thanks!
You should check the official Python tutorial and look at the section on classes. Basically, your scoping and namespaces are not what you think they are. Every class method (unless it's been designated as static) is first passed the instance itself, usually denoted with self. You would then refer to instance attributes with self.myattribute. In getInfo, for example, what you call entries isn't entries at all, but rather the instance of the GUI class that has been created.
I highly recommend you look up some tutorials for how to make an OO Tkinter app. It generally goes like this:
class App:
def __init__(self, parent):
self.parent = parent
self.parent.after(5000, self.other_method) # just a demo
# create buttons, lay out geometry, etc.
def other_method(self):
self.do_print()
def do_print(self):
print('hello world')
root = Tk()
app = App(root)
root.mainloop()

I can't figure out this Tkinter error

I'm using Python's Tkinter to create a GUI for a project i'm working on.
When I try to run part of the code though, I get this error:
Traceback (most recent call last):
File "calculator.py", line 59, in <module>
app = Application()
File "calculator.py", line 28, in __init__
self.create_widgets()
File "calculator.py", line 45, in create_widgets
self.special_chars.create_button(char, self.add_char_event(special_characters[char]))
File "calculator.py", line 20, in create_button
self.button_list += Button(self, text = txt, command = fcn)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/
lib-tk/Tkinter.py", line 1206, in cget
TypeError: cannot concatenate 'str' and 'int' objects
The problem is that I can't find the file that the error message references; my python2.7/lib-tk folder only contains complied versions (.pyo and .pyc) of Tkinter.
Is there a way to figure out what's going wrong?
Here's the source of calculator.py
from Tkinter import *
from exp import full_eval
from maths import special_characters
class special_char_frame(LabelFrame):
def __init__(self, master = None, text = 'Special Characters'):
LabelFrame.__init__(self, master)
self.grid()
self.button_list = []
def create_button(self, txt, fcn):
self.button_list += Button(self, text = txt, command = fcn)
self.button_list[-1].grid(row = 0)
class Application(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
## equation entry pane
self.text_entry = Entry(self, width = 100)
self.text_entry.grid(row = 0, column = 0)
self.text_entry.bind('<KeyPress-Return>', self.calculate)
## result pane
self.result = StringVar()
self.result_label = Label(self, textvariable = self.result, wraplength = 815, justify = LEFT)
self.result_label.grid(row = 1, column = 0, columnspan = 2, sticky = W)
self.result.set('')
## calculate button
self.calc_button = Button(self, text = 'Calculate', command = self.calculate)
self.calc_button.grid(row = 0, column = 1)
## special character button pane
self.special_chars = special_char_frame(self)
for char in special_characters:
self.special_chars.create_button(char, self.add_char_event(special_characters[char]))
self.special_chars.grid(column = 0, columnspan = 2, row = 2)
def calculate(self, event = None):
try:
self.result.set(full_eval(self.text_entry.get()))
except Exception as error:
raise
#self.result.set(str(error))
self.text_entry.select_range(0, END)
def add_char_event(self, char):
def add_char(self = self, event = None):
self.text_entry.insert(INSERT, char)
return add_char
app = Application()
app.master.title('Calculator')
app.mainloop()
full_eval is a function for evaluating mathematical expressions.
special_characters is a dict containing special characters and their explanations. For now it's just special_characters = {'imaginary unit' : u'\u2148'}
Ok, so I missed this the first time, but the issue is actually that you are trying to add a Button to a list:
self.button_list += Button(self, text = txt, command = fcn)
If you simply wrap the Button in brackets, the error goes away (which makes sense because you are supposed to be able to add two lists):
self.button_list += [Button(self, text = txt, command = fcn)]
ORIGINAL ATTEMPT
My guess:
special_characters is a dictionary. It has key-value mappings where the values are ints. Then, when used in self.text_entry.insert(INSERT, char), text_entry is trying to insert an int into a str and causing the above error. The simple solution: wrap char with str in add_char.
def add_char_event(self, char):
def add_char(self = self, event = None):
self.text_entry.insert(INSERT, str(char))
return add_char
Your other option is to wrap str around the special_characters lookup:
for char in special_characters:
self.special_chars.create_button(char,
self.add_char_event(str(special_characters[char])))

Categories

Resources