4
\$\begingroup\$

I'm in the process of making my first GUI with Python. I'm trying to make something for work, so I've replaced all the info with fruits. It just lets the user tick some boxes, then creates a word doc

It's quite messy, and the variable names will be renamed later. But what I wanted to ask about was the functions I've defined. I wasn't sure whether to define the ship_collect and ship_click function within the FruitClass class, or outside of it.

I figured it made more sense to put it outside, that way the function only gets defined once. Whereas if I defined it inside in the class, it would be defined every time an instance gets created, taking up more memory - is this correct or am I totally wrong here?

Additionally, I thought the functions would have to be defined before the class, since some the widgets defined in the class use them for commands...

If the functions are defined outside the class, would it make more sense to create the widgets (for example .ent_qty) within the functions, or within the class? Again I thought outside made more sense, as it seemed more sensible to define these widgets only if the original checkbox gets ticked.

import tkinter as tk
from tkinter import ttk

from docx import Document

def ship_collect(fruit_object):
    '''Function to make additional checkbuttons appear if the first column boxes get ticked.'''
    if fruit_object.chkVal.get() == True:
        # quantity
        fruit_object.lbl_qty = tk.Label(master=fruit_object.frm, text="Quantity: ", bg="red")
        fruit_object.ent_qty = tk.Entry(master=fruit_object.frm, width=10, bg="blue")
        fruit_object.lbl_qty.grid(row=1, column=0, sticky='e')
        fruit_object.ent_qty.grid(row=1, column=1, sticky='w', padx=(0,20))

        # shipping/collect
        fruit_object.chkVal_ship = tk.BooleanVar()
        fruit_object.chkVal_ship.set(False)
        fruit_object.chk_ship = tk.Checkbutton(master=fruit_object.frm, text="ship", variable=fruit_object.chkVal_ship, bg="green", command=lambda fruit_object=fruit_object:ship_clicked(fruit_object))
        fruit_object.chk_ship.grid(row=0,column=2, sticky='w')

        fruit_object.chkVal_collect = tk.BooleanVar()
        fruit_object.chkVal_collect.set(False)
        fruit_object.chk_collect = tk.Checkbutton(master=fruit_object.frm, text="collect", variable=fruit_object.chkVal_collect, bg="yellow")
        fruit_object.chk_collect.grid(row=1,column=2, sticky='w')

    else:
        fruit_object.chk_ship.grid_forget()
        fruit_object.chk_collect.grid_forget()
        fruit_object.lbl_qty.grid_forget()
        fruit_object.ent_qty.grid_forget()
        fruit_object.combo_ship.grid_forget()
        


def ship_clicked(fruit_object):
    ''' Create drop-down menus if ship button gets clicked'''
    if fruit_object.chkVal_ship.get() == True:
        fruit_object.combo_ship = ttk.Combobox(master=fruit_object.frm, 
                    values=[
                            "Next day - £5", 
                            "Standard - £2"],
                    state="readonly")

        fruit_object.combo_ship.grid(row=0, column=3)
        fruit_object.combo_ship.current(0)

        
class FruitClass:
    def __init__(self, fruitname):
        self.fruitname = fruitname
        self.chkVal = tk.BooleanVar()
        self.chkVal.set(False)
        self.frm = tk.Frame(master=frm_fruits)
        self.chk = tk.Checkbutton(master=self.frm, text=self.fruitname, variable=self.chkVal, command = lambda self=self: ship_collect(self))
        self.chk.grid(row=0,column=0, sticky='w')
        self.frm.columnconfigure(0, weight=1, minsize=80)


window = tk.Tk()

lbl_fruits = tk.Label(master=window, text="Select fruits:")
lbl_fruits.grid(row=0, sticky="w")

frm_fruits = tk.Frame(master=window)
frm_fruits.grid(row=1, column=0)

# use a list of fruit names to create a dict of objects
objectNames = ('apple','banana','orange','pear','mango')
objectDictionary = {}
for name in objectNames:
    objectDictionary[name] = FruitClass(fruitname=name)

# append each fruit frame (containing checkboxes) to the window
for i, k in enumerate(objectDictionary):
    objectDictionary[k].frm.grid(row=i, column=0, sticky='w', pady=(0,10))



def onSubmit():
    myDoc = Document()

    myTable = myDoc.add_table(1,3)

    heading_cells = myTable.rows[0].cells
    heading_cells[0].text = 'Fruit'
    heading_cells[1].text = 'Quantity'
    heading_cells[2].text = 'Delivery'

    # check which boxes were ticked, add these to the table in word
    for name, obj in objectDictionary.items():
        if obj.chkVal.get() == True:
            cells = myTable.add_row().cells
            cells[0].text = name
            cells[1].text = str(obj.ent_qty.get())

            if obj.chkVal_ship.get() == True:
                cells[2].text = obj.combo_ship.get()

    myTable.style = 'LightShading-Accent1'
    myDoc.save('shopping.docx')

btn_submit = tk.Button(
        master=window,
        text="Submit",
        command=onSubmit
)

btn_submit.grid(row=3, column=3)

window.mainloop()
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Have you tried to measure the performance difference between defining it inside the class versus outside the class? \$\endgroup\$ – G. Sliepen Sep 7 '20 at 20:16
  • \$\begingroup\$ No, not entirely sure how to do that, still just a beginner. I just thought there might be a standard for where to define these types of functions \$\endgroup\$ – mmmmm Sep 8 '20 at 14:54
  • \$\begingroup\$ For timing code performance I would recommend this subject: Python Timer Functions: Three Ways to Monitor Your Code. Armed with this knowledge you will be able to easily measure performance of chosen portions of code. \$\endgroup\$ – Anonymous Sep 8 '20 at 19:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.