Posts: 5
Threads: 2
Joined: Aug 2022
Hi everyone,
I am designing my first GUI app using Tkinter and have somme issues.
The purpose of the app is to read a SWMM file in order to get some data series using the specific library Swmmtoolbox.
This code works well.
Now I want to share it with my desk, thus create an .exe with a GUI.
But, I am having trouble retriveing the path of my input files from the browser widget outside the getFilepath methods.
Coul you please help me solving this issue ?
class Application(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
filenameCSV = tk.StringVar()
filenameSWMM = tk.StringVar()
self.createWidget()
def createWidget(self):
ttk.Button(self, text="Sélectionner le fichier SWMM:", command=self.getFilepathSWMM).pack()
ttk.Button(self, text="Sélectionner le fichier CSV:", command=self.getFilepathCSV).pack()
ttk.Button(self, text="Extraire les données SWMM.", command= lambda: self.extraction(filenameSWMM, filenameCSV)).pack()
ttk.Button(self, text="Quitter", command=self.quit).pack()
def getFilepathSWMM(self):
self.filenameSWMM = filedialog.askopenfilename(initialdir = "/", title = "Sélectionner le fichier", filetype = [('SWMM File', '*.out')])
self.label = ttk.Label(self, text = "")
self.label.pack()
self.label.configure(text = self.filename)
def getFilepathCSV(self):
self.filenameCSV = filedialog.askopenfilename(initialdir = "/", title = "Sélectionner le fichier", filetype = [('CSV File', '*.csv')])
self.label = ttk.Label(self, text = "")
self.label.pack()
self.label.configure(text = self.filename)
def quit(self):
self.destroy()
def extraction(path1,path2):
book = Workbook()
feuill1 = book.active
file = open(path2)
data = csv.reader(file, delimiter=';')
array = list(data)
for i in range(1, len(array)+1):
extracti = swmmtoolbox.extract(path1, [array[i-1][0], array[i-1][1], array[i-1][2]])
extracti = extracti.reset_index()
feuill1.cell(1,column=3*i).value = array[i-1][1]
feuill1.cell(1,column=3*i+1).value = array[i-1][1]
feuill1.column_dimensions[get_column_letter(3*i+1)].width = 20
feuill1.column_dimensions[get_column_letter(3*i)].width = 20
for j in range(2,extracti.shape[0]):
feuill1.cell(j,column=3*i).value = extracti.iat[j,0]
feuill1.cell(j,column=3*i+1).value = extracti.iat[j,1]
book.save("Extraction_SWMM_2.xlsx")
if __name__ == "__main__":
app = Application()
app.title("SWMM Extraction")
app.geometry("200x200")
app.mainloop()
Posts: 6,788
Threads: 20
Joined: Feb 2020
Aug-22-2022, 04:06 PM
(This post was last modified: Aug-22-2022, 04:06 PM by deanhystad.)
You could treat it as a file dialog and just return the selected filename.
def getFilepathCSV(self):
return filedialog.askopenfilename(initialdir = "/", title = "Sélectionner le fichier", filetype = [('CSV File', '*.csv')]) or you could wrap it in a function that uses the filename,
def openFilepathCSV(self):
filename = filedialog.askopenfilename(initialdir = "/", title = "Sélectionner le fichier", filetype = [('CSV File', '*.csv')]))
with open(filename, "r") as file:
call_some_func(file) Either way your GUI will need a way to call code that draws the browser and processes the selected filename. This could be a button, or a menu.
Posts: 536
Threads: 0
Joined: Feb 2018
You save it as self.filenameSWMM. What is the problem. Where do you want to use it, and is it in this program or another?
Posts: 5
Threads: 2
Joined: Aug 2022
(Aug-22-2022, 07:22 PM)woooee Wrote: You save it as self.filenameSWMM. What is the problem. Where do you want to use it, and is it in this program or another?
That's the problem. I want to use the path save as self.filenameSWMM in the method extraction as path2.
Posts: 6,788
Threads: 20
Joined: Feb 2020
Aug-23-2022, 11:21 AM
(This post was last modified: Aug-23-2022, 11:21 AM by deanhystad.)
filenameCSV and filenameSWMM should both be instance variables so they can be seen outside the __init__() method. You don't use these as StringVars and they are reassigned in getFilepath***, so may as well initialize them to None.
class Application(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
self.filenameCSV = None
self.filenameSWMM = None
self.createWidget() If you want getFilepathCSV to be a static method you should use a static decorator. When calling the function you need to use self.filenameSWMM and self.filenameCSV because there are no filenameSWMM or filenameCSV variables.
def createWidget(self):
ttk.Button(self, text="Sélectionner le fichier SWMM:", command=self.getFilepathSWMM).pack()
ttk.Button(self, text="Sélectionner le fichier CSV:", command=self.getFilepathCSV).pack()
ttk.Button(self, text="Extraire les données SWMM.",
command= lambda: self.extraction(self.filenameSWMM, self.filenameCSV)).pack()
ttk.Button(self, text="Quitter", command=self.quit).pack()
@staticmethod
def extraction(path1,path2):
book = Workbook()
feuill1 = book.active
file = open(path2)
data = csv.reader(file, delimiter=';')
array = list(data)
for i in range(1, len(array)+1):
extracti = swmmtoolbox.extract(path1, [array[i-1][0], array[i-1][1], array[i-1][2]])
extracti = extracti.reset_index()
feuill1.cell(1,column=3*i).value = array[i-1][1]
feuill1.cell(1,column=3*i+1).value = array[i-1][1]
feuill1.column_dimensions[get_column_letter(3*i+1)].width = 20
feuill1.column_dimensions[get_column_letter(3*i)].width = 20
for j in range(2,extracti.shape[0]):
feuill1.cell(j,column=3*i).value = extracti.iat[j,0]
feuill1.cell(j,column=3*i+1).value = extracti.iat[j,1]
book.save("Extraction_SWMM_2.xlsx")
Bettter yet, make extraction an instance method. Now you don't need the lambda.
def createWidget(self):
ttk.Button(self, text="Sélectionner le fichier SWMM:", command=self.getFilepathSWMM).pack()
ttk.Button(self, text="Sélectionner le fichier CSV:", command=self.getFilepathCSV).pack()
ttk.Button(self, text="Extraire les données SWMM.", command= self.extraction).pack()
ttk.Button(self, text="Quitter", command=self.quit).pack()
def extraction(self):
book = Workbook()
feuill1 = book.active
file = open(self.filenameCSV)
data = csv.reader(file, delimiter=';')
array = list(data)
for i in range(1, len(array)+1):
extracti = swmmtoolbox.extract(self.filenameSWMM, [array[i-1][0], array[i-1][1], array[i-1][2]])
extracti = extracti.reset_index()
feuill1.cell(1,column=3*i).value = array[i-1][1]
feuill1.cell(1,column=3*i+1).value = array[i-1][1]
feuill1.column_dimensions[get_column_letter(3*i+1)].width = 20
feuill1.column_dimensions[get_column_letter(3*i)].width = 20
for j in range(2,extracti.shape[0]):
feuill1.cell(j,column=3*i).value = extracti.iat[j,0]
feuill1.cell(j,column=3*i+1).value = extracti.iat[j,1]
book.save("Extraction_SWMM_2.xlsx") You could make.filenameCSV and filenameSWMM StringVars and use them to update the labels in your program. Note that StringVars use get() and set() to get the value or set the value of the variable. This is your code reimagined (minus the extract method since I don't care to install the package it uses).
import tkinter as tk
from tkinter import filedialog
from tkinter import ttk
class Application(tk.Tk):
def __init__(self, *args, title=None, **kwargs):
super().__init__()
if title:
self.title(title)
self.csv_file = tk.StringVar()
tk.Label(self, text="CSV", width=5).grid(row=0, column=0, padx=5, pady=5)
tk.Label(self, textvariable=self.csv_file, width=64).grid(row=0, column=1)
ttk.Button(self, text="Select", command=self.get_csv).grid(row=0, column=2, padx=5, pady=5)
self.swmm_file = tk.StringVar()
tk.Label(self, text="SWMM", width=5).grid(row=1, column=0, padx=5, pady=5)
tk.Label(self, textvariable=self.swmm_file, width=64).grid(row=1, column=1)
ttk.Button(self, text="Select", command=self.get_swwm).grid(row=1, column=2, padx=5, pady=5)
ttk.Button(self, text="Extraire les données SWMM.", command= self.extraction) \
.grid(row=2, column=1)
ttk.Button(self, text="Quitter", command=self.quit).grid(row=2, column=2, padx=5, pady=5)
def get_swwm(self):
self.swmm_file.set(
filedialog.askopenfilename(
initialdir = "/",
title = "Sélectionner le fichier",
filetype = [('SWMM File', '*.out')]))
def get_csv(self):
self.csv_file.set(
filedialog.askopenfilename(initialdir = "/",
title = "Sélectionner le fichier",
filetype = [('CSV File', '*.csv')]))
def quit(self):
self.destroy()
def extraction(self):
print(f"Extracting {self.csv_file.get()} to make {self.swmm_file.get()}")
if __name__ == "__main__":
Application(title="SWMM Extraction").mainloop()
Posts: 536
Threads: 0
Joined: Feb 2018
Make extraction() a member of the class
def extraction(self):
print(self.filenameSWMM)
Posts: 6,788
Threads: 20
Joined: Feb 2020
Aug-23-2022, 09:34 PM
(This post was last modified: Aug-23-2022, 09:34 PM by deanhystad.)
extraction is a "method" of the class just by being indented at the same level as all the other methods. "self" has nothing to do with it being method. "self" is a naming convention for the first argument that is passed to any instance method of a class.
There are many problems with this:
def extraction(path1,path2): 1. path1 is where we expect to see "self". The path1 variable is going to reference an Application object, not a filename.
2. path2 is going to get filenameSWMM but the programmer expects it to get filenameCSV
3. There is no argument to receive filenameSWMM
If you rewrite so the first argument is self, you fix those problems, but get new problems.
def extraction(self, path1, path2): This straightens out the arguments passed to extraction, but you still have the problem that neither filenameSWMM nor filenameCSV are defined when this is executed:
ttk.Button(self, text="Extraire les données SWMM.", command= lambda: self.extraction(filenameSWMM, filenameCSV)).pack() That can be fixed by making filenameSWMM and filenameCSV instance variables of Application.
class Application(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
self.filenameCSV = self.filenameSWMM = None
self.createWidget() Now that filenameCSV and filenameSWMM are instance variables it doesn't make sense to use a lambda expression when binding the extraction method. Now the button that calls extraction should be made like this:
ttk.Button(self, text="Extraire les données SWMM.", command=self.extraction).pack() And the extraction method should look like this:
book = Workbook()
feuill1 = book.active
file = open(self.filenameCSV) # Use the instance variables
data = csv.reader(file, delimiter=';')
array = list(data)
for i in range(1, len(array)+1):
extracti = swmmtoolbox.extract(self.filenameSWMM, [array[i-1][0], array[i-1][1], array[i-1][2]]) # Use the instance variables
extracti = extracti.reset_index()
feuill1.cell(1,column=3*i).value = array[i-1][1]
feuill1.cell(1,column=3*i+1).value = array[i-1][1]
feuill1.column_dimensions[get_column_letter(3*i+1)].width = 20
feuill1.column_dimensions[get_column_letter(3*i)].width = 20
for j in range(2,extracti.shape[0]):
feuill1.cell(j,column=3*i).value = extracti.iat[j,0]
feuill1.cell(j,column=3*i+1).value = extracti.iat[j,1]
book.save("Extraction_SWMM_2.xlsx") Or at least something like that, No doubt the function contains errors since it hasn't been run yet.
Another option is to make extraction a static method. static methods are just functions that are associated with a class. They are not automatically passed an instance or a class as the fist argument. I mentioned this in my earlier post. But I agree wooee that extraction should really be an instance method and have "self" as the first (and I think the only) argument.
And finally we have the problem that your program never calls tkinter.Tk() Tk() does more than make a window. It also initializes tkinter.
Posts: 5
Threads: 2
Joined: Aug 2022
Thanks a lot for your answers guys.
I definitely have a better understanding of instance variable and methods now.
I'll try to rewrite my code using your suggestions in order to have a "proper" script.
Thanks again!!
|