not1mm 24.4.30__py3-none-any.whl → 24.5.9__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 (45) hide show
  1. not1mm/__main__.py +40 -67
  2. not1mm/bandmap.py +3 -1
  3. not1mm/checkwindow.py +5 -1
  4. not1mm/data/new_contest.ui +5 -0
  5. not1mm/lib/playsound.py +296 -0
  6. not1mm/lib/version.py +1 -1
  7. not1mm/logwindow.py +5 -1
  8. not1mm/playsoundtest.py +15 -0
  9. not1mm/plugins/10_10_fall_cw.py +2 -0
  10. not1mm/plugins/10_10_spring_cw.py +2 -0
  11. not1mm/plugins/10_10_summer_phone.py +2 -0
  12. not1mm/plugins/10_10_winter_phone.py +2 -0
  13. not1mm/plugins/arrl_10m.py +2 -0
  14. not1mm/plugins/arrl_dx_cw.py +2 -0
  15. not1mm/plugins/arrl_dx_ssb.py +2 -0
  16. not1mm/plugins/arrl_ss_cw.py +2 -0
  17. not1mm/plugins/arrl_ss_phone.py +2 -0
  18. not1mm/plugins/arrl_vhf_jan.py +2 -0
  19. not1mm/plugins/arrl_vhf_jun.py +2 -0
  20. not1mm/plugins/arrl_vhf_sep.py +2 -0
  21. not1mm/plugins/canada_day.py +2 -0
  22. not1mm/plugins/cq_160_cw.py +2 -0
  23. not1mm/plugins/cq_160_ssb.py +2 -0
  24. not1mm/plugins/cq_wpx_cw.py +39 -0
  25. not1mm/plugins/cq_wpx_ssb.py +2 -0
  26. not1mm/plugins/cq_ww_cw.py +2 -0
  27. not1mm/plugins/cq_ww_ssb.py +2 -0
  28. not1mm/plugins/cwt.py +26 -0
  29. not1mm/plugins/general_logging.py +2 -0
  30. not1mm/plugins/iaru_hf.py +2 -0
  31. not1mm/plugins/icwc_mst.py +372 -0
  32. not1mm/plugins/jidx_cw.py +31 -0
  33. not1mm/plugins/jidx_ph.py +2 -0
  34. not1mm/plugins/naqp_cw.py +32 -0
  35. not1mm/plugins/naqp_ssb.py +2 -0
  36. not1mm/plugins/stew_perry_topband.py +2 -0
  37. not1mm/radio.py +12 -9
  38. not1mm/vfo.py +5 -1
  39. not1mm/voice_keying.py +103 -0
  40. {not1mm-24.4.30.dist-info → not1mm-24.5.9.dist-info}/METADATA +9 -21
  41. {not1mm-24.4.30.dist-info → not1mm-24.5.9.dist-info}/RECORD +45 -41
  42. {not1mm-24.4.30.dist-info → not1mm-24.5.9.dist-info}/LICENSE +0 -0
  43. {not1mm-24.4.30.dist-info → not1mm-24.5.9.dist-info}/WHEEL +0 -0
  44. {not1mm-24.4.30.dist-info → not1mm-24.5.9.dist-info}/entry_points.txt +0 -0
  45. {not1mm-24.4.30.dist-info → not1mm-24.5.9.dist-info}/top_level.txt +0 -0
not1mm/__main__.py CHANGED
@@ -1,6 +1,9 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- NOT1MM Logger
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
@@ -35,7 +38,6 @@ except OSError as exception:
35
38
  print(exception)
36
39
  print("portaudio is not installed")
37
40
  sd = None
38
- import soundfile as sf
39
41
  from PyQt6 import QtCore, QtGui, QtWidgets, uic
40
42
  from PyQt6.QtCore import QDir, Qt, QThread
41
43
  from PyQt6.QtGui import QFontDatabase, QColorConstants, QPalette, QColor
@@ -75,6 +77,7 @@ from not1mm.checkwindow import CheckWindow
75
77
  from not1mm.bandmap import BandMapWindow
76
78
  from not1mm.vfo import VfoWindow
77
79
  from not1mm.radio import Radio
80
+ from not1mm.voice_keying import Voice
78
81
 
79
82
  poll_time = datetime.datetime.now()
80
83
 
@@ -188,6 +191,7 @@ class MainWindow(QtWidgets.QMainWindow):
188
191
  current_palette = None
189
192
 
190
193
  radio_thread = QThread()
194
+ voice_thread = QThread()
191
195
 
192
196
  rig_control = None
193
197
  log_window = None
@@ -479,6 +483,18 @@ class MainWindow(QtWidgets.QMainWindow):
479
483
  with open(fsutils.APP_DATA_PATH / "cty.json", "rt", encoding="utf-8") as c_file:
480
484
  self.ctyfile = loads(c_file.read())
481
485
  self.readpreferences()
