0

I've been looking for a solution to my problem too long. I've reviewed all the pages that are proposed in the following question, exhaustively: Switch between two frames in tkinter, I have documented myself reviewing information from classes on the official python page, I've been reading about self, in case I'm not considering something; I have also gone through more pages, questions and solutions that I have come across along the way and rearranged my code several times, but I cannot find the solution and need help.

The code, as simplified as possible, is the following:

import os, easygui
import xml.etree.ElementTree as ET
import tkinter as tk

LARGE_FONT = ("Verdana", 12)


class PageOne(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        print("Second TC:", self.controller.shared_data['tc'])

        for i in self.controller.shared_data['tc']:
            print("****: ", i)
            print("Name:", i['Name'], "Id:", i['Id'])


class StartPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller

        label = tk.Label(self, text='File Upload: Press the button to open the XML file.', font=LARGE_FONT)
        label.place(relx=.01, rely=.01)

        # Function to separate the tc "Child_4" inside the xml file
        def separate_tc(xml_name):
            file_xml = ET.parse(xml_name)
            # Get the name and id attributes that the Child_4 tag contains
            self.controller.shared_data['tc'] = [
                {"Name": signal.attrib["Name"],
                 "Id": signal.attrib["Id"],
                 } for signal in file_xml.findall(".//Child_4")
            ]
            print('First TC:', self.controller.shared_data['tc'])
            controller.show_frame("PageOne")

        # Function to open xml file
        def open_file():
            try:
                xml_name = str(easygui.fileopenbox(title='Select XML file', default='*.xml'))
                if str(os.path.abspath(xml_name)) != os.path.join(os.path.abspath(os.getcwd()),
                                                                   os.path.basename(xml_name)):
                    separate_tc(os.path.basename(str(xml_name)))
                else:
                    separate_tc(os.path.basename(str(xml_name)))
            except FileNotFoundError:
                print('XML file was not loaded.')

        button_open = tk.Button(self, text="Open File XML", command=open_file)
        button_open.place(relx=.01, rely=.05)


class AppMain(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.wm_title(self, 'Analysis of a XML')

        self.shared_data = {'tc': []}

        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 = {}

        for F in (StartPage, PageOne):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame("StartPage")

    def show_frame(self, page_name):
        '''Show a frame for the given page name'''
        frame = self.frames[page_name]
        frame.tkraise()


if __name__ == "__main__":
    app = AppMain(None)
    app.title('Analysis of XML')
    app.geometry("1024x920")
    app.mainloop()

What I want to do is obtain the value of tc_xml and that is why I am assigning it in the variable controller.shared_data ['tc'] in the Home class, as I think I understand from the response of Storing data in the controller, but at the time of sending calling the variable in the PageOne class, brings it to me empty and I don't understand why. If someone could help me I would be very grateful, please.

MayEncoding
  • 87
  • 1
  • 12
  • At one point you set `shared_data['tc']` to an instance of `StringVar`, but later you are setting it to a list of dictionaries. What do you want the shared data to be, a `StringVar` or a list of dictionaries? – Bryan Oakley Sep 21 '21 at 02:52
  • Thanks for your answer @Bryan Sorry, I was trying both, but I can use only one. – MayEncoding Sep 21 '21 at 02:54
  • You didn't answer my question. Are you expecting it to be a list of dictionaries or a StringVar? – Bryan Oakley Sep 21 '21 at 02:57
  • I expect it is StringVar() – MayEncoding Sep 21 '21 at 02:59
  • Are you expecting to store the list of dictionaries in the `StringVar`? If so, why? If not, what do you expect to store in it? – Bryan Oakley Sep 21 '21 at 03:05
  • Through the `separate_tc` function, I extract the `Child_4` tags that are repeated in the [xml file](https://github.com/MarshRangel/Python/blob/develop/TestCase.xml) and that in turn have an id as an attribute, as can see, in the `PageOne` class I use a `for` to separate those id in buttons and later to be able to graph some values ​​that contain other labels of that same tree, I already do all that, I'm just adapting the pages to classes to make the window more dynamic in tkinter. – MayEncoding Sep 21 '21 at 03:23
  • That didn't answer my question. It's not clear why you think you need to use a `StringVar`, or what you want to put in the `StringVar`. – Bryan Oakley Sep 21 '21 at 03:27
  • In the variable `"tc": tk.StringVar()`, which I declare inside the `self.shared_data` dictionary, I want to put the list of `tc_xml` dictionaries found in the `Home` class, which contains a name and an id, contained within the xml file, to later be able to obtain it from the `PageOne` class on the line `self.controller.shared_data ['tc'].get()`, which is where the empty value is returning. – MayEncoding Sep 21 '21 at 03:35
  • A `StringVar` is designed to take a string, not a list of dictionaries. It makes no sense to put it in a `StringVar`. When you get the data back out it will no longer be a list of dictionaries. – Bryan Oakley Sep 21 '21 at 03:36
  • Ok thanks, what do you recommend me to use? to get the value of the variable tc_xml. – MayEncoding Sep 21 '21 at 03:39
  • Just use the original list. – Bryan Oakley Sep 21 '21 at 03:40
  • Ok, just a comment: as it is currently, when I execute `print("First values ​​TC:", controller.shared_data['tc'])` if I paint the values ​​well, which are a list of dictionaries, only when I send a call the values ​​in the `PageOne` class, executing `self.tc_xml = self.controller.shared_data['tc'].get()` `print ("Values ​​TC xml:" + self.tc_xml)`, is where it sends me empty as if the value did not I was getting it. It is what I do not understand why. – MayEncoding Sep 21 '21 at 03:49

1 Answers1

0

In the comments you say you want this bit of shared data to be a StringVar, but then you set it to a list of dictionaries. I don't understand why you need to use a StringVar. Storing a list of dictionaries in a StringVar won't still be a list of dictionaries when you take the data back out of the StringVar.

I think you're overcomplicating the problem, Within the app you can use self.shared_data['tc'], and within any other class that has a self.controller property you use self.controller.shared_data['tc'].

For example, in the Home class, inside the separate_tc function, you can set the value like you would any other dictionary:

class Home(tk.Frame):
    def __init__(self, parent, controller):
        super().__init__(parent)
        self.controller = controller
        ...
        def separate_tc(xml_name):
            file_xml = ET.parse(xml_name)
            self.controller.shared_data['tc'] = [
                {"Name": signal.attrib["Name"],
                "Id": signal.attrib["Id"],
                } for signal in file_xml.findall(".//Child_4")
            ]

Later, you can use this data in the PageOne class:

class PageOne(tk.Frame):
    def __init__(self, parent, controller):
        super().__init__(parent)
        self.controller = controller
        ...
        for item in self.controller.shared_data['tc']:
            print("name:", item['Name'], "id:", item['id'])

The only remaining piece of the puzzle is that you need to define the shared data before you attempt to use it. In the code you shared, self.shared_data['tc'] isn't defined until you try to open a file. However, both PageOne and PageTwo try to access the data when the classes are first created rather when they are first shown. You must make sure that you don't try to use the data before you define it.

Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Thanks Byran, I'm using the code as you are putting it: `self.controller.shared_data ['tc'] = [{...} for ... ]` and then I call the class, executing: `controller.show_frame ("PageOne")`, and when I execute `for i in self.controller.shared_data ['tc']: print("Name:", i ['Name'], "Id:", i ['Id'])` still nothing comes out, what's more, a line above I put: `print("Second TC:", self.controller.shared_data ['tc'])` it comes empty. Will it be a versioning problem? I've Python 3.6.5, but I don't think so. – MayEncoding Sep 22 '21 at 00:14
  • @MarshRangel: no, it's not a versioning problem. Tkinter hasn't changed significantly in years, and none of this is unique to tkinter. My guess is that you're simply trying to access the data before you define it. – Bryan Oakley Sep 22 '21 at 01:45
  • I edited my code with your solution, I rearranged my code leaving only two classes and when I pass the variables to `PageOne`, I practically left it only with the printing of the variable that I want to pass to that class and it keeps sending me the variable empty when I execute the line : `print("Second TC:", self.controller.shared_data ['tc'])` or `print("Name:", i ['Name'], "Id:", i ['Id'])` – MayEncoding Sep 22 '21 at 15:55
  • @MarshRangel: your print statement occurs before you open the XML file because `PageOne` is instantiated at the very start of the program, just as the answer says. – Bryan Oakley Sep 22 '21 at 18:05