0

I'm building my first python tkinter gui, part of which is an editable table. The code is based on this post where Bryan Oakley's answer is a fantastic resource: SQL Query results in tkinter

I also found this reference useful for editable guis based on SQL: https://www.ssucet.org/old/pluginfile.php/778/mod_resource/content/2/Python%20Database%20Lab.pdf

In Bryan Oakely's solution, I think the data is a tuple of tuples. For example,

data = (
(45417, "rodringof", "CSP L2 Review", 0.000394, "2014-12-19 
10:08:12", "2014-12-19 10:08:12"),...

The data I obtain from my SQL connection is a list of tuples. I converted it to a tuple of tuples - which solved errors I was receiving and enabled the GUI to be populated with the data. I'd be grateful for comments describing a better way to restructure SQL data for tkinter equivalent to my 'big_tuple' creation in the code? (Note, this is reading an Access .mdb file which is the only type of SQL connection I can make - perhaps this is a specific issue I've had to overcome). Full code is posted at the end of the question.

More importantly, here is the real question:

Image shows snippet making 'big_tuple' and discrepancy between GUI and console output enter image description here

You can see here that the data contained datetime objects which were re-formatted using strftime in the 'for' loop that assigns tuple of tuples 'data1'. However, according to the GUI, the floats in 'data1' were not re-formatted in this loop . If 'big_tuple' is printed to the console it shows floats were successfully re-formatted, but in the GUI they remain un-formatted. Please can you tell me why do the floats appear formatted in the print output but not in the GIU? Here is the full code:

#import tkinter as tk
#import pyodbc
import datetime

# db = pyodbc.connect('DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};\
#                     UID=admin;UserCommitSync=Yes;Threads=3;SafeTransactions=0;\
#                     PageTimeout=5;MaxScanRows=8;MaxBufferSize=2048;FIL={MS Access};DriverId=25;\
#                     DefaultDir=C:\\Tim local ;DBQ=C:\\Tim local\\copy2MobileBases.mdb')

# s= 'SELECT * FROM MASTER1' 
# cursor = db.cursor()
# cursor.execute(s)
# data = cursor.fetchall() # our SQL data 

data = [(1, 'AAA001', '07770000000', datetime.datetime(2016, 2, 16, 0, 0), datetime.datetime(2018, 2, 15, 0, 0), 10.0, 0.0, 0.0, 0.0),
        (2, 'AAA001', '07770000001', datetime.datetime(2016, 2, 26, 0, 0), datetime.datetime(2018, 2, 25, 0, 0), 10.129999999999999, 0.0, 0.0, 0.0),
        (3, 'AAA001', '07770000002', datetime.datetime(2016, 3, 8, 0, 0), datetime.datetime(2018, 2, 15, 0, 0), 10.129999999999999, 2.5, 0.0, 0.0),
        (4, 'AAA001', '07770000003', datetime.datetime(2016, 4, 8, 0, 0), datetime.datetime(2018, 2, 15, 0, 0), 10.129999999999999, 0.0, 0.0, 0.0)
       ]

# class to convert the floats to 2dp
class prettyfloat(float):
    def __repr__(self):
        return "%0.2f" % self

# format to convert the strftime
format = ('%d/%m/%Y')

# MAKE 'big_tuple': solving the list of tuples problem - make a tuple of tuples and format too
x = list(data)
list_of_lists = [list(elem) for elem in x]

big_list = []
for i in list_of_lists:
    data1=(str(i[0]),i[1],str(i[2]),(i[3].strftime(format)), (i[4].strftime(format)), 
           prettyfloat(i[5]), prettyfloat(i[6]), prettyfloat(i[7]), prettyfloat(i[8]))
    big_list.append(data1)

big_tuple = tuple(big_list)
print(big_tuple)

class Example(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        b = tk.Button(self, text="Done!", command=self.upload_cor)
        b.pack()
        table = tk.Frame(self)
        table.pack(side="top", fill="both", expand=True)

        self.widgets = {}
        row = 0
        for rent_id, Client, CLI, Connected_Date, Contract_End_Date, Current_Line_Rental, V1, V2, V3 in (big_tuple):
            row += 1
            self.widgets[rent_id] = {
                "rent_id": tk.Label(table, text=rent_id),
                "Client": tk.Label(table, text=Client),
                "CLI": tk.Label(table, text=CLI),
                "handset": tk.Entry(table),
                "Connected_Date": tk.Label(table, text=Connected_Date),
                "Contract_End_Date": tk.Label(table, text=Contract_End_Date),
                "Current_Line_Rental": tk.Label(table, text=str(Current_Line_Rental)),
                "V1": tk.Label(table, text=V1),
                           }

            self.widgets[rent_id]["rent_id"].grid(row=row, column=0, sticky="nsew")
            self.widgets[rent_id]["Client"].grid(row=row, column=1, sticky="nsew")
            self.widgets[rent_id]["CLI"].grid(row=row, column=2, sticky="nsew")
            self.widgets[rent_id]["handset"].grid(row=row, column=3, sticky="nsew")
            self.widgets[rent_id]["Connected_Date"].grid(row=row, column=4, sticky="nsew")
            self.widgets[rent_id]["Contract_End_Date"].grid(row=row, column=5, sticky="nsew")
            self.widgets[rent_id]["Current_Line_Rental"].grid(row=row, column=6, sticky="nsew")
            self.widgets[rent_id]["V1"].grid(row=row, column=7, sticky="nsew")


        table.grid_columnconfigure(1, weight=1)
        table.grid_columnconfigure(2, weight=1)
        # invisible row after last row gets all extra space
        table.grid_rowconfigure(row+1, weight=1)



    def upload_cor(self):
        updates = []
        for rent_id in (sorted(self.widgets.keys())):
            entry_widget = self.widgets[rent_id]["handset"]
            new_value = entry_widget.get()
            print("%s: %s" % (rent_id, new_value))
            updates.append(new_value)
        print (updates)

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()


# cursor.close()    
# db.close()  
Community
  • 1
  • 1
Tim0th1
  • 65
  • 8
  • In the case of my original answer, a tuple of tuples, a list of tuples, or a list of lists behave exactly the same way. – Bryan Oakley Apr 28 '17 at 14:05
  • We can't run your code because we don't have your database. Please rewrite your example with some static data. Also, you have indentation problems. For example, `upload_cor` has the wrong indentation. – Bryan Oakley Apr 28 '17 at 14:10
  • @BryanOakley, apologies for incorrectly formatted code. I've also replaced the connection with static data. – Tim0th1 Apr 28 '17 at 14:46

1 Answers1

2

The reason you see a discrepancy is because print will print the string value of prettyfloat, but you're passing the actual floating point number to the label widget. This floating point value is passed to the underlying tcl interpreter upon which tkinter is based, and it is this tcl interpreter that is converting the floating point value for display. The tcl interpreter doesn't know how to call your __repr__ function.

You can see the pretty version if you explicitly convert the value to a string using repr, since you defined the __repr__ method:

self.widgets[rent_id] = {
    ...
    "Current_Line_Rental": tk.Label(..., text=repr(Current_Line_Rental)),
    ...
}

Or, you could define __str__, and then use str(Current_Line_Rental).

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • @ Bryan_Oakley thank you for your prompt response. I've tried your suggestion, but the explicit conversion also does not format the GUI: "Current_Line_Rental": tk.Label(table, text=str(Current_Line_Rental)), – Tim0th1 Apr 28 '17 at 14:30
  • @Tim0th1: you are right; my apologies. You've defined `__repr__`, not `__str__`. I'll update my answer. – Bryan Oakley Apr 28 '17 at 14:47