0

In my program snippet I create a python window with 2 fields and 3 buttons. The left two buttons should perform some action but instead an error is thrown:

Exception in Tkinter callback
Traceback (most recent call last):
  File "/usr/lib64/python3.4/tkinter/__init__.py", line 1538, in __call__
    return self.func(*args)
  File ".../GuiFile.py", line 11, in <lambda>
    self.F[2] = ButtonClass().make_button(stacked="left",buttontext= "Action button", buttoncommand = lambda: cf.mainButtons.doButtonAction1(self))
  File ".../ClassFile.py", line 11, in doButtonAction1
    print(gf.StartGui.F[0].textField.get("1.0","end-1c"))
AttributeError: 'NoneType' object has no attribute 'textField'

Why is dict item F[0] (created in line 9 of GuiFile.py) not recognized as Text() class with the attribute textField (defined in line 43 of GuiFile.py)?

MainProgramFile.py

#!/usr/bin/env python3

import sys
import ClassFile
import GuiFile as gf

if __name__== '__main__': 
    gf.StartGui().mainloop()

GuiFile.py

import sys
from tkinter import *
import ClassFile as cf

class StartGui(Frame):
    F = {}
    def __init__(self,parent=None):
        Frame.__init__(self, parent)
        self.F[0] = FieldTextClass().make_field(labeltext="Label of field 1", fieldtext="veld 1", fieldheight=90) 
        self.F[1] = FieldTextClass().make_field(labeltext="Label of field 2", fieldtext="veld 2")
        self.F[2] = ButtonClass().make_button(stacked="left",buttontext= "Action button", buttoncommand = lambda: cf.mainButtons.doButtonAction1(self))
        self.F[3] = ButtonClass().make_button(stacked="left", buttontext= "Exchange button", buttoncommand = lambda: cf.mainButtons.doButtonSwitchValues(self))
        self.F[4] = ButtonClass().make_button(stacked="right",buttontext= "Quit button",buttoncommand = lambda: cf.mainButtons.doButtonQuit(self))
        self.pack(expand=True, fill=BOTH, anchor="nw", side=LEFT)
        #for i in range(self.F.__len__()): print(self.F[i].__class__,self.F[i].objectType)


class ButtonClass (Frame, Button):
    objectType = "button"

    def make_button(self, parent=None, stacked="horizontal", buttontext="Button", buttonwidth=120, buttonheight=32, buttoncommand=""):
        self.buttonwidth=buttonwidth
        self.buttonheight=buttonheight
        self.buttontext=buttontext
        self.buttoncommand=buttoncommand

        if stacked=="vertical": 
            BUTTONSTACK = TOP 
        elif stacked=="right": 
            BUTTONSTACK = RIGHT 
        elif stacked=="horizontal" or stacked=="left": 
            BUTTONSTACK = LEFT
        else:
            BUTTONSTACK = LEFT 

        self.top = Frame(parent, height=self.buttonheight, width=self.buttonwidth)
        self.top.pack_propagate(False)
        self.top.pack(side=BUTTONSTACK)       
        button = Button(self.top, text=self.buttontext, command=self.buttoncommand,height=self.buttonheight, width=self.buttonwidth)
        button.pack(fill=BOTH)

class FieldTextClass(Frame,Text,Label):
    textField = None
    objectType = "inputField" 

    def make_field(self, parent=None,  labeltext="Empty", fieldtext="Empty", fieldwidth=600, fieldheight=20, labelwidth=120, labelheight=20):
        self.fieldheight=fieldheight
        self.fieldwidth=fieldwidth
        self.fieldtext=fieldtext
        self.labeltext=labeltext
        self.labelheight=labelheight
        self.labelwidth=labelwidth
        self.top = Frame(parent)

        #create the label, whith the text shifted left/top in a separate Frame
        labelFrame = Frame(self.top, height = self.labelheight,width=self.labelwidth)
        label = Label(labelFrame, text=self.labeltext, fg="black", anchor="nw") 
        label.pack(expand=True, fill=BOTH, anchor="nw", side=LEFT)
        labelFrame.pack_propagate(False) 
        labelFrame.pack(side=LEFT,  anchor="nw")

        #create the text field, packed in a separate Frame
        fieldFrame = Frame(self.top, height = self.fieldheight,width=self.fieldwidth)
        self.textField = Text(fieldFrame, fg="black",bg="white")
        self.textField.insert(INSERT,self.fieldtext)
        self.textField.pack(expand=True, fill=BOTH, side=LEFT)
        fieldFrame.pack_propagate(False)
        fieldFrame.pack(side=LEFT)

        self.top.pack(side=TOP)

ClassFile.py

import sys 
from tkinter import *
import GuiFile as gf

class mainButtons():
    def doButtonQuit(self):
        print("Quitting test via ClassFile")
        self.quit()

    def doButtonAction1(self):
        print(gf.StartGui.F[0].textField.get("1.0","end-1c"))
        print(gf.StartGui.F[1].textField.get("1.0","end-1c"))
        gf.StartGui.F[0].textField.delete("1.0","end") 
        gf.StartGui.F[0].textField.insert(INSERT, "New text")

    def doButtonSwitchValues(self):
        tmp0=gf.StartGui.F[0].textField.get("1.0","end-1c")
        tmp1=gf.StartGui.F[1].textField.get("1.0","end-1c")
        gf.StartGui.F[0].textField.delete("1.0","end") 
        gf.StartGui.F[0].textField.insert(INSERT, tmp1)
        gf.StartGui.F[1].textField.delete("1.0","end") 
        gf.StartGui.F[1].textField.insert(INSERT, tmp0)
1
  • 1
    There are a whole bunch of problems here. You need to understand the difference between classes and instances, and between class attributes and instance attributes. Commented Dec 29, 2017 at 10:24

1 Answer 1

1

When you do ButtonClass().make_button() (or FieldTextClass.make_field()) , python will return the value of the function, not the instance of the class. The function returns None, so the dictionary elements are None.

The way you're using the custom classes is very strange. Instead of creating special functions, put that code in an __init__, and use the class like you would any other class.

For example:

class ButtonClass (Frame):
    def __init__(self, parent=None, stacked="horizontal",
                 buttontext="Button", buttonwidth=120, buttonheight=32,
                 buttoncommand=""):
        Frame.__init__(self, parent)
        self.buttonwidth=buttonwidth
        ...

...
self.F[2] = ButtonClass(stacked="left",buttontext= "Action button", buttoncommand = lambda: cf.mainButtons.doButtonAction1(self))

Note: when doing it this way, you don't have to create a separate frame inside __init__ (ie: self.top), since self is itself already a Frame.

Sign up to request clarification or add additional context in comments.

2 Comments

OK, that's clear. It works. However using Frame.__init__(parent) throws an error on this line: AttributeError: 'NoneType' object has no attribute 'widgetName'. What am I missing?
@Mike: sorry, I left out a parameter to Frame.__init__. I've updated the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.