diff --git a/UserDB/UserDBmain.py b/UserDB/UserDBmain.py index f36adec5..1c3d69fa 100644 --- a/UserDB/UserDBmain.py +++ b/UserDB/UserDBmain.py @@ -120,9 +120,9 @@ def get_entry(self, call_str, add_new=True): else: return False else: - self.entry_var_upgrade(call_tup[0]) + # self.entry_var_upgrade(call_tup[0]) return self.db[call_tup[0]] - self.entry_var_upgrade(call_str) + # self.entry_var_upgrade(call_str) return self.db[call_str] return False diff --git a/ax25/ax25Connection.py b/ax25/ax25Connection.py index f86de2c3..e728f1e3 100644 --- a/ax25/ax25Connection.py +++ b/ax25/ax25Connection.py @@ -230,13 +230,12 @@ def __init__(self, ax25_frame: AX25Frame, cfg, port, rx=True): """ Encoding """ self.encoding = 'CP437' # 'UTF-8' """ User DB Entry """ - # self.user_db = USER_DB self.user_db_ent = False + self.cli_language = 0 self.set_user_db_ent() """ Station Individual Parameter """ self.set_packet_param() """ Init CLI """ - self.cli_language = 0 self.cli = cli.cliMain.NoneCLI(self) self.cli_type = '' if self.stat_cfg.stat_parm_pipe is None: @@ -386,9 +385,13 @@ def set_user_db_ent(self): self.user_db_ent.Language = 0 else: self.user_db_ent.Language = int(self.gui.language) - self.cli_language = int(self.gui.language) + self.cli_language = self.user_db_ent.Language self.set_distance() + def set_user_db_language(self, lang_ind: int): + self.user_db_ent.Language = int(lang_ind) + self.cli_language = int(lang_ind) + def set_distance(self): if self.user_db_ent: if self.my_locator and self.user_db_ent.LOC: diff --git a/ax25/ax25Statistics.py b/ax25/ax25Statistics.py index 2f6865d9..a897cd0d 100644 --- a/ax25/ax25Statistics.py +++ b/ax25/ax25Statistics.py @@ -1,3 +1,4 @@ +import time from collections import deque from datetime import datetime from datetime import timedelta @@ -8,49 +9,8 @@ from constant import CFG_mh_data_file, CFG_port_stat_data_file from fnc.cfg_fnc import cleanup_obj_dict, set_obj_att from fnc.socket_fnc import check_ip_add_format -from fnc.str_fnc import conv_time_for_sorting, get_timedelta_str - - -def get_time_str(): - return datetime.now().strftime('%d/%m/%y %H:%M:%S') - - -def get_bandwidth_struct(): - _struct = {} - for _h in range(24): - for _m in range(60): - for _s in range(6): - _ts_str = f'{str(_h).zfill(2)}:{str(_m).zfill(2)}:{_s}' - _struct[_ts_str] = 0 - return _struct - - -def get_port_stat_struct(): - struct_hour = {} - for key in [ - 'N_pack', - 'I', - 'SABM', - 'DM', - 'DISC', - 'REJ', - 'RR', - 'RNR', - 'UI', - 'FRMR', - 'DATA_W_HEADER', - 'DATA' - ]: - struct_hour[key] = {minute: 0 for minute in range(60)} - return struct_hour - - -def init_day_dic(): - - ret = {} - for hour in range(24): - ret[hour] = get_port_stat_struct() - return ret +from fnc.str_fnc import conv_time_for_sorting +from fnc.struct_fnc import get_bandwidth_struct, init_day_dic, get_dx_tx_alarm_his_pack class MyHeard: @@ -59,15 +19,17 @@ class MyHeard: route = [] all_routes = [] port = '' - port_id = 0 # Not used yet + port_id = 0 # Not used yet first_seen = datetime.now() last_seen = datetime.now() - pac_n = 0 # N Packets - byte_n = 0 # N Bytes - h_byte_n = 0 # N Header Bytes - rej_n = 0 # N REJ - axip_add = '', 0 # IP, Port - axip_fail = 0 # Fail Counter + pac_n = 0 # N Packets + byte_n = 0 # N Bytes + h_byte_n = 0 # N Header Bytes + rej_n = 0 # N REJ + axip_add = '', 0 # IP, Port + axip_fail = 0 # Fail Counter + locator = '' + distance = -1 class MH(object): @@ -88,7 +50,8 @@ def __init__(self): self.calls: {str: MyHeard} = {} for call in mh_load: self.calls[call] = set_obj_att(new_obj=MyHeard(), input_obj=mh_load[call]) - + # Fix: Delete empty Routes in routes entry + # self.calls[call].all_routes = list(filter(lambda a: a != [], self.calls[call].all_routes)) try: with open(CFG_port_stat_data_file, 'rb') as inp: self.port_statistik_DB = pickle.load(inp) @@ -103,19 +66,53 @@ def __init__(self): if not hasattr(self.calls[call], att): setattr(self.calls[call], att, getattr(MyHeard, att)) - self.new_call_alarm = False + self.dx_alarm_trigger = False + self.last_dx_alarm = time.time() + self.dx_alarm_hist = [] + self.dx_alarm_perma_hist = {} + self.parm_new_call_alarm = False + self.parm_distance_alarm = 50 + self.parm_lastseen_alarm = 1 + self.parm_alarm_ports = [] def __del__(self): pass + def _set_dx_alarm(self, ent): + port_id = ent.port_id + if port_id in self.parm_alarm_ports: + self.dx_alarm_trigger = True + self.last_dx_alarm = time.time() + self.dx_alarm_hist.append(ent.own_call) + self._add_dx_alarm_hist(ent=ent) + + def _add_dx_alarm_hist(self, ent): + _via = '' + if ent.route: + _via = ent.route[-1] + _hist_struc = get_dx_tx_alarm_his_pack( + port_id=ent.port_id, + call_str=ent.own_call, + via=_via, + path=ent.route, + locator=ent.locator, + distance=ent.distance, + typ='MHEARD', + ) + self.dx_alarm_perma_hist[str(_hist_struc['key'])] = dict(_hist_struc) + + def reset_dx_alarm_his(self): + self.dx_alarm_hist = [] + self.dx_alarm_trigger = False + def bw_mon_inp(self, ax25_frame, port_id): if port_id not in self.port_statistik_DB.keys(): self.port_statistik_DB[port_id] = {} - self.init_bw_struct(port_id) - self.input_stat_db(ax_frame=ax25_frame, port_id=port_id) + self._init_bw_struct(port_id) + self._input_stat_db(ax_frame=ax25_frame, port_id=port_id) - def input_bw_calc(self, port_id, ax_frame=None, ): - self.init_bw_struct(port_id=port_id) + def _input_bw_calc(self, port_id, ax_frame=None, ): + self._init_bw_struct(port_id=port_id) if ax_frame is not None: if self.now_min == datetime.now().strftime('%H:%M:%S')[:-1]: self.bandwidth[port_id][self.now_min] += len(ax_frame.data_bytes) @@ -127,12 +124,12 @@ def input_bw_calc(self, port_id, ax_frame=None, ): self.now_min = datetime.now().strftime('%H:%M:%S')[:-1] self.bandwidth[port_id][self.now_min] = 0 - def init_bw_struct(self, port_id): + def _init_bw_struct(self, port_id): if port_id not in self.bandwidth: self.bandwidth[port_id] = get_bandwidth_struct() def get_bandwidth(self, port_id, baud=1200): - self.init_bw_struct(port_id=port_id) + self._init_bw_struct(port_id=port_id) ret = deque([0] * 60, maxlen=60) now = datetime.now() ten_minutes_ago = now - timedelta(minutes=10) @@ -149,7 +146,7 @@ def get_bandwidth(self, port_id, baud=1200): i += 1 return ret - def input_stat_db(self, ax_frame, port_id): + def _input_stat_db(self, ax_frame, port_id): now = datetime.now() date_str = now.strftime('%d/%m/%y') hour = now.hour @@ -164,7 +161,7 @@ def input_stat_db(self, ax_frame, port_id): if dt not in last_days: del self.port_statistik_DB[port_id][dt] - self.input_bw_calc(ax_frame=ax_frame, port_id=port_id) + self._input_bw_calc(ax_frame=ax_frame, port_id=port_id) if date_str not in list(self.port_statistik_DB[port_id].keys()): self.port_statistik_DB[port_id][date_str] = init_day_dic() self.port_statistik_DB[port_id][date_str][hour]['N_pack'][minute] += 1 @@ -177,55 +174,85 @@ def mh_inp_axip_add(self, ent: '', axip_add: tuple): if ent in self.calls.keys(): self.calls[ent].axip_add = axip_add - def mh_inp(self, ax25_frame, port_name, port_id): - ######################## - # Port Stat - if port_id not in self.port_statistik_DB.keys(): - self.port_statistik_DB[port_id] = init_day_dic() - # self.port_statistik_DB[port_id].input_stat_db(ax_frame=ax25_frame) - self.input_stat_db(ax25_frame, port_id) + def mh_inp(self, ax25_frame, port_name, port_id, digi=''): + _dx_alarm = False + if not digi: + ######################## + # Port Stat + if port_id not in self.port_statistik_DB.keys(): + self.port_statistik_DB[port_id] = init_day_dic() + # self.port_statistik_DB[port_id].input_stat_db(ax_frame=ax25_frame) + self._input_stat_db(ax25_frame, port_id) ######################## # MH Entry - call_str = ax25_frame.from_call.call_str + if digi: + call_str = digi + else: + call_str = str(ax25_frame.from_call.call_str) if call_str not in self.calls.keys(): - self.new_call_alarm = True ent = MyHeard() - ent.first_seen = datetime.now() + if self.parm_new_call_alarm: + _dx_alarm = True else: ent = self.calls[call_str] + _t_delta = datetime.now() - ent.last_seen + if self.parm_lastseen_alarm: + if _t_delta.days >= self.parm_lastseen_alarm: + _dx_alarm = True ent.last_seen = datetime.now() ent.own_call = call_str ent.pac_n += 1 - ent.port = port_name - ent.port_id = port_id - ent.byte_n += ax25_frame.data_len + ent.port = str(port_name) + ent.port_id = int(port_id) + ent.byte_n += int(ax25_frame.data_len) ent.h_byte_n += len(ax25_frame.data_bytes) - ax25_frame.data_len if ax25_frame.ctl_byte.flag == 'REJ': ent.rej_n += 1 # TO Calls - to_c_str = ax25_frame.to_call.call_str + to_c_str = str(ax25_frame.to_call.call_str) if to_c_str not in ent.to_calls: ent.to_calls.append(to_c_str) # Routes - ent.route = [] # Last Route + ent.route = [] # Last Route + _last_digi = '' if ax25_frame.via_calls: for call in ax25_frame.via_calls: if call.c_bit: - ent.route.append(call.call_str) - + ent.route.append(str(call.call_str)) + _last_digi = str(call.call_str) + if ent.route and digi: + ent.route = ent.route[:-1] if ent.route not in ent.all_routes: ent.all_routes.append(list(ent.route)) + # Update AXIP Address if ax25_frame.axip_add[0]: if ent.axip_add[0]: if check_ip_add_format(ent.axip_add[0]): if check_ip_add_format(ax25_frame.axip_add[0]): - ent.axip_add = ax25_frame.axip_add + ent.axip_add = tuple(ax25_frame.axip_add) else: - ent.axip_add = ax25_frame.axip_add + ent.axip_add = tuple(ax25_frame.axip_add) + # Get Locator and Distance from User-DB + + db_ent = USER_DB.get_entry(call_str, add_new=True) + if db_ent: + ent.locator = str(db_ent.LOC) + ent.distance = float(db_ent.Distance) + if self.parm_distance_alarm: + if ent.distance >= self.parm_distance_alarm: + _dx_alarm = True + if _dx_alarm: + self._set_dx_alarm(ent=ent) self.calls[call_str] = ent + if digi: + USER_DB.set_typ(call_str=digi, add_new=False, typ='DIGI') + return + if _last_digi: + self.mh_inp(ax25_frame, port_name, port_id, _last_digi) + def mh_get_data_fm_call(self, call_str): if call_str in self.calls.keys(): return self.calls[call_str] @@ -256,14 +283,17 @@ def get_sort_mh_entry(self, flag_str: str, reverse: bool): self.calls: {str: MyHeard} for k in self.calls.keys(): flag: MyHeard = self.calls[k] + key: str = { 'last': conv_time_for_sorting(flag.last_seen), 'first': conv_time_for_sorting(flag.first_seen), 'port': str(flag.port_id), 'call': flag.own_call, + 'loc': flag.locator, + 'dist': str(flag.distance), 'pack': str(flag.pac_n), 'rej': str(flag.rej_n), - 'route': str(max(flag.all_routes)), + 'route': str(flag.route), 'axip': str(flag.axip_add), 'axipfail': str(flag.axip_fail), }[flag_str] @@ -287,6 +317,7 @@ def mh_get_last_ip(self, call_str: str, param_fail=20): return self.calls[call_str].axip_add return '', 0 + """ def mh_get_ip_fm_all(self, param_fail=20): ret: [(str, (str, int))] = [] for stat_call in self.calls.keys(): @@ -295,6 +326,7 @@ def mh_get_ip_fm_all(self, param_fail=20): ent = stat_call, station.axip_add ret.append(ent) return ret + """ def mh_ip_failed(self, axip: str): for k in self.calls.keys(): @@ -304,92 +336,6 @@ def mh_ip_failed(self, axip: str): def mh_set_ip(self, call: str, axip: (str, int)): self.calls[call].axip_add = axip - def mh_out_cli(self, max_ent=20): - out = '\r' - # out += '\r < MH - List >\r\r' - c = 0 - max_c = 0 - """ - tp = 0 - tb = 0 - rj = 0 - """ - sort_list = self.get_sort_mh_entry('last', False) - - for call in list(sort_list.keys()): - max_c += 1 - if max_c > max_ent: - break - time_delta_str = get_timedelta_str(sort_list[call].last_seen) - - out += f'{time_delta_str} P:{sort_list[call].port:4} {sort_list[call].own_call:9}'.ljust(27, " ") - """ - tp += sort_list[call].pac_n - tb += sort_list[call].byte_n - rj += sort_list[call].rej_n - """ - c += 1 - if c == 2: # Breite - c = 0 - out += '\r' - """ - out += '\r' - out += '\rTotal Packets Rec.: ' + str(tp) - out += '\rTotal REJ-Packets Rec.: ' + str(rj) - out += '\rTotal Bytes Rec.: ' + str(tb) - """ - out += '\r' - - return out - - def mh_long_out_cli(self, max_ent=10): - out = '\r' - out += "-----Time-Port---Call------via-------LOC------Dist(km)--Type---Packets\r" - max_c = 0 - """ - tp = 0 - tb = 0 - rj = 0 - """ - sort_list = self.get_sort_mh_entry('last', False) - - for call in list(sort_list.keys()): - max_c += 1 - if max_c > max_ent: - break - time_delta_str = get_timedelta_str(sort_list[call].last_seen) - via = sort_list[call].route - if via: - via = via[-1] - else: - via = '' - loc = '' - dis = '' - typ = '' - userdb_ent = USER_DB.get_entry(sort_list[call].own_call, add_new=False) - if userdb_ent: - loc = userdb_ent.LOC - if userdb_ent.Distance: - dis = str(userdb_ent.Distance) - typ = userdb_ent.TYP - - out += (f' {time_delta_str:9}{sort_list[call].port:7}{sort_list[call].own_call:10}' - f'{via:10}{loc:9}{dis:10}{typ:7}{sort_list[call].pac_n}') - """ - tp += sort_list[call].pac_n - tb += sort_list[call].byte_n - rj += sort_list[call].rej_n - """ - out += '\r' - out += '\r' - """ - out += '\rTotal Packets Rec.: ' + str(tp) - out += '\rTotal REJ-Packets Rec.: ' + str(rj) - out += '\rTotal Bytes Rec.: ' + str(tb) - out += '\r' - """ - return out - def mh_out_beacon(self, max_ent=12): _tmp = self.get_sort_mh_entry('last', False) _ret = '' diff --git a/ax25aprs/aprs_dec.py b/ax25aprs/aprs_dec.py index 1338f13f..27108763 100644 --- a/ax25aprs/aprs_dec.py +++ b/ax25aprs/aprs_dec.py @@ -140,10 +140,8 @@ def format_aprs_msg(aprs_frame: aprslib.parse, own_locator, full_aprs_frame: apr ret = '' dist = '' typ = '' - db_ent = USER_DB.get_entry(full_aprs_frame['from'], add_new=add_new_user) + loc = '' for k in aprs_frame: - # print(f"{k}: {aprs_frame[k]}") - if aprs_frame[k]: # if k not in ['from', 'to', 'symbol_table', 'symbol', 'subpacket', 'weather']: if k not in ['from', 'to', 'raw', 'symbol_table', 'symbol', 'subpacket', 'weather']: @@ -175,13 +173,8 @@ def format_aprs_msg(aprs_frame: aprslib.parse, own_locator, full_aprs_frame: apr loc = coordinates_to_locator(latitude=aprs_frame['latitude'], longitude=aprs_frame['longitude']) ret += f"├►LOCATOR : {loc}\n" - if db_ent: - if not db_ent.LOC: - db_ent.LOC = loc - if not db_ent.Lat: - db_ent.Lat = aprs_frame['latitude'] - db_ent.Lon = aprs_frame['longitude'] - if own_locator: + + if own_locator and loc: dist = locator_distance(own_locator, loc) ret += f"├►DISTANCE : {dist} km\n" # if db_ent: @@ -201,11 +194,15 @@ def format_aprs_msg(aprs_frame: aprslib.parse, own_locator, full_aprs_frame: apr ret += f"├►{k.upper().ljust(13)}: {aprs_frame[k]}\n" if k in ['weather', 'wx']: typ = 'APRS-WX' - if db_ent: - # if not db_ent.Distance: - if db_ent.LOC and own_locator: - db_ent.Distance = locator_distance(own_locator, db_ent.LOC) - dist = locator_distance(own_locator, db_ent.LOC) + + if loc: + db_ent = USER_DB.get_entry(full_aprs_frame.get('from', ''), add_new=add_new_user) + if db_ent: + db_ent.LOC = loc + db_ent.Lat = aprs_frame['latitude'] + db_ent.Lon = aprs_frame['longitude'] + if type(dist) == float: + db_ent.Distance = float(dist) if not db_ent.TYP and typ: db_ent.TYP = typ diff --git a/ax25aprs/aprs_station.py b/ax25aprs/aprs_station.py index b12bd4d1..cff9be23 100644 --- a/ax25aprs/aprs_station.py +++ b/ax25aprs/aprs_station.py @@ -10,7 +10,8 @@ from constant import APRS_SW_ID, APRS_TRACER_COMMENT, CFG_aprs_data_file from fnc.cfg_fnc import cleanup_obj, save_to_file, load_fm_file, set_obj_att from fnc.loc_fnc import decimal_degrees_to_aprs, locator_distance, coordinates_to_locator -from fnc.str_fnc import convert_umlaute_to_ascii, get_timedelta_str, convert_str_to_datetime +from fnc.str_fnc import convert_umlaute_to_ascii +from fnc.struct_fnc import get_dx_tx_alarm_his_pack logger = logging.getLogger(__name__) @@ -53,15 +54,17 @@ def __init__(self, load_cfg=True): self.aprs_wx_msg_pool = {} """ Beacon Tracer """ # Param - self.be_tracer_active = False self.be_tracer_interval = 5 self.be_tracer_port = 0 self.be_tracer_station = 'NOCALL' self.be_tracer_wide = 1 self.be_tracer_alarm_active = False self.be_tracer_alarm_range = 50 - # Packet Pool TODO: extra var f Packet history + self.be_auto_tracer_duration = 60 + + # Packet Pool self.be_tracer_traced_packets = {} + self.be_tracer_alarm_hist = {} # Control vars self._be_tracer_is_alarm = False self._be_tracer_tx_trace_packet = '' @@ -78,6 +81,9 @@ def __init__(self, load_cfg=True): } self.aprs_wx_msg_pool = {} """ + self.be_tracer_active = False + self.be_auto_tracer_active = False + self._be_auto_tracer_timer = 0 """""" self.ais = None self.ais_mon_gui = None @@ -381,29 +387,6 @@ def get_wx_entry_sort_distance(self): def get_wx_data(self): return dict(self.aprs_wx_msg_pool) - def get_wx_cli_out(self, max_ent=10): - _data = self.get_wx_entry_sort_distance() - if not _data: - return '' - max_c = 0 - out = '\r-----Last-Port--Call------LOC-------------Temp-Press---Hum-Lum-Rain(24h)-\r' - for k in _data: - max_c += 1 - if max_c > max_ent: - break - _ent = self.aprs_wx_msg_pool[k][-1] - _td = get_timedelta_str(_ent['rx_time']) - _loc = f'{_ent.get("locator", "------")[:6]}({round(_ent.get("distance", -1))}km)' - _pres = f'{_ent["weather"].get("pressure", 0):.2f}' - _rain = f'{_ent["weather"].get("rain_24h", 0):.3f}' - out += f'{_td.rjust(9):10}{_ent.get("port_id", ""):6}{k:10}{_loc:16}' - out += f'{str(round(_ent["weather"].get("temperature", 0))):5}' - out += f'{_pres:7} ' - out += f'{_ent["weather"].get("humidity", 0):3} ' - out += f'{_ent["weather"].get("luminosity", 0):3} ' - out += f'{_rain:6}\r' - return out - ##################### # @staticmethod @@ -689,6 +672,15 @@ def _tracer_task(self): if time.time() > self._be_tracer_interval_timer: # print("TRACER TASKER") self.tracer_sendit() + return + if self.be_auto_tracer_active: + if not self.be_auto_tracer_duration: + return + if time.time() > self._be_auto_tracer_timer: + return + if time.time() > self._be_tracer_interval_timer: + self.tracer_sendit() + return def _tracer_build_msg(self): # !5251.12N\01109.78E-27.235MHz P.ython o.ther P.acket T.erminal (PoPT) @@ -723,9 +715,18 @@ def tracer_sendit(self): if _pack.get('raw_message_text', '') and _pack.get('comment', ''): self._be_tracer_tx_trace_packet = _pack.get('comment', '') self._send_as_UI(_pack) - self._be_tracer_interval_timer = time.time() + (60 * self.be_tracer_interval) + self._tracer_reset_timer() # print(self._tracer_build_msg()) + def _tracer_reset_timer(self): + self._be_tracer_interval_timer = time.time() + (60 * self.be_tracer_interval) + + def tracer_reset_auto_timer(self, ext_timer=None): + if ext_timer is None: + self._be_auto_tracer_timer = time.time() + (self.be_auto_tracer_duration * 60) + else: + self._be_auto_tracer_timer = ext_timer + (self.be_auto_tracer_duration * 60) + def tracer_get_last_send(self): return time.time() - (self._be_tracer_interval_timer - (60 * self.be_tracer_interval)) @@ -774,13 +775,13 @@ def _tracer_add_traced_packet(self, pack): _dist = _user_db_ent.Distance pack['distance'] = _dist pack['locator'] = _loc - + pack['tr_alarm'] = self._tracer_check_alarm(pack) if _k in self.be_tracer_traced_packets.keys(): self.be_tracer_traced_packets[_k].append(pack) else: self.be_tracer_traced_packets[_k] = deque([pack], maxlen=500) # print(f'Tracer RX dict: {self.be_tracer_traced_packets}') - self._tracer_check_alarm(pack) + # self._tracer_check_alarm(pack) self.tracer_update_gui() return True return False @@ -791,6 +792,33 @@ def _tracer_check_alarm(self, pack): _dist = pack.get('distance', 0) if _dist >= self.be_tracer_alarm_range: self._be_tracer_is_alarm = True + self._tracer_add_alarm_hist(pack) + return True + return False + + def _tracer_add_alarm_hist(self, aprs_pack): + _via = '' + if aprs_pack.get('via', ''): + if aprs_pack.get('path', []): + _via = get_last_digi_fm_path(aprs_pack) + else: + _via_list = [] + for _digi in aprs_pack.get('path', []): + if '*' == _digi[-1]: + _via_list.append(str(_digi)) + if len(_via_list) > 1: + _via = _via_list[-2] + + _hist_struc = get_dx_tx_alarm_his_pack( + port_id=aprs_pack.get('port_id', -1), + call_str=aprs_pack.get('call', ''), + via=_via, + path=aprs_pack.get('path', []), + locator=aprs_pack.get('locator', ''), + distance=aprs_pack.get('distance', -1), + typ='TRACE', + ) + self.be_tracer_alarm_hist[str(_hist_struc['key'])] = dict(_hist_struc) def tracer_is_alarm(self): return self._be_tracer_is_alarm @@ -807,3 +835,19 @@ def tracer_update_gui(self): def tracer_traces_get(self): return self.be_tracer_traced_packets + + def tracer_auto_tracer_set(self, state=None): + if self.be_tracer_active: + self.be_auto_tracer_active = False + return False + if state is None: + self.be_auto_tracer_active = not self.be_auto_tracer_active + self.tracer_reset_auto_timer() + return bool(self.be_auto_tracer_active) + self._be_auto_tracer_timer = 0 + self.be_auto_tracer_active = state + return bool(self.be_auto_tracer_active) + + def tracer_auto_tracer_duration_set(self, dur: int): + self.be_auto_tracer_duration = dur + diff --git a/cli/cliMain.py b/cli/cliMain.py index bbbbc4a5..16f77600 100644 --- a/cli/cliMain.py +++ b/cli/cliMain.py @@ -93,32 +93,36 @@ def __init__(self, connection): # Standard Commands ( GLOBAL ) self.commands = { b'QUIT': (self.cmd_q, 'Quit'), - b'BYE': (self.cmd_q, ''), + b'BYE': (self.cmd_q, 'Bye'), b'CONNECT': (self.cmd_connect, 'Connect'), + b'ECHO': (self.cmd_echo, 'Echo'), b'PORT': (self.cmd_port, 'Ports'), - b'LCSTATUS': (self.cmd_lcstatus, STR_TABLE['cmd_help_lcstatus'][self.connection.cli_language]), - b'MH': (self.cmd_mh, 'MYHeard Liste'), - b'LMH': (self.cmd_mhl, 'Long MYHeard Liste'), + b'MH': (self.cmd_mh, 'MYHeard List'), + b'LMH': (self.cmd_mhl, 'Long MYHeard List'), + b'AXIP': (self.cmd_axip, 'AXIP-MH List'), b'ATR': (self.cmd_aprs_trace, 'APRS-Tracer'), + b'DXLIST': (self.cmd_dxlist, 'DX/Tracer Alarm List'), + b'LCSTATUS': (self.cmd_lcstatus, STR_TABLE['cmd_help_lcstatus'][self.connection.cli_language]), b'WX': (self.cmd_wx, 'Wetterstationen'), - b'ECHO': (self.cmd_echo, 'Echo'), + + b'INFO': (self.cmd_i, 'Info'), + b'LINFO': (self.cmd_li, 'Long Info'), + b'NEWS': (self.cmd_news, 'NEWS'), b'VERSION': (self.cmd_ver, 'Version'), - b'HELP': (self.cmd_help, STR_TABLE['help'][self.connection.cli_language]), - b'?': (self.cmd_help, ''), - - b'NAME': (self.cmd_set_name, STR_TABLE['cmd_help_set_name'][self.connection.cli_language]), - b'QTH': (self.cmd_set_qth, STR_TABLE['cmd_help_set_qth'][self.connection.cli_language]), - b'LOC': (self.cmd_set_loc, STR_TABLE['cmd_help_set_loc'][self.connection.cli_language]), - b'ZIP': (self.cmd_set_zip, STR_TABLE['cmd_help_set_zip'][self.connection.cli_language]), - b'PRMAIL': (self.cmd_set_pr_mail, STR_TABLE['cmd_help_set_prmail'][self.connection.cli_language]), - b'EMAIL': (self.cmd_set_e_mail, STR_TABLE['cmd_help_set_email'][self.connection.cli_language]), - b'WEB': (self.cmd_set_http, STR_TABLE['cmd_help_set_http'][self.connection.cli_language]), b'USER': (self.cmd_user_db_detail, STR_TABLE['cmd_help_user_db'][self.connection.cli_language]), + b'DBNAME': (self.cmd_set_name, STR_TABLE['cmd_help_set_name'][self.connection.cli_language]), + b'DBQTH': (self.cmd_set_qth, STR_TABLE['cmd_help_set_qth'][self.connection.cli_language]), + b'DBLOC': (self.cmd_set_loc, STR_TABLE['cmd_help_set_loc'][self.connection.cli_language]), + b'DBZIP': (self.cmd_set_zip, STR_TABLE['cmd_help_set_zip'][self.connection.cli_language]), + b'DBPRMAIL': (self.cmd_set_pr_mail, STR_TABLE['cmd_help_set_prmail'][self.connection.cli_language]), + b'DBEMAIL': (self.cmd_set_e_mail, STR_TABLE['cmd_help_set_email'][self.connection.cli_language]), + b'DBWEB': (self.cmd_set_http, STR_TABLE['cmd_help_set_http'][self.connection.cli_language]), + b'LANG': (self.cmd_lang, STR_TABLE['cli_change_language'][self.connection.cli_language]), b'UMLAUT': (self.cmd_umlaut, STR_TABLE['auto_text_encoding'][self.connection.cli_language]), - b'INFO': (self.cmd_i, 'Info'), - b'LINFO': (self.cmd_li, 'Lange Info'), - b'NEWS': (self.cmd_news, 'NEWS'), b'POPT': (self.cmd_popt_banner, 'PoPT Banner'), + b'HELP': (self.cmd_help, STR_TABLE['help'][self.connection.cli_language]), + b'?': (self.cmd_shelp, STR_TABLE['cmd_shelp'][self.connection.cli_language]), + } self.str_cmd_exec = { @@ -343,6 +347,9 @@ def find_cmd(self): treffer.append(cmd) if not treffer: return f"\r # {STR_TABLE['cmd_not_known'][self.connection.cli_language]}\r" + if len(treffer) > 1: + return (f"\r # {STR_TABLE['cmd_not_known'][self.connection.cli_language]}" + f"\r # {(b' '.join(treffer)).decode(self.encoding[0], 'ignore')} ?\r") self.cmd = b'' ret = self.commands[treffer[0]][0]() # self.last_line = b'' @@ -463,6 +470,83 @@ def cmd_q(self): # Quit self.crone_state_index = 100 # Quit State return '' + def cmd_lang(self): + if not self.parameter: + return f'\r # {STR_TABLE["cli_no_lang_param"][self.connection.cli_language]}{" ".join(list(constant.LANG_IND.keys()))}\r' + self.decode_param() + if self.parameter[0].upper() in constant.LANG_IND.keys(): + self.connection.set_user_db_language(constant.LANG_IND[self.parameter[0].upper()]) + return f'\r # {STR_TABLE["cli_lang_set"][self.connection.cli_language]}\r' + return f'\r # {STR_TABLE["cli_no_lang_param"][self.connection.cli_language]}{" ".join(list(constant.LANG_IND.keys()))}\r' + + def cmd_dxlist(self): + parm = 10 + if self.parameter: + try: + parm = int(self.parameter[0]) + except ValueError: + pass + ret = self._get_alarm_out_cli(max_ent=parm) + + return ret + '\r' + + def _get_alarm_out_cli(self, max_ent=10): + alarm_his = dict(MH_LIST.dx_alarm_perma_hist) + alarm_his.update(dict(self.port_handler.get_aprs_ais().be_tracer_alarm_hist)) + if not alarm_his: + return f'\r # {STR_TABLE["cli_no_data"][self.connection.cli_language]}\r' + out = '\r' + out += "-----Time-Port---Call------via-------LOC------Dist(km)--Type---\r" + max_c = 0 + key_list = list(alarm_his.keys()) + key_list.sort(reverse=True) + for _k in key_list: + max_c += 1 + if max_c > max_ent: + break + time_delta_str = get_timedelta_str(alarm_his[_k]['ts']) + via = alarm_his[_k]['via'] + loc = alarm_his[_k]['loc'] + dis = str(alarm_his[_k]['dist']) + typ = alarm_his[_k]['typ'] + port = str(alarm_his[_k]['port_id']) + call = alarm_his[_k]['call_str'] + + out += f' {time_delta_str} {port:6} {call:10}{via:10}{loc:9}{dis:10}{typ}\r' + + return out + + def cmd_axip(self): + parm = 10 + if self.parameter: + try: + parm = int(self.parameter[0]) + except ValueError: + pass + ret = self._get_axip_out_cli(max_ent=parm) + + return ret + '\r' + + def _get_axip_out_cli(self, max_ent=10): + _ent = MH_LIST.get_sort_mh_entry('last', reverse=False) + if not _ent: + return f'\r # {STR_TABLE["cli_no_data"][self.connection.cli_language]}\r' + max_c = 0 + out = '\r' + # out += '\r < AXIP - Clients >\r\r' + out += '-Call------IP:Port---------------------------Last------------\r' + for k in _ent.keys(): + if _ent[k].axip_add[0]: + max_c += 1 + if max_c > max_ent: + break + out += ' {:9} {:33} {:8}\r'.format( + _ent[k].own_call, + _ent[k].axip_add[0] + ':' + str(_ent[k].axip_add[1]), + get_timedelta_str(_ent[k].last_seen, r_just=False) + ) + return out + def cmd_mh(self): parm = 20 if self.parameter: @@ -470,10 +554,51 @@ def cmd_mh(self): parm = int(self.parameter[0]) except ValueError: pass - ret = MH_LIST.mh_out_cli(max_ent=parm) + ret = self._get_mh_out_cli(max_ent=parm) return ret + '\r' + def _get_mh_out_cli(self, max_ent=20): + sort_list = MH_LIST.get_sort_mh_entry('last', False) + if not sort_list: + return f'\r # {STR_TABLE["cli_no_data"][self.connection.cli_language]}\r' + out = '\r' + # out += '\r < MH - List >\r\r' + c = 0 + max_c = 0 + """ + tp = 0 + tb = 0 + rj = 0 + """ + + for call in list(sort_list.keys()): + max_c += 1 + if max_c > max_ent: + break + time_delta_str = get_timedelta_str(sort_list[call].last_seen) + _call_str = sort_list[call].own_call + if sort_list[call].route: + _call_str += '*' + out += f'{time_delta_str} P:{sort_list[call].port:4} {_call_str:10}'.ljust(27, " ") + """ + tp += sort_list[call].pac_n + tb += sort_list[call].byte_n + rj += sort_list[call].rej_n + """ + c += 1 + if c == 2: # Breite + c = 0 + out += '\r' + """ + out += '\r' + out += '\rTotal Packets Rec.: ' + str(tp) + out += '\rTotal REJ-Packets Rec.: ' + str(rj) + out += '\rTotal Bytes Rec.: ' + str(tb) + """ + + return out + def cmd_mhl(self): parm = 10 if self.parameter: @@ -481,24 +606,140 @@ def cmd_mhl(self): parm = int(self.parameter[0]) except ValueError: pass - ret = MH_LIST.mh_long_out_cli(max_ent=parm) + ret = self._get_mh_long_out_cli(max_ent=parm) return ret + '\r' + def _get_mh_long_out_cli(self, max_ent=10): + sort_list = MH_LIST.get_sort_mh_entry('last', False) + if not sort_list: + return f'\r # {STR_TABLE["cli_no_data"][self.connection.cli_language]}\r' + out = '\r' + out += "-----Time-Port---Call------via-------LOC------Dist(km)--Type---Packets\r" + max_c = 0 + """ + tp = 0 + tb = 0 + rj = 0 + """ + for call in list(sort_list.keys()): + max_c += 1 + if max_c > max_ent: + break + time_delta_str = get_timedelta_str(sort_list[call].last_seen) + via = sort_list[call].route + if via: + via = via[-1] + else: + via = '' + loc = '' + dis = '' + typ = '' + userdb_ent = USER_DB.get_entry(sort_list[call].own_call, add_new=False) + if userdb_ent: + loc = userdb_ent.LOC + if userdb_ent.Distance: + dis = str(userdb_ent.Distance) + typ = userdb_ent.TYP + + out += (f' {time_delta_str:9}{sort_list[call].port:7}{sort_list[call].own_call:10}' + f'{via:10}{loc:9}{dis:10}{typ:7}{sort_list[call].pac_n}') + """ + tp += sort_list[call].pac_n + tb += sort_list[call].byte_n + rj += sort_list[call].rej_n + """ + out += '\r' + """ + out += '\rTotal Packets Rec.: ' + str(tp) + out += '\rTotal REJ-Packets Rec.: ' + str(rj) + out += '\rTotal Bytes Rec.: ' + str(tb) + out += '\r' + """ + return out + def cmd_wx(self): """ WX Stations """ if self.port_handler.aprs_ais is None: return f'\r # {STR_TABLE["cli_no_wx_data"][self.connection.cli_language]}\r\r' parm = 10 + _ret = '' + self.decode_param() if self.parameter: - try: - parm = int(self.parameter[0]) - except ValueError: - pass - ret = self.port_handler.aprs_ais.get_wx_cli_out(max_ent=parm) # TODO move CLI shit to cliMain - if not ret: + if self.parameter[0].isdigit(): + try: + parm = int(self.parameter[0]) + except ValueError: + pass + _ret = self._get_wx_cli_out(max_ent=parm) + else: + _call = validate_call(self.parameter[0]) + if _call: + _len = parm + if len(self.parameter) == 2: + try: + _len = int(self.parameter[1]) + except ValueError: + pass + _ret = self._get_wx_fm_call_cli_out(call=_call, max_ent=_len) + else: + _ret = self._get_wx_cli_out(max_ent=parm) + if not _ret: return f'\r # {STR_TABLE["cli_no_wx_data"][self.connection.cli_language]}\r\r' - return ret + '\r' + return _ret + '\r' + + def _get_wx_fm_call_cli_out(self, call, max_ent=10): + _data = self.port_handler.aprs_ais.get_wx_data().get(call, '') + if not _data: + return '' + _data.reverse() + max_c = 0 + _loc = f'{_data[0].get("locator", "------")[:6]}({round(_data[0].get("distance", -1))}km)' + out = '\r' + out += f'WX-Station: {call}\r' + out += f'Locator : {_loc}\r' + out += f'Comment : {_data[0].get("comment", "")}\r' + out += f'Datapoints: {len(_data)}\r\r' + out += '-----Last-Port--Temp-Press---Hum-Lum-Rain(24h)-WindGust\r' + for el in _data: + max_c += 1 + if max_c > max_ent: + break + # _ent = self.port_handler.aprs_ais.aprs_wx_msg_pool[k][-1] + _td = get_timedelta_str(el['rx_time']) + _pres = f'{el["weather"].get("pressure", 0):.2f}' + _rain = f'{el["weather"].get("rain_24h", 0):.3f}' + out += f'{_td.rjust(9):10}{el.get("port_id", ""):6}' + out += f'{str(round(el["weather"].get("temperature", 0))):5}' + out += f'{_pres:7} ' + out += f'{el["weather"].get("humidity", 0):3} ' + out += f'{el["weather"].get("luminosity", 0):3} ' + out += f'{_rain:9} ' + out += f'{el["weather"].get("wind_gust", 0):.3f}\r' + return out + + def _get_wx_cli_out(self, max_ent=10): + _data = self.port_handler.aprs_ais.get_wx_entry_sort_distance() + if not _data: + return '' + max_c = 0 + out = '\r-----Last-Port--Call------LOC-------------Temp-Press---Hum-Lum-Rain(24h)-\r' + for k in _data: + max_c += 1 + if max_c > max_ent: + break + _ent = self.port_handler.aprs_ais.aprs_wx_msg_pool[k][-1] + _td = get_timedelta_str(_ent['rx_time']) + _loc = f'{_ent.get("locator", "------")[:6]}({round(_ent.get("distance", -1))}km)' + _pres = f'{_ent["weather"].get("pressure", 0):.2f}' + _rain = f'{_ent["weather"].get("rain_24h", 0):.3f}' + out += f'{_td.rjust(9):10}{_ent.get("port_id", ""):6}{k:10}{_loc:16}' + out += f'{str(round(_ent["weather"].get("temperature", 0))):5}' + out += f'{_pres:7} ' + out += f'{_ent["weather"].get("humidity", 0):3} ' + out += f'{_ent["weather"].get("luminosity", 0):3} ' + out += f'{_rain:6}\r' + return out def cmd_aprs_trace(self): """APRS Tracer""" @@ -521,11 +762,14 @@ def cmd_aprs_trace(self): intervall_str = 'off' else: intervall_str = str(_intervall) - out = '\r # APRS-Tracer Beacon\r\r' - out += f'Tracer Call : {self.port_handler.aprs_ais.be_tracer_port}\r' - out += f'Tracer Port : {self.port_handler.aprs_ais.be_tracer_station}\r' + # out = '\r # APRS-Tracer Beacon\r\r' + out = '\r' + out += f'Tracer Port : {self.port_handler.aprs_ais.be_tracer_port}\r' + out += f'Tracer Call : {self.port_handler.aprs_ais.be_tracer_station}\r' out += f'Tracer WIDE Path: {self.port_handler.aprs_ais.be_tracer_wide}\r' out += f'Tracer intervall: {intervall_str}\r' + out += f'Auto Tracer : {constant.BOOL_ON_OFF.get(self.port_handler.aprs_ais.be_auto_tracer_active, False).lower()}\r' + # out += f'APRS-Server : {constant.BOOL_ON_OFF.get(self.port_handler.aprs_ais., False).lower()}\r' out += f'Last Trace send : {_last_send}\r\r' out += '-----Last-Port--Call------LOC-------------Path----------------------------------\r' max_c = 0 @@ -538,9 +782,18 @@ def cmd_aprs_trace(self): _path = ', '.join(_ent.get('path', [])) _loc = f'{_ent.get("locator", "------")[:6]}({round(_ent.get("distance", -1))}km)' _call = _ent.get('call', '') - _path = ', '.join(_ent.get('path', [])) + _path_raw = _ent.get('path', []) + _path = '' + _c = 0 + for _el in _path_raw: + _path += f'{_el}> ' + _c += 1 + if _c == 3: + _path += '\r' + ''.rjust(42, ' ') + _c = 0 + out += f'{_td.rjust(9):10}{_ent.get("port_id", "-"):6}{_call:10}{_loc:16}' - out += f'{_path}' + out += f'{_path[:-2]}' out += '\r' return out + '\r' @@ -826,7 +1079,8 @@ def cmd_lcstatus(self): return ret def cmd_help(self): - ret = f"\r < {STR_TABLE['help'][self.connection.cli_language]} >\r" + # ret = f"\r < {STR_TABLE['help'][self.connection.cli_language]} >\r" + ret = "\r" """ c = 1 new_cmd = {} @@ -861,6 +1115,19 @@ def cmd_help(self): ret += '\r\r' return ret + def cmd_shelp(self): + ret = '\r # ' + _c = 0 + _cmds = list(self.commands.keys()) + _cmds.sort() + for k in _cmds: + ret += (k + b' ').decode(self.encoding[0], 'ignore') + if len(ret) - _c > 60: + ret += '\r # ' + _c += 60 + ret += '\r\r' + return ret + def cmd_umlaut(self): # print(self.parameter) if not self.parameter: diff --git a/constant.py b/constant.py index 17e76b06..bdc16ad3 100644 --- a/constant.py +++ b/constant.py @@ -3,13 +3,19 @@ https://stackoverflow.com/questions/2682745/how-do-i-create-a-constant-in-python """ -VER = '2.97.9' +VER = '2.98.15' LANGUAGE = 0 # QUICK FIX """ 0 = German 1 = English 2 = Dutch """ +LANG_IND = { + 'DE': 0, + 'EN': 1, + 'NL': 2, + } + CFG_data_path = 'data/' CFG_usertxt_path = 'userdata/' CFG_ft_downloads = 'ft_downloads/' @@ -17,6 +23,9 @@ CFG_mh_data_file = 'data/mh_data.popt' CFG_port_stat_data_file = 'data/port_stat.popt' CFG_aprs_data_file = 'aprs.popt' +CFG_sound_DICO = '//data//sound//disco_alarm.wav' +CFG_sound_CONN = '//data//sound//conn_alarm.wav' +CFG_sound_RX_BEEP = '//data//sound//rx_beep.wav' CFG_txt_save = { 'stat_parm_cli_ctext': 'ctx', @@ -121,6 +130,7 @@ FONT_STAT_BAR = 'Arial' PARAM_MAX_MON_LEN = 100000 CFG_clr_sys_msg = 'red' +CFG_TR_DX_ALARM_BG_CLR = '#55ed9f' POPT_BANNER = '\r$$$$$$$\ $$$$$$\ $$$$$$$\ $$$$$$$$|\r' \ @@ -173,3 +183,8 @@ 'DEST-RNR-REJ': 'sky blue', # 15 'BOTH-RNR-REJ': 'deep sky blue', # 16 } + +BOOL_ON_OFF = { + True: 'ON', + False: 'OFF', +} diff --git a/fnc/str_fnc.py b/fnc/str_fnc.py index a750e09d..09f77ebc 100644 --- a/fnc/str_fnc.py +++ b/fnc/str_fnc.py @@ -31,6 +31,10 @@ def conv_time_for_sorting(dateti: datetime.now()): return dateti.strftime('%y/%m/%d %H:%M:%S') +def conv_time_for_key(dateti: datetime.now()): + return dateti.strftime('%y%m%d%H%M%S') + + def conv_time_US_str(dateti: datetime.now()): return dateti.strftime('%m/%d/%y %H:%M:%S') @@ -43,7 +47,7 @@ def get_file_timestamp(): return datetime.now().strftime('%d%m/%y-%H%M') -def get_timedelta_str(dateti: datetime.now()): +def get_timedelta_str(dateti: datetime.now(), r_just=True): _time_delta = datetime.now() - dateti _td_days = _time_delta.days _td_hours = int(_time_delta.seconds / 3600) @@ -52,15 +56,27 @@ def get_timedelta_str(dateti: datetime.now()): if _td_days: # td_hours = td_hours - td_days * 24 - _time_delta_str = f'{str(_td_days).rjust(3, " ")}d,{str(_td_hours).rjust(2, " ")}h' + if r_just: + _time_delta_str = f'{str(_td_days).rjust(3, " ")}d,{str(_td_hours).rjust(2, " ")}h' + else: + _time_delta_str = f'{_td_days}d,{_td_hours}h' elif _td_hours: _td_min = _td_min - _td_hours * 60 - _time_delta_str = f'{str(_td_hours).rjust(3, " ")}h,{str(_td_min).rjust(2, " ")}m' + if r_just: + _time_delta_str = f'{str(_td_hours).rjust(3, " ")}h,{str(_td_min).rjust(2, " ")}m' + else: + _time_delta_str = f'{_td_hours}h,{_td_min}m' elif _td_min: _td_sec = _td_sec - _td_min * 60 - _time_delta_str = f'{str(_td_min).rjust(3, " ")}m,{str(_td_sec).rjust(2, " ")}s' + if r_just: + _time_delta_str = f'{str(_td_min).rjust(3, " ")}m,{str(_td_sec).rjust(2, " ")}s' + else: + _time_delta_str = f'{_td_min}m,{_td_sec}s' else: - _time_delta_str = f'{str(_td_sec).rjust(7, " ")}s' + if r_just: + _time_delta_str = f'{str(_td_sec).rjust(7, " ")}s' + else: + _time_delta_str = f'{_td_sec}s' return _time_delta_str @@ -104,7 +120,6 @@ def convert_str_to_datetime(date_str, date_format='%d/%m/%y %H:%M:%S'): except ValueError: return 0 - def conv_timestamp_delta(delta): if delta: timestamp = str(delta) diff --git a/fnc/struct_fnc.py b/fnc/struct_fnc.py new file mode 100644 index 00000000..3bdfe61a --- /dev/null +++ b/fnc/struct_fnc.py @@ -0,0 +1,65 @@ +from datetime import datetime + +from fnc.str_fnc import conv_time_for_key + + +def get_dx_tx_alarm_his_pack( + port_id: int, + call_str: str, + via: str, + path: list, + locator: str, + distance: float, + typ: str, +): + _now = datetime.now() + return { + 'ts': _now, + 'port_id': port_id, + 'call_str': call_str, + 'via': via, + 'path': path, + 'loc': locator, + 'dist': distance, + 'typ': typ, + 'key': f"{conv_time_for_key(_now)}{call_str}", + + } + + +def get_bandwidth_struct(): + _struct = {} + for _h in range(24): + for _m in range(60): + for _s in range(6): + _ts_str = f'{str(_h).zfill(2)}:{str(_m).zfill(2)}:{_s}' + _struct[_ts_str] = 0 + return _struct + + +def get_port_stat_struct(): + struct_hour = {} + for key in [ + 'N_pack', + 'I', + 'SABM', + 'DM', + 'DISC', + 'REJ', + 'RR', + 'RNR', + 'UI', + 'FRMR', + 'DATA_W_HEADER', + 'DATA' + ]: + struct_hour[key] = {minute: 0 for minute in range(60)} + return struct_hour + + +def init_day_dic(): + + ret = {} + for hour in range(24): + ret[hour] = get_port_stat_struct() + return ret diff --git a/gui/guiAPRS_be_tracer.py b/gui/guiAPRS_be_tracer.py index b4c3951b..59d503cd 100644 --- a/gui/guiAPRS_be_tracer.py +++ b/gui/guiAPRS_be_tracer.py @@ -3,6 +3,7 @@ from tkinter import ttk from ax25.ax25InitPorts import PORT_HANDLER +from constant import CFG_TR_DX_ALARM_BG_CLR logger = logging.getLogger(__name__) @@ -212,8 +213,12 @@ def update_tree_data(self): def _update_tree(self): for i in self._tree.get_children(): self._tree.delete(i) + self._tree.tag_configure("tr_alarm", background=CFG_TR_DX_ALARM_BG_CLR, foreground='black') for ret_ent in self._tree_data: - self._tree.insert('', tk.END, values=ret_ent) + if ret_ent[1]: + self._tree.insert('', tk.END, values=ret_ent[0], tags=('tr_alarm',)) + else: + self._tree.insert('', tk.END, values=ret_ent[0], ) def _format_tree_data(self): _traces = PORT_HANDLER.get_aprs_ais().tracer_traces_get() @@ -232,7 +237,7 @@ def _format_tree_data(self): _loc = _pack.get('locator', '') _dist = _pack.get('distance', 0) - self._tree_data.append(( + self._tree_data.append((( _rx_time, _call, _port_id, @@ -240,7 +245,8 @@ def _format_tree_data(self): f'{_rtt:.2f}', _loc, _dist, - )) + ), _pack.get('tr_alarm', False))) + _pack['tr_alarm'] = False def _save_btn(self): self._save_vars() @@ -283,9 +289,7 @@ def _chk_port(self, event=None): def _chk_active(self, event=None): self._save_vars() - _root_gui = PORT_HANDLER.get_root_gui() - if _root_gui is not None: - _root_gui.tabbed_sideFrame.set_tracer_chk(PORT_HANDLER.get_aprs_ais().be_tracer_active) + self._root_win.set_tracer_fm_aprs() def close(self): self._root_win.be_tracer_win = None diff --git a/gui/guiAPRS_wx_plot.py b/gui/guiAPRS_wx_plot.py index bb605395..312df364 100644 --- a/gui/guiAPRS_wx_plot.py +++ b/gui/guiAPRS_wx_plot.py @@ -6,9 +6,6 @@ from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -from fnc.loc_fnc import coordinates_to_locator -from fnc.str_fnc import convert_str_to_datetime - def adjust_list_len(target_list: list, compare_list: list): if len(target_list) < len(compare_list): diff --git a/gui/guiAPRS_wx_tree.py b/gui/guiAPRS_wx_tree.py index f56048ce..18192622 100644 --- a/gui/guiAPRS_wx_tree.py +++ b/gui/guiAPRS_wx_tree.py @@ -3,7 +3,6 @@ from tkinter import ttk from ax25.ax25InitPorts import PORT_HANDLER -from fnc.loc_fnc import coordinates_to_locator, locator_distance from gui.guiAPRS_wx_plot import WXPlotWindow logger = logging.getLogger(__name__) diff --git a/gui/guiMH.py b/gui/guiMH.py index b462df58..3c713bb1 100644 --- a/gui/guiMH.py +++ b/gui/guiMH.py @@ -1,8 +1,10 @@ import logging import tkinter as tk -from tkinter import Menu from tkinter import ttk + +from ax25.ax25InitPorts import PORT_HANDLER from ax25.ax25Statistics import MyHeard, MH_LIST +from constant import CFG_TR_DX_ALARM_BG_CLR from fnc.str_fnc import conv_time_DE_str logger = logging.getLogger(__name__) @@ -12,10 +14,6 @@ class MHWin(tk.Toplevel): def __init__(self, root_win): tk.Toplevel.__init__(self) self._root_win = root_win - ################################### - # Vars - self._rev_ent = False - # self.mh_win = tk.Tk() self.title("MyHEARD") self.style = self._root_win.style # self.geometry("1250x700") @@ -30,21 +28,152 @@ def __init__(self, root_win): self.iconbitmap("favicon.ico") except tk.TclError: pass - self.lift() + """ self._menubar = Menu(self) self.config(menu=self._menubar) self._menubar.add_command(label="Quit", command=self.close) self._menubar.add_command(label="Port-Statistik", command=lambda: self._root_win.open_port_stat_win()) + """ + self.lift() + ################################### + # Vars + self._rev_ent = False + # self._alarm_active_var = self._root_win.setting_dx_alarm + self._alarm_newCall_var = tk.BooleanVar(self) + self._alarm_seenSince_var = tk.StringVar(self) + self._alarm_distance_var = tk.StringVar(self) + # self._tracer_active_var = tk.BooleanVar(self) + self._tracer_duration_var = tk.StringVar(self) + self._alarm_ports = [] + _ports = list(PORT_HANDLER.get_all_ports().keys()) + for _por_id in _ports: + self._alarm_ports.append(tk.BooleanVar(self)) + self._get_vars() # ############################### Columns ############################ self.grid_columnconfigure(0, weight=1) self.grid_rowconfigure(0, weight=0, minsize=50) - self.grid_rowconfigure(1, weight=1) + self.grid_rowconfigure(1, weight=0, minsize=25) + self.grid_rowconfigure(2, weight=0, minsize=25) + self.grid_rowconfigure(3, weight=1) + # ###################### DX Alarm Settings ###################### + # ALARM + lower_frame = tk.Frame(self) + lower_frame.grid(row=0, column=0, columnspan=2, sticky='nsew') + frame_11_label = tk.Frame(lower_frame) + frame_11_label.pack(side=tk.TOP) + tk.Label(frame_11_label, text='DX-Alarm Setting').pack() + ### + # activ Checkbox + frame_21_active = tk.Frame(lower_frame) + frame_21_active.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) + + tk.Label(frame_21_active, text='Activate ').pack(side=tk.LEFT, ) + tk.Checkbutton(frame_21_active, + variable=self._root_win.setting_dx_alarm, + # command=self._chk_alarm_active + ).pack(side=tk.LEFT, ) + + # New Call in List Checkbox + frame_21_newCall = tk.Frame(lower_frame) + frame_21_newCall.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) + + tk.Label(frame_21_newCall, text='new Call ').pack(side=tk.LEFT, ) + tk.Checkbutton(frame_21_newCall, + variable=self._alarm_newCall_var, + command=self._set_alarm_newCall + ).pack(side=tk.LEFT, ) + + # Alarm seen since Days + frame_21_seen = tk.Frame(lower_frame) + frame_21_seen.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) + + tk.Label(frame_21_seen, text='seen since (Days) (0 = off)').pack(side=tk.LEFT, ) + tk.Spinbox(frame_21_seen, + from_=1, + to=365, + increment=1, + width=4, + textvariable=self._alarm_seenSince_var, + command=self._set_alarm_last_seen + ).pack(side=tk.LEFT, ) + + # Alarm Distance + frame_21_distance = tk.Frame(lower_frame) + frame_21_distance.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) + + tk.Label(frame_21_distance, text='Distance (0 = off)').pack(side=tk.LEFT, ) + tk.Spinbox(frame_21_distance, + from_=0, + to=20000, + increment=1, + width=6, + textvariable=self._alarm_distance_var, + command=self._set_alarm_distance + ).pack(side=tk.LEFT, ) + + # ###################### Ports ############################ + lower_frame_ports = tk.Frame(self) + lower_frame_ports.grid(row=1, column=0, columnspan=2, sticky='nsew') + frame_13_label = tk.Frame(lower_frame_ports) + frame_13_label.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) + tk.Label(frame_13_label, text='Ports: ').pack(side=tk.LEFT, ) + _i = 0 + for _port_id in _ports: + _frame = tk.Frame(lower_frame_ports) + _frame.pack(side=tk.LEFT, fill=tk.BOTH, padx=7) + _text = f'{_port_id}' + tk.Label(_frame, text=_text, width=3).pack(side=tk.LEFT, padx=1) + tk.Checkbutton(_frame, + variable=self._alarm_ports[_i], + command=self._set_alarm_ports, + ).pack(side=tk.LEFT, padx=1) + _i += 1 + # ###################### Auto Tracer ###################### + # Tracer + _auto_tracer_state = { + True: 'disabled', + False: 'normal' + }.get(self._root_win.get_tracer(), 'disabled') + lower_frame_tracer = tk.Frame(self) + lower_frame_tracer.grid(row=2, column=0, columnspan=2, sticky='nsew') + frame_12_label = tk.Frame(lower_frame_tracer) + frame_12_label.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) + tk.Label(frame_12_label, text='Auto APRS-Tracer: ').pack(side=tk.LEFT, ) + ### + # activ Checkbox + frame_22_active = tk.Frame(lower_frame_tracer) + frame_22_active.pack(side=tk.LEFT, fill=tk.BOTH, ) + + tk.Label(frame_22_active, text='Activate ').pack(side=tk.LEFT, ) + tk.Checkbutton(frame_22_active, + variable=self._root_win.setting_auto_tracer, + command=self._root_win.set_auto_tracer, + state=_auto_tracer_state + ).pack(side=tk.LEFT, ) + # duration + frame_22_duration = tk.Frame(lower_frame_tracer) + frame_22_duration.pack(side=tk.LEFT, fill=tk.BOTH, padx=30) + + tk.Label(frame_22_duration, text='Duration (min) ').pack(side=tk.LEFT, ) + tk.Spinbox(frame_22_duration, + from_=5, + to=1440, + increment=5, + width=5, + textvariable=self._tracer_duration_var, + state=_auto_tracer_state, + command=self._set_auto_tracer, + ).pack(side=tk.LEFT, ) + ########################################################################################## # TREE columns = ( 'mh_last_seen', - 'mh_first_seen' , - 'mh_port', 'mh_call', + 'mh_first_seen', + 'mh_port', + 'mh_call', + 'mh_loc', + 'mh_dist', 'mh_nPackets', 'mh_REJ', 'mh_route', @@ -52,25 +181,32 @@ def __init__(self, root_win): 'mh_ip_fail' ) self._tree = ttk.Treeview(self, columns=columns, show='headings') - self._tree.grid(row=1, column=0, sticky='nsew') + self._tree.grid(row=3, column=0, sticky='nsew') # add a scrollbar scrollbar = ttk.Scrollbar(self, orient=tk.VERTICAL, command=self._tree.yview) self._tree.configure(yscrollcommand=scrollbar.set) - scrollbar.grid(row=1, column=1, sticky='ns') + scrollbar.grid(row=3, column=1, sticky='ns') self._tree.heading('mh_last_seen', text='Letzte Paket', command=lambda: self._sort_entry('last')) self._tree.heading('mh_first_seen', text='Erste Paket', command=lambda: self._sort_entry('first')) self._tree.heading('mh_port', text='Port', command=lambda: self._sort_entry('port')) self._tree.heading('mh_call', text='Call', command=lambda: self._sort_entry('call')) + self._tree.heading('mh_loc', text='LOC', command=lambda: self._sort_entry('loc')) + self._tree.heading('mh_dist', text='km', command=lambda: self._sort_entry('dist')) self._tree.heading('mh_nPackets', text='Packets', command=lambda: self._sort_entry('pack')) self._tree.heading('mh_REJ', text='REJs', command=lambda: self._sort_entry('rej')) self._tree.heading('mh_route', text='Route', command=lambda: self._sort_entry('route')) self._tree.heading('mh_last_ip', text='AXIP', command=lambda: self._sort_entry('axip')) self._tree.heading('mh_ip_fail', text='Fail', command=lambda: self._sort_entry('axipfail')) - self._tree.column("mh_port", anchor=tk.CENTER, stretch=tk.NO, width=80) - self._tree.column("mh_nPackets", anchor=tk.CENTER, stretch=tk.NO, width=80) - self._tree.column("mh_REJ", anchor=tk.CENTER, stretch=tk.NO, width=55) - self._tree.column("mh_ip_fail", anchor=tk.CENTER, stretch=tk.NO, width=50) + self._tree.column("mh_last_seen", anchor=tk.W, stretch=tk.YES, width=180) + self._tree.column("mh_first_seen", anchor=tk.W, stretch=tk.YES, width=180) + self._tree.column("mh_call", anchor=tk.W, stretch=tk.YES, width=120) + self._tree.column("mh_loc", anchor=tk.W, stretch=tk.YES, width=100) + self._tree.column("mh_dist", anchor=tk.W, stretch=tk.YES, width=70) + self._tree.column("mh_port", anchor=tk.W, stretch=tk.NO, width=80) + self._tree.column("mh_nPackets", anchor=tk.W, stretch=tk.NO, width=80) + self._tree.column("mh_REJ", anchor=tk.W, stretch=tk.NO, width=55) + self._tree.column("mh_ip_fail", anchor=tk.W, stretch=tk.NO, width=50) # self.tree.column("# 2", anchor=tk.CENTER, stretch=tk.YES) # tree.column(1, stretch=True) @@ -79,13 +215,66 @@ def __init__(self, root_win): self._update_mh() self._tree.bind('<>', self.entry_selected) + def _get_vars(self): + # self._alarm_active_var.set(bool()) + self._alarm_newCall_var.set(bool(MH_LIST.parm_new_call_alarm)) + self._alarm_seenSince_var.set(str(MH_LIST.parm_lastseen_alarm)) + self._alarm_distance_var.set(str(MH_LIST.parm_distance_alarm)) + # self._tracer_active_var.set(bool(self._root_win.setting_auto_tracer.get())) + self._tracer_duration_var.set(str(self._root_win.get_auto_tracer_duration())) + _i = 0 + for _var in self._alarm_ports: + if _i in MH_LIST.parm_alarm_ports: + _var.set(True) + else: + _var.set(False) + _i += 1 + + def _set_alarm_ports(self, event=None): + _i = 0 + for _var in self._alarm_ports: + if _var.get(): + if _i not in MH_LIST.parm_alarm_ports: + MH_LIST.parm_alarm_ports.append(int(_i)) + else: + if _i in MH_LIST.parm_alarm_ports: + MH_LIST.parm_alarm_ports.remove(int(_i)) + _i += 1 + + def _set_alarm_distance(self, event=None): + _var = self._alarm_distance_var.get() + try: + _var = int(_var) + except ValueError: + return + MH_LIST.parm_distance_alarm = _var + + def _set_alarm_last_seen(self, event=None): + _var = self._alarm_seenSince_var.get() + try: + _var = int(_var) + except ValueError: + return + MH_LIST.parm_lastseen_alarm = _var + + def _set_alarm_newCall(self, event=None): + MH_LIST.parm_new_call_alarm = bool(self._alarm_newCall_var.get()) + + def _set_auto_tracer(self, event=None): + _dur = self._tracer_duration_var.get() + try: + _dur = int(_dur) + except ValueError: + return + self._root_win.set_auto_tracer_duration(_dur) + def entry_selected(self, event): for selected_item in self._tree.selection(): item = self._tree.item(selected_item) record = item['values'] # show a message call = record[3] - vias = record[6] + vias = record[8] port = record[2] port = int(port.split(' ')[0]) if vias: @@ -104,8 +293,12 @@ def _init_tree_data(self): def _update_tree(self): for i in self._tree.get_children(): self._tree.delete(i) + self._tree.tag_configure("dx_alarm", background=CFG_TR_DX_ALARM_BG_CLR, foreground='black') for ret_ent in self._tree_data: - self._tree.insert('', tk.END, values=ret_ent) + if ret_ent[1]: + self._tree.insert('', tk.END, values=ret_ent[0], tags=('dx_alarm',)) + else: + self._tree.insert('', tk.END, values=ret_ent[0], ) def _sort_entry(self, flag: str): sort_date = MH_LIST.get_sort_mh_entry(flag_str=flag, reverse=self._rev_ent) @@ -125,26 +318,29 @@ def _format_tree_ent(self, mh_list): axip_str = '{} - {}'.format(ent.axip_add[0], ent.axip_add[1]) else: axip_str = '' - route = '' - if ent.all_routes: - route = min(ent.all_routes) + _dx_alarm = False + if ent.own_call in list(MH_LIST.dx_alarm_hist): + _dx_alarm = True - self._tree_data.append(( + self._tree_data.append((( f'{conv_time_DE_str(ent.last_seen)}', f'{conv_time_DE_str(ent.first_seen)}', f'{ent.port_id} {ent.port}', f'{ent.own_call}', + f'{ent.locator}', + f'{ent.distance}', f'{ent.pac_n}', f'{ent.rej_n}', - ' '.join(route), + ' '.join(ent.route), f'{axip_str}', f'{ent.axip_fail}', - )) + ), _dx_alarm)) def __del__(self): self._root_win.mh_window = None # self.destroy() def close(self): + MH_LIST.reset_dx_alarm_his() self._root_win.mh_window = None self.destroy() diff --git a/gui/guiMain.py b/gui/guiMain.py index d88ffa50..4a0468f3 100644 --- a/gui/guiMain.py +++ b/gui/guiMain.py @@ -48,7 +48,8 @@ from gui.guiFileTX import FileSend from constant import LANGUAGE, FONT, POPT_BANNER, WELCOME_SPEECH, VER, CFG_clr_sys_msg, STATION_TYPS, \ ENCODINGS, TEXT_SIZE_STATUS, TXT_BACKGROUND_CLR, TXT_OUT_CLR, TXT_INP_CLR, TXT_INP_CURSOR_CLR, TXT_MON_CLR, \ - STAT_BAR_CLR, STAT_BAR_TXT_CLR, FONT_STAT_BAR, STATUS_BG, PARAM_MAX_MON_LEN + STAT_BAR_CLR, STAT_BAR_TXT_CLR, FONT_STAT_BAR, STATUS_BG, PARAM_MAX_MON_LEN, CFG_sound_RX_BEEP, CFG_sound_CONN, \ + CFG_sound_DICO from string_tab import STR_TABLE from fnc.os_fnc import is_linux, is_windows, get_root_dir from fnc.gui_fnc import get_all_tags, set_all_tags, generate_random_hex_color @@ -88,39 +89,39 @@ def __init__(self, main_cl): self.style = main_cl.style self.ch_index = main_cl.channel_index - self._tab_side_frame = tk.Frame( + _tab_side_frame = tk.Frame( main_cl.get_side_frame(), # width=300, height=400 ) - self._tab_side_frame.grid(row=3, column=0, columnspan=6, pady=10, sticky="nsew") + _tab_side_frame.grid(row=3, column=0, columnspan=6, pady=10, sticky="nsew") self._tabControl = ttk.Notebook( - self._tab_side_frame, + _tab_side_frame, height=300, # width=500 ) tab1_kanal = ttk.Frame(self._tabControl) # self.tab1_1_RTT = ttk.Frame(self._tabControl) - self.tab2_mh = tk.Frame(self._tabControl) - # self.tab2_mh.bind("", self.reset_dx_alarm) - self.tab2_mh_def_bg_clr = self.tab2_mh.cget('bg') - self.tab4_settings = ttk.Frame(self._tabControl) - self.tab5_ch_links = ttk.Frame(self._tabControl) # TODO - self.tab6_monitor = ttk.Frame(self._tabControl) - self.tab7_tracer = ttk.Frame(self._tabControl) + tab2_mh = tk.Frame(self._tabControl) + # tab2_mh.bind("", self.reset_dx_alarm) + # self.tab2_mh_def_bg_clr = tab2_mh.cget('bg') + tab4_settings = ttk.Frame(self._tabControl) + # self.tab5_ch_links = ttk.Frame(self._tabControl) # TODO + tab6_monitor = ttk.Frame(self._tabControl) + tab7_tracer = ttk.Frame(self._tabControl) self._tabControl.add(tab1_kanal, text='Kanal') # tab3 = ttk.Frame(self._tabControl) # TODO # self._tabControl.add(tab3, text='Ports') # TODO - self._tabControl.add(self.tab4_settings, text='Global') - self._tabControl.add(self.tab6_monitor, text='Monitor') - self._tabControl.add(self.tab2_mh, text='MH') - self._tabControl.add(self.tab7_tracer, text='Tracer') + self._tabControl.add(tab4_settings, text='Global') + self._tabControl.add(tab6_monitor, text='Monitor') + self._tabControl.add(tab2_mh, text='MH') + self._tabControl.add(tab7_tracer, text='Tracer') # self._tabControl.add(self.tab5_ch_links, text='CH-Echo') # TODO self._tabControl.pack(expand=0, fill="both") - self._tabControl.select(self.tab2_mh) + self._tabControl.select(tab2_mh) ################################################ # Kanal parm_y = 20 @@ -316,104 +317,107 @@ def __init__(self, main_cl): ################################ # MH ########################## # TREE - self.tab2_mh.columnconfigure(0, minsize=300, weight=1) + tab2_mh.columnconfigure(0, minsize=300, weight=1) columns = ( 'mh_last_seen', 'mh_call', + 'mh_dist', 'mh_port', 'mh_nPackets', 'mh_route', ) - self._tree = ttk.Treeview(self.tab2_mh, columns=columns, show='headings') + self._tree = ttk.Treeview(tab2_mh, columns=columns, show='headings') self._tree.grid(row=0, column=0, sticky='nsew') self._tree.heading('mh_last_seen', text='Zeit') self._tree.heading('mh_call', text='Call') + self._tree.heading('mh_dist', text='km') self._tree.heading('mh_port', text='Port') self._tree.heading('mh_nPackets', text='PACK') self._tree.heading('mh_route', text='Route') - self._tree.column("mh_last_seen", anchor=tk.CENTER, stretch=tk.NO, width=90) - self._tree.column("mh_call", stretch=tk.NO, width=100) - self._tree.column("mh_port", anchor=tk.CENTER, stretch=tk.NO, width=80) - self._tree.column("mh_nPackets", anchor=tk.CENTER, stretch=tk.NO, width=60) - self._tree.column("mh_route", stretch=tk.YES, width=180) + self._tree.column("mh_last_seen", anchor=tk.W, stretch=tk.NO, width=85) + self._tree.column("mh_call", anchor=tk.W, stretch=tk.NO, width=105) + self._tree.column("mh_dist", anchor=tk.CENTER, stretch=tk.NO, width=70) + self._tree.column("mh_port", anchor=tk.W, stretch=tk.NO, width=61) + self._tree.column("mh_nPackets", anchor=tk.W, stretch=tk.NO, width=60) + self._tree.column("mh_route", anchor=tk.W, stretch=tk.YES, width=180) self._tree_data = [] self._last_mh_ent = [] self._update_side_mh() self._tree.bind('<>', self._entry_selected) - # Settings ########################## + # Global Settings ########################## # Global Sound - self.sound_on = tk.BooleanVar(self.tab4_settings) - Checkbutton(self.tab4_settings, + Checkbutton(tab4_settings, text="Sound", - variable=self.sound_on, + variable=self._main_win.setting_sound, ).place(x=10, y=10) - # self.sound_on.set(True) # Global Sprech - self.sprech_on = tk.BooleanVar(self.tab4_settings) - sprech_btn = Checkbutton(self.tab4_settings, + sprech_btn = Checkbutton(tab4_settings, text="Sprachausgabe", - variable=self.sprech_on, + variable=self._main_win.setting_sprech, command=self._chk_sprech_on ) sprech_btn.place(x=10, y=35) - if is_linux(): - self.sprech_on.set(True) - else: - self.sprech_on.set(False) + if not is_linux(): sprech_btn.configure(state='disabled') - # Global Bake - self.bake_on = tk.BooleanVar(self.tab4_settings) - Checkbutton(self.tab4_settings, + Checkbutton(tab4_settings, text="Baken", - variable=self.bake_on, + variable=self._main_win.setting_bake, ).place(x=10, y=60) - self.bake_on.set(True) - # self.bake_on.set(True) # DX Alarm > dx_alarm_on - self.tracer_on = tk.BooleanVar(self.tab4_settings) - - _chk_btn = Checkbutton(self.tab4_settings, - text="Tracer", - variable=self.tracer_on, - command=self._chk_tracer, - # state='disabled' - ) - _chk_btn.place(x=10, y=85) - + Checkbutton(tab4_settings, + text="Tracer", + variable=self._main_win.setting_tracer, + command=self._chk_tracer, + # state='disabled' + ).place(x=10, y=85) + _auto_tracer_state = { + True: 'disabled', + False: 'normal' + }.get(self._main_win.get_tracer(), 'disabled') + self._autotracer_chk_btn = Checkbutton(tab4_settings, + text="Auto-Tracer", + variable=self._main_win.setting_auto_tracer, + command=self._chk_auto_tracer, + state=_auto_tracer_state + ) + self._autotracer_chk_btn.place(x=10, y=110) + Checkbutton(tab4_settings, + text="DX-Alarm", + variable=self._main_win.setting_dx_alarm, + command=self._main_win.set_dx_alarm, + # state='disabled' + ).place(x=10, y=135) # RX ECHO - self.rx_echo_on = tk.BooleanVar(self.tab4_settings) - _chk_btn = Checkbutton(self.tab4_settings, - text="RX-Echo", - variable=self.rx_echo_on, - ) - _chk_btn.place(x=10, y=115) - + Checkbutton(tab4_settings, + text="RX-Echo", + variable=self._main_win.setting_rx_echo, + ).place(x=10, y=160) ############ # CH ECHO - self.chk_btn_default_clr = _chk_btn.cget('bg') - self.ch_echo_vars = {} + self._chk_btn_default_clr = self._autotracer_chk_btn.cget('bg') + self._ch_echo_vars = {} ################# ################# # Monitor Frame # Address _x = 10 _y = 10 - self.to_add_var = tk.StringVar(self.tab6_monitor) - tk.Label(self.tab6_monitor, text=f"{STR_TABLE['to'][self._lang]}:").place(x=_x, y=_y) - self.to_add_ent = tk.Entry(self.tab6_monitor, textvariable=self.to_add_var) + self.to_add_var = tk.StringVar(tab6_monitor) + tk.Label(tab6_monitor, text=f"{STR_TABLE['to'][self._lang]}:").place(x=_x, y=_y) + self.to_add_ent = tk.Entry(tab6_monitor, textvariable=self.to_add_var) self.to_add_ent.place(x=_x + 40, y=_y) # CMD/RPT _x = 10 _y = 80 - self.cmd_var = tk.BooleanVar(self.tab6_monitor) - self.cmd_ent = tk.Checkbutton(self.tab6_monitor, + self.cmd_var = tk.BooleanVar(tab6_monitor) + self.cmd_ent = tk.Checkbutton(tab6_monitor, variable=self.cmd_var, text='CMD/RPT') self.cmd_ent.place(x=_x, y=_y) @@ -421,8 +425,8 @@ def __init__(self, main_cl): # Poll _x = 10 _y = 105 - self.poll_var = tk.BooleanVar(self.tab6_monitor) - self.poll_ent = tk.Checkbutton(self.tab6_monitor, + self.poll_var = tk.BooleanVar(tab6_monitor) + self.poll_ent = tk.Checkbutton(tab6_monitor, variable=self.poll_var, text='Poll') self.poll_ent.place(x=_x, y=_y) @@ -430,13 +434,13 @@ def __init__(self, main_cl): # Port _x = 40 _y = 140 - tk.Label(self.tab6_monitor, text=f"{STR_TABLE['port'][self._lang]}:").place(x=_x, y=_y) - self.mon_port_var = tk.StringVar(self.tab6_monitor) + tk.Label(tab6_monitor, text=f"{STR_TABLE['port'][self._lang]}:").place(x=_x, y=_y) + self.mon_port_var = tk.StringVar(tab6_monitor) self.mon_port_var.set('0') _vals = ['0'] if PORT_HANDLER.get_all_ports().keys(): _vals = [str(x) for x in list(PORT_HANDLER.get_all_ports().keys())] - self.mon_port_ent = tk.ttk.Combobox(self.tab6_monitor, + self.mon_port_ent = tk.ttk.Combobox(tab6_monitor, width=4, textvariable=self.mon_port_var, values=_vals, @@ -446,11 +450,11 @@ def __init__(self, main_cl): # Calls _x = 40 _y = 175 - self.mon_call_var = tk.StringVar(self.tab6_monitor) + self.mon_call_var = tk.StringVar(tab6_monitor) _vals = [] # if self.main_win.ax25_port_handler.ax25_ports.keys(): # _vals = [str(x) for x in list(self.main_win.ax25_port_handler.ax25_ports.keys())] - self.mon_call_ent = tk.ttk.Combobox(self.tab6_monitor, + self.mon_call_ent = tk.ttk.Combobox(tab6_monitor, width=9, textvariable=self.mon_call_var, values=_vals, @@ -460,8 +464,8 @@ def __init__(self, main_cl): # Auto Scrolling _x = 10 _y = 210 - self.mon_scroll_var = tk.BooleanVar(self.tab6_monitor) - self.mon_scroll_ent = tk.Checkbutton(self.tab6_monitor, + self.mon_scroll_var = tk.BooleanVar(tab6_monitor) + self.mon_scroll_ent = tk.Checkbutton(tab6_monitor, variable=self.mon_scroll_var, text=STR_TABLE['scrolling'][self._lang]) self.mon_scroll_ent.place(x=_x, y=_y) @@ -469,9 +473,9 @@ def __init__(self, main_cl): # Monitor APRS Decoding Output _x = 10 _y = 235 - self.mon_aprs_var = tk.BooleanVar(self.tab6_monitor) + self.mon_aprs_var = tk.BooleanVar(tab6_monitor) self.mon_aprs_var.set(True) - self.mon_aprs_ent = tk.Checkbutton(self.tab6_monitor, + self.mon_aprs_ent = tk.Checkbutton(tab6_monitor, variable=self.mon_aprs_var, text='APRS-Decoding') self.mon_aprs_ent.place(x=_x, y=_y) @@ -479,15 +483,15 @@ def __init__(self, main_cl): # PID _x = 10 _y = 45 - self.mon_pid_var = tk.StringVar(self.tab6_monitor) - tk.Label(self.tab6_monitor, text='PID:').place(x=_x, y=_y) + self.mon_pid_var = tk.StringVar(tab6_monitor) + tk.Label(tab6_monitor, text='PID:').place(x=_x, y=_y) pid = PIDByte() pac_types = dict(pid.pac_types) _vals = [] for x in list(pac_types.keys()): pid.pac_types[int(x)]() _vals.append(f"{str(hex(int(x))).upper()}>{pid.flag}") - self.mon_pid_ent = tk.ttk.Combobox(self.tab6_monitor, + self.mon_pid_ent = tk.ttk.Combobox(tab6_monitor, width=20, values=_vals, textvariable=self.mon_pid_var) @@ -498,10 +502,10 @@ def __init__(self, main_cl): self.mon_port_on_vars = {} all_ports = PORT_HANDLER.get_all_ports() for port_id in all_ports: - self.mon_port_on_vars[port_id] = tk.BooleanVar(self.tab6_monitor) + self.mon_port_on_vars[port_id] = tk.BooleanVar(tab6_monitor) _x = 170 _y = 80 + (25 * port_id) - tk.Checkbutton(self.tab6_monitor, + tk.Checkbutton(tab6_monitor, text=f"Port {port_id}", variable=self.mon_port_on_vars[port_id], command=self._chk_mon_port_filter @@ -510,10 +514,10 @@ def __init__(self, main_cl): ################################ # TRACER # TREE - self.tab7_tracer.columnconfigure(0, minsize=150, weight=1) - self.tab7_tracer.columnconfigure(1, minsize=150, weight=1) - self.tab7_tracer.rowconfigure(0, minsize=100, weight=1) - self.tab7_tracer.rowconfigure(1, minsize=50, weight=1) + tab7_tracer.columnconfigure(0, minsize=150, weight=1) + tab7_tracer.columnconfigure(1, minsize=150, weight=1) + tab7_tracer.rowconfigure(0, minsize=100, weight=1) + tab7_tracer.rowconfigure(1, minsize=50, weight=1) tracer_columns = ( 'rx_time', @@ -523,7 +527,7 @@ def __init__(self, main_cl): 'path', ) - self._trace_tree = ttk.Treeview(self.tab7_tracer, columns=tracer_columns, show='headings') + self._trace_tree = ttk.Treeview(tab7_tracer, columns=tracer_columns, show='headings') self._trace_tree.grid(row=0, column=0, columnspan=2, sticky='nsew') self._trace_tree.heading('rx_time', text='Zeit') @@ -541,11 +545,11 @@ def __init__(self, main_cl): self._trace_tree_data_old = {} self._update_side_trace() - tk.Button(self.tab7_tracer, + tk.Button(tab7_tracer, text="SEND", command=self._tracer_send ).grid(row=1, column=0, padx=10) - # tk.Button(self.tab7_tracer, text="SEND").grid(row=1, column=1, padx=10) + # tk.Button(tab7_tracer, text="SEND").grid(row=1, column=1, padx=10) self._trace_tree.bind('<>', self._trace_entry_selected) ################## @@ -557,29 +561,36 @@ def __init__(self, main_cl): } self._chk_mon_port() - self._update_ch_echo() + # self._update_ch_echo() self._update_side_mh() self._update_side_trace() """ def reset_dx_alarm(self, event=None): self._main_win.reset_dx_alarm() - # self.tab2_mh.configure(bg=self.tab2_mh_def_bg_clr) + # tab2_mh.configure(bg=self.tab2_mh_def_bg_clr) """ - def _update_ch_echo(self): + def set_auto_tracer_state(self): + _bool_state = self._main_win.get_tracer() or not self._main_win.get_dx_alarm() + _state = { + True: 'disabled', + False: 'normal' + }.get(_bool_state, 'disabled') + self._autotracer_chk_btn.configure(state=_state) + def _update_ch_echo(self): # TODO AGAIN !! _tab = self.tab5_ch_links akt_ch_id = self._main_win.channel_index _var = tk.BooleanVar(_tab) - for ch_id in list(self.ch_echo_vars.keys()): + for ch_id in list(self._ch_echo_vars.keys()): if ch_id not in list(PORT_HANDLER.get_all_connections().keys()): - self.ch_echo_vars[ch_id][1].destroy() - del self.ch_echo_vars[ch_id] + self._ch_echo_vars[ch_id][1].destroy() + del self._ch_echo_vars[ch_id] for ch_id in list(PORT_HANDLER.get_all_connections().keys()): conn = PORT_HANDLER.get_all_connections()[ch_id] - if ch_id not in self.ch_echo_vars.keys(): + if ch_id not in self._ch_echo_vars.keys(): chk_bt_var = tk.IntVar() chk_bt = tk.Checkbutton(_tab, text=conn.to_call_str, @@ -591,30 +602,30 @@ def _update_ch_echo(self): chk_bt.place(x=10, y=10 + (28 * (ch_id - 1))) # _chk_bt.configure(state='disabled') tmp = chk_bt_var, chk_bt - self.ch_echo_vars[ch_id] = tmp + self._ch_echo_vars[ch_id] = tmp else: - self.ch_echo_vars[ch_id][1].configure(state='normal') - self.ch_echo_vars[ch_id][1].configure(text=conn.to_call_str) + self._ch_echo_vars[ch_id][1].configure(state='normal') + self._ch_echo_vars[ch_id][1].configure(text=conn.to_call_str) if ch_id != akt_ch_id: - self.ch_echo_vars[ch_id][1].configure(state='normal') + self._ch_echo_vars[ch_id][1].configure(state='normal') else: - self.ch_echo_vars[ch_id][1].configure(state='disabled') - if akt_ch_id in self.ch_echo_vars.keys(): - if self.ch_echo_vars[ch_id][0].get() and self.ch_echo_vars[akt_ch_id][0].get(): - self.ch_echo_vars[ch_id][1].configure(bg='green1') - self.ch_echo_vars[akt_ch_id][1].configure(bg='green1') + self._ch_echo_vars[ch_id][1].configure(state='disabled') + if akt_ch_id in self._ch_echo_vars.keys(): + if self._ch_echo_vars[ch_id][0].get() and self._ch_echo_vars[akt_ch_id][0].get(): + self._ch_echo_vars[ch_id][1].configure(bg='green1') + self._ch_echo_vars[akt_ch_id][1].configure(bg='green1') else: - self.ch_echo_vars[ch_id][1].configure(bg=self.chk_btn_default_clr) - self.ch_echo_vars[akt_ch_id][1].configure(bg=self.chk_btn_default_clr) + self._ch_echo_vars[ch_id][1].configure(bg=self._chk_btn_default_clr) + self._ch_echo_vars[akt_ch_id][1].configure(bg=self._chk_btn_default_clr) # self.sound_on.set(1) def _chk_ch_echo(self): # self.main_win.channel_index - for ch_id in list(self.ch_echo_vars.keys()): - _vars = self.ch_echo_vars[ch_id] + for ch_id in list(self._ch_echo_vars.keys()): + _vars = self._ch_echo_vars[ch_id] if ch_id != self._main_win.channel_index: - if _vars[0].get() and self.ch_echo_vars[self._main_win.channel_index][0].get(): + if _vars[0].get() and self._ch_echo_vars[self._main_win.channel_index][0].get(): PORT_HANDLER.get_all_connections()[ch_id].ch_echo.append( PORT_HANDLER.get_all_connections()[self._main_win.channel_index]) PORT_HANDLER.get_all_connections()[self._main_win.channel_index].ch_echo.append( @@ -650,10 +661,10 @@ def _chk_dx_alarm(self): """ def _chk_tracer(self): - self._main_win.set_tracer(self.tracer_on.get()) + self._main_win.set_tracer() - def set_tracer_chk(self, state: bool): - self.tracer_on.set(state) + def _chk_auto_tracer(self): + self._main_win.set_auto_tracer() def _chk_rnr(self): conn = self._main_win.get_conn() @@ -687,7 +698,8 @@ def _chk_t2auto(self): _conn.calc_irtt() def _chk_sprech_on(self): - if self.sprech_on.get(): + + if self._main_win.setting_sprech.get(): self.t2speech.configure(state='normal') else: self.t2speech.configure(state='disabled') @@ -731,8 +743,8 @@ def _entry_selected(self, event): record = item['values'] # show a message call = record[1] - vias = record[4] - port = record[2] + vias = record[5] + port = record[3] port = int(port.split(' ')[0]) if vias: call = f'{call} {vias}' @@ -758,6 +770,7 @@ def _format_tree_ent(self): self._tree_data.append(( f"{conv_time_DE_str(ent.last_seen).split(' ')[1]}", f'{ent.own_call}', + f'{ent.distance}', f'{ent.port_id} {ent.port}', f'{ent.pac_n}', ' '.join(route), @@ -907,7 +920,7 @@ def on_ch_stat_change(self): self.rx_count_var.set('RX: --- kb') self.t2speech_var.set(self._main_win.get_ch_param().t2speech) - self._update_ch_echo() + # self._update_ch_echo() def set_max_frame(self): conn = self._main_win.get_conn() @@ -933,32 +946,32 @@ def chk_autoscroll(self): class TxTframe: def __init__(self, main_win): - self.pw = ttk.PanedWindow(orient=tk.VERTICAL) + self._pw = ttk.PanedWindow(orient=tk.VERTICAL) self._main_class = main_win - self.text_size = main_win.text_size + self._text_size = main_win.text_size ################### # Input Win - self.status_frame = tk.Frame(self.pw, width=500, height=320, bd=0, borderwidth=0, bg=STAT_BAR_CLR) - self.status_frame.pack(side=tk.BOTTOM, expand=0) - - self.status_frame.columnconfigure(1, minsize=60, weight=2) # Name - self.status_frame.columnconfigure(2, minsize=40, weight=3) # Status - self.status_frame.columnconfigure(3, minsize=40, weight=4) # unACK - self.status_frame.columnconfigure(4, minsize=40, weight=4) # VS VR - self.status_frame.columnconfigure(5, minsize=20, weight=5) # N2 - self.status_frame.columnconfigure(6, minsize=20, weight=5) # T1 - self.status_frame.columnconfigure(7, minsize=20, weight=5) # T1 - self.status_frame.columnconfigure(8, minsize=20, weight=5) # T2 - self.status_frame.columnconfigure(9, minsize=20, weight=5) # T3 - self.status_frame.columnconfigure(10, minsize=50, weight=1) # RX Beep - self.status_frame.columnconfigure(11, minsize=20, weight=1) # TimeStamp - self.status_frame.rowconfigure(0, weight=1) # Stat - self.status_frame.rowconfigure(1, minsize=20, weight=0) # Out - - self.in_txt_win = scrolledtext.ScrolledText(self.status_frame, + self._status_frame = tk.Frame(self._pw, width=500, height=320, bd=0, borderwidth=0, bg=STAT_BAR_CLR) + self._status_frame.pack(side=tk.BOTTOM, expand=0) + + self._status_frame.columnconfigure(1, minsize=60, weight=2) # Name + self._status_frame.columnconfigure(2, minsize=40, weight=3) # Status + self._status_frame.columnconfigure(3, minsize=40, weight=4) # unACK + self._status_frame.columnconfigure(4, minsize=40, weight=4) # VS VR + self._status_frame.columnconfigure(5, minsize=20, weight=5) # N2 + self._status_frame.columnconfigure(6, minsize=20, weight=5) # T1 + self._status_frame.columnconfigure(7, minsize=20, weight=5) # T1 + self._status_frame.columnconfigure(8, minsize=20, weight=5) # T2 + self._status_frame.columnconfigure(9, minsize=20, weight=5) # T3 + self._status_frame.columnconfigure(10, minsize=50, weight=1) # RX Beep + self._status_frame.columnconfigure(11, minsize=20, weight=1) # TimeStamp + self._status_frame.rowconfigure(0, weight=1) # Stat + self._status_frame.rowconfigure(1, minsize=20, weight=0) # Out + + self.in_txt_win = scrolledtext.ScrolledText(self._status_frame, background=TXT_BACKGROUND_CLR, foreground=TXT_INP_CLR, - font=(FONT, self.text_size), + font=(FONT, self._text_size), insertbackground=TXT_INP_CURSOR_CLR, height=100, width=82, @@ -970,61 +983,83 @@ def __init__(self, main_win): self.in_txt_win.grid(row=0, column=0, columnspan=12, sticky="nsew") ############## # Status Frame - self.status_name = Label(self.status_frame, text="", font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), - foreground=STAT_BAR_TXT_CLR, - bg=STAT_BAR_CLR) - self.status_name.grid(row=1, column=1, sticky="nsew") - - self.status_status = Label(self.status_frame, text="", + self.status_name_var = tk.StringVar(self._status_frame) + self.status_status_var = tk.StringVar(self._status_frame) + self.status_unack_var = tk.StringVar(self._status_frame) + self.status_vs_var = tk.StringVar(self._status_frame) + self.status_n2_var = tk.StringVar(self._status_frame) + self.status_t1_var = tk.StringVar(self._status_frame) + self.status_t2_var = tk.StringVar(self._status_frame) + self.status_rtt_var = tk.StringVar(self._status_frame) + self.status_t3_var = tk.StringVar(self._status_frame) + Label(self._status_frame, + textvariable=self.status_name_var, + font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), + foreground=STAT_BAR_TXT_CLR, + bg=STAT_BAR_CLR + ).grid(row=1, column=1, sticky="nsew") + + self.status_status = Label(self._status_frame, + textvariable=self.status_status_var, font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), bg=STAT_BAR_CLR, - foreground=STAT_BAR_TXT_CLR) + foreground=STAT_BAR_TXT_CLR + ) self.status_status.grid(row=1, column=2, sticky="nsew") - self.status_unack = Label(self.status_frame, text="", + self.status_unack = Label(self._status_frame, + textvariable=self.status_unack_var, foreground=STAT_BAR_TXT_CLR, font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), - bg=STAT_BAR_CLR) + bg=STAT_BAR_CLR + ) self.status_unack.grid(row=1, column=3, sticky="nsew") - self.status_vs = Label(self.status_frame, text="", - font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), - bg=STAT_BAR_CLR, - foreground=STAT_BAR_TXT_CLR) - self.status_vs.grid(row=1, column=4, sticky="nsew") + Label(self._status_frame, + textvariable=self.status_vs_var, + font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), + bg=STAT_BAR_CLR, + foreground=STAT_BAR_TXT_CLR + ).grid(row=1, column=4, sticky="nsew") - self.status_n2 = Label(self.status_frame, text="", + self.status_n2 = Label(self._status_frame, + textvariable=self.status_n2_var, font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), bg=STAT_BAR_CLR, - foreground=STAT_BAR_TXT_CLR) + foreground=STAT_BAR_TXT_CLR + ) self.status_n2.grid(row=1, column=7, sticky="nsew") - self.status_t1 = Label(self.status_frame, text="", - font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), - bg=STAT_BAR_CLR, - foreground=STAT_BAR_TXT_CLR) - self.status_t1.grid(row=1, column=8, sticky="nsew") + Label(self._status_frame, + textvariable=self.status_t1_var, + font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), + bg=STAT_BAR_CLR, + foreground=STAT_BAR_TXT_CLR + ).grid(row=1, column=8, sticky="nsew") # PARM T2 - self.status_t2 = Label(self.status_frame, text="", - font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), - bg=STAT_BAR_CLR, - foreground=STAT_BAR_TXT_CLR) - self.status_t2.grid(row=1, column=5, sticky="nsew") + Label(self._status_frame, + textvariable=self.status_t2_var, + font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), + bg=STAT_BAR_CLR, + foreground=STAT_BAR_TXT_CLR + ).grid(row=1, column=5, sticky="nsew") # RTT - self.status_rtt = Label(self.status_frame, text="", - font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), - bg=STAT_BAR_CLR, - foreground=STAT_BAR_TXT_CLR) - self.status_rtt.grid(row=1, column=6, sticky="nsew") - - self.status_t3 = Label(self.status_frame, text="", - font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), - bg=STAT_BAR_CLR, - foreground=STAT_BAR_TXT_CLR) - self.status_t3.grid(row=1, column=9, sticky="nsew") + Label(self._status_frame, + textvariable=self.status_rtt_var, + font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), + bg=STAT_BAR_CLR, + foreground=STAT_BAR_TXT_CLR + ).grid(row=1, column=6, sticky="nsew") + + Label(self._status_frame, + textvariable=self.status_t3_var, + font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), + bg=STAT_BAR_CLR, + foreground=STAT_BAR_TXT_CLR + ).grid(row=1, column=9, sticky="nsew") # Checkbox RX-BEEP self.rx_beep_var = tk.IntVar() - self.rx_beep_box = Checkbutton(self.status_frame, + self.rx_beep_box = Checkbutton(self._status_frame, text="RX-BEEP", bg=STAT_BAR_CLR, font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), @@ -1033,12 +1068,12 @@ def __init__(self, main_win): onvalue=1, offvalue=0, foreground=STAT_BAR_TXT_CLR, variable=self.rx_beep_var, - command=self.chk_rx_beep + command=self._chk_rx_beep ) self.rx_beep_box.grid(row=1, column=10, sticky="nsew") # TODO Checkbox Time Stamp self.ts_box_var = tk.IntVar() - self.ts_box_box = Checkbutton(self.status_frame, + self.ts_box_box = Checkbutton(self._status_frame, text="T-S", font=(FONT_STAT_BAR, TEXT_SIZE_STATUS), bg=STAT_BAR_CLR, @@ -1051,28 +1086,28 @@ def __init__(self, main_win): state='disabled' ) self.ts_box_box.grid(row=1, column=11, sticky="nsew") - self.status_frame.pack(side=tk.BOTTOM) + self._status_frame.pack(side=tk.BOTTOM) #################### # Output - self.out_frame = tk.Frame(self.pw, width=500, height=320, bd=0, borderwidth=0, ) - self.out_frame.pack(side=tk.BOTTOM, expand=0) - self.out_frame.rowconfigure(1, minsize=22, weight=1) - self.out_frame.rowconfigure(0, weight=1) - self.out_frame.columnconfigure(0, minsize=3, weight=0) # Spacer - self.out_frame.columnconfigure(1, minsize=80, weight=2) # Name - self.out_frame.columnconfigure(2, minsize=60, weight=3) # QTH - self.out_frame.columnconfigure(3, minsize=20, weight=4) # LOC - self.out_frame.columnconfigure(4, minsize=20, weight=5) # Typ - self.out_frame.columnconfigure(5, minsize=80, weight=4) # Software - self.out_frame.columnconfigure(6, minsize=28, weight=4) # Status (PIPE/FT) - self.out_frame.columnconfigure(7, minsize=30, weight=4) # Conn Timer - self.out_frame.columnconfigure(8, minsize=30, weight=4) # Text Encoding - self.out_frame.columnconfigure(9, minsize=3, weight=0) # Spacer - self.out_txt_win = scrolledtext.ScrolledText(self.out_frame, + self._out_frame = tk.Frame(self._pw, width=500, height=320, bd=0, borderwidth=0, ) + self._out_frame.pack(side=tk.BOTTOM, expand=0) + self._out_frame.rowconfigure(1, minsize=22, weight=1) + self._out_frame.rowconfigure(0, weight=1) + self._out_frame.columnconfigure(0, minsize=3, weight=0) # Spacer + self._out_frame.columnconfigure(1, minsize=80, weight=2) # Name + self._out_frame.columnconfigure(2, minsize=60, weight=3) # QTH + self._out_frame.columnconfigure(3, minsize=20, weight=4) # LOC + self._out_frame.columnconfigure(4, minsize=20, weight=5) # Typ + self._out_frame.columnconfigure(5, minsize=80, weight=4) # Software + self._out_frame.columnconfigure(6, minsize=28, weight=4) # Status (PIPE/FT) + self._out_frame.columnconfigure(7, minsize=30, weight=4) # Conn Timer + self._out_frame.columnconfigure(8, minsize=30, weight=4) # Text Encoding + self._out_frame.columnconfigure(9, minsize=3, weight=0) # Spacer + self.out_txt_win = scrolledtext.ScrolledText(self._out_frame, background=TXT_BACKGROUND_CLR, foreground=TXT_OUT_CLR, - font=(FONT, self.text_size), + font=(FONT, self._text_size), height=100, width=82, bd=0, @@ -1082,16 +1117,16 @@ def __init__(self, main_win): self.out_txt_win.tag_config("input", foreground="yellow") self.out_txt_win.grid(row=0, column=0, columnspan=10, sticky="nsew") # Stat INFO (Name,QTH usw) - self.stat_info_name_var = tk.StringVar(self.out_frame) - self.stat_info_qth_var = tk.StringVar(self.out_frame) - self.stat_info_loc_var = tk.StringVar(self.out_frame) - self.stat_info_typ_var = tk.StringVar(self.out_frame) - self.stat_info_sw_var = tk.StringVar(self.out_frame) - self.stat_info_timer_var = tk.StringVar(self.out_frame) - self.stat_info_encoding_var = tk.StringVar(self.out_frame) - self.stat_info_status_var = tk.StringVar(self.out_frame) + self.stat_info_name_var = tk.StringVar(self._out_frame) + self.stat_info_qth_var = tk.StringVar(self._out_frame) + self.stat_info_loc_var = tk.StringVar(self._out_frame) + self.stat_info_typ_var = tk.StringVar(self._out_frame) + self.stat_info_sw_var = tk.StringVar(self._out_frame) + self.stat_info_timer_var = tk.StringVar(self._out_frame) + self.stat_info_encoding_var = tk.StringVar(self._out_frame) + self.stat_info_status_var = tk.StringVar(self._out_frame) size = 0 - name_label = tk.Label(self.out_frame, + name_label = tk.Label(self._out_frame, textvariable=self.stat_info_name_var, # bg=STAT_BAR_CLR, height=1, @@ -1102,7 +1137,7 @@ def __init__(self, main_win): ) name_label.grid(row=1, column=1, sticky="nsew") name_label.bind('', self._main_class.open_user_db_win) - qth_label = tk.Label(self.out_frame, + qth_label = tk.Label(self._out_frame, textvariable=self.stat_info_qth_var, bg=STAT_BAR_CLR, fg=STAT_BAR_TXT_CLR, @@ -1113,7 +1148,7 @@ def __init__(self, main_win): ) qth_label.bind('', self._main_class.open_user_db_win) qth_label.grid(row=1, column=2, sticky="nsew") - loc_label = tk.Label(self.out_frame, + loc_label = tk.Label(self._out_frame, textvariable=self.stat_info_loc_var, bg=STAT_BAR_CLR, fg=STAT_BAR_TXT_CLR, @@ -1127,10 +1162,10 @@ def __init__(self, main_win): opt = list(STATION_TYPS) stat_typ = tk.OptionMenu( - self.out_frame, + self._out_frame, self.stat_info_typ_var, *opt, - command=self.set_stat_typ + command=self._set_stat_typ ) stat_typ.configure( background="#0ed8c3", @@ -1143,7 +1178,7 @@ def __init__(self, main_win): ) stat_typ.grid(row=1, column=4, sticky="nsew") - tk.Label(self.out_frame, + tk.Label(self._out_frame, textvariable=self.stat_info_sw_var, width=20, bg="#ffd444", @@ -1154,7 +1189,7 @@ def __init__(self, main_win): font=(FONT_STAT_BAR, TEXT_SIZE_STATUS - size) ).grid(row=1, column=5, sticky="nsew") - self.status_label = tk.Label(self.out_frame, + self.status_label = tk.Label(self._out_frame, textvariable=self.stat_info_status_var, bg=STAT_BAR_CLR, fg="red3", @@ -1166,7 +1201,7 @@ def __init__(self, main_win): self.status_label.grid(row=1, column=6, sticky="nsew") self.status_label.bind('', self._main_class.do_priv) - tk.Label(self.out_frame, + tk.Label(self._out_frame, textvariable=self.stat_info_timer_var, width=10, height=1, @@ -1178,10 +1213,10 @@ def __init__(self, main_win): ).grid(row=1, column=7, sticky="nsew") opt = ENCODINGS txt_encoding_ent = tk.OptionMenu( - self.out_frame, + self._out_frame, self.stat_info_encoding_var, *opt, - command=self.change_txt_encoding + command=self._change_txt_encoding ) txt_encoding_ent.configure( background="steel blue", @@ -1194,10 +1229,10 @@ def __init__(self, main_win): txt_encoding_ent.grid(row=1, column=8, sticky="nsew", ) ############# # Monitor - self.mon_txt = scrolledtext.ScrolledText(self.pw, + self.mon_txt = scrolledtext.ScrolledText(self._pw, background=TXT_BACKGROUND_CLR, foreground=TXT_MON_CLR, - font=(FONT, self.text_size), + font=(FONT, self._text_size), height=100, width=82, bd=0, @@ -1209,24 +1244,26 @@ def __init__(self, main_win): # paned window - self.pw.add(self.status_frame, weight=1) + self._pw.add(self._status_frame, weight=1) # self.pw.paneconfig(self.status_frame, height=40) - self.pw.add(self.out_frame, weight=1) + self._pw.add(self._out_frame, weight=1) - self.pw.add(self.mon_txt, weight=1) + self._pw.add(self.mon_txt, weight=1) # place the panedwindow on the root window - self.pw.pack(fill=tk.BOTH, expand=True) - self.pw.grid(row=1, column=0, sticky="nsew") + self._pw.pack(fill=tk.BOTH, expand=True) + self._pw.grid(row=1, column=0, sticky="nsew") def update_status_win(self): station = self._main_class.get_conn(self._main_class.channel_index) if station: _from_call = station.ax25_out_frame.from_call.call_str + """ _via_calls = '' for _via in station.ax25_out_frame.via_calls: # via: Call _via_calls += _via.call_str + ' ' + """ _status = station.zustand_tab[station.zustand_exec.stat_index][1] # uid = station.ax25_out_frame.addr_uid _n2 = station.n2 @@ -1240,83 +1277,79 @@ def update_status_win(self): _t2_text = f"T2: {int(station.parm_T2 * 1000)}A" else: _t2_text = f"T2: {int(station.parm_T2 * 1000)}" - - if self.status_name.cget('text') != _from_call: - self.status_name.configure(text=_from_call) - if self.status_status.cget('text') != _status: + if self.status_name_var.get() != _from_call: + self.status_name_var.set(_from_call) + if self.status_status_var.get() != _status: _status_bg = STATUS_BG[_status] - self.status_status.configure(text=_status, bg=_status_bg) - - if len(station.tx_buf_unACK.keys()): - if self.status_unack.cget('bg') != 'yellow': - self.status_unack.configure(bg='yellow') - else: - if self.status_unack.cget('bg') != 'green': - self.status_unack.configure(bg='green') - if self.status_unack.cget('text') != _unAck: - self.status_unack.configure(text=_unAck) - - if self.status_vs.cget('text') != _vs_vr: - self.status_vs.configure(text=_vs_vr) - - if _n2 > 4: - if self.status_n2.cget('bg') != 'yellow': - self.status_n2.configure(bg='yellow') - elif _n2 > 10: - if self.status_n2.cget('bg') != 'orange': - self.status_n2.configure(bg='orange') - else: - if self.status_n2.cget('bg') != STAT_BAR_CLR: - self.status_n2.configure(bg=STAT_BAR_CLR) - - if self.status_n2.cget('text') != _n2_text: - self.status_n2.configure(text=_n2_text) - - if self.status_t1.cget('text') != _t1_text: - self.status_t1.configure(text=_t1_text) - - if self.status_t2.cget('text') != _t2_text: - self.status_t2.configure(text=_t2_text) - - if self.status_rtt.cget('text') != _rtt_text: - self.status_rtt.configure(text=_rtt_text) - - if self.status_t3.cget('text') != _t3_text: - self.status_t3.configure(text=_t3_text) + self.status_status_var.set(_status) + self.status_status.configure(bg=_status_bg) + if self.status_unack_var.get() != _unAck: + self.status_unack_var.set(_unAck) + if len(station.tx_buf_unACK.keys()): + if self.status_unack.cget('bg') != 'yellow': + self.status_unack.configure(bg='yellow') + else: + if self.status_unack.cget('bg') != 'green': + self.status_unack.configure(bg='green') + if self.status_vs_var.get() != _vs_vr: + self.status_vs_var.set(_vs_vr) + if self.status_n2_var.get() != _n2_text: + self.status_n2_var.set(_n2_text) + if _n2 > 4: + if self.status_n2.cget('bg') != 'yellow': + self.status_n2.configure(bg='yellow') + elif _n2 > 10: + if self.status_n2.cget('bg') != 'orange': + self.status_n2.configure(bg='orange') + else: + if self.status_n2.cget('bg') != STAT_BAR_CLR: + self.status_n2.configure(bg=STAT_BAR_CLR) + if self.status_t1_var.get() != _t1_text: + self.status_t1_var.set(_t1_text) + if self.status_t2_var.get() != _t2_text: + self.status_t2_var.set(_t2_text) + if self.status_rtt_var.get() != _rtt_text: + self.status_rtt_var.set(_rtt_text) + if self.status_t3_var.get() != _t3_text: + self.status_t3_var.set(_t3_text) else: if self.status_status.cget('text') or self.status_status.cget('bg') != STAT_BAR_CLR: - self.status_name.configure(text="", bg=STAT_BAR_CLR) - self.status_status.configure(text="", bg=STAT_BAR_CLR) - self.status_unack.configure(text="", bg=STAT_BAR_CLR) - self.status_vs.configure(text="", bg=STAT_BAR_CLR) - self.status_n2.configure(text="", bg=STAT_BAR_CLR) - self.status_t1.configure(text="", bg=STAT_BAR_CLR) - self.status_t2.configure(text="", bg=STAT_BAR_CLR) - self.status_t3.configure(text="", bg=STAT_BAR_CLR) - self.status_rtt.configure(text="", bg=STAT_BAR_CLR) + # self.status_name.configure(text="", bg=STAT_BAR_CLR) + self.status_name_var.set('') + self.status_status.configure(bg=STAT_BAR_CLR) + self.status_status_var.set('') + self.status_unack.configure(bg=STAT_BAR_CLR) + self.status_unack_var.set('') + self.status_vs_var.set('') + self.status_n2.configure(bg=STAT_BAR_CLR) + self.status_n2_var.set('') + self.status_t1_var.set('') + self.status_t2_var.set('') + self.status_t3_var.set('') + self.status_rtt_var.set('') def switch_mon_mode(self): # TODO Save Stretched Positions if self._main_class.mon_mode: try: - self.pw.remove(self.status_frame) - self.pw.remove(self.mon_txt) + self._pw.remove(self._status_frame) + self._pw.remove(self.mon_txt) except tk.TclError: pass - self.pw.add(self.status_frame, weight=1) - self.pw.add(self.out_frame, weight=1) - self.pw.add(self.mon_txt, weight=1) + self._pw.add(self._status_frame, weight=1) + self._pw.add(self._out_frame, weight=1) + self._pw.add(self.mon_txt, weight=1) # self.pw.configure(height=837) else: # _pw_height = self.pw.winfo_height() # _mon_txt_height = self.mon_txt.winfo_height() # _out_txt_height = self.out_txt_win.winfo_height() - self.pw.remove(self.out_frame) + self._pw.remove(self._out_frame) # self.pw.configure(height=837) - def chk_rx_beep(self): + def _chk_rx_beep(self): _rx_beep_check = self.rx_beep_var.get() if _rx_beep_check: if self.rx_beep_box.cget('bg') != 'green': @@ -1336,7 +1369,7 @@ def chk_timestamp(self): self.ts_box_box.configure(bg=STAT_BAR_CLR, activebackground=STAT_BAR_CLR) self._main_class.get_ch_param().timestamp_opt = _ts_check - def set_stat_typ(self, event=None): + def _set_stat_typ(self, event=None): conn = self._main_class.get_conn() if conn: db_ent = conn.user_db_ent @@ -1345,7 +1378,7 @@ def set_stat_typ(self, event=None): else: self.stat_info_typ_var.set('-----') - def change_txt_encoding(self, event=None, enc=''): + def _change_txt_encoding(self, event=None, enc=''): conn = self._main_class.get_conn() if conn: db_ent = conn.user_db_ent @@ -1360,96 +1393,97 @@ def change_txt_encoding(self, event=None, enc=''): class ChBtnFrm: def __init__(self, main_win): self._main_class = main_win - self.ch_btn_blink_timer = time.time() - self.ch_btn_frame = tk.Frame(self._main_class.main_win, width=500, height=10) + self._ch_btn_blink_timer = time.time() + _ch_btn_frame = tk.Frame(self._main_class.main_win, width=500, height=10) + _ch_btn_frame.grid(row=2, column=0, columnspan=1, sticky="nsew") _btn_font = ("fixedsys", 8,) - self.ch_btn_frame.columnconfigure(1, minsize=50, weight=1) - self.ch_btn_frame.columnconfigure(2, minsize=50, weight=1) - self.ch_btn_frame.columnconfigure(3, minsize=50, weight=1) - self.ch_btn_frame.columnconfigure(4, minsize=50, weight=1) - self.ch_btn_frame.columnconfigure(5, minsize=50, weight=1) - self.ch_btn_frame.columnconfigure(6, minsize=50, weight=1) - self.ch_btn_frame.columnconfigure(7, minsize=50, weight=1) - self.ch_btn_frame.columnconfigure(8, minsize=50, weight=1) - self.ch_btn_frame.columnconfigure(9, minsize=50, weight=1) - self.ch_btn_frame.columnconfigure(10, minsize=50, weight=1) + _ch_btn_frame.columnconfigure(1, minsize=50, weight=1) + _ch_btn_frame.columnconfigure(2, minsize=50, weight=1) + _ch_btn_frame.columnconfigure(3, minsize=50, weight=1) + _ch_btn_frame.columnconfigure(4, minsize=50, weight=1) + _ch_btn_frame.columnconfigure(5, minsize=50, weight=1) + _ch_btn_frame.columnconfigure(6, minsize=50, weight=1) + _ch_btn_frame.columnconfigure(7, minsize=50, weight=1) + _ch_btn_frame.columnconfigure(8, minsize=50, weight=1) + _ch_btn_frame.columnconfigure(9, minsize=50, weight=1) + _ch_btn_frame.columnconfigure(10, minsize=50, weight=1) # self.ch_btn_frame.grid(row=1, column=1, sticky="nsew") - self.ch_1_var = tk.StringVar(self.ch_btn_frame) - self.ch_2_var = tk.StringVar(self.ch_btn_frame) - self.ch_3_var = tk.StringVar(self.ch_btn_frame) - self.ch_4_var = tk.StringVar(self.ch_btn_frame) - self.ch_5_var = tk.StringVar(self.ch_btn_frame) - self.ch_6_var = tk.StringVar(self.ch_btn_frame) - self.ch_7_var = tk.StringVar(self.ch_btn_frame) - self.ch_8_var = tk.StringVar(self.ch_btn_frame) - self.ch_9_var = tk.StringVar(self.ch_btn_frame) - self.ch_10_var = tk.StringVar(self.ch_btn_frame) - self.ch_1_var.set('1') - self.ch_2_var.set('2') - self.ch_3_var.set('3') - self.ch_4_var.set('4') - self.ch_5_var.set('5') - self.ch_6_var.set('6') - self.ch_7_var.set('7') - self.ch_8_var.set('8') - self.ch_9_var.set('9') - self.ch_10_var.set('10') - self.ch_button1 = tk.Button(self.ch_btn_frame, font=_btn_font, textvariable=self.ch_1_var, bg="red", - command=lambda: self._main_class.switch_channel(1)) - self.ch_button2 = tk.Button(self.ch_btn_frame, font=_btn_font, textvariable=self.ch_2_var, bg="red", - command=lambda: self._main_class.switch_channel(2)) - self.ch_button3 = tk.Button(self.ch_btn_frame, font=_btn_font, textvariable=self.ch_3_var, bg="red", - command=lambda: self._main_class.switch_channel(3)) - self.ch_button4 = tk.Button(self.ch_btn_frame, font=_btn_font, textvariable=self.ch_4_var, bg="red", - command=lambda: self._main_class.switch_channel(4)) - self.ch_button5 = tk.Button(self.ch_btn_frame, font=_btn_font, textvariable=self.ch_5_var, bg="red", - command=lambda: self._main_class.switch_channel(5)) - self.ch_button6 = tk.Button(self.ch_btn_frame, font=_btn_font, textvariable=self.ch_6_var, bg="red", - command=lambda: self._main_class.switch_channel(6)) - self.ch_button7 = tk.Button(self.ch_btn_frame, font=_btn_font, textvariable=self.ch_7_var, bg="red", - command=lambda: self._main_class.switch_channel(7)) - self.ch_button8 = tk.Button(self.ch_btn_frame, font=_btn_font, textvariable=self.ch_8_var, bg="red", - command=lambda: self._main_class.switch_channel(8)) - self.ch_button9 = tk.Button(self.ch_btn_frame, font=_btn_font, textvariable=self.ch_9_var, bg="red", - command=lambda: self._main_class.switch_channel(9)) - self.ch_button10 = tk.Button(self.ch_btn_frame, font=_btn_font, textvariable=self.ch_10_var, bg="red", - command=lambda: self._main_class.switch_channel(10)) - self.ch_button1.grid(row=1, column=1, sticky="nsew") - self.ch_button2.grid(row=1, column=2, sticky="nsew") - self.ch_button3.grid(row=1, column=3, sticky="nsew") - self.ch_button4.grid(row=1, column=4, sticky="nsew") - self.ch_button5.grid(row=1, column=5, sticky="nsew") - self.ch_button6.grid(row=1, column=6, sticky="nsew") - self.ch_button7.grid(row=1, column=7, sticky="nsew") - self.ch_button8.grid(row=1, column=8, sticky="nsew") - self.ch_button9.grid(row=1, column=9, sticky="nsew") - self.ch_button10.grid(row=1, column=10, sticky="nsew") + ch_1_var = tk.StringVar(_ch_btn_frame) + ch_2_var = tk.StringVar(_ch_btn_frame) + ch_3_var = tk.StringVar(_ch_btn_frame) + ch_4_var = tk.StringVar(_ch_btn_frame) + ch_5_var = tk.StringVar(_ch_btn_frame) + ch_6_var = tk.StringVar(_ch_btn_frame) + ch_7_var = tk.StringVar(_ch_btn_frame) + ch_8_var = tk.StringVar(_ch_btn_frame) + ch_9_var = tk.StringVar(_ch_btn_frame) + ch_10_var = tk.StringVar(_ch_btn_frame) + ch_1_var.set('1') + ch_2_var.set('2') + ch_3_var.set('3') + ch_4_var.set('4') + ch_5_var.set('5') + ch_6_var.set('6') + ch_7_var.set('7') + ch_8_var.set('8') + ch_9_var.set('9') + ch_10_var.set('10') + ch_button1 = tk.Button(_ch_btn_frame, font=_btn_font, textvariable=ch_1_var, bg="red", + command=lambda: self._main_class.switch_channel(1)) + ch_button2 = tk.Button(_ch_btn_frame, font=_btn_font, textvariable=ch_2_var, bg="red", + command=lambda: self._main_class.switch_channel(2)) + ch_button3 = tk.Button(_ch_btn_frame, font=_btn_font, textvariable=ch_3_var, bg="red", + command=lambda: self._main_class.switch_channel(3)) + ch_button4 = tk.Button(_ch_btn_frame, font=_btn_font, textvariable=ch_4_var, bg="red", + command=lambda: self._main_class.switch_channel(4)) + ch_button5 = tk.Button(_ch_btn_frame, font=_btn_font, textvariable=ch_5_var, bg="red", + command=lambda: self._main_class.switch_channel(5)) + ch_button6 = tk.Button(_ch_btn_frame, font=_btn_font, textvariable=ch_6_var, bg="red", + command=lambda: self._main_class.switch_channel(6)) + ch_button7 = tk.Button(_ch_btn_frame, font=_btn_font, textvariable=ch_7_var, bg="red", + command=lambda: self._main_class.switch_channel(7)) + ch_button8 = tk.Button(_ch_btn_frame, font=_btn_font, textvariable=ch_8_var, bg="red", + command=lambda: self._main_class.switch_channel(8)) + ch_button9 = tk.Button(_ch_btn_frame, font=_btn_font, textvariable=ch_9_var, bg="red", + command=lambda: self._main_class.switch_channel(9)) + ch_button10 = tk.Button(_ch_btn_frame, font=_btn_font, textvariable=ch_10_var, bg="red", + command=lambda: self._main_class.switch_channel(10)) + ch_button1.grid(row=1, column=1, sticky="nsew") + ch_button2.grid(row=1, column=2, sticky="nsew") + ch_button3.grid(row=1, column=3, sticky="nsew") + ch_button4.grid(row=1, column=4, sticky="nsew") + ch_button5.grid(row=1, column=5, sticky="nsew") + ch_button6.grid(row=1, column=6, sticky="nsew") + ch_button7.grid(row=1, column=7, sticky="nsew") + ch_button8.grid(row=1, column=8, sticky="nsew") + ch_button9.grid(row=1, column=9, sticky="nsew") + ch_button10.grid(row=1, column=10, sticky="nsew") self._con_btn_dict = { - 1: self.ch_button1, - 2: self.ch_button2, - 3: self.ch_button3, - 4: self.ch_button4, - 5: self.ch_button5, - 6: self.ch_button6, - 7: self.ch_button7, - 8: self.ch_button8, - 9: self.ch_button9, - 10: self.ch_button10, + 1: ch_button1, + 2: ch_button2, + 3: ch_button3, + 4: ch_button4, + 5: ch_button5, + 6: ch_button6, + 7: ch_button7, + 8: ch_button8, + 9: ch_button9, + 10: ch_button10, } self._ch_btn_textvar = { - 1: self.ch_1_var, - 2: self.ch_2_var, - 3: self.ch_3_var, - 4: self.ch_4_var, - 5: self.ch_5_var, - 6: self.ch_6_var, - 7: self.ch_7_var, - 8: self.ch_8_var, - 9: self.ch_9_var, - 10: self.ch_10_var, + 1: ch_1_var, + 2: ch_2_var, + 3: ch_3_var, + 4: ch_4_var, + 5: ch_5_var, + 6: ch_6_var, + 7: ch_7_var, + 8: ch_8_var, + 9: ch_9_var, + 10: ch_10_var, } - def ch_btn_status_update(self): + def _ch_btn_status_update(self): # self.main_class.on_channel_status_change() _ch_alarm = False # if PORT_HANDLER.get_all_connections().keys(): @@ -1488,8 +1522,8 @@ def ch_btn_status_update(self): _ch_alarm = False else: _ch_alarm = True - if self.ch_btn_blink_timer < time.time(): - self.ch_btn_alarm(self._con_btn_dict[i]) + if self._ch_btn_blink_timer < time.time(): + self._ch_btn_alarm(self._con_btn_dict[i]) else: if _is_link: _ch_alarm = False @@ -1518,35 +1552,172 @@ def ch_btn_status_update(self): if self._con_btn_dict[i].cget('bg') != 'yellow': self._con_btn_dict[i].configure(bg='yellow') - if self.ch_btn_blink_timer < time.time(): - self.ch_btn_blink_timer = time.time() + self._main_class.parm_btn_blink_time + if self._ch_btn_blink_timer < time.time(): + self._ch_btn_blink_timer = time.time() + self._main_class.parm_btn_blink_time self._main_class.ch_alarm = _ch_alarm - def ch_btn_alarm(self, btn: tk.Button): - # TODO find another solution - if self.ch_btn_blink_timer < time.time(): + def _ch_btn_alarm(self, btn: tk.Button): + if self._ch_btn_blink_timer < time.time(): _clr = generate_random_hex_color() if btn.cget('bg') != _clr: btn.configure(bg=_clr) - # self.ch_btn_blink_timer = time.time() + self.main_class.parm_btn_blink_time class TkMainWin: def __init__(self): + ###################################### + # GUI Stuff + self.main_win = tk.Tk() + self.main_win.title(f"P.ython o.ther P.acket T.erminal {VER}") + self.main_win.geometry("1400x850") + try: + self.main_win.iconbitmap("favicon.ico") + except TclError: + pass + self.main_win.protocol("WM_DELETE_WINDOW", self._destroy_win) + ########################## + self.style = ttk.Style() + # self.style.theme_use('classic') + # self.style.theme_use('clam') + ###################################### + # Init Vars + self._init_vars() + ############### + self.text_size = 14 + ############################ + # Windows + self.new_conn_win = None + self.settings_win = None + self.mh_window = None + self.wx_window = None + self.port_stat_win = None + self.be_tracer_win = None + self.locator_calc_window = None + self.aprs_mon_win = None + self.aprs_pn_msg_win = None + self.userdb_win = None + self.userDB_tree_win = None + ###################################### + # .... + self.main_win.columnconfigure(0, minsize=500, weight=1) + self.main_win.columnconfigure(1, minsize=2, weight=5) + self.main_win.rowconfigure(0, minsize=3, weight=1) # Boarder + self.main_win.rowconfigure(1, minsize=220, weight=2) + self.main_win.rowconfigure(2, minsize=28, weight=1) # CH BTN + ############################ + # Input Output TXT Frames and Status Bar + self._txt_win = TxTframe(self) + self._out_txt = self._txt_win.out_txt_win + self._inp_txt = self._txt_win.in_txt_win + self._mon_txt = self._txt_win.mon_txt + ####################### + # Window Text Buffers + self._win_buf: {int: ChVars} = {} + for i in range(11): + self._win_buf[i] = ChVars() + self._win_buf[i].input_win_index = str(self._inp_txt.index(tk.INSERT)) + ######################### + # Channel Buttons + self._ch_btn = ChBtnFrm(self) + # self._ch_btn._ch_btn_frame.grid(row=2, column=0, columnspan=1, sticky="nsew") + ######################### + # Tabbed Frame right + self._side_btn_frame_top = tk.Frame(self.main_win, width=200, height=540) + self._side_btn_frame_top.grid(row=1, rowspan=2, column=1, sticky="nsew") + self._side_btn_frame_top.rowconfigure(0, minsize=40, weight=0) # CONN BTN + self._side_btn_frame_top.rowconfigure(1, minsize=40, weight=0) # BTN row 2 + self._side_btn_frame_top.rowconfigure(2, minsize=1, weight=0) # Dummy + self._side_btn_frame_top.rowconfigure(3, minsize=300, weight=10) # Reiter Frame + self._side_btn_frame_top.rowconfigure(4, minsize=100, weight=1) # Reiter Frame + + self._side_btn_frame_top.columnconfigure(0, minsize=10, weight=0) + self._side_btn_frame_top.columnconfigure(1, minsize=100, weight=2) + self._side_btn_frame_top.columnconfigure(2, minsize=100, weight=2) + self._side_btn_frame_top.columnconfigure(3, minsize=10, weight=1) + self._side_btn_frame_top.columnconfigure(4, minsize=10, weight=5) + self._side_btn_frame_top.columnconfigure(6, minsize=10, weight=1) + ############## + # GUI Buttons + self._init_btn() + ############## + # Side Frame + self.tabbed_sideFrame = SideTabbedFrame(self) + ############################ + # Canvas Plot + self._init_bw_plot() + ########################### + # set KEY BINDS + self._set_binds() + self._set_keybinds() + # Menubar + self._init_menubar() + # set Ch Btn Color + self.ch_status_update() + # ..... + self._monitor_start_msg() + ############################# + # set GUI Var to Port Handler + PORT_HANDLER.set_gui(self) + ####################### + # LOOP LOOP LOOP + self.main_win.after(self._loop_delay, self._tasker) + self.main_win.mainloop() + + def __del__(self): + pass + + def _destroy_win(self): + self.msg_to_monitor("PoPT wird beendet.") + self._is_closing = True + logging.info('Closing GUI: Closing Ports.') + PORT_HANDLER.close_all_ports() + + logging.info('Closing GUI.') + self._close_port_stat_win() + if self.settings_win is not None: + self.settings_win.destroy() + if self.mh_window is not None: + self.mh_window.destroy() + if self.wx_window is not None: + self.wx_window.destroy() + if self.userdb_win is not None: + self.userdb_win.destroy() + if self.userDB_tree_win is not None: + self.userDB_tree_win.destroy() + if self.aprs_mon_win is not None: + self.aprs_mon_win.destroy() + if self.aprs_pn_msg_win is not None: + self.aprs_pn_msg_win.destroy() + if self.be_tracer_win is not None: + self.be_tracer_win.destroy() + self.main_win.update_idletasks() + self._loop_delay = 800 + + #################### + # Init Stuff + def _init_vars(self): self.language = LANGUAGE ############################### self._root_dir = get_root_dir() self._root_dir = self._root_dir.replace('/', '//') ##################### - ##################### # GUI VARS - self._sound_th = None + self.connect_history = {} + # GLb Setting Vars + self.setting_sound = tk.BooleanVar(self.main_win) + self.setting_sprech = tk.BooleanVar(self.main_win) + self.setting_bake = tk.BooleanVar(self.main_win) + self.setting_rx_echo = tk.BooleanVar(self.main_win) + self.setting_tracer = tk.BooleanVar(self.main_win) + self.setting_auto_tracer = tk.BooleanVar(self.main_win) + self.setting_dx_alarm = tk.BooleanVar(self.main_win) + # Controlling self.ch_alarm = False self.ch_alarm_sound_one_time = False self.channel_index = 1 self.mon_mode = 0 self._mon_buff = [] - self.connect_history = {} + self._sound_th = None self._is_closing = False #################### # GUI PARAM @@ -1554,7 +1725,8 @@ def __init__(self): self._parm_rx_beep_cooldown = 2 # s # Tasker Timings self._loop_delay = 250 # ms - self._parm_non_prio_task_timer = 0.5 # s + self._parm_non_prio_task_timer = 0.25 # s + self._prio_task_flip = True self._parm_non_non_prio_task_timer = 1 # s self._parm_non_non_non_prio_task_timer = 5 # s self._parm_test_task_timer = 60 # 5 # s @@ -1562,34 +1734,57 @@ def __init__(self): self._non_non_prio_task_timer = time.time() self._non_non_non_prio_task_timer = time.time() self._test_task_timer = time.time() - ############### - self.text_size = 14 - ###################################### - # GUI Stuff - self.main_win = tk.Tk() - self.main_win.title("P.ython o.ther P.acket T.erminal {}".format(VER)) - self.main_win.geometry("1400x850") - try: - self.main_win.iconbitmap("favicon.ico") - except TclError: - pass - self.main_win.protocol("WM_DELETE_WINDOW", self._destroy_win) - ########################## - self.style = ttk.Style() - # self.style.theme_use('classic') - # self.style.theme_use('clam') - self.main_win.columnconfigure(0, minsize=500, weight=1) - self.main_win.columnconfigure(1, minsize=2, weight=5) - self.main_win.rowconfigure(0, minsize=3, weight=1) # Boarder - # self.main_win.rowconfigure(1, minsize=0, weight=1) # BTN SIDE - self.main_win.rowconfigure(1, minsize=220, weight=2) - self.main_win.rowconfigure(2, minsize=28, weight=1) # CH BTN - # self.main_win.rowconfigure(3, minsize=2, weight=0) # Boarder - ############################ - ############################ - ############################ - ############## - # Menüleiste + ############################## + # BW-Plot + self._bw_plot_x_scale = [] + for _i in list(range(60)): + self._bw_plot_x_scale.append(_i / 6) + self._bw_plot_lines = {} + ######################################## + # Set Default Settings TODO Save to cfg + self.setting_sound.set(False) + self.setting_bake.set(True) + self.setting_rx_echo.set(False) + self.setting_tracer.set(False) + self.setting_auto_tracer.set(False) + self.setting_dx_alarm.set(True) + if is_linux(): + self.setting_sprech.set(True) + else: + self.setting_sprech.set(False) + # MH + """ + MH_LIST.parm_distance_alarm = 50 + MH_LIST.parm_lastseen_alarm = 1 + """ + MH_LIST.parm_new_call_alarm = True + # Set Port 0 for DX Alarm as default # TODO remove + if PORT_HANDLER.get_port_by_index(0): + MH_LIST.parm_alarm_ports = [0] + else: + MH_LIST.parm_alarm_ports = [] + + def _init_bw_plot(self): + self._bw_fig = Figure(figsize=(8, 5), dpi=80) + self._ax = self._bw_fig.add_subplot(111) + self._bw_fig.subplots_adjust(left=0.1, right=0.95, top=0.99, bottom=0.1) + self._ax.axis([0, 10, 0, 100]) # TODO As Option + self._bw_fig.set_facecolor('xkcd:light grey') + self._ax.set_facecolor('#000000') + self._ax.xaxis.label.set_color('black') + self._ax.yaxis.label.set_color('black') + self._ax.tick_params(axis='x', colors='black') + self._ax.tick_params(axis='y', colors='black') + self._ax.set_xlabel(STR_TABLE['minutes'][self.language]) + self._ax.set_ylabel(STR_TABLE['occup'][self.language]) + self._canvas = FigureCanvasTkAgg(self._bw_fig, master=self._side_btn_frame_top) + self._canvas.flush_events() + self._canvas.draw() + self._canvas.get_tk_widget().grid(row=4, column=0, columnspan=7, sticky="nsew") + # self._canvas.get_tk_widget().config(cursor="none") + self._bw_fig.canvas.flush_events() + + def _init_menubar(self): _menubar = Menu(self.main_win, tearoff=False) self.main_win.config(menu=_menubar) # Menü 1 "Verbindungen" @@ -1709,45 +1904,7 @@ def __init__(self): underline=0) _menubar.add_cascade(label=STR_TABLE['help'][self.language], menu=_MenuHelp, underline=0) - # Menü 4 "Debug" - # menubar.add_command(label="Debug") - ############################ - ############################ - # Input Output TXT Frames and Status Bar - self._txt_win = TxTframe(self) - self._out_txt = self._txt_win.out_txt_win - self._inp_txt = self._txt_win.in_txt_win - self._mon_txt = self._txt_win.mon_txt - ####################### - # Window Text Buffers - self._win_buf: {int: ChVars} = {} - for i in range(11): - self._win_buf[i] = ChVars() - self._win_buf[i].input_win_index = str(self._inp_txt.index(tk.INSERT)) - # Channel Buttons - self._ch_btn = ChBtnFrm(self) - self._ch_btn.ch_btn_frame.grid(row=2, column=0, columnspan=1, sticky="nsew") - ######################### - # BTN and Tabbed Frame right side - self._side_btn_frame_top = tk.Frame(self.main_win, width=200, height=540) - # self.side_btn_frame_top = tk.Frame(self.pw, width=200, height=540) - # self.pw.add(self.txt_win.pw) - self._side_btn_frame_top.grid(row=1, rowspan=2, column=1, sticky="nsew") - self._side_btn_frame_top.rowconfigure(0, minsize=40, weight=0) # CONN BTN - self._side_btn_frame_top.rowconfigure(1, minsize=40, weight=0) # BTN row 2 - self._side_btn_frame_top.rowconfigure(2, minsize=1, weight=0) # Dummy - # self._side_btn_frame_top.rowconfigure(3, minsize=1, weight=2) # Dummy - self._side_btn_frame_top.rowconfigure(3, minsize=300, weight=10) # Reiter Frame - self._side_btn_frame_top.rowconfigure(4, minsize=100, weight=1) # Reiter Frame - # self._side_btn_frame_top.rowconfigure(5, minsize=15, weight=1) # Reiter Frame - - self._side_btn_frame_top.columnconfigure(0, minsize=10, weight=0) - self._side_btn_frame_top.columnconfigure(1, minsize=100, weight=2) - self._side_btn_frame_top.columnconfigure(2, minsize=100, weight=2) - self._side_btn_frame_top.columnconfigure(3, minsize=10, weight=1) - self._side_btn_frame_top.columnconfigure(4, minsize=10, weight=5) - self._side_btn_frame_top.columnconfigure(6, minsize=10, weight=1) - + def _init_btn(self): _btn_upper_frame = tk.Frame(self._side_btn_frame_top) _btn_lower_frame = tk.Frame(self._side_btn_frame_top) _btn_upper_frame.place(x=5, y=5) @@ -1787,107 +1944,65 @@ def __init__(self): text="Kaffèmaschine", bg="HotPink2", width=12, command=self._kaffee).place(x=215, y=10) """ - self.tabbed_sideFrame = SideTabbedFrame(self) - # self.pw.add(self.tabbed_sideFrame.tab_side_frame) - self.setting_sound = self.tabbed_sideFrame.sound_on - self.setting_sprech = self.tabbed_sideFrame.sprech_on - self.setting_bake = self.tabbed_sideFrame.bake_on - self.setting_rx_echo = self.tabbed_sideFrame.rx_echo_on - # self.setting_dx_alarm = self.tabbed_sideFrame.dx_alarm_on - ############################ - # Canvas Plot ( TEST ) - # plt.ion() - self._bw_fig = Figure(figsize=(8, 5), dpi=80) - # plt.style.use('dark_background') - self._ax = self._bw_fig.add_subplot(111) - self._bw_fig.subplots_adjust(left=0.1, right=0.95, top=0.99, bottom=0.1) - self._ax.axis([0, 10, 0, 100]) - self._bw_fig.set_facecolor('xkcd:light grey') - self._ax.set_facecolor('#000000') - # self.bw_fig.xlim(0, 10) # TODO As Option - self._ax.xaxis.label.set_color('black') - self._ax.yaxis.label.set_color('black') - self._ax.tick_params(axis='x', colors='black') - self._ax.tick_params(axis='y', colors='black') - self._ax.set_xlabel(STR_TABLE['minutes'][self.language]) - self._ax.set_ylabel(STR_TABLE['occup'][self.language]) - self._bw_plot_lines = {} - # plt.xlim(0, 10) # TODO As Option - self._canvas = FigureCanvasTkAgg(self._bw_fig, master=self._side_btn_frame_top) # A tk.DrawingArea. - self._canvas.flush_events() - self._canvas.draw() - self._canvas.get_tk_widget().grid(row=4, column=0, columnspan=7, sticky="nsew") - # self._canvas.get_tk_widget().pack(fill=tk.BOTH) - # self._canvas.get_tk_widget().config(cursor="none") - self._bw_fig.canvas.flush_events() - self._bw_plot_x_scale = [] - for _i in list(range(60)): - self._bw_plot_x_scale.append(_i / 6) - self._bw_plot_lines = {} + ####################################### + # KEYBIND Stuff + def _set_binds(self): + self._inp_txt.bind("", self._on_click_inp_txt) - ############################ - # Windows - self.new_conn_win = None - self.settings_win = None - self.mh_window = None - self.wx_window = None - self.port_stat_win = None - self.be_tracer_win = None - self.locator_calc_window = None - self.aprs_mon_win = None - self.aprs_pn_msg_win = None - self.userdb_win = None - self.userDB_tree_win = None - ########################### - # Init - # set Ch Btn Color - self.ch_status_update() - # set KEY BINDS - self._set_binds() - self._set_keybinds() - # ..... - self._monitor_start_msg() - ####################### - # set GUI Vav - PORT_HANDLER.set_gui(self) - ####################### - # LOOP - self.main_win.after(self._loop_delay, self._tasker) - self.main_win.mainloop() + def _set_keybinds(self): + self.main_win.unbind("") + self.main_win.unbind("") + # self.main_win.bind("",lambda event: self.callback(event)) + self.main_win.bind('', lambda event: self.switch_channel(1)) + self.main_win.bind('', lambda event: self.switch_channel(2)) + self.main_win.bind('', lambda event: self.switch_channel(3)) + self.main_win.bind('', lambda event: self.switch_channel(4)) + self.main_win.bind('', lambda event: self.switch_channel(5)) + self.main_win.bind('', lambda event: self.switch_channel(6)) + self.main_win.bind('', lambda event: self.switch_channel(7)) + self.main_win.bind('', lambda event: self.switch_channel(8)) + self.main_win.bind('', lambda event: self.switch_channel(9)) + self.main_win.bind('', lambda event: self.switch_channel(10)) + self.main_win.bind('', lambda event: self.switch_channel(0)) + self.main_win.bind('', self._snd_text) + self.main_win.bind('', self._release_return) + self.main_win.bind('', self._shift_return) + self.main_win.bind('', self._arrow_keys) + self.main_win.bind('', self._arrow_keys) + self.main_win.bind('', self._arrow_keys) + self.main_win.bind('', self._arrow_keys) + # self.main_win.bind('', self.snd_text) + self.main_win.bind('', lambda event: self.open_new_conn_win()) + self.main_win.bind('', lambda event: self.open_new_conn_win()) + self.main_win.bind('', lambda event: self._disco_conn()) + self.main_win.bind('', lambda event: self._copy_select()) + self.main_win.bind('', lambda event: self._cut_select()) + # self.main_win.bind('', lambda event: self.clipboard_past()) + self.main_win.bind('', lambda event: self._select_all()) + self.main_win.bind('', lambda event: self._increase_textsize()) + self.main_win.bind('', lambda event: self._decrease_textsize()) + self.main_win.bind('', lambda event: self._text_win_bigger()) + self.main_win.bind('', lambda event: self._text_win_smaller()) - def __del__(self): - # self.disco_all() - # self.ax25_port_handler.close_all() - pass + self.main_win.bind('', lambda event: self._any_key(event)) - def _destroy_win(self): - self.msg_to_monitor("PoPT wird beendet.") - self._is_closing = True - logging.info('Closing GUI: Closing Ports.') - PORT_HANDLER.close_all_ports() + def _any_key(self, event: tk.Event): + if event.keycode == 104: # Numpad Enter + self._snd_text(event) - logging.info('Closing GUI.') - self._close_port_stat_win() - if self.settings_win is not None: - self.settings_win.destroy() - if self.mh_window is not None: - self.mh_window.destroy() - if self.wx_window is not None: - self.wx_window.destroy() - if self.userdb_win is not None: - self.userdb_win.destroy() - if self.userDB_tree_win is not None: - self.userDB_tree_win.destroy() - if self.aprs_mon_win is not None: - self.aprs_mon_win.destroy() - if self.aprs_pn_msg_win is not None: - self.aprs_pn_msg_win.destroy() - if self.be_tracer_win is not None: - self.be_tracer_win.destroy() + def _arrow_keys(self, event=None): + self._on_click_inp_txt() - def _monitor_start_msg(self): + def _shift_return(self, event=None): + pass + + def _release_return(self, event=None): + pass + ########################## + # Start Message in Monitor + def _monitor_start_msg(self): # tmp_lang = int(self.language) # self.language = random.choice([0, 1, 2, 3, 4, 5, 6, 7, 8]) self.sprech(random.choice(WELCOME_SPEECH)) @@ -1915,6 +2030,36 @@ def _monitor_start_msg(self): PORT_HANDLER.get_all_ports()[port_k].port_cfg.parm_PortParm[1] )) + ########################## + # GUI Sizing/Formatting Stuff + def _increase_textsize(self): + self.text_size += 1 + self.text_size = max(self.text_size, 3) + width = self._inp_txt.cget('width') + self._inp_txt.configure(font=(FONT, self.text_size), width=width + 1) + self._out_txt.configure(font=(FONT, self.text_size), width=width + 1) + self._mon_txt.configure(font=(FONT, self.text_size), width=width + 1) + + def _decrease_textsize(self): + self.text_size -= 1 + self.text_size = max(self.text_size, 3) + width = self._inp_txt.cget('width') + self._inp_txt.configure(font=(FONT, self.text_size), width=width - 1) + self._out_txt.configure(font=(FONT, self.text_size), width=width - 1) + self._mon_txt.configure(font=(FONT, self.text_size), width=width - 1) + + def _text_win_bigger(self): + _width = self._inp_txt.cget('width') + self._inp_txt.configure(width=_width + 1) + self._out_txt.configure(width=_width + 1) + self._mon_txt.configure(width=_width + 1) + + def _text_win_smaller(self): + _width = self._inp_txt.cget('width') + self._inp_txt.configure(width=max(_width - 1, 56)) + self._out_txt.configure(width=max(_width - 1, 56)) + self._mon_txt.configure(width=max(_width - 1, 56)) + ########################## # Clipboard Stuff def _copy_select(self): @@ -1995,87 +2140,6 @@ def _save_monitor_to_file(self): data = self._mon_txt.get('1.0', tk.END) save_file_dialog(data) - def _set_binds(self): - self._inp_txt.bind("", self._on_click_inp_txt) - - def _set_keybinds(self): - self.main_win.unbind("") - self.main_win.unbind("") - # self.main_win.bind("",lambda event: self.callback(event)) - self.main_win.bind('', lambda event: self.switch_channel(1)) - self.main_win.bind('', lambda event: self.switch_channel(2)) - self.main_win.bind('', lambda event: self.switch_channel(3)) - self.main_win.bind('', lambda event: self.switch_channel(4)) - self.main_win.bind('', lambda event: self.switch_channel(5)) - self.main_win.bind('', lambda event: self.switch_channel(6)) - self.main_win.bind('', lambda event: self.switch_channel(7)) - self.main_win.bind('', lambda event: self.switch_channel(8)) - self.main_win.bind('', lambda event: self.switch_channel(9)) - self.main_win.bind('', lambda event: self.switch_channel(10)) - self.main_win.bind('', lambda event: self.switch_channel(0)) - self.main_win.bind('', self._snd_text) - self.main_win.bind('', self._release_return) - self.main_win.bind('', self._shift_return) - self.main_win.bind('', self._arrow_keys) - self.main_win.bind('', self._arrow_keys) - self.main_win.bind('', self._arrow_keys) - self.main_win.bind('', self._arrow_keys) - # self.main_win.bind('', self.snd_text) - self.main_win.bind('', lambda event: self.open_new_conn_win()) - self.main_win.bind('', lambda event: self.open_new_conn_win()) - self.main_win.bind('', lambda event: self._disco_conn()) - self.main_win.bind('', lambda event: self._copy_select()) - self.main_win.bind('', lambda event: self._cut_select()) - # self.main_win.bind('', lambda event: self.clipboard_past()) - self.main_win.bind('', lambda event: self._select_all()) - self.main_win.bind('', lambda event: self._increase_textsize()) - self.main_win.bind('', lambda event: self._decrease_textsize()) - self.main_win.bind('', lambda event: self._text_win_bigger()) - self.main_win.bind('', lambda event: self._text_win_smaller()) - - self.main_win.bind('', lambda event: self._any_key(event)) - - def _any_key(self, event: tk.Event): - if event.keycode == 104: # Numpad Enter - self._snd_text(event) - - def _arrow_keys(self, event=None): - self._on_click_inp_txt() - - def _shift_return(self, event=None): - pass - - def _release_return(self, event=None): - pass - - def _increase_textsize(self): - self.text_size += 1 - self.text_size = max(self.text_size, 3) - width = self._inp_txt.cget('width') - self._inp_txt.configure(font=(FONT, self.text_size), width=width + 1) - self._out_txt.configure(font=(FONT, self.text_size), width=width + 1) - self._mon_txt.configure(font=(FONT, self.text_size), width=width + 1) - - def _decrease_textsize(self): - self.text_size -= 1 - self.text_size = max(self.text_size, 3) - width = self._inp_txt.cget('width') - self._inp_txt.configure(font=(FONT, self.text_size), width=width - 1) - self._out_txt.configure(font=(FONT, self.text_size), width=width - 1) - self._mon_txt.configure(font=(FONT, self.text_size), width=width - 1) - - def _text_win_bigger(self): - _width = self._inp_txt.cget('width') - self._inp_txt.configure(width=_width + 1) - self._out_txt.configure(width=_width + 1) - self._mon_txt.configure(width=_width + 1) - - def _text_win_smaller(self): - _width = self._inp_txt.cget('width') - self._inp_txt.configure(width=max(_width - 1, 56)) - self._out_txt.configure(width=max(_width - 1, 56)) - self._mon_txt.configure(width=max(_width - 1, 56)) - def change_conn_btn(self): # TODO Nur triggern wenn ch_btn click | neue in conn | disco # TODO extra Funktionen für on_disco & on_newconn @@ -2228,13 +2292,13 @@ def _rx_beep_sound(self): if tr: if temp.rx_beep_tr: temp.rx_beep_tr = False - self._sound_play(self._root_dir + '//data//sound//rx_beep.wav', False) + self._sound_play(self._root_dir + CFG_sound_RX_BEEP, False) def new_conn_sound(self): - self._sound_play(self._root_dir + '//data//sound//conn_alarm.wav', False) + self._sound_play(self._root_dir + CFG_sound_CONN, False) def disco_sound(self): - self._sound_play(self._root_dir + '//data//sound//disco_alarm.wav', False) + self._sound_play(self._root_dir + CFG_sound_DICO, False) # Sound Ende ################# @@ -2243,10 +2307,13 @@ def disco_sound(self): def _dx_alarm(self): """ Alarm when new User in MH List """ - # self.tabbed_sideFrame.tabControl.select(self.tabbed_sideFrame.tab2_mh) - _clr = generate_random_hex_color() - if self._mh_btn.cget('bg') != _clr: - self._mh_btn.configure(bg=_clr) + if self.setting_dx_alarm.get(): + _clr = generate_random_hex_color() + if self._mh_btn.cget('bg') != _clr: + self._mh_btn.configure(bg=_clr) + _aprs_obj = PORT_HANDLER.get_aprs_ais() + if _aprs_obj is not None: + _aprs_obj.tracer_reset_auto_timer(MH_LIST.last_dx_alarm) def _tracer_alarm(self): """ Tracer Alarm """ @@ -2262,7 +2329,7 @@ def _reset_tracer_alarm(self): self._tracer_btn.configure(bg=self._tracer_btn_def_clr) def _reset_dx_alarm(self): - MH_LIST.new_call_alarm = False + MH_LIST.dx_alarm_trigger = False if self._mh_btn.cget('bg') != self._mh_btn_def_clr: self._mh_btn.configure(bg=self._mh_btn_def_clr) @@ -2273,11 +2340,12 @@ def _tasker(self): # MAINLOOP if self._is_closing: self._tasker_quit() else: - # self._tasker_prio() - self._tasker_05_sec() + self._tasker_prio() + # self._tasker_05_sec() self._tasker_1_sec() self._tasker_5_sec() # self._tasker_tester() + # self.main_win.update_idletasks() self.main_win.after(self._loop_delay, self._tasker) @staticmethod @@ -2287,23 +2355,26 @@ def _tasker_quit(): logging.info('Closing GUI: Done.') def _tasker_prio(self): - """ Prio Tasks """ - pass - - def _tasker_05_sec(self): - """ 0.5 Sec """ - if time.time() > self._non_prio_task_timer: - self._non_prio_task_timer = time.time() + self._parm_non_prio_task_timer - ##################### + """ Prio Tasks 250 ms each flip """ + if self._prio_task_flip: self._aprs_task() self._monitor_task() self._update_qso_win() + else: self._txt_win.update_status_win() self.change_conn_btn() if self.setting_sound: self._rx_beep_sound() if self.setting_sprech: self._check_sprech_ch_buf() + self._prio_task_flip = not self._prio_task_flip + + def _tasker_05_sec(self): + """ 0.5 Sec """ + if time.time() > self._non_prio_task_timer: + self._non_prio_task_timer = time.time() + self._parm_non_prio_task_timer + ##################### + pass def _tasker_1_sec(self): """ 1 Sec """ @@ -2316,7 +2387,7 @@ def _tasker_1_sec(self): # if MH_LIST.new_call_alarm and self.setting_dx_alarm.get(): if self.ch_alarm: self.ch_status_update() - if MH_LIST.new_call_alarm: + if MH_LIST.dx_alarm_trigger: self._dx_alarm() if PORT_HANDLER.get_aprs_ais() is not None: if PORT_HANDLER.get_aprs_ais().tracer_is_alarm(): @@ -2517,7 +2588,6 @@ def _monitor_task(self): self._mon_txt.configure(state="disabled", exportselection=True) if tr or self.tabbed_sideFrame.mon_scroll_var.get(): self._see_end_mon_win() - self.main_win.update_idletasks() def see_end_qso_win(self): self._out_txt.see("end") @@ -2657,9 +2727,14 @@ def _UserDB_tree(self): self.userDB_tree_win = UserDBtreeview(self) def gui_set_distance(self): + self._set_distance_fm_conn() + + def _set_distance_fm_conn(self): _conn = self.get_conn() if _conn: _conn.set_distance() + return True + return False # ############## # DISCO @@ -2867,7 +2942,7 @@ def switch_channel(self, ch_ind: int = 0): def ch_status_update(self): """ Triggered by tasker !!! """ """Triggerd when Connection Status has changed""" - self._ch_btn.ch_btn_status_update() + self._ch_btn._ch_btn_status_update() # self.change_conn_btn() self.on_channel_status_change() @@ -2912,7 +2987,7 @@ def _ch_btn_clk(self, ind: int): self._txt_win.ts_box_box.configure(bg=STAT_BAR_CLR) self.on_channel_status_change() - self._ch_btn.ch_btn_status_update() + self._ch_btn._ch_btn_status_update() self._kanal_switch() # Sprech def on_channel_status_change(self): @@ -3046,26 +3121,62 @@ def _update_ft_info(self): def get_ch_new_data_tr(self, ch_id): return bool(self._win_buf[ch_id].new_data_tr) + def set_tracer(self, state=None): + _ais_obj = PORT_HANDLER.get_aprs_ais() + if _ais_obj is not None: + _ais_obj.be_tracer_active = bool(self.setting_tracer.get()) + else: + self.setting_tracer.set(False) + self.set_auto_tracer() + self.tabbed_sideFrame.set_auto_tracer_state() + @staticmethod - def set_tracer(state: bool): + def get_tracer(): _ais_obj = PORT_HANDLER.get_aprs_ais() if _ais_obj is not None: - _ais_obj.be_tracer_active = bool(state) + return _ais_obj.be_tracer_active + return False + def set_tracer_fm_aprs(self): + _ais_obj = PORT_HANDLER.get_aprs_ais() + if _ais_obj is not None: + self.setting_tracer.set(_ais_obj.be_tracer_active) + else: + self.setting_tracer.set(False) + self.tabbed_sideFrame.set_auto_tracer_state() -""" -if __name__ == '__main__': - logger.info(f"PoPT_{VER} start....") - ############# - # INIT GUI - # TODO: if setting_gui (running without GUI option): - logger.info(f"Loading GUI.") - root = tk.Tk() - # gui.guiMain.TkMainWin() - TkMainWin(root) - root.mainloop() + def set_auto_tracer(self, event=None): + _ais_obj = PORT_HANDLER.get_aprs_ais() + set_to = False + if _ais_obj is not None: + self.set_tracer_fm_aprs() + if self.setting_tracer.get(): + set_to = False + else: + set_to = bool(self.setting_auto_tracer.get()) + _ais_obj.tracer_auto_tracer_set(set_to) + self.setting_auto_tracer.set(set_to) + self.tabbed_sideFrame.set_auto_tracer_state() - logger.info(f"PoPT_{VER} ENDE.") - print('ENDE') + @staticmethod + def get_auto_tracer_duration(): + _ais_obj = PORT_HANDLER.get_aprs_ais() + if _ais_obj is None: + return 0 + return _ais_obj.be_auto_tracer_duration -""" + def set_auto_tracer_duration(self, dur): + _ais_obj = PORT_HANDLER.get_aprs_ais() + if _ais_obj is not None: + if type(dur) == int: + _ais_obj.tracer_auto_tracer_duration_set(dur) + self.set_auto_tracer() + + def set_dx_alarm(self, event=None): + _dx_alarm = bool(self.setting_dx_alarm.get()) + if not _dx_alarm: + self.setting_auto_tracer.set(False) + self.set_auto_tracer() + + def get_dx_alarm(self): + return bool(self.setting_dx_alarm.get()) diff --git a/gui/guiUserDB.py b/gui/guiUserDB.py index bb350168..d95a3cad 100644 --- a/gui/guiUserDB.py +++ b/gui/guiUserDB.py @@ -23,7 +23,7 @@ def __init__(self, root, ent_key=''): f"{self.win_height}+" f"{self.root.main_win.winfo_x()}+" f"{self.root.main_win.winfo_y()}") - self.protocol("WM_DELETE_WINDOW", self.destroy_win) + self.protocol("WM_DELETE_WINDOW", self._destroy_win) self.resizable(False, False) try: self.iconbitmap("favicon.ico") @@ -59,7 +59,7 @@ def __init__(self, root, ent_key=''): # bg="green", height=1, width=8, - command=self.destroy_win) + command=self._destroy_win) ok_bt.place(x=20, y=self.win_height - 50) save_bt.place(x=110, y=self.win_height - 50) cancel_bt.place(x=self.win_width - 120, y=self.win_height - 50) @@ -404,6 +404,7 @@ def __init__(self, root, ent_key=''): root.userdb_win = self def select_entry(self, event=None): + self._save_vars() for selected_item in self.tree.selection(): item = self.tree.item(selected_item) record = item['values'][0] @@ -474,15 +475,14 @@ def set_var_to_ent(self): self.db_ent.sys_pw_parm.append('SYS') self.login_cmd_var.set(str(self.db_ent.sys_pw_parm[2])) - - self.update_sysop_opt() - self.update_stations() + self._update_sysop_opt() + self._update_stations() def sysop_opt_remove(self): self.sysop_var.set('') # remove default selection only, not the full list self.sysop_ent['menu'].delete(0, 'end') # remove full list - def update_sysop_opt(self): + def _update_sysop_opt(self): self.sysop_opt_remove() # remove all options if self.db_ent.TYP == 'SYSOP': @@ -498,12 +498,13 @@ def update_sysop_opt(self): for opt in sorted(self._user_db.get_keys_by_typ(typ='SYSOP')): self.sysop_ent['menu'].add_command(label=opt, command=tk._setit(self.sysop_var, opt)) - def update_stations(self): + def _update_stations(self): sysop_key = '' - if self.db_ent.TYP == 'SYSOP': - sysop_key = self.db_ent.call_str - else: - sysop_key = self.db_ent.Sysop_Call + if self.db_ent: + if self.db_ent.TYP == 'SYSOP': + sysop_key = self.db_ent.call_str + else: + sysop_key = self.db_ent.Sysop_Call node_str = 'NODES: ' bbs_str = 'BBS: ' @@ -527,6 +528,13 @@ def update_stations(self): self.stations_other_var.set(other_str) def save_btn_cmd(self): + self._save_vars() + self._user_db.save_data() + self.select_entry() + self.root.update_station_info() + self.root.msg_to_monitor(f'Info: User Daten für {self.db_ent.call_str} wurden gespeichert..') + + def _save_vars(self): if self.db_ent: self.db_ent.Name = self.name_var.get() self.db_ent.QTH = self.qth_var.get() @@ -560,17 +568,12 @@ def save_btn_cmd(self): if tmp != self.db_ent.Sysop_Call: self.db_ent.Sysop_Call = tmp self.on_select_sysop() - - self._user_db.save_data() - self.select_entry() self.root.gui_set_distance() - self.root.update_station_info() - self.root.msg_to_monitor(f'Info: User Daten für {self.db_ent.call_str} wurden gespeichert..') def ok_btn_cmd(self): - + self._save_vars() self.root.msg_to_monitor('Lob: Du hast dir heute noch kein Lob verdient.') - self.destroy_win() + self._destroy_win() def del_btn_cmd(self): if self.db_ent: @@ -587,7 +590,7 @@ def del_btn_cmd(self): self.db_ent = self._user_db.get_entry(ents[0]) self.select_entry() - def destroy_win(self): + def _destroy_win(self): self.root.userdb_win = None self.destroy() diff --git a/idea.txt b/idea.txt index 519f746a..6be5eb7b 100644 --- a/idea.txt +++ b/idea.txt @@ -2,6 +2,7 @@ Ideas/TODO's : - ESC Befehle - Individuelle Farben (Vorschreib Fenster) +- Beacon System überarbeiten ✓ UserDB - Infos / Ctext usw. in User-DB speichern - User-DB Sync System (Manuell zu triggern bei connect zu anderer PoPT Station) @@ -11,6 +12,8 @@ Ideas/TODO's : - Debugging Tool - Simple Mailbox - BBS Tool was Bulletins und PMs von/an Heimat-BBS holt/sendet und Mails aufbereitet darstellt. +- BBS (S&F und all den Gedöns) +- "Nachrichten-Center" fasst alle Nachrichtenkanäle (APRS Bulletin/PN, BBS) übersichtlich zusammen. - Telnet Port - Loop Port oder einfach localhost via AXIP - MultiKISS @@ -27,6 +30,7 @@ Ideas/TODO's : ✓ APRS Private Message System ✓ APRS Tracer ✓ APRS Tracer Alarm +✓ APRS Auto-Tracer nach DX-Alarm(MH-Liste) ✓ APRS WX Stationen (Wetterstationen) Monitoring Auswertung - APRS Digi / I-Gate Funktion - "Managed-DIGI" Funktion (Die DIGI Verbindung wird wie ein Noden Link behandelt) @@ -36,7 +40,7 @@ Ideas/TODO's : - Daten Komprimierung - Daten Verschlüsselung (AES/RSA) ✓ ordentliches Noden System bzw CLI überarbeiten -- DX-Alarm überarbeiten +✓ DX-Alarm überarbeiten ✓ Autoscrolling überarbeiten. - Optional nur als Konsolen Anwendung ohne GUI. (Ist bereits dafür vorbereitet) - Drucker Ausgabe vom QSO diff --git a/string_tab.py b/string_tab.py index bd49b96d..8281ab07 100644 --- a/string_tab.py +++ b/string_tab.py @@ -201,6 +201,9 @@ 'port_overview': ('Port Übersicht', 'Port Overview', 'Lekker Ports'), + 'cmd_shelp': ('Kurzhilfe', + 'Short help', + 'Lekker Korte hulp'), 'time_connected': ('Connect Dauer', 'Connect duration', @@ -216,7 +219,15 @@ 'cmd_help_lcstatus': ('Verbundene Terminalkanäle anzeigen (ausführliche Version)', 'Show connected terminal channels (detailed version)', 'Toon lekker aangesloten terminalkanalen (gedetailleerde versie)'), - 'cli_no_wx_data': ('Keine Wetterdaten vorhanden.', 'No WX data available', 'No WX data available'), - 'cli_no_tracer_data': ('Keine Tracerdaten vorhanden.', 'No Tracer data available', 'No Tracer data available'), + 'cli_no_wx_data': ('Keine Wetterdaten vorhanden.', 'No WX data available', 'Geen lekker WX beschikbaar.'), + 'cli_no_data': ('Keine Daten vorhanden.', 'No data available.', 'Geen gegevens beschikbaar.'), + 'cli_no_tracer_data': ('Keine Tracerdaten vorhanden.', 'No Tracer data available', 'Geen tracergegevens beschikbaar.'), + 'cli_change_language': ('Sprache ändern.', 'Change language.', 'Lekker taal veranderen'), + 'cli_lang_set': ('Sprache auf Deutsch geändert.', + 'Language changed to English.', + 'Taal veranderd naar lekker Nederlands.'), + 'cli_no_lang_param': ('Sprache nicht erkannt! Mögliche Sprachen: ', + 'Language not recognized! Possible languages: ', + 'Taal niet herkend! Mogelijke talen: '), }