not1mm 23.3.8__py3-none-any.whl → 23.3.15__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
not1mm/__main__.py CHANGED
@@ -9,44 +9,49 @@ import logging
9
9
  import os
10
10
  import pkgutil
11
11
  import re
12
+ import socket
12
13
  import sqlite3
13
14
  import sys
15
+ import subprocess
16
+ import threading
17
+ import uuid
14
18
  from datetime import datetime
15
19
  from json import dumps, loads
16
20
  from pathlib import Path
17
21
  from shutil import copyfile
18
22
  from xmlrpc.client import Error, ServerProxy
19
23
 
24
+ # import dbus
20
25
  import psutil
21
26
  from PyQt5 import QtCore, QtGui, QtWidgets, uic
22
27
  from PyQt5.QtCore import (
23
28
  QDir,
24
- QPoint,
25
- QSize,
29
+ QPoint, # pylint: disable=no-name-in-module
26
30
  QRect,
31
+ QSize,
27
32
  Qt,
28
- ) # pylint: disable=no-name-in-module
33
+ )
29
34
  from PyQt5.QtGui import QFontDatabase # pylint: disable=no-name-in-module
30
35
 
31
- from not1mm.lib.database import DataBase
32
36
  from not1mm.lib.cat_interface import CAT
33
37
  from not1mm.lib.cwinterface import CW
34
- from not1mm.lib.edit_settings import EditSettings
38
+ from not1mm.lib.database import DataBase
35
39
  from not1mm.lib.edit_macro import EditMacro
36
40
  from not1mm.lib.edit_opon import OpOn
37
- from not1mm.lib.qrz_dialog import UseQRZ
41
+ from not1mm.lib.edit_settings import EditSettings
38
42
  from not1mm.lib.ham_utility import (
39
43
  bearing,
40
- distance,
41
- reciprocol,
42
44
  bearing_with_latlon,
45
+ calculate_wpx_prefix,
46
+ distance,
43
47
  distance_with_latlon,
44
48
  getband,
49
+ reciprocol,
45
50
  )
46
51
  from not1mm.lib.lookup import QRZlookup
52
+ from not1mm.lib.qrz_dialog import UseQRZ
47
53
  from not1mm.lib.version import __version__
48
54
 
49
-
50
55
  # os.environ["QT_QPA_PLATFORM"] = "wayland"
51
56
  os.environ["QT_QPA_PLATFORMTHEME"] = "gnome"
52
57
  # os.environ["QT_STYLE_OVERRIDE"] = "Fusion"
@@ -163,6 +168,12 @@ class MainWindow(QtWidgets.QMainWindow):
163
168
  super().__init__(*args, **kwargs)
164
169
  logger.info("MainWindow: __init__")
165
170
  self.database = DataBase(self.dbname, WORKING_PATH)
171
+ _ = subprocess.Popen(
172
+ [sys.executable, WORKING_PATH + "/logwindow.py"]
173
+ ) # Call subprocess
174
+ # subprocess.run(WORKING_PATH + "/logwindow.py")
175
+ self.cw = CW(1, "127.0.0.1", 6789)
176
+ self.contact = self.database.empty_contact
166
177
  data_path = WORKING_PATH + "/data/main.ui"
167
178
  uic.loadUi(data_path, self)
168
179
 
@@ -177,6 +188,11 @@ class MainWindow(QtWidgets.QMainWindow):
177
188
  self.radioButton_sp.clicked.connect(self.run_sp_buttons_clicked)
178
189
  self.score.setText("0")
179
190
  self.callsign.textEdited.connect(self.callsign_changed)
191
+ self.callsign.returnPressed.connect(self.save_contact)
192
+ self.sent.returnPressed.connect(self.save_contact)
193
+ self.receive.returnPressed.connect(self.save_contact)
194
+ self.other_1.returnPressed.connect(self.save_contact)
195
+ self.other_2.returnPressed.connect(self.save_contact)
180
196
  self.sent.setText("59")
181
197
  self.receive.setText("59")
182
198
  icon_path = WORKING_PATH + "/data/"
@@ -278,6 +294,8 @@ class MainWindow(QtWidgets.QMainWindow):
278
294
  # if self.cw.servertype == 1:
279
295
  # self.cw.speed -= 1
280
296
  # self.cw.sendcw(f"\x1b2{self.cw.speed}")
297
+ # if event.key() == Qt.Key.Key_Enter:
298
+ # self.save_contact()
281
299
  if event.key() == Qt.Key.Key_Tab or event.key() == Qt.Key.Key_Backtab:
282
300
  if self.sent.hasFocus():
283
301
  logger.debug("From sent")
@@ -339,25 +357,18 @@ class MainWindow(QtWidgets.QMainWindow):
339
357
  prev_tab.deselect()
340
358
  prev_tab.end(False)
341
359
  else:
360
+ text = self.callsign.text()
361
+ text = text.upper()
362
+ _thethread = threading.Thread(
363
+ target=self.check_callsign2,
364
+ args=(text,),
365
+ daemon=True,
366
+ )
367
+ _thethread.start()
342
368
  next_tab = self.tab_next.get(self.callsign)
343
369
  next_tab.setFocus()
344
370
  next_tab.deselect()
345
371
  next_tab.end(False)
346
- # cse = self.callsign_entry.text()
347
- # if len(cse):
348
- # if cse[0] == ".":
349
- # self.keyboardcommand(cse)
350
- # return
351
- # else:
352
- # _thethread = threading.Thread(
353
- # target=self.lazy_lookup,
354
- # args=(self.callsign_entry.text(),),
355
- # daemon=True,
356
- # )
357
- # _thethread.start()
358
- # self.class_entry.setFocus()
359
- # self.class_entry.deselect()
360
- # self.class_entry.end(False)
361
372
  return
362
373
  if event.key() == Qt.Key_F1:
363
374
  self.sendf1()
