not1mm 24.3.19__py3-none-any.whl → 24.3.21__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.
Files changed (66) hide show
  1. not1mm/__main__.py +289 -123
  2. not1mm/bandmap.py +168 -163
  3. not1mm/checkwindow.py +55 -107
  4. not1mm/data/MASTER.SCP +1077 -749
  5. not1mm/data/bandmap.ui +4 -4
  6. not1mm/data/checkwindow.ui +9 -16
  7. not1mm/data/logwindow.ui +16 -11
  8. not1mm/data/main.ui +16 -6
  9. not1mm/data/vfo.ui +64 -52
  10. not1mm/data/vfo2.ui +84 -0
  11. not1mm/fsutils.py +38 -0
  12. not1mm/lib/about.py +4 -2
  13. not1mm/lib/cat_interface.py +1 -1
  14. not1mm/lib/cwinterface.py +1 -1
  15. not1mm/lib/database.py +4 -4
  16. not1mm/lib/edit_contact.py +2 -2
  17. not1mm/lib/edit_macro.py +2 -3
  18. not1mm/lib/edit_opon.py +2 -2
  19. not1mm/lib/edit_station.py +3 -3
  20. not1mm/lib/ham_utility.py +1 -1
  21. not1mm/lib/lookup.py +1 -1
  22. not1mm/lib/multicast.py +9 -4
  23. not1mm/lib/n1mm.py +1 -1
  24. not1mm/lib/new_contest.py +2 -2
  25. not1mm/lib/select_contest.py +2 -2
  26. not1mm/lib/settings.py +3 -4
  27. not1mm/lib/super_check_partial.py +6 -5
  28. not1mm/lib/version.py +1 -1
  29. not1mm/logwindow.py +60 -104
  30. not1mm/plugins/10_10_fall_cw.py +1 -1
  31. not1mm/plugins/10_10_spring_cw.py +1 -1
  32. not1mm/plugins/10_10_summer_phone.py +1 -1
  33. not1mm/plugins/10_10_winter_phone.py +1 -1
  34. not1mm/plugins/arrl_10m.py +1 -1
  35. not1mm/plugins/arrl_dx_cw.py +1 -1
  36. not1mm/plugins/arrl_dx_ssb.py +1 -1
  37. not1mm/plugins/arrl_field_day.py +1 -1
  38. not1mm/plugins/arrl_ss_cw.py +1 -1
  39. not1mm/plugins/arrl_ss_phone.py +1 -1
  40. not1mm/plugins/arrl_vhf_jan.py +1 -1
  41. not1mm/plugins/arrl_vhf_jun.py +1 -1
  42. not1mm/plugins/arrl_vhf_sep.py +1 -1
  43. not1mm/plugins/canada_day.py +1 -1
  44. not1mm/plugins/cq_160_cw.py +1 -1
  45. not1mm/plugins/cq_160_ssb.py +1 -1
  46. not1mm/plugins/cq_wpx_cw.py +1 -1
  47. not1mm/plugins/cq_wpx_ssb.py +1 -1
  48. not1mm/plugins/cq_ww_cw.py +1 -1
  49. not1mm/plugins/cq_ww_ssb.py +1 -1
  50. not1mm/plugins/cwt.py +1 -1
  51. not1mm/plugins/general_logging.py +1 -1
  52. not1mm/plugins/iaru_hf.py +1 -1
  53. not1mm/plugins/jidx_cw.py +1 -1
  54. not1mm/plugins/jidx_ph.py +1 -1
  55. not1mm/plugins/naqp_cw.py +1 -1
  56. not1mm/plugins/naqp_ssb.py +1 -1
  57. not1mm/plugins/phone_weekly_test.py +1 -1
  58. not1mm/plugins/stew_perry_topband.py +1 -1
  59. not1mm/plugins/winter_field_day.py +1 -1
  60. not1mm/vfo.py +77 -98
  61. {not1mm-24.3.19.dist-info → not1mm-24.3.21.dist-info}/METADATA +3 -1
  62. {not1mm-24.3.19.dist-info → not1mm-24.3.21.dist-info}/RECORD +66 -64
  63. {not1mm-24.3.19.dist-info → not1mm-24.3.21.dist-info}/LICENSE +0 -0
  64. {not1mm-24.3.19.dist-info → not1mm-24.3.21.dist-info}/WHEEL +0 -0
  65. {not1mm-24.3.19.dist-info → not1mm-24.3.21.dist-info}/entry_points.txt +0 -0
  66. {not1mm-24.3.19.dist-info → not1mm-24.3.21.dist-info}/top_level.txt +0 -0
