not1mm 24.3.16__py3-none-any.whl → 24.3.24__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 (68) hide show
  1. not1mm/__main__.py +288 -124
  2. not1mm/bandmap.py +207 -191
  3. not1mm/checkwindow.py +62 -109
  4. not1mm/data/MASTER.SCP +1077 -749
  5. not1mm/data/bandmap.ui +4 -4
  6. not1mm/data/checkwindow.ui +9 -16
  7. not1mm/data/cty.json +1 -1
  8. not1mm/data/logwindow.ui +16 -11
  9. not1mm/data/main.ui +16 -6
  10. not1mm/data/vfo.ui +64 -52
  11. not1mm/fsutils.py +63 -0
  12. not1mm/lib/about.py +4 -2
  13. not1mm/lib/cat_interface.py +1 -1
  14. not1mm/lib/cwinterface.py +1 -1
  15. not1mm/lib/database.py +4 -4
  16. not1mm/lib/edit_contact.py +2 -2
  17. not1mm/lib/edit_macro.py +2 -3
  18. not1mm/lib/edit_opon.py +2 -2
  19. not1mm/lib/edit_station.py +3 -3
  20. not1mm/lib/ham_utility.py +1 -1
  21. not1mm/lib/lookup.py +1 -1
  22. not1mm/lib/multicast.py +9 -4
  23. not1mm/lib/n1mm.py +1 -1
  24. not1mm/lib/new_contest.py +2 -2
  25. not1mm/lib/select_contest.py +2 -2
  26. not1mm/lib/settings.py +3 -4
  27. not1mm/lib/super_check_partial.py +8 -5
  28. not1mm/lib/version.py +1 -1
  29. not1mm/logwindow.py +62 -105
  30. not1mm/plugins/10_10_fall_cw.py +1 -1
  31. not1mm/plugins/10_10_spring_cw.py +1 -1
  32. not1mm/plugins/10_10_summer_phone.py +1 -1
  33. not1mm/plugins/10_10_winter_phone.py +1 -1
  34. not1mm/plugins/arrl_10m.py +1 -1
  35. not1mm/plugins/arrl_dx_cw.py +1 -1
  36. not1mm/plugins/arrl_dx_ssb.py +1 -1
  37. not1mm/plugins/arrl_field_day.py +1 -1
  38. not1mm/plugins/arrl_ss_cw.py +1 -1
  39. not1mm/plugins/arrl_ss_phone.py +1 -1
  40. not1mm/plugins/arrl_vhf_jan.py +1 -1
  41. not1mm/plugins/arrl_vhf_jun.py +1 -1
  42. not1mm/plugins/arrl_vhf_sep.py +1 -1
  43. not1mm/plugins/canada_day.py +1 -1
  44. not1mm/plugins/cq_160_cw.py +1 -1
  45. not1mm/plugins/cq_160_ssb.py +1 -1
  46. not1mm/plugins/cq_wpx_cw.py +1 -1
  47. not1mm/plugins/cq_wpx_ssb.py +1 -1
  48. not1mm/plugins/cq_ww_cw.py +1 -1
  49. not1mm/plugins/cq_ww_ssb.py +1 -1
  50. not1mm/plugins/cwt.py +1 -1
  51. not1mm/plugins/general_logging.py +1 -1
  52. not1mm/plugins/iaru_hf.py +1 -1
  53. not1mm/plugins/jidx_cw.py +1 -1
  54. not1mm/plugins/jidx_ph.py +1 -1
  55. not1mm/plugins/naqp_cw.py +1 -1
  56. not1mm/plugins/naqp_ssb.py +1 -1
  57. not1mm/plugins/phone_weekly_test.py +1 -1
  58. not1mm/plugins/stew_perry_topband.py +1 -1
  59. not1mm/plugins/winter_field_day.py +1 -1
  60. not1mm/vfo.py +78 -98
  61. {not1mm-24.3.16.dist-info → not1mm-24.3.24.dist-info}/METADATA +67 -55
  62. {not1mm-24.3.16.dist-info → not1mm-24.3.24.dist-info}/RECORD +68 -66
  63. testing/detectdark.py +35 -0
  64. testing/test.py +13 -11
  65. {not1mm-24.3.16.dist-info → not1mm-24.3.24.dist-info}/LICENSE +0 -0
  66. {not1mm-24.3.16.dist-info → not1mm-24.3.24.dist-info}/WHEEL +0 -0
  67. {not1mm-24.3.16.dist-info → not1mm-24.3.24.dist-info}/entry_points.txt +0 -0
  68. {not1mm-24.3.16.dist-info → not1mm-24.3.24.dist-info}/top_level.txt +0 -0
