not1mm 24.5.1__tar.gz → 24.5.10__tar.gz
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-24.5.1 → not1mm-24.5.10}/PKG-INFO +9 -3
- {not1mm-24.5.1 → not1mm-24.5.10}/README.md +8 -2
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/__main__.py +18 -10
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/bandmap.py +3 -1
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/checkwindow.py +5 -1
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/new_contest.ui +5 -0
- not1mm-24.5.10/not1mm/lib/playsound.py +296 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/version.py +1 -1
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/logwindow.py +5 -1
- not1mm-24.5.10/not1mm/playsoundtest.py +15 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/10_10_fall_cw.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/10_10_spring_cw.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/10_10_summer_phone.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/10_10_winter_phone.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/arrl_10m.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/arrl_dx_cw.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/arrl_dx_ssb.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/arrl_ss_cw.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/arrl_ss_phone.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/arrl_vhf_jan.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/arrl_vhf_jun.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/arrl_vhf_sep.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/canada_day.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/cq_160_cw.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/cq_160_ssb.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/cq_wpx_cw.py +39 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/cq_wpx_ssb.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/cq_ww_cw.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/cq_ww_ssb.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/cwt.py +26 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/general_logging.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/iaru_hf.py +2 -0
- not1mm-24.5.10/not1mm/plugins/icwc_mst.py +372 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/jidx_cw.py +31 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/jidx_ph.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/naqp_cw.py +32 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/naqp_ssb.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/stew_perry_topband.py +2 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/radio.py +15 -9
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/vfo.py +5 -1
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/voice_keying.py +3 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm.egg-info/PKG-INFO +9 -3
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm.egg-info/SOURCES.txt +3 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/pyproject.toml +1 -1
- {not1mm-24.5.1 → not1mm-24.5.10}/LICENSE +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/__init__.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/JetBrainsMono-Regular.ttf +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/MASTER.SCP +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/about.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/alpha bravo charlie delta.txt +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/bandmap.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/check.png +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/checkwindow.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/configuration.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/contests.sql +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/cty.json +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/cwmacros.txt +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/donors.html +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/editcontact.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/editmacro.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/greendot.png +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/k6gte-not1mm.desktop +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/k6gte.not1mm-128.png +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/k6gte.not1mm-32.png +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/k6gte.not1mm-64.png +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/logwindow.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/logwindowx.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/main.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/not1mm.html +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/opon.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/0.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/1.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/2.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/3.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/4.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/5.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/6.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/7.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/73.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/8.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/9.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/a.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/again.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/b.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/c.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/contest.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/cq.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/d.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/e.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/f.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/g.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/h.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/i.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/j.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/k.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/k6gte.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/l.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/m.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/mynumber.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/n.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/nil.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/o.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/p.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/q.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/r.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/roger.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/s.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/space.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/t.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/thankyou.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/thankyouqrz.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/u.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/v.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/w.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/x.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/y.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/yourcall.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/phonetics/z.wav +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/pickcontest.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/radio_green.png +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/radio_grey.png +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/radio_red.png +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/reddot.png +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/settings.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/ssbmacros.txt +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/data/vfo.ui +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/fsutils.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/__init__.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/about.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/cat_interface.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/cwinterface.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/database.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/edit_contact.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/edit_macro.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/edit_opon.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/edit_station.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/ft8_watcher.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/ham_utility.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/lookup.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/multicast.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/n1mm.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/new_contest.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/plugin_common.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/select_contest.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/settings.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/super_check_partial.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/lib/versiontest.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/__init__.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/arrl_field_day.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/arrl_rtty_ru.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/phone_weekly_test.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm/plugins/winter_field_day.py +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm.egg-info/dependency_links.txt +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm.egg-info/entry_points.txt +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm.egg-info/requires.txt +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/not1mm.egg-info/top_level.txt +0 -0
- {not1mm-24.5.1 → not1mm-24.5.10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: not1mm
|
3
|
-
Version: 24.5.
|
3
|
+
Version: 24.5.10
|
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
|
@@ -169,6 +169,7 @@ I wish to thank those who've contributed to the project.
|
|
169
169
|
- CQ World Wide SSB
|
170
170
|
- CWOps CWT
|
171
171
|
- IARU HF
|
172
|
+
- ICWC MST
|
172
173
|
- Japan International DX CW
|
173
174
|
- Japan International DX SSB
|
174
175
|
- NAQP CW
|
@@ -180,6 +181,8 @@ I wish to thank those who've contributed to the project.
|
|
180
181
|
|
181
182
|
## Recent Changes
|
182
183
|
|
184
|
+
- [24-5-10] Add sanity check for VFO freq.
|
185
|
+
- [24-5-9] Add ICWC MST.
|
183
186
|
- [24-5-1] Moved the voice keying into it's own thread.
|
184
187
|
|
185
188
|
See [CHANGELOG.md](CHANGELOG.md) for prior changes.
|
@@ -212,8 +215,11 @@ noted the minimum steps needed to install not1mm.
|
|
212
215
|
```bash
|
213
216
|
sudo apt update
|
214
217
|
sudo apt upgrade
|
215
|
-
sudo apt install -y
|
216
|
-
pip install -U
|
218
|
+
sudo apt install -y python3-pip python3-numpy libxcb-cursor0 libportaudio2
|
219
|
+
python3 -m pip install -U pip
|
220
|
+
# Logout and back in
|
221
|
+
pip3 install PyQt6
|
222
|
+
pip3 install not1mm
|
217
223
|
```
|
218
224
|
|
219
225
|
#### Ubuntu 23.04
|
@@ -137,6 +137,7 @@ I wish to thank those who've contributed to the project.
|
|
137
137
|
- CQ World Wide SSB
|
138
138
|
- CWOps CWT
|
139
139
|
- IARU HF
|
140
|
+
- ICWC MST
|
140
141
|
- Japan International DX CW
|
141
142
|
- Japan International DX SSB
|
142
143
|
- NAQP CW
|
@@ -148,6 +149,8 @@ I wish to thank those who've contributed to the project.
|
|
148
149
|
|
149
150
|
## Recent Changes
|
150
151
|
|
152
|
+
- [24-5-10] Add sanity check for VFO freq.
|
153
|
+
- [24-5-9] Add ICWC MST.
|
151
154
|
- [24-5-1] Moved the voice keying into it's own thread.
|
152
155
|
|
153
156
|
See [CHANGELOG.md](CHANGELOG.md) for prior changes.
|
@@ -180,8 +183,11 @@ noted the minimum steps needed to install not1mm.
|
|
180
183
|
```bash
|
181
184
|
sudo apt update
|
182
185
|
sudo apt upgrade
|
183
|
-
sudo apt install -y
|
184
|
-
pip install -U
|
186
|
+
sudo apt install -y python3-pip python3-numpy libxcb-cursor0 libportaudio2
|
187
|
+
python3 -m pip install -U pip
|
188
|
+
# Logout and back in
|
189
|
+
pip3 install PyQt6
|
190
|
+
pip3 install not1mm
|
185
191
|
```
|
186
192
|
|
187
193
|
#### Ubuntu 23.04
|
@@ -1,6 +1,9 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
"""
|
3
|
-
|
3
|
+
not1mm Contest logger
|
4
|
+
Email: michael.bridak@gmail.com
|
5
|
+
GPL V3
|
6
|
+
Purpose: Provides main logging window and a crap ton more.
|
4
7
|
"""
|
5
8
|
# pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines, no-name-in-module
|
6
9
|
# pylint: disable=logging-fstring-interpolation, logging-not-lazy, line-too-long, bare-except
|
@@ -1247,6 +1250,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1247
1250
|
else:
|
1248
1251
|
self.cw_speed.hide()
|
1249
1252
|
|
1253
|
+
self.clearinputs()
|
1250
1254
|
cmd = {}
|
1251
1255
|
cmd["cmd"] = "NEWDB"
|
1252
1256
|
cmd["station"] = platform.node()
|
@@ -1848,6 +1852,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1848
1852
|
self.contact = self.database.empty_contact
|
1849
1853
|
self.heading_distance.setText("")
|
1850
1854
|
self.dx_entity.setText("")
|
1855
|
+
|
1851
1856
|
if self.contest:
|
1852
1857
|
mults = self.contest.show_mults(self)
|
1853
1858
|
qsos = self.contest.show_qso(self)
|
@@ -1856,13 +1861,16 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
1856
1861
|
score = self.contest.calc_score(self)
|
1857
1862
|
self.score.setText(str(score))
|
1858
1863
|
self.contest.reset_label(self)
|
1864
|
+
if self.contest.name != "ICWC Medium Speed Test":
|
1865
|
+
if self.current_mode == "CW":
|
1866
|
+
self.sent.setText("599")
|
1867
|
+
self.receive.setText("599")
|
1868
|
+
else:
|
1869
|
+
self.sent.setText("59")
|
1870
|
+
self.receive.setText("59")
|
1871
|
+
else:
|
1872
|
+
self.sent.setText("")
|
1859
1873
|
self.callsign.clear()
|
1860
|
-
if self.current_mode == "CW":
|
1861
|
-
self.sent.setText("599")
|
1862
|
-
self.receive.setText("599")
|
1863
|
-
else:
|
1864
|
-
self.sent.setText("59")
|
1865
|
-
self.receive.setText("59")
|
1866
1874
|
self.other_1.clear()
|
1867
1875
|
self.other_2.clear()
|
1868
1876
|
self.callsign.setFocus()
|
@@ -3010,15 +3018,16 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
3010
3018
|
if mode == "CW":
|
3011
3019
|
if self.current_mode != "CW":
|
3012
3020
|
self.current_mode = "CW"
|
3013
|
-
# self.mode.setText("CW")
|
3014
3021
|
self.sent.setText("599")
|
3015
3022
|
self.receive.setText("599")
|
3016
3023
|
self.read_cw_macros()
|
3024
|
+
if self.contest:
|
3025
|
+
if self.contest.name == "ICWC Medium Speed Test":
|
3026
|
+
self.contest.prefill(self)
|
3017
3027
|
return
|
3018
3028
|
if mode == "SSB":
|
3019
3029
|
if self.current_mode != "SSB":
|
3020
3030
|
self.current_mode = "SSB"
|
3021
|
-
# self.mode.setText("SSB")
|
3022
3031
|
self.sent.setText("59")
|
3023
3032
|
self.receive.setText("59")
|
3024
3033
|
self.read_cw_macros()
|
@@ -3026,7 +3035,6 @@ class MainWindow(QtWidgets.QMainWindow):
|
|
3026
3035
|
if mode == "RTTY":
|
3027
3036
|
if self.current_mode != "RTTY":
|
3028
3037
|
self.current_mode = "RTTY"
|
3029
|
-
# self.mode.setText("RTTY")
|
3030
3038
|
self.sent.setText("59")
|
3031
3039
|
self.receive.setText("59")
|
3032
3040
|
|
@@ -1,8 +1,10 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
"""
|
3
|
-
|
3
|
+
not1mm Contest logger
|
4
4
|
Email: michael.bridak@gmail.com
|
5
5
|
GPL V3
|
6
|
+
Class: BandMapWindow
|
7
|
+
Purpose: Onscreen widget to show realtime spots from an AR cluster.
|
6
8
|
"""
|
7
9
|
|
8
10
|
# pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines
|
@@ -1,6 +1,10 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
"""
|
3
|
-
|
3
|
+
not1mm Contest logger
|
4
|
+
Email: michael.bridak@gmail.com
|
5
|
+
GPL V3
|
6
|
+
Class: CheckWindow
|
7
|
+
Purpose: Onscreen widget to show possible matches to callsigns entered in the main window.
|
4
8
|
"""
|
5
9
|
# pylint: disable=no-name-in-module, unused-import, no-member, invalid-name, c-extension-no-member
|
6
10
|
# pylint: disable=logging-fstring-interpolation, line-too-long
|
@@ -0,0 +1,296 @@
|
|
1
|
+
import logging
|
2
|
+
from platform import system
|
3
|
+
|
4
|
+
logger = logging.getLogger(__name__)
|
5
|
+
|
6
|
+
|
7
|
+
class PlaysoundException(Exception):
|
8
|
+
pass
|
9
|
+
|
10
|
+
|
11
|
+
def _canonicalizePath(path):
|
12
|
+
"""
|
13
|
+
Support passing in a pathlib.Path-like object by converting to str.
|
14
|
+
"""
|
15
|
+
import sys
|
16
|
+
|
17
|
+
if sys.version_info[0] >= 3:
|
18
|
+
return str(path)
|
19
|
+
else:
|
20
|
+
# On earlier Python versions, str is a byte string, so attempting to
|
21
|
+
# convert a unicode string to str will fail. Leave it alone in this case.
|
22
|
+
return path
|
23
|
+
|
24
|
+
|
25
|
+
def _playsoundWin(sound, block=True):
|
26
|
+
"""
|
27
|
+
Utilizes windll.winmm. Tested and known to work with MP3 and WAVE on
|
28
|
+
Windows 7 with Python 2.7. Probably works with more file formats.
|
29
|
+
Probably works on Windows XP thru Windows 10. Probably works with all
|
30
|
+
versions of Python.
|
31
|
+
|
32
|
+
Inspired by (but not copied from) Michael Gundlach <gundlach@gmail.com>'s mp3play:
|
33
|
+
https://github.com/michaelgundlach/mp3play
|
34
|
+
|
35
|
+
I never would have tried using windll.winmm without seeing his code.
|
36
|
+
"""
|
37
|
+
sound = '"' + _canonicalizePath(sound) + '"'
|
38
|
+
|
39
|
+
from ctypes import create_unicode_buffer, windll, wintypes
|
40
|
+
|
41
|
+
windll.winmm.mciSendStringW.argtypes = [
|
42
|
+
wintypes.LPCWSTR,
|
43
|
+
wintypes.LPWSTR,
|
44
|
+
wintypes.UINT,
|
45
|
+
wintypes.HANDLE,
|
46
|
+
]
|
47
|
+
windll.winmm.mciGetErrorStringW.argtypes = [
|
48
|
+
wintypes.DWORD,
|
49
|
+
wintypes.LPWSTR,
|
50
|
+
wintypes.UINT,
|
51
|
+
]
|
52
|
+
|
53
|
+
def winCommand(*command):
|
54
|
+
bufLen = 600
|
55
|
+
buf = create_unicode_buffer(bufLen)
|
56
|
+
command = " ".join(command)
|
57
|
+
errorCode = int(
|
58
|
+
windll.winmm.mciSendStringW(command, buf, bufLen - 1, 0)
|
59
|
+
) # use widestring version of the function
|
60
|
+
if errorCode:
|
61
|
+
errorBuffer = create_unicode_buffer(bufLen)
|
62
|
+
windll.winmm.mciGetErrorStringW(
|
63
|
+
errorCode, errorBuffer, bufLen - 1
|
64
|
+
) # use widestring version of the function
|
65
|
+
exceptionMessage = (
|
66
|
+
"\n Error " + str(errorCode) + " for command:"
|
67
|
+
"\n " + command + "\n " + errorBuffer.value
|
68
|
+
)
|
69
|
+
logger.error(exceptionMessage)
|
70
|
+
raise PlaysoundException(exceptionMessage)
|
71
|
+
return buf.value
|
72
|
+
|
73
|
+
try:
|
74
|
+
logger.debug("Starting")
|
75
|
+
winCommand("open {}".format(sound))
|
76
|
+
winCommand("play {}{}".format(sound, " wait" if block else ""))
|
77
|
+
logger.debug("Returning")
|
78
|
+
finally:
|
79
|
+
try:
|
80
|
+
winCommand("close {}".format(sound))
|
81
|
+
except PlaysoundException:
|
82
|
+
logger.warning("Failed to close the file: {}".format(sound))
|
83
|
+
# If it fails, there's nothing more that can be done...
|
84
|
+
pass
|
85
|
+
|
86
|
+
|
87
|
+
def _handlePathOSX(sound):
|
88
|
+
sound = _canonicalizePath(sound)
|
89
|
+
|
90
|
+
if "://" not in sound:
|
91
|
+
if not sound.startswith("/"):
|
92
|
+
from os import getcwd
|
93
|
+
|
94
|
+
sound = getcwd() + "/" + sound
|
95
|
+
sound = "file://" + sound
|
96
|
+
|
97
|
+
try:
|
98
|
+
# Don't double-encode it.
|
99
|
+
sound.encode("ascii")
|
100
|
+
return sound.replace(" ", "%20")
|
101
|
+
except UnicodeEncodeError:
|
102
|
+
try:
|
103
|
+
from urllib.parse import quote # Try the Python 3 import first...
|
104
|
+
except ImportError:
|
105
|
+
from urllib import (
|
106
|
+
quote,
|
107
|
+
) # Try using the Python 2 import before giving up entirely...
|
108
|
+
|
109
|
+
parts = sound.split("://", 1)
|
110
|
+
return parts[0] + "://" + quote(parts[1].encode("utf-8")).replace(" ", "%20")
|
111
|
+
|
112
|
+
|
113
|
+
def _playsoundOSX(sound, block=True):
|
114
|
+
"""
|
115
|
+
Utilizes AppKit.NSSound. Tested and known to work with MP3 and WAVE on
|
116
|
+
OS X 10.11 with Python 2.7. Probably works with anything QuickTime supports.
|
117
|
+
Probably works on OS X 10.5 and newer. Probably works with all versions of
|
118
|
+
Python.
|
119
|
+
|
120
|
+
Inspired by (but not copied from) Aaron's Stack Overflow answer here:
|
121
|
+
http://stackoverflow.com/a/34568298/901641
|
122
|
+
|
123
|
+
I never would have tried using AppKit.NSSound without seeing his code.
|
124
|
+
"""
|
125
|
+
try:
|
126
|
+
from AppKit import NSSound
|
127
|
+
except ImportError:
|
128
|
+
logger.warning(
|
129
|
+
"playsound could not find a copy of AppKit - falling back to using macOS's system copy."
|
130
|
+
)
|
131
|
+
sys.path.append(
|
132
|
+
"/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC"
|
133
|
+
)
|
134
|
+
from AppKit import NSSound
|
135
|
+
|
136
|
+
from Foundation import NSURL
|
137
|
+
from time import sleep
|
138
|
+
|
139
|
+
sound = _handlePathOSX(sound)
|
140
|
+
url = NSURL.URLWithString_(sound)
|
141
|
+
if not url:
|
142
|
+
raise PlaysoundException("Cannot find a sound with filename: " + sound)
|
143
|
+
|
144
|
+
for i in range(5):
|
145
|
+
nssound = NSSound.alloc().initWithContentsOfURL_byReference_(url, True)
|
146
|
+
if nssound:
|
147
|
+
break
|
148
|
+
else:
|
149
|
+
logger.debug("Failed to load sound, although url was good... " + sound)
|
150
|
+
else:
|
151
|
+
raise PlaysoundException(
|
152
|
+
"Could not load sound with filename, although URL was good... " + sound
|
153
|
+
)
|
154
|
+
nssound.play()
|
155
|
+
|
156
|
+
if block:
|
157
|
+
sleep(nssound.duration())
|
158
|
+
|
159
|
+
|
160
|
+
def _playsoundNix(sound, block=True):
|
161
|
+
"""Play a sound using GStreamer.
|
162
|
+
|
163
|
+
Inspired by this:
|
164
|
+
https://gstreamer.freedesktop.org/documentation/tutorials/playback/playbin-usage.html
|
165
|
+
"""
|
166
|
+
sound = _canonicalizePath(sound)
|
167
|
+
|
168
|
+
# pathname2url escapes non-URL-safe characters
|
169
|
+
from os.path import abspath, exists
|
170
|
+
|
171
|
+
try:
|
172
|
+
from urllib.request import pathname2url
|
173
|
+
except ImportError:
|
174
|
+
# python 2
|
175
|
+
from urllib import pathname2url
|
176
|
+
|
177
|
+
import gi
|
178
|
+
|
179
|
+
gi.require_version("Gst", "1.0")
|
180
|
+
from gi.repository import Gst
|
181
|
+
|
182
|
+
Gst.init(None)
|
183
|
+
|
184
|
+
playbin = Gst.ElementFactory.make("playbin", "playbin")
|
185
|
+
if sound.startswith(("http://", "https://")):
|
186
|
+
playbin.props.uri = sound
|
187
|
+
else:
|
188
|
+
path = abspath(sound)
|
189
|
+
if not exists(path):
|
190
|
+
raise PlaysoundException("File not found: {}".format(path))
|
191
|
+
playbin.props.uri = "file://" + pathname2url(path)
|
192
|
+
|
193
|
+
set_result = playbin.set_state(Gst.State.PLAYING)
|
194
|
+
if set_result != Gst.StateChangeReturn.ASYNC:
|
195
|
+
raise PlaysoundException("playbin.set_state returned " + repr(set_result))
|
196
|
+
|
197
|
+
# FIXME: use some other bus method than poll() with block=False
|
198
|
+
# https://lazka.github.io/pgi-docs/#Gst-1.0/classes/Bus.html
|
199
|
+
logger.debug("Starting play")
|
200
|
+
if block:
|
201
|
+
bus = playbin.get_bus()
|
202
|
+
try:
|
203
|
+
bus.poll(Gst.MessageType.EOS, Gst.CLOCK_TIME_NONE)
|
204
|
+
finally:
|
205
|
+
playbin.set_state(Gst.State.NULL)
|
206
|
+
|
207
|
+
logger.debug("Finishing play")
|
208
|
+
|
209
|
+
|
210
|
+
def _playsoundAnotherPython(otherPython, sound, block=True, macOS=False):
|
211
|
+
"""
|
212
|
+
Mostly written so that when this is run on python3 on macOS, it can invoke
|
213
|
+
python2 on macOS... but maybe this idea could be useful on linux, too.
|
214
|
+
"""
|
215
|
+
from inspect import getsourcefile
|
216
|
+
from os.path import abspath, exists
|
217
|
+
from subprocess import check_call
|
218
|
+
from threading import Thread
|
219
|
+
|
220
|
+
sound = _canonicalizePath(sound)
|
221
|
+
|
222
|
+
class PropogatingThread(Thread):
|
223
|
+
def run(self):
|
224
|
+
self.exc = None
|
225
|
+
try:
|
226
|
+
self.ret = self._target(*self._args, **self._kwargs)
|
227
|
+
except BaseException as e:
|
228
|
+
self.exc = e
|
229
|
+
|
230
|
+
def join(self, timeout=None):
|
231
|
+
super().join(timeout)
|
232
|
+
if self.exc:
|
233
|
+
raise self.exc
|
234
|
+
return self.ret
|
235
|
+
|
236
|
+
# Check if the file exists...
|
237
|
+
if not exists(abspath(sound)):
|
238
|
+
raise PlaysoundException("Cannot find a sound with filename: " + sound)
|
239
|
+
|
240
|
+
playsoundPath = abspath(getsourcefile(lambda: 0))
|
241
|
+
t = PropogatingThread(
|
242
|
+
target=lambda: check_call(
|
243
|
+
[otherPython, playsoundPath, _handlePathOSX(sound) if macOS else sound]
|
244
|
+
)
|
245
|
+
)
|
246
|
+
t.start()
|
247
|
+
if block:
|
248
|
+
t.join()
|
249
|
+
|
250
|
+
|
251
|
+
system = system()
|
252
|
+
|
253
|
+
if system == "Windows":
|
254
|
+
playsound = _playsoundWin
|
255
|
+
elif system == "Darwin":
|
256
|
+
playsound = _playsoundOSX
|
257
|
+
import sys
|
258
|
+
|
259
|
+
if sys.version_info[0] > 2:
|
260
|
+
try:
|
261
|
+
from AppKit import NSSound
|
262
|
+
except ImportError:
|
263
|
+
logger.warning(
|
264
|
+
"playsound is relying on a python 2 subprocess. Please use `pip3 install PyObjC` if you want playsound to run more efficiently."
|
265
|
+
)
|
266
|
+
playsound = lambda sound, block=True: _playsoundAnotherPython(
|
267
|
+
"/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python",
|
268
|
+
sound,
|
269
|
+
block,
|
270
|
+
macOS=True,
|
271
|
+
)
|
272
|
+
else:
|
273
|
+
playsound = _playsoundNix
|
274
|
+
if (
|
275
|
+
__name__ != "__main__"
|
276
|
+
): # Ensure we don't infinitely recurse trying to get another python instance.
|
277
|
+
try:
|
278
|
+
import gi
|
279
|
+
|
280
|
+
gi.require_version("Gst", "1.0")
|
281
|
+
from gi.repository import Gst
|
282
|
+
except:
|
283
|
+
logger.warning(
|
284
|
+
"playsound is relying on another python subprocess. Please use `pip install pygobject` if you want playsound to run more efficiently."
|
285
|
+
)
|
286
|
+
playsound = lambda sound, block=True: _playsoundAnotherPython(
|
287
|
+
"/usr/bin/python3", sound, block, macOS=False
|
288
|
+
)
|
289
|
+
|
290
|
+
del system
|
291
|
+
|
292
|
+
if __name__ == "__main__":
|
293
|
+
# block is always True if you choose to run this from the command line.
|
294
|
+
from sys import argv
|
295
|
+
|
296
|
+
playsound(argv[1])
|
@@ -1,6 +1,10 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
"""
|
3
|
-
|
3
|
+
not1mm Contest logger
|
4
|
+
Email: michael.bridak@gmail.com
|
5
|
+
GPL V3
|
6
|
+
Class: LogWindow
|
7
|
+
Purpose: Onscreen widget to show and edit logged contacts.
|
4
8
|
"""
|
5
9
|
# pylint: disable=no-name-in-module, unused-import, no-member, c-extension-no-member
|
6
10
|
# pylint: disable=logging-fstring-interpolation, too-many-lines
|
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
NOT1MM Logger
|
4
|
+
Purpose: test alternative sound playing interface
|
5
|
+
"""
|
6
|
+
# pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines, no-name-in-module
|
7
|
+
# pylint: disable=logging-fstring-interpolation, logging-not-lazy, line-too-long, bare-except
|
8
|
+
|
9
|
+
from not1mm.lib.playsound import playsound
|
10
|
+
|
11
|
+
import not1mm.fsutils as fsutils
|
12
|
+
|
13
|
+
filename = fsutils.APP_DATA_PATH / "phonetics/cq.wav"
|
14
|
+
|
15
|
+
playsound(filename, True)
|
@@ -48,6 +48,8 @@ def interface(self):
|
|
48
48
|
self.field2.show()
|
49
49
|
self.field3.hide()
|
50
50
|
self.field4.show()
|
51
|
+
self.snt_label.setText("SNT")
|
52
|
+
self.field1.setAccessibleName("RST Sent")
|
51
53
|
label = self.field4.findChild(QtWidgets.QLabel)
|
52
54
|
label.setText("Name 1010# SPC")
|
53
55
|
self.field4.setAccessibleName("Name 10 10 # SPC")
|
@@ -47,6 +47,8 @@ def interface(self):
|
|
47
47
|
self.field2.show()
|
48
48
|
self.field3.hide()
|
49
49
|
self.field4.show()
|
50
|
+
self.snt_label.setText("SNT")
|
51
|
+
self.field1.setAccessibleName("RST Sent")
|
50
52
|
label = self.field4.findChild(QtWidgets.QLabel)
|
51
53
|
label.setText("Name 1010# SPC")
|
52
54
|
self.field4.setAccessibleName("Name 10 10 # SPC")
|
@@ -49,6 +49,8 @@ def interface(self):
|
|
49
49
|
self.field2.show()
|
50
50
|
self.field3.hide()
|
51
51
|
self.field4.show()
|
52
|
+
self.snt_label.setText("SNT")
|
53
|
+
self.field1.setAccessibleName("RST Sent")
|
52
54
|
label = self.field4.findChild(QtWidgets.QLabel)
|
53
55
|
label.setText("Name 1010# SPC")
|
54
56
|
self.field4.setAccessibleName("Name 10 10 # SPC")
|
@@ -49,6 +49,8 @@ def interface(self):
|
|
49
49
|
self.field2.show()
|
50
50
|
self.field3.hide()
|
51
51
|
self.field4.show()
|
52
|
+
self.snt_label.setText("SNT")
|
53
|
+
self.field1.setAccessibleName("RST Sent")
|
52
54
|
label = self.field4.findChild(QtWidgets.QLabel)
|
53
55
|
label.setText("Name 1010# SPC")
|
54
56
|
self.field4.setAccessibleName("Name 10 10 # SPC")
|
@@ -94,6 +94,8 @@ def interface(self):
|
|
94
94
|
self.field2.show()
|
95
95
|
self.field3.show()
|
96
96
|
self.field4.show()
|
97
|
+
self.snt_label.setText("SNT")
|
98
|
+
self.field1.setAccessibleName("RST Sent")
|
97
99
|
label = self.field3.findChild(QtWidgets.QLabel)
|
98
100
|
label.setText("SentNR")
|
99
101
|
self.field3.setAccessibleName("Sent Number")
|
@@ -53,6 +53,8 @@ def interface(self):
|
|
53
53
|
self.field2.show()
|
54
54
|
self.field3.hide()
|
55
55
|
self.field4.show()
|
56
|
+
self.snt_label.setText("SNT")
|
57
|
+
self.field1.setAccessibleName("RST Sent")
|
56
58
|
label = self.field4.findChild(QtWidgets.QLabel)
|
57
59
|
label.setText("Power")
|
58
60
|
self.field4.setAccessibleName("Power")
|
@@ -53,6 +53,8 @@ def interface(self):
|
|
53
53
|
self.field2.show()
|
54
54
|
self.field3.hide()
|
55
55
|
self.field4.show()
|
56
|
+
self.snt_label.setText("SNT")
|
57
|
+
self.field1.setAccessibleName("RST Sent")
|
56
58
|
label = self.field4.findChild(QtWidgets.QLabel)
|
57
59
|
label.setText("Power")
|
58
60
|
self.field4.setAccessibleName("Power")
|
@@ -52,6 +52,8 @@ def interface(self):
|
|
52
52
|
self.field2.show()
|
53
53
|
self.field3.show()
|
54
54
|
self.field4.show()
|
55
|
+
self.snt_label.setText("SNT")
|
56
|
+
self.field1.setAccessibleName("RST Sent")
|
55
57
|
label = self.field3.findChild(QtWidgets.QLabel)
|
56
58
|
label.setText("SentNR")
|
57
59
|
self.field3.setAccessibleName("Sent Number")
|
@@ -52,6 +52,8 @@ def interface(self):
|
|
52
52
|
self.field2.show()
|
53
53
|
self.field3.show()
|
54
54
|
self.field4.show()
|
55
|
+
self.snt_label.setText("SNT")
|
56
|
+
self.field1.setAccessibleName("RST Sent")
|
55
57
|
label = self.field3.findChild(QtWidgets.QLabel)
|
56
58
|
label.setText("SentNR")
|
57
59
|
self.field3.setAccessibleName("Sent Number")
|
@@ -83,6 +83,8 @@ def interface(self):
|
|
83
83
|
self.field2.show()
|
84
84
|
self.field3.show()
|
85
85
|
self.field4.show()
|
86
|
+
self.snt_label.setText("SNT")
|
87
|
+
self.field1.setAccessibleName("RST Sent")
|
86
88
|
label = self.field3.findChild(QtWidgets.QLabel)
|
87
89
|
label.setText("SentNR")
|
88
90
|
self.field3.setAccessibleName("Sent Grid")
|
@@ -51,6 +51,8 @@ def interface(self):
|
|
51
51
|
self.field2.show()
|
52
52
|
self.field3.show()
|
53
53
|
self.field4.show()
|
54
|
+
self.snt_label.setText("SNT")
|
55
|
+
self.field1.setAccessibleName("RST Sent")
|
54
56
|
label = self.field3.findChild(QtWidgets.QLabel)
|
55
57
|
label.setText("SentNR")
|
56
58
|
self.field3.setAccessibleName("Sent Grid")
|
@@ -51,6 +51,8 @@ def interface(self):
|
|
51
51
|
self.field2.show()
|
52
52
|
self.field3.show()
|
53
53
|
self.field4.show()
|
54
|
+
self.snt_label.setText("SNT")
|
55
|
+
self.field1.setAccessibleName("RST Sent")
|
54
56
|
label = self.field3.findChild(QtWidgets.QLabel)
|
55
57
|
label.setText("SentNR")
|
56
58
|
self.field3.setAccessibleName("Sent Grid")
|
@@ -84,6 +84,8 @@ def interface(self):
|
|
84
84
|
self.field2.show()
|
85
85
|
self.field3.show()
|
86
86
|
self.field4.show()
|
87
|
+
self.snt_label.setText("SNT")
|
88
|
+
self.field1.setAccessibleName("RST Sent")
|
87
89
|
label = self.field3.findChild(QtWidgets.QLabel)
|
88
90
|
label.setText("SentNR")
|
89
91
|
self.field3.setAccessibleName("Sent Number")
|
@@ -53,6 +53,8 @@ def interface(self):
|
|
53
53
|
self.field2.show()
|
54
54
|
self.field3.hide()
|
55
55
|
self.field4.show()
|
56
|
+
self.snt_label.setText("SNT")
|
57
|
+
self.field1.setAccessibleName("RST Sent")
|
56
58
|
# self.other_label.setText("SentNR")
|
57
59
|
# self.field3.setAccessibleName("Sent Number")
|
58
60
|
self.exch_label.setText("ST/Prov/CQ Zone")
|
@@ -53,6 +53,8 @@ def interface(self):
|
|
53
53
|
self.field2.show()
|
54
54
|
self.field3.hide()
|
55
55
|
self.field4.show()
|
56
|
+
self.snt_label.setText("SNT")
|
57
|
+
self.field1.setAccessibleName("RST Sent")
|
56
58
|
# self.other_label.setText("SentNR")
|
57
59
|
# self.field3.setAccessibleName("Sent Number")
|
58
60
|
self.exch_label.setText("ST/Prov/CQ Zone")
|