not1mm 24.8.20__py3-none-any.whl → 24.8.27__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
@@ -35,9 +35,9 @@ except OSError as exception:
35
35
  print("portaudio is not installed")
36
36
  sd = None
37
37
  from PyQt6 import QtCore, QtGui, QtWidgets, uic
38
- from PyQt6.QtCore import QDir, Qt, QThread, QSettings
39
- from PyQt6.QtGui import QFontDatabase, QColorConstants, QPalette, QColor
40
- from PyQt6.QtWidgets import QFileDialog
38
+ from PyQt6.QtCore import QDir, Qt, QThread, QSettings, QCoreApplication
39
+ from PyQt6.QtGui import QFontDatabase, QColorConstants, QPalette, QColor, QPixmap
40
+ from PyQt6.QtWidgets import QFileDialog, QSplashScreen
41
41
 
42
42
  from not1mm.lib.about import About
43
43
  from not1mm.lib.cwinterface import CW
@@ -3398,12 +3398,24 @@ logging.basicConfig(
3398
3398
  logging.getLogger("PyQt6.uic.uiparser").setLevel("INFO")
3399
3399
  logging.getLogger("PyQt6.uic.properties").setLevel("INFO")
3400
3400
  app = QtWidgets.QApplication(sys.argv)
3401
+
3402
+ pixmap = QPixmap(f"{os.fspath(fsutils.APP_DATA_PATH)}/splash.png")
3403
+ splash = QSplashScreen(pixmap)
3404
+ splash.show()
3405
+ app.processEvents()
3406
+ splash.showMessage(
3407
+ "Starting Up",
3408
+ alignment=Qt.AlignmentFlag.AlignBottom | Qt.AlignmentFlag.AlignCenter,
3409
+ color=QColor(255, 255, 0),
3410
+ )
3411
+ QCoreApplication.processEvents()
3412
+
3401
3413
  families = load_fonts_from_dir(os.fspath(fsutils.APP_DATA_PATH))
3402
3414
  logger.info(f"font families {families}")
3403
3415
  window = MainWindow()
3404
3416
  window.callsign.setFocus()
3405
3417
  window.show()
3406
-
3418
+ splash.finish(window)
3407
3419
 
3408
3420
  if __name__ == "__main__":
3409
3421
  run()
@@ -297,6 +297,11 @@
297
297
  <string>CWT</string>
298
298
  </property>
299
299
  </item>
300
+ <item>
301
+ <property name="text">
302
+ <string>HELVETIA</string>
303
+ </property>
304
+ </item>
300
305
  <item>
301
306
  <property name="text">
302
307
  <string>ICWC MST</string>
@@ -356,7 +361,7 @@
356
361
  </property>
357
362
  <property name="time">
358
363
  <time>
359
- <hour>16</hour>
364
+ <hour>0</hour>
360
365
  <minute>0</minute>
361
366
  <second>0</second>
362
367
  </time>
not1mm/data/splash.png ADDED
Binary file
not1mm/lib/database.py CHANGED
@@ -567,7 +567,7 @@ class DataBase:
567
567
  def fetch_country_band_count(self) -> dict:
568
568
  """
569
569
  returns dict with count of unique NR.
570
- {nr_count: count}
570
+ {cb_count: count}
571
571
  """
572
572
  try:
573
573
  with sqlite3.connect(self.database) as conn:
@@ -581,6 +581,23 @@ class DataBase:
581
581
  logger.debug("%s", exception)
582
582
  return {}
583
583
 
584
+ def fetch_country_count(self) -> dict:
585
+ """
586
+ Fetch count of unique countries
587
+ {dxcc_count: count}
588
+ """
589
+ try:
590
+ with sqlite3.connect(self.database) as conn:
591
+ conn.row_factory = self.row_factory
592
+ cursor = conn.cursor()
593
+ cursor.execute(
594
+ f"select count(DISTINCT(CountryPrefix)) as dxcc_count from dxlog where ContestNR = {self.current_contest};"
595
+ )
596
+ return cursor.fetchone()
597
+ except sqlite3.OperationalError as exception:
598
+ logger.debug("%s", exception)
599
+ return {}
600
+
584
601
  def fetch_arrldx_country_band_count(self) -> dict:
585
602
  """
586
603
  returns dict with count of unique NR.
@@ -883,6 +900,20 @@ class DataBase:
883
900
  logger.debug("%s", exception)
884
901
  return {}
885
902
 
903
+ def fetch_mult2_count(self) -> dict:
904
+ """return QSO count"""
905
+ try:
906
+ with sqlite3.connect(self.database) as conn:
907
+ conn.row_factory = self.row_factory
908
+ cursor = conn.cursor()
909
+ cursor.execute(
910
+ f"select count(*) as count from dxlog where IsMultiplier2 = 1 and ContestNR = {self.current_contest};"
911
+ )
912
+ return cursor.fetchone()
913
+ except sqlite3.OperationalError as exception:
914
+ logger.debug("%s", exception)
915
+ return {}
916
+
886
917
  def fetch_qso_count(self) -> dict:
887
918
  """return QSO count"""
888
919
  try:
not1mm/lib/ham_utility.py CHANGED
@@ -373,3 +373,29 @@ def distance_with_latlon(grid1: str, lat2: float, lon2: float) -> float:
373
373
  logger.debug("lat1:%d lon1:%d lat2:%d lon2:%d", lat1, lon1, lat2, lon2)
374
374
  # lat2, lon2 = gridtolatlon(grid2)
375
375
  return round(haversine(lon1, lat1, lon2, lat2))
376
+
377
+
378
+ def parse_udc(filename: str) -> dict:
379
+ """
380
+ simply parses a n1mm style udc file and returns a dict with key value pairs.
381
+ """
382
+
383
+ udc_contest = {}
384
+ the_good_stuff = False
385
+
386
+ try:
387
+ with open(filename, "r", encoding="utf-8") as file_descriptor:
388
+ for line in file_descriptor:
389
+ if "[CONTEST]" in line.upper():
390
+ the_good_stuff = True
391
+ continue
392
+ if "=" in line and the_good_stuff is True:
393
+ try:
394
+ key, value = line.split("=")
395
+ udc_contest[key.strip()] = value.strip()
396
+ except ValueError:
397
+ ...
398
+ except FileNotFoundError:
399
+ logger.debug("UDC file not found.")
400
+ return {}
401
+ return udc_contest
not1mm/lib/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """It's the version"""
2
2
 
3
- __version__ = "24.8.20"
3
+ __version__ = "24.8.27"
@@ -0,0 +1,497 @@
1
+ """
2
+ Helvetica
3
+ -------------------------------------------------
4
+ Status: Active
5
+ Geographic Focus: Switzerland
6
+ Participation: Worldwide
7
+ Mode: CW, SSB, Digital
8
+ Bands: 160, 80, 40, 20, 15, 10m
9
+ Classes: Single Op (CW/SSB) High
10
+ Single Op Mixed (QRP/Low/High)
11
+ Multi-Op (CW/SSB/Mixed) High
12
+ SWL
13
+ Max operating hours: 18 with a maximum of two rest periods of any length
14
+ Max power: HP: >100 watts
15
+ LP: 100 watts
16
+ QRP: 5 watts (CW/Digital) or 10 watts (SSB)
17
+ Exchange: HB: RS(T) + 2-letter canton
18
+ non-HB: RS(T) + Serial No.
19
+ Work stations: Once per band per mode
20
+
21
+ Scoring:
22
+ Contact with a station in Switzerland: 10 points
23
+ Contact with a station within the same continent: 1 point
24
+ Contact with a station outside the operator’s continent: 3 points
25
+
26
+ Multipliers: Canton and DXCC country (including Switzerland) per band: 1 point
27
+
28
+ Score Calculation: Total score = total QSO points x total mults
29
+ E-mail logs to: contest[at]uska[dot]ch
30
+ Mail logs to: (none)
31
+ Find rules at: https://www.uska.ch/events/uska-helvetia-contest-concours-helvetia-hf/
32
+ Cabrillo name: HELVETIA
33
+ """
34
+
35
+ import datetime
36
+ import logging
37
+ import platform
38
+
39
+ from pathlib import Path
40
+
41
+ from PyQt6 import QtWidgets
42
+
43
+ from not1mm.lib.plugin_common import gen_adif, get_points
44
+
45
+ from not1mm.lib.version import __version__
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+ EXCHANGE_HINT = "Canton or #"
50
+
51
+ name = "HELVETIA"
52
+ cabrillo_name = "HELVETIA"
53
+ mode = "BOTH" # CW SSB BOTH RTTY
54
+
55
+ columns = [
56
+ "YYYY-MM-DD HH:MM:SS",
57
+ "Call",
58
+ "Freq",
59
+ "Mode",
60
+ "Snt",
61
+ "Rcv",
62
+ "SentNr",
63
+ "RcvNr",
64
+ "M1",
65
+ "M2",
66
+ "PTS",
67
+ ]
68
+
69
+ advance_on_space = [True, True, True, True, True]
70
+
71
+ # 1 once per contest, 2 work each band, 3 each band/mode, 4 no dupe checking
72
+ dupe_type = 3
73
+
74
+ cantons = [
75
+ "AG",
76
+ "AI",
77
+ "AR",
78
+ "BE",
79
+ "BL",
80
+ "BS",
81
+ "CH",
82
+ "FR",
83
+ "GE",
84
+ "GL",
85
+ "GR",
86
+ "JU",
87
+ "LU",
88
+ "NE",
89
+ "NW",
90
+ "OW",
91
+ "SG",
92
+ "SH",
93
+ "SO",
94
+ "SZ",
95
+ "TG",
96
+ "TI",
97
+ "UR",
98
+ "VD",
99
+ "VS",
100
+ "ZG",
101
+ "ZH",
102
+ ]
103
+
104
+
105
+ def init_contest(self):
106
+ """setup plugin"""
107
+ set_tab_next(self)
108
+ set_tab_prev(self)
109
+ interface(self)
110
+ self.next_field = self.other_2
111
+
112
+
113
+ def interface(self):
114
+ """Setup user interface"""
115
+ self.field1.show()
116
+ self.field2.show()
117
+ self.field3.show()
118
+ self.field4.show()
119
+ label = self.field3.findChild(QtWidgets.QLabel)
120
+ label.setText("Sent")
121
+ self.field3.setAccessibleName("Sent")
122
+ label = self.field4.findChild(QtWidgets.QLabel)
123
+ label.setText("Canton/SN")
124
+ self.field4.setAccessibleName("Canton or SN")
125
+
126
+
127
+ def reset_label(self):
128
+ """reset label after field cleared"""
129
+
130
+
131
+ def set_tab_next(self):
132
+ """Set TAB Advances"""
133
+ self.tab_next = {
134
+ self.callsign: self.field3.findChild(QtWidgets.QLineEdit),
135
+ self.field1.findChild(QtWidgets.QLineEdit): self.field3.findChild(
136
+ QtWidgets.QLineEdit
137
+ ),
138
+ self.field2.findChild(QtWidgets.QLineEdit): self.field3.findChild(
139
+ QtWidgets.QLineEdit
140
+ ),
141
+ self.field3.findChild(QtWidgets.QLineEdit): self.field4.findChild(
142
+ QtWidgets.QLineEdit
143
+ ),
144
+ self.field4.findChild(QtWidgets.QLineEdit): self.callsign,
145
+ }
146
+
147
+
148
+ def set_tab_prev(self):
149
+ """Set TAB Advances"""
150
+ self.tab_prev = {
151
+ self.callsign: self.field4.findChild(QtWidgets.QLineEdit),
152
+ self.field1.findChild(QtWidgets.QLineEdit): self.callsign,
153
+ self.field2.findChild(QtWidgets.QLineEdit): self.callsign,
154
+ self.field3.findChild(QtWidgets.QLineEdit): self.callsign,
155
+ self.field4.findChild(QtWidgets.QLineEdit): self.field3.findChild(
156
+ QtWidgets.QLineEdit
157
+ ),
158
+ }
159
+
160
+
161
+ def set_contact_vars(self):
162
+ """Contest Specific"""
163
+ self.contact["SNT"] = self.sent.text()
164
+ self.contact["RCV"] = self.receive.text()
165
+ self.contact["SentNr"] = self.other_1.text().upper()
166
+ self.contact["NR"] = self.other_2.text().upper()
167
+
168
+ self.contact["IsMultiplier1"] = 0
169
+ self.contact["IsMultiplier2"] = 0
170
+
171
+ if (
172
+ self.contact.get("CountryPrefix", "") == "HB"
173
+ and self.contact.get("NR", "").isalpha()
174
+ ):
175
+ canton = self.contact.get("NR", "").upper()
176
+ band = self.contact.get("Band", "")
177
+ query = (
178
+ f"select count(*) as canton_count from dxlog where "
179
+ f"NR = '{canton}' "
180
+ f"and Band = '{band}' "
181
+ f"and ContestNR = {self.pref.get('contest', '1')};"
182
+ )
183
+ result = self.database.exec_sql(query)
184
+ count = int(result.get("canton_count", 0))
185
+ if count == 0:
186
+ self.contact["IsMultiplier1"] = 1
187
+
188
+ if self.contact.get("CountryPrefix", ""):
189
+ dxcc = self.contact.get("CountryPrefix", "")
190
+ band = self.contact.get("Band", "")
191
+ query = (
192
+ f"select count(*) as dxcc_count from dxlog where "
193
+ f"CountryPrefix = '{dxcc}' "
194
+ f"and Band = '{band}' "
195
+ f"and ContestNR = {self.pref.get('contest', '1')};"
196
+ )
197
+ result = self.database.exec_sql(query)
198
+ if not result.get("dxcc_count", ""):
199
+ self.contact["IsMultiplier2"] = 1
200
+
201
+
202
+ def predupe(self):
203
+ """called after callsign entered"""
204
+
205
+
206
+ def prefill(self):
207
+ """Fill SentNR"""
208
+ field = self.field3.findChild(QtWidgets.QLineEdit)
209
+ sent_sxchange_setting = self.contest_settings.get("SentExchange", "")
210
+ if sent_sxchange_setting.strip() == "#":
211
+ result = self.database.get_serial()
212
+ serial_nr = str(result.get("serial_nr", "1")).zfill(3)
213
+ if serial_nr == "None":
214
+ serial_nr = "001"
215
+ if len(field.text()) == 0:
216
+ field.setText(serial_nr)
217
+ else:
218
+ field.setText(sent_sxchange_setting)
219
+
220
+
221
+ def points(self):
222
+ """
223
+ Scoring:
224
+ Contact with a station within the same continent: 1 point
225
+ Contact with a station outside the operator’s continent: 3 points
226
+ Contact with a station in Switzerland: 10 points
227
+ self.contact["CountryPrefix"]
228
+ self.contact["Continent"]
229
+ """
230
+ result = self.cty_lookup(self.station.get("Call", ""))
231
+ if result:
232
+ for item in result.items():
233
+ my_continent = item[1].get("continent", "")
234
+ result = self.cty_lookup(self.contact.get("Call", ""))
235
+ if result:
236
+ for item in result.items():
237
+ their_country = item[1].get("entity", "")
238
+ their_continent = item[1].get("continent", "")
239
+
240
+ if their_country == "Switzerland":
241
+ return 10
242
+
243
+ if my_continent != their_continent:
244
+ return 3
245
+
246
+ return 1
247
+ # Something wrong
248
+ return 0
249
+
250
+
251
+ def show_mults(self):
252
+ """Return display string for mults"""
253
+ return int(self.database.fetch_mult1_count().get("count", 0)) + int(
254
+ self.database.fetch_mult2_count().get("count", 0)
255
+ )
256
+
257
+
258
+ def show_qso(self):
259
+ """Return qso count"""
260
+ result = self.database.fetch_qso_count()
261
+ if result:
262
+ return int(result.get("qsos", 0))
263
+ return 0
264
+
265
+
266
+ def calc_score(self):
267
+ """Return calculated score"""
268
+ result = self.database.fetch_points()
269
+ if result is not None:
270
+ score = result.get("Points", "0")
271
+ if score is None:
272
+ score = "0"
273
+ contest_points = int(score)
274
+ mults = show_mults(self)
275
+ return contest_points * mults
276
+ return 0
277
+
278
+
279
+ def recalculate_mults(self):
280
+ """Recalculates multipliers after change in logged qso."""
281
+
282
+ all_contacts = self.database.fetch_all_contacts_asc()
283
+ for contact in all_contacts:
284
+
285
+ contact["IsMultiplier1"] = 0
286
+ contact["IsMultiplier2"] = 0
287
+
288
+ time_stamp = contact.get("TS", "")
289
+ canton = contact.get("NR", "")
290
+ dxcc = contact.get("CountryPrefix", "")
291
+ band = contact.get("Band", "")
292
+ if dxcc == "HB" and canton.isalpha():
293
+ query = (
294
+ f"select count(*) as canton_count from dxlog where TS < '{time_stamp}' "
295
+ f"and NR = '{canton.upper()}' "
296
+ f"and Band = '{band}' "
297
+ f"and ContestNR = {self.pref.get('contest', '1')};"
298
+ )
299
+ result = self.database.exec_sql(query)
300
+ count = int(result.get("canton_count", 0))
301
+ if count == 0:
302
+ contact["IsMultiplier1"] = 1
303
+
304
+ if dxcc:
305
+ query = (
306
+ f"select count(*) as dxcc_count from dxlog where TS < '{time_stamp}' "
307
+ f"and CountryPrefix = '{dxcc}' "
308
+ f"and Band = '{band}' "
309
+ f"and ContestNR = {self.pref.get('contest', '1')};"
310
+ )
311
+ result = self.database.exec_sql(query)
312
+ if not result.get("dxcc_count", ""):
313
+ contact["IsMultiplier2"] = 1
314
+
315
+ self.database.change_contact(contact)
316
+ cmd = {}
317
+ cmd["cmd"] = "UPDATELOG"
318
+ cmd["station"] = platform.node()
319
+ self.multicast_interface.send_as_json(cmd)
320
+
321
+
322
+ def adif(self):
323
+ """Call the generate ADIF function"""
324
+ gen_adif(self, cabrillo_name, "HELVETIA")
325
+
326
+
327
+ def cabrillo(self):
328
+ """Generates Cabrillo file. Maybe."""
329
+ # https://www.cqwpx.com/cabrillo.htm
330
+ logger.debug("******Cabrillo*****")
331
+ logger.debug("Station: %s", f"{self.station}")
332
+ logger.debug("Contest: %s", f"{self.contest_settings}")
333
+ now = datetime.datetime.now()
334
+ date_time = now.strftime("%Y-%m-%d_%H-%M-%S")
335
+ filename = (
336
+ str(Path.home())
337
+ + "/"
338
+ + f"{self.station.get('Call', '').upper()}_{cabrillo_name}_{date_time}.log"
339
+ )
340
+ logger.debug("%s", filename)
341
+ log = self.database.fetch_all_contacts_asc()
342
+ try:
343
+ with open(filename, "w", encoding="ascii") as file_descriptor:
344
+ print("START-OF-LOG: 3.0", end="\r\n", file=file_descriptor)
345
+ print(
346
+ f"CREATED-BY: Not1MM v{__version__}",
347
+ end="\r\n",
348
+ file=file_descriptor,
349
+ )
350
+ print(
351
+ f"CONTEST: {cabrillo_name}",
352
+ end="\r\n",
353
+ file=file_descriptor,
354
+ )
355
+ if self.station.get("Club", ""):
356
+ print(
357
+ f"CLUB: {self.station.get('Club', '').upper()}",
358
+ end="\r\n",
359
+ file=file_descriptor,
360
+ )
361
+ print(
362
+ f"CALLSIGN: {self.station.get('Call','')}",
363
+ end="\r\n",
364
+ file=file_descriptor,
365
+ )
366
+ print(
367
+ f"LOCATION: {self.station.get('ARRLSection', '')}",
368
+ end="\r\n",
369
+ file=file_descriptor,
370
+ )
371
+ # print(
372
+ # f"ARRL-SECTION: {self.pref.get('section', '')}",
373
+ # end="\r\n",
374
+ # file=file_descriptor,
375
+ # )
376
+ print(
377
+ f"CATEGORY-OPERATOR: {self.contest_settings.get('OperatorCategory','')}",
378
+ end="\r\n",
379
+ file=file_descriptor,
380
+ )
381
+ print(
382
+ f"CATEGORY-ASSISTED: {self.contest_settings.get('AssistedCategory','')}",
383
+ end="\r\n",
384
+ file=file_descriptor,
385
+ )
386
+ print(
387
+ f"CATEGORY-BAND: {self.contest_settings.get('BandCategory','')}",
388
+ end="\r\n",
389
+ file=file_descriptor,
390
+ )
391
+ print(
392
+ f"CATEGORY-MODE: {self.contest_settings.get('ModeCategory','')}",
393
+ end="\r\n",
394
+ file=file_descriptor,
395
+ )
396
+ print(
397
+ f"CATEGORY-TRANSMITTER: {self.contest_settings.get('TransmitterCategory','')}",
398
+ end="\r\n",
399
+ file=file_descriptor,
400
+ )
401
+ if self.contest_settings.get("OverlayCategory", "") != "N/A":
402
+ print(
403
+ f"CATEGORY-OVERLAY: {self.contest_settings.get('OverlayCategory','')}",
404
+ end="\r\n",
405
+ file=file_descriptor,
406
+ )
407
+ print(
408
+ f"GRID-LOCATOR: {self.station.get('GridSquare','')}",
409
+ end="\r\n",
410
+ file=file_descriptor,
411
+ )
412
+ # print(
413
+ # f"CATEGORY: {None}",
414
+ # end="\r\n",
415
+ # file=file_descriptor,
416
+ # )
417
+ print(
418
+ f"CATEGORY-POWER: {self.contest_settings.get('PowerCategory','')}",
419
+ end="\r\n",
420
+ file=file_descriptor,
421
+ )
422
+
423
+ print(
424
+ f"CLAIMED-SCORE: {calc_score(self)}",
425
+ end="\r\n",
426
+ file=file_descriptor,
427
+ )
428
+ ops = f"@{self.station.get('Call','')}"
429
+ list_of_ops = self.database.get_ops()
430
+ for op in list_of_ops:
431
+ ops += f", {op.get('Operator', '')}"
432
+ print(
433
+ f"OPERATORS: {ops}",
434
+ end="\r\n",
435
+ file=file_descriptor,
436
+ )
437
+ print(
438
+ f"NAME: {self.station.get('Name', '')}",
439
+ end="\r\n",
440
+ file=file_descriptor,
441
+ )
442
+ print(
443
+ f"ADDRESS: {self.station.get('Street1', '')}",
444
+ end="\r\n",
445
+ file=file_descriptor,
446
+ )
447
+ print(
448
+ f"ADDRESS-CITY: {self.station.get('City', '')}",
449
+ end="\r\n",
450
+ file=file_descriptor,
451
+ )
452
+ print(
453
+ f"ADDRESS-STATE-PROVINCE: {self.station.get('State', '')}",
454
+ end="\r\n",
455
+ file=file_descriptor,
456
+ )
457
+ print(
458
+ f"ADDRESS-POSTALCODE: {self.station.get('Zip', '')}",
459
+ end="\r\n",
460
+ file=file_descriptor,
461
+ )
462
+ print(
463
+ f"ADDRESS-COUNTRY: {self.station.get('Country', '')}",
464
+ end="\r\n",
465
+ file=file_descriptor,
466
+ )
467
+ print(
468
+ f"EMAIL: {self.station.get('Email', '')}",
469
+ end="\r\n",
470
+ file=file_descriptor,
471
+ )
472
+ for contact in log:
473
+ the_date_and_time = contact.get("TS", "")
474
+ themode = contact.get("Mode", "")
475
+ if themode == "LSB" or themode == "USB":
476
+ themode = "PH"
477
+ frequency = str(int(contact.get("Freq", "0"))).rjust(5)
478
+
479
+ loggeddate = the_date_and_time[:10]
480
+ loggedtime = the_date_and_time[11:13] + the_date_and_time[14:16]
481
+ print(
482
+ f"QSO: {frequency} {themode} {loggeddate} {loggedtime} "
483
+ f"{contact.get('StationPrefix', '').ljust(13)} "
484
+ f"{str(contact.get('SNT', '')).ljust(3)} "
485
+ f"{str(contact.get('SentNr', '')).ljust(6)} "
486
+ f"{contact.get('Call', '').ljust(13)} "
487
+ f"{str(contact.get('RCV', '')).ljust(3)} "
488
+ f"{str(contact.get('NR', '')).ljust(6)}",
489
+ end="\r\n",
490
+ file=file_descriptor,
491
+ )
492
+ print("END-OF-LOG:", end="\r\n", file=file_descriptor)
493
+ self.show_message_box(f"Cabrillo saved to: {filename}")
494
+ except IOError as exception:
495
+ logger.critical("cabrillo: IO error: %s, writing to %s", exception, filename)
496
+ self.show_message_box(f"Error saving Cabrillo: {exception} {filename}")
497
+ return
@@ -6,10 +6,8 @@ Purpose: test alternative sound playing interface
6
6
  # pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines, no-name-in-module
7
7
  # pylint: disable=logging-fstring-interpolation, logging-not-lazy, line-too-long, bare-except
8
8
 
9
- from not1mm.lib.playsound import playsound
9
+ from not1mm.lib.ham_utility import parse_udc
10
10
 
11
- import not1mm.fsutils as fsutils
11
+ filename = "./testing/K1USNSSTOP.udc"
12
12
 
13
- filename = fsutils.APP_DATA_PATH / "phonetics/cq.wav"
14
-
15
- playsound(filename, True)
13
+ print(f"{parse_udc(filename)}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: not1mm
3
- Version: 24.8.20
3
+ Version: 24.8.27
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
@@ -69,6 +69,7 @@ Requires-Dist: Levenshtein
69
69
  - [Fedora 38 \& 39](#fedora-38--39)
70
70
  - [Fedora 40](#fedora-40)
71
71
  - [Manjaro](#manjaro)
72
+ - [Mint](#mint)
72
73
  - [Python, PyPI, pip and pipx](#python-pypi-pip-and-pipx)
73
74
  - [Bootstrapping pipx](#bootstrapping-pipx)
74
75
  - [Installing with pipx](#installing-with-pipx)
@@ -209,6 +210,7 @@ generated, 'cause I'm lazy, list of those who've submitted PR's.
209
210
  - CQ World Wide CW
210
211
  - CQ World Wide SSB
211
212
  - CWOps CWT
213
+ - Helvetia
212
214
  - IARU HF
213
215
  - ICWC MST
214
216
  - Japan International DX CW
@@ -223,6 +225,8 @@ generated, 'cause I'm lazy, list of those who've submitted PR's.
223
225
 
224
226
  ## Recent Changes
225
227
 
228
+ - [24-8-27] Added Helvetia contest.
229
+ - [24-8-22] Add loading splash screen.
226
230
  - [24-8-20] Added K1USN Slow Speed Test
227
231
  - [24-8-17-1] Did an oops. Fixed the oops.
228
232
  - [24-8-17] Removed some cruft. Made dockable widgets not floatable since Wayland breaks this.
@@ -240,8 +244,14 @@ clue me into the black magic needed to get it to work.
240
244
 
241
245
  ### Prerequisites
242
246
 
243
- not1mm requires Python 3.9+, PyQt6 and libportaudio2. You should install these
244
- through your distribution's package manager before continuing.
247
+ not1mm requires:
248
+
249
+ - Python 3.9+
250
+ - PyQt6
251
+ - libportaudio2
252
+ - libxcb-cursor0 (maybe... Depends on the distro)
253
+
254
+ You should install these through your distribution's package manager before continuing.
245
255
 
246
256
  ### Common installation recipes for Ubuntu and Fedora
247
257
 
@@ -318,6 +328,22 @@ pip install not1mm
318
328
  pamac build not1mm-git
319
329
  ```
320
330
 
331
+ </details>
332
+
333
+ <details>
334
+
335
+ <summary><b>Mint 22</b></summary>
336
+
337
+ #### Mint
338
+
339
+ ```bash
340
+ sudo apt install python3-pip
341
+ sudo apt install pipx
342
+ sudo apt install libxcb-cursor0
343
+ pipx install not1mm
344
+ pipx ensurepath
345
+ ```
346
+
321
347
  </details>
322
348
  <br>
323
349
 
@@ -429,7 +455,9 @@ qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it
429
455
  This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.
430
456
  ```
431
457
 
432
- To avoid this you can export an environment variable and launch the app like this:
458
+ You can use your package manager to load libxcb-cursor0.
459
+
460
+ If that's not an option, you can export an environment variable and launch the app like this:
433
461
 
434
462
  `mbridak@vm:~$ export QT_QPA_PLATFORM=wayland; not1mm`
435
463
 
@@ -1,11 +1,11 @@
1
1
  not1mm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- not1mm/__main__.py,sha256=Y6uMGEKIF5jvovj02CXlrgr-ThBPQ1F-A5lpWBP5n5k,121455
2
+ not1mm/__main__.py,sha256=QJRnfWEcHEQ40ESeIeEtPB6lOwMySP8r6Ao-4TBqLoc,121832
3
3
  not1mm/bandmap.py,sha256=1b5tXCfGTnpqqn6hPNt7zRA8SmuwSXzSwNHZXhCRt70,31434
4
4
  not1mm/checkwindow.py,sha256=aI-nr8OF90IWV7R_XRdmitvBJ9M85evCs72HoU3Jnvc,10374
5
5
  not1mm/fsutils.py,sha256=ukHKxKTeNKxKwqRaJjtzRShL4X5Xl0jRBbADyy3Ifp8,1701
6
6
  not1mm/logwindow.py,sha256=pwhiwolmGnW01LF4sjlu3ywLsgfxL6KuGuKuYKYmgeY,44403
7
- not1mm/playsoundtest.py,sha256=_kvvOfgs-u-H4kVSiSlnDtn9HWv0RiJQybkv24CEngc,464
8
7
  not1mm/radio.py,sha256=eiB04LPMPBeMrBRI021Z7VXth66EOYb0Ujh11T9877c,3362
8
+ not1mm/test.py,sha256=ev_YgRFBurFw7_W1SXn509XyUkC6tWchLF5w_c8Z-ow,422
9
9
  not1mm/vfo.py,sha256=IvmUQYMIPzLJw_BHQGis4J_IEW-vlBtdfxZLXPh7OzI,12335
10
10
  not1mm/voice_keying.py,sha256=sA3gw5_k7kShTg2qhG7HkKDM5M6KheJVRkAc_C7mxDk,3006
11
11
  not1mm/data/JetBrainsMono-Regular.ttf,sha256=UOHctAKY_PzCGh7zy-6f6egnCcSK0wzmF0csBqO9lDY,203952
@@ -30,7 +30,7 @@ not1mm/data/k6gte.not1mm-64.png,sha256=6ku45Gq1g5ezh04F07osoKRtanb3e4kbx5XdIEh3N
30
30
  not1mm/data/logwindow.ui,sha256=vfkNdzJgFs3tTOBKLDavF2zVMvNHWOZ82fAErRi6pQY,1436
31
31
  not1mm/data/logwindowx.ui,sha256=9FzDJtLRpagvAWcDjFdB9NnvNZ4bVxdTNHy1Jit2ido,1610
32
32
  not1mm/data/main.ui,sha256=Y8F0gOqMTcsxBmc3QvWkJMxhYFpxUJQl-btGiM1-31w,56506
33
- not1mm/data/new_contest.ui,sha256=ZRuQu9qYDGUTF81uOeiR9KIURPvELt8yxiyV3tt6DdU,22034
33
+ not1mm/data/new_contest.ui,sha256=6j4OYcTjSx-5ywvBf4GQfuAEaavCJVPVwzj_es5ajsc,22138
34
34
  not1mm/data/not1mm.html,sha256=c9-mfjMwDt4f5pySUruz2gREW33CQ2_rCddM2z5CZQo,23273
35
35
  not1mm/data/opon.ui,sha256=mC4OhoVIfR1H9IqHAKXliPMm8VOVmxSEadpsFQ7XnS4,2247
36
36
  not1mm/data/pickcontest.ui,sha256=_9JFiJw4l-bRRgNDtVg1DpxreylYd_UqEq0wfcr9gJc,1600
@@ -39,6 +39,7 @@ not1mm/data/radio_grey.png,sha256=9eOtMHDpQvRYY29D7_vPeacWbwotRXZTMm8EiHE9TW0,12
39
39
  not1mm/data/radio_red.png,sha256=QvkMk7thd_hCEIyK5xGAG4xVVXivl39nwOfD8USDI20,957
40
40
  not1mm/data/reddot.png,sha256=M33jEMoU8W4rQ4_MVyzzKxDPDte1ypKBch5VnUMNLKE,565
41
41
  not1mm/data/settings.ui,sha256=xXvcL8nRIbGYU3tr9YjLAlTAYxFbMtRXjlZlv_CLg4A,40112
42
+ not1mm/data/splash.png,sha256=85_BQotR1q24uCthrKm4SB_6ZOMwRjR-Jdp1XBHSTyg,5368
42
43
  not1mm/data/ssbmacros.txt,sha256=0Qccj4y0nlK-w5da9a9ti-jILkURtwztoDuL_D0pEJM,470
43
44
  not1mm/data/vfo.ui,sha256=ixer0pVVr8o21j_AmBA3J1OEGd96EXVFhkoNhqIQG10,2054
44
45
  not1mm/data/phonetics/0.wav,sha256=0OpYiR-3MK6fVHE6MB-HeOxSAPiDNMjqvx5JcIZtsQk,42590
@@ -93,13 +94,13 @@ not1mm/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
94
  not1mm/lib/about.py,sha256=sWycfGcruN3SaEe4JmaJ61K6D8Itq0WxpUYT-lEcmYM,416
94
95
  not1mm/lib/cat_interface.py,sha256=aazvNTSeZAROq3KL8gPx-I95iVez2IiIOSk22qeqVCQ,19502
95
96
  not1mm/lib/cwinterface.py,sha256=Q8p3pScOHczZ8ptICfH1Yu6rCEwQJLgrNwYMN76B2i8,3389
96
- not1mm/lib/database.py,sha256=RQoj3JsTejMiiFIN42lY3N4jrj80htftdoFeRqVKnKs,42480
97
+ not1mm/lib/database.py,sha256=rX1wOoSiSuX7CCFghvD_yc4q8E0FHwZp1k7Z2iVNYv8,43680
97
98
  not1mm/lib/edit_contact.py,sha256=Ki9bGPpqyQQBB1cU8VIBDCal3lbXeQ6qxhzklmhE2_w,353
98
99
  not1mm/lib/edit_macro.py,sha256=raKWBwsHInj5EUKmvyLQ6gqc3ZFDlstsD3xqoM4PC8E,517
99
100
  not1mm/lib/edit_opon.py,sha256=j3qJ1aBsQoIOnQ9yiBl3lyeISvKTP0I_rtBYBPAfgeI,359
100
101
  not1mm/lib/edit_station.py,sha256=doL21Hs6jzIE43ohAopdFt_iqnRJZHFcqzcnCS0-iio,1965
101
102
  not1mm/lib/ft8_watcher.py,sha256=ISfXjs-Mgbz_lE5SThEnFoCe8apNLElgSuECAMCH18I,4080
102
- not1mm/lib/ham_utility.py,sha256=rHYTHxeoMhpzmh_J6jEqCPJmVLpJbM2o4mbvydGLyvE,10979
103
+ not1mm/lib/ham_utility.py,sha256=uRErxCxZr8dfxzekPyett0e_BABDVOCvSUUTzXq6ctE,11790
103
104
  not1mm/lib/lookup.py,sha256=F2fl5QkMxaGSxl1XMWnLUub3T9Mt7LhCX4acOlAsks4,13952
104
105
  not1mm/lib/multicast.py,sha256=bnFUNHyy82GmIb3_88EPBVVssj7-HzkJPaH671cK8Qw,3249
105
106
  not1mm/lib/n1mm.py,sha256=H54mpgJF0GAmKavM-nb5OAq2SJFWYkux4eMWWiSRxJc,6288
@@ -109,7 +110,7 @@ not1mm/lib/plugin_common.py,sha256=wuG7B0OJx9zYc5Gew3fdt_lNyan8Ul9KNlPQ7PDKGsU,9
109
110
  not1mm/lib/select_contest.py,sha256=WsptLuwkouIHeocJL3oZ6-eUfEnhpwdc-x7eMZ_TIVM,359
110
111
  not1mm/lib/settings.py,sha256=MWiKXbasaFbzeHTjfzTaTqbCBrIijudP_-0a5jNmUAA,9265
111
112
  not1mm/lib/super_check_partial.py,sha256=p5l3u2ZOCBtlWgbvskC50FpuoaIpR07tfC6zTdRWbh4,2334
112
- not1mm/lib/version.py,sha256=X0Ug9ODH4KaaGsn2VY3cXPVJ5MAQIn7ob4m613XrOdo,48
113
+ not1mm/lib/version.py,sha256=1KiLcfpXjS8-V6c6tXOR_NoOKat-5IpaUjb1wrD5Q8g,48
113
114
  not1mm/lib/versiontest.py,sha256=8vDNptuBBunn-1IGkjNaquehqBYUJyjrPSF8Igmd4_Y,1286
114
115
  not1mm/plugins/10_10_fall_cw.py,sha256=IttjX1yy4nDdACGsiYlPteFG8eVseX_WtoFio6bqHE8,10953
115
116
  not1mm/plugins/10_10_spring_cw.py,sha256=ThCptdM3dX4ywhoy2JRcOEyHSqcJolFaT7O_PYzM1Mg,10958
@@ -135,6 +136,7 @@ not1mm/plugins/cq_ww_cw.py,sha256=ltXFnSXabCOuW70s-WOydgghZTNpztX8TKLpVIV50B4,11
135
136
  not1mm/plugins/cq_ww_ssb.py,sha256=kt-EQofmCbynX1iXFm9ehffi_TMW25ke8Qi9MiR69ZQ,11199
136
137
  not1mm/plugins/cwt.py,sha256=4xdXN6ZJM5k-6gn0hJzNheWfFlGiqquC2p0ZMEe516M,12818
137
138
  not1mm/plugins/general_logging.py,sha256=t02xtJs601qRICGdrvLs3G9y4GCG9H4AgQNkgA18CYs,3467
139
+ not1mm/plugins/helvetia.py,sha256=OtMTOw3-SavrhTNRb_lulTX9BEaNbQdK5lLufowKihY,15432
138
140
  not1mm/plugins/iaru_hf.py,sha256=-ROUo2gBkw3xB89t8bd-4f7_1hROw2VXZXVHLFdB62s,11541
139
141
  not1mm/plugins/icwc_mst.py,sha256=BaUP2kzrT2D27un_WLGT4HCTTi1e7CNYC4NHcC_9r74,11842
140
142
  not1mm/plugins/jidx_cw.py,sha256=9oV4hDxMiGXa9wuYUNYOCsr-mz8LYB-4WMHBN8u2dFk,12153
@@ -145,9 +147,9 @@ not1mm/plugins/naqp_ssb.py,sha256=VLWVrSzI0UP1AhSXYn61eZ7or1rz6a_pS_xCKfgS4Jw,11
145
147
  not1mm/plugins/phone_weekly_test.py,sha256=fLpMe03WB9_KgRl6vMgQQt_aktFdqfNt2Sw81CTRAUs,12325
146
148
  not1mm/plugins/stew_perry_topband.py,sha256=CKBQbYl4ETxhXJd2dma4fg_C5pag_s7Nf61SCztZtqE,10668
147
149
  not1mm/plugins/winter_field_day.py,sha256=4rcfRtobwjHO6BNL3WOTHzBmyyeuX79BNGBG8PfjrI8,10238
148
- not1mm-24.8.20.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
149
- not1mm-24.8.20.dist-info/METADATA,sha256=DrtQTSEIEKUrrEkEF66gOjp-UayWRuxPAgkjVo-Wce4,29256
150
- not1mm-24.8.20.dist-info/WHEEL,sha256=nCVcAvsfA9TDtwGwhYaRrlPhTLV9m-Ga6mdyDtuwK18,91
151
- not1mm-24.8.20.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
152
- not1mm-24.8.20.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
153
- not1mm-24.8.20.dist-info/RECORD,,
150
+ not1mm-24.8.27.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
151
+ not1mm-24.8.27.dist-info/METADATA,sha256=PC9MtwYLjp66VOkyLGbCkMNw4Eb1Z5PoF3AkrKQCh6I,29688
152
+ not1mm-24.8.27.dist-info/WHEEL,sha256=UvcQYKBHoFqaQd6LKyqHw9fxEolWLQnlzP0h_LgJAfI,91
153
+ not1mm-24.8.27.dist-info/entry_points.txt,sha256=pMcZk_0dxFgLkcUkF0Q874ojpwOmF3OL6EKw9LgvocM,47
154
+ not1mm-24.8.27.dist-info/top_level.txt,sha256=0YmTxEcDzQlzXub-lXASvoLpg_mt1c2thb5cVkDf5J4,7
155
+ not1mm-24.8.27.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (73.0.0)
2
+ Generator: setuptools (74.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5