Search code examples
pythonpython-class

Struggling with class methods, cls, and self when using functions cross classes in python while updating a dictionary


I am trying to build a small UI for updating some settings and then displaying some intake data on a raspberrypi. Python version is 3.9.2. The most logical way to save the settings I thought would be building JSON and then saving it to a file.

I am struggling to get the json_data dictionary to update and retain the settings. I got rid of the __init__ for some tries using @classmethod with cls instead of self. I tried @staticmethod, but that didn't seem right based on what I think I understand about that decorator. I tried using args, **args, **kwargs into the function.

The Settings.update_settings() is called from multiple classes at different times to update the settings. I have been able to get the json_data to update, but the problem is after each call to update_settings the json_data is reset when using classmethod. When using self it is, which I think is expected, a missing self argument when called outside the Settings class.

The reason I need to save off the pieces of the json as they are entered is I am destroying the widgets when moving between settings groups.

Here is the main screen (MainApp) where they input the first update for the JSON in the Settings class.

from guizero import App, Box, info, Picture, PushButton, Text, TextBox, Window, yesno
from datetime import datetime as dt
import subprocess
import sys
from data_output_setup_page import LoggerSetupPage
from logging_data_display import DataDisplayPage
import logging as log
from utilities import Settings


class MainApp:


    def __init__(self):

        self.submit_site_text = 'Submit'
        self.app = App('Epic One Water Pulse Logging',
                       width=800, height=480, bg='#050f2b')
        # Maximize the app window
        #self.app.tk.attributes('-fullscreen', True)

        # Top Box for header
        self.top_box = Box(self.app, layout="grid")
        self.brand = Picture(self.top_box, image=r'/home/ect-one-user'
                                                 r'/Desktop/One_Water_Pulse_Logger'
                                                 r'/assets/Brand.png',
                             align='left', grid=[0, 0])
        self.header = Text(self.top_box, text=dt.now().strftime('%Y-%m-%d %H:%M:%S'),
                           align='right', grid=[1, 0], width='fill')
        self.header.text_color = 'white'
        self.header.width = 90
        
        self.welcome_box = Box(self.app, layout='grid', align='top')
        self.welcome_text = Text(self.welcome_box, text="Welcome to the ECT Pulse Logger Prototype.",
                                 size=14, grid=[0, 0])
        self.welcome_text.text_color = 'white'
        self.welcome_text.width = 90

        self.welcome_text_l2 = Text(self.welcome_box, text="Please send any feedback to this_email@Gmail.com",
                                    size=14, grid=[0, 1])
        self.welcome_text_l2.text_color = 'white'
        self.welcome_text_l2.width = 90

        # Middle of Screen box
        self.box = Box(self.app, layout='grid', align='top')
        self.spacer = Text(self.box, text='', grid=[0, 1], width='fill')

        self.site_name_label = Text(self.box, text='Site Name:', grid=[0, 2])
        self.site_name_label.text_color = 'white'

        self.l_spacer = Text(self.box, text='', grid=[1, 2])  

        self.site_name = TextBox(self.box, grid=[2, 2])
        self.site_name.width = 20
        self.site_name.bg = 'white'

        self.r_spacer = Text(self.box, text='', grid=[3, 2])
        
        self.submit_site =  PushButton(self.box, text=self.submit_site_text,
        command=self.site_lock, grid = [4, 2])
        self.submit_site.text_color = 'white'
        
        self.spacer = Text(self.box, text='', grid=[0, 3], width='fill')
        
        self.sv_stg_to_file = PushButton(self.box, text='Save Settings File',
        command=Settings.save_to_json, grid=[0, 4, 3, 1])
        self.sv_stg_to_file.text_color = 'white'
        

        # Create a button holder at bottom of screen
        self.bottom_box = Box(self.app, layout='grid', align='bottom')
        self.open_ds_button = PushButton(self.bottom_box, text='Logger Setup',
                                         command=lambda: self.open_window(
                                             LoggerSetupPage, 'Logger Setup'),
                                         align='left', grid=[0, 0])
        self.open_ds_button.text_color = 'white'
        self.open_ds_button.hide()
            

        self.open_db_button = PushButton(self.bottom_box, text='Open Logging',
                                         command=lambda: self.open_window(
                                             DataDisplayPage, 'Logging'),
                                         align='left', grid=[1, 0])
        self.open_db_button.text_color = 'white'
        self.open_db_button.hide()

        self.close_button = PushButton(self.bottom_box, text='Shutdown Logger',
                                       command=self.exit_pgm,
                                       align='left', grid=[2, 0])
        self.close_button.text_color = 'white'

    def run(self):

        self.app.display()
        
    def get_settings(self):

        self.settings = Settings.retrieve_settings()
        print(self.settings)
        if not isinstance(self.settings, type(None)):
            load_settings = yesno('Load Settings', 'Settings file found. Load settings?')
            if load_settings:
                self.site_name.value = self.settings['Site Name']
                self.logger_setup.import_settings(self.settings)
        elif isinstance(self.settings, type(None)):
            info('Config', 'No settings file found. Please configure settings.')

    def check_json(self):

        self.local_settings = Settings.check_json()
        print(self.local_settings)
        if self.local_settings:
            info('Config', 'Settings ready for save.')
            self.sv_stg_to_file.show()
        else:
            self.sv_stg_to_file.hide()

    def site_lock(self):

        if self.submit_site_text == 'Submit':
            self.site_name.disable()
            self.submit_site_text = 'Alter Site Name'
            Settings.update_settings({
                'Settings':
                    {'Site Name': self.site_name.value}
                })
            self.get_settings()
            self.open_ds_button.show()
            self.open_db_button.show()

            # Add a log statement
            log.info('Site name updated to {0}'.format(self.site_name.value))

        else:
            self.site_name.enable()
            self.submit_site_text = 'Submit'
            self.open_ds_button.hide()
            self.open_db_button.hide()

            # Add a log statement
            log.info('Site name updated to {0}'.format(self.site_name.value))

        self.submit_site.text = self.submit_site_text

    def open_window(self, module, wdw_name):

        self.app.hide()
        new_window = Window(
            self.app, title=wdw_name, width=800, height=480, bg='#050f2b')
        #new_window.tk.attributes('-fullscreen', True)
        
        # Create an instance of DataDisplayPage
        open_page = module(new_window, self)
        new_window.show()

    def exit_pgm(self):

        self.app.destroy()
        # subprocess.Popen(['shutdown','-h','now'])
        sys.exit()


