I'm writing a multi-window GUI in tkinter. Clicking on the 'Load CSV Data' button in the the Main Window creates an instance of the LoadWindow class which inherits from tkinter.TopLevel and the MainGui instance is passedto LoadWindow since I want to manipulate it from LoadWindow. However when I call self.destroy to close the Load CSV window when the Load CSV button is clicked, I get the following error even though the Load CSV data window closes.
if self._name in self.master.children:
AttributeError: 'MainGUI' object has no attribute 'children'
Below is the code:
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
import os
import pandas as pd
class MainGUI:
def __init__(self, master):
self.master = master
master.title("Main Window")
master.geometry("700x500")
# create all elements in main window
load_csv_button = ttk.Button(master,
text='Load CSV Data',
command=lambda: LoadWindow(master=self))
load_db_button = ttk.Button(master, text='Load from Database')
save_db_button = ttk.Button(master, text='Save Current File to Database')
data_transform_button = ttk.Button(master, text='Data Transformation')
data_analysis_button = ttk.Button(master, text='Data Analysis')
#update later
preview_df_button = ttk.Button(master, text='Preview Data Frame',
command=lambda: print(self.main_df))
self.text_box = tk.Text(master, bg='grey')
# insert welcome message into text box and disable
self.text_box.insert(tk.END, 'Welcome to the Data Analysis Hub')
self.text_box.config(state='disabled')
# snap all elements to grid
load_csv_button.grid(row=0, column=1, sticky='NSEW')
load_db_button.grid(row=1, column=1, sticky='NSEW')
save_db_button.grid(row=2, column=1, sticky='NSEW')
data_transform_button.grid(row=0, column=2, sticky='NSEW')
data_analysis_button.grid(row=1, column=2, sticky='NSEW')
preview_df_button.grid(row=2, column=2, sticky='NSEW')
self.text_box.grid(row=4, column=1, columnspan=2)
self.main_df = None
def update_textbox(self, message):
self.text_box.config(state='normal')
self.text_box.delete('1.0', 'end')
self.text_box.insert(tk.END, message)
self.text_box.config(state='disabled')
class LoadWindow(tk.Toplevel):
def __init__(self, master):
tk.Toplevel.__init__(self)
self.title("Load CSV")
self.geometry("200x200")
self.master = master
# get csvs in current directory
listbox = tk.Listbox(self, selectmode=tk.SINGLE)
csv_files = self.find_csv_files(os.getcwd())
for csv in csv_files:
listbox.insert(tk.END, csv)
listbox.grid(row=1, column=1, columnspan=3, sticky='NSEW')
# assign selected csv to maindf
active = listbox.get(tk.ACTIVE)
load_csv_button = ttk.Button(self, text='Load CSV',
command=lambda: self.load_selected(active))
load_csv_button.grid(row=2, column=1)
def find_csv_files(self, path):
# Check for csvs in path
filenames = os.listdir(path)
csv_files = [x for x in filenames if x.endswith('.csv')]
return csv_files
def load_selected(self, active):
try:
csv_path = os.getcwd()+"/"+active
main_df = pd.read_csv(csv_path)
# update maingui variable
self.master.main_df = main_df
# update maingui status on df loaded
self.master.update_textbox(f'{active} loaded as DataFrame')
self.destroy()
except pd.errors.ParserError:
error = 'Looks like you either have no csvs in working directory ' \
'or loaded a file that is not a csv, please try another file'
messagebox.showerror(title='Load error', message=error)
if __name__ == '__main__':
window = tk.Tk()
maingui = MainGUI(window)
window.mainloop()
Start with this line:
LoadWindow(master=self)
self is not a window. Inside the __init__ you do self.master = master. Thus, self.master inside of LoadWindow is not a widget and thus it has no children attribute.
You need to change how you create LoadWindow to be something like this:
LoadWindow(master=self.master)
Related
In main.py there is class MainFrame which display Player._money
In lands.py there is class Lands which allows the player to buy more lands.
Inside class Lands, there is a function called def buy_lands(self) that respond to a button.
here's my problem:
When buy_lands(self) is clicked, i've managed to update the label that shows the Player._lands as it's in the same class, same frame. However, the label that display the money is in a different frame, different class and a different file as it's in main.py class MainFrame.
How can i update Player._money in MainFrame from buy_lands(self) in class Lands without having a circular import error?
Here's the code if that's help:
main.py
import tkinter as tk
from tkinter import ttk
from lands import Lands
from player import Player
class MainFrame(ttk.Frame):
def __init__(self, container):
super().__init__(container)
options = {'padx': 5, 'pady': 5}
self.player = Player._name
self.__create_widgets()
# show the frame on the container
self.pack(**options)
def __create_widgets(self):
# Initialize style
s = ttk.Style()
# Frame Top Menu
s.configure('Menu.TFrame', background='blue') # define the style
self.menu = ttk.Frame(self, height=100, width=450, style='Menu.TFrame') # create the frame
self.menu.pack() # place the frame
# Main Frame
s.configure('Main.TFrame', background='red')
self.main = ttk.Frame(self, height=300, width=300, style='Main.TFrame')
self.main.pack()
# Create Widgets
self.name_label = ttk.Label(self.menu, text=f'Welcome {Player._name}')
self.money_label = ttk.Label(self.menu, text=f'money: {Player._money} £')
self.button_1 = ttk.Button(self.menu, text="Button 1")
self.lands_button = ttk.Button(self.menu, text="Lands", command=self.show_lands)
self.button_3 = ttk.Button(self.menu, text="Button 3")
self.button_4 = ttk.Button(self.menu, text="Button 4")
# Display Widgets
self.name_label.grid(column=0, columnspan=1, row=0, sticky='w')
self.money_label.grid(column=2, row=0, columnspan=3, sticky='e')
self.button_1.grid(column=0, row=1)
self.lands_button.grid(column=1, row=1)
self.button_3.grid(column=2, row=1)
self.button_4.grid(column=3, row=1)
def show_lands(self):
for widget in self.main.winfo_children():
widget.destroy()
Lands(self.main)
lands.py
import tkinter as tk
from tkinter import ttk
from player import Player
class Lands(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.lands = Player._lands
options = {'padx': 5, 'pady': 5}
# show the frame on the container
self.pack(**options)
self.__create_widgets()
def __create_widgets(self):
self.lands_label = ttk.Label(self, text=f'You have {Player._lands} acres of lands')
self.buy_lands_button = ttk.Button(self, text="Buy Lands", command=self.buy_lands)
self.lands_label.pack()
self.buy_lands_button.pack()
def buy_lands(self):
Player._money -= 5000
Player._lands += 10
self.lands_label.config(text=f'You have {Player._lands} acres of lands')
self.lands_label.pack()
print(Player._money)
print(Player._lands)
i've tryed lambda methods but as i'm still learning i'm not too sure how to use it. I've tried global method but again because it's not in the same file, it doesn't work. And i tried to import MainFrame which shows a circular import error.
I want the user to click a button called "choose a folder" and it allows them to choose a folder from their directory and then click a button called "choose csv" and it allows them to choose a csv. Then I want the user to click price difference, where it will get those folder paths and find a price difference within the file.
I created functions to get the folder path and csv, but I am having trouble returning the results from the user to avgprice().
Here is the code I have so far:
import tkinter as tk
global folder_path
import tkinter.ttk
from tkinter import filedialog
from tkinter import *
import pandas as pd
from tkinter.filedialog import askopenfilename
LARGE_FONT= ("Verdana", 12)
class SeaofBTCapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand = True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
frame = StartPage(container, self)
self.frames[StartPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="BTC Price Difference", font=LARGE_FONT)
label.pack(pady=10,padx=10)
first_window_button = tk.Button(self, text="select Folder", command= self.browse_button)
first_window_button.pack()
first_window_csv_button = tk.Button(self, text="select csv", command= self.import_csv_data)
first_window_csv_button.pack()
first_window_diffbutton = tk.Button(self, text="Price difference", command= self.avgprice)
first_window_diffbutton.pack()
closebutton=tk.Button(self,text="Quit",command=controller.destroy)
closebutton.pack()
def browse_button(self):
# Allow user to select a directory and store it in global var
#called folder_path
folder_path = StringVar()
filename = filedialog.askdirectory()
folder_path.set(filename)
return filename
def import_csv_data(self):
csv_file_path = askopenfilename()
df = pd.read_csv(csv_file_path)
return df
def avgprice(self):
path1=self.browse_button()
path2=self.import_csv_data()
I am running into an issue in avgprice() as I do not want the window to select file show up again, I just want the entries to save from what the user chose when clicking all the buttons. Is there a way to use .get() here?
I see a few things here.
you are using global in a class. Use a class attribute instead.
self.askopenfilename is wrong it is just askopenfilename. self. will make the program look for a class attribute called self.askopenfilename and that does not exist so that will error out.
you have v.set() but do not define v so that will error as well. This again should be a class attribute.
pd is not defined. I am assuming this is from pandas or some other library that can read csv but you do not show this in your imports.
path2=self.import_csv_data does nothing.
You use global folder_path in the global namespace. global is not used this way and in this case is does nothing of use. global is used inside of a function to tell that function that a variable exist outside of the function in the global namespace so using it in the global name space is pointless.
You are already doing import tkinter as tk you do not also need to do from tkinter import *. The * method can cause you to overwrite methods if you are not careful so the preferred method for importing tkinter is to use import tkinter as tk and simply use the tk. prefix for everything.
Cleaned up example. Let me know if you have any questions:
import tkinter as tk
from tkinter import filedialog
from tkinter.filedialog import askopenfilename
LARGE_FONT = ("Verdana", 12)
class SeaofBTCapp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.grid(row=0, column=0, sticky='ew')
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
frame = StartPage()
self.frames[StartPage] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self):
super().__init__()
self.v = ''
self.file_path = ''
self.folder_path = ''
tk.Label(self, text="BTC Price Difference", font=LARGE_FONT).pack(pady=10, padx=10)
tk.Button(self, text="select Folder", command=self.browse_button).pack()
tk.Button(self, text="select csv", command=self.import_csv_data).pack()
tk.Button(self, text="Price difference", command=self.avgprice).pack()
tk.Button(self, text="Quit", command=self.master.destroy).pack()
def browse_button(self):
filename = filedialog.askdirectory()
print(filename)
self.folder_path = filename
def import_csv_data(self):
csv_file_path = askopenfilename()
print(csv_file_path)
self.file_path = csv_file_path
# df = pd.read_csv(csv_file_path)
def avgprice(self):
path1 = self.folder_path
path2 = self.file_path
print('folder_path: ', path1)
print('file_path: ', path2)
SeaofBTCapp().mainloop()
I am using; Python 3.4, Windows 8, tkinter. I am trying to create a generic browse button that will get a file name and assign it to a variable.
I have created the following code to do this.
from tkinter import *
from tkinter import filedialog
from tkinter import ttk
class Application(Frame):
# A GUI Application.
# Initialize the Frame
def __init__(self, master):
Frame.__init__(self, master)
nbook = ttk.Notebook(root)
nbook.pack(fill='both', expand='yes')
f1 = ttk.Frame(nbook)
nbook.add(f1, text='QC1')
self.qc1_tab(f1)
# create QC1 tab contents
def qc1_tab(self, tab_loc):
# Set up file name entry.
Label(tab_loc, text="Select file:").grid(pady=v_pad, row=0, column=0, sticky=W)
self.flnm = ttk.Entry(tab_loc, width=60)
self.flnm.focus_set()
self.flnm.grid(pady=v_pad, row=0, column=1, columnspan=2, sticky=W)
ttk.Button(tab_loc, text="Browse...", width=10, command=self.browse).grid(row=0, column=3)
def browse(self):
temp = filedialog.askopenfilename()
self.flnm.delete(0, END)
self.flnm.insert(0, temp)
root = Tk()
app = Application(root)
root.mainloop()
The only problem with this is that the browse button is tied to self.flnm and cannot be used for anything else. I plan to use the browse button several times to acquire the file name of several different files and would rather not have multiple browse commands.
I need to call it from a button and somehow assign it to a variable afterwards.
I was thinking of something like
ttk.Button(..., command=lambda: self.flnm = self.browse)
...
def browse(self):
filename = filedialog.askopenfilename()
return filename
but that failed terribly.
How can I make a general purpose browse button?
You can write:
def browse(self, target):
temp = filedialog.askopenfilename()
target.delete(0, END)
target.insert(0, temp)
ttk.Button(..., command=lambda: self.browse(self.flnm))
I have a window to browse a folder containing necessary files. I am using tkFileDialog for the same. I want to set the value of Entry widget equal to this selected folder. Initially when no folder is selected it will be null. As soon as I select the folder, the path of the selected folder should appear in the Entry widget. The user should be able to modify.Below mentioned is the code for the same.
from Tkinter import *
from tkFileDialog import *
class Checkit:
root = Tk()
#default string to be displayed in the entry of path
path_to_file = StringVar(root, value="abc")
def __init__(self):
self.inputDetail()
def inputDetail(self):
#copy the root window
master = self.root
#create frame for details in the root window
details_frame = Frame(master)
details_frame.grid(row=0, column=0)
#Create the Labels
papercode_label = Label(details_frame, text="Paper code:")
subject_label = Label(details_frame, text="Subject:")
chapter_label = Label(details_frame, text="Chapter:")
batch_label = Label(details_frame, text="Batch:")
ansFolder_label = Label(details_frame, text="Folder containing answer-keys:")
#create entry for the labels
papercode_entry = Entry(details_frame)
subject_entry = Entry(details_frame)
chapter_entry = Entry(details_frame)
batch_entry = Entry(details_frame)
ansFolder_entry = Entry(details_frame)
#create button to add path
path = Button(details_frame, text="Browse", command= lambda: self.addpath(details_frame))
#button to enter the next window
next = Button(details_frame, text="Next", command= lambda: self.checkOmr(details_frame, master))
#Use grid layout to place labels and entry
papercode_label.grid(row=0, sticky=W)
papercode_entry.grid(row=1, sticky=W)
subject_label.grid(row=2, sticky=W)
subject_entry.grid(row=3, column=0, sticky=W)
chapter_label.grid(row=4, sticky=W)
chapter_entry.grid(row=5, column=0, sticky=W)
batch_label.grid(row=6, sticky=W)
batch_entry.grid(row=7, column=0, sticky=W)
ansFolder_label.grid(row=8, sticky=W)
path.grid(row=9, sticky=W, columnspan=2)
next.grid(row=10, sticky=E, columnspan=2)
master.mainloop()
def checkOmr(self, old_frame, master):
#destoy the old frame
old_frame.destroy()
#create frame for details in the root window
inputPath_frame = Frame(master)
inputPath_frame.grid(row=0, column=0)
#create label to input folder containing
omrFolder_label = Label(inputPath_frame, text="Folder containing OMR sheet to be checked:")
#create button to add path
path = Button(inputPath_frame, text="Browse", command= lambda: self.addpath(inputPath_frame))
selected_path = Entry(inputPath_frame, textvariable=self.path_to_file)
#place the label and button on the grid
omrFolder_label.grid(row=0, sticky=W)
path.grid(row=1, column=0, sticky=W)
selected_path.grid(row=1, column=1, sticky=W)
#master.mainloop()
def addpath(self, details_frame):
self.path_to_file = askdirectory(parent=details_frame,initialdir="/",title='Please select a directory')
if __name__=='__main__':
handle = Checkit()
Here I am trying to change the modifying the self. path_to_file value on the click of the button. I tried to print the value of self.path_to_file value in addpath(). It gives correct result there but the value in the Entry selected_path in checkOMR() does not modify.
Can somebody suggest what changes should I make to make that thing possible.
Look at this line:
self.path_to_file = askdirectory(...)
Before this line of code runs, self.path_to_file is an instance of a StringVar. After this line of code has run, self.path_to_file is reset to be just a string.
Assuming you want self.path_to_file to remain an instance of StringVar, you need to change that line to this:
path = askdirectory(...)
self.path_to_file.set(path)
I have created an coding gui,which doesnot show 'File' and 'save' in the Gui
Please help me to fix my problem.
I have created a Function for File and Save ,still not working!
please help me to rectify my code!
from tkinter import *
import tkinter.messagebox
import tkinter
import tkinter as tki
import tkinter.filedialog as th1
import re
class App(object):
def __init__(self,root):
self.root = root
# create a Frame for the Text and Scrollbar
txt_frm = tki.Frame(self.root, width=600, height=400)
txt_frm.pack(fill="both", expand=True)
# ensure a consistent GUI size
txt_frm.grid_propagate(False)
# create first Text label, widget and scrollbar
self.lbl1 = tki.Label(txt_frm, text="Type")
self.lbl1.grid(row=0,column=0,padx=2,pady=2)
self.txt1 = tki.Text(txt_frm, borderwidth=3, relief="sunken", height=4,width=55)
self.txt1.config(font=("consolas", 12), undo=True, wrap='word')
self.txt1.grid(row=0, column=1, sticky="nsew", padx=2, pady=2)
scrollb1 = tki.Scrollbar(txt_frm, command=self.txt1.yview)
scrollb1.grid(row=0, column=2, sticky='nsew')
self.txt1['yscrollcommand'] = scrollb1.set
button = tki.Button(txt_frm,text="Clickone", command = self.retrieve_input)
button.grid(column=2,row=0)
button1 = tki.Button(txt_frm,text="Cli", command = self.clearBox)
button1.grid(column=2,row=0)
def retrieve_input(self):
input1 = self.txt1.get("0.0",'end-1c')
with open('text.txt','a+') as f:
f.write(input1+'\n')
f.close()
def clearBox(self):
self.txt1.delete('1.0', 'end')#<-0.0/1.0
def file_save():
f = th1.asksaveasfile(mode='w', defaultextension=".txt")
if f is None: # asksaveasfile return `None` if dialog closed with "cancel".
return
text2save = str(text.get(1.0, END))
a= (f)
f.write(text2save)
f.close()
root = tki.Tk()
menubar=Menu(root)
filemenu=Menu(menubar,tearoff=0)
filemenu.add_command(label="Save", command=file_save)
app = App(root)
root.mainloop()
Please help!Answers will be appreciated!
You aren't adding the menubar to the window. Add this after you create the menubar.
root.configure(menu=menubar)
You then also have to add the file menu to the menubar:
menubar.add_cascade(label="File", menu=filemenu)