not1mm 24.4.26__py3-none-any.whl → 24.5.1__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/lib/cwinterface.py CHANGED
@@ -15,10 +15,17 @@ logger = logging.getLogger("cwinterface")
15
15
 
16
16
 
17
17
  class CW:
18
- """An interface to cwdaemon and PyWinkeyerSerial"""
18
+ """
19
+
20
+ An interface to cwdaemon and PyWinkeyerSerial
21
+
22
+ servertype: int 1=cwdaemon, 2=pywinkeyer, 3=rigctld
23
+
24
+ """
19
25
 
20
26
  def __init__(self, servertype: int, host: str, port: int) -> None:
21
27
  self.servertype = servertype
28
+ self.cat = None
22
29
  self.host = host
23
30
  self.port = port
24
31
  self.speed = 20
@@ -42,35 +49,40 @@ class CW:
42
49
 
43
50
  def sendcw(self, texttosend):
44
51
  """sends cw to k1el"""
45
- logger.info("sendcw: %s", texttosend)
52
+ logger.debug(f"{texttosend=} {self.servertype=}")
46
53
  if self.servertype == 2:
47
54
  self._sendcw_xmlrpc(texttosend)
48
55
  if self.servertype == 1:
49
56
  self._sendcw_udp(texttosend)
57
+ if self.servertype == 3 and self.cw is not None:
58
+ self._sendcwcat(texttosend)
50
59
 
51
60
  def _sendcw_xmlrpc(self, texttosend):
52
61
  """sends cw to xmlrpc"""
53
- logger.info("xmlrpc: %s", texttosend)
62
+ logger.debug("xmlrpc: %s", texttosend)
54
63
  with ServerProxy(f"http://{self.host}:{self.port}") as proxy:
55
64
  try:
56
65
  proxy.k1elsendstring(texttosend)
57
66
  except Error as exception:
58
- logger.info(
67
+ logger.debug(
59
68
  "http://%s:%s, xmlrpc error: %s", self.host, self.port, exception
60
69
  )
61
70
  except ConnectionRefusedError:
62
- logger.info(
71
+ logger.debug(
63
72
  "http://%s:%s, xmlrpc Connection Refused", self.host, self.port
64
73
  )
65
74
 
66
75
  def _sendcw_udp(self, texttosend):
67
76
  """send cw to udp port"""
68
- logger.info("UDP: %s", texttosend)
77
+ logger.debug("UDP: %s", texttosend)
69
78
  server_address_port = (self.host, self.port)
70
79
  # bufferSize = 1024
71
80
  udp_client_socket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
72
81
  udp_client_socket.sendto(bytes(texttosend, "utf-8"), server_address_port)
73
82
 
83
+ def _sendcwcat(self, texttosend):
84
+ """..."""
85
+
74
86
  def set_winkeyer_speed(self, speed):
75
87
  """doc"""
76
88
  with ServerProxy(f"http://{self.host}:{self.port}") as proxy:
not1mm/lib/settings.py CHANGED
@@ -58,6 +58,9 @@ class Settings(QtWidgets.QDialog):
58
58
  self.usepywinkeyer_radioButton.setChecked(
59
59
  bool(self.preference.get("cwtype") == 2)
60
60
  )
61
+ self.usecwviacat_radioButton.setChecked(
62
+ bool(self.preference.get("cwtype") == 3)
63
+ )
61
64
  self.connect_to_server.setChecked(bool(self.preference.get("useserver")))
62
65
  self.multicast_group.setText(str(self.preference.get("multicast_group", "")))
63
66
  self.multicast_port.setText(str(self.preference.get("multicast_port", "")))
@@ -131,8 +134,10 @@ class Settings(QtWidgets.QDialog):
131
134
  self.preference["cwtype"] = 0
132
135
  if self.usecwdaemon_radioButton.isChecked():
133
136
  self.preference["cwtype"] = 1
134
- if self.usepywinkeyer_radioButton.isChecked():
137
+ elif self.usepywinkeyer_radioButton.isChecked():
135
138
  self.preference["cwtype"] = 2
139
+ elif self.usecwviacat_radioButton.isChecked():
140
+ self.preference["cwtype"] = 3
136
141
  self.preference["useserver"] = self.connect_to_server.isChecked()
137
142
  self.preference["multicast_group"] = self.multicast_group.text()
138
143
  self.preference["multicast_port"] = self.multicast_port.text()
not1mm/lib/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """It's the version"""
2
2
 
3
- __version__ = "24.4.26"
3
+ __version__ = "24.5.1"
not1mm/logwindow.py CHANGED
@@ -17,6 +17,7 @@ import math
17
17
  from PyQt6 import QtCore, QtGui, QtWidgets, uic
18
18
  from PyQt6.QtCore import QItemSelectionModel
19
19
  from PyQt6.QtWidgets import QDockWidget
20
+ from PyQt6.QtGui import QColorConstants, QPalette, QColor
20
21
 
21
22
  import not1mm.fsutils as fsutils
22
23
  from not1mm.lib.database import DataBase
@@ -167,58 +168,44 @@ class LogWindow(QDockWidget):
167
168
 
168
169
  self.multicast_interface.send_as_json(cmd)
169
170
 
170
- def set_dark_mode(self, dark: bool):
171
- """testing"""
171
+ def set_dark_mode(self, dark: bool) -> None:
172
+ """Forces a darkmode palette."""
172
173
 
173
174
  if dark:
174
- dark_palette = QtGui.QPalette()
175
- dark_color = QtGui.QColor(56, 56, 56)
176
- disabled_color = QtGui.QColor(127, 127, 127)
177
- dark_palette.setColor(QtGui.QPalette.ColorRole.Window, dark_color)
178
- dark_palette.setColor(
179
- QtGui.QPalette.ColorRole.WindowText, QtGui.QColorConstants.White
180
- )
181
- dark_palette.setColor(
182
- QtGui.QPalette.ColorRole.Base, QtGui.QColor(45, 45, 45)
183
- )
184
- dark_palette.setColor(QtGui.QPalette.ColorRole.AlternateBase, dark_color)
185
- dark_palette.setColor(
186
- QtGui.QPalette.ColorRole.Text, QtGui.QColorConstants.White
187
- )
188
- dark_palette.setColor(
189
- QtGui.QPalette.ColorGroup.Disabled,
190
- QtGui.QPalette.ColorRole.Text,
191
- disabled_color,
192
- )
193
- dark_palette.setColor(QtGui.QPalette.ColorRole.Button, dark_color)
194
- dark_palette.setColor(
195
- QtGui.QPalette.ColorRole.ButtonText, QtGui.QColorConstants.White
196
- )
197
- dark_palette.setColor(
198
- QtGui.QPalette.ColorGroup.Disabled,
199
- QtGui.QPalette.ColorRole.ButtonText,
200
- disabled_color,
201
- )
202
- dark_palette.setColor(
203
- QtGui.QPalette.ColorRole.BrightText, QtGui.QColorConstants.Red
204
- )
205
- dark_palette.setColor(
206
- QtGui.QPalette.ColorRole.Link, QtGui.QColor(42, 130, 218)
207
- )
208
- dark_palette.setColor(
209
- QtGui.QPalette.ColorRole.Highlight, QtGui.QColor(42, 130, 218)
210
- )
211
- dark_palette.setColor(
212
- QtGui.QPalette.ColorRole.HighlightedText, QtGui.QColorConstants.Black
213
- )
214
- dark_palette.setColor(
215
- QtGui.QPalette.ColorGroup.Disabled,
216
- QtGui.QPalette.ColorRole.HighlightedText,
217
- disabled_color,
218
- )
219
-
220
- self.setPalette(dark_palette)
221
- self.current_palette = dark_palette
175
+ darkPalette = QPalette()
176
+ darkColor = QColor(56, 56, 56)
177
+ disabledColor = QColor(127, 127, 127)
178
+ darkPalette.setColor(QPalette.ColorRole.Window, darkColor)
179
+ darkPalette.setColor(QPalette.ColorRole.WindowText, QColorConstants.White)
180
+ darkPalette.setColor(QPalette.ColorRole.Base, QColor(45, 45, 45))
181
+ darkPalette.setColor(QPalette.ColorRole.AlternateBase, darkColor)
182
+ darkPalette.setColor(QPalette.ColorRole.Text, QColorConstants.White)
183
+ darkPalette.setColor(QPalette.ColorRole.Button, darkColor)
184
+ darkPalette.setColor(QPalette.ColorRole.ButtonText, QColorConstants.White)
185
+ darkPalette.setColor(QPalette.ColorRole.BrightText, QColorConstants.Red)
186
+ darkPalette.setColor(QPalette.ColorRole.Link, QColor(42, 130, 218))
187
+ darkPalette.setColor(QPalette.ColorRole.Highlight, QColor(42, 130, 218))
188
+ darkPalette.setColor(
189
+ QPalette.ColorRole.HighlightedText, QColorConstants.Black
190
+ )
191
+ darkPalette.setColor(
192
+ QPalette.ColorGroup.Disabled,
193
+ QPalette.ColorRole.ButtonText,
194
+ disabledColor,
195
+ )
196
+ darkPalette.setColor(
197
+ QPalette.ColorGroup.Disabled,
198
+ QPalette.ColorRole.HighlightedText,
199
+ disabledColor,
200
+ )
201
+ darkPalette.setColor(
202
+ QPalette.ColorGroup.Disabled,
203
+ QPalette.ColorRole.Text,
204
+ disabledColor,
205
+ )
206
+
207
+ self.setPalette(darkPalette)
208
+ self.current_palette = darkPalette
222
209
  else:
223
210
  palette = self.style().standardPalette()
224
211
  self.setPalette(palette)
not1mm/radio.py CHANGED
@@ -10,10 +10,13 @@ GPL V3
10
10
  # pylint: disable=logging-fstring-interpolation, line-too-long, no-name-in-module
11
11
 
12
12
  import datetime
13
+ import logging
13
14
 
14
15
  from PyQt6.QtCore import QObject, pyqtSignal, QThread
15
16
  from not1mm.lib.cat_interface import CAT
16
17
 
18
+ logger = logging.getLogger("cat_interface")
19
+
17
20
 
18
21
  class Radio(QObject):
19
22
  """Radio class"""
@@ -72,6 +75,12 @@ class Radio(QObject):
72
75
  )
73
76
  QThread.msleep(100)
74
77
 
78
+ def sendcw(self, texttosend):
79
+ """..."""
80
+ logger.debug(f"Send CW: {texttosend}")
81
+ if self.cat:
82
+ self.cat.sendcw(texttosend)
83
+
75
84
  def set_vfo(self, vfo):
76
85
  if self.cat:
77
86
  self.cat.set_vfo(vfo)
not1mm/vfo.py CHANGED
@@ -14,10 +14,10 @@ import platform
14
14
  from json import loads, JSONDecodeError
15
15
 
16
16
  import serial
17
- from PyQt6 import QtCore, QtGui, QtWidgets, uic
17
+ from PyQt6 import QtCore, QtWidgets, uic
18
18
  from PyQt6.QtCore import QTimer
19
19
  from PyQt6.QtWidgets import QDockWidget
20
- from PyQt6.QtGui import QColorConstants
20
+ from PyQt6.QtGui import QColorConstants, QPalette, QColor
21
21
 
22
22
  import not1mm.fsutils as fsutils
23
23
  from not1mm.lib.cat_interface import CAT
@@ -61,57 +61,45 @@ class VfoWindow(QDockWidget):
61
61
  self.poll_rig_timer.timeout.connect(self.poll_radio)
62
62
  self.poll_rig_timer.start(500)
63
63
 
64
- def setDarkMode(self, dark: bool):
65
- """testing"""
64
+ def setDarkMode(self, dark: bool) -> None:
65
+ """Forces a darkmode palette."""
66
66
 
67
67
  if dark:
68
- darkPalette = QtGui.QPalette()
69
- darkColor = QtGui.QColor(56, 56, 56)
70
- self.text_color = QColorConstants.White
71
- disabledColor = QtGui.QColor(127, 127, 127)
72
- darkPalette.setColor(QtGui.QPalette.ColorRole.Window, darkColor)
73
- darkPalette.setColor(
74
- QtGui.QPalette.ColorRole.WindowText, QColorConstants.White
75
- )
68
+ darkPalette = QPalette()
69
+ darkColor = QColor(56, 56, 56)
70
+ disabledColor = QColor(127, 127, 127)
71
+ darkPalette.setColor(QPalette.ColorRole.Window, darkColor)
72
+ darkPalette.setColor(QPalette.ColorRole.WindowText, QColorConstants.White)
73
+ darkPalette.setColor(QPalette.ColorRole.Base, QColor(45, 45, 45))
74
+ darkPalette.setColor(QPalette.ColorRole.AlternateBase, darkColor)
75
+ darkPalette.setColor(QPalette.ColorRole.Text, QColorConstants.White)
76
+ darkPalette.setColor(QPalette.ColorRole.Button, darkColor)
77
+ darkPalette.setColor(QPalette.ColorRole.ButtonText, QColorConstants.White)
78
+ darkPalette.setColor(QPalette.ColorRole.BrightText, QColorConstants.Red)
79
+ darkPalette.setColor(QPalette.ColorRole.Link, QColor(42, 130, 218))
80
+ darkPalette.setColor(QPalette.ColorRole.Highlight, QColor(42, 130, 218))
76
81
  darkPalette.setColor(
77
- QtGui.QPalette.ColorRole.Base, QtGui.QColor(45, 45, 45)
82
+ QPalette.ColorRole.HighlightedText, QColorConstants.Black
78
83
  )
79
- darkPalette.setColor(QtGui.QPalette.ColorRole.AlternateBase, darkColor)
80
- darkPalette.setColor(QtGui.QPalette.ColorRole.Text, QColorConstants.White)
81
84
  darkPalette.setColor(
82
- QtGui.QPalette.ColorGroup.Disabled,
83
- QtGui.QPalette.ColorRole.Text,
85
+ QPalette.ColorGroup.Disabled,
86
+ QPalette.ColorRole.ButtonText,
84
87
  disabledColor,
85
88
  )
86
- darkPalette.setColor(QtGui.QPalette.ColorRole.Button, darkColor)
87
- darkPalette.setColor(
88
- QtGui.QPalette.ColorRole.ButtonText, QColorConstants.White
89
- )
90
89
  darkPalette.setColor(
91
- QtGui.QPalette.ColorGroup.Disabled,
92
- QtGui.QPalette.ColorRole.ButtonText,
90
+ QPalette.ColorGroup.Disabled,
91
+ QPalette.ColorRole.HighlightedText,
93
92
  disabledColor,
94
93
  )
95
94
  darkPalette.setColor(
96
- QtGui.QPalette.ColorRole.BrightText, QColorConstants.Red
97
- )
98
- darkPalette.setColor(
99
- QtGui.QPalette.ColorRole.Link, QtGui.QColor(42, 130, 218)
100
- )
101
- darkPalette.setColor(
102
- QtGui.QPalette.ColorRole.Highlight, QtGui.QColor(42, 130, 218)
103
- )
104
- darkPalette.setColor(
105
- QtGui.QPalette.ColorRole.HighlightedText, QColorConstants.Black
106
- )
107
- darkPalette.setColor(
108
- QtGui.QPalette.ColorGroup.Disabled,
109
- QtGui.QPalette.ColorRole.HighlightedText,
95
+ QPalette.ColorGroup.Disabled,
96
+ QPalette.ColorRole.Text,
110
97
  disabledColor,
111
98
  )
112
99
 
113
- self.setPalette(darkPalette)
114
100
  self.current_palette = darkPalette
101
+ self.setPalette(darkPalette)
102
+ self.text_color = QColorConstants.White
115
103
  else:
116
104
  palette = self.style().standardPalette()
117
105
  self.current_palette = palette
not1mm/voice_keying.py ADDED
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """
4
+ Not1MM Contest logger
5
+ Email: michael.bridak@gmail.com
6
+ GPL V3
7
+ """
8
+
9
+ # pylint: disable=unused-import, c-extension-no-member, no-member, invalid-name, too-many-lines
10
+ # pylint: disable=logging-fstring-interpolation, line-too-long, no-name-in-module
11
+
12
+ import logging
13
+ from pathlib import Path
14
+
15
+ try:
16
+ import sounddevice as sd
17
+ except OSError as exception:
18
+ print(exception)
19
+ print("portaudio is not installed")
20
+ sd = None
21
+ import soundfile as sf
22
+
23
+ from PyQt6.QtCore import QObject, pyqtSignal, QThread
24
+
25
+ logger = logging.getLogger("cat_interface")
26
+
27
+
28
+ class Voice(QObject):
29
+ """Voice class"""
30
+
31
+ ptt_on = pyqtSignal()
32
+ ptt_off = pyqtSignal()
33
+ data_path = None
34
+ current_op = None
35
+ sounddevice = None
36
+ voicings = []
37
+
38
+ def __init__(self) -> None:
39
+ super().__init__()
40
+ """setup interface"""
41
+
42
+ def run(self):
43
+ while True:
44
+ keyed = False
45
+ while len(self.voicings):
46
+ if not keyed:
47
+ self.ptt_on.emit()
48
+ keyed = True
49
+ filename = self.voicings.pop(0)
50
+ if Path(filename).is_file():
51
+ logger.debug("Voicing: %s", filename)
52
+ data, _fs = sf.read(filename, dtype="float32")
53
+ # self.ptt_on.emit()
54
+ try:
55
+ sd.default.device = self.sounddevice
56
+ sd.default.samplerate = 44100.0
57
+ sd.play(data, blocking=True)
58
+ # _status = sd.wait()
59
+ # https://snyk.io/advisor/python/sounddevice/functions/sounddevice.PortAudioError
60
+ except sd.PortAudioError as err:
61
+ logger.warning("%s", f"{err}")
62
+ if keyed:
63
+ self.ptt_off.emit()
64
+ QThread.msleep(100)
65
+
66
+ def voice_string(self, the_string: str) -> None:
67
+ """
68
+ voices string using nato phonetics.
69
+
70
+ Parameters
71
+ ----------
72
+ the_string : str
73
+ String to voicify.
74
+
75
+ Returns
76
+ -------
77
+ None
78
+ """
79
+
80
+ logger.debug("Voicing: %s", the_string)
81
+ if sd is None:
82
+ logger.warning("Sounddevice/portaudio not installed.")
83
+ return
84
+ # fsutils.USER_DATA_PATH
85
+ # self.current_op
86
+ op_path = self.data_path / self.current_op
87
+ if "[" in the_string:
88
+ sub_string = the_string.strip("[]").lower()
89
+ filename = f"{str(op_path)}/{sub_string}.wav"
90
+ if Path(filename).is_file():
91
+ self.voicings.append(filename)
92
+ return
93
+ for letter in the_string.lower():
94
+ if letter in "abcdefghijklmnopqrstuvwxyz 1234567890":
95
+ if letter == " ":
96
+ letter = "space"
97
+ filename = f"{str(op_path)}/{letter}.wav"
98
+ if Path(filename).is_file():
99
+ logger.debug("Voicing: %s", filename)
100
+ self.voicings.append(filename)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: not1mm
3
- Version: 24.4.26
3
+ Version: 24.5.1
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
@@ -31,6 +31,7 @@ Requires-Dist: appdata
31
31
  Requires-Dist: Levenshtein
32
32
 
33
33
  # Not1MM
34
+ <!-- markdownlint-disable MD001 MD033 -->
34
35
 
35
36
  ![logo](https://github.com/mbridak/not1mm/raw/master/not1mm/data/k6gte.not1mm.svg)
36
37
 
@@ -55,16 +56,16 @@ Requires-Dist: Levenshtein
55
56
  - [Common installation recipes for Ubuntu and Fedora](#common-installation-recipes-for-ubuntu-and-fedora)
56
57
  - [Ubuntu 22.04 LTS](#ubuntu-2204-lts)
57
58
  - [Ubuntu 23.04](#ubuntu-2304)
58
- - [Ubuntu 24.04](#ubuntu-2404)
59
+ - [Ubuntu 24.04 LTS](#ubuntu-2404-lts)
59
60
  - [Fedora 38 \& 39](#fedora-38--39)
60
61
  - [Fedora 40](#fedora-40)
61
62
  - [Python, PyPI, pip and pipx](#python-pypi-pip-and-pipx)
62
63
  - [Bootstrapping pipx](#bootstrapping-pipx)
63
64
  - [Installing with pipx](#installing-with-pipx)
65
+ - [Installing from GitHub source](#installing-from-github-source)
64
66
  - [After the install](#after-the-install)
65
67
  - [You may or may not get a warning message like](#you-may-or-may-not-get-a-warning-message-like)
66
68
  - [Or this fan favorite](#or-this-fan-favorite)
67
- - [Installing from GitHub source](#installing-from-github-source)
68
69
  - [Various data file locations](#various-data-file-locations)
69
70
  - [Data](#data)
70
71
  - [Config](#config)
@@ -179,22 +180,7 @@ I wish to thank those who've contributed to the project.
179
180
 
180
181
  ## Recent Changes
181
182
 
182
- - [24-4-25-1] Reduce delta time to poll. Reorder poll_radio callback. Remove unused CAT lib from main.
183
- - [24-4-25] Limited loop in radio.py, reducing clock cycles used. Moved Log window to the top of the logger.
184
- - [24-4-24] Placed CAT control into a thread so disconnecting the radio wouldn't lock up the interface.
185
- - [24-4-17] Trap OSError if no sound device. Stop fsutils/appdata from creating useless .not1mm and .username folder structures on Linux platforms.
186
- - [24-4-15] checkwindow.py Tighter results. Changed the call selection to use a single click.
187
- - [24-4-9-4] Check for portaudio instead of crash boom. Removed empty dockwidget. Tested on Plasma 6.
188
- - [24-4-9-3] Ugh. It's not a real day unless you forget to test.
189
- - [24-4-9-2] Put back the floatable dock widgets, 'cause Wayland strikes again.
190
- - [24-4-9-1] Removed DockWidgetFloatable from the dock widgets since my wee brain can't figure out how to add a dragable window frame to them once they are floating. Added a minimum size for the VFO LCD digits. Defaulted bandmap window to the right.
191
- - [24-4-9] Fixed Checkwindow not showing calls from logged contacts.
192
- - [24-4-7] Added FT8Watcher class to prep for FT8 support.
193
- - [24-4-4-1] Made docking widgets open state persistent.
194
- - [24-4-4] Added per-contest echange hint when adding new contest.
195
- - [24-4-2] Migrated to PyQt6. I'm sure there are broken things.
196
- - [24-4-1-2] Added color text indicators to the Check Partial window. Poached the code from @kyleboyle. Thanks! Fixed the Log, VFO and Check Partial windows to be actual docking widgets. Refocus call field after double clicking on item in the check partial window.
197
- - [24-4-1] Removed some un-needed loops and widgets from the check window. Fixed docking to the left side.
183
+ - [24-5-1] Moved the voice keying into it's own thread.
198
184
 
199
185
  See [CHANGELOG.md](CHANGELOG.md) for prior changes.
200
186
 
@@ -217,6 +203,10 @@ through your distribution's package manager before continuing.
217
203
  I've taken the time to install some common Linux distributions into a VM and
218
204
  noted the minimum steps needed to install not1mm.
219
205
 
206
+ <details>
207
+
208
+ <summary><b>Ubuntu 22.04 LTS, 23.04 and 24.04 LTS</b></summary>
209
+
220
210
  #### Ubuntu 22.04 LTS
221
211
 
222
212
  ```bash
@@ -236,7 +226,7 @@ pipx install not1mm
236
226
  pipx ensurepath
237
227
  ```
238
228
 
239
- #### Ubuntu 24.04
229
+ #### Ubuntu 24.04 LTS
240
230
 
241
231
  ```bash
242
232
  sudo apt update
@@ -246,6 +236,12 @@ pip install --break-system-packages not1mm
246
236
  source .profile
247
237
  ```
248
238
 
239
+ </details>
240
+
241
+ <details>
242
+
243
+ <summary><b>Fedora 38, 39 and 40</b></summary>
244
+
249
245
  #### Fedora 38 & 39
250
246
 
251
247
  ```bash
@@ -262,6 +258,9 @@ sudo dnf install python3-pip python3-pyqt6 portaudio
262
258
  pip install not1mm
263
259
  ```
264
260
 
261
+ </details>
262
+ <br>
263
+
265
264
  You can now open a new terminal and type not1mm. On it's first run, it may or
266
265
  may not install a lovely non AI generated icon, which you can later click on to
267
266
  launch the application.
@@ -313,6 +312,40 @@ If you need to later update not1mm, you can do so with:
313
312
  pipx upgrade not1mm
314
313
  ```
315
314
 
315
+ <details>
316
+ <summary><b>Installing from GitHub source.</b></summary>
317
+
318
+ ### Installing from GitHub source
319
+
320
+ Since this is packaged for PyPI, if you want to work on your own source branch,
321
+ after cloning from github you would:
322
+
323
+ ```bash
324
+ pip install --upgrade pip
325
+ pip install setuptools
326
+ pip install build
327
+ source rebuild.sh
328
+ ```
329
+
330
+ from the root directory. This installs a build chain and a local editable copy
331
+ of not1mm.
332
+
333
+ There's two ways to launch the program from the local editable copy.
334
+
335
+ You can either be in the root of the source directory and type:
336
+
337
+ ```bash
338
+ python not1mm
339
+ ```
340
+
341
+ or be in some other directory and just type:
342
+
343
+ ```bash
344
+ not1mm
345
+ ```
346
+
347
+ </details>
348
+
316
349
  ## After the install
317
350
 
318
351
  You can now open a new terminal and type `not1mm`. On it's first run, it may or
@@ -344,35 +377,6 @@ For a more permanent solution you can place the line
344
377
  `export QT_QPA_PLATFORM=wayland` in your home directories .bashrc file. Then
345
378
  after logging out and back in you should be able to launch it normally.
346
379
 
347
- ### Installing from GitHub source
348
-
349
- Since this is packaged for PyPI, if you want to work on your own source branch,
350
- after cloning from github you would:
351
-
352
- ```bash
353
- pip install --upgrade pip
354
- pip install setuptools
355
- pip install build
356
- source rebuild.sh
357
- ```
358
-
359
- from the root directory. This installs a build chain and a local editable copy
360
- of not1mm.
361
-
362
- There's two ways to launch the program from the local editable copy.
363
-
364
- You can either be in the root of the source directory and type:
365
-
366
- ```bash
367
- python not1mm
368
- ```
369
-
370
- or be in some other directory and just type:
371
-
372
- ```bash
373
- not1mm
374
- ```
375
-
376
380
  ## Various data file locations
377
381
 
378
382
  ### Data
@@ -482,7 +486,10 @@ onscreen icon for CAT status. Green good, Red bad, Grey neither.
482
486
 
483
487
  Under the `CW` TAB, There are three options. `cwdaemon`, which normally uses IP
484
488
  `127.0.0.1` and port `6789`. `pywinkeyer` which normally uses IP `127.0.0.1` and
485
- port `8000`. Or `None`, if you want to Morse it like it's 1899.
489
+ `CAT` which if your radio supports it, sends Morse characters via rigctld. As far
490
+ as I can tell rigctld does not support setting the radios internal keyer speed. So
491
+ the CW speed control widget will not be functional and you'd need to control the
492
+ keyer speed thru the radios interface.
486
493
 
487
494
  ### Cluster
488
495