@@ -389,6 +400,8 @@ class MainWindow(QtWidgets.QMainWindow):
389
400
  vfoa = self.radio_state.get("vfoa", "")
390
401
  if vfoa:
391
402
  vfoa = int(vfoa) / 1000
403
+ else:
404
+ vfoa = 0.0
392
405
  self.setWindowTitle(
393
406
  f"{round(vfoa,2)} "
394
407
  f"{self.radio_state.get('mode', '')} - Not1MM v{__version__}"
@@ -396,6 +409,7 @@ class MainWindow(QtWidgets.QMainWindow):
396
409
 
397
410
  def clearinputs(self):
398
411
  """Clears the text input fields and sets focus to callsign field."""
412
+ self.contact = self.database.empty_contact
399
413
  self.heading_distance.setText("No Heading")
400
414
  self.dx_entity.setText("")
401
415
  self.callsign.clear()
@@ -409,6 +423,59 @@ class MainWindow(QtWidgets.QMainWindow):
409
423
  self.other_2.clear()
410
424
  self.callsign.setFocus()
411
425
 
426
+ def save_contact(self):
427
+ """Save to db"""
428
+ logger.debug("saving")
429
+ self.contact["TS"] = datetime.utcnow().isoformat(" ")[:19]
430
+ self.contact["Call"] = self.callsign.text()
431
+ self.contact["Freq"] = round(float(self.radio_state.get("vfoa", 0.0)) / 1000, 2)
432
+ self.contact["QSXFreq"] = round(
433
+ float(self.radio_state.get("vfoa", 0.0)) / 1000, 2
434
+ )
435
+ self.contact["Mode"] = self.radio_state.get("mode", "")
436
+ self.contact["ContestName"] = self.contest.name
437
+ self.contact["SNT"] = self.sent.text()
438
+ self.contact["RCV"] = self.receive.text()
439
+ # self.contact["CountryPrefix"]
440
+ # self.contact["StationPrefix"]
441
+ # self.contact["QTH"]
442
+ # self.contact["Name"]
443
+ # self.contact["Comment"]
444
+ # self.contact["NR"]
445
+ # self.contact["Sect"]
446
+ # self.contact["Prec"]
447
+ # self.contact["CK"]
448
+ # self.contact["ZN"]
449
+ # self.contact["SentNr"]
450
+ # self.contact["Points"]
451
+ # self.contact["IsMultiplier1"]
452
+ # self.contact["IsMultiplier2"]
453
+ # self.contact["Power"]
454
+ # self.contact["Band"]
455
+ self.contact["WPXPrefix"] = calculate_wpx_prefix(self.callsign.text())
456
+ # self.contact["Exchange1"]
457
+ # self.contact["RadioNR"]
458
+ # self.contact["ContestNR"]
459
+ # self.contact["isMultiplier3"]
460
+ # self.contact["MiscText"]
461
+ # self.contact["IsRunQSO"]
462
+ # self.contact["ContactType"]
463
+ # self.contact["Run1Run2"]
464
+ # self.contact["GridSquare"]
465
+ self.contact["Operator"] = self.current_op
466
+ # self.contact["Continent"]
467
+ # self.contact["RoverLocation"]
468
+ # self.contact["RadioInterfaced"]
469
+ # self.contact["NetworkedCompNr"]
470
+ self.contact["NetBiosName"] = socket.gethostname()
471
+ # self.contact["IsOriginal"]
472
+ self.contact["ID"] = uuid.uuid4().hex
473
+ # self.contact["CLAIMEDQSO"]
474
+ debug_output = f"{self.contact}"
475
+ logger.debug(debug_output)
476
+ self.database.log_contact(self.contact)
477
+ self.clearinputs()
478
+
412
479
  def qrz_preference_selected(self):
413
480
  """Show QRZ settings dialog"""
414
481
  logger.debug("QRZ preference selected")
@@ -578,53 +645,91 @@ class MainWindow(QtWidgets.QMainWindow):
578
645
  logger.debug("F12 Right Clicked.")
579
646
  self.edit_macro(self.F12)
580
647
 
648
+ def process_macro(self, macro: str) -> str:
649
+ """Process CW macro substitutions"""
650
+ macro = macro.upper()
651
+ # if self.groupcall and self.connect_to_server:
652
+ # macro = macro.replace("{MYCALL}", self.groupcall)
653
+ # else:
654
+ macro = macro.replace("{MYCALL}", self.pref.get("callsign"))
655
+ # macro = macro.replace("{MYCLASS}", self.preference.get("myclass"))
656
+ # macro = macro.replace("{MYSECT}", self.preference.get("mysection"))
657
+ macro = macro.replace("{HISCALL}", self.callsign.text())
658
+ return macro
659
+
581
660
  def sendf1(self):
582
661
  """stub"""
583
662
  logger.debug("F1 Clicked")
663
+ if self.cw:
664
+ # if self.preference.get("send_n1mm_packets"):
665
+ # self.n1mm.radio_info["FunctionKeyCaption"] = self.F1.text()
666
+ self.cw.sendcw(self.process_macro(self.F1.toolTip()))
584
667
 
585
668
  def sendf2(self):
586
669
  """stub"""
587
670
  logger.debug("F2 Clicked")
671
+ if self.cw:
672
+ self.cw.sendcw(self.process_macro(self.F2.toolTip()))
588
673
 
589
674
  def sendf3(self):
590
675
  """stub"""
591
676
  logger.debug("F3 Clicked")
677
+ if self.cw:
678
+ self.cw.sendcw(self.process_macro(self.F3.toolTip()))
592
679
 
593
680
  def sendf4(self):
594
681
  """stub"""
595
682
  logger.debug("F4 Clicked")
683
+ if self.cw:
684
+ self.cw.sendcw(self.process_macro(self.F4.toolTip()))
596
685
 
597
686
  def sendf5(self):
598
687
  """stub"""
599
688
  logger.debug("F5 Clicked")
689
+ if self.cw:
690
+ self.cw.sendcw(self.process_macro(self.F5.toolTip()))
600
691
 
601
692
  def sendf6(self):
602
693
  """stub"""
603
694
  logger.debug("F6 Clicked")
695
+ if self.cw:
696
+ self.cw.sendcw(self.process_macro(self.F6.toolTip()))
604
697
 
605
698
  def sendf7(self):
606
699
  """stub"""
607
700
  logger.debug("F7 Clicked")
701
+ if self.cw:
702
+ self.cw.sendcw(self.process_macro(self.F7.toolTip()))
608
703
 
609
704
  def sendf8(self):
610
705
  """stub"""
611
706
  logger.debug("F8 Clicked")
707
+ if self.cw:
708
+ self.cw.sendcw(self.process_macro(self.F8.toolTip()))
612
709
 
613
710
  def sendf9(self):
614
711
  """stub"""
615
712
  logger.debug("F9 Clicked")
713
+ if self.cw:
714
+ self.cw.sendcw(self.process_macro(self.F9.toolTip()))
616
715
 
617
716
  def sendf10(self):
618
717
  """stub"""
619
718
  logger.debug("F10 Clicked")
719
+ if self.cw:
720
+ self.cw.sendcw(self.process_macro(self.F10.toolTip()))
620
721
 
621
722
  def sendf11(self):
622
723
  """stub"""
623
724
  logger.debug("F11 Clicked")
725
+ if self.cw:
726
+ self.cw.sendcw(self.process_macro(self.F11.toolTip()))
624
727
 
625
728
  def sendf12(self):
626
729
  """stub"""
627
730
  logger.debug("F12 Clicked")
731
+ if self.cw:
732
+ self.cw.sendcw(self.process_macro(self.F12.toolTip()))
628
733
 
629
734
  def run_sp_buttons_clicked(self):
630
735
  """Handle run/s&p mode"""
@@ -779,21 +884,28 @@ class MainWindow(QtWidgets.QMainWindow):
779
884
  self.setmode("CW")
780
885
  if self.rig_control.online:
781
886
  self.rig_control.set_mode("CW")
887
+ else:
888
+ self.radio_state["mode"] = "CW"
889
+ self.set_window_title()
782
890
  self.clearinputs()
783
891
  return
784
892
  if stripped_text == "RTTY":
785
893
  self.setmode("RTTY")
786
894
  if self.rig_control.online:
787
895
  self.rig_control.set_mode("RTTY")
896
+ else:
897
+ self.radio_state["mode"] = "RTTY"
898
+ self.set_window_title()
788
899
  self.clearinputs()
789
900
  return
790
901
  if stripped_text == "SSB":
791
902
  self.setmode("SSB")
792
- if self.rig_control.online:
793
- if int(self.radio_state.get("vfoa", 0)) > 10000000:
794
- self.rig_control.set_mode("USB")
795
- else:
796
- self.rig_control.set_mode("LSB")
903
+ self.radio_state["mode"] = "SSB"
904
+ self.set_window_title()
905
+ if int(self.radio_state.get("vfoa", 0)) > 10000000:
906
+ self.rig_control.set_mode("USB")
907
+ else:
908
+ self.rig_control.set_mode("LSB")
797
909
  self.clearinputs()
798
910
  return
799
911
  if stripped_text == "OPON":
@@ -805,13 +917,19 @@ class MainWindow(QtWidgets.QMainWindow):
805
917
  vfo = int(vfo * 1000)
806
918
  band = getband(str(vfo))
807
919
  self.set_band_indicator(band)
808
- logger.debug("Set VFO - %s", vfo)
920
+ self.radio_state["vfoa"] = vfo
921
+ self.set_window_title()
809
922
  self.clearinputs()
810
923
  self.rig_control.set_vfo(vfo)
811
924
  return
812
925
 
813
926
  self.check_callsign(text)
814
- self.check_callsign2(text)
927
+ _thethread = threading.Thread(
928
+ target=self.check_callsign2,
929
+ args=(text,),
930
+ daemon=True,
931
+ )
932
+ _thethread.start()
815
933
  self.next_field.setFocus()
816
934
  return
817
935
  self.check_callsign(stripped_text)
@@ -837,6 +955,9 @@ class MainWindow(QtWidgets.QMainWindow):
837
955
  f"Regional Hdg {heading}° LP {reciprocol(heading)}° / "
838
956
  f"distance {int(kilometers*0.621371)}mi {kilometers}km"
839
957
  )