if __name__ == "__main__":
    app = MainApp()
    app.check_json()
    app.run()

Here is my settings class

from botocore.client import Config
import boto3
import collections.abc
import ftplib
import json
import logging

from base64 import b64decode as bd
from datetime import datetime as dt


class Settings:

settings_directory = '/home/ect-one-user/Desktop/One_Water_Pulse_Logger/config/'
settings_filename = '_logger_config.json'
json_data = {
        'Settings': {
            'Site Name': None,
            'Sensor': {

                },
            'Data Output': {
                
                },
            'Email Address': {

                }
            }
        }

@staticmethod
def update_settings(d):
    
   Settings.json_data.update(d)

@staticmethod
def check_json():
    print(Settings.json_data)
    try:
        if Settings.json_data['Settings']['Site Name'] is not None \
            and Settings.json_data['Settings']['Sensor']['Name'] is not None \
            and Settings.json_data['Settings']['Data Output']['Location'] is not None:
            return True
    except:
        return False

LoggerSetupPage Class

from base64 import b64encode as be
from base64 import b64decode as bd
from datetime import datetime as dt
from guizero import Box, Combo, info, Picture, ListBox, PushButton, Text, TextBox, Window
from utilities import Settings

class LoggerSetupPage:

    def __init__(self, parent, main_app):

        self.parent = parent
        self.main_app = main_app
        self.current_row = 0       
        self.settings_dict = {}
        self.widgets_to_destroy = []
        
        # Top Box for header
        self.top_box = Box(self.parent, layout='grid')

        # Display the brand logo in the top left corner of the main window.
        self.brand = Picture(self.top_box,
                             image='/home/ect-one-user/Desktop/One_Water_Pulse_Logger/assets/Brand.png'
                             , align='left', grid=[0, 0])

        self.header = Text(self.top_box,
                           text=dt.now().strftime('%Y-%m-%d %H:%M:%S'),
                           align='right', grid=[1, 0])
        self.header.width = 90
        self.header.text_color = 'white'


        # Middle box
        self.mid_box = Box(self.parent, layout='grid')

        self.config_selection = Combo(
            self.mid_box,
            options=[ 'Data Output Config', 'Sensor Config'],
            command=self.check_selection,
            grid=[0, 0]
        )
        self.config_selection.text_color = 'white'
        self.config_selection.text_size = 16

        # Bottom box for buttons
        self.bottom_box = Box(self.parent, layout='grid', align='bottom')

        self.return_button = PushButton(self.bottom_box,
                text='Return to Main Page',
                command=self.return_to_main, align='bottom', grid=[0, 2])
        self.return_button.text_color = 'white'

    def return_to_main(self):

        self.main_app.app.show()
        self.main_app.check_json()
        self.parent.destroy()

    def create_input_list(self):
        
            if self.config_selection.value == 'Data Output Config':
                    
                self.data_output_choice_label = Text(self.mid_box, text='Data Output:',
                grid=[0, 0])
                self.data_output_choice_label.text_color = 'white'

                self.data_output_choice = Combo(self.mid_box,
                options=['local', 's3', 'ftp'], command=self.check_sub_selection,
                grid=[1, 0])
                self.data_output_choice.text_color = 'white'

                self.current_row += 1

                self.widgets_to_destroy.extend([
                    self.data_output_choice_label,
                    self.data_output_choice
                    ])

    def create_inputs(self):

        if self.config_selection.value == 'Sensor Config':

            self.sn_label = Text(self.mid_box, text='Sensor Name:',
                align='left', grid=[0, 1])
            self.sn_label.text_color = 'white'
            self.sn_input = TextBox(self.mid_box, grid=[1, 1], width=30,
                align='left',)
            self.sn_input.text_color = 'white'

            self.current_row += 1
            
            self.kf_label = Text(self.mid_box, text='K Factor:',
                align='left', grid=[0, 2])
            self.kf_label.text_color = 'white'
            self.kf_input = TextBox(self.mid_box, grid=[1, 2], width=10,
            align='left',)
            self.kf_input.text_color = 'white'

            self.current_row += 1
            
            self.su_label = Text(self.mid_box, text='Sensor Units:',
                        align='left', grid=[0, 3])
            self.su_label.text_color = 'white'
            self.su_input = TextBox(self.mid_box, grid=[1, 3], width=10,
            align='left',)
            self.su_input.text_color = 'white'

            self.current_row += 1
            
            self.du_label = Text(self.mid_box, text='Desired Units:', grid=[0, 4])
            self.du_label.text_color = 'white'
            self.du_input = TextBox(self.mid_box, grid=[1, 4], width=10,
            align='left',)
            self.du_input.text_color = 'white'

            self.current_row += 1

            self.widgets_to_destroy.extend([
                self.sn_label,
                self.sn_input,
                self.kf_label,
                self.kf_input,
                self.su_label,
                self.su_input,
                self.du_label,
                self.du_input
                ])
                      
        elif self.data_output_choice.value == 's3':

            self.l_spacer = Text(self.mid_box, text='', grid=[0, 1], width = 'fill')

            self.current_row += 1             

            self.s3_bucket_label = Text(self.mid_box, text='S3 Bucket:',
            grid=[0, 2], align='left')
            self.s3_bucket_label.text_color = 'white'

            self.s3_bucket_input = TextBox(self.mid_box, grid=[1, 2], width=30,
            align='left')
            self.s3_bucket_input.text_color = 'white'

            self.current_row += 1

            self.s3_prefix_label = Text(self.mid_box, text='S3 Folder:',
            grid=[0, 3], align='left')
            self.s3_prefix_label.text_color = 'white'

            self.s3_prefix_input = TextBox(self.mid_box, grid=[1, 3], width=30,
            align='left')
            self.s3_prefix_input.text_color = 'white'

            self.current_row += 1

            self.s3_key_label = Text(self.mid_box, text='S3 Filename:', 
            grid=[0, 4], align='left')
            self.s3_key_label.text_color = 'white'

            self.s3_key_input = TextBox(self.mid_box, grid=[1, 4], width=30,
            align='left')
            self.s3_key_input.text_color = 'white'

            self.current_row += 1

            self.s3_ak_label = Text(self.mid_box, text='User Access Key:',
            grid=[0, 5], align='left')
            self.s3_ak_label.text_color = 'white'

            self.s3_ak_input = TextBox(self.mid_box, grid=[1, 5], width=30,
            align='left')
            self.s3_ak_input.text_color = 'white'

            self.current_row += 1

            self.s3_sk_label = Text(self.mid_box, text='User Secret Key:',
            grid=[0, 6], align='left')
            self.s3_sk_label.text_color = 'white'

            self.s3_sk_input = TextBox(self.mid_box, grid=[1, 6], width=30,
            align='left')
            self.s3_sk_input.text_color = 'white'

            self.current_row += 1

            self.s3_role_label = Text(self.mid_box, text='Role to Assume:',
            grid=[0, 7], align='left')
            self.s3_role_label.text_color = 'white'

            self.s3_role_input = TextBox(self.mid_box, grid=[1, 7], width=30,
            align='left')
            self.s3_role_input.text_color = 'white'

            self.current_row += 1

            self.widgets_to_destroy.extend([
                self.s3_bucket_label,
                self.s3_bucket_input,
                self.s3_prefix_label,
                self.s3_prefix_input,
                self.s3_key_label,
                self.s3_key_input,
                self.s3_ak_label,
                self.s3_ak_input,
                self.s3_sk_label,
                self.s3_sk_input,
                self.s3_role_label,
                self.s3_role_input,
                self.l_spacer
                ])

        elif self.data_output_choice.value == 'ftp':
            
            self.l_spacer = Text(self.mid_box, text='', grid=[0, 1], width = 'fill')

            self.ftp_host_label = Text(self.mid_box, text='FTP Host:',
            grid=[0, 2], align='left')
            self.ftp_host_label.text_color = 'white'

            self.ftp_host_input = TextBox(self.mid_box, grid=[1, 2], width=30,
            align='left')
            self.ftp_host_input.text_color = 'white'

            self.current_row += 1

            self.ftp_port_label = Text(self.mid_box, text='FTP Port:',
            grid=[0, 3], align='left')
            self.ftp_port_label.text_color = 'white'

            self.ftp_port_input = TextBox(self.mid_box, grid=[1, 3], width=30,
            align='left')
            self.ftp_port_input.text_color = 'white'

            self.current_row += 1

            self.ftp_un_label = Text(self.mid_box, text='FTP Username:',
            grid=[0, 4], align='left')
            self.ftp_un_label.text_color = 'white'

            self.ftp_un_input = TextBox(self.mid_box, grid=[1, 4], width=30,
            align='left')
            self.ftp_un_input.text_color = 'white'

            self.current_row += 1

            self.ftp_pwd_label = Text(self.mid_box, text='FTP Password:',
            grid=[0, 5], align='left')
            self.ftp_pwd_label.text_color = 'white'

            self.ftp_pwd_input = TextBox(self.mid_box, grid=[1, 5], width=30,
            align='left')
            self.ftp_pwd_input.text_color = 'white'

            self.current_row += 1

            self.ftp_dir_label = Text(self.mid_box, text='Save Location:',
            grid=[0, 6], align='left')
            self.ftp_dir_label.text_color='white'

            self.ftp_dir_input = TextBox(self.mid_box, grid=[1, 6], width=30,
            align='left')
            self.ftp_dir_input.text_color='white'

            self.current_row += 1

            self.widgets_to_destroy.extend([
                self.ftp_host_label,
                self.ftp_host_input,
                self.ftp_port_label,
                self.ftp_port_input,
                self.ftp_un_label,
                self.ftp_un_input,
                self.ftp_pwd_label,
                self.ftp_pwd_input,
                self.ftp_dir_label,
                self.ftp_dir_input,
                self.l_spacer
                ])

        elif self.data_output_choice.value == 'local':
            
            self.l_spacer = Text(self.mid_box, text='', grid=[0, 1], width = 'fill')

            self.email_address_label = Text(self.mid_box, text='Email Address:',
            grid=[0, 2], align='left')
            self.email_address_label.text_color = 'white'

            self.email_address_input = TextBox(self.mid_box, grid=[1, 2], width=40,
            align='left')
            self.email_address_input.text_color = 'white'

            self.current_row += 1

            self.widgets_to_destroy.extend([
                self.email_address_label,
                self.email_address_input,
                self.l_spacer
                ])
                         
        # Create a button to return the ListBox to visible
        self.show_list_btn = PushButton(self.bottom_box, text='Back to List',
        command=self.show_config, grid=[0, self.current_row+1],
        align='bottom')
        self.show_list_btn.text_color = 'white'

        self.save_settings_btn = PushButton(self.bottom_box, text='Save Settings',
        command=self.save_settings, grid=[1, self.current_row+1], align='bottom')
        self.save_settings_btn.text_color = 'white'
        
        self.widgets_to_destroy.extend([
            self.show_list_btn,
            self.save_settings_btn
            ])
                
    def import_settings(self, kwargs):
        if kwargs['Location'] == 's3':
            self.data_output_choice.value = 's3'
            self.s3_bucket_input.value = kwargs['Settings']['Data Output']['Bucket']
            self.s3_prefix_input.value = kwargs['Settings']['Data Output']['Prefix']
            self.s3_key_input.value = kwargs['Settings']['Data Output']['Key']
            self.s3_ak_input.value = bd(kwargs['Settings']['Data Output']\
                ['Auth']['Access Key']).decode('utf-8')
            self.s3_sk_input.value = bd(kwargs['Settings']['Data Output']\
                ['Auth']['Secret Key']).decode('utf-8')
            self.s3_role_input.value = kwargs['Settings']['Data Output']\
                ['Auth']['Role']
        elif kwargs['Location'] == 'ftp':
            self.data_output_choice.value = 'ftp'
            self.ftp_host_input.value = kwargs['Settings']['Data Output']['Host']
            self.ftp_port_input.value = kwargs['Settings']['Data Output']['Port']
            self.ftp_un_input.value = bd(kwargs['Settings']['Data Output']\
                ['Auth']['Username']).decode('utf-8')
            self.ftp_pwd_input.value = bd(kwargs['Settings']['Data Output']\
                ['Auth']['Password']).decode('utf-8')
            self.ftp_dir_input.value = kwargs['Settings']['Data Output']['Directory']
        else:
            self.data_output_choice.value = 'local'
            self.email_input.value = kwargs['Email Address']
        
        self.sn_input.value = kwargs['Settings']['Sensor']['Name']
        self.kf_input.value = kwargs['Settings']['Sensor']['K Factor']
        self.su_input.value = kwargs['Settings']['Sensor']['Standard Unit']
        self.du_input.value = kwargs['Settings']['Sensor']['Desired Unit']
    
    def save_settings(self):

        if self.config_selection.value == 'Data Output Config':
            if self.data_output_choice.value == 's3':
                self.settings_dict.update({
                    'Settings': {
                        'Data Ouput': {
                            'Location': self.data_output_choice.value,
                            'Bucket': self.s3_bucket_input.value,
                            'Prefeix': self.s3_prefix_input.value,
                            'Key': self.s3_key_input.value,
                            'Access Key': be(self.s3_ak_input.value.encode('utf-8')),
                            'Secret Key': be(self.s3_sk_input.value.encode('utf-8')),
                            'Role': self.s3_role_input.value
                        }
                    }
                })

            elif self.data_output_choice.value == 'ftp':
                self.settings_dict.update({
                    'Settings': {
                        'Data Ouput': {
                            'Location': self.data_output_choice.value,
                            'Host': self.ftp_host_input.value, 
                            'Port': self.ftp_port_input.value,
                            'Username': be(self.ftp_un_input.value.encode('utf-8')),
                            'Password': be(self.ftp_pwd_input.value.encode('utf-8')),
                            'Directory': self.ftp_dir_input.value
                        }
                    }
                })
            else:
                self.settings_dict.update({
                    'Settings': {
                        'Data Ouput': {
                            'Location': self.data_output_choice.value,
                            'Email Address': self.email_address_input.value
                        }
                    }
                })

        elif self.config_selection.value == 'Sensor Config':
            self.settings_dict.update({
                'Settings': {
                    'Sensor': {
                        'Name': self.sn_input.value,
                        'K Factor': self.kf_input.value,
                        'Standard Unit': self.su_input.value,
                        'Desired Unit': self.du_input.value
                    }
                }
            })
            
        Settings.update_settings(self.settings_dict)
        info('success', 'settings staged.')
        self.return_to_main()

    def check_selection(self):

        if self.config_selection.value == 'Data Output Config':          
            # Hide the ListBox
            self.config_selection.hide()
            self.return_button.hide()
            
            # Create input widgets
            self.create_input_list()
            self.create_inputs()

        elif self.config_selection.value == 'Sensor Config':
            # Hide the ListBox
            self.config_selection.hide()
            self.return_button.hide()
            
            # Create input widgets
            self.create_inputs()

    def check_sub_selection(self):

        if self.data_output_choice.value in ['ftp', 's3'] \
            and self.config_selection.visible == False:
            # Destroy input widgets and the "Show List" button
            self.destroy_widgets()

            # Create input widgets
            self.create_inputs()

    def destroy_widgets(self):

        # Destroy existing input widgets if there are any
        for widget in self.widgets_to_destroy:
            widget.destroy()

        self.widgets_to_destroy = []  # Clear the list

    def show_config(self):

        # Destroy input widgets and the "Show List" button
        self.destroy_widgets()

        # Show the ListBox
        self.config_selection.show()
        self.return_button.show()

