<commit_before>#!/usr/bin/env python3

import sys
import os
import gi
import time

gi.require_version('Gtk', '3.0')
gi.require_version('Notify', '0.7')
from gi.repository import Gtk,Gdk,GLib,GObject,Notify

import serial.tools.list_ports

from TncModel import TncModel

def glade_location():

    bin_path = os.path.abspath(os.path.dirname(sys.argv[0]))

    if bin_path in ["/usr/bin", "/bin"]:
        share_path = '/usr/share/TncConfigApp'
    else:
        share_path = bin_path
    
    return share_path

        
class TncConfigApp(object):

    def __init__(self):
        self.tnc = None
        self.device_path = None
        self.connect_message = None
        
        self.builder = Gtk.Builder()
        self.builder.add_from_file(
            os.path.join(glade_location(), "glade/TncConfigApp.glade"))
        
        # settings = Gtk.Settings.get_default()
        # settings.set_property("gtk-font-name", "Cantarell")
        # settings.set_property("gtk-icon-theme-name", "Oxygen")

        cssProvider = Gtk.CssProvider()
        cssProvider.load_from_path(os.path.join(glade_location(), 'glade/TncConfigApp.css'))
        Gtk.StyleContext.add_provider_for_screen(
            Gdk.Screen.get_default(),
            cssProvider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        )

        self.main_window = self.builder.get_object("main_window")
        self.main_window.connect("delete-event", self.close)

        self.stack = self.builder.get_object("config_stack")
        self.stack.set_sensitive(False)
        self.sidebar = self.builder.get_object("config_sidebar")
        self.sidebar.set_sensitive(False)

        self.init_serial_port_combobox()
        self.init_audio_input_frame()
        self.init_audio_output_frame()
        self.init_power_settings_frame()
        self.init_kiss_parameters_frame()
        self.init_modem_settings_frame()
        self.init_tnc_information_frame()
        self.init_update_firmware_frame()
        self.init_save_settings_frame()

        self.builder.connect_signals(self)
        
        self.main_window.show()
         
        self.visible_frame=None
        self.change_visible_frame('audio_input')

        Notify.init("TncConfigApp")
        Gtk.main()

    def __del__(self):
        
        Notify.uninit()
    
    def close(self, widget, data=None):
    
        if self.tnc is not None:
            self.tnc.disconnect()
            self.tnc = None
        Gtk.main_quit()


    def need_save(self):
        pass

    ### Main UI Section
    
    def init_serial_port_combobox(self):
        self.connect_button = self.builder.get_object("connect_button")
        self.connect_button.set_sensitive(False)
        self.serial_port_combo_box_text = self.builder.get_object("serial_port_combo_box_text")
        assert(self.serial_port_combo_box_text is not None)
        for port in serial.tools.list_ports.comports():
            self.serial_port_combo_box_text.append_text(port[0])
        
    def on_connect_button_toggled(self, widget):
    
        if widget.get_active():
            self.tnc = TncModel(self, self.device_path)
            self.tnc.connect()
        elif self.tnc is not None:   # Possible race condition here...
            self.tnc.disconnect()
            self.tnc = None
            
    
    def on_serial_port_combo_box_changed(self, widget, data = None):
        
        self.device_path = widget.get_active_text()
        self.connect_button.set_sensitive(True)
    
    ### GtkStack
    def on_config_stack_visible_child_name_notify(self, widget, param):
        # print("visible-child-name", widget.get_visible_child_name())
        self.change_visible_frame(widget.get_visible_child_name())
    
    def leave_frame(self, widget_name):
        """Dynamic dispatch to on_*_leave() function"""
        getattr(self, "on_" + widget_name + "_leave")()
    
    def enter_frame(self, widget_name):
        """Dynamic dispatch to on_*_enter() function"""
        getattr(self, "on_" + widget_name + "_enter")()
    
    def change_visible_frame(self, name):
    
        if self.visible_frame == name: return
        if self.visible_frame is not None:
            self.leave_frame(self.visible_frame)
        self.visible_frame = name
        self.enter_frame(self.visible_frame)
    
    ### Audio Input...
    def init_audio_input_frame(self):
        self.audio_input_frame = self.builder.get_object("audio_input_frame")
        
        self.audio_input_level_bar = self.builder.get_object("audio_input_level_bar")
        self.audio_input_level_bar.add_offset_value("audio_input_level_bar-high", 10);
        self.audio_input_level_bar.add_offset_value("audio_input_level_bar-medium", 8);
        self.audio_input_level_bar.add_offset_value("audio_input_level_bar-low", 6);
        
        self.input_attenuation_box = self.builder.get_object("input_attenuation_box")
        self.input_attenuation_box.set_visible(False)
        self.input_attenuation_check_button = self.builder.get_object("input_attenuation_check_button")
        
        self.input_gain_box = self.builder.get_object("input_gain_box")
        self.input_gain_box.set_visible(False)
        self.input_gain_scale = self.builder.get_object("input_gain_scale")
        self.input_gain_min_label = self.builder.get_object("input_gain_min_label")
        self.input_gain_max_label = self.builder.get_object("input_gain_max_label")
        
        self.input_twist_box = self.builder.get_object("input_twist_box")
        self.input_twist_box.set_visible(False)
        self.input_twist_scale = self.builder.get_object("input_twist_scale")
        self.input_twist_min_label = self.builder.get_object("input_twist_min_label")
        self.input_twist_max_label = self.builder.get_object("input_twist_max_label")
        
        self.input_auto_adjust_button = self.builder.get_object("input_auto_adjust_button")
        self.input_auto_adjust_button.set_visible(False)
        
        self.last_audio_input_update_time = time.time()

    
    def on_audio_input_enter(self):
        self.last_audio_input_update_time = 0
        if self.tnc is not None:
            self.tnc.stream_audio_on()
        
    def on_audio_input_leave(self):
        # print('on_audio_input_leave')
        if self.tnc is not None:
            self.tnc.stream_audio_off()
    
    def on_input_attenuation_check_button_toggled(self, widget):
        self.tnc.set_input_atten(widget.get_active())
        self.tnc.stream_audio_on()<commit_msg>Leave demodulator running when disconnecting.  This fixes a defect where disconnecting when not on the audio input tab would leave the TNC in a state of streaming audio rather than with the demodulator active.  The affects the breadboard TNC which cannot detect connect/disconnect.<commit_after>#!/usr/bin/env python3

