Click here to Skip to main content
15,895,142 members
Home / Discussions / Python
   

Python

 
GeneralRe: ObjectDetectionCoral v1.5.1 - where is python.exe Pin
Richard N7-Oct-23 0:21
Richard N7-Oct-23 0:21 
GeneralRe: ObjectDetectionCoral v1.5.1 - where is python.exe Pin
Richard MacCutchan7-Oct-23 0:48
mveRichard MacCutchan7-Oct-23 0:48 
QuestionHOW TO register in Azure DevOps a CallBack, which triggers a Python function when a work items has been updated Pin
Member 140680196-Sep-23 9:32
Member 140680196-Sep-23 9:32 
AnswerRe: HOW TO register in Azure DevOps a CallBack, which triggers a Python function when a work items has been updated Pin
Richard MacCutchan6-Sep-23 21:57
mveRichard MacCutchan6-Sep-23 21:57 
GeneralRe: HOW TO register in Azure DevOps a CallBack, which triggers a Python function when a work items has been updated Pin
Member 140680197-Sep-23 13:44
Member 140680197-Sep-23 13:44 
GeneralRe: HOW TO register in Azure DevOps a CallBack, which triggers a Python function when a work items has been updated Pin
Richard MacCutchan7-Sep-23 23:23
mveRichard MacCutchan7-Sep-23 23:23 
QuestionPython Project Pin
African Electronic Literature2-Sep-23 21:15
African Electronic Literature2-Sep-23 21:15 
QuestionUpdating a Dictionary in python iteratively Pin
Maverick49426-Aug-23 12:39
Maverick49426-Aug-23 12:39 
I will preface this by saying this is going to be a bit long. I have a few code piece to post, but I will try and only post the relevant pieces unless someone requests all of it.

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 in different .py files 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/staticmethod. When using self it is, which I think is expected, throwing a missing self argument when called outside the Settings class.

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. So I am looking for some help to get this JSON to not reset every time I try and add to it.

Here is the relevant portions of the MainApp where they input the first updates 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)
        
        ........
    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


LoggerSetupPage class is next, it is long because I think it is important to understand what I am doing with it. Basically I have created a window where I create and destroy widgets after they are used to make the entries. This is why I need to iteratively update the json_data in Settings. I will remove as much as I can that is not relevant, but if you want the entire code I can link it from GitHub.


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 = []
        
        ...

        # 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 == '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()


Finally, the relevant pieces of the current iteration of 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


I have tried multiple ways of updating the json_data as well - .update(), setattr, d | json_data. So far .update is the only one that has done anything, but it isn't quite right. With this current iteration of my Settings class and the other two classes which I posted here is the outcome

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

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

after save settings button on LoggerSetupPage post Data Output when I entered sensor setup - check_json {'Settings': {'Sensor': {'Name': '123', 'K Factor': '123', 'Standard Unit': '2512', 'Desired Unit': '441'}}}


I think this has something to do with different instances of the class perhaps, but I can't figure out how to just use the same class instance for each additional piece of the dictionary.

modified 26-Aug-23 18:47pm.

AnswerRe: Updating a Dictionary in python iteratively Pin
Richard MacCutchan26-Aug-23 21:11
mveRichard MacCutchan26-Aug-23 21:11 
GeneralRe: Updating a Dictionary in python iteratively Pin
User 1604268027-Aug-23 6:27
User 1604268027-Aug-23 6:27 
GeneralRe: Updating a Dictionary in python iteratively Pin
Richard MacCutchan27-Aug-23 6:30
mveRichard MacCutchan27-Aug-23 6:30 
QuestionWhich technique to apply if function Y changes with values of X variables other than linear regression Pin
Member 106396367-Aug-23 12:42
Member 106396367-Aug-23 12:42 
AnswerRe: Which technique to apply if function Y changes with values of X variables other than linear regression Pin
Dave Kreskowiak7-Aug-23 12:57
mveDave Kreskowiak7-Aug-23 12:57 
GeneralRe: Which technique to apply if function Y changes with values of X variables other than linear regression Pin
Andre Oosthuizen7-Aug-23 23:18
mveAndre Oosthuizen7-Aug-23 23:18 
QuestionFile memory usage issues Pin
DevilDuck1236-Jul-23 20:26
DevilDuck1236-Jul-23 20:26 
AnswerRe: File memory usage issues Pin
Maverick49426-Aug-23 13:05
Maverick49426-Aug-23 13:05 
QuestionRoom arrangements Pin
ajay singh May20232-Jul-23 8:53
ajay singh May20232-Jul-23 8:53 
AnswerRe: Room arrangements Pin
Richard MacCutchan2-Jul-23 21:52
mveRichard MacCutchan2-Jul-23 21:52 
AnswerRe: Room arrangements Pin
Andre Oosthuizen3-Jul-23 0:43
mveAndre Oosthuizen3-Jul-23 0:43 
QuestionInvalid signature - Fondy payment gateway Pin
Kostiantyn Lahutin27-Jun-23 4:04
Kostiantyn Lahutin27-Jun-23 4:04 
AnswerRe: Invalid signature - Fondy payment gateway Pin
Richard MacCutchan27-Jun-23 5:23
mveRichard MacCutchan27-Jun-23 5:23 
GeneralRe: Invalid signature - Fondy payment gateway Pin
Kostiantyn Lahutin29-Jun-23 0:15
Kostiantyn Lahutin29-Jun-23 0:15 
AnswerRe: Invalid signature - Fondy payment gateway Pin
jschell28-Jun-23 6:15
jschell28-Jun-23 6:15 
GeneralRe: Invalid signature - Fondy payment gateway Pin
Kostiantyn Lahutin29-Jun-23 0:13
Kostiantyn Lahutin29-Jun-23 0:13 
AnswerRe: Invalid signature - Fondy payment gateway Pin
Maverick49426-Aug-23 13:40
Maverick49426-Aug-23 13:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.