958
+ self.contact["CountryPrefix"] = primary_pfx
959
+ self.contact["ZN"] = int(cq)
960
+ self.contact["Continent"] = continent
840
961
  self.dx_entity.setText(
841
962
  f"{primary_pfx}: {continent}/{entity} cq:{cq} itu:{itu}"
842
963
  )
@@ -852,6 +973,7 @@ class MainWindow(QtWidgets.QMainWindow):
852
973
  logger.debug("The Response: %s\n", debug_response)
853
974
  if response:
854
975
  theirgrid = response.get("grid")
976
+ self.contact["GridSquare"] = theirgrid
855
977
  _theircountry = response.get("country")
856
978
  if self.pref.get("gridsquare"):
857
979
  heading = bearing(self.pref.get("gridsquare"), theirgrid)
@@ -906,6 +1028,8 @@ class MainWindow(QtWidgets.QMainWindow):
906
1028
  self.setmode(mode)
907
1029
  if mode == "LSB" or mode == "USB":
908
1030
  self.setmode("SSB")
1031
+ if mode == "RTTY":
1032
+ self.setmode("RTTY")
909
1033
  self.radio_state["vfoa"] = vfo
910
1034
  band = getband(str(vfo))
911
1035
  self.set_band_indicator(band)
@@ -1018,7 +1142,6 @@ def run():
1018
1142
  sys.exit(app.exec())
1019
1143
 
1020
1144
 