not1mm/vfo.py CHANGED
@@ -9,58 +9,35 @@ VFO Window
9
9
  # usb-Raspberry_Pi_Pico_W_E6614C311B331139-if00
10
10
 
11
11
  import logging
12
-
13
12
  import os
14
-
15
13
  import platform
16
- import queue
17
- import sys
18
14
  from json import loads, JSONDecodeError
19
- from pathlib import Path
20
15
 
21
16
  import serial
22
- from PyQt5 import QtCore, QtNetwork, uic, QtWidgets
23
- from PyQt5.QtCore import QTimer
24
- from PyQt5.QtWidgets import QApplication, QMainWindow
17
+ from PyQt5 import QtCore, QtGui, QtWidgets, uic
18
+ from PyQt5.QtCore import Qt, QTimer
19
+ from PyQt5.QtWidgets import QWidget
25
20
 
21
+ import not1mm.fsutils as fsutils
26
22
  from not1mm.lib.cat_interface import CAT
27
23
  from not1mm.lib.multicast import Multicast
28
24
 
29
- os.environ["QT_QPA_PLATFORMTHEME"] = "gnome"
30
-
31
- if __loader__:
32
- WORKING_PATH = os.path.dirname(__loader__.get_filename())
33
- else:
34
- WORKING_PATH = os.path.dirname(os.path.realpath(__file__))
25
+ logger = logging.getLogger(__name__)
35
26
 
36
- if "XDG_DATA_HOME" in os.environ:
37
- DATA_PATH = os.environ.get("XDG_DATA_HOME")
38
- else:
39
- DATA_PATH = str(Path.home() / ".local" / "share")
40
- DATA_PATH += "/not1mm"
41
27
 
42
- if "XDG_CONFIG_HOME" in os.environ:
43
- CONFIG_PATH = os.environ.get("XDG_CONFIG_HOME")
44
- else:
45
- CONFIG_PATH = str(Path.home() / ".config")
46
- CONFIG_PATH += "/not1mm"
47
-
48
-
49
- class MainWindow(QMainWindow):
50
- """
51
- The main window
52
- """
28
+ class VfoWindow(QWidget):
29
+ """The VFO window."""
53
30
 
54
31
  pref = {}
55
32
  old_vfo = ""
56
33
  old_pico = ""
57
34
  message_shown = False
58
35
  multicast_interface = None
36
+ current_palette = None
59
37
 
60
38
  def __init__(self, *args, **kwargs):
61
39
  super().__init__(*args, **kwargs)
62
- data_path = WORKING_PATH + "/data/vfo.ui"
63
- uic.loadUi(data_path, self)
40
+ uic.loadUi(fsutils.APP_DATA_PATH / "vfo.ui", self)
64
41
  self.rig_control = None
65
42
  self.timer = QTimer()
66
43
  self.timer.timeout.connect(self.getwaiting)
@@ -69,7 +46,7 @@ class MainWindow(QMainWindow):
69
46
  self.lcdNumber.display(0)
70
47
  self.pico = None
71
48
  self._udpwatch = None
72
- self.udp_fifo = queue.Queue()
49
+
73
50
  self.multicast_interface = Multicast(
74
51
  self.pref.get("multicast_group", "239.1.1.1"),
75
52
  self.pref.get("multicast_port", 2239),
@@ -77,9 +54,45 @@ class MainWindow(QMainWindow):
77
54
  )
78
55
  self.multicast_interface.ready_read_connect(self.watch_udp)
79
56
 
