Как отобразить переменную в окне tkinter?

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

После запуска скрипта открывается окно tkinter, где вы можете выбрать файл gcode (это файл с множеством строк инструкций для 3D-принтера), а затем будет найдено конкретное значение из этого файла.

Чего я хотел бы достичь, так это:

1) Отобразите это значение под кнопкой загрузки GCODE в окне tkinter с описанием/меткой.

2) Сделайте некоторые вычисления для этого значения и отобразите их также в окне tkinter.

3) Наконец, сделайте исполняемый файл этого скрипта, чтобы каждый мог его использовать, даже без установленного Python.

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

Файл Gcode для тестирования кода: ФАЙЛ GCODE.

Наконец код:

from tkinter import *
import re
from tkinter import messagebox
from tkinter import filedialog

# Here, we are creating our class, Window, and inheriting from the Frame
# class. Frame is a class from the tkinter module. (see Lib/tkinter/__init__)
class Window(Frame):

    # Define settings upon initialization. Here you can specify
    def __init__(self, master=None):

        # parameters that you want to send through the Frame class. 
        Frame.__init__(self, master)   

        #reference to the master widget, which is the tk window                 
        self.master = master

        #with that, we want to then run init_window, which doesn't yet exist
        self.init_window()

    # Load the gcode file in and extract the filament value
    def get_filament_value(self, fileName):
        with open(fileName, 'r') as f_gcode:
            data = f_gcode.read()
            re_value = re.search('filament used = .*? \(([0-9.]+)', data)

            if re_value:
                value = float(re_value.group(1))
                return('Volume of the print is {} cm3'.format(value))
            else:
                value = 0.0
                return('Filament volume was not found in {}'.format(fileName))
        return value

    def read_gcode(self):
        root.fileName = filedialog.askopenfilename(filetypes = (("GCODE files", "*.gcode"), ("All files", "*.*")))
        self.value.set = self.get_filament_value(root.fileName)
#       self.value.set('Button pressed')

    def client_exit(self):
        exit()

    def about_popup(self):
        messagebox.showinfo("About", "Small software created by Bartosz Domagalski to find used filament parameters from Sli3er generated GCODE")

    #Creation of init_window
    def init_window(self):

        # changing the title of our master widget      
        self.master.title("Filament Data")

        # allowing the widget to take the full space of the root window
        self.pack(fill=BOTH, expand=1)

        # creating a menu instance
        menu = Menu(self.master)
        self.master.config(menu=menu)

        # create the file object)
        file = Menu(menu)
        help = Menu(menu)

        # adds a command to the menu option, calling it exit, and the
        # command it runs on event is client_exit
        file.add_command(label="Exit", command=self.client_exit)
        help.add_command(label="About", command=self.about_popup)

        #added "file" to our menu
        menu.add_cascade(label="File", menu=file)
        menu.add_cascade(label="Help", menu=help)


        #Creating the labels
        self.value = StringVar()
        l_instruction = Label(self, justify=CENTER, compound=TOP, text="Load GCODE file to find volume, \n weight and price of used filament.")
        l = Label(self, justify=CENTER, compound=BOTTOM, textvariable=self.value)
#       l.place(x=85, y=45)
        l_instruction.pack()
        l.pack()

        #Creating the button
        gcodeButton = Button(self, text="Load GCODE", command=self.read_gcode)
        gcodeButton.pack()
#       gcodeButton.place(x=140, y=10)

        #status Bar
        status = Label(self, text="Waiting for file...", bd=1, relief=SUNKEN, anchor=W)
        status.pack(side=BOTTOM, fill=X)

# root window created. Here, that would be the only window, but you can later have windows within windows.
root = Tk()
root.resizable(width=False,height=False);
root.geometry("220x300")


#creation of an instance
app = Window(root)

#mainloop 
root.mainloop()

person Bart    schedule 07.01.2016    source источник


Ответы (1)


Вы должны поместить Label(s) внутрь того же Frame, что и кнопка. И когда я скопировал ваш код, мне также пришлось импортировать модуль filedalog (from tkinter import filedalog), импорт звездочки, похоже, не покрыл его.

Вы можете использовать переменную StringVar() (из tkinter) и назначить ее метке. Из этой переменной вы можете получить значения .set() и .get(). Создайте переменную и назначьте ее метке:

self.value = StringVar('', value="  Load GCODE file to find volume, \n weight and price of used filament.")
l = Label(self, textvariable=self.value)

Изменить значение переменной:

self.value.set(self.get_filament_value(root.fileName))

Метку вам нужно будет разместить так, как вы хотите, точно так же, как вы сделали с кнопкой с quitButton.place. Существуют и другие способы обработки макета, сетки и упаковки. Но, насколько я знаю, для всех элементов рекомендуется выбирать один стиль оформления.

Создание исполняемого файла, «замораживание» кода — более широкая тема. Посмотрите здесь некоторые варианты, которые вы можете изучить подробнее.


изменить:

Обновленный рабочий код. С изменением размещения виджетов разберетесь ;) Не смотрел никаких новых элементов в вашем коде.

from tkinter import *
import re
from tkinter import messagebox, filedialog


# Here, we are creating our class, Window, and inheriting from the Frame
# class. Frame is a class from the tkinter module. (see Lib/tkinter/__init__)
class Window(Frame):
    # Define settings upon initialization. Here you can specify
    def __init__(self, master=None):

        # parameters that you want to send through the Frame class.
        Frame.__init__(self, master)

        # reference to the master widget, which is the tk window
        self.master = master

        # with that, we want to then run init_window, which doesn't yet exist
        self.init_window()

    # Load the gcode file in and extract the filament value
    def get_filament_value(self, fileName):
        with open(fileName, 'r') as f_gcode:
            data = f_gcode.read()
            re_value = re.search('filament used = .*? \(([0-9.]+)', data)

            if re_value:
                value = float(re_value.group(1))
                return 'Volume of the print is {} cm3'.format(value)
            else:
                return 'Filament volume was not found in {}'.format(fileName)

    def read_gcode(self):
        root.fileName = filedialog.askopenfilename(filetypes=(("GCODE files", "*.gcode"), ("All files", "*.*")))
        self.value.set(self.get_filament_value(root.fileName))

    def client_exit(self):
        exit()

    def about_popup(self):
        messagebox.showinfo("About",
                            "Small software created by Bartosz Domagalski to find used filament parameters from Sli3er generated GCODE")

    # Creation of init_window
    def init_window(self):

        # changing the title of our master widget
        self.master.title("Filament Data")

        # allowing the widget to take the full space of the root window
        self.pack(fill=BOTH, expand=1)

        # creating a menu instance
        menu = Menu(self.master)
        self.master.config(menu=menu)

        # create the file object)
        file = Menu(menu)
        help = Menu(menu)

        # adds a command to the menu option, calling it exit, and the
        # command it runs on event is client_exit
        file.add_command(label="Exit", command=self.client_exit)
        help.add_command(label="About", command=self.about_popup)

        # added "file" to our menu
        menu.add_cascade(label="File", menu=file)
        menu.add_cascade(label="Help", menu=help)

        # Creating the labels
        self.value = StringVar()
        l_instruction = Label(self, justify=CENTER, compound=TOP,
                              text="  Load GCODE file to find volume, \n weight and price of used filament.")
        l = Label(self, justify=CENTER, compound=BOTTOM, textvariable=self.value)
        #       l.place(x=85, y=45)
        l_instruction.pack()
        l.pack()

        l_instruction.pack()
        self.value = StringVar()
        l = Label(self, textvariable=self.value)
        l.pack()

        # Creating the button
        gcodeButton = Button(self, text="Load GCODE", command=self.read_gcode)
        gcodeButton.pack()
        #       gcodeButton.place(x=140, y=10)

        # status Bar
        status = Label(self, text="Waiting for file...", bd=1, relief=SUNKEN, anchor=W)
        status.pack(side=BOTTOM, fill=X)


# root window created. Here, that would be the only window, but you can later have windows within windows.
root = Tk()
root.resizable(width=False, height=False);
# root.geometry("400x300")


# creation of an instance
app = Window(root)

