J.Deere
J.Deere

Reputation: 50

Python - Initialzing tkinter inside class,and what happens when need another gui in another class?

I'm trying to create a 'read from csv file' class. This class should read a csv file and return result as a list.So far- quite simple even for a newbie as me.

BUT- I need something a bit more complexed:

1) If filename is not given ( as an argument to class) - a file selection dialog box pops up to select one.

2) Optional: show file's content in a GUI, using a CSVGui class (using method self.out_in_gui).

Since ReadCSV is not GUI based class, GUI is initialized only in need to launch a askopenfilename. A use of widtdraw() is made in order not to show additinal blank GUI.

The probelm is: When I try to read a file, without spcifing one, a file selection dialog pops up, PLUS calling method self.out_in_gui to execute CSVGui class - file contents does not show in GUI (Entries left empty).

TRIES:when I specify as a parameter, the file to be loaded, everything runs OK. My guess- it has something to do with using the GUI in ReadFile to load file selection GUI.

Thanks J

import csv
import tkinter as tk
from tkinter import ttk
from tkinter.filedialog import askopenfilename


class ReadFile:
    """This class reads CSV w/out header line, and returen it as a list"""

    def __init__(self, fname='', path='', **kwargs):
        if fname == '' and path == '':
            tk.Tk().withdraw()
            self.filename = askopenfilename(title='Select CSV file', filetypes=[('CommaSeperatedFile', '*.csv')])
        else:
            if path != '':
                self.filename = path + '/' + fname
            else:
                self.filename = fname
        self.file_content = []
        self.read_file(kwargs)
        self.out_in_gui()

    def read_file(self, kwargs):
        with open(self.filename, newline='', **kwargs) as csvfile:
            file_obj = csv.reader(csvfile)
            for i in file_obj:
                self.file_content.append(i)
            print("file %s was read successfully, containing %d lines" % (self.filename, len(self.file_content)))

    def out_in_gui(self):
        print("send data to CSVGui")
        CSVGui(self, data=self.file_content)

    def file_contents(self):
        return self.file_content


class CSVGui(ttk.Frame):
    def __init__(self, master=None, data=[]):
        ttk.Frame.__init__(self, master)
        self.master = master
        # csvroot = tk.Tk()
        fname = 'No file loaded'
        # if data is not None and master is not None:
        #     fname = self.master.filename
        # csvroot.title("CSVGui - " + fname)
        self.frame = ttk.LabelFrame(self, text='Data set', padding=10)
        self.frame.grid(row=0, column=0, padx=5, pady=5)
        self.widgets_frame = ttk.Frame(self)
        self.widgets_frame.grid(row=1, column=0)

        self.m, self.row_vars = [], []
        self.exec_gui(data)
        print(data)
        # self.read_from_gui()
        # csvroot.mainloop()

    def exec_gui(self, data):
        # populted and create GUI with data
        for r in range(len(data)):
            self.m = []
            self.ent = ''
            for col in range(len(max(data))):
                self.m.append(tk.StringVar())
                self.ent = ttk.Entry(self.frame, textvariable=self.m[-1], state=tk.NORMAL)
                self.ent.grid(row=r, column=col)
                try:
                    self.m[-1].set(data[r][col])
                    print("IN")
                except IndexError:
                    self.m[-1].set('')
                    print("OUT")
            # create structured vector of var for use in save mode
            self.row_vars.append(self.m)

        self.create_widgets()
        print("CSVGui - Created")

    def create_widgets(self):
        self.save_button = ttk.Button(self.widgets_frame, text='Save', command=self.save2file)
        self.save_button.grid(row=0, column=0)
        self.exit_button = ttk.Button(self.widgets_frame, text='Exit')  # , command=print('Exit'))
        self.exit_button.grid(row=0, column=1)
        tk.Spinbox(self.widgets_frame, width=5, from_=0, to=20).grid()

    def read_from_gui(self):
        data_list = []
        for r, rows in enumerate(self.row_vars):
            c_data = []
            for c, cols in enumerate(rows):
                if cols.get() != '':
                    c_data.append(cols.get())
            data_list.append(c_data)

        return data_list

    def save2file(self):
        WriteFile('w', self.master.filename, data=self.read_from_gui())

Upvotes: 2

Views: 572

Answers (1)

furas
furas

Reputation: 142859

I tried your code and it doesn't work for me because problem is master

In ReadFile you run CSVGui(self, ...) and self means ReadFile. In CSVGui you assign this ReadFile to master and use in ttk.Frame.__init__(self, master) - but master has to be any tkinter's widget, not ReadFile.