import sys
import os
import gi
import time

gi.require_version('Gtk', '3.0')
gi.require_version('Notify', '0.7')
from gi.repository import Gtk,Gdk,GLib,GObject,Notify

import serial.tools.list_ports

from TncModel import TncModel

def glade_location():

    bin_path = os.path.abspath(os.path.dirname(sys.argv[0]))

    if bin_path in ["/usr/bin", "/bin"]:
        share_path = '/usr/share/TncConfigApp'
    else:
        share_path = bin_path
    
    return share_path

        
class TncConfigApp(object):

    def __init__(self):
        self.tnc = None
        self.device_path = None
        self.connect_message = None
        
        self.builder = Gtk.Builder()
        self.builder.add_from_file(
            os.path.join(glade_location(), "glade/TncConfigApp.glade"))
        
        # settings = Gtk.Settings.get_default()
        # settings.set_property("gtk-font-name", "Cantarell")
        # settings.set_property("gtk-icon-theme-name", "Oxygen")

        cssProvider = Gtk.CssProvider()
        cssProvider.load_from_path(os.path.join(glade_location(), 'glade/TncConfigApp.css'))
        Gtk.StyleContext.add_provider_for_screen(
            Gdk.Screen.get_default(),
            cssProvider,
            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        )

        self.main_window = self.builder.get_object("main_window")
        self.main_window.connect("delete-event", self.close)

        self.stack = self.builder.get_object("config_stack")
        self.stack.set_sensitive(False)
        self.sidebar = self.builder.get_object("config_sidebar")
        self.sidebar.set_sensitive(False)

        self.init_serial_port_combobox()
        self.init_audio_input_frame()
        self.init_audio_output_frame()
        self.init_power_settings_frame()
        self.init_kiss_parameters_frame()
        self.init_modem_settings_frame()
        self.init_tnc_information_frame()
        self.init_update_firmware_frame()
        self.init_save_settings_frame()

        self.builder.connect_signals(self)
        
        self.main_window.show()
         
        self.visible_frame=None
        self.change_visible_frame('audio_input')

        Notify.init("TncConfigApp")
        Gtk.main()

    def __del__(self):
        
        Notify.uninit()
    
    def close(self, widget, data=None):
    
        if self.tnc is not None:
            self.tnc.disconnect()
            self.tnc = None
        Gtk.main_quit()


    def need_save(self):
        pass

    ### Main UI Section
    
    def init_serial_port_combobox(self):
        self.connect_button = self.builder.get_object("connect_button")
        self.connect_button.set_sensitive(False)
        self.serial_port_combo_box_text = self.builder.get_object("serial_port_combo_box_text")
        assert(self.serial_port_combo_box_text is not None)
        for port in serial.tools.list_ports.comports():
            self.serial_port_combo_box_text.append_text(port[0])
        
    def on_connect_button_toggled(self, widget):
    
        if widget.get_active():
            self.tnc = TncModel(self, self.device_path)
            self.tnc.connect()
        elif self.tnc is not None:   # Possible race condition here...
            self.tnc.disconnect()
            self.tnc = None
            
    
    def on_serial_port_combo_box_changed(self, widget, data = None):
        
        self.device_path = widget.get_active_text()
        self.connect_button.set_sensitive(True)
    
    ### GtkStack
    def on_config_stack_visible_child_name_notify(self, widget, param):
        # print("visible-child-name", widget.get_visible_child_name())
        self.change_visible_frame(widget.get_visible_child_name())
    
    def leave_frame(self, widget_name):
        """Dynamic dispatch to on_*_leave() function"""
        getattr(self, "on_" + widget_name + "_leave")()
    
    def enter_frame(self, widget_name):
        """Dynamic dispatch to on_*_enter() function"""
        getattr(self, "on_" + widget_name + "_enter")()
    
    def change_visible_frame(self, name):
    
        if self.visible_frame == name: return
        if self.visible_frame is not None:
            self.leave_frame(self.visible_frame)
        self.visible_frame = name
        self.enter_frame(self.visible_frame)
    
    ### Audio Input...
    def init_audio_input_frame(self):
        self.audio_input_frame = self.builder.get_object("audio_input_frame")
        
        self.audio_input_level_bar = self.builder.get_object("audio_input_level_bar")
        self.audio_input_level_bar.add_offset_value("audio_input_level_bar-high", 10);
        self.audio_input_level_bar.add_offset_value("audio_input_level_bar-medium", 8);
        self.audio_input_level_bar.add_offset_value("audio_input_level_bar-low", 6);
        
        self.input_attenuation_box = self.builder.get_object("input_attenuation_box")
        self.input_attenuation_box.set_visible(False)
        self.input_attenuation_check_button = self.builder.get_object("input_attenuation_check_button")
        
        self.input_gain_box = self.builder.get_object("input_gain_box")
        self.input_gain_box.set_visible(False)
        self.input_gain_scale = self.builder.get_object("input_gain_scale")
        self.input_gain_min_label = self.builder.get_object("input_gain_min_label")
        self.input_gain_max_label = self.builder.get_object("input_gain_max_label")
        
        self.input_twist_box = self.builder.get_object("input_twist_box")
        self.input_twist_box.set_visible(False)
        self.input_twist_scale = self.builder.get_object("input_twist_scale")
        self.input_twist_min_label = self.builder.get_object("input_twist_min_label")
        self.input_twist_max_label = self.builder.get_object("input_twist_max_label")
        
        self.input_auto_adjust_button = self.builder.get_object("input_auto_adjust_button")
        self.input_auto_adjust_button.set_visible(False)
        
        self.last_audio_input_update_time = time.time()

    
    def on_audio_input_enter(self):
        self.last_audio_input_update_time = 0
        if self.tnc is not None and self.connect_button.get_active():
            self.tnc.stream_audio_on()
        
    def on_audio_input_leave(self):
        # print('on_audio_input_leave')
        if self.tnc is not None:
            self.tnc.stream_audio_off()
    
    def on_input_attenuation_check_button_toggled(self, widget):
        self.tnc.set_input_atten(widget.get_active())
        self.tnc.stream_audio_on()
{'fraction_non_alphanumeric': 0.09853987270685137, 'fraction_numerical': 0.0017970797454137028, 'mean_word_length': 2.850100893629288, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7508009', 'n_tokens_mistral': 4302, 'n_tokens_neox': 4210, 'n_words': 594}