# mainloop
root.mainloop()
person oystein-hr    schedule 08.01.2016
comment
Привет, спасибо за ответ, я попытался реализовать его, как вы предлагаете, но без хороших результатов (я обновил код в основном вопросе). Я не уверен в этом модуле filedalog, так как при компиляции у меня нет подсказок об этом. Спасибо также за ссылку, я посмотрю ее после того, как код будет готов. - person Bart; 08.01.2016
comment
Я заметил, что у меня была опечатка на этикетке, я использовал root в качестве родителя вместо себя. Исправлено. Также добавлено, как это выглядит при размещении внутри вашего кода. - person oystein-hr; 08.01.2016
comment
Хорошо, я постепенно начинаю понимать это. Таким образом, ваш пример с жестко закодированным сообщением о нажатии кнопки работает. Но все же я не могу отобразить свое значение, найденное в файле gcode. Кроме того, я понял, что таким образом новое сообщение (кнопка нажата в вашем примере) отображается поверх метки, и эта метка исчезает. Чего я хотел добиться, так это того, что после загрузки gcode значение отображается в окне tkinter под кнопкой, а не вместо метки. Надеюсь, мое объяснение достаточно понятно ;) - person Bart; 08.01.2016
comment
Я обновил код до последней версии, также вы можете скачать файл gcode для тестирования. Спасибо за помощь! - person Bart; 08.01.2016
comment
Чтобы уточнить еще больше, когда вы используете кнопку и загружаете файл gcode в оболочку, вы получаете следующее сообщение: Volume of the print is 19.5 cm3 и я хотел бы, чтобы это сообщение отображалось в окне tkinter как новый элемент под кнопкой загрузки GCODE. - person Bart; 08.01.2016
comment
Вам нужно будет добавить еще один Label и присвоить ему StringVar значение textvariable. И, как уже упоминалось, чтобы изменить значение StringVar, вы должны использовать .set(), как в жестко закодированном примере 'Button Pressed'. И чтобы вернуть это сообщение, вам действительно нужно return его из вашей функции, а не просто значение. - person oystein-hr; 08.01.2016
comment
И если вы не хотите, чтобы существующий Label менял свой текст, вам не нужно, чтобы он имел StringVar. - person oystein-hr; 08.01.2016
comment
Теперь после последних изменений я получаю ошибку errorTraceback (most recent call last): File "/Users/domagalski/Desktop/test.py", line 98, in <module> app = Window(root) File "/Users/domagalski/Desktop/test.py", line 20, in __init__ self.init_window() File "/Users/domagalski/Desktop/test.py", line 77, in init_window l = Label(self, justify=CENTER, compound=BOTTOM, textvariable=self.value) AttributeError: 'Window' object has no attribute 'value'. Спасибо за терпение ;) - person Bart; 08.01.2016
comment
Похоже, вы не определили self.value перед тем, как использовать его как textvariable=self.value. - person oystein-hr; 08.01.2016
comment
Но разве это не определено в `def get_filament_value(self, fileName)` или мне нужно переопределить его и в метке? - person Bart; 08.01.2016
comment
Вам нужно определить/создать его перед использованием, в первый раз вы используете его при назначении его Label. Установка переменной — это не то же самое, что ее определение/создание. self.value = StringVar() создает StringVar() под названием self.value. Позже вы можете установить для него значение с помощью self.value.set('Some string value'). - person oystein-hr; 08.01.2016
comment
Хорошо, моя плохая ошибка была связана с тем, что я позже написал значение с большой буквы вместо нижнего регистра. Во всяком случае, значение объема нити, которое я ищу, не отображается в окне tkinter, я снова загрузил окончательную версию кода. - person Bart; 08.01.2016
comment
Значение, которое вы хотите установить, должно быть в круглых скобках, а не после =. Проверьте это в длинном коде, который я обновил ранее;) - person oystein-hr; 08.01.2016
comment
Да, омг, глупая ошибка, но я писал сам, а не копировал, и не заметил этого изменения. Большое вам спасибо, теперь это работает почти так, как хотелось ;) Хотя я все еще могу беспокоить вас по поводу других значений, но я надеюсь решить это самостоятельно. - person Bart; 08.01.2016
comment
Я уверен, что вы сможете понять это, иногда бывает полезно просто написать небольшую часть кода отдельно. Вместо того, чтобы пытаться реализовать что-то новое напрямую. А когда понимаешь, как это работает, проще вставлять код в существующий проект. Удачи :) - person oystein-hr; 08.01.2016
comment
Это хороший совет, иначе многое может пойти не так, еще раз спасибо за вашу помощь! :D - person Bart; 08.01.2016