80
- def quit_app(self) -> None:
81
- """Shutdown the app."""
82
- app.quit()
57
+ self.setup_serial()
58
+ # app.processEvents()
59
+ self.poll_rig_timer = QtCore.QTimer()
60
+ self.poll_rig_timer.timeout.connect(self.poll_radio)
61
+ self.poll_rig_timer.start(500)
62
+
63
+ def setDarkMode(self, dark: bool):
64
+ """testing"""
65
+
66
+ if dark:
67
+ darkPalette = QtGui.QPalette()
68
+ darkColor = QtGui.QColor(45, 45, 45)
69
+ disabledColor = QtGui.QColor(127, 127, 127)
70
+ darkPalette.setColor(QtGui.QPalette.Window, darkColor)
71
+ darkPalette.setColor(QtGui.QPalette.WindowText, Qt.white)
72
+ darkPalette.setColor(QtGui.QPalette.Base, QtGui.QColor(18, 18, 18))
73
+ darkPalette.setColor(QtGui.QPalette.AlternateBase, darkColor)
74
+ darkPalette.setColor(QtGui.QPalette.Text, Qt.white)
75
+ darkPalette.setColor(
76
+ QtGui.QPalette.Disabled, QtGui.QPalette.Text, disabledColor
77
+ )
78
+ darkPalette.setColor(QtGui.QPalette.Button, darkColor)
79
+ darkPalette.setColor(QtGui.QPalette.ButtonText, Qt.white)
80
+ darkPalette.setColor(
81
+ QtGui.QPalette.Disabled, QtGui.QPalette.ButtonText, disabledColor
82
+ )
83
+ darkPalette.setColor(QtGui.QPalette.BrightText, Qt.red)
84
+ darkPalette.setColor(QtGui.QPalette.Link, QtGui.QColor(42, 130, 218))
85
+ darkPalette.setColor(QtGui.QPalette.Highlight, QtGui.QColor(42, 130, 218))
86
+ darkPalette.setColor(QtGui.QPalette.HighlightedText, Qt.black)
87
+ darkPalette.setColor(
88
+ QtGui.QPalette.Disabled, QtGui.QPalette.HighlightedText, disabledColor
89
+ )
90
+ self.current_palette = darkPalette
91
+ self.setPalette(darkPalette)
92
+ else:
93
+ palette = self.style().standardPalette()
94
+ self.current_palette = palette
95
+ self.setPalette(palette)
83
96
 
84
97
  def load_pref(self) -> None:
85
98
  """
@@ -87,9 +100,9 @@ class MainWindow(QMainWindow):
87
100
  Get CAT interface.
88
101
  """
89
102
  try:
90
- if os.path.exists(CONFIG_PATH + "/not1mm.json"):
103
+ if os.path.exists(fsutils.CONFIG_FILE):
91
104
  with open(
92
- CONFIG_PATH + "/not1mm.json", "rt", encoding="utf-8"
105
+ fsutils.CONFIG_FILE, "rt", encoding="utf-8"
93
106
  ) as file_descriptor:
94
107
  self.pref = loads(file_descriptor.read())
95
108
  logger.info("%s", self.pref)
@@ -119,6 +132,7 @@ class MainWindow(QMainWindow):
119
132
  int(self.pref.get("CAT_port", 4532)),
120
133
  )
121
134
  self.timer.start(100)
135
+ self.setDarkMode(self.pref.get("darkmode", False))
122
136
 
123
137
  def discover_device(self) -> str:
124
138
  """
@@ -131,7 +145,7 @@ class MainWindow(QMainWindow):
131
145
 
132
146
  devices = None
133
147
  data = None
134
- app.processEvents()
148
+ # app.processEvents()
135
149
  try:
136
150
  devices = os.listdir("/dev/serial/by-id")
137
151
  except FileNotFoundError:
@@ -154,29 +168,28 @@ class MainWindow(QMainWindow):
154
168
  Setup the device returned by discover_device()
155
169
  Or display message saying we didn't find one.
156
170
  """
157
- while True:
158
- device = self.discover_device()
159
- if device:
160
- try:
161
- self.pico = serial.Serial("/dev/serial/by-id/" + device, 115200)
162
- self.pico.timeout = 100
163
- self.lcdNumber.setStyleSheet("QLCDNumber { color: white; }")
164
- break
165
- except OSError:
166
- if self.message_shown is False:
167
- self.message_shown = True
168
- self.show_message_box(
169
- "Unable to locate or open the VFO knob serial device."
170
- )
171
- self.lcdNumber.setStyleSheet("QLCDNumber { color: red; }")
172
- else:
171
+
172
+ device = self.discover_device()
173
+ if device:
174
+ try:
175
+ self.pico = serial.Serial("/dev/serial/by-id/" + device, 115200)
176
+ self.pico.timeout = 100
177
+ self.lcdNumber.setStyleSheet("QLCDNumber { color: white; }")
178
+ except OSError:
173
179
  if self.message_shown is False:
174
180
  self.message_shown = True