not1mm/bandmap.py CHANGED
@@ -8,55 +8,25 @@ GPL V3
8
8
  # pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines
9
9
  # pylint: disable=logging-fstring-interpolation, line-too-long
10
10
 
11
- from datetime import datetime, timezone
12
- from decimal import Decimal
13
- from json import loads
14
- from pathlib import Path
15
-
16
11
  import logging
17
12
  import os
18
-
19
- # import pkgutil
20
13
  import platform
21
- import sys
22
14
  import sqlite3
15
+ from datetime import datetime, timezone
16
+ from decimal import Decimal
17
+ from json import loads
23
18
 
24
- from PyQt5 import QtCore, QtGui
25
- from PyQt5 import QtNetwork
26
- from PyQt5 import QtWidgets, uic
19
+ from PyQt5 import QtCore, QtGui, QtWidgets, uic, QtNetwork
20
+ from PyQt5.QtCore import Qt
27
21
 
22
+ import not1mm.fsutils as fsutils
28
23
  from not1mm.lib.multicast import Multicast
29
24
 
30
- os.environ["QT_QPA_PLATFORMTHEME"] = "gnome"
25
+ logger = logging.getLogger(__name__)
31
26
 
32
27
  PIXELSPERSTEP = 10
33
28
  UPDATE_INTERVAL = 2000
34
29
 
35
- # DeprecationWarning: 'pkgutil.get_loader' is deprecated and slated for removal in Python 3.14
36
- # loader = pkgutil.get_loader("not1mm")
37
- # WORKING_PATH = os.path.dirname(loader.get_filename())
38
- WORKING_PATH = os.path.dirname(__loader__.get_filename())
39
-
40
- if "XDG_DATA_HOME" in os.environ:
41
- DATA_PATH = os.environ.get("XDG_DATA_HOME")
42
- else:
43
- DATA_PATH = str(Path.home() / ".local" / "share")
44
- DATA_PATH += "/not1mm"
45
-
46
- if "XDG_CONFIG_HOME" in os.environ:
47
- CONFIG_PATH = os.environ.get("XDG_CONFIG_HOME")
48
- else:
49
- CONFIG_PATH = str(Path.home() / ".config")
50
- CONFIG_PATH += "/not1mm"
51
-
52
- DARK_STYLESHEET = ""
53
-
54
- PREF = {}
55
- if os.path.exists(CONFIG_PATH + "/not1mm.json"):
56
- with open(CONFIG_PATH + "/not1mm.json", "rt", encoding="utf-8") as file_descriptor:
57
- PREF = loads(file_descriptor.read())
58
-
59
-
60
30
  class Band:
61
31
  """the band"""
62
32
 
@@ -98,6 +68,8 @@ class Band:
98
68
  self.altname = self.othername.get(band, 0.0)
99
69
 
100
70
 
71
+
72
+
101
73
  class Database:
