Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQL Query results in tkinter

I have a tkinter interface where I need to display some query results and I need for the user to be able to modify a column and submit the results. Currently to pull the queries I'm doing something like this:

conn = connection_info_goes_here
cur = conn.cursor()
cur.execute(query_goes_here)

And this is my query:

SELECT id, reviewer, task, num_seconds, start_time, end_time
FROM hours
WHERE DATE(start_time) = '2014-12-18'
AND reviewer = 'john'

The field that the user needs to modify is num_seconds (just numbers). My question is, how do I make the query results show in the grid and how do I make one of the fields modifiable with a button to submit the changes?

Additional info: I already did this in a very messy way using exec() and programmatically creating variables for each field. It became very long and confusing and I really think there has to be a better and easier way to do this.

Any help is appreciated. Thanks!!

Quick Update: since this was put on hold, i'll add an image of something similar to what I'm looking for:

enter image description here

The values in the entry label must replace the values in the column to the right when I upload them back to the DB.

When I say I did this in a messy way, is because I did (the only way I could think of):

def cor_window():
    corrections = Tk()
    corrections.title("Corrections")
    corrections_frame = ttk.Frame(corrections)

    cor_values = []
    count=0
    cor_count=0
    for x in results:
        count2=0
        for y in results[count]:

            if count2 == 3:
                exec('int' + str(cor_count) + '=tkinter.StringVar')
                exec('int' + str(cor_count) + '_entry = ttk.Entry(corrections, width=20, textvariable=int' + str(cor_count) + ')')
                exec('int' + str(cor_count) + '_entry.grid(column=count2, row=count+2)')

                cor_count = cor_count+1
                cor_values.append('int' + str(cor_count) + '_entry')

                ttk.Label(corrections, width=20, anchor=CENTER, relief=SUNKEN, borderwidth=1, text= results[count][count2]).grid(column=count2+1, row=count+2)

            elif count2 > 3:
                ttk.Label(corrections, width=20, anchor=CENTER, relief=SUNKEN, borderwidth=1, text= results[count][count2]).grid(column=count2+1, row=count+2)
            else:
                ttk.Label(corrections, width=20, anchor=CENTER, relief=SUNKEN, borderwidth=1, text= results[count][count2]).grid(column=count2, row=count+2)
            count2=count2+1
        count=count+1

    ttk.Button(corrections, text="Done!", command=upload_cor).grid(column=0, row=1)

Where results is the list that contains the query results and upload_cor is the function the will upload the changes to the DB. Since I used exec, even if the user modifies the entry box, I can't use .get() to get what the user typed. When I try use .get(), I only get None even if something was typed in the entry box.

I just need a different method to do this, again, any ideas are welcome.

like image 523
rodrigocf Avatar asked Dec 19 '14 01:12

rodrigocf


1 Answers

You definitely don't want to use exec, and you don't need to use the textvariable option. Both of those just add to the confusion. Simply store your widgets as a dictionary, get the data straight from the entry widget, and it all becomes very easy to manage.

Here's a working example:

import tkinter as tk

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)

        data = (
            (45417, "rodringof", "CSP L2 Review", 0.000394, "2014-12-19 10:08:12", "2014-12-19 10:08:12"),
            (45418, "rodringof", "CSP L2 Review", 0.000394, "2014-12-19 10:08:12", "2014-12-19 10:08:12"),
            (45419, "rodringof", "CSP L2 Review", 0.000394, "2014-12-19 10:08:12", "2014-12-19 10:08:12"),
            (45420, "rodringof", "CSP L2 Review", 0.000394, "2014-12-19 10:08:12", "2014-12-19 10:08:12"),
            (45421, "rodringof", "CSP L2 Review", 0.000394, "2014-12-19 10:08:12", "2014-12-19 10:08:12"),
            (45422, "rodringof", "CSP L2 Review", 0.000394, "2014-12-19 10:08:12", "2014-12-19 10:08:12"),
            (45423, "rodringof", "CSP L2 Review", 0.000394, "2014-12-19 10:08:12", "2014-12-19 10:08:12"),
        )

        self.widgets = {}
        row = 0
        for rowid, reviewer, task, num_seconds, start_time, end_time in (data):
            row += 1
            self.widgets[rowid] = {
                "rowid": tk.Label(table, text=rowid),
                "reviewer": tk.Label(table, text=reviewer),
                "task": tk.Label(table, text=task),
                "num_seconds_correction": tk.Entry(table),
                "num_seconds": tk.Label(table, text=num_seconds),
                "start_time": tk.Label(table, text=start_time),
                "end_time": tk.Label(table, text=start_time)
            }

            self.widgets[rowid]["rowid"].grid(row=row, column=0, sticky="nsew")
            self.widgets[rowid]["reviewer"].grid(row=row, column=1, sticky="nsew")
            self.widgets[rowid]["task"].grid(row=row, column=2, sticky="nsew")
            self.widgets[rowid]["num_seconds_correction"].grid(row=row, column=3, sticky="nsew")
            self.widgets[rowid]["num_seconds"].grid(row=row, column=4, sticky="nsew")
            self.widgets[rowid]["start_time"].grid(row=row, column=5, sticky="nsew")
            self.widgets[rowid]["end_time"].grid(row=row, column=6, 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):
        for rowid in sorted(self.widgets.keys()):
            entry_widget = self.widgets[rowid]["num_seconds_correction"]
            new_value = entry_widget.get()
            print("%s: %s" % (rowid, new_value))

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

I would actually implement this a little differently by creating a Table class with an add_row method, but I didn't want to get too complex. The basic idea is the same whether you create a Table class, do it all in the one class, or do it procedurally - create a dictionary to represent your data. You could use nested lists too, but I find dictionaries to be much easier to use. They are also self-documenting, since you reference things by a symbolic name rather than just knowing that column 4 is the start time.

like image 56
Bryan Oakley Avatar answered Oct 12 '22 08:10

Bryan Oakley