I have not used classes in pretty much any of my previous projects because it was a single use script for a very specific purpose generally related to moving data around which only had a few small functions. I am trying to expand my abilities, but I am really struggling to understand and fix this issue. I have spent way too much time on this and have to go to the next hard part of using threading to display the data and then log it at certain intervals.

What is the most efficient way to change the inputs or function to retain the necessary settings and then make the save settings button visible for writing the JSON to a file?

current status based on provided answer:

On Start up - check_json {'Settings': {'Site Name': None, 'Sensor': {}, 'Data Output': {}, 'Email Address': {}}}

after save settings button on LoggerSetupPage with local chosen - check_json {'Settings': {'Data Ouput': {'Location': 'local', 'Email Address': ''}}}

after save settings button on LoggerSetupPage with local chosen and sensor setup entered - check_json {'Settings': {'Sensor': {'Name': '123', 'K Factor': '123', 'Standard Unit': '2512', 'Desired Unit': '441'}}}

This is the behavior I saw before. it isn't retaining the previously added dictionary items. It is resetting itself. Probably because I am updating an instance and not the actual class level dictionary.


Solution

  • This is the place I ended up. There might be more efficient ways, but this is working after multiple tests.

    from benedict import benedict as bdict
    from botocore.client import Config
    import boto3
    import ftplib
    import json
    import logging
    
    from base64 import b64decode as bd
    from datetime import datetime as dt
    
    class LoggerSettings:
    
    settings_directory = '/home/ect-one-user/Desktop/One_Water_Pulse_Logger/config/'
    settings_filename = '_logger_config.json'
    json_data = {}
    
    @staticmethod
    def update_settings(d):
        bdict(LoggerSettings.json_data).merge(d, overwrite=True)
    
    @classmethod
    def check_json(cls):
        
        keys_to_get = ['Site Name', 'Sensor', 'Data Output']
        keys_exist = []
        
        while len(keys_exist) < len(keys_to_get):
            keys_exist.append(False)
        
        for i in range(len(keys_to_get)):
            if bdict.from_json(cls.json_data)\
                .search(keys_to_get[i], in_keys=True, in_values=True):        
                keys_exist[i] = True
    
        return keys_exist
    
    @staticmethod
    def save_to_json():
        
        json_file = LoggerSettings.settings_directory + \
            LoggerSettings.json_data['Settings']['Site Name'] + \
            LoggerSettings.settings_filename
        # Serialize the JSON data to a file
        with open(json_file, 'w') as sf:
            json.dump(LoggerSettings.json_data, sf, indent=4)  # Use json.dump() to serialize the data
    
        sf.close()
        return {'Settings' : 'Saved to file'}
    

    The check_json when now called from MainApp after entries is showing something like

    {'Site Name': 'Test Site', 'Sensor': {'Name': '1245123', 'K Factor': '12354', 'Standard Unit': '123asdf', 'Desired Unit': '123561231'}, 'Data Ouput': {'Location': 's3', 'Bucket': '123', 'Prefeix': '1155123', 'Key': '155123', 'Access Key': b'MTI1MTIz', 'Secret Key': b'MTEyMzEyNjY3', 'Role': '1123125'}}}
    

    This edit includes the updated JSON check, and here update to MainApp class verify_json function to use all() on the list returned.

    def verify_json(self):
    
        self.local_settings = LoggerSettings.check_json()
        print(self.local_settings)
        if all(self.local_settings):
            info('Config', 'Settings ready for save.')
            self.sv_stg_to_file.show()
    

    Thanks for not being like the common theme of people here Tax Evader; I appreciate your help. I understand classes and singletons a bit better, but still a way to go.