102
74
  """
103
75
  An in memory Database class to hold spots.
@@ -108,18 +80,24 @@ class Database:
108
80
  self.db.row_factory = self.row_factory
109
81
  self.cursor = self.db.cursor()
110
82
  sql_command = (
111
- "create table spots("
112
- "ts DATETIME NOT NULL, "
83
+ "create table spots ("
113
84
  "callsign VARCHAR(15) NOT NULL, "
85
+ "ts DATETIME NOT NULL, "
114
86
  "freq DOUBLE NOT NULL, "
115
- "band VARCHAR(6), "
116
87
  "mode VARCHAR(6), "
117
88
  "spotter VARCHAR(15) NOT NULL, "
118
89
  "comment VARCHAR(45));"
119
90
  )
120
91
  self.cursor.execute(sql_command)
92
+
93
+ self.cursor.execute('CREATE INDEX spot_call_index ON spots (callsign);')
94
+ self.cursor.execute('CREATE INDEX spot_freq_index ON spots (freq);')
95
+ self.cursor.execute('CREATE INDEX spot_ts_index ON spots (ts);')
96
+
121
97
  self.db.commit()
122
98
 
99
+
100
+
123
101
  @staticmethod
124
102
  def row_factory(cursor, row):
125
103
  """
@@ -175,31 +153,19 @@ class Database:
175
153
  If False, do not delete any previous spots with the same callsign.
176
154
  Default is True.
177
155
 
178
-
179
156
  Returns
180
157
  -------
181
158
  Nothing.
182
159
  """
183
160
  try:
184
161
  if erase:
185
- delete_call = (
186
- f"delete from spots where callsign = '{spot.get('callsign')}';"
187
- )
188
- self.cursor.execute(delete_call)
162
+ delete_call = ("delete from spots where callsign = ?;")
163
+ self.cursor.execute(delete_call, (spot.get('callsign'),))
189
164
  self.db.commit()
190
165
 
191
- pre = "INSERT INTO spots("
192
- values = []
193
- columns = ""
194
- placeholders = ""
195
- for key in spot.keys():
196
- columns += f"{key},"
197
- values.append(spot[key])
198
- placeholders += "?,"
199
- post = f") VALUES({placeholders[:-1]});"
200
-
201
- sql = f"{pre}{columns[:-1]}{post}"
202
- self.cursor.execute(sql, tuple(values))
166
+ self.cursor.execute(
167
+ "INSERT INTO spots(callsign, ts, freq, mode, spotter, comment) VALUES(?, ?, ?, ?, ?, ?)",
168
+ (spot['callsign'], spot['ts'], spot['freq'], spot.get('mode', None), spot['spotter'], spot.get('comment', None)))
203
169
  self.db.commit()
204
170
  except sqlite3.IntegrityError:
205
171
  ...
@@ -239,7 +205,8 @@ class Database:
239
205
  A list of dicts.
240
206
  """
241
207
  self.cursor.execute(
242
- f"select * from spots where freq >= {start} and freq <= {end} order by freq ASC;"
208
+ "select * from spots where freq >= ? and freq <= ? order by freq ASC;",
209
+ (start, end)
243
210
  )
244
211
  return self.cursor.fetchall()
245
212
 
@@ -261,7 +228,9 @@ class Database:
261
228
  """
262
229
 
263
230
  self.cursor.execute(
264
- f"select * from spots where freq > {current} and freq <= {limit} order by freq ASC;"
231
+ "select * from spots where freq > ? and freq <= ? order by freq ASC;",
232
+ ({current}, limit)
233
+
265
234
  )
266
235
  return self.cursor.fetchone()
267
236
 
@@ -284,8 +253,8 @@ class Database:
284
253
  """
285
254
 
286
255
  self.cursor.execute(
287
- f"select * from spots where freq >= {start} "
288
- f"and freq <= {end} and callsign like '%{dx}%';"
256
+ "select * from spots where freq >= ? and freq <= ? and callsign like ?;",
257
+ (start, end, f'%{dx}%')
289
258
  )
290
259
  return self.cursor.fetchone()
291
260
 
@@ -305,7 +274,8 @@ class Database:
305
274
  A list of dicts.
306
275
  """
307
276
  self.cursor.execute(
308
- f"select * from spots where freq < {current} and freq >= {limit} order by freq DESC;"
277
+ f"select * from spots where freq < ? and freq >= ? order by freq DESC;",
278
+ (current, limit)
309
279
  )
310
280
  return self.cursor.fetchone()
311
281
 
@@ -323,14 +293,12 @@ class Database:
323
293
  None
324
294
  """