175
181
  self.show_message_box(
176
182
  "Unable to locate or open the VFO knob serial device."
177
183
  )
178
184
  self.lcdNumber.setStyleSheet("QLCDNumber { color: red; }")
179
- app.processEvents()
185
+ else:
186
+ if self.message_shown is False:
187
+ self.message_shown = True
188
+ self.show_message_box(
189
+ "Unable to locate or open the VFO knob serial device."
190
+ )
191
+ self.lcdNumber.setStyleSheet("QLCDNumber { color: red; }")
192
+ # app.processEvents()
180
193
 
181
194
  def watch_udp(self) -> None:
182
195
  """
@@ -200,8 +213,7 @@ class MainWindow(QMainWindow):
200
213
  if json_data.get("station", "") != platform.node():
201
214
  continue
202
215
  logger.debug(f"{json_data=}")
203
- if json_data.get("cmd", "") == "HALT":
204
- self.quit_app()
216
+
205
217
  if json_data.get("cmd", "") == "TUNE":
206
218
  # b'{"cmd": "TUNE", "freq": 7.0235, "spot": "MM0DGI"}'
207
219
  vfo = json_data.get("freq")
@@ -214,6 +226,9 @@ class MainWindow(QMainWindow):
214
226
  logger.critical("Unable to write to serial device.")
215
227
  except AttributeError:
216
228
  logger.critical("Unable to write to serial device.")
229
+ continue
230
+ if json_data.get("cmd", "") == "DARKMODE":
231
+ self.setDarkMode(json_data.get("state", False))
217
232
 
218
233
  def showNumber(self, the_number) -> None:
219
234
  """Display vfo value with dots"""
@@ -221,7 +236,7 @@ class MainWindow(QMainWindow):
221
236
  if len(dvfo) > 6:
222
237
  dnum = f"{dvfo[:len(dvfo)-6]}.{dvfo[-6:-3]}.{dvfo[-3:]}"
223
238
  self.lcdNumber.display(dnum)
224
- app.processEvents()
239
+ # app.processEvents()
225
240
 
226
241
  def poll_radio(self) -> None:
227
242
  """
@@ -244,7 +259,7 @@ class MainWindow(QMainWindow):
244
259
  logger.debug(f"{vfo}")
245
260
  self.showNumber(vfo)
246
261
  # self.lcdNumber.display(dnum)
247
- app.processEvents()
262
+ # app.processEvents()
248
263
  cmd = f"F {vfo}\r"
249
264
  try:
250
265
  if self.pico:
@@ -271,56 +286,21 @@ class MainWindow(QMainWindow):
271
286
  self.rig_control.set_vfo(result)
272
287
  self.showNumber(result)
273
288
  # self.lcdNumber.display(result)
274
- app.processEvents()
289
+ # app.processEvents()
275
290
  except OSError:
276
291
  logger.critical("Unable to write to serial device.")
277
292
  except AttributeError:
278
293
  logger.critical("Unable to write to serial device.")
279
- app.processEvents()
294
+ # app.processEvents()
280
295
 
281
296
  def show_message_box(self, message: str) -> None:
282
297
  """
283
298
  Display an alert box with the supplied message.
284
299
  """
285
300
  message_box = QtWidgets.QMessageBox()
301
+ message_box.setPalette(self.current_palette)
286
302
  message_box.setIcon(QtWidgets.QMessageBox.Information)
287
303
  message_box.setText(message)
288
304
  message_box.setWindowTitle("Information")
289
305
  message_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
290
306
  _ = message_box.exec_()
291
-
292
-
293
- def main():
294
- """main entry"""
295
- window.show()
296
- window.setup_serial()
297
- app.processEvents()
298
- timer = QtCore.QTimer()
299
- timer.timeout.connect(window.poll_radio)
300
- timer.start(250)
301
- sys.exit(app.exec())
302
-
303
-
304
- logger = logging.getLogger("__main__")
305
- handler = logging.StreamHandler()
306
- formatter = logging.Formatter(
307
- datefmt="%H:%M:%S",
308
- fmt="[%(asctime)s] %(levelname)s %(module)s - %(funcName)s Line %(lineno)d:\n%(message)s",
309
- )
310
- handler.setFormatter(formatter)
311
- logger.addHandler(handler)
312
-
313
- if Path("./debug").exists():
314
- logger.setLevel(logging.DEBUG)
315
- logger.debug("debugging on")
316
- else:
317
- logger.setLevel(logging.WARNING)
318
- logger.warning("debugging off")
319
-
320
- app = QApplication(sys.argv)
321
- app.setStyle("Adwaita-Dark")
322
- window = MainWindow()
323
-
324
-
325
- if __name__ == "__main__":
326
- main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: not1mm
3
- Version: 24.3.16
3
+ Version: 24.3.24
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
@@ -27,6 +27,7 @@ Requires-Dist: soundfile
27
27
  Requires-Dist: numpy