I made changes in two places

First: in ReadFile I create Tk() window and after askopenfilename I destroy it. This way if I run ReadFile() or ReadFile(filename.csv) I always will run CSVGui without master in computer memory.

   if fname == '' and path == '':
        master = tk.Tk()
        master.withdraw()
        self.filename = askopenfilename(title='Select CSV file', filetypes=[('CommaSeperatedFile', '*.csv')])
        master.destroy()

Second: CSVGui doesn't need master as second argument because I create it inside CSVGui

class CSVGui(ttk.Frame):
    def __init__(self, data=[]):
        master = tk.Tk()
        ttk.Frame.__init__(self, master)

It also need self.pack() to put class CSVGui(ttk.Frame) inside this master, and master.mainloop() to keep it open.


import csv
import tkinter as tk
from tkinter import ttk
from tkinter.filedialog import askopenfilename


class ReadFile:
    """This class reads CSV w/out header line, and returen it as a list"""

    def __init__(self, fname='', path='', **kwargs):
        if fname == '' and path == '': # <--- changes
            master = tk.Tk()
            master.withdraw()
            self.filename = askopenfilename(title='Select CSV file', filetypes=[('CommaSeperatedFile', '*.csv')])
            master.destroy()
        else:
            if path != '':
                self.filename = path + '/' + fname
            else:
                self.filename = fname
        self.file_content = []
        self.read_file(kwargs)
        self.out_in_gui()

    def read_file(self, kwargs):
        with open(self.filename, newline='', **kwargs) as csvfile:
            file_obj = csv.reader(csvfile)
            for i in file_obj:
                self.file_content.append(i)
            print("file %s was read successfully, containing %d lines" % (self.filename, len(self.file_content)))

    def out_in_gui(self):
        print("send data to CSVGui")
        CSVGui(self.filename, data=self.file_content) # <--- changes

    def file_contents(self):
        return self.file_content


class CSVGui(ttk.Frame):
    def __init__(self, filename, data=[]): # <--- changes
        self.filename = filename # <--- changes

        master = tk.Tk() # <--- changes
        ttk.Frame.__init__(self, master)
        self.grid() # <--- changes (updated. was `pack` before)

        # csvroot = tk.Tk()
        fname = 'No file loaded'
        # if data is not None and master is not None:
        #     fname = self.master.filename
        # csvroot.title("CSVGui - " + fname)
        self.frame = ttk.LabelFrame(self, text='Data set', padding=10)
        self.frame.grid(row=0, column=0, padx=5, pady=5)
        self.widgets_frame = ttk.Frame(self)
        self.widgets_frame.grid(row=1, column=0)

        self.m, self.row_vars = [], []
        self.exec_gui(data)
        print(data)
        # self.read_from_gui()
        # csvroot.mainloop()
        master.mainloop() # <--- changes

    def exec_gui(self, data):
        # populted and create GUI with data
        for r in range(len(data)):
            self.m = []
            self.ent = ''
            for col in range(len(max(data))):
                self.m.append(tk.StringVar())
                self.ent = ttk.Entry(self.frame, textvariable=self.m[-1], state=tk.NORMAL)
                self.ent.grid(row=r, column=col)
                try:
                    self.m[-1].set(data[r][col])
                    print("IN")
                except IndexError:
                    self.m[-1].set('')
                    print("OUT")
            # create structured vector of var for use in save mode
            self.row_vars.append(self.m)

        self.create_widgets()
        print("CSVGui - Created")

    def create_widgets(self):
        self.save_button = ttk.Button(self.widgets_frame, text='Save', command=self.save2file)
        self.save_button.grid(row=0, column=0)
        self.exit_button = ttk.Button(self.widgets_frame, text='Exit')  # , command=print('Exit'))
        self.exit_button.grid(row=0, column=1)
        tk.Spinbox(self.widgets_frame, width=5, from_=0, to=20).grid()

    def read_from_gui(self):
        data_list = []
        for r, rows in enumerate(self.row_vars):
            c_data = []
            for c, cols in enumerate(rows):
                if cols.get() != '':
                    c_data.append(cols.get())
            data_list.append(c_data)

        return data_list

    def save2file(self):
        WriteFile('w', self.filename, data=self.read_from_gui()) # <--- changed

ReadFile()

EDIT: I added filename to CSVGui(self.filename, data=self.file_content)

Upvotes: 1

Related Questions