325
295
  self.cursor.execute(
326
- f"delete from spots where ts < datetime('now', '-{minutes} minutes');"
296
+ f"delete from spots where ts < datetime('now', ?);",
297
+ (f'-{minutes} minutes',)
327
298
  )
328
299
 
329
300
 
330
- class MainWindow(QtWidgets.QMainWindow):
331
- """
332
- The main window
333
- """
301
+ class BandMapWindow(QtWidgets.QDockWidget):
334
302
 
335
303
  zoom = 5
336
304
  currentBand = Band("20m")
@@ -346,14 +314,14 @@ class MainWindow(QtWidgets.QMainWindow):
346
314
  bandwidth_mark = []
347
315
  worked_list = {}
348
316
  multicast_interface = None
317
+ text_color = QtGui.QColor(45, 45, 45)
349
318
 
350
319
  def __init__(self, *args, **kwargs):
351
320
  super().__init__(*args, **kwargs)
352
321
  self._udpwatch = None
353
- data_path = WORKING_PATH + "/data/bandmap.ui"
354
- uic.loadUi(data_path, self)
355
- if PREF.get("dark_mode"):
356
- self.setStyleSheet(DARK_STYLESHEET)
322
+
323
+ uic.loadUi(fsutils.APP_DATA_PATH / "bandmap.ui", self)
324
+ self.settings = self.get_settings()
357
325
  self.agetime = self.clear_spot_olderSpinBox.value()
358
326
  self.clear_spot_olderSpinBox.valueChanged.connect(self.spot_aging_changed)
359
327
  self.clearButton.clicked.connect(self.clear_spots)
@@ -375,35 +343,74 @@ class MainWindow(QtWidgets.QMainWindow):
375
343
  self.update_timer = QtCore.QTimer()
376
344
  self.update_timer.timeout.connect(self.update_station_timer)
377
345
  self.update_timer.start(UPDATE_INTERVAL)
346
+ self.setDarkMode(self.settings.get("darkmode", False))
378
347
  self.update()
379
348
  self.multicast_interface = Multicast(
380
- PREF.get("multicast_group", "239.1.1.1"),
381
- PREF.get("multicast_port", 2239),
382
- PREF.get("interface_ip", "0.0.0.0"),
349
+ self.settings.get("multicast_group", "239.1.1.1"),
350
+ self.settings.get("multicast_port", 2239),
351
+ self.settings.get("interface_ip", "0.0.0.0"),
383
352
  )
384
353
  self.multicast_interface.ready_read_connect(self.watch_udp)
385
354
  self.request_workedlist()
355
+ self.request_contest()
356
+
357
+
358
+ def get_settings(self) -> dict:
359
+ if os.path.exists(fsutils.CONFIG_FILE):
360
+ with open(fsutils.CONFIG_FILE, "rt", encoding="utf-8") as file_descriptor:
361
+ return loads(file_descriptor.read())
362
+
363
+ def setDarkMode(self, setdarkmode=False):
364
+ """testing"""
365
+
366
+ if setdarkmode:
367
+ darkPalette = QtGui.QPalette()
368
+ darkColor = QtGui.QColor(45, 45, 45)
369
+ self.text_color = Qt.white
370
+ disabledColor = QtGui.QColor(127, 127, 127)
371
+ darkPalette.setColor(QtGui.QPalette.Window, darkColor)
372
+ darkPalette.setColor(QtGui.QPalette.WindowText, Qt.white)
373
+ darkPalette.setColor(QtGui.QPalette.Base, QtGui.QColor(18, 18, 18))
374
+ darkPalette.setColor(QtGui.QPalette.AlternateBase, darkColor)
375
+ darkPalette.setColor(QtGui.QPalette.Text, Qt.white)
376
+ darkPalette.setColor(
377
+ QtGui.QPalette.Disabled, QtGui.QPalette.Text, disabledColor
378
+ )
379
+ darkPalette.setColor(QtGui.QPalette.Button, darkColor)
380
+ darkPalette.setColor(QtGui.QPalette.ButtonText, Qt.white)
381
+ darkPalette.setColor(
382
+ QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, disabledColor
383
+ )
384
+ darkPalette.setColor(QtGui.QPalette.BrightText, Qt.red)
385
+ darkPalette.setColor(QtGui.QPalette.Link, QtGui.QColor(42, 130, 218))
386
+ darkPalette.setColor(QtGui.QPalette.Highlight, QtGui.QColor(42, 130, 218))
387
+ darkPalette.setColor(QtGui.QPalette.HighlightedText, Qt.black)
388
+ darkPalette.setColor(
389
+ QtGui.QPalette.Disabled, QtGui.QPalette.HighlightedText, disabledColor
390
+ )
386
391
 
387
- def quit_app(self):
388
- """doc"""
389
- app.quit()
392
+ self.setPalette(darkPalette)
393
+ self.callsignField.setPalette(darkPalette)
394
+ self.update()
395
+ else:
396
+ palette = self.style().standardPalette()
397
+ self.setPalette(palette)
398
+ self.callsignField.setPalette(palette)
399
+ self.text_color = Qt.black
400
+ self.update()
390
401
 
391
402
  def connect(self):
392
- """doc"""
403
+ if not self.callsignField.text():
404
+ self.callsignField.setFocus()
405
+ return
393
406
  if self.connected is True:
394
- self.socket.close()
395
- self.connected = False
396
- self.connectButton.setStyleSheet("color: red;")
397
- self.connectButton.setText("Closed")
407
+ self.close_cluster()
398
408
  return
399
- if os.path.exists(CONFIG_PATH + "/not1mm.json"):
400
- with open(
401
- CONFIG_PATH + "/not1mm.json", "rt", encoding="utf-8"
402
- ) as _file_descriptor:
403
- globals()["PREF"] = loads(_file_descriptor.read())
404
- server = PREF.get("cluster_server", "dxc.nc7j.com")
405
- port = PREF.get("cluster_port", 7373)
406
- logger.debug("%s", f"{server} {port}")
409
+ # refresh settings
410
+ self.settings = self.get_settings()
411
+ server = self.settings.get("cluster_server", "dxc.nc7j.com")
412
+ port = self.settings.get("cluster_port", 7373)
413
+ logger.info(f"connecting to dx cluster {server} {port}")
407
414
  self.socket.connectToHost(server, port)
408
415
  self.connectButton.setStyleSheet("color: white;")
409
416
  self.connectButton.setText("Connecting")
@@ -514,8 +521,13 @@ class MainWindow(QtWidgets.QMainWindow):
514
521
  cmd["spots"] = []
515
522
  self.multicast_interface.send_as_json(cmd)
516
523
  continue
517
- if packet.get("cmd", "") == "HALT":
518
- self.quit_app()
524
+ if packet.get("cmd", "") == "CONTESTSTATUS":
525
+ if not self.callsignField.text():
526
+ self.callsignField.setText(packet.get("operator", "").upper())
527
+ self.callsignField.selectAll()
528
+ continue
529
+ if packet.get("cmd", "") == "DARKMODE":
530
+ self.setDarkMode(packet.get("state", False))
519
531
 
520
532
  def spot_clicked(self):
521
533
  """dunno"""
@@ -536,13 +548,23 @@ class MainWindow(QtWidgets.QMainWindow):
536
548
  cmd["station"] = platform.node()
537
549
  self.multicast_interface.send_as_json(cmd)
538
550
 
551
+ def request_contest(self):
552
+ """Request active contest from logger"""
553
+ cmd = {}
554
+ cmd["cmd"] = "GETCONTESTSTATUS"
555
+ cmd["station"] = platform.node()
556
+ self.multicast_interface.send_as_json(cmd)
557
+
539
558
  def update_station_timer(self):
540
559
  """doc"""
541
560
  self.update_stations()
542
561
 
543
562
  def update(self):
544
563
  """doc"""
545
- self.update_timer.setInterval(UPDATE_INTERVAL)
564
+ try:
565
+ self.update_timer.setInterval(UPDATE_INTERVAL)
566
+ except AttributeError:
567
+ ...
546
568
  self.clear_all_callsign_from_scene()
547
569
  self.clear_freq_mark(self.rxMark)
548
570
  self.clear_freq_mark(self.txMark)
@@ -562,12 +584,13 @@ class MainWindow(QtWidgets.QMainWindow):
562
584
  i * PIXELSPERSTEP,
563
585
  length + 10,
564
586
  i * PIXELSPERSTEP,
565
- QtGui.QPen(QtGui.QColor(192, 192, 192)),
587
+ QtGui.QPen(self.text_color),
566
588
  )
567
589
  if i % 5 == 0: # Add Frequency
568
590
  freq = self.currentBand.start + step * i
569
591
  text = f"{freq:.3f}"
570
592
  self.something = self.bandmap_scene.addText(text)
593
+ self.something.setDefaultTextColor(self.text_color)
571
594
  self.something.setPos(
572
595
  -(self.something.boundingRect().width()) + 10,
573
596
  i * PIXELSPERSTEP - (self.something.boundingRect().height() / 2),
@@ -610,7 +633,7 @@ class MainWindow(QtWidgets.QMainWindow):
610
633
 
611
634
  def Freq2ScenePos(self, freq: float):
612
635
  """doc"""
613
- if freq < self.currentBand.start or freq > self.currentBand.end:
636
+ if not freq or freq < self.currentBand.start or freq > self.currentBand.end:
614
637
  return QtCore.QPointF()
615
638
  step, _digits = self.determine_step_digits()
616
639
  ret = QtCore.QPointF(
@@ -687,16 +710,21 @@ class MainWindow(QtWidgets.QMainWindow):
687
710
  def update_stations(self):
688
711
  """doc"""
689
712
  self.update_timer.setInterval(UPDATE_INTERVAL)
713
+ if not self.connected:
714
+ return
715
+
690
716
  self.clear_all_callsign_from_scene()
691
717
  self.spot_aging()
692
718
  step, _digits = self.determine_step_digits()
693
719
 
694
720
  result = self.spots.getspotsinband(self.currentBand.start, self.currentBand.end)
721
+ logger.debug(f"{len(result)} spots in range {self.currentBand.start} - {self.currentBand.end}")
722
+
695
723
  entity = ""
696
724
  if result:
697
725
  min_y = 0.0
698
726
  for items in result:
699
- pen_color = QtGui.QColor(192, 192, 192)
727
+ pen_color = self.text_color
700
728
  if items.get("comment") == "MARKED":
701
729
  pen_color = QtGui.QColor(47, 47, 255)
702
730
  if items.get("callsign") in self.worked_list:
@@ -786,44 +814,41 @@ class MainWindow(QtWidgets.QMainWindow):
786
814
  currentPolygon.clear()
787
815
 
788
816
  def receive(self):
789
- """doc"""
790
- data = self.socket.readAll()
791
- data = str(data, "utf-8").strip()
792
- if "login:" in data:
793
- self.send_command(self.callsignField.text())
794
- self.send_command(PREF.get("cluster_filter", ""))
795
- self.send_command("set dx extension Section")
796
- self.send_command("set dx mode " + PREF.get("cluster_mode", "OPEN"))
797
- return
798
- if "call:" in data or "callsign:" in data:
799
- self.send_command(self.callsignField.text())
800
- self.send_command(PREF.get("cluster_filter", ""))
801
- self.send_command("set dx extension Section")
802
- self.send_command("set dx mode " + PREF.get("cluster_mode", "OPEN"))
803
- return
804
- if "DX de" in data:
805
- parts = data.split()
806
- spotter = parts[2]
807
- freq = parts[3]
808
- dx = parts[4]
809
- _time = parts[-1]
810
- comment = " ".join(parts[5:-1])
811
- # spot = DxSpot()
812
- spot = {}
813
- spot["ts"] = datetime.now(timezone.utc).isoformat(" ")[:19]
814
- spot["callsign"] = dx
815
- spot["spotter"] = spotter
816
- spot["comment"] = comment
817
- try:
818
- spot["freq"] = float(freq) / 1000
819
- except ValueError:
820
- logger.debug("%s", f"{data}")
821
- self.spots.addspot(spot)
822
- return
823
- if self.callsignField.text().upper() in data:
824
- self.connectButton.setStyleSheet("color: green;")
825
- self.connectButton.setText("Connected")
826
- logger.debug("%s", f"{data}")
817
+ while self.socket.bytesAvailable():
818
+ data = self.socket.readLine(1000)
819
+ data = str(data, "utf-8").strip()
820
+ #logger.debug(f"cluster recv line: {data}")
821
+
822
+ if "login:" in data or "call:" in data or "callsign:" in data:
823
+ self.send_command(self.callsignField.text())
824
+ self.send_command(self.settings.get("cluster_filter", ""))
825
+ self.send_command("set dx extension Section")
826
+ self.send_command("set dx mode " + self.settings.get("cluster_mode", "OPEN"))
827
+ return
828
+ if "DX de" in data:
829
+ parts = data.split()
830
+ spotter = parts[2]
831
+ freq = parts[3]
832
+ dx = parts[4]
833
+ _time = parts[-1]
834
+ comment = " ".join(parts[5:-1])
835
+ # spot = DxSpot()
836
+ spot = {}
837
+ spot["ts"] = datetime.now(timezone.utc).isoformat(" ")[:19]
838
+ spot["callsign"] = dx
839
+ spot["spotter"] = spotter
840
+ spot["comment"] = comment
841
+ logger.debug(f"{spot}")
842
+ try:
843
+ spot["freq"] = float(freq) / 1000
844
+ self.spots.addspot(spot)
845
+ except ValueError:
846
+ logger.debug(f"couldn't parse freq from datablock {data}")
847
+ return
848
+ if self.callsignField.text().upper() in data:
849
+ self.connectButton.setStyleSheet("color: green;")
850
+ self.connectButton.setText("Connected")
851
+ logger.debug(f"callsign login acknowledged {data}")
827
852
 
828
853
  def maybeconnected(self):
829
854
  """doc"""
@@ -860,33 +885,13 @@ class MainWindow(QtWidgets.QMainWindow):
860
885
  def showContextMenu(self):
861
886
  """doc"""
862
887
 
888
+ def close_cluster(self):
889
+ if self.socket and self.socket.isOpen():
890
+ logger.info("Closing dx cluster connection")
891
+ self.socket.close()
892
+ self.connected = False
893
+ self.connectButton.setStyleSheet("color: red;")
894
+ self.connectButton.setText("Closed")
863
895
 
864
- def run():
865
- """doc"""
866
- sys.exit(app.exec())
867
-
868
-
869
- logger = logging.getLogger("__main__")
870
- handler = logging.StreamHandler()
871
- formatter = logging.Formatter(
872
- datefmt="%H:%M:%S",
873
- fmt="[%(asctime)s] %(levelname)s %(module)s - %(funcName)s Line %(lineno)d:\n%(message)s",
874
- )
875
- handler.setFormatter(formatter)
876
- logger.addHandler(handler)
877
-
878
- if Path("./debug").exists():
879
- logger.setLevel(logging.DEBUG)
880
- logger.debug("debugging on")
881
- else:
882
- logger.setLevel(logging.WARNING)
883
- logger.warning("debugging off")
884
-
885
- app = QtWidgets.QApplication(sys.argv)
886
-
887
-
888
- app.setStyle("Adwaita-Dark")
889
- window = MainWindow()
890
- window.show()
891
- if __name__ == "__main__":
892
- run()
896
+ def closeEvent(self, event: QtGui.QCloseEvent) -> None:
897
+ self.close_cluster()