-1

I'm generally new to python and tkinter. I've been programming maybe about a year or so, and I've just started to try to make each tkinter toplevel window its own class because I've heard that it's the right way to do it.

I'm making a program in which I have a treeview with a button to add contents to it. The button opens up a new window that allows the user to input the contents. The problem I'm having is when I have to instantiate the first window to update the treeview, it seems like it's doubling all of the widgets on the first window. This causes it to keep building up and it looks strange.

Is this normal or is there a better way to do it?

Thank you. I can post a picture or my code if necessary.

edit: shortened code

from tkinter import *
from tkinter import ttk


class MainWindow:
    Items = {'test': ['Material', '500']}
    def __init__(self, master):
        self.master = master

        self.style = ttk.Style()
        self.style.configure('TLabel', font=12)

        ttk.Label(self.master, text="Items").grid(row=0, column=0, columnspan=3)

        self.frmItems = ttk.Frame(self.master)
        self.frmItems.grid(row=1, column=0, padx=5, pady=5, columnspan=3)

        self.treeItems = ttk.Treeview(self.frmItems, columns=(0, 1, 2))
        self.treeItems.column('#0', width=0, minwidth=0)
        self.treeItems.column(1, width=80)
        self.treeItems.column(2, width=80)
        self.treeItems.heading(0, text="Name")
        self.treeItems.heading(1, text="Type")
        self.treeItems.heading(2, text="Price")
        self.treeItems.grid(row=0, column=0)

        self.itemscroll = ttk.Scrollbar(self.frmItems, command=self.treeItems.yview)
        self.itemscroll.grid(row=0, column=1, sticky='ns')
        self.treeItems.config(yscrollcommand=self.itemscroll.set)

        ttk.Button(self.master, text="New", command=self.item_input_show).grid(row=2, column=0, padx=5, pady=5,
                                                                              sticky='e')
        ttk.Button(self.master, text="Edit").grid(row=2, column=1, padx=5, pady=5)
        ttk.Button(self.master, text="Remove").grid(row=2, column=2, padx=5, pady=5, sticky='w')

    def item_input_show(self):
        ItemInput(self.master)


class ItemInput:
    def __init__(self, master):
        self.master = master
        self.MainWindow = MainWindow(master)

        self.topItemInput = Toplevel(self.master)
        self.topItemInput.title("Input Item Properties")


def main():
    root = Tk()
    MainWindow(root)
    root.mainloop()

if __name__ == "__main__":
    main()
  • 1
    It's not normal. Your code is doing something to cause this. Without us being able to see code that reproduces the problem there is no way we can help. Please read and follow the advice here: [mcve] – Bryan Oakley Jun 22 '17 at 13:57
  • I added the code. I'm sorry I wasn't able to make it shorter. I'm not sure what's causing this, but it's something I did wrong with the specific program – Russell Boeger Jun 22 '17 at 14:25
  • Please make it shorter. There's simply too much unrelated code there. For example, if the problem is with the appearance of a widget, there's no need for the code that does the actual conversion. Also, there shouldn't be the need to read data from a file; for the example you can hard-code a line or two of data. – Bryan Oakley Jun 22 '17 at 14:28
  • ok how's this. I think I limited it to the MainWindow class – Russell Boeger Jun 22 '17 at 14:38

1 Answers1

1

You are calling class MainWindow: every time you press the New Button. This is remaking all the widgets over and over. And they way you are creating the MainWindow is affecting how you can interact with MainWindow.

Change:

def main():
    root = Tk()
    MainWindow(root)
    root.mainloop()

if __name__ == "__main__":
    main()

To:

 if __name__ == "__main__":
    root = Tk()
    main = MainWindow(root)
    root.mainloop()

Once you have made this change you are able to interact with the instance attributes and methods of main

Below is a modified version of your code. You will notice when you press the button I added to the TopLevel window it will print information from an atribute and method of main variable. It will also place some text in the entry box in the MainWindow.

from tkinter import *
from tkinter import ttk

class MainWindow:
    def __init__(self, master):
        self.master = master
        self.btn = ttk.Button(self.master, text="New", command=self.item_input_show)
        self.btn.pack(side = TOP)
        self.entry = Entry(self.master)
        self.entry.pack(side = BOTTOM)
        self.numbers = 200

    def two_plus_x(self, x):
        math = 2 + x
        return math

    def item_input_show(self):
        ItemInput(self.master)


class ItemInput:
    def __init__(self, master):
        self.master = master
        self.topItemInput = Toplevel(master)
        self.btn = ttk.Button(self.topItemInput, text="Use method in MainWindow", command = self.do_something_from_main)
        self.btn.pack()

    def do_something_from_main(self):
        print(main.numbers)
        print(main.two_plus_x(10))
        main.entry.delete(0, END)
        main.entry.insert(0, "From ItemInput Class")

# notice I removed def main(): as it was preventing us from interacting with the main variable.
if __name__ == "__main__":
    root = Tk()
    main = MainWindow(root)
    root.mainloop()
Mike - SMT
  • 14,784
  • 4
  • 35
  • 79
  • I have to run methods from the MainWindow class inside of the ItemInput class – Russell Boeger Jun 22 '17 at 15:34
  • Is there a reason to use 2 separate classes? You could place the top level window in a method of MainWindow. – Mike - SMT Jun 22 '17 at 15:49
  • Yea I was considering that. I was just trying to get used to making each window its own class because people say it's cleaner and easier to work with – Russell Boeger Jun 22 '17 at 16:04
  • Well the issues you said you were having was the doubling `self.MainWindow = MainWindow(master)` was the cause of the doubling. Let me see what I can do about the other problem. – Mike - SMT Jun 22 '17 at 16:13
  • I'll probably just end up putting the other windows inside of methods like you were saying. I'm just confused with how you would run methods from another class without it doubling the widgets. Thank you for the help by the way – Russell Boeger Jun 22 '17 at 16:17
  • @RussellBoeger: I just updated my answer with an example of how you can interact with the instance of a class. Make sure you change your code to reflect the way I create the `main = MainWindow(root)` and take it out of the function you made. – Mike - SMT Jun 22 '17 at 16:22
  • @RussellBoeger Did my example help with understanding how to interact with one class instance from another class? – Mike - SMT Jun 22 '17 at 16:32
  • Yes that makes sense now. I should be able to make everything work with that change. I will experiment with it when I get home later. Thank you for your help! – Russell Boeger Jun 22 '17 at 16:46
  • Just want to say I tried incorporating your changes and it works great! Thank you again for the help! – Russell Boeger Jun 23 '17 at 01:26