Giter Site home page Giter Site logo

Gradually Increases Memory about guizero HOT 11 CLOSED

iyssoft avatar iyssoft commented on July 27, 2024
Gradually Increases Memory

from guizero.

Comments (11)

aajshaw avatar aajshaw commented on July 27, 2024

Can we see the source code of the app?

from guizero.

iyssoft avatar iyssoft commented on July 27, 2024

First one is MixerUI.p
Second one is Mixer.py
Third one is MixerItem.py
`from i2c_discoverer import I2cDiscoverer

from guizero import App

from Mixer import Mixer

from common import debug, set_debug_level, is_running_on_raspberry_pi

import argparse

from pin_manager import PinManager

from slot_sensor_and_stepper_motor_client import SlotSensorAndStepperMotorClient

if is_running_on_raspberry_pi():

import microcontroller

from microcontroller.pin import i2cPorts

microcontroller.pin.i2cPorts = (

    microcontroller.pin.i2cPorts) + ((3, 7, 8), (4, 9, 10),)

def handle_args():

parser = argparse.ArgumentParser(description='GUI Scale pgm!')

parser.add_argument("--d", default=50, type=int,

                    help="This is the debug level  variable")

args = parser.parse_args()

debug_level = args.d

print("debug_level= "+str(debug_level) + " args="+str(args))

set_debug_level(debug_level)

return args

def main():

handle_args()

discoverer = I2cDiscoverer()

root = App(height=600, width=1000, title="Ifs Mixer")

root.full_screen = True

Mixer(root, discoverer, PinManager())

discoverer.start()

root.display()

if name == "main":

main()

`

from guizero.

iyssoft avatar iyssoft commented on July 27, 2024