28
28
  Requires-Dist: notctyparser >=23.6.21
29
29
  Requires-Dist: rapidfuzz
30
+ Requires-Dist: appdata
30
31
 
31
32
  # Not1MM
32
33
 
@@ -47,19 +48,16 @@ The worlds #1 unfinished contest logger <sup>*According to my daughter Corinna.<
47
48
  - [Our Code Contributors ✨](#our-code-contributors-)
48
49
  - [List of should be working contests](#list-of-should-be-working-contests)
49
50
  - [Recent Changes](#recent-changes)
50
- - [Installing from PyPi](#installing-from-pypi)
51
- - [Python and pip](#python-and-pip)
52
- - [Installing with pip](#installing-with-pip)
53
- - [Ubuntu 22.04 LTS](#ubuntu-2204-lts)
54
- - [Ubuntu 23.04](#ubuntu-2304)
55
- - [Fedora 38 \& 39](#fedora-38--39)
51
+ - [Installation](#installation)
52
+ - [Python, PyPI, pip and pipx](#python-pypi-pip-and-pipx)
53
+ - [Bootstrapping pipx](#bootstrapping-pipx)
54
+ - [Installing with pipx](#installing-with-pipx)
55
+ - [Installing portaudio](#installing-portaudio)
56
+ - [After install](#after-install)
56
57
  - [You may or may not get a warning message like](#you-may-or-may-not-get-a-warning-message-like)
57
58
  - [Or this fan favorite](#or-this-fan-favorite)
58
- - [Updating with pip/pipx](#updating-with-pippipx)
59
- - [Other Libraries](#other-libraries)
60
- - [Dark mode on Ubuntu](#dark-mode-on-ubuntu)
61
59
  - [Wayland Compositor](#wayland-compositor)
62
- - [Running from source](#running-from-source)
60
+ - [Running from source](#running-from-source)
63
61
  - [Various data file locations](#various-data-file-locations)
64
62
  - [Data](#data)
65
63
  - [Config](#config)
@@ -70,13 +68,14 @@ The worlds #1 unfinished contest logger <sup>*According to my daughter Corinna.<
70
68
  - [Revisiting an old friend](#revisiting-an-old-friend)
71
69
  - [Station Settings dialog (REQUIRED)](#station-settings-dialog-required)
72
70
  - [Changing station information](#changing-station-information)
73
- - [Adding a contest to the current dababase (REQUIRED)](#adding-a-contest-to-the-current-dababase-required)
74
- - [Selecting an existing contest as the current contest](#selecting-an-existing-contest-as-the-current-contest)
75
- - [Editing existing contest parameters](#editing-existing-contest-parameters)
71
+ - [Selecting a contest (REQUIRED)](#selecting-a-contest-required)
72
+ - [Selecting a new contest](#selecting-a-new-contest)
73
+ - [Selecting an existing contest as the current contest](#selecting-an-existing-contest-as-the-current-contest)
74
+ - [Editing existing contest parameters](#editing-existing-contest-parameters)
76
75
  - [Configuration Settings](#configuration-settings)
77
76
  - [Lookup](#lookup)
78
77
  - [Soundcard](#soundcard)
79
- - [CAT](#cat)
78
+ - [CAT Control](#cat-control)
80
79
  - [CW Keyer interface](#cw-keyer-interface)
81
80
  - [Cluster](#cluster)
82
81
  - [N1MM Packets](#n1mm-packets)
@@ -89,7 +88,7 @@ The worlds #1 unfinished contest logger <sup>*According to my daughter Corinna.<
89
88
  - [Other uses for the call field](#other-uses-for-the-call-field)
90
89
  - [Windows](#windows)
91
90
  - [The Main Window](#the-main-window)
92
- - [Keyboard commands](#keyboard-commands)
91
+ - [Keyboard commands](#keyboard-commands)
93
92
  - [Log Display](#log-display)
94
93
  - [Editing a contact](#editing-a-contact)
95
94
  - [Recalulate Mults](#recalulate-mults)
@@ -168,6 +167,10 @@ I wish to thank those who've contributed to the project.
168
167
 
169
168
  ## Recent Changes
170
169
 
170
+ - [24-3-24] Reworked fsutil.py to correct directory paths for Linux.
171
+ - [24-3-23] Yanked version 24-3-21, too many bugs for existing userbase.
172
+ - [24-3-21] Merged PR from @kyleboyle for docking windows. MacOS and Windows support.
173
+ - [24-3-19] Removed some useless bloat causing slow interface on FreeBSD 13/14 and maybe others.
171
174
  - [24-3-16] Add Save/Fail confirmation dialogs when saving ADIF of Cabrillo files.
172
175
  - [24-3-15] Change 'CWR' to 'CW' in the ADIF output.
173
176
  - [24-3-13] Added CQ 160 CW and SSB
@@ -178,43 +181,64 @@ I wish to thank those who've contributed to the project.
178
181
 
179
182
  See [CHANGELOG.md](CHANGELOG.md) for prior changes.
180
183
 
181
- ## Installing from PyPi
184
+ ## Installation
182
185
 
183
- ### Python and pip
186
+ ### Python, PyPI, pip and pipx
184
187
 
185
- This software is a Python package hosted on PyPi, and installable with the pip or pipx command. If this is your first exposure to pip you can get all the details from [The PyPA](https://packaging.python.org/en/latest/tutorials/installing-packages/). In short, most linux distros come with Python pre installed. If pip is not installed by default, you can usually load it through your package manager. For example `sudo apt install python3-pip` or `sudo dnf install python3-pip`.
188
+ This software is a Python package hosted on PyPI, and installable with the pip or pipx command. If this is your first exposure to Python packaging you can get all the details from:
186
189
 
187
- ### Installing with pip
190
+ - [The PyPA](https://packaging.python.org/en/latest/tutorials/installing-packages/)
191
+ - [Install packages in a virtual environment using pip and venv](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/)
192
+ - [Installing stand alone command line tools](https://packaging.python.org/en/latest/guides/installing-stand-alone-command-line-tools/)
188
193
 
189
- I've included what installation steps I took to install on fresh images of Ubuntu and Fedora below. YMMV.
194
+ In short, You should install stuff into a Python virtual environment. Newer Linux distros will make you do this unless you include a command line argument akin to '--break-my-system' when using pip. I'm not telling you to use pipx. But... **Use pipx**.
190
195
 
191
- #### Ubuntu 22.04 LTS
196
+ ### Bootstrapping pipx
197
+
198
+ Assuming you have only Python installed, your path to pipx is:
192
199
 
193
200
  ```bash
194
- sudo apt update
195
- sudo apt upgrade
196
- sudo apt install -y libportaudio2 python3-pip python3-pyqt5 python3-numpy adwaita-qt
197
- pip install -U not1mm
201
+ # First get pip installed. Either with apt or dnf, or the ensurepip command.
202
+ python3 -m ensurepip
203
+
204
+ # Update the pip that was just installed.
205
+ python3 -m pip install --upgrade pip
206
+
207
+ # Install pipx
208
+ python3 -m pip install --user pipx
209
+ python3 -m pipx ensurepath
198
210
  ```
199
211
 
200
- #### Ubuntu 23.04
212
+ ### Installing with pipx
213
+
214
+ Then installing not1mm is as simple as:
201
215
 
202
216
  ```bash
203
- sudo apt update
204
- sudo apt upgrade
205
- sudo apt install -y libportaudio2 adwaita-qt pipx
217
+ # Install not1mm
206
218
  pipx install not1mm
207
- pipx ensurepath
208
219
  ```
209
220
 
210
- #### Fedora 38 & 39
221
+ If you need to later update not1mm, you can do so with:
222
+
223
+ ```bash
224
+ # Update not1mm
225
+ pipx upgrade not1mm
226
+ ```
227
+
228
+ ### Installing portaudio
229
+
230
+ not1mm uses portaudio to play audio. You can install it with:
211
231
 
212
232
  ```bash
213
- sudo dnf upgrade --refresh
233
+ # Ubuntu
234
+ sudo apt install -y libportaudio2
235
+
236
+ # Fedora
214
237
  sudo dnf install python3-pip portaudio
215
- pip install not1mm
216
238
  ```
217
239
 
240
+ ## After install
241
+
218
242
  You can now open a new terminal and type `not1mm`. On it's first run, it may or may not install a lovely non AI generated
219
243
  icon, which you can later click on to launch the application.
220
244
 
@@ -241,27 +265,13 @@ To avoid this you can export an environment variable and launch the app like thi
241
265
 
242
266
  For a more permanent solution you can place the line `export QT_QPA_PLATFORM=wayland` in your home directories .bashrc file. Then after logging out and back in you should be able to launch it normally.
243
267
 
244
- ### Updating with pip/pipx
245
-
246
- I've been posting updates just about everyday. Sometimes multiple times a day. It's early days, so there is much to do. You can check for and install updates with `pip install -U not1mm` or if installed with pipx `pipx upgrade not1mm`.
247
-
248
- ## Other Libraries
249
-
250
- The audio library used, uses pipewire/portaudio. You may need to install portaudio. Ubuntu: `sudo apt install libportaudio2`
251
-
252
- ### Dark mode on Ubuntu
253
-
254
- I believe I figured out dark mode in Ubuntu and have it working on my shack PC that runs Ubuntu 22.04. The secret sauce seems to be installing adwaita-qt with apt, and setting an environment variable `QT_STYLE_OVERRIDE` to `Adwaita-Dark`. I set the environment variable in the start of the program if running on a Gnome platform. So you don't need to do that part.
255
-
256
- Or see this discussion [darkmode](https://github.com/mbridak/not1mm/discussions/60).
257
-
258
268
  ## Wayland Compositor
259
269
 
260
270
  One side effect of Wayland is that we are not able to request for a window to regain or retain focus. So if you were to click on a spot in the bandmap window to tune to that spot, you would have to then click on the main window to continue entering contest data. I'm aware of this, but I can not change it.
261
271
 
262
- ## Running from source
272
+ ### Running from source
263
273
 
264
- Since this is packaged for PyPi, if you want to work on your own source branch, after cloning from github you would:
274
+ Since this is packaged for PyPI, if you want to work on your own source branch, after cloning from github you would:
265
275
 
266
276
  ```bash
267
277
  pip install --upgrade pip
@@ -330,19 +340,21 @@ You can fill. You can fill. Everyone look at your keys.
330
340
 
331
341
  Station information can be changed any time by going to `File` > `Station Settings` and editing the information.
332
342
 
333
- ## Adding a contest to the current dababase (REQUIRED)
343
+ ## Selecting a contest (REQUIRED)
344
+
345
+ ### Selecting a new contest
334
346
 
335
347
  Select `File` > `New Contest`
336
348
 
337
349
  ![New Contest Dialog](https://github.com/mbridak/not1mm/raw/master/pic/new_contest.png)
338
350
 
339
- ## Selecting an existing contest as the current contest
351
+ ### Selecting an existing contest as the current contest
340
352
 
341
353
  Select `File` > `Open Contest`
342
354
 
343
355
  ![Open an existing contest](https://github.com/mbridak/not1mm/raw/master/pic/select_contest.png)
344
356
 
345
- ## Editing existing contest parameters
357
+ ### Editing existing contest parameters
346
358
 
347
359
  You can edit the parameters of a previously defined contest by selecting it as the current contest. Then select `File` > `Edit Current Contest`. Click `OK` to save the new values and reload the contest. `Cancel` to keep the existing parameters.
348
360
 
@@ -362,7 +374,7 @@ For callsign lookup, Two services are supported. QRZ and HamQTH. They require a
362
374
 
363
375
  Choose the sound output device for the voice keyer.
364
376
 
365
- ### CAT
377
+ ### CAT Control
366
378
 
367
379
  Under the `CAT` TAB, you can choose either `rigctld` normally with an IP of `127.0.0.1` and a port of `4532`. Or `flrig`, IP normally of `127.0.0.1` and a port of `12345`. `None` is always an option, but is it really? There's an onscreen icon for CAT status. Green good, Red bad, Grey neither.
368
380
 
@@ -445,7 +457,7 @@ After this, a request is made to QRZ for the gridsquare of the callsign. If ther
445
457
 
446
458
  ![Main screen with callouts](https://github.com/mbridak/not1mm/raw/master/pic/mainwithcallouts.png)
447
459
 
448
- #### Keyboard commands
460
+ ### Keyboard commands
449
461
 
450
462
  | Key | Result |
451
463
  | -------------- | --- |