Ошибка типа: объект 'StringVar' не вызывается - PullRequest
0 голосов
/ 05 мая 2019

Я пробираюсь через книгу по обучению использованию tkinter и созданию графических интерфейсов на Python. Ссылка на книгу

Я до третьей главы и пока все хорошо. Я прочитал главу и вернулся, чтобы пройтись по коду и реконструировать их приложение. Насколько я вижу, я скопировал все слово в слово, но когда я запускаю код, я получаю следующую ошибку

self.input = input_class(self, **input_args) TypeError: 'StringVar' object is not callable

Я погуглил ошибку, и она, похоже, связана с синтаксической ошибкой или просто несовпадением имен переменных в какой-то момент. Я просмотрел то, что написал несколько раз, и начинаю думать, что не вижу дрова для деревьев. Я не вижу НИКАКОЙ разницы в коде, который я написал, к тому, что я скопировал из книги, или по любой причине, по которой он не должен работать.

Python GUI TypeError: объект 'str' не вызывается

https://errorcodespro.com/typeerror-list-object-is-not-callable/

Я скачал образец файла, который идет в комплекте с книгой, и он работает нормально (хотя, похоже, он несколько обновился, поскольку он отличается по частям от книги, но раздел, выделенный сообщением об ошибке, выглядит следующим образом: будь таким же.)