`import time

import gaugelib

from datetime import datetime

from guizero import App, Text, Box, CheckBox, Window, TextBox, PushButton, Combo, error, info

from MixerConfiguration import MixerConfiguration

from MixerItem import MixerItem

from FakeRaspiController import FakeRaspiController

from RaspiController import RaspiController

from Settings import Settings

from calibration import Calibration

from i2c_devices_gui import I2cDevicesGui

from general_settings import GeneralSettings

from general_configuration import GeneralConfiguration

from common import debug, is_running_on_raspberry_pi, time_diff, compute_time_diff, debug_debug, debug_fatal, debug_error, debug_trace, debug_info

if is_running_on_raspberry_pi():

from i2c_devices_control import I2cDevicesControl

from mixer_mode import MixerMode

from mixer_socket import MixerSocket

import os

class Mixer:

DEBUG_LEVEL = -60



def __init__(self, app, i2c_discoverer, pin_manager):

    self._delete_logs()

    self._pin_manager = pin_manager

    self._startMixerTime = 0

    self._app = app

    self._raspi_controller = None

    self._rounds_start_time = None

    self._previous_round = 0

    self._mixer_box = Box(app, layout="grid")

    self._gap1 = Box(self._mixer_box,    width=24, height=24, grid=[0, 0])

    self._mixer_item_box = Box(self._mixer_box, grid=[1, 3])

    self._gap2 = Box(self._mixer_box,    width=24, height=24, grid=[2, 2])

    self._i2c_discoverer = i2c_discoverer

    self._mixer_configuration = MixerConfiguration(

        './Settings/MixerSettings.json', i2c_discoverer)

    self._mixer_configuration.load_settings(

        './Settings/MixerSettings.json', i2c_discoverer)

    self._general_configuration = GeneralConfiguration()

    self._general_configuration.load_settings()

    self._settings = Settings(

        self._app, self._mixer_configuration, i2c_discoverer, self._general_configuration)

    self._general_settings_window = GeneralSettings(

        self._app, self._general_configuration)

    self._calibration = Calibration(self._app,   self)

    self._active_mixer_ids = []

    self._active_mixer_items = []

    self._item_relay_modes = [0 for _ in range(

        len(self._mixer_configuration.item_configurations))]

    self._mixer_socket = MixerSocket(self)

    self._mixer_socket.connect()

    self._mixer_socket.start()

    self._item_count = len(self._mixer_configuration.item_configurations)

    self.loggers = [0 for _ in range(self._item_count)]

    self._load_mixer_parts()

    self.update_flush_text("")

    self._is_stopped = False

    self._is_paused = False

    self._is_in_relay_mode = False

    self._is_flushing = False

    self.show_all_clicked()

    



def _delete_logs(self):

    today = "{:%Y-%m-%d}".format(datetime.now())

    for file in os.listdir("./logs"):

        if not today in file:

            os.remove("./logs/" + file)



def _update_display(self):

    if self._general_configuration.mode() == MixerMode.RUN_CONTINOUS:

        self._update_display_for_continuos_mode()

    elif self._general_configuration.mode() == MixerMode.OPEN_RELAYS_SIMULTANEOUSLY:

        self._update_display_for_simultaneous_mode()



def _update_display_for_continuos_mode(self):

    if(self._is_flushing == False) :

        self._raspi_controller.read_values()

    for id in self._active_mixer_ids:

        if self._item_relay_modes[id]:

            # no-op yet

            pass

        elif self._raspi_controller.is_item_measurement_complete(id):

            self._item_relay_modes[id] = True

        else:

            item = self._all_mixer_items[id]

            item._update_display()

    if not self._is_in_relay_mode:

        if self._raspi_controller.is_measurement_complete() and not self._is_paused:

            self._raspi_controller.open_relays()

            self._is_paused = True

            self._is_in_relay_mode = True

            self.schedule_new_mix(

                self._mixer_configuration.max_relay_time_in_seconds())

    else:

        for item in self._active_mixer_items:

            item._update_only_stable_min_max()

    for item in self._active_mixer_items:

        item._update_display()



    if not self._is_stopped:

        self._app.after(50, self._update_display)



def _update_display_for_simultaneous_mode(self):

    if(self._is_flushing == False) :

        self._raspi_controller.read_values()

    for item in self._active_mixer_items:

        item._update_display()

    if not self._is_in_relay_mode:

        self._update_display_for_simultaneous_mode_relay_phase()

    else:

        for item in self._active_mixer_items:

            item._update_only_stable_min_max()

    if not self._is_stopped:

        self._app.after(50, self._update_display)



def _update_display_for_simultaneous_mode_relay_phase(self):

    if self._raspi_controller.is_measurement_complete() and not self._is_paused:

        self._raspi_controller.open_relays()

        self._is_paused = True

        self._is_in_relay_mode = True

        if self._general_configuration.is_using_stepper_motors():

            # no-op yet

            pass

        else:

            self.schedule_new_mix(

                self._mixer_configuration.max_relay_time_in_seconds())

    for item in self._active_mixer_items:

        item._update_display()



def schedule_new_mix(self, delay):

    self._app.after(int(delay), self._raspi_controller._close_relays)

    self._check_flushing()

    if(self._is_flushing):

        self.update_flush_text("Flushing...")

    self._flushing()

    self._app.after(int(delay+300), self._start_new_mix)

    #self._app.after(int(delay), self._raspi_controller.reLocate)



def add_flush(self):

    self._flushBox_box = Box(self._mixer_box,   grid=[1, 10],  layout="grid")

    self._lbl_flush = Text(self._flushBox_box,     grid=[

        25, 10], text="Flushing...", color="#800000")  



def destroy_flush(self):

    self._flushBox_box.destroy()



def update_flush_text(self,txt):

    self._lbl_flush.value = txt



def _add_buttons(self):

    self._buttons_box = Box(self._mixer_box,   grid=[1, 1],  layout="grid")

    self._start_button = PushButton(self._buttons_box,   pady=1, text="Start",

                                    align="left", command=self._start, grid=[10, 0])

    self._gap1 = Box(self._buttons_box,   align="top", grid=[20, 0],

                     width=100, height=10)

    self._stop_button = PushButton(self._buttons_box,  pady=1,   grid=[30, 0], text="Stop",

                                   align="left", command=self._stop)

    self._gap2 = Box(self._buttons_box,   align="top", grid=[40, 0],

                     width=100, height=10)

    self._start_fake_button = PushButton(self._buttons_box,  pady=1,   grid=[50, 0], text="Fake",

                                         align="left", command=self._start_fake)

    self._i2c_button = PushButton(self._buttons_box,  pady=1,   grid=[70, 0], text="I2C Devices",

                                  align="left", command=self._i2c_devices)

    self._i2c_button = PushButton(self._buttons_box,  pady=1,   grid=[90, 0], text="Devices Control",

                                  align="left", command=self._i2c_devices_control)

    self._zero_all_button = PushButton(self._buttons_box,  pady=1,   grid=[110, 0], text="Zero All",

                                       align="left", command=self._zero_all)

    self._open_relays_button = PushButton(self._buttons_box,  pady=1,   grid=[120, 0], text="Open Relays",

                                          align="left", command=self._open_relays)

    self._exit_button = PushButton(self._buttons_box,  pady=1,   grid=[130, 0], text="General Settings",

                                   align="left", command=self._general_settings)

    self._exit_button = PushButton(self._buttons_box,  pady=1,   grid=[150, 0], text="Exit",

                                   align="left", command=self._exit)

                                         

    self._set_enabled(True)

    self.toolbar_second_line()



def toolbar_second_line(self):

    self._gap1 = Box(self._mixer_box,    width=24,

                     height=24, grid=[0, 49])

    self._rounds_box = Box(self._mixer_box,   grid=[

        0, 50, 20, 1],  layout="grid")



    self._lbl_start_time = Text(self._rounds_box,     grid=[

                                5, 2], text="Start Time")

    self._lbl_total_time_gross = Text(self._rounds_box,     grid=[

        15, 2], text="Total Time")

    self._lbl_average_time_gross = Text(self._rounds_box,     grid=[

        25, 2], text="Average Time")

    self._lbl_rounds = Text(self._rounds_box,     grid=[

        35, 2], text="Rounds")

    self._txt_start_time = Text(self._rounds_box, grid=[

                                10, 2], color="#ff6600",  width=10, text="4")

    self._txt_total_time_gross = Text(

        self._rounds_box, grid=[20, 2], color="#800000",  text="5", width=10)

    self._txt_average_time_gross = Text(

        self._rounds_box, grid=[30, 2], text="6", color="#666699",  width=10)

    self._txt_rounds = Text(self._rounds_box,   color="#009933",   grid=[

                            40, 2], text="7", width=10)



    self._btn_rounds = PushButton(self._rounds_box,     grid=[

        9, 2], text="Reset Rounds", command=self.reset_rounds)

    self._show_all = CheckBox(

        self._rounds_box, text="Show All", width=10, grid=[3, 2], command=self.show_all_clicked)

    self._combo_settings = Combo(self._rounds_box,     grid=[50, 2], options=[

        "Pick Settings", "Settings 1", "Settings 2", "Settings 3", "Settings 4", "Settings 5", "Settings 6"], command=self.combo_settings_click)

    self.reset_rounds()



def _set_enabled(self, value):

    self._start_button.enabled = value

    self._stop_button.enabled = not value

    self._start_fake_button.enabled = value



def _load_mixer_parts(self):

    self._add_buttons()

    self.add_flush()

    index = 0

    self._all_mixer_items = []



    for index in range(len(self._mixer_configuration.item_configurations)):

        x = MixerItem(index, self)

        self._all_mixer_items.append(x)

        index = index+1



def _get_raspi_controller(self):

    return self._raspi_controller



def _start_new_mix(self,fromStart=False):        

    debug_info('_start_new_mix')     

    self._raspi_controller.new_mix(self._active_mixer_ids)              

    self._is_paused = False

    self._is_in_relay_mode = False        

    if fromStart == False:

        self._raspi_controller.reLocate()

    self.update_flush_text("")



def _check_flushing(self):

    genConfig = self._general_configuration

    now = time.time()

    [hours, minutes, seconds] = compute_time_diff(

        self._startMixerTime, now)

    totalSeconds = hours *3600 + minutes * 60 + seconds

    #print(seconds)

    flushTime = genConfig.flushScaleSec()

    if(totalSeconds > flushTime):            

        self._is_flushing = True       

    

def _flushing(self):      

    if(self._is_flushing):            

        for i in self._active_mixer_ids:

            self._raspi_controller._scales[i].close()

            self._raspi_controller._scales[i].connect()  

        now = time.time()

        self._startMixerTime = now 

    self._is_flushing = False;       

def _start_fake(self):

    if MixerMode.is_valid_mode(self._general_configuration.mode()):

        debug_trace("valid MODE")

        self._do_start_fake()

    else:

        error("mode is not selected",

              "invalid MODE. Please select RUN_CONTINOUS or OPEN_RELAYS_SIMULTANEOUSLY mode.")



def _do_start_fake(self):

    old_controller = self._raspi_controller

    controller = FakeRaspiController(self._mixer_configuration)

    if old_controller != None:

        controller._cnt_rounds = old_controller._cnt_rounds-1

    self._set_raspi_controller(controller)

    self._is_stopped = False

    self._set_enabled(False)

    self._start_new_mix()

    self._update_display()



def _i2c_devices(self):

    devices_gui = I2cDevicesGui(self._app, self._i2c_discoverer)

    devices_gui.open_window(self._app)



def _i2c_devices_control(self):

    gui = I2cDevicesControl(self._app, self._i2c_discoverer)

    gui.open_window(self._app)



def _set_raspi_controller(self, controller):

    self._raspi_controller = controller



def _start(self):

    if MixerMode.is_valid_mode(self._general_configuration.mode()):

        debug_trace("valid MODE")

        conf = self._mixer_configuration

        mixer_ip_list = list()

        for i in self._active_mixer_ids:

            mixer_ip_list.append(conf.stepper_motor_remote_host(i))

        connected_clients = self._mixer_socket.get_connected_clients()

        non_match  = set(mixer_ip_list) - set(connected_clients)           

        if len(non_match) == 0:

            self._do_start()                

        else:

            error("Not Connected", ','.join(non_match)+" not connected to socket")



    else:

        debug_trace("invalid MODE")



def _do_start(self):

    now = time.time()

    self._startMixerTime = now

    self.start_controller()

    self._is_stopped = False

    self._set_enabled(False)

    self._start_new_mix(True)

    self._update_display()

    #self._raspi_controller.calibrateMotors()

    



def start_controller(self):

    debug_info('Mixer.start_controller')

    old_controller = self._raspi_controller

    controller = RaspiController(self)

    if old_controller != None:

        controller._cnt_rounds = old_controller._cnt_rounds-1

    self._set_raspi_controller(controller)

    for mi in self._all_mixer_items:

        mi.set_raspi_controller(self._get_raspi_controller)

    return controller



def _stop(self):

    # self._add_debug_text('_stop')

    self._is_stopped = True

    self._set_enabled(True)

    if self._raspi_controller != None:

        self._raspi_controller.stop()





def start_calibration(self):

    debug_info("Mixer.start_calibration")

    return self.start_controller()



def stop_calibration(self):

    debug_info('Mixer.stop_calibration')

    if self._raspi_controller != None:

        self._raspi_controller.stop()

        self._set_raspi_controller(None)



def _exit(self):

    self._stop()

    self._app.destroy()



def _zero_all(self):

    if self._raspi_controller != None:

        self._raspi_controller.zero_all_scales()



def _open_relays(self):

    debug_info('Mixer.open_relays')

    self.start_controller()

    debug_info('Mixer.start_controller done')

    self._raspi_controller.open_relays()



def update_calibration_ratio_and_save(self, id, updated_val):

    self._mixer_configuration._update_calibration_ratio_and_save(

        id, updated_val)



def combo_settings_click(self, value):

    words = value.split()

    if words[1].isdigit():

        id_no = int(words[1])-1

        print("combo_settings_click value="+str(id_no))

        self._settings.open_window(

            id_no, self._all_mixer_items[id_no])



def reset_rounds(self):

    self._rounds_start_time = time.time()

    if self._raspi_controller != None:

        self._raspi_controller.reset_rounds()

    self._txt_start_time.value = time.strftime(

        '%H:%M', time.localtime())

    self._txt_average_time_gross.value = "00:00.0"

    self.update_times()



def update_times(self):

    now = time.time()

    self._txt_total_time_gross.value = time_diff(

        self._rounds_start_time, now)

    if self.cnt_rounds() > 0 and self.cnt_rounds() > self._previous_round:

        average = (now-self._rounds_start_time)/float(self.cnt_rounds())

        print("average = " + str(average))

        hours, rem = divmod(average, 3600)

        minutes, seconds = divmod(rem, 60)

        self._txt_average_time_gross.value = "{:0>2}:{:03.1f}".format(

            int(minutes), seconds)

    self._txt_rounds.value = str(self.cnt_rounds())

    self._previous_round = self.cnt_rounds()

    self._app.after(100, self.update_times)



@staticmethod

def time_diff(start, end):

    hours, rem = divmod(end-start, 3600)

    minutes, seconds = divmod(rem, 60)

    return "{:0>2}:{:0>2}:{:02.0f}".format(int(hours), int(minutes), seconds)



def cnt_rounds(self):

    if self._raspi_controller != None:

        return self._raspi_controller._cnt_rounds

    return 0



def _settings_saved(self):

    self.show_all_clicked()



def show_all_clicked(self):

    self._active_mixer_ids = []

    for index in range(len(self._mixer_configuration.item_configurations)):

        item = self._all_mixer_items[index]

        is_active = self._mixer_configuration.is_active(

            index)

        item.show_hide(is_active or self._show_all.value)

        if is_active == 1:

            self._active_mixer_ids.append(index)

            self._active_mixer_items.append(item)

        item.set_uom(self._mixer_configuration.uom(index))

        index = index+1



def _general_settings(self):

    self._general_settings_window.open_window(self)



def general_configuration(self):

    return self._general_configuration

`