486
+
487
+ self.voice_process = Voice()
488
+ self.voice_process.moveToThread(self.voice_thread)
489
+ self.voice_thread.started.connect(self.voice_process.run)
490
+ self.voice_thread.finished.connect(self.voice_process.deleteLater)
491
+ self.voice_process.ptt_on.connect(self.ptt_on)
492
+ self.voice_process.ptt_off.connect(self.ptt_off)
493
+ self.voice_process.current_op = self.current_op
494
+ self.voice_process.data_path = fsutils.USER_DATA_PATH
495
+ self.voice_process.sounddevice = self.pref.get("sounddevice", "default")
496
+ self.voice_thread.start()
497
+
482
498
  self.dbname = fsutils.USER_DATA_PATH / self.pref.get(
483
499
  "current_database", "ham.db"
484
500
  )
@@ -492,6 +508,7 @@ class MainWindow(QtWidgets.QMainWindow):
492
508
  self.station = {}
493
509
  self.contact = self.database.empty_contact
494
510
  self.current_op = self.station.get("Call", "")
511
+ self.voice_process.current_op = self.current_op
495
512
  self.make_op_dir()
496
513
  self.read_cw_macros()
497
514
  self.clearinputs()
@@ -922,6 +939,7 @@ class MainWindow(QtWidgets.QMainWindow):
922
939
  if self.station is None:
923
940
  self.station = {}
924
941
  self.current_op = self.station.get("Call", "")
942
+ self.voice_process.current_op = self.current_op
925
943
  self.make_op_dir()
926
944
  cmd = {}
927
945
  cmd["cmd"] = "NEWDB"
@@ -958,6 +976,7 @@ class MainWindow(QtWidgets.QMainWindow):
958
976
  if self.station.get("Call", "") == "":
959
977
  self.edit_station_settings()
960
978
  self.current_op = self.station.get("Call", "")
979
+ self.voice_process.current_op = self.current_op
961
980
  self.make_op_dir()
962
981
  cmd = {}
963
982
  cmd["cmd"] = "NEWDB"
@@ -1231,6 +1250,7 @@ class MainWindow(QtWidgets.QMainWindow):
1231
1250
  else:
1232
1251
  self.cw_speed.hide()
1233
1252
 
1253
+ self.clearinputs()
1234
1254
  cmd = {}
1235
1255
  cmd["cmd"] = "NEWDB"
1236
1256
  cmd["station"] = platform.node()
@@ -1832,6 +1852,7 @@ class MainWindow(QtWidgets.QMainWindow):
1832
1852
  self.contact = self.database.empty_contact
1833
1853
  self.heading_distance.setText("")
1834
1854
  self.dx_entity.setText("")
1855
+
1835
1856
  if self.contest:
1836
1857
  mults = self.contest.show_mults(self)
1837
1858
  qsos = self.contest.show_qso(self)
@@ -1840,13 +1861,16 @@ class MainWindow(QtWidgets.QMainWindow):
1840
1861
  score = self.contest.calc_score(self)
1841
1862
  self.score.setText(str(score))
1842
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("")
1843
1873
  self.callsign.clear()
1844
- if self.current_mode == "CW":
1845
- self.sent.setText("599")
1846
- self.receive.setText("599")
1847
- else:
1848
- self.sent.setText("59")
1849
- self.receive.setText("59")
1850
1874
  self.other_1.clear()
1851
1875
  self.other_2.clear()
1852
1876
  self.callsign.setFocus()
@@ -2118,6 +2142,7 @@ class MainWindow(QtWidgets.QMainWindow):
2118
2142
  self.settings_dialog.close()
2119
2143
  if self.current_op == "":
2120
2144
  self.current_op = self.station.get("Call", "")
2145
+ self.voice_process.current_op = self.current_op
2121
2146
  self.make_op_dir()
2122
2147
  contest_count = self.database.fetch_all_contests()
2123
2148
  if len(contest_count) == 0:
@@ -2199,61 +2224,6 @@ class MainWindow(QtWidgets.QMainWindow):
2199
2224
  )
2200
2225
  return macro
2201
2226
 