1021
- # logger = logging
1022
1145
  logger = logging.getLogger("__main__")
1023
1146
  handler = logging.StreamHandler()
1024
1147
  formatter = logging.Formatter(
@@ -1029,7 +1152,6 @@ handler.setFormatter(formatter)
1029
1152
  logger.addHandler(handler)
1030
1153
 
1031
1154
  if Path("./debug").exists():
1032
- # if True:
1033
1155
  logger.setLevel(logging.DEBUG)
1034
1156
  logger.debug("debugging on")
1035
1157
  else:
@@ -1037,7 +1159,6 @@ else:
1037
1159
  logger.warning("debugging off")
1038
1160
 
1039
1161
  app = QtWidgets.QApplication(sys.argv)
1040
- # app.setStyle("Fusion")
1041
1162
  font_path = WORKING_PATH + "/data"
1042
1163
  families = load_fonts_from_dir(os.fspath(font_path))
1043
1164
  logger.info(families)
@@ -0,0 +1,41 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ui version="4.0">
3
+ <class>MainWindow</class>
4
+ <widget class="QMainWindow" name="MainWindow">
5
+ <property name="geometry">
6
+ <rect>
7
+ <x>0</x>
8
+ <y>0</y>
9
+ <width>1189</width>
10
+ <height>366</height>
11
+ </rect>
12
+ </property>
13
+ <property name="windowTitle">
14
+ <string>MainWindow</string>
15
+ </property>
16
+ <widget class="QWidget" name="centralwidget">
17
+ <layout class="QGridLayout" name="gridLayout">
18
+ <item row="0" column="0">
19
+ <widget class="QTableWidget" name="generalLog">
20
+ <property name="font">
21
+ <font>
22
+ <family>JetBrains Mono</family>
23
+ </font>
24
+ </property>
25
+ </widget>
26
+ </item>
27
+ <item row="1" column="0">
28
+ <widget class="QTableWidget" name="focusedLog">
29
+ <property name="font">
30
+ <font>
31
+ <family>JetBrains Mono</family>
32
+ </font>
33
+ </property>
34
+ </widget>
35
+ </item>
36
+ </layout>
37
+ </widget>
38
+ </widget>
39
+ <resources/>
40
+ <connections/>
41
+ </ui>
not1mm/data/main.ui CHANGED
@@ -48,6 +48,12 @@
48
48
  <layout class="QHBoxLayout" name="mainsection">
49
49
  <item>
50
50
  <widget class="QFrame" name="Band_Mode_Frame">
51
+ <property name="minimumSize">
52
+ <size>
53
+ <width>40</width>
54
+ <height>0</height>
55
+ </size>
56
+ </property>
51
57
  <property name="lineWidth">
52
58
  <number>0</number>
53
59
  </property>
@@ -309,6 +315,12 @@
309
315
  </item>
310
316
  <item>
311
317
  <layout class="QHBoxLayout" name="run_vs_sanp">
318
+ <property name="spacing">
319
+ <number>6</number>
320
+ </property>
321
+ <property name="sizeConstraint">
322
+ <enum>QLayout::SetDefaultConstraint</enum>
323
+ </property>
312
324
  <item>
313
325
  <widget class="QLabel" name="leftdot">
314
326
  <property name="text">
@@ -345,11 +357,26 @@
345
357
  </item>
346
358
  <item>
347
359
  <widget class="QSpinBox" name="cw_speed">
360
+ <property name="sizePolicy">
361
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
362
+ <horstretch>0</horstretch>
363
+ <verstretch>0</verstretch>
364
+ </sizepolicy>
365
+ </property>
348
366
  <property name="focusPolicy">
349
367
  <enum>Qt::NoFocus</enum>
350
368
  </property>
369
+ <property name="wrapping">
370
+ <bool>false</bool>
371
+ </property>
372
+ <property name="frame">
373
+ <bool>true</bool>
374
+ </property>
375
+ <property name="alignment">
376
+ <set>Qt::AlignCenter</set>
377
+ </property>
351
378
  <property name="buttonSymbols">
352
- <enum>QAbstractSpinBox::PlusMinus</enum>
379
+ <enum>QAbstractSpinBox::UpDownArrows</enum>
353
380
  </property>
354
381
  <property name="suffix">
355
382
  <string/>
@@ -357,6 +384,15 @@
357
384
  <property name="prefix">
358
385
  <string/>
359
386
  </property>
387
+ <property name="minimum">
388
+ <number>5</number>
389
+ </property>
390
+ <property name="maximum">
391
+ <number>40</number>
392
+ </property>
393
+ <property name="value">
394
+ <number>20</number>
395
+ </property>
360
396
  </widget>
361
397
  </item>
362
398
  <item>
@@ -756,6 +792,11 @@
756
792
  <property name="text">
757
793
  <string>About</string>
758
794
  </property>
795
+ <property name="font">
796
+ <font>
797
+ <family>JetBrains Mono</family>
798
+ </font>
799
+ </property>
759
800
  </action>
760
801
  <action name="actionCommand_Buttons">
761
802
  <property name="checkable">
@@ -813,23 +854,43 @@
813
854
  <bool>true</bool>
814
855
  </property>
815
856
  <property name="text">
816
- <string>Dark Mode</string>
857
+ <string>Force Dark Mode</string>
858
+ </property>
859
+ <property name="font">
860
+ <font>
861
+ <family>JetBrains Mono</family>
862
+ </font>
817
863
  </property>
818
864
  </action>
819
865
  <action name="actionPreferences">
820
866
  <property name="text">
821
867
  <string>Station Settings</string>
822
868
  </property>
869
+ <property name="font">
870
+ <font>
871
+ <family>JetBrains Mono</family>
872
+ </font>
873
+ </property>
823
874
  </action>
824
875
  <action name="actionQRZ_Settings">
825
876
  <property name="text">
826
877
  <string>QRZ Settings</string>
827
878
  </property>
879
+ <property name="font">
880
+ <font>
881
+ <family>JetBrains Mono</family>
882
+ </font>
883
+ </property>
828
884
  </action>
829
885
  <action name="actionConnection_Settings">
830
886
  <property name="text">
831
887
  <string>Connection Settings</string>
832
888
  </property>
889
+ <property name="font">
890
+ <font>
891
+ <family>JetBrains Mono</family>
892
+ </font>
893
+ </property>
833
894
  </action>
834
895
  </widget>
835
896
  <resources/>
not1mm/lib/database.py CHANGED
@@ -35,17 +35,17 @@ class DataBase:
35
35
  "QTH": "",
36
36
  "Name": "",
37
37
  "Comment": "",
38
- "NR": "",
38
+ "NR": 0,
39
39
  "Sect": "",
40
40
  "Prec": "",
41
- "CK": "",
42
- "ZN": "",
43
- "SentNr": "",
44
- "Points": "",
45
- "IsMultiplier1": "",
46
- "IsMultiplier2": "",
47
- "Power": "",
48
- "Band": "",
41
+ "CK": 0,
42
+ "ZN": 0,
43
+ "SentNr": 0,
44
+ "Points": 0,
45
+ "IsMultiplier1": 0,
46
+ "IsMultiplier2": 0,
47
+ "Power": 0,
48
+ "Band": 0.0,
49
49
  "WPXPrefix": "",
50
50
  "Exchange1": "",
51
51
  "RadioNR": "",
@@ -60,11 +60,11 @@ class DataBase:
60
60
  "Continent": "",
61
61
  "RoverLocation": "",
62
62
  "RadioInterfaced": "",
63
- "NetworkedCompNr": "",
63
+ "NetworkedCompNr": 1,
64
64
  "NetBiosName": "",
65
- "IsOriginal": "",
65
+ "IsOriginal": 1,
66
66
  "ID": "",
67
- "CLAIMEDQSO": "",
67
+ "CLAIMEDQSO": 1,
68
68
  }