from guizero.

iyssoft avatar iyssoft commented on July 27, 2024

`import time

import os

import socket

from datetime import datetime

from guizero import Text, Box, CheckBox, TextBox, PushButton, Drawing

import gaugelib

from common import debug, is_running_on_raspberry_pi, to_hex, time_diff, compute_time_diff, debug_debug, debug_fatal, debug_error, debug_trace, debug_info

import Mixer

import logging

from logging.handlers import TimedRotatingFileHandler

from ads1115_mode import Ads1115Mode

from ssh_controller import SSHController

from slot_sensor_and_stepper_motor_client import SlotSensorAndStepperMotorClient

if is_running_on_raspberry_pi():

from simple_ads1115_scale import SimpleAds1115Scale

class MixerItem:

DEBUG_LEVEL = 60



def __init__(self,   id, parent):        

    self._app = parent._mixer_item_box

    self._mixer_socket = parent._mixer_socket

    self._configuration = parent._mixer_configuration

    self._active_mixer_ids = parent._active_mixer_ids

    self._id = id

    self._parent = parent

    self._raspi_controller = parent._get_raspi_controller

    self._settings_window = parent._settings

    self._calibration_window = parent._calibration

    self._general_configuration = parent._general_configuration

    self.reset_min_max_values()

    self._layout()

    self._logSetup()        

    client_cmd = self._configuration.clientCmd(id)

    ip_address = " {}".format((([ip for ip in socket.gethostbyname_ex(socket.gethostname())[2] if not ip.startswith("127.")] or [[(s.connect(("8.8.8.8", 53)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1]]) + ["no IP found"])[0])        

    self.client = SSHController(self._configuration.stepper_motor_remote_host(id), self._configuration.raspiUserName(id), self._configuration.raspiPassword(id), client_cmd, args=ip_address)

    self._run_client_cmd()

    



def __str__(self):

    return "MixerItem["+str(self._id)+"]"



def _logSetup (self):

    self._parent.loggers[self._id] = logging.getLogger('scalelog '+str(self._id))

    self._parent.loggers[self._id].setLevel(logging.DEBUG)

    formatter = logging.Formatter(fmt='%(asctime)s %(name)-12s %(levelname)-8s %(message)s')

    #fh = TimedRotatingFileHandler('/home/pi/Mixer/logs/scale_{}.log'.format(self._id+1), when='D', interval=1, backupCount=0, encoding=None, delay=False, utc=False)

    fh = logging.FileHandler('./logs/scale_{}_{:%Y-%m-%d}.log'.format(self._id+1,datetime.now()))

    fh.setFormatter(formatter)

    self._parent.loggers[self._id].addHandler(fh)



def reset_min_max_values(self):

    self._min_weight_measured = 9999.9

    self._max_weight_measured = -9999.9

    self._min_max_start_time = time.time()

    self._stable_min_weight_measured = 9999.9

    self._stable_max_weight_measured = -9999.9

    self._stable_min_max_start_time = time.time()



def _update_only_stable_min_max(self):

    debug_trace("_update_only_stable_min_max ("+str(self._id)+")")

    controller = self._raspi_controller()

    if controller != None:

        self._update_stable_min_max(controller.get_weight(self._id))



def _update_display(self):

    controller = self._raspi_controller()

    is_relay_open = controller.get_relay(self._id)

    speed = controller.get_speed(self._id)

    weight = controller.get_weight(self._id)

    self._gauge.set_value(weight)



    self._speed_value.value = ('%.0f' % speed)

    self._weight_value.value = ('%.0f' % weight)

    speed_to_color = controller.speed_to_color(self._id)

    self._speed_indicator.oval(

        3, 3, 24, 24, color=speed_to_color, outline=False)



    self._update_min_max(weight)

    self._update_stable_min_max(weight)



    if not is_relay_open == 0.0:

        debug_trace("is_relay_open:"+str(is_relay_open))

    relay_color = 'red'

    if is_relay_open > 0:

        relay_color = 'green'

    self._relay_indicator.oval(

        3, 3, 24, 24, color=relay_color, outline=False)



def _update_min_max(self, weight):

    self._max_weight_measured = max(weight, self._max_weight_measured)

    self._min_weight_measured = min(weight, self._min_weight_measured)

    txt = "Min:{:4.0f} ║ Max:{:4.0f} ║ Diff: {:4.0f}".format(

        self._min_weight_measured, self._max_weight_measured, abs(self._max_weight_measured-self._min_weight_measured))

    self._min_max_label.value = txt

    now = time.time()

    self._txt_min_max_timer.value = time_diff(

        self._min_max_start_time, now)

    target = self._configuration.target(self._id) * 1.5

    if target < self._max_weight_measured :

        self._parent.loggers[self._id].debug(txt)



def _update_stable_min_max(self, weight):

    self._stable_max_weight_measured = max(

        weight, self._stable_max_weight_measured)

    self._stable_min_weight_measured = min(

        weight, self._stable_min_weight_measured)

    now = time.time()

    [_, _, seconds] = compute_time_diff(

        self._stable_min_max_start_time, now)

    if seconds > 0.4:

        self._update_stable_min_max_label(weight)

        if seconds > 0.8:

            self._stable_min_max_start_time = now

            self._stable_max_weight_measured = weight

            self._stable_min_weight_measured = weight



def _update_stable_min_max_label(self, weight):

    diff = abs(self._stable_max_weight_measured -

               self._stable_min_weight_measured)

    # debug_trace("_update_stable_min_max weight = {:4.0f} : {:4.0f}-{:4.0f}={:4.0f} isStable:{:4.0f}"

    #             .format(weight,  self._stable_min_weight_measured, self._stable_max_weight_measured, diff, diff < 25))

    self._stable_label.value = "{:4.0f}-{:4.0f}:{:4.0f}".format(

        self._stable_min_weight_measured, self._stable_max_weight_measured, diff)

    # debug_trace("resetting stable timer weight = {:4.0f} : {:4.0f}-{:4.0f}={:4.0f} isStable:{:4.0f}"

    #             .format(weight,  self._stable_min_weight_measured, self._stable_max_weight_measured, diff, diff < 25))

    color = None

    self._stable_indicator.clear()

    if diff < 30:

        self._stable_label.value = "STABLE "+self._stable_label.value

        color = 'purple'

        # debug_debug("STABLE")

    self._stable_indicator.oval(3, 3, 24, 24, color=color, outline=False)



def _layout(self):

    self._widgets_box = Box(self._app,

                            align="left", layout="grid", width=300, height=600)

    self._indicator_box = Box(self._widgets_box,  grid=[1, 2],

                              align="left", layout="grid")

    self._relay_indicator = Drawing(

        self._indicator_box,  align="left", width=30, height=30,  grid=[0, 0])



    self._speed_value = Text(self._indicator_box, align="left", color='blue',

                             text="0", width=4, grid=[1, 0])

    self._speed_indicator = Drawing(

        self._indicator_box,   width=30, height=30,  grid=[5, 0])

    self._speed_indicator_gap1 = Box(

        self._indicator_box, width=2, height=5, grid=[4, 0])

    self._speed_indicator.oval(

        3, 3, 24, 24, color='orange', outline=False)

    self._weight_value = Text(self._indicator_box, align="left",

                              color='green', size=26, text="0", width=5, grid=[2, 0, 3, 1])

    self._min_max_label = Text(

        self._indicator_box, text="Min/Max=0/0", size=15, width=32, grid=[0, 3, 10, 1], align="left")

    self._txt_min_max_timer = Text(self._indicator_box, size=16, grid=[

        2, 5], color="#800000",  text="5", width=10)

    self._reset_min_max_button = PushButton(

        self._indicator_box, align="top", text="Reset Min/Max", grid=[0, 5, 2, 1],  command=self.reset_min_max_values)

    self._stable_label = Text(self._indicator_box, text="Stable Min/Max=0/0",

                              size=15, width=32, grid=[0, 6, 6, 1], align="left")

    self._stable_indicator = Drawing(

        self._indicator_box,   width=30, height=30,  grid=[5, 6, 2, 1])

    self._stable_indicator.oval(

        3, 3, 24, 24, color=None, outline=False)



    self._gauge_box = Box(self._widgets_box, width='fill',

                          height='fill', grid=[1, 3], align="left")

    self._gauge = gaugelib.DrawGauge3(self._gauge_box.tk,  max_value=self._configuration.target(

        self._id)*1.2,  min_value=0.0,   size=230,  bg_col='#444444',   unit="Weight(gr)", bg_sel=2)

    self._gauge.pack()

    self._gauge.set_value(0)

    self._relay_indicator.oval(3, 3, 24, 24, color="red", outline=False)

    self.display_configuration()

    self._id_label = Text(self._widgets_box, align="left",

                          text=str(self._id+1)+" - "+self._configuration.label(self._id), grid=[1, 4, 100, 1])

    self._id_label.text_size = 24

    self._buttons_box = Box(self._widgets_box,   align="top",

                            layout="grid", grid=[1, 7])

    self._settings_button = PushButton(self._buttons_box,   padx=13, text="Settings",

                                       align="left", command=self._open_settings, grid=[1, 0])

    self._calibration_button = PushButton(self._buttons_box,   padx=2, text="Calibration",

                                          align="left", command=self._calibration, grid=[3, 0])

    self._zero_button = PushButton(self._buttons_box,   padx=7, text="Zero",

                                   align="left", command=self._zero, grid=[5, 0])

    self._open_relay_button = PushButton(self._buttons_box,   padx=2, text="Open Relay",

                                         align="left", command=self._open_relay, grid=[1, 3])

    self._open_log_button = PushButton(self._buttons_box,   padx=7, text="Open Log",

                                         align="left", command=self._open_log, grid=[3, 3])

    self._open_log_button = PushButton(self._buttons_box,   padx=2, text="Run Cmd",

                                         align="left", command=self._run_client_cmd, grid=[5, 3])                                                                                           

    # self._controller._layout(self._widgets_box)



def _open_settings(self):

    self._settings_window.open_window(self._id, self)



def _settings_saved(self):

    self.display_configuration()

    self._parent._settings_saved()

    self._gauge_box.destroy()

    self._gauge_box = Box(self._widgets_box, width='fill',

                          height='fill', grid=[1, 3], align="left")

    self._gauge = gaugelib.DrawGauge3(self._gauge_box.tk,  max_value=self._configuration.target(

        self._id)*1.2,  min_value=0.0,   size=230,  bg_col='#444444',   unit="Weight(gr)", bg_sel=2)

    self._gauge.pack()

    self._gauge.set_value(0)



def _calibration(self):

    self._parent.update_calibration_ratio_and_save(self._id, 1)

    debug_debug("MixerItem._calibration : "+self._configuration.format_i2c_address(

        self._configuration.scale_addres(self._id)))

    debug_debug("MixerItem._calibration:  self._parent=" +

                str(self._parent))

    self._configuration.scale_addres(self._id)

    self._calibration_window._scale = SSHController(self._configuration.stepper_motor_remote_host(self._id), self._configuration.raspiUserName(self._id), self._configuration.raspiPassword(self._id), self._configuration.raspiCmd(self._id), 1)

    #self._calibration_window._scale  = SlotSensorAndStepperMotorClient(self,self._configuration.stepper_motor_remote_host(self._id), self._id)

    self._mixer_socket.set_raspi_controller(self._calibration_window)        

    self._calibration_window.open_window(self._id, self)



def _open_log(self):

    os.system("mousepad ./logs/scale_{}_{:%Y-%m-%d}.log".format(self._id+1,datetime.now()))



def _run_client_cmd(self):

    if self._configuration.is_active(self._id):

        self.client.close()

        self.client.connect()

        #self.client.recv()



def _open_relay(self):

    debug_debug("MixerItem._open_relay id="+str(self._id))

    self._parent._get_raspi_controller().set_relay(self._id, 1)

    self._app.after(2000, self._close_relay)



def _close_relay(self):

    debug_debug("MixerItem._close_relay id=" + str(self._id))

    self._parent._get_raspi_controller().set_relay(self._id, 0)



def stop_calibration(self):

    debug_info("MixerItem.stop_calibration")

    self._parent.stop_calibration()



def start_calibration(self):

    debug_info("start_calibration")



def calibration_done(self, id, calibration_ratio, tare):

    debug_info("calibration_done. calibration_ratio= " +

               str(calibration_ratio)+" tare="+str(tare))

    self._parent.update_calibration_ratio_and_save(id, calibration_ratio)



def get_calibration_weight(self, id):

    return self._configuration.calibration_weight(self._id)



def get_scale(self, id):

    result = self._raspi_controller.get_scale(self._id)

    debug_trace("MixerItem.get_scale[" + str(id)+"] = " + str(result))

    return result



def display_configuration(self):

    self._configuration_box = Box(

        self._widgets_box,   layout="grid", grid=[0, 5, 100, 1])

    # Box(self._configuration_box, width=50, height=5, grid=[90, 1])

    self._target_label = Text(self._configuration_box, align="left",

                              text="CutOff/Target", grid=[1, 1])

    self._target_value = Text(self._configuration_box, align="left",

                              text=str(self._configuration.cutoff(self._id))+"/"+str(self._configuration.target(self._id)), grid=[2, 1])



    self._max_vibration_label = Text(self._configuration_box, align="left",

                                     text="Min/Max Vibration", grid=[1, 3])

    self._max_vibration_value = Text(self._configuration_box, align="left",

                                     text=str(self._configuration.min_vibration(self._id))+"/"+str(self._configuration.max_vibration(self._id)), grid=[2, 3])



    self._relay_duration_label = Text(self._configuration_box, align="left",

                                      text="Duration/Clb Wgh", grid=[1, 5])

    self._relay_duration_value = Text(self._configuration_box, align="left",

                                      text=str(self._configuration.relay_duration(self._id))+"/" +

                                      "%.1f" % (self._configuration.calibration_ratio(self._id))+"/" +

                                      str(self._configuration.calibration_weight(self._id)), grid=[2, 5])

    self._scale_address_label = Text(self._configuration_box,

                                     align="left", text="Scale Address", grid=[1, 7])

    self._scale_address_value = Text(self._configuration_box, align="left", text=str(

        self._configuration.format_i2c_address(self._configuration.scale_addres(self._id))), grid=[2, 7])

    self._relay_address_label = Text(self._configuration_box,

                                     align="left", text="Relay Address", grid=[1, 9])

    self._relay_address_value = Text(self._configuration_box, align="left", text=self._configuration.relay_address_text(

        self._id, self._general_configuration.relay_mode()), grid=[2, 9],size=10)

    self._vibration_address_label = Text(self._configuration_box,

                                         align="left", text="vibration Address", grid=[1, 11])

    self._vibration_address_value = Text(self._configuration_box, align="left", text=str(

        self._configuration.format_i2c_address(self._configuration.vibration_addres(self._id))), grid=[2, 11])



    #self._configuration_box.text_size = 14



def _start(self):

    val = self._gauge.get_value()

    self._gauge.set_value(val)



def _zero(self):

    self._raspi_controller().zero_scale(self._id)



def set_raspi_controller(self, value):

    self._raspi_controller = value



def show_hide(self, is_active):

    self._widgets_box.visible = is_active



def set_uom(self, uom_name):

    self._gauge.set_uom(uom_name)

`