2202
- def voice_string(self, the_string: str) -> None:
2203
- """
2204
- voices string using nato phonetics.
2205
-
2206
- Parameters
2207
- ----------
2208
- the_string : str
2209
- String to voicify.
2210
-
2211
- Returns
2212
- -------
2213
- None
2214
- """
2215
-
2216
- logger.debug("Voicing: %s", the_string)
2217
- if sd is None:
2218
- logger.warning("Sounddevice/portaudio not installed.")
2219
- return
2220
- op_path = fsutils.USER_DATA_PATH / self.current_op
2221
- if "[" in the_string:
2222
- sub_string = the_string.strip("[]").lower()
2223
- filename = f"{str(op_path)}/{sub_string}.wav"
2224
- if Path(filename).is_file():
2225
- logger.debug("Voicing: %s", filename)
2226
- data, _fs = sf.read(filename, dtype="float32")
2227
- self.ptt_on()
2228
- try:
2229
- sd.default.device = self.pref.get("sounddevice", "default")
2230
- sd.default.samplerate = 44100.0
2231
- sd.play(data, blocking=False)
2232
- # _status = sd.wait()
2233
- # https://snyk.io/advisor/python/sounddevice/functions/sounddevice.PortAudioError
2234
- except sd.PortAudioError as err:
2235
- logger.warning("%s", f"{err}")
2236
-
2237
- self.ptt_off()
2238
- return
2239
- self.ptt_on()
2240
- for letter in the_string.lower():
2241
- if letter in "abcdefghijklmnopqrstuvwxyz 1234567890":
2242
- if letter == " ":
2243
- letter = "space"
2244
- filename = f"{str(op_path)}/{letter}.wav"
2245
- if Path(filename).is_file():
2246
- logger.debug("Voicing: %s", filename)
2247
- data, _fs = sf.read(filename, dtype="float32")
2248
- try:
2249
- sd.default.device = self.pref.get("sounddevice", "default")
2250
- sd.default.samplerate = 44100.0
2251
- sd.play(data, blocking=False)
2252
- logger.debug("%s", f"{sd.wait()}")
2253
- except sd.PortAudioError as err:
2254
- logger.warning("%s", f"{err}")
2255
- self.ptt_off()
2256
-
2257
2227
  def ptt_on(self) -> None:
2258
2228
  """
2259
2229
  Turn on ptt for rig.
@@ -2285,6 +2255,7 @@ class MainWindow(QtWidgets.QMainWindow):
2285
2255
  -------
2286
2256
  None
2287
2257
  """
2258
+
2288
2259
  logger.debug("PTT Off")
2289
2260
  if self.rig_control:
2290
2261
  self.leftdot.setPixmap(self.reddot)
@@ -2309,7 +2280,8 @@ class MainWindow(QtWidgets.QMainWindow):
2309
2280
  if self.n1mm:
2310
2281
  self.n1mm.radio_info["FunctionKeyCaption"] = function_key.text()
2311
2282
  if self.radio_state.get("mode") in ["LSB", "USB", "SSB"]:
2312
- self.voice_string(self.process_macro(function_key.toolTip()))
2283
+ self.voice_process.voice_string(self.process_macro(function_key.toolTip()))
2284
+ # self.voice_string(self.process_macro(function_key.toolTip()))
2313
2285
  return
2314
2286
  if self.cw:
2315
2287
  if self.pref.get("cwtype") == 3 and self.rig_control is not None:
@@ -3046,15 +3018,16 @@ class MainWindow(QtWidgets.QMainWindow):
3046
3018
  if mode == "CW":
3047
3019
  if self.current_mode != "CW":
3048
3020
  self.current_mode = "CW"
3049
- # self.mode.setText("CW")
3050
3021
  self.sent.setText("599")
3051
3022
  self.receive.setText("599")
3052
3023
  self.read_cw_macros()
3024
+ if self.contest:
3025
+ if self.contest.name == "ICWC Medium Speed Test":
3026
+ self.contest.prefill(self)
3053
3027
  return
3054
3028
  if mode == "SSB":
3055
3029
  if self.current_mode != "SSB":
3056
3030
  self.current_mode = "SSB"
3057
- # self.mode.setText("SSB")
3058
3031
  self.sent.setText("59")
3059
3032
  self.receive.setText("59")
3060
3033
  self.read_cw_macros()
@@ -3062,7 +3035,6 @@ class MainWindow(QtWidgets.QMainWindow):
3062
3035
  if mode == "RTTY":
3063
3036
  if self.current_mode != "RTTY":
3064
3037
  self.current_mode = "RTTY"
3065
- # self.mode.setText("RTTY")
3066
3038
  self.sent.setText("59")
3067
3039
  self.receive.setText("59")
3068
3040
 
@@ -3103,6 +3075,7 @@ class MainWindow(QtWidgets.QMainWindow):
3103
3075
 
3104
3076
  if self.opon_dialog.NewOperator.text():
3105
3077
  self.current_op = self.opon_dialog.NewOperator.text().upper()
3078
+ self.voice_process.current_op = self.current_op
3106
3079
  self.opon_dialog.close()
3107
3080
  logger.debug("New Op: %s", self.current_op)
3108
3081
  self.make_op_dir()
not1mm/bandmap.py CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- K6GTE Contest logger
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
not1mm/checkwindow.py CHANGED
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- Check Window
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
@@ -297,6 +297,11 @@
297
297
  <string>CWT</string>
298
298
  </property>
299
299
  </item>
300
+ <item>
301
+ <property name="text">
302
+ <string>ICWC MST</string>
303
+ </property>
304
+ </item>
300
305
  <item>
301
306
  <property name="text">
302
307
  <string>IARU HF</string>
@@ -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])
not1mm/lib/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """It's the version"""
2
2
 
3
- __version__ = "24.4.30"
3
+ __version__ = "24.5.9"
not1mm/logwindow.py CHANGED
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- Display current log
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")