Код, который я написал, приведен ниже (если у кого-то есть книга, есть небольшие отличия, поскольку я не заполнил все элементы формы, поскольку это просто повторяющееся размещение виджетов в форме, и я не понимаю, почему их не включать приведет к ошибке, например 'self.inputs['Date'] = LabelInput(recordinfo, "Date", input_var=tk.StringVar()) self.inputs['Date'].grid(row=0, column=0)':

Я выделил строку, которая, по-видимому, вызывает ошибку, с кодом >>>>>>> здесь <<<<<<<<<< на случай, если кто-то подумает, что это проблема, которую я просто пытаюсь выделить это для вас. </p>

from datetime import datetime
import os
import csv
import tkinter as tk
from tkinter import ttk

class LabelInput (tk.Frame):

def __init__(self, parent, label='', input_class=ttk.Entry, input_var=None, input_args=None, label_args=None, **kwargs):
    super().__init__(parent, **kwargs)
    input_args = input_args or {}
    label_args = label_args or {}
    self.variable = input_var

    if input_class in (ttk.Checkbutton, ttk.Button, ttk.Radiobutton):
        input_args["text"] = label
        input_args["variable"] = input_var
    else:
        self.label = ttk.Label(self, text=label, **label_args)
        self.label.grid(row=0, column=0, sticky=(tk.W + tk.E))
        input_args["textvariable"] = input_var

    >>>>>>>self.input = input_class(self, **input_args)<<<<<<<<<<<<
    self.input.grid(row=1, column=0, sticky=(tk.W + tk.E))
    self.columnconfigure(0, weight=1)

def grid(self, sticky=(tk.E + tk.W), **kwargs):
    super().grid(sticky=sticky, **kwargs)

def get(self):
    try:
        if self.variable:
            return self.variable.get()
        elif type(self.input) == tk.Text:
            return self.input.get('1.0', tk.END)
        else:
            return self.input.get()
    except (TypeError, tk.TclError):
        return ''

def set(self, value, *args, **kwargs):
    if type(self.variable) == tk.BooleanVar:
        self.variable.set(bool(value))
    elif self.variable:
        self.variable.set(value, *args, **kwargs)
    elif type(self.input) in (ttk.Checkbutton, ttk.Radiobutton):
        if value:
            self.input.select()
        else:
            self.input.deselect()
    elif type(self.input) == tk.Text:
        self.input.delete('1.0', tk.END)
        self.input.insert('1.0', value)
    else:
        self.input.delete(0, tk.END)
        self.input.insert(0, value)

class DataRecordForm(tk.Frame):

def __init__(self, parent, *args, **kwargs):
    super().__init__(parent, *args, **kwargs)

    self.inputs = {}

    recordinfo = tk.LabelFrame(self, text="Record Information")

    self.inputs['Date'] = LabelInput(recordinfo, "Date", input_var=tk.StringVar())
    self.inputs['Date'].grid(row=0, column=0)

    self.inputs['Time'] = LabelInput (recordinfo, "Time", input_class=ttk.Combobox, input_var=tk.StringVar(), input_args={"values": ["8:00", "12:00", "16:00", "20:00"]})
    self.inputs['Time'].grid(row=0, column=1)

    self.inputs['Technician'] = LabelInput (recordinfo, "Technician", input_class=tk.StringVar())
    self.inputs['Technician'].grid(row=0, column=2)

    self.inputs['Lab'] = LabelInput(recordinfo, "Lab", input_class=ttk.Combobox, input_var=tk.StingVar(), input_args = {"values": ["A", "B", "C"]})
    self.inputs['Labs'].grid(row=1, column=0)

    environmentinfo = tk.LabelFrame(self, text="Enivronment Information")

    self.inputs['Humidity'] = LabelInput(environmentinfo, "Humidity (g/m3)", input_class=tk.Spinbox, input_var=tk.DoubleVar(), input_args={"from_": 0.5, "to": 52.0, "increment": .01})
    self.inputs['Humidity'].grid(row=0, column=0)

    self.inputs['Equipment Fault'] = LabelInput(environmentinfo, "Equipment Fault", input_class=ttk.Checkbutton, input_var=tk.BooleanVar())
    self.inputs['Equipment Fault'].grid(row=1, column=0, columnspan=3)

    self.inputs['Notes'] = LabelInput(self, "Notes", input_class=tk.Text, input_args={"width": 75, "height": 10})
    self.inputs['Notes'].grid(sticky="w", row=3, column=0)

def get(self):
    data = {}
    for key, widget in self.inputs.items():
        data[key] = widget.get()
    return data

def reset(self):
    for widget in self.inputs.values():
        widget.set('')

class Application(tk.Tk):
""" ROOT WINDOW """
def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    self.title("Data Entry")
    self.resizable(width=False, height=False)

    ttk.Label(self, text="Data Entry", font=("TkDefaultFont", 16)).grid(row=0)

    self.recordform = DataRecordForm(self)
    self.recordform.grid(row=1, padx=10)

    self.savebutton = ttk.Button(self, text="Save", command=self.on_save)
    self.savebutton.grid(sticky=tk.E, row=2, padx=10)

    self.status = tk.StringVar()
    self.statusbar = ttk.Label(self, textvariable=self.status)
    self.statusbar.grid(sticky=tk.W + tk.E, row=3, padx=10)

    def on_save(self):
        datestring = datetime.today().strftime("%Y-%m-%d")
        filename = "Data Entry_{}.csv".format(datestring)
        newfile = not os.path.exists(filename)

        data = self.recordform.get()

        with open(filename, 'a') as fh:
            csvwriter = csv.DictWriter(fh, fieldnames=data.keys())
            if newfile:
                csvwriter.writeheader()
            csvwriter.writerow(data)

if __name__ == "__main__":
app = Application()
app.mainloop()

Ниже приведен пример кода, который я скачал и который сопровождает книгу. Как указано выше, похоже, что он был обновлен, но проблемный для меня раздел выглядит так же.

from datetime import datetime
import os
import csv
import tkinter as tk
from tkinter import ttk`


class LabelInput(tk.Frame):
"""A widget containing a label and input together."""

def __init__(self, parent, label='', input_class=ttk.Entry,
             input_var=None, input_args=None, label_args=None,
             **kwargs):
    super().__init__(parent, **kwargs)
    input_args = input_args or {}
    label_args = label_args or {}
    self.variable = input_var

    if input_class in (ttk.Checkbutton, ttk.Button, ttk.Radiobutton):
        input_args["text"] = label
        input_args["variable"] = input_var
    else:
        self.label = ttk.Label(self, text=label, **label_args)
        self.label.grid(row=0, column=0, sticky=(tk.W + tk.E))
        input_args["textvariable"] = input_var

    self.input = input_class(self, **input_args)
    self.input.grid(row=1, column=0, sticky=(tk.W + tk.E))
    self.columnconfigure(0, weight=1)

def grid(self, sticky=(tk.E + tk.W), **kwargs):
    super().grid(sticky=sticky, **kwargs)

def get(self):
    if self.variable:
        return self.variable.get()
    elif type(self.input) == tk.Text:
        return self.input.get('1.0', tk.END)
    else:
        return self.input.get()

def set(self, value, *args, **kwargs):
    if type(self.variable) == tk.BooleanVar:
            self.variable.set(bool(value))
    elif self.variable:
            self.variable.set(value, *args, **kwargs)
    elif type(self.input).__name__.endswith('button'):
        if value:
            self.input.select()
        else:
            self.input.deselect()
    elif type(self.input) == tk.Text:
        self.input.delete('1.0', tk.END)
        self.input.insert('1.0', value)
    else:
        self.input.delete(0, tk.END)
        self.input.insert(0, value)


class DataRecordForm(tk.Frame):
"""The input form for our widgets"""

def __init__(self, parent, *args, **kwargs):
    super().__init__(parent, *args, **kwargs)
    # A dict to keep track of input widgets
    self.inputs = {}

    # Build the form
    # recordinfo section
    recordinfo = tk.LabelFrame(self, text="Record Information")

    # line 1
    self.inputs['Date'] = LabelInput(
        recordinfo, "Date",
        input_var=tk.StringVar()
    )
    self.inputs['Date'].grid(row=0, column=0)
    self.inputs['Time'] = LabelInput(
        recordinfo, "Time",
        input_class=ttk.Combobox,
        input_var=tk.StringVar(),
        input_args={"values": ["8:00", "12:00", "16:00", "20:00"]}
    )
    self.inputs['Time'].grid(row=0, column=1)
    self.inputs['Technician'] = LabelInput(
        recordinfo, "Technician",
        input_var=tk.StringVar()
    )
    self.inputs['Technician'].grid(row=0, column=2)

    # line 2
    self.inputs['Lab'] = LabelInput(
        recordinfo, "Lab",
        input_class=ttk.Combobox,
        input_var=tk.StringVar(),
        input_args={"values": ["A", "B", "C", "D", "E"]}
    )
    self.inputs['Lab'].grid(row=1, column=0)
    self.inputs['Plot'] = LabelInput(
        recordinfo, "Plot",
        input_class=ttk.Combobox,
        input_var=tk.IntVar(),
        input_args={"values": list(range(1, 21))}
    )
    self.inputs['Plot'].grid(row=1, column=1)
    self.inputs['Seed sample'] = LabelInput(
        recordinfo, "Seed sample",
        input_var=tk.StringVar()
    )
    self.inputs['Seed sample'].grid(row=1, column=2)

    recordinfo.grid(row=0, column=0, sticky=(tk.W + tk.E))

    # Environment Data
    environmentinfo = tk.LabelFrame(self, text="Environment Data")
    self.inputs['Humidity'] = LabelInput(
        environmentinfo, "Humidity (g/m³)",
        input_class=tk.Spinbox,
        input_var=tk.DoubleVar(),
        input_args={"from_": 0.5, "to": 52.0, "increment": .01}
    )
    self.inputs['Humidity'].grid(row=0, column=0)
    self.inputs['Light'] = LabelInput(
        environmentinfo, "Light (klx)",
        input_class=tk.Spinbox,
        input_var=tk.DoubleVar(),
        input_args={"from_": 0, "to": 100, "increment": .01}
    )
    self.inputs['Light'].grid(row=0, column=1)
    self.inputs['Temperature'] = LabelInput(
        environmentinfo, "Tenmperature (°C)",
        input_class=tk.Spinbox,
        input_var=tk.DoubleVar(),
        input_args={"from_": 4, "to": 40, "increment": .01}
    )
    self.inputs['Temperature'].grid(row=0, column=2)
    self.inputs['Equipment Fault'] = LabelInput(
        environmentinfo, "Equipment Fault",
        input_class=ttk.Checkbutton,
        input_var=tk.BooleanVar()
    )
    self.inputs['Equipment Fault'].grid(row=1, column=0, columnspan=3)
    environmentinfo.grid(row=1, column=0, sticky=(tk.W + tk.E))

    # Plant Data section
    plantinfo = tk.LabelFrame(self, text="Plant Data")

    self.inputs['Plants'] = LabelInput(
        plantinfo, "Plants",
        input_class=tk.Spinbox,
        input_var=tk.IntVar(),
        input_args={"from_": 0, "to": 20}
    )
    self.inputs['Plants'].grid(row=0, column=0)
    self.inputs['Blossoms'] = LabelInput(
        plantinfo, "Blossoms",
        input_class=tk.Spinbox,
        input_var=tk.IntVar(),
        input_args={"from_": 0, "to": 1000}
    )
    self.inputs['Blossoms'].grid(row=0, column=1)
    self.inputs['Fruit'] = LabelInput(
        plantinfo, "Fruit",
        input_class=tk.Spinbox,
        input_var=tk.IntVar(),
        input_args={"from_": 0, "to": 1000}
    )
    self.inputs['Fruit'].grid(row=0, column=2)

    # Height data
    self.inputs['Min Height'] = LabelInput(
        plantinfo, "Min Height (cm)",
        input_class=tk.Spinbox,
        input_var=tk.DoubleVar(),
        input_args={"from_": 0, "to": 1000, "increment": .01}
    )
    self.inputs['Min Height'].grid(row=1, column=0)
    self.inputs['Max Height'] = LabelInput(
        plantinfo, "Max Height (cm)",
        input_class=tk.Spinbox,
        input_var=tk.DoubleVar(),
        input_args={"from_": 0, "to": 1000, "increment": .01}
    )
    self.inputs['Max Height'].grid(row=1, column=1)
    self.inputs['Median Height'] = LabelInput(
        plantinfo, "Median Height (cm)",
        input_class=tk.Spinbox,
        input_var=tk.DoubleVar(),
        input_args={"from_": 0, "to": 1000, "increment": .01}
    )
    self.inputs['Median Height'].grid(row=1, column=2)

    plantinfo.grid(row=2, column=0, sticky=(tk.W + tk.E))

    # Notes section
    self.inputs['Notes'] = LabelInput(
        self, "Notes",
        input_class=tk.Text,
        input_args={"width": 75, "height": 10}
    )
    self.inputs['Notes'].grid(sticky=tk.W, row=3, column=0)

    # default the form
    self.reset()

def get(self):
    """Retrieve data from form as a dict"""

    # We need to retrieve the data from Tkinter variables
    # and place it in regular Python objects

    data = {}
    for key, widget in self.inputs.items():
        data[key] = widget.get()
    return data

def reset(self):
    """Resets the form entries"""

    # clear all values
    for widget in self.inputs.values():
        widget.set('')


class Application(tk.Tk):
"""Application root window"""

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)

    self.title("ABQ Data Entry Application")
    self.resizable(width=False, height=False)

    ttk.Label(
        self,
        text="ABQ Data Entry Application",
        font=("TkDefaultFont", 16)
    ).grid(row=0)

    self.recordform = DataRecordForm(self)
    self.recordform.grid(row=1, padx=10)

    self.savebutton = ttk.Button(self, text="Save", command=self.on_save)
    self.savebutton.grid(sticky=tk.E, row=2, padx=10)

    # status bar
    self.status = tk.StringVar()
    self.statusbar = ttk.Label(self, textvariable=self.status)
    self.statusbar.grid(sticky=(tk.W + tk.E), row=3, padx=10)

    self.records_saved = 0

def on_save(self):
    """Handles save button clicks"""

    # For now, we save to a hardcoded filename with a datestring.
    # If it doesnt' exist, create it,
    # otherwise just append to the existing file
    datestring = datetime.today().strftime("%Y-%m-%d")
    filename = "abq_data_record_{}.csv".format(datestring)
    newfile = not os.path.exists(filename)

    data = self.recordform.get()

    with open(filename, 'a') as fh:
        csvwriter = csv.DictWriter(fh, fieldnames=data.keys())
        if newfile:
            csvwriter.writeheader()
        csvwriter.writerow(data)

    self.records_saved += 1
    self.status.set(
        "{} records saved this session".format(self.records_saved))
    self.recordform.reset()


if __name__ == "__main__":

app = Application()
app.mainloop()

Этот код работает, но я просто не могу понять, какая разница в том, что заставляет эту версию работать, а моя не работает.

1 Ответ

0 голосов
/ 05 мая 2019

В функции DataRecordForm class __init__() вы создаете количество LabelInput экземпляров. Среди прочих:

self.inputs['Technician'] = LabelInput (recordinfo, "Technician", input_class=tk.StringVar())

Этот вызов передает аргумент input_class как tk.StringVar().

Из того, что я понимаю, вы должны передать это как input_var, а не input_class. Это также, кажется, единственная строка с этой ошибкой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...