from guizero.

martinohanlon avatar martinohanlon commented on July 27, 2024

we think it's because of guizero (Interface)

What makes you think this?

What would you recommend to solve this issue?

Debug your application. Make sure you are destroying objects / widgets and removing references so Python's garbage collection can do it's job.

from guizero.

aajshaw avatar aajshaw commented on July 27, 2024

OK, I got what I asked for :-) There are a lot of imports in that code that I do not have (gaugelib, FakeRaspiController, RaspiController, calibration, i2c_devices_gui, common, etc.) any one of which could be leaking memory. I could try stubbing them all out as no-ops but if that cut down app did not leak memory then it would not help to isolate your problem.

Like @martinohanlon I think that you need to debug your application, try using a memory profiler to see where the leak(s) are. You could also try to get the GUI working with just stub code and let it run, then if that does not leak memory add in a bit more of the code base and try again, building up until the memory leak manifests.

from guizero.

iyssoft avatar iyssoft commented on July 27, 2024

we think it's because of guizero (Interface)

What makes you think this?

What would you recommend to solve this issue?

Debug your application. Make sure you are destroying objects / widgets and removing references so Python's garbage collection can do it's job.

When we stop updating guizero widget values memory is not increasing

from guizero.

iyssoft avatar iyssoft commented on July 27, 2024

