Comments (11)
Can we see the source code of the app?
from guizero.
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.
`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.
`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.
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.
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.
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.
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.
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.
^ 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.
Closing as there has been no movement. Feel free to reopen if you have more information.
from guizero.
Related Issues (20)
- Widgets can be given a tab order
- Color control - TextBox Background and Pushbutton HOT 1
- Destroying a container could destroy the widgets contained inside it. HOT 3
- Guizero animation importing picture into a moving oval HOT 2
- Scrollable boxes (really a box inside a scrollable canvas)
- Create right-click context menu for copy-paste with app.add_tk_widget HOT 1
- MCP3008 HOT 4
- Pillow.Image.ANTIALIAS is deprecated and will be removed HOT 1
- #Recursive call dont work in Guizero_Auto_Update HOT 6
- TextBox command HOT 2
- a issue on https://lawsie.github.io/guizero/about/ HOT 6
- Using Drawing... HOT 7
- WafflePixel properties doc bug HOT 1
- Docs site uses an old version of jquery HOT 8
- The grid layout is not displayed according to coordinates, more like displaying the order and size of coordinates HOT 4
- Slider hide/show bug HOT 10
- Add options properties to Combo, ButtonGroup & ListBox HOT 1
- You cant programmatically add items to a ListBox that isn't enabled HOT 1
- Error (typo) on the book manual HOT 1
- Documentation Issue HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from guizero.