Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c209457dc |
@@ -1,3 +1,9 @@
|
|||||||
|
Quisk Version 4.2.41 January 2025
|
||||||
|
==================================
|
||||||
|
I made some improvements to the top-level Config/Radios screen. There is now an option to
|
||||||
|
make a new radio by copying settings from an existing radio. I worked around a bug in wxPython
|
||||||
|
and gtk3 that caused drawing problems on the config screens. This problem is not present on Windows.
|
||||||
|
|
||||||
Quisk Version 4.2.40 November 2024
|
Quisk Version 4.2.40 November 2024
|
||||||
===================================
|
===================================
|
||||||
Python 3.13 eliminated the telnetlib module, so I wrote a replacement. I fixed a bug in window sizing.
|
Python 3.13 eliminated the telnetlib module, so I wrote a replacement. I fixed a bug in window sizing.
|
||||||
|
|||||||
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
#Quisk version 4.2.40
|
#Quisk version 4.2.41
|
||||||
from .quisk import main
|
from .quisk import main
|
||||||
|
|||||||
+137
-46
@@ -48,6 +48,7 @@ name2format = {
|
|||||||
|
|
||||||
# Increasing the software version will display a message to re-read the soapy device.
|
# Increasing the software version will display a message to re-read the soapy device.
|
||||||
soapy_software_version = 3
|
soapy_software_version = 3
|
||||||
|
wxpython_gtk3_bug = 0
|
||||||
|
|
||||||
def FormatKhz(dnum): # Round to 3 decimal places; remove ending ".000"
|
def FormatKhz(dnum): # Round to 3 decimal places; remove ending ".000"
|
||||||
t = "%.3f" % dnum
|
t = "%.3f" % dnum
|
||||||
@@ -384,10 +385,20 @@ class Configuration:
|
|||||||
app.bottom_widgets = dct['BottomWidgets'](app, hardware, conf, frame, gbs, vertBox)
|
app.bottom_widgets = dct['BottomWidgets'](app, hardware, conf, frame, gbs, vertBox)
|
||||||
return True
|
return True
|
||||||
def OnPageChanging(self, event): # Called when the top level page changes (not RadioNotebook pages)
|
def OnPageChanging(self, event): # Called when the top level page changes (not RadioNotebook pages)
|
||||||
|
global wxpython_gtk3_bug
|
||||||
event.Skip()
|
event.Skip()
|
||||||
notebook = event.GetEventObject()
|
notebook = event.GetEventObject()
|
||||||
index = event.GetSelection()
|
index = event.GetSelection()
|
||||||
page = notebook.GetPage(index)
|
page = notebook.GetPage(index)
|
||||||
|
if sys.platform != 'win32': # Work around a bug in wxPython 4.2.1 and gtk3
|
||||||
|
w, h = application.main_frame.GetSize() # Change main window size by +/- one pixel to recalculate sizes
|
||||||
|
if wxpython_gtk3_bug:
|
||||||
|
wxpython_gtk3_bug = 0
|
||||||
|
h += 1
|
||||||
|
else:
|
||||||
|
wxpython_gtk3_bug = 1
|
||||||
|
h -= 1
|
||||||
|
application.main_frame.SetSize((w, h))
|
||||||
if isinstance(page, RadioNotebook):
|
if isinstance(page, RadioNotebook):
|
||||||
if not page.pages:
|
if not page.pages:
|
||||||
page.MakePages()
|
page.MakePages()
|
||||||
@@ -419,6 +430,14 @@ class Configuration:
|
|||||||
elif udp > 0:
|
elif udp > 0:
|
||||||
return 'HiQSDR'
|
return 'HiQSDR'
|
||||||
return 'SoftRock USB'
|
return 'SoftRock USB'
|
||||||
|
def FindNotebookPage(self, name):
|
||||||
|
for index in range(self.radios_page_start, self.notebk.GetPageCount()):
|
||||||
|
tab_text = self.notebk.GetPageText(index)
|
||||||
|
if tab_text[0] == '*':
|
||||||
|
tab_text = tab_text[1:-1]
|
||||||
|
if tab_text == name:
|
||||||
|
return index
|
||||||
|
return None
|
||||||
def AddRadio(self, radio_name, typ):
|
def AddRadio(self, radio_name, typ):
|
||||||
radio_dict = {}
|
radio_dict = {}
|
||||||
radio_dict['hardware_file_type'] = typ
|
radio_dict['hardware_file_type'] = typ
|
||||||
@@ -435,13 +454,24 @@ class Configuration:
|
|||||||
page = RadioNotebook(self.notebk, radio_name)
|
page = RadioNotebook(self.notebk, radio_name)
|
||||||
self.notebk.AddPage(page, radio_name)
|
self.notebk.AddPage(page, radio_name)
|
||||||
return True
|
return True
|
||||||
|
def CopyRadio(self, old_name, new_name):
|
||||||
|
radio_dict = {}
|
||||||
|
index = Settings[2].index(old_name)
|
||||||
|
radio_dict.update(Settings[3][index])
|
||||||
|
Settings[2].append(new_name)
|
||||||
|
Settings[3].append(radio_dict)
|
||||||
|
page = RadioNotebook(self.notebk, new_name)
|
||||||
|
self.notebk.AddPage(page, new_name)
|
||||||
|
return True
|
||||||
def RenameRadio(self, old, new):
|
def RenameRadio(self, old, new):
|
||||||
index = Settings[2].index(old)
|
n = self.FindNotebookPage(old)
|
||||||
n = self.radios_page_start + index
|
if n is None:
|
||||||
|
return
|
||||||
if old == Settings[1]:
|
if old == Settings[1]:
|
||||||
self.notebk.SetPageText(n, "*%s*" % new)
|
self.notebk.SetPageText(n, "*%s*" % new)
|
||||||
else:
|
else:
|
||||||
self.notebk.SetPageText(n, new)
|
self.notebk.SetPageText(n, new)
|
||||||
|
index = Settings[2].index(old)
|
||||||
Settings[2][index] = new
|
Settings[2][index] = new
|
||||||
self.notebk.GetPage(n).NewName(new)
|
self.notebk.GetPage(n).NewName(new)
|
||||||
if old == "ConfigFileRadio":
|
if old == "ConfigFileRadio":
|
||||||
@@ -452,11 +482,9 @@ class Configuration:
|
|||||||
index = Settings[2].index(name)
|
index = Settings[2].index(name)
|
||||||
del Settings[2][index]
|
del Settings[2][index]
|
||||||
del Settings[3][index]
|
del Settings[3][index]
|
||||||
try:
|
n = self.FindNotebookPage(name)
|
||||||
n = self.radios_page_start + index
|
if n is not None:
|
||||||
self.notebk.DeletePage(n)
|
self.notebk.DeletePage(n)
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return True
|
return True
|
||||||
def GetRadioDict(self, radio_name=None): # None radio_name means the current radio
|
def GetRadioDict(self, radio_name=None): # None radio_name means the current radio
|
||||||
if radio_name:
|
if radio_name:
|
||||||
@@ -1295,6 +1323,7 @@ class RadioNotebook(wx.Notebook): # The second-level notebook for each radio nam
|
|||||||
self.SetBackgroundColour(parent.bg_color)
|
self.SetBackgroundColour(parent.bg_color)
|
||||||
self.radio_name = radio_name
|
self.radio_name = radio_name
|
||||||
self.pages = []
|
self.pages = []
|
||||||
|
self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanging)
|
||||||
def MakePages(self):
|
def MakePages(self):
|
||||||
radio_name = self.radio_name
|
radio_name = self.radio_name
|
||||||
radio_dict = local_conf.GetRadioDict(radio_name)
|
radio_dict = local_conf.GetRadioDict(radio_name)
|
||||||
@@ -1325,6 +1354,20 @@ class RadioNotebook(wx.Notebook): # The second-level notebook for each radio nam
|
|||||||
self.radio_name = new_name
|
self.radio_name = new_name
|
||||||
for page in self.pages:
|
for page in self.pages:
|
||||||
page.radio_name = new_name
|
page.radio_name = new_name
|
||||||
|
def OnPageChanging(self, event):
|
||||||
|
global wxpython_gtk3_bug
|
||||||
|
event.Skip()
|
||||||
|
index = event.GetSelection()
|
||||||
|
page = self.GetPage(index)
|
||||||
|
if sys.platform != 'win32': # Work around a bug in wxPython 4.2.1 and gtk3
|
||||||
|
w, h = application.main_frame.GetSize() # Change main window size by +/- one pixel to recalculate sizes
|
||||||
|
if wxpython_gtk3_bug:
|
||||||
|
wxpython_gtk3_bug = 0
|
||||||
|
h += 1
|
||||||
|
else:
|
||||||
|
wxpython_gtk3_bug = 1
|
||||||
|
h -= 1
|
||||||
|
application.main_frame.SetSize((w, h))
|
||||||
|
|
||||||
class ChoiceCombo(wx.Choice):
|
class ChoiceCombo(wx.Choice):
|
||||||
text_for_blank = "-blank-"
|
text_for_blank = "-blank-"
|
||||||
@@ -1439,7 +1482,13 @@ class ComboCtrl(wxcombo.ComboCtrl):
|
|||||||
wm = w
|
wm = w
|
||||||
wm += charx * 5
|
wm += charx * 5
|
||||||
self.SetSizeHints(wm, self.height, 9999, self.height)
|
self.SetSizeHints(wm, self.height, 9999, self.height)
|
||||||
|
def SetValue(self, value):
|
||||||
|
wxcombo.ComboCtrl.SetValue(self, value)
|
||||||
|
self.SetBackgroundColour(self.bgColor)
|
||||||
|
self.Refresh()
|
||||||
def SetSelection(self, n): # Set text to item in list box. Name conflict with wxcombo.ComboCtrl.
|
def SetSelection(self, n): # Set text to item in list box. Name conflict with wxcombo.ComboCtrl.
|
||||||
|
self.SetBackgroundColour(self.bgColor)
|
||||||
|
self.Refresh()
|
||||||
try:
|
try:
|
||||||
text = self.choices[n]
|
text = self.choices[n]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
@@ -2328,9 +2377,9 @@ class ControlMixin:
|
|||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
class BaseWindow(wx.ScrolledCanvas, ControlMixin):
|
class BaseWindow(wx.ScrolledWindow, ControlMixin):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
wx.ScrolledCanvas.__init__(self, parent)
|
wx.ScrolledWindow.__init__(self, parent)
|
||||||
ControlMixin.__init__(self, parent)
|
ControlMixin.__init__(self, parent)
|
||||||
|
|
||||||
class ConfigConfig(BaseWindow):
|
class ConfigConfig(BaseWindow):
|
||||||
@@ -2653,17 +2702,21 @@ class Radios(BaseWindow): # The "Radios" first-level page
|
|||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
BaseWindow.__init__(self, parent)
|
BaseWindow.__init__(self, parent)
|
||||||
self.SetBackgroundColour(parent.bg_color)
|
self.SetBackgroundColour(parent.bg_color)
|
||||||
self.num_cols = 8
|
self.num_cols = 5
|
||||||
self.radio_name = None
|
self.radio_name = None
|
||||||
self.cur_radio_text = self.AddTextL(1, 'xx', self.num_cols - 1)
|
self.index = 0
|
||||||
self.SetCurrentRadioText()
|
txt = 'Each "radio" is a block of settings that describes a radio to use with Quisk.'
|
||||||
|
self.AddTextL(1, txt, self.num_cols - 1)
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
item = self.AddTextL(1, "When Quisk starts, use the radio")
|
self.radio_buttons = []
|
||||||
|
rb = self.AddRadioButton(1, "When Quisk starts, use the radio", self.NewTop, start=True)
|
||||||
|
rb.SetValue(True)
|
||||||
self.start_radio = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True)
|
self.start_radio = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True)
|
||||||
self.start_radio.handler = self.OnChoiceStartup
|
self.start_radio.handler = self.OnChoiceStartup
|
||||||
|
self.radio_buttons.append((rb, self.start_radio))
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
item = self.AddTextL(1, "Add a new radio with the general type")
|
rb = self.AddRadioButton(1, "Add a new radio with the general type", self.NewTop)
|
||||||
choices = []
|
choices = []
|
||||||
for name, data in local_conf.receiver_data:
|
for name, data in local_conf.receiver_data:
|
||||||
choices.append(name)
|
choices.append(name)
|
||||||
@@ -2671,30 +2724,60 @@ class Radios(BaseWindow): # The "Radios" first-level page
|
|||||||
self.add_type = self.AddComboCtrl(2, '', choices=choices, no_edit=True)
|
self.add_type = self.AddComboCtrl(2, '', choices=choices, no_edit=True)
|
||||||
self.add_type.SetSelection(0)
|
self.add_type.SetSelection(0)
|
||||||
item = self.AddTextL(3, "and name the new radio")
|
item = self.AddTextL(3, "and name the new radio")
|
||||||
self.add_name = self.AddComboCtrl(4, '', choices=["My Radio", "SR with XVtr", "SoftRock"])
|
self.add_name = self.AddComboCtrl(4, '', choices=["My Radio", "Transverter", "SoftRock", "HL2"])
|
||||||
item = self.AddPushButton(5, "Add", self.OnBtnAdd)
|
self.radio_buttons.append((rb, self.add_type, self.add_name))
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
item = self.AddTextL(1, "Rename the radio named")
|
rb = self.AddRadioButton(1, "Add a new radio copied from radio", self.NewTop)
|
||||||
|
self.add_from = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True)
|
||||||
|
item = self.AddTextL(3, "and name the new radio")
|
||||||
|
self.add_name2 = self.AddComboCtrl(4, '', choices=["My Radio", "Transverter", "SoftRock", "HL2"])
|
||||||
|
self.radio_buttons.append((rb, self.add_from, self.add_name2))
|
||||||
|
self.NextRow()
|
||||||
|
rb = self.AddRadioButton(1, "Rename the radio named", self.NewTop)
|
||||||
self.rename_old = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True)
|
self.rename_old = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True)
|
||||||
item = self.AddTextL(3, "to the new name")
|
item = self.AddTextL(3, "to the new name")
|
||||||
self.rename_new = self.AddComboCtrl(4, '', choices=["My Radio", "SR with XVtr", "SoftRock"])
|
self.rename_new = self.AddComboCtrl(4, '', choices=["My Radio", "Transverter", "SoftRock", "HL2"])
|
||||||
item = self.AddPushButton(5, "Rename", self.OnBtnRename)
|
self.radio_buttons.append((rb, self.rename_old, self.rename_new))
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
item = self.AddTextL(1, "Delete the radio named")
|
rb = self.AddRadioButton(1, "Delete the radio named", self.NewTop)
|
||||||
self.delete_name = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True)
|
self.delete_name = self.AddComboCtrl(2, 'big_radio_name', choices=[], no_edit=True)
|
||||||
item = self.AddPushButton(3, "Delete", self.OnBtnDelete)
|
self.radio_buttons.append((rb, self.delete_name))
|
||||||
self.NextRow()
|
self.NextRow()
|
||||||
self.FitInside()
|
self.NextRow()
|
||||||
self.SetScrollRate(1, 1)
|
self.apply_btn = self.AddPushButton(3, "Apply", self.OnBtnApply)
|
||||||
self.NewRadioNames()
|
self.NewRadioNames()
|
||||||
def SetCurrentRadioText(self):
|
self.NewTop()
|
||||||
radio_dict = local_conf.GetRadioDict(self.radio_name)
|
self.SetScrollRate(1, 1)
|
||||||
radio_type = radio_dict['hardware_file_type']
|
def NewTop(self, event=None):
|
||||||
if Settings[1] == "ConfigFileRadio":
|
if event:
|
||||||
text = 'The current radio is ConfigFileRadio, so all settings come from the config file. The hardware type is %s.' % radio_type
|
btn = event.GetEventObject()
|
||||||
|
else: # set first button
|
||||||
|
btn = self.radio_buttons[0][0]
|
||||||
|
for i in range(len(self.radio_buttons)):
|
||||||
|
data = self.radio_buttons[i]
|
||||||
|
rb = data[0]
|
||||||
|
if rb == btn:
|
||||||
|
self.index = i
|
||||||
|
for c in data[1:]:
|
||||||
|
c.Enable(rb == btn)
|
||||||
|
if self.index == 0:
|
||||||
|
self.apply_btn.Enable(False)
|
||||||
else:
|
else:
|
||||||
text = "Quisk is running with settings from the radio %s. The hardware type is %s." % (Settings[1], radio_type)
|
self.apply_btn.Enable(True)
|
||||||
self.cur_radio_text.SetLabel(text)
|
def OnBtnApply(self, event):
|
||||||
|
index = self.index
|
||||||
|
ok = False
|
||||||
|
if index == 1: # add from hardware type
|
||||||
|
ok = self.OnBtnAdd()
|
||||||
|
elif index == 2: # add from existing radio
|
||||||
|
ok = self.OnBtnCopy()
|
||||||
|
elif index == 3: # rename
|
||||||
|
ok = self.OnBtnRename()
|
||||||
|
elif index == 4: # delete
|
||||||
|
ok = self.OnBtnDelete()
|
||||||
|
if ok:
|
||||||
|
self.radio_buttons[0][0].SetValue(True)
|
||||||
|
self.NewTop()
|
||||||
def DuplicateName(self, name):
|
def DuplicateName(self, name):
|
||||||
if name in Settings[2] or name == "ConfigFileRadio":
|
if name in Settings[2] or name == "ConfigFileRadio":
|
||||||
dlg = wx.MessageDialog(self, "The name already exists. Please choose a different name.",
|
dlg = wx.MessageDialog(self, "The name already exists. Please choose a different name.",
|
||||||
@@ -2703,34 +2786,39 @@ class Radios(BaseWindow): # The "Radios" first-level page
|
|||||||
dlg.Destroy()
|
dlg.Destroy()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
def OnBtnAdd(self, event):
|
def OnBtnAdd(self):
|
||||||
name = self.add_name.GetValue().strip()
|
name = self.add_name.GetValue().strip()
|
||||||
if not name or self.DuplicateName(name):
|
if not name or self.DuplicateName(name):
|
||||||
return
|
return
|
||||||
self.add_name.SetValue('')
|
self.add_name.SetValue('')
|
||||||
typ = self.add_type.GetValue().strip()
|
typ = self.add_type.GetValue().strip()
|
||||||
if local_conf.AddRadio(name, typ):
|
if local_conf.AddRadio(name, typ):
|
||||||
if Settings[0] != "Ask me":
|
|
||||||
Settings[0] = name
|
|
||||||
self.NewRadioNames()
|
self.NewRadioNames()
|
||||||
local_conf.settings_changed = True
|
local_conf.settings_changed = True
|
||||||
def OnBtnRename(self, event):
|
return True
|
||||||
|
def OnBtnCopy(self):
|
||||||
|
new_name = self.add_name2.GetValue().strip()
|
||||||
|
if not new_name or self.DuplicateName(new_name):
|
||||||
|
return
|
||||||
|
self.add_name2.SetValue('')
|
||||||
|
old_name = self.add_from.GetValue()
|
||||||
|
if local_conf.CopyRadio(old_name, new_name):
|
||||||
|
self.NewRadioNames()
|
||||||
|
local_conf.settings_changed = True
|
||||||
|
return True
|
||||||
|
def OnBtnRename(self):
|
||||||
old = self.rename_old.GetValue()
|
old = self.rename_old.GetValue()
|
||||||
new = self.rename_new.GetValue().strip()
|
new = self.rename_new.GetValue().strip()
|
||||||
if not old or not new or self.DuplicateName(new):
|
if not old or not new or self.DuplicateName(new):
|
||||||
return
|
return
|
||||||
self.rename_new.SetValue('')
|
self.rename_new.SetValue('')
|
||||||
if local_conf.RenameRadio(old, new):
|
if local_conf.RenameRadio(old, new):
|
||||||
if old == 'ConfigFileRadio' and Settings[1] == "ConfigFileRadio":
|
if Settings[1] == old:
|
||||||
Settings[1] = new
|
Settings[1] = new
|
||||||
elif Settings[1] == old:
|
|
||||||
Settings[1] = new
|
|
||||||
self.SetCurrentRadioText()
|
|
||||||
if Settings[0] != "Ask me":
|
|
||||||
Settings[0] = new
|
|
||||||
self.NewRadioNames()
|
self.NewRadioNames()
|
||||||
local_conf.settings_changed = True
|
local_conf.settings_changed = True
|
||||||
def OnBtnDelete(self, event):
|
return True
|
||||||
|
def OnBtnDelete(self):
|
||||||
name = self.delete_name.GetValue()
|
name = self.delete_name.GetValue()
|
||||||
if not name:
|
if not name:
|
||||||
return
|
return
|
||||||
@@ -2742,6 +2830,7 @@ class Radios(BaseWindow): # The "Radios" first-level page
|
|||||||
if ret == wx.ID_OK and local_conf.DeleteRadio(name):
|
if ret == wx.ID_OK and local_conf.DeleteRadio(name):
|
||||||
self.NewRadioNames()
|
self.NewRadioNames()
|
||||||
local_conf.settings_changed = True
|
local_conf.settings_changed = True
|
||||||
|
return True
|
||||||
def OnChoiceStartup(self, ctrl):
|
def OnChoiceStartup(self, ctrl):
|
||||||
choice = self.start_radio.GetValue()
|
choice = self.start_radio.GetValue()
|
||||||
if Settings[0] != choice:
|
if Settings[0] != choice:
|
||||||
@@ -2751,6 +2840,8 @@ class Radios(BaseWindow): # The "Radios" first-level page
|
|||||||
choices = Settings[2][:] # can rename any available radio
|
choices = Settings[2][:] # can rename any available radio
|
||||||
self.rename_old.SetItems(choices)
|
self.rename_old.SetItems(choices)
|
||||||
self.rename_old.SetSelection(0)
|
self.rename_old.SetSelection(0)
|
||||||
|
self.add_from.SetItems(choices)
|
||||||
|
self.add_from.SetSelection(0)
|
||||||
if "ConfigFileRadio" in choices: # can not delete ConfigFileRadio
|
if "ConfigFileRadio" in choices: # can not delete ConfigFileRadio
|
||||||
choices.remove("ConfigFileRadio")
|
choices.remove("ConfigFileRadio")
|
||||||
self.delete_name.SetItems(choices)
|
self.delete_name.SetItems(choices)
|
||||||
@@ -2762,13 +2853,13 @@ class Radios(BaseWindow): # The "Radios" first-level page
|
|||||||
try: # Set text in control
|
try: # Set text in control
|
||||||
index = choices.index(Settings[0]) # last used radio, or new or renamed radio
|
index = choices.index(Settings[0]) # last used radio, or new or renamed radio
|
||||||
except:
|
except:
|
||||||
num = len(Settings[2])
|
num = len(Settings[2]) # number of radios
|
||||||
if len == 0:
|
if num == 0: # no radios
|
||||||
index = 1
|
index = 1 # use ConfigFileRadio
|
||||||
elif num == 1:
|
elif num == 1: # one radio
|
||||||
index = 0
|
index = 0 # use the one radio
|
||||||
else:
|
else:
|
||||||
index = len(choices) - 2
|
index = len(choices) - 2 # use the last radio
|
||||||
Settings[0] = choices[index]
|
Settings[0] = choices[index]
|
||||||
self.start_radio.SetSelection(index)
|
self.start_radio.SetSelection(index)
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ Quisk is free open source software written in Python and C. It is hosted on the
|
|||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
If you have Python 3 installed on your computer, just continue to use that version.
|
If you have Python 3 installed on your computer, just continue to use that version.
|
||||||
<b>On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11 or 3.12.</b>
|
<b>On Windows Quisk requires Python 3.8, 3.9, 3.10, 3.11, 3.12 or 3.13.</b>
|
||||||
If you want to install a more recent version of Python, uninstall the old version first.
|
If you want to install a more recent version of Python, uninstall the old version first.
|
||||||
Running multiple versions of Python is possible but can be confusing and is not necessary.
|
Running multiple versions of Python is possible but can be confusing and is not necessary.
|
||||||
A Quisk installation is needed for each version of Python you have.
|
A Quisk installation is needed for each version of Python you have.
|
||||||
@@ -204,7 +204,7 @@ A Quisk installation is needed for each version of Python you have.
|
|||||||
<p>
|
<p>
|
||||||
Windows does not include Python, so you must first install a recent version of 64-bit Python 3.
|
Windows does not include Python, so you must first install a recent version of 64-bit Python 3.
|
||||||
If you already have 64-bit Python 3 installed, just keep using it.
|
If you already have 64-bit Python 3 installed, just keep using it.
|
||||||
<b>On Windows Quisk requires Python 3.8 or 3.9 or 3.10 or 3.11 or 3.12.</b>
|
<b>On Windows Quisk requires Python 3.8, 3.9, 3.10, 3.11, 3.12 or 3.13.</b>
|
||||||
There may be newer versions of Python, but until support is added, use the versions listed above.
|
There may be newer versions of Python, but until support is added, use the versions listed above.
|
||||||
Python is available from <a href="http://www.python.org/">http://www.python.org</a>.
|
Python is available from <a href="http://www.python.org/">http://www.python.org</a>.
|
||||||
There is an option to <b>Add Python to PATH</b>. You MUST select
|
There is an option to <b>Add Python to PATH</b>. You MUST select
|
||||||
@@ -256,6 +256,7 @@ If you change to that directory you can run Quisk with "pythonw quisk.py".
|
|||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
To get started you must tell Quisk what kind of radio hardware you have. Press the Config button and select Radios.
|
To get started you must tell Quisk what kind of radio hardware you have. Press the Config button and select Radios.
|
||||||
|
Add a radio from the list of hardware radios available. Or add the best match.
|
||||||
Then set your sound devices for that radio; the device for the radio speakers, the microphone and so forth. All configuration
|
Then set your sound devices for that radio; the device for the radio speakers, the microphone and so forth. All configuration
|
||||||
is (mostly) from the Config button. Ignore old directions and don't bother with a config file.
|
is (mostly) from the Config button. Ignore old directions and don't bother with a config file.
|
||||||
</p>
|
</p>
|
||||||
@@ -385,6 +386,7 @@ You run Quisk by running the file quisk.py with python3. For example, "python3 /
|
|||||||
You can create a desktop icon to do that, but the method depends on your version of Linux.
|
You can create a desktop icon to do that, but the method depends on your version of Linux.
|
||||||
<b>Never run Quisk with root privelege by using sudo. Fix the permission problem instead.</b>
|
<b>Never run Quisk with root privelege by using sudo. Fix the permission problem instead.</b>
|
||||||
To get started you must tell Quisk what kind of radio hardware you have. Press the Config button and select Radios.
|
To get started you must tell Quisk what kind of radio hardware you have. Press the Config button and select Radios.
|
||||||
|
Add a radio from the list of hardware radios available. Or add the best match.
|
||||||
Then set your sound devices for that radio; the device for the radio speakers, the microphone and so forth. All configuration
|
Then set your sound devices for that radio; the device for the radio speakers, the microphone and so forth. All configuration
|
||||||
is (mostly) from the Config button. Ignore old directions and don't bother with a config file.
|
is (mostly) from the Config button. Ignore old directions and don't bother with a config file.
|
||||||
To uninstall Quisk, just delete the quisk directory.
|
To uninstall Quisk, just delete the quisk directory.
|
||||||
@@ -532,7 +534,7 @@ When you first install Quisk, you will not have any settings for your radio.
|
|||||||
Press the Config button and go to the Radios screen.
|
Press the Config button and go to the Radios screen.
|
||||||
Then create a radio by specifying the general hardware type and give it a name of your choosing.
|
Then create a radio by specifying the general hardware type and give it a name of your choosing.
|
||||||
For a Hermes-Lite, specify "Hermes" as the hardware type and call it "HL2" (or some other name).
|
For a Hermes-Lite, specify "Hermes" as the hardware type and call it "HL2" (or some other name).
|
||||||
Press "Add" and a new tab for your radio will appear. Look through the various settings on the HL2 tab.
|
Press "Apply" and a new tab for your radio will appear. Look through the various settings on the HL2 tab.
|
||||||
The parameters for the radios are stored in the file quisk_settings.json.
|
The parameters for the radios are stored in the file quisk_settings.json.
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
@@ -1493,16 +1495,24 @@ at his site <a href="https://dk1mi.radio/quisk-fldigi/">https://dk1mi.radio/quis
|
|||||||
If your digital program only uses Alsa devices or you don't want to use PulseAudio,
|
If your digital program only uses Alsa devices or you don't want to use PulseAudio,
|
||||||
you can use the ALSA loopback device.
|
you can use the ALSA loopback device.
|
||||||
It works the same way as the Windows VAC. First create the loopback device with this command:
|
It works the same way as the Windows VAC. First create the loopback device with this command:
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
<pre>
|
<pre>
|
||||||
sudo modprobe snd-aloop index=19
|
sudo modprobe snd-aloop index=19
|
||||||
</pre>
|
</pre>
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
The "index" is the card number, and it is best to use a number higher than your regular cards.
|
The "index" is the card number, and it is best to use a number higher than your regular cards.
|
||||||
The loopback device will be gone on the next restart.
|
The loopback device will be gone on the next restart.
|
||||||
You can create the loopback device every time the system starts, but the way to do that depends on your version of Linux.
|
You can create the loopback device every time the system starts, but the way to do that depends on your version of Linux.
|
||||||
You could add this line to /etc/modprobe.d/alsa-base.conf:
|
You could add this line to /etc/modprobe.d/alsa-base.conf:
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
<pre>
|
<pre>
|
||||||
options snd-aloop index=19
|
options snd-aloop index=19
|
||||||
</pre>
|
</pre>
|
||||||
|
<br>
|
||||||
|
<p>
|
||||||
Or you could add snd-aloop to /etc/modules or put the modprobe command in /etc/rc.local instead.
|
Or you could add snd-aloop to /etc/modules or put the modprobe command in /etc/rc.local instead.
|
||||||
Restart Linux. Now you can enter "cat /proc/asound/cards" to
|
Restart Linux. Now you can enter "cat /proc/asound/cards" to
|
||||||
print out your sound cards, and you should see a "Loopback" card listed.
|
print out your sound cards, and you should see a "Loopback" card listed.
|
||||||
@@ -1517,6 +1527,7 @@ Your audio is now connected and you
|
|||||||
should be able to receive digital signals. Be sure to test your
|
should be able to receive digital signals. Be sure to test your
|
||||||
transmit signal off the air. You may need to reduce power to
|
transmit signal off the air. You may need to reduce power to
|
||||||
improve linearity.
|
improve linearity.
|
||||||
|
</p>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import os
|
|||||||
# You must define the version here. A title string including
|
# You must define the version here. A title string including
|
||||||
# the version will be written to __init__.py and read by quisk.py.
|
# the version will be written to __init__.py and read by quisk.py.
|
||||||
|
|
||||||
Version = '4.2.40'
|
Version = '4.2.41'
|
||||||
|
|
||||||
fp = open("__init__.py", "w") # write title string
|
fp = open("__init__.py", "w") # write title string
|
||||||
fp.write("#Quisk version %s\n" % Version)
|
fp.write("#Quisk version %s\n" % Version)
|
||||||
|
|||||||
Reference in New Issue
Block a user