OK, I got what I asked for :-) There are a lot of imports in that code that I do not have (gaugelib, FakeRaspiController, RaspiController, calibration, i2c_devices_gui, common, etc.) any one of which could be leaking memory. I could try stubbing them all out as no-ops but if that cut down app did not leak memory then it would not help to isolate your problem.

Like @martinohanlon I think that you need to debug your application, try using a memory profiler to see where the leak(s) are. You could also try to get the GUI working with just stub code and let it run, then if that does not leak memory add in a bit more of the code base and try again, building up until the memory leak manifests.

We already did it, and observed guizero (update the widget values) cause the problem

from guizero.

martinohanlon avatar martinohanlon commented on July 27, 2024

observed guizero (update the widgwt values) cause the problem

I need some more information to work on. What widgets, where in your application, which updates

Can you recreate the problem in a simple test application?

My gut feeling is that the application is destroying widgets not 'gracefully' leaving the equivalent of 'zombies'. Dead but not gone and still consuming memory.

As we say in the documentation you need to be super careful when destroying widgets.

https://lawsie.github.io/guizero/usingwidgets/#destroying-widgets

from guizero.

martinohanlon avatar martinohanlon commented on July 27, 2024

^ an example of what I mean.

    self._gauge_box.destroy()

    self._gauge_box = Box(self._widgets_box, width='fill',

                          height='fill', grid=[1, 3], align="left")

    self._gauge = gaugelib.DrawGauge3(self._gauge_box.tk,  max_value=self._configuration.target(

        self._id)*1.2,  min_value=0.0,   size=230,  bg_col='#444444',   unit="Weight(gr)", bg_sel=2)

The box is destroyed every time this code is run, but the non guizero gauge widget isn't. I have no idea what state this would leave the application in, but it looks suspicious.

You may also find it helpful to read the docs on integrating tk widgets, there are specific methods add_tk_widget for including widgets in containers (although this isn't the issue with the application) https://lawsie.github.io/guizero/usingtk/

from guizero.

martinohanlon avatar martinohanlon commented on July 27, 2024

Closing as there has been no movement. Feel free to reopen if you have more information.

from guizero.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.