69
69
  self.database = database
70
70
  self.create_dxlog_table()
@@ -326,7 +326,7 @@ class DataBase:
326
326
  with sqlite3.connect(self.database) as conn:
327
327
  conn.row_factory = self.row_factory
328
328
  cursor = conn.cursor()
329
- cursor.execute("select * from dxlog order by ts ASC;")
329
+ cursor.execute("select * from dxlog order by TS ASC;")
330
330
  return cursor.fetchall()
331
331
 
332
332
  def fetch_all_contacts_desc(self) -> list:
not1mm/lib/ham_utility.py CHANGED
@@ -1,13 +1,38 @@
1
1
  """A sad collection of maybe useful things."""
2
2
 
3
- import socket
4
3
  import logging
4
+ import socket
5
+ import re
5
6
  from datetime import datetime
6
- from math import sin, cos, radians, asin, sqrt, atan2, pi
7
+ from math import asin, atan2, cos, pi, radians, sin, sqrt
7
8
 
8
9
  logger = logging.getLogger("__main__")
9
10
 
10
11
 
12
+ def calculate_wpx_prefix(the_call: str) -> str:
13
+ """Calculate a WPX Prefix"""
14
+ if not the_call:
15
+ return ""
16
+ suffix_to_ignore = ["M", "MM", "P", "QRP", "A", "LH", "NLD"]
17
+ result = None
18
+ working_call = the_call.split("/")
19
+ if len(working_call) > 1:
20
+ result = min(working_call, key=len)
21
+ if not result.isnumeric():
22
+ if result not in suffix_to_ignore:
23
+ if any(chr.isdigit() for chr in result):
24
+ return result
25
+ return result + "0"
26
+
27
+ working_call = max(working_call, key=len)
28
+ last_digit = re.match(".+([0-9])[^0-9]*$", working_call)
29
+ position = last_digit.start(1)
30
+ prefix = working_call[: position + 1]
31
+ if not result:
32
+ return prefix
33
+ return prefix[:-1] + result
34
+
35
+
11
36
  def gridtolatlon(maiden):
12
37
  """
13
38
  Converts a maidenhead gridsquare to a latitude longitude pair.
@@ -125,9 +150,10 @@ def update_time() -> None:
125
150
  Update local and UTC time on screen.
126
151
  """
127
152
  _now = datetime.now().isoformat(" ")[5:19].replace("-", "/")
128
- _utcnow = datetime.utcnow().isoformat(" ")[5:19].replace("-", "/")
153
+ _utcnow = datetime.utcnow().isoformat(" ")[1:19]
129
154
  # self.localtime.setText(now)
130
155
  # self.utctime.setText(utcnow)
156
+ return _utcnow
131
157
 
132
158
 
133
159
  def haversine(lon1, lat1, lon2, lat2):
not1mm/lib/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  """It's the version"""
2
- __version__ = "23.3.8"
2
+ __version__ = "23.3.15"
not1mm/logwindow.py ADDED
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Display current log
4
+ """
5
+ # pylint: disable=no-name-in-module, unused-import, no-member
6
+ # QTableWidget
7
+ # focusedLog, generalLog
8
+ import os
9
+ import pkgutil
10
+ import sys
11
+
12
+ # from json import dumps, loads
13
+ from pathlib import Path
14
+
15
+ from PyQt5 import QtCore, QtGui, QtWidgets, uic
16
+ from PyQt5.QtCore import QDir, QItemSelectionModel
17
+ from PyQt5.QtGui import QFontDatabase
18
+ from watchdog.events import FileSystemEventHandler
19
+ from watchdog.observers import Observer
20
+
21
+ from not1mm.lib.database import DataBase
22
+
23
+ loader = pkgutil.get_loader("not1mm")
24
+ WORKING_PATH = os.path.dirname(loader.get_filename())
25
+
26
+ if "XDG_DATA_HOME" in os.environ:
27
+ DATA_PATH = os.environ.get("XDG_DATA_HOME")
28
+ else:
29
+ DATA_PATH = str(Path.home() / ".local" / "share")
30
+ DATA_PATH += "/not1mm"
31
+
32
+ if "XDG_CONFIG_HOME" in os.environ:
33
+ CONFIG_PATH = os.environ.get("XDG_CONFIG_HOME")
34
+ else:
35
+ CONFIG_PATH = str(Path.home() / ".config")
36
+ CONFIG_PATH += "/not1mm"
37
+
38
+
39
+ class Watcher:
40
+ """Keep an eye out"""
41
+
42
+ def __init__(self, directory=".", handler=FileSystemEventHandler()):
43
+ self.observer = Observer()
44
+ self.handler = handler
45
+ self.directory = directory
46
+
47
+ def start(self):
48
+ """Starts the file watcher"""
49
+ self.observer.schedule(self.handler, self.directory, recursive=True)
50
+ self.observer.start()
51
+
52
+ def stop(self):
53
+ """Stops the file watcher"""
54
+ self.observer.stop()
55
+ self.observer.join()
56
+
57
+
58
+ class Handler(FileSystemEventHandler):
59
+ """Make them accoutable"""
60
+
61
+ def on_modified(self, event):
62
+ if not event.is_directory:
63
+ window.get_log()
64
+
65
+
66
+ class MainWindow(QtWidgets.QMainWindow):
67
+ """
68
+ The main window
69
+ """
70
+
71
+ dbname = DATA_PATH + "/ham.db"
72
+
73
+ def __init__(self, *args, **kwargs):
74
+ super().__init__(*args, **kwargs)
75
+ self.database = DataBase(self.dbname, WORKING_PATH)
76
+ self.contact = self.database.empty_contact
77
+ data_path = WORKING_PATH + "/data/logwindow.ui"
78
+ uic.loadUi(data_path, self)
79
+ self.generalLog.setColumnCount(11)
80
+ # item = QtWidgets.QTableWidgetItem("")
81
+ self.generalLog.setHorizontalHeaderItem(0, QtWidgets.QTableWidgetItem("MM-DD"))
82
+ self.generalLog.setHorizontalHeaderItem(1, QtWidgets.QTableWidgetItem("HH:MM"))
83
+ self.generalLog.setHorizontalHeaderItem(2, QtWidgets.QTableWidgetItem("Call"))
84
+ self.generalLog.setHorizontalHeaderItem(3, QtWidgets.QTableWidgetItem("Freq"))
85
+ self.generalLog.setHorizontalHeaderItem(4, QtWidgets.QTableWidgetItem("Snt"))
86
+ self.generalLog.setHorizontalHeaderItem(5, QtWidgets.QTableWidgetItem("Rcv"))
87
+ self.generalLog.setHorizontalHeaderItem(6, QtWidgets.QTableWidgetItem("M1"))
88
+ self.generalLog.setHorizontalHeaderItem(7, QtWidgets.QTableWidgetItem("ZN"))
89
+ self.generalLog.setHorizontalHeaderItem(8, QtWidgets.QTableWidgetItem("M2"))
90
+ self.generalLog.setHorizontalHeaderItem(9, QtWidgets.QTableWidgetItem("PFX"))
91
+ self.generalLog.setHorizontalHeaderItem(10, QtWidgets.QTableWidgetItem("PTS"))
92
+ path = sys.argv[1] if len(sys.argv) > 1 else DATA_PATH + "/ham.db"
93
+ watcher = Watcher(path, Handler())
94
+ watcher.start()
95
+ self.get_log()
96
+
97
+ def get_log(self):
98
+ """Get Log, Show it."""
99
+ current_log = self.database.fetch_all_contacts_asc()
100
+ self.generalLog.setRowCount(0)
101
+ for log_item in current_log:
102
+ number_of_rows = self.generalLog.rowCount()
103
+ self.generalLog.insertRow(number_of_rows)
104
+ time_stamp = log_item.get("TS", "MM-DD HH:MM")
105
+ month_day, hour_min = time_stamp.split(" ")
106
+ _, month, day = month_day.split("-")
107
+ hour, minute, _ = hour_min.split(":")
108
+ month_day = f"{month}-{day}"
109
+ hour_min = f"{hour}:{minute}"
110
+
111
+ first_item = QtWidgets.QTableWidgetItem(month_day)
112
+ self.generalLog.setItem(number_of_rows, 0, first_item)
113
+ self.generalLog.setCurrentItem(first_item, QItemSelectionModel.NoUpdate)
114
+ self.generalLog.setItem(
115
+ number_of_rows, 1, QtWidgets.QTableWidgetItem(hour_min)
116
+ )
117
+
118
+ self.generalLog.setItem(
119
+ number_of_rows,
120
+ 2,
121
+ QtWidgets.QTableWidgetItem(str(log_item.get("Call", ""))),
122
+ )
123
+ self.generalLog.setItem(
124
+ number_of_rows,
125
+ 3,
126
+ QtWidgets.QTableWidgetItem(str(log_item.get("Freq", ""))),
127
+ )
128
+ self.generalLog.setItem(
129
+ number_of_rows,
130
+ 4,
131
+ QtWidgets.QTableWidgetItem(str(log_item.get("SNT", ""))),
132
+ )
133
+ self.generalLog.setItem(
134
+ number_of_rows,
135
+ 5,
136
+ QtWidgets.QTableWidgetItem(str(log_item.get("RCV", ""))),
137
+ )
138
+ self.generalLog.setItem(
139
+ number_of_rows,
140
+ 6,
141
+ QtWidgets.QTableWidgetItem(str(log_item.get("IsMultiplier1", ""))),
142
+ )
143
+ self.generalLog.setItem(
144
+ number_of_rows,
145
+ 7,
146
+ QtWidgets.QTableWidgetItem(str(log_item.get("ZN", ""))),
147
+ )
148
+ self.generalLog.setItem(
149
+ number_of_rows,
150
+ 8,
151
+ QtWidgets.QTableWidgetItem(str(log_item.get("IsMultiplier2", ""))),
152
+ )
153
+ self.generalLog.setItem(
154
+ number_of_rows,
155
+ 9,
156
+ QtWidgets.QTableWidgetItem(str(log_item.get("WPXPrefix", ""))),
157
+ )
158
+ self.generalLog.setItem(
159
+ number_of_rows,
160
+ 10,
161
+ QtWidgets.QTableWidgetItem(str(log_item.get("Points", ""))),
162
+ )
163
+
164
+
165
+ def load_fonts_from_dir(directory: str) -> set:
166
+ """
167
+ Well it loads fonts from a directory...
168
+ """
169
+ font_families = set()
170
+ for _fi in QDir(directory).entryInfoList(["*.ttf", "*.woff", "*.woff2"]):
171
+ _id = QFontDatabase.addApplicationFont(_fi.absoluteFilePath())
172
+ font_families |= set(QFontDatabase.applicationFontFamilies(_id))
173
+ return font_families
174
+
175
+
176
+ def main():
177
+ """main entry"""
178
+ sys.exit(app.exec())
179
+
180
+
181
+ if __name__ == "__main__":
182
+ app = QtWidgets.QApplication(sys.argv)
183
+ font_path = WORKING_PATH + "/data"
184
+ _families = load_fonts_from_dir(os.fspath(font_path))
185
+ window = MainWindow()
186
+ window.setWindowTitle("Log Display")
187
+ window.show()
188
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: not1mm
3
- Version: 23.3.8
3
+ Version: 23.3.15
4
4
  Summary: NOT1MM Logger
5
5
  Author-email: Michael Bridak <michael.bridak@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/mbridak/not1mm
@@ -20,6 +20,7 @@ Requires-Dist: PyQt5
20
20
  Requires-Dist: requests
21
21
  Requires-Dist: dicttoxml
22
22
  Requires-Dist: xmltodict
23
+ Requires-Dist: watchdog
23
24
 
24
25
  # Not1MM
25
26
 
@@ -41,6 +42,7 @@ Requires-Dist: xmltodict
41
42
  - [cty.dat and QRZ lookups for distance and bearing](#ctydat-and-qrz-lookups-for-distance-and-bearing)
42
43
  - [Settings dialog](#settings-dialog)
43
44
  - [Other uses for the call field](#other-uses-for-the-call-field)
45
+ - [Log Display](#log-display)
44
46
 
45
47
  ## What and why is Not1MM
46
48
 
@@ -69,10 +71,13 @@ Feature complete.
69
71
 
70
72
  ## Changes of note
71
73
 
72
- - [23.3.8] Band/Frequency/Mode indicators. Direct frequency/mode entry in call field.
73
- - [23.3.7] Changed dxlog table column names.
74
- - [23.3.1] Add shift tab for field movement.
75
- - [23.2.23] Dialogs now do darkmode, Add settings dialog. App remembers window size and location.
74
+ - [23-3-15] Added a rudimentary log view window.
75
+ - [23-3-10] Started work on saving contacts to the DB. Added a claculate_wpx_prefix routine.
76
+ - [23-3-9] Placed network call lookup in a thread. Display freq/mode for non CAT radios. Hooked up the CW macros to cwdaemon.
77
+ - [23-3-8] Band/Frequency/Mode indicators. Direct frequency/mode entry in call field.
78
+ - [23-3-7] Changed dxlog table column names.
79
+ - [23-3-1] Add shift tab for field movement.
80
+ - [23-2-23] Dialogs now do darkmode, Add settings dialog. App remembers window size and location.
76
81
  - [23-2-22] Added cty.dat file.
77
82
  - [23-2-21] Added edit macro dialog.
78
83
  - [23-2-20] Save view states. fixed debug messages. Started coding plugins/stubs.
@@ -149,3 +154,9 @@ You can fill. You can fill. Everyone look at your keys.
149
154
  - [OPON] Change the operator currently logging.
150
155
 
151
156
  **You must press the SPACE bar after entering any of the above.**
157
+
158
+ ## Log Display
159
+
160
+ At this point the log display window doesn't do all that much except exist.
161
+
162
+ ![Log Display Window](https://github.com/mbridak/not1mm/raw/master/pic/logdisplay.png)
@@ -1,5 +1,6 @@
1
1
  not1mm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- not1mm/__main__.py,sha256=tJ5H9Tg-_81D2ZHoc68estUtG7deV-aUE8txzzh_jys,38753
2
+ not1mm/__main__.py,sha256=CeZZAz4qQm39_azVP8gpqCpF-pr0ZzHvy7QgIVh7VIA,43790
3
+ not1mm/logwindow.py,sha256=stL0majNjXnlqJfcsBWxh_O7uJqMTmB7SB2DSV_BgOo,6487
3
4
  not1mm/test.py,sha256=sCAd6OZQlakGm2gMbjhwZ17UyI-Y5L2CCqsYymx7IPQ,3906
4
5
  not1mm/data/Combinear.qss,sha256=SKqM0g8GvNXay1ovgtwCw3Egt0eLfN5P3iTREInC1eE,16590
5
6
  not1mm/data/JetBrainsMono-Regular.ttf,sha256=UOHctAKY_PzCGh7zy-6f6egnCcSK0wzmF0csBqO9lDY,203952
@@ -13,7 +14,8 @@ not1mm/data/k6gte-not1mm.desktop,sha256=bSiSG7PzGygv0bBaF7Nf48PApVyrobSBVNk7j7wR
13
14
  not1mm/data/k6gte.not1mm-128.png,sha256=vWqt3Cgsaguj-BBiIoSJApzzhisPxldM8HZQbZ05z6Y,4010
14
15
  not1mm/data/k6gte.not1mm-32.png,sha256=yucSwzlmqv3NegdWUvPvZzSgP7G22Ky3se8TWRXvzfI,1108
15
16
  not1mm/data/k6gte.not1mm-64.png,sha256=1KQvk0WBckUds79BvIFUt-KdTwQKKvTz6hiJu8MiT68,2152
16
- not1mm/data/main.ui,sha256=PVPGU8FhmPNwYb9zKe2V5PyC95MpRej1RphCQUtzDDQ,26359
17
+ not1mm/data/logwindow.ui,sha256=Ujeqppqm-U53CRKzkesiQ3kkCooYIv-uixCYEedvs7A,971
18
+ not1mm/data/main.ui,sha256=YelX04sNlp5Wu_Hq06fr5Ezscj4kjpLX5GIoKsHpW4E,28116
17
19
  not1mm/data/opon.ui,sha256=6r9_6ORGfNqwOnpzQjaJ1tWP_81amuXqLYlx1hHgdME,2018
18
20
  not1mm/data/reddot.png,sha256=M33jEMoU8W4rQ4_MVyzzKxDPDte1ypKBch5VnUMNLKE,565
19
21
  not1mm/data/settings.ui,sha256=egXLgKAKKwwlqE8t0D8aXaP-4mRAV3EAcuSTnAeVwFc,12459
@@ -21,15 +23,15 @@ not1mm/data/use_qrz_dialog.ui,sha256=sXV1K1tsmYEF7K8nnV_t_UR3k1PzQp43CgvX4KSnR94
21
23
  not1mm/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
24
  not1mm/lib/cat_interface.py,sha256=mYkDV80gMDuchkEMKyf7geroplGJ1Ne8cEBoH9nrq3o,10311
23
25
  not1mm/lib/cwinterface.py,sha256=i9VCxORHXYOK44Rcco7deHXdmTyrI1lA_pKm-kpdxtE,1748
24
- not1mm/lib/database.py,sha256=Yke4S78Vc8xPejzPVj0o0jOomU9xomTLAgOKu9Bf4jU,13987
26
+ not1mm/lib/database.py,sha256=fIJHL0FaqxgCNqfpP6EL2l7_XSToU1TYCppfEZ4WgQQ,13977
25
27
  not1mm/lib/edit_macro.py,sha256=lyToZb1nmcONNFh6W35NzYHLU48gbAs2_OsnuRQGHck,559
26
28
  not1mm/lib/edit_opon.py,sha256=GqAOJMiC265_4YRVbkT1PflBuCa4HiCNfRhq8-J6ZVY,363
27
29
  not1mm/lib/edit_settings.py,sha256=lFTUDB-BjuV57QPMn8EqWjks-uAB1xripV4FBduQIDA,727
28
- not1mm/lib/ham_utility.py,sha256=_ZbzCguJkBlqyXULBwcTO6hp8yR5FFbsdMIEpI2y0K0,6571
30
+ not1mm/lib/ham_utility.py,sha256=PEF9ZU_HI3HeDsoIfxD6sx-Qdb3SYKyqLPILYdq3bn8,7367
29
31
  not1mm/lib/lookup.py,sha256=4OiWZWc3smcB0lqLk4WTs1U3i2k8yZFsX_1OoCGlaWc,13916
30
32
  not1mm/lib/n1mm.py,sha256=zfz0PU-iEz9dd6-wnIHXvPAmmxSYJ2bzk_MgnsIX3Sw,4461
31
33
  not1mm/lib/qrz_dialog.py,sha256=fLFBkvQ9cuIqKk0-1yl5OFuQ40n904fNi_Vt23je99E,350
32
- not1mm/lib/version.py,sha256=QbTy-5mSYnc1yFhyYDrL0nHoxaQotCRaR9MI-bbL7qI,46
34
+ not1mm/lib/version.py,sha256=A6ywWYCqMVtSmnrAAoGWfNhZe3G67p542uCjfZ-HLz0,47
33
35
  not1mm/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
36
  not1mm/plugins/arrl_dx_cw.py,sha256=VRP-H65SgEaCff6efF85DzU5KMoECkLQad4DraNgUec,1382
35
37
  not1mm/plugins/arrl_dx_phone.py,sha256=9uLZWogzTfLm0SjmcG149dTG_GjDbbVhdInl4Uz7Fck,1385
@@ -41,9 +43,9 @@ not1mm/plugins/cqww_dx_cw.py,sha256=rr0vqgIqQ9kErxL8pcVDQh60lkIad5f049270_cF2FQ,
41
43
  not1mm/plugins/cqww_dx_ssb.py,sha256=ejT_V5WSYbtaIBqSkSSAEmIVECA0g9Z2nMQ0d3xKpXw,1392
42
44
  not1mm/plugins/general_logging.py,sha256=41iuEN8mnLjIIK4SBnwG0RGp2Or9ue-x-j-wZI8MYGU,1630
43
45
  not1mm/plugins/winter_field_day.py,sha256=L7co3YEoKt-jaAFJfGw_Y3-uK-G6eQNsOQSCEkT5U24,1631
44
- not1mm-23.3.8.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
45
- not1mm-23.3.8.dist-info/METADATA,sha256=yqoZMc4fo9dPnDHv5uLaRQ_4lN3CboUHMjOjz8cdaAQ,6402
46
- not1mm-23.3.8.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
47
- not1mm-23.3.8.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
48
- not1mm-23.3.8.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
49
- not1mm-23.3.8.dist-info/RECORD,,
46
+ not1mm-23.3.15.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
47
+ not1mm-23.3.15.dist-info/METADATA,sha256=lBNy3ePPu7B_OxRl5KanH-JJzaM4b0h4-ZyhDrGpSzU,6908
48
+ not1mm-23.3.15.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
49
+ not1mm-23.3.15.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
50
+ not1mm-23.3.15.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
51
+ not1mm-23.3.15.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.38.4)
2
+ Generator: bdist_wheel (0.40.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5