not1mm 24.8.20__py3-none-any.whl → 24.9.3__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 +77 -39
- not1mm/data/new_contest.ui +6 -1
- not1mm/data/splash.png +0 -0
- not1mm/lib/database.py +20 -3
- not1mm/lib/ham_utility.py +26 -0
- not1mm/lib/version.py +1 -1
- not1mm/plugins/arrl_field_day.py +14 -3
- not1mm/plugins/cq_160_cw.py +2 -2
- not1mm/plugins/cq_160_ssb.py +2 -2
- not1mm/plugins/helvetia.py +497 -0
- not1mm/plugins/ref_cw.py +502 -0
- not1mm/test.py +10 -0
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.dist-info}/METADATA +37 -7
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.dist-info}/RECORD +18 -15
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.dist-info}/WHEEL +1 -1
- not1mm/playsoundtest.py +0 -15
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.dist-info}/LICENSE +0 -0
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.dist-info}/entry_points.txt +0 -0
- {not1mm-24.8.20.dist-info → not1mm-24.9.3.dist-info}/top_level.txt +0 -0
@@ -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_mult_count(1).get("count", 0)) + int(
|
254
|
+
self.database.fetch_mult_count(2).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
|