kopia lustrzana https://github.com/ctjacobs/pyqso
Added a new menu item for updating the ADIF modes from the ADIF website. Addresses issue #69.
rodzic
091da72806
commit
2487424839
|
@ -85,6 +85,7 @@ class PyQSO:
|
|||
# Kills the application if the close button is clicked on the main window itself.
|
||||
self.window.connect("delete-event", Gtk.main_quit)
|
||||
|
||||
# Status bar.
|
||||
self.statusbar = self.builder.get_object("statusbar")
|
||||
context_id = self.statusbar.get_context_id("Status")
|
||||
self.statusbar.push(context_id, "No logbook is currently open.")
|
||||
|
|
|
@ -27,6 +27,8 @@ except ImportError:
|
|||
import ConfigParser as configparser
|
||||
from os.path import expanduser
|
||||
|
||||
from pyqso.modes import Modes
|
||||
|
||||
# ADIF field names and their associated data types available in PyQSO.
|
||||
AVAILABLE_FIELD_NAMES_TYPES = {"CALL": "S",
|
||||
"QSO_DATE": "D",
|
||||
|
@ -98,98 +100,6 @@ AVAILABLE_FIELD_NAMES_FRIENDLY = {"CALL": "Callsign",
|
|||
# E: Enumerated
|
||||
DATA_TYPES = ["A", "B", "N", "S", "I", "D", "T", "M", "G", "L", "E"]
|
||||
|
||||
# All the valid modes listed in the ADIF specification. This is a dictionary with the key-value pairs holding the MODE and SUBMODE(s) respectively.
|
||||
MODES = {"": ("",),
|
||||
"AM": ("",),
|
||||
"ATV": ("",),
|
||||
"CHIP": ("", "CHIP64", "CHIP128"),
|
||||
"CLO": ("",),
|
||||
"CONTESTI": ("",),
|
||||
"CW": ("", "PCW"),
|
||||
"DIGITALVOICE": ("",),
|
||||
"DOMINO": ("", "DOMINOEX", "DOMINOF"),
|
||||
"DSTAR": ("",),
|
||||
"FAX": ("",),
|
||||
"FM": ("",),
|
||||
"FSK441": ("",),
|
||||
"FT8": ("",),
|
||||
"HELL": ("", "FMHELL", "FSKHELL", "HELL80", "HFSK", "PSKHELL"),
|
||||
"ISCAT": ("", "ISCAT-A", "ISCAT-B"),
|
||||
"JT4": ("", "JT4A", "JT4B", "JT4C", "JT4D", "JT4E", "JT4F", "JT4G"),
|
||||
"JT6M": ("",),
|
||||
"JT9": ("",),
|
||||
"JT44": ("",),
|
||||
"JT65": ("", "JT65A", "JT65B", "JT65B2", "JT65C", "JT65C2"),
|
||||
"MFSK": ("", "MFSK4", "MFSK8", "MFSK11", "MFSK16", "MFSK22", "MFSK31", "MFSK32", "MFSK64", "MFSK128"),
|
||||
"MT63": ("",),
|
||||
"OLIVIA": ("", "OLIVIA 4/125", "OLIVIA 4/250", "OLIVIA 8/250", "OLIVIA 8/500", "OLIVIA 16/500", "OLIVIA 16/1000", "OLIVIA 32/1000"),
|
||||
"OPERA": ("", "OPERA-BEACON", "OPERA-QSO"),
|
||||
"PAC": ("", "PAC2", "PAC3", "PAC4"),
|
||||
"PAX": ("", "PAX2"),
|
||||
"PKT": ("",),
|
||||
"PSK": ("", "FSK31", "PSK10", "PSK31", "PSK63", "PSK63F", "PSK125", "PSK250", "PSK500", "PSK1000", "PSKAM10", "PSKAM31", "PSKAM50", "PSKFEC31", "QPSK31", "QPSK63", "QPSK125", "QPSK250", "QPSK500"),
|
||||
"PSK2K": ("",),
|
||||
"Q15": ("",),
|
||||
"ROS": ("", "ROS-EME", "ROS-HF", "ROS-MF"),
|
||||
"RTTY": ("", "ASCI"),
|
||||
"RTTYM": ("",),
|
||||
"SSB": ("", "LSB", "USB"),
|
||||
"SSTV": ("",),
|
||||
"THOR": ("",),
|
||||
"THRB": ("", "THRBX"),
|
||||
"TOR": ("", "AMTORFEC", "GTOR"),
|
||||
"V4": ("",),
|
||||
"VOI": ("",),
|
||||
"WINMOR": ("",),
|
||||
"WSPR": ("",)
|
||||
}
|
||||
|
||||
# A dictionary of all the deprecated MODE values.
|
||||
MODES_DEPRECATED = {"AMTORFEC": ("",),
|
||||
"ASCI": ("",),
|
||||
"CHIP64": ("",),
|
||||
"CHIP128": ("",),
|
||||
"DOMINOF": ("",),
|
||||
"FMHELL": ("",),
|
||||
"FSK31": ("",),
|
||||
"GTOR": ("",),
|
||||
"HELL80": ("",),
|
||||
"HFSK": ("",),
|
||||
"JT4A": ("",),
|
||||
"JT4B": ("",),
|
||||
"JT4C": ("",),
|
||||
"JT4D": ("",),
|
||||
"JT4E": ("",),
|
||||
"JT4F": ("",),
|
||||
"JT4G": ("",),
|
||||
"JT65A": ("",),
|
||||
"JT65B": ("",),
|
||||
"JT65C": ("",),
|
||||
"MFSK8": ("",),
|
||||
"MFSK16": ("",),
|
||||
"PAC2": ("",),
|
||||
"PAC3": ("",),
|
||||
"PAX2": ("",),
|
||||
"PCW": ("",),
|
||||
"PSK10": ("",),
|
||||
"PSK31": ("",),
|
||||
"PSK63": ("",),
|
||||
"PSK63F": ("",),
|
||||
"PSK125": ("",),
|
||||
"PSKAM10": ("",),
|
||||
"PSKAM31": ("",),
|
||||
"PSKAM50": ("",),
|
||||
"PSKFEC31": ("",),
|
||||
"PSKHELL": ("",),
|
||||
"QPSK31": ("",),
|
||||
"QPSK63": ("",),
|
||||
"QPSK125": ("",),
|
||||
"THRBX": ("",)
|
||||
}
|
||||
|
||||
# Include all deprecated modes.
|
||||
MODES.update(MODES_DEPRECATED)
|
||||
|
||||
# All the bands listed in the ADIF specification.
|
||||
BANDS = ["", "2190m", "630m", "560m", "160m", "80m", "60m", "40m", "30m", "20m", "17m", "15m", "12m", "10m", "6m", "4m", "2m", "1.25m", "70cm", "33cm", "23cm", "13cm", "9cm", "6cm", "3cm", "1.25cm", "6mm", "4mm", "2.5mm", "2mm", "1mm"]
|
||||
# The lower and upper frequency bounds (in MHz) for each band in BANDS.
|
||||
|
@ -207,6 +117,7 @@ class ADIF:
|
|||
|
||||
def __init__(self):
|
||||
""" Initialise class for I/O of files using the Amateur Data Interchange Format (ADIF). """
|
||||
self.modes = Modes()
|
||||
return
|
||||
|
||||
def read(self, path):
|
||||
|
@ -521,9 +432,9 @@ class ADIF:
|
|||
elif(data_type == "E" or data_type == "A"):
|
||||
# Enumeration, AwardList.
|
||||
if(field_name == "MODE"):
|
||||
return (data in list(MODES.keys()))
|
||||
return (data in list(self.modes.all.keys()))
|
||||
elif(field_name == "SUBMODE"):
|
||||
submodes = [submode for mode in list(MODES.keys()) for submode in MODES[mode]]
|
||||
submodes = [submode for mode in list(self.modes.all.keys()) for submode in self.modes.all[mode]]
|
||||
return (data in submodes)
|
||||
elif(field_name == "BAND"):
|
||||
return (data in BANDS)
|
||||
|
|
|
@ -38,6 +38,7 @@ from pyqso.summary import Summary
|
|||
from pyqso.blank import Blank
|
||||
from pyqso.printer import Printer
|
||||
from pyqso.compare import compare_date_and_time, compare_default
|
||||
from pyqso.update_modes_dialog import UpdateModesDialog
|
||||
|
||||
|
||||
class Logbook:
|
||||
|
@ -907,7 +908,7 @@ class Logbook:
|
|||
return
|
||||
log = self.logs[log_index]
|
||||
|
||||
(sort_model, path) = self.treeselection[log_index].get_selected_rows() # Get the selected row in the log
|
||||
(sort_model, path) = self.treeselection[log_index].get_selected_rows() # Get the selected row in the log.
|
||||
try:
|
||||
sort_iter = sort_model.get_iter(path[0])
|
||||
filter_iter = self.sorter[log_index].convert_iter_to_child_iter(sort_iter)
|
||||
|
@ -1113,6 +1114,16 @@ class Logbook:
|
|||
|
||||
return
|
||||
|
||||
def update_modes_callback(self, widget=None, path=None):
|
||||
umd = UpdateModesDialog(self.application)
|
||||
response = umd.dialog.run()
|
||||
if(response == Gtk.ResponseType.OK):
|
||||
modes = Modes()
|
||||
modes.update(url=umd.url)
|
||||
umd.dialog.destroy()
|
||||
return
|
||||
|
||||
|
||||
@property
|
||||
def log_count(self):
|
||||
""" Return the total number of logs in the logbook.
|
||||
|
|
|
@ -109,6 +109,10 @@ class Menu:
|
|||
self.items["RECORD_COUNT"] = self.builder.get_object("mitem_record_count")
|
||||
self.items["RECORD_COUNT"].connect("activate", self.application.logbook.record_count_callback)
|
||||
|
||||
# Record count
|
||||
self.items["UPDATE_MODES"] = self.builder.get_object("mitem_update_modes")
|
||||
self.items["UPDATE_MODES"].connect("activate", self.application.logbook.update_modes_callback)
|
||||
|
||||
# View toolbox
|
||||
self.items["TOOLBOX"] = self.builder.get_object("mitem_toolbox")
|
||||
config = configparser.ConfigParser()
|
||||
|
|
198
pyqso/modes.py
198
pyqso/modes.py
|
@ -17,24 +17,192 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with PyQSO. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sqlite3
|
||||
import os
|
||||
from urllib.request import urlopen
|
||||
from bs4 import BeautifulSoup
|
||||
import logging
|
||||
|
||||
page = urlopen('http://www.adif.org/307/ADIF_307.htm').read()
|
||||
soup = BeautifulSoup(page, "html.parser")
|
||||
MODES_FILE = os.path.expanduser("~/.config/pyqso/modes.db")
|
||||
|
||||
# Remove the <span> tags but keep the tags' contents.
|
||||
for match in soup.findAll('span'):
|
||||
match.unwrap()
|
||||
|
||||
# Find the MODES table.
|
||||
rows = soup.find(id="Enumeration_Mode").find_all('tr')
|
||||
class Modes:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
try:
|
||||
connection = sqlite3.connect(MODES_FILE)
|
||||
c = connection.cursor()
|
||||
c.execute("""CREATE TABLE IF NOT EXISTS modes (
|
||||
mode TEXT NOT NULL,
|
||||
submode TEXT NOT NULL,
|
||||
UNIQUE(mode, submode)
|
||||
); """)
|
||||
|
||||
# Fill the new table with the basic list of modes and submodes.
|
||||
for mode in self.basic:
|
||||
for submode in self.basic[mode]:
|
||||
c.execute("""REPLACE INTO modes(mode, submode) VALUES(?, ?)""", (mode, submode))
|
||||
connection.commit()
|
||||
connection.close()
|
||||
except sqlite3.Error as e:
|
||||
logging.exception(e)
|
||||
|
||||
#self.update("http://www.adif.org/309/ADIF_309.htm")
|
||||
return
|
||||
|
||||
@property
|
||||
def basic(self):
|
||||
""" A basic list of valid modes listed in the ADIF specification.
|
||||
This is a dictionary with the key-value pairs holding the MODE and SUBMODE(s) respectively. """
|
||||
|
||||
modes = {"": ("",),
|
||||
"AM": ("",),
|
||||
"ATV": ("",),
|
||||
"CHIP": ("", "CHIP64", "CHIP128"),
|
||||
"CLO": ("",),
|
||||
"CONTESTI": ("",),
|
||||
"CW": ("", "PCW"),
|
||||
"DIGITALVOICE": ("",),
|
||||
"DOMINO": ("", "DOMINOEX", "DOMINOF"),
|
||||
"DSTAR": ("",),
|
||||
"FAX": ("",),
|
||||
"FM": ("",),
|
||||
"FSK441": ("",),
|
||||
"FT8": ("",),
|
||||
"HELL": ("", "FMHELL", "FSKHELL", "HELL80", "HFSK", "PSKHELL"),
|
||||
"ISCAT": ("", "ISCAT-A", "ISCAT-B"),
|
||||
"JT4": ("", "JT4A", "JT4B", "JT4C", "JT4D", "JT4E", "JT4F", "JT4G"),
|
||||
"JT6M": ("",),
|
||||
"JT9": ("",),
|
||||
"JT44": ("",),
|
||||
"JT65": ("", "JT65A", "JT65B", "JT65B2", "JT65C", "JT65C2"),
|
||||
"MFSK": ("", "MFSK4", "MFSK8", "MFSK11", "MFSK16", "MFSK22", "MFSK31", "MFSK32", "MFSK64", "MFSK128"),
|
||||
"MT63": ("",),
|
||||
"OLIVIA": ("", "OLIVIA 4/125", "OLIVIA 4/250", "OLIVIA 8/250", "OLIVIA 8/500", "OLIVIA 16/500", "OLIVIA 16/1000", "OLIVIA 32/1000"),
|
||||
"OPERA": ("", "OPERA-BEACON", "OPERA-QSO"),
|
||||
"PAC": ("", "PAC2", "PAC3", "PAC4"),
|
||||
"PAX": ("", "PAX2"),
|
||||
"PKT": ("",),
|
||||
"PSK": ("", "FSK31", "PSK10", "PSK31", "PSK63", "PSK63F", "PSK125", "PSK250", "PSK500", "PSK1000", "PSKAM10", "PSKAM31", "PSKAM50", "PSKFEC31", "QPSK31", "QPSK63", "QPSK125", "QPSK250", "QPSK500"),
|
||||
"PSK2K": ("",),
|
||||
"Q15": ("",),
|
||||
"ROS": ("", "ROS-EME", "ROS-HF", "ROS-MF"),
|
||||
"RTTY": ("", "ASCI"),
|
||||
"RTTYM": ("",),
|
||||
"SSB": ("", "LSB", "USB"),
|
||||
"SSTV": ("",),
|
||||
"THOR": ("",),
|
||||
"THRB": ("", "THRBX"),
|
||||
"TOR": ("", "AMTORFEC", "GTOR"),
|
||||
"V4": ("",),
|
||||
"VOI": ("",),
|
||||
"WINMOR": ("",),
|
||||
"WSPR": ("",)
|
||||
}
|
||||
|
||||
# A dictionary of all the deprecated MODE values.
|
||||
deprecated = {"AMTORFEC": ("",),
|
||||
"ASCI": ("",),
|
||||
"CHIP64": ("",),
|
||||
"CHIP128": ("",),
|
||||
"DOMINOF": ("",),
|
||||
"FMHELL": ("",),
|
||||
"FSK31": ("",),
|
||||
"GTOR": ("",),
|
||||
"HELL80": ("",),
|
||||
"HFSK": ("",),
|
||||
"JT4A": ("",),
|
||||
"JT4B": ("",),
|
||||
"JT4C": ("",),
|
||||
"JT4D": ("",),
|
||||
"JT4E": ("",),
|
||||
"JT4F": ("",),
|
||||
"JT4G": ("",),
|
||||
"JT65A": ("",),
|
||||
"JT65B": ("",),
|
||||
"JT65C": ("",),
|
||||
"MFSK8": ("",),
|
||||
"MFSK16": ("",),
|
||||
"PAC2": ("",),
|
||||
"PAC3": ("",),
|
||||
"PAX2": ("",),
|
||||
"PCW": ("",),
|
||||
"PSK10": ("",),
|
||||
"PSK31": ("",),
|
||||
"PSK63": ("",),
|
||||
"PSK63F": ("",),
|
||||
"PSK125": ("",),
|
||||
"PSKAM10": ("",),
|
||||
"PSKAM31": ("",),
|
||||
"PSKAM50": ("",),
|
||||
"PSKFEC31": ("",),
|
||||
"PSKHELL": ("",),
|
||||
"QPSK31": ("",),
|
||||
"QPSK63": ("",),
|
||||
"QPSK125": ("",),
|
||||
"THRBX": ("",)
|
||||
}
|
||||
|
||||
# Include all deprecated modes.
|
||||
modes.update(deprecated)
|
||||
return modes
|
||||
|
||||
@property
|
||||
def all(self):
|
||||
try:
|
||||
connection = sqlite3.connect(MODES_FILE)
|
||||
c = connection.cursor()
|
||||
result = c.execute("""SELECT * FROM modes""")
|
||||
rows = result.fetchall()
|
||||
|
||||
modes = {}
|
||||
for row in rows:
|
||||
mode = row[0]
|
||||
submode = row[1]
|
||||
if(mode in modes.keys()):
|
||||
modes[mode].append(submode)
|
||||
else:
|
||||
modes[mode] = [submode]
|
||||
connection.close()
|
||||
except sqlite3.Error as e:
|
||||
logging.exception(e)
|
||||
return modes
|
||||
|
||||
def update(self, url):
|
||||
modes = self.parse(url)
|
||||
try:
|
||||
connection = sqlite3.connect(MODES_FILE)
|
||||
c = connection.cursor()
|
||||
for mode in modes:
|
||||
for submode in modes[mode]:
|
||||
c.execute("REPLACE INTO modes(mode, submode) VALUES(?,?)", (mode, submode))
|
||||
connection.commit()
|
||||
connection.close()
|
||||
except sqlite3.Error as e:
|
||||
logging.exception(e)
|
||||
|
||||
return
|
||||
|
||||
def parse(self, url):
|
||||
page = urlopen(url).read()
|
||||
soup = BeautifulSoup(page, "html.parser")
|
||||
|
||||
# Remove the <span> tags but keep the tags' contents.
|
||||
for match in soup.findAll("span"):
|
||||
match.unwrap()
|
||||
|
||||
# Find the MODES table.
|
||||
rows = soup.find(id="Enumeration_Mode").find_all("tr")
|
||||
|
||||
# Extract modes and submodes.
|
||||
modes = {}
|
||||
for row in rows[1:]: # Ignores the header row.
|
||||
mode, submode = row.find_all("td")[0:2]
|
||||
mode = mode.text.split(" (import-only)")[0].strip()
|
||||
submode = tuple(submode.text.strip().split(", "))
|
||||
if(mode not in modes):
|
||||
modes[mode] = submode
|
||||
|
||||
return modes
|
||||
|
||||
# Extract modes and submodes.
|
||||
modes = {}
|
||||
for row in rows[1:]:
|
||||
mode, submode, description = row.find_all('td')
|
||||
mode = mode.text.split(" (import-only)")[0].strip()
|
||||
submode = tuple(submode.text.strip().split(", "))
|
||||
modes[mode] = submode
|
||||
print(modes)
|
||||
|
|
|
@ -38,7 +38,8 @@ except ImportError:
|
|||
logging.warning("Could not import the geocoder module!")
|
||||
have_geocoder = False
|
||||
|
||||
from pyqso.adif import AVAILABLE_FIELD_NAMES_ORDERED, MODES
|
||||
from pyqso.adif import AVAILABLE_FIELD_NAMES_ORDERED
|
||||
from pyqso.modes import Modes
|
||||
from pyqso.auxiliary_dialogs import error
|
||||
|
||||
PREFERENCES_FILE = os.path.expanduser("~/.config/pyqso/preferences.ini")
|
||||
|
@ -289,27 +290,28 @@ class RecordsPage:
|
|||
# Default values
|
||||
|
||||
# Mode
|
||||
self.modes = Modes()
|
||||
self.sources["DEFAULT_MODE"] = self.builder.get_object("default_values_mode_combo")
|
||||
for mode in sorted(MODES.keys()):
|
||||
for mode in sorted(self.modes.all.keys()):
|
||||
self.sources["DEFAULT_MODE"].append_text(mode)
|
||||
(section, option) = ("records", "default_mode")
|
||||
if(have_config and config.has_option(section, option)):
|
||||
mode = config.get(section, option)
|
||||
else:
|
||||
mode = ""
|
||||
self.sources["DEFAULT_MODE"].set_active(sorted(MODES.keys()).index(mode))
|
||||
self.sources["DEFAULT_MODE"].set_active(sorted(self.modes.all.keys()).index(mode))
|
||||
self.sources["DEFAULT_MODE"].connect("changed", self.on_mode_changed)
|
||||
|
||||
# Submode
|
||||
self.sources["DEFAULT_SUBMODE"] = self.builder.get_object("default_values_submode_combo")
|
||||
for submode in MODES[mode]:
|
||||
for submode in self.modes.all[mode]:
|
||||
self.sources["DEFAULT_SUBMODE"].append_text(submode)
|
||||
(section, option) = ("records", "default_submode")
|
||||
if(have_config and config.has_option(section, option)):
|
||||
submode = config.get(section, option)
|
||||
else:
|
||||
submode = ""
|
||||
self.sources["DEFAULT_SUBMODE"].set_active(MODES[mode].index(submode))
|
||||
self.sources["DEFAULT_SUBMODE"].set_active(self.modes.all[mode].index(submode))
|
||||
|
||||
# Power
|
||||
self.sources["DEFAULT_POWER"] = self.builder.get_object("default_values_tx_power_entry")
|
||||
|
@ -384,9 +386,9 @@ class RecordsPage:
|
|||
""" If the MODE field has changed its value, then fill the SUBMODE field with all the available SUBMODE options for that new MODE. """
|
||||
self.sources["DEFAULT_SUBMODE"].get_model().clear()
|
||||
mode = combo.get_active_text()
|
||||
for submode in MODES[mode]:
|
||||
for submode in self.modes.all[mode]:
|
||||
self.sources["DEFAULT_SUBMODE"].append_text(submode)
|
||||
self.sources["DEFAULT_SUBMODE"].set_active(MODES[mode].index(""))
|
||||
self.sources["DEFAULT_SUBMODE"].set_active(self.modes.all[mode].index(""))
|
||||
return
|
||||
|
||||
|
||||
|
@ -406,8 +408,8 @@ class ImportExportPage:
|
|||
config = configparser.ConfigParser()
|
||||
have_config = (config.read(PREFERENCES_FILE) != [])
|
||||
|
||||
# Import
|
||||
self.sources["MERGE_COMMENT"] = self.builder.get_object("adif_import_merge_comment_checkbutton")
|
||||
# ADIF
|
||||
self.sources["MERGE_COMMENT"] = self.builder.get_object("adif_merge_comment_checkbutton")
|
||||
(section, option) = ("import_export", "merge_comment")
|
||||
if(have_config and config.has_option(section, option)):
|
||||
self.sources["MERGE_COMMENT"].set_active(config.getboolean(section, option))
|
||||
|
|
|
@ -104,8 +104,9 @@ class RecordDialog:
|
|||
self.sources["BAND"].set_active(0) # Set an empty string as the default option.
|
||||
|
||||
# MODE
|
||||
self.modes = Modes().all
|
||||
self.sources["MODE"] = self.builder.get_object("qso_mode_combo")
|
||||
for mode in sorted(MODES.keys()):
|
||||
for mode in sorted(self.modes.keys()):
|
||||
self.sources["MODE"].append_text(mode)
|
||||
self.sources["MODE"].set_active(0) # Set an empty string as the default option.
|
||||
self.sources["MODE"].connect("changed", self.on_mode_changed)
|
||||
|
@ -199,12 +200,12 @@ class RecordDialog:
|
|||
converted = self.convert_frequency(data, from_unit="MHz", to_unit=self.frequency_unit)
|
||||
self.sources[field_names[i]].set_text(str(converted))
|
||||
elif(field_names[i] == "MODE"):
|
||||
self.sources[field_names[i]].set_active(sorted(MODES.keys()).index(data))
|
||||
self.sources[field_names[i]].set_active(sorted(self.modes.keys()).index(data))
|
||||
# Handle SUBMODE at the same time.
|
||||
submode_data = record["submode"]
|
||||
if(submode_data is None):
|
||||
submode_data = ""
|
||||
self.sources["SUBMODE"].set_active(MODES[data].index(submode_data))
|
||||
self.sources["SUBMODE"].set_active(self.modes[data].index(submode_data))
|
||||
elif(field_names[i] == "SUBMODE"):
|
||||
# Skip, because this has been (or will be) handled when populating the MODE field.
|
||||
continue
|
||||
|
@ -227,7 +228,7 @@ class RecordDialog:
|
|||
mode = config.get(section, option)
|
||||
else:
|
||||
mode = ""
|
||||
self.sources["MODE"].set_active(sorted(MODES.keys()).index(mode))
|
||||
self.sources["MODE"].set_active(sorted(self.modes.keys()).index(mode))
|
||||
|
||||
# Submode
|
||||
(section, option) = ("records", "default_submode")
|
||||
|
@ -235,7 +236,7 @@ class RecordDialog:
|
|||
submode = config.get(section, option)
|
||||
else:
|
||||
submode = ""
|
||||
self.sources["SUBMODE"].set_active(MODES[mode].index(submode))
|
||||
self.sources["SUBMODE"].set_active(self.modes[mode].index(submode))
|
||||
|
||||
# Power
|
||||
(section, option) = ("records", "default_power")
|
||||
|
@ -303,9 +304,9 @@ class RecordDialog:
|
|||
""" If the MODE field has changed its value, then fill the SUBMODE field with all the available SUBMODE options for that new MODE. """
|
||||
self.sources["SUBMODE"].get_model().clear()
|
||||
mode = combo.get_active_text()
|
||||
for submode in MODES[mode]:
|
||||
for submode in self.modes[mode]:
|
||||
self.sources["SUBMODE"].append_text(submode)
|
||||
self.sources["SUBMODE"].set_active(MODES[mode].index("")) # Set the submode to an empty string.
|
||||
self.sources["SUBMODE"].set_active(self.modes[mode].index("")) # Set the submode to an empty string.
|
||||
return
|
||||
|
||||
def on_key_press(self, widget, event):
|
||||
|
@ -375,10 +376,10 @@ class RecordDialog:
|
|||
if(mode == "USB" or mode == "LSB"):
|
||||
submode = mode
|
||||
mode = "SSB"
|
||||
self.sources["MODE"].set_active(sorted(MODES.keys()).index(mode))
|
||||
self.sources["SUBMODE"].set_active(MODES[mode].index(submode))
|
||||
self.sources["MODE"].set_active(sorted(self.modes.keys()).index(mode))
|
||||
self.sources["SUBMODE"].set_active(self.modes[mode].index(submode))
|
||||
else:
|
||||
self.sources["MODE"].set_active(sorted(MODES.keys()).index(mode))
|
||||
self.sources["MODE"].set_active(sorted(self.modes.keys()).index(mode))
|
||||
except:
|
||||
logging.error("Could not obtain the current mode (e.g. FM, AM, CW) via Hamlib!")
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<!-- Generated with glade 3.22.1 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<object class="GtkImage" id="image1">
|
||||
|
@ -68,6 +68,12 @@
|
|||
<property name="stock">gtk-open</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="image25">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-refresh</property>
|
||||
<property name="icon_size">1</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="image29">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -128,6 +134,9 @@
|
|||
<property name="default_width">800</property>
|
||||
<property name="default_height">600</property>
|
||||
<property name="icon">log_64x64.png</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="vbox_outer">
|
||||
<property name="visible">True</property>
|
||||
|
@ -364,6 +373,28 @@
|
|||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="mitem_adif">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">ADIF</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu" id="subm_adif">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImageMenuItem" id="mitem_update_modes">
|
||||
<property name="label" translatable="yes">Update Modes</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">image25</property>
|
||||
<property name="use_stock">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="mitem_view">
|
||||
<property name="visible">True</property>
|
||||
|
@ -927,6 +958,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.</pro
|
|||
<property name="authors">Christian Thomas Jacobs, M0UOS</property>
|
||||
<property name="logo">log_64x64.png</property>
|
||||
<property name="license_type">custom</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="about_dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -956,6 +990,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.</pro
|
|||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="transient_for">pyqso</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="cabrillo_export_dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -1098,6 +1135,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.</pro
|
|||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="transient_for">pyqso</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="log_name_dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -1195,6 +1235,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.</pro
|
|||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="transient_for">pyqso</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="preferences_dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -2505,7 +2548,7 @@ Base64-encoded plain text in the configuration file.</property>
|
|||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="preferences_adif_import">
|
||||
<object class="GtkFrame" id="preferences_adif">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
|
@ -2522,7 +2565,7 @@ Base64-encoded plain text in the configuration file.</property>
|
|||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child>
|
||||
<object class="GtkCheckButton" id="adif_import_merge_comment_checkbutton">
|
||||
<object class="GtkCheckButton" id="adif_merge_comment_checkbutton">
|
||||
<property name="label" translatable="yes">Merge any text in the COMMENT field with the NOTES field</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
|
@ -2541,10 +2584,10 @@ Base64-encoded plain text in the configuration file.</property>
|
|||
</object>
|
||||
</child>
|
||||
<child type="label">
|
||||
<object class="GtkLabel" id="preferences_adif_import_label">
|
||||
<object class="GtkLabel" id="preferences_adif_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">ADIF Import</property>
|
||||
<property name="label" translatable="yes">ADIF</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -3029,6 +3072,9 @@ Base64-encoded plain text in the configuration file.</property>
|
|||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="transient_for">pyqso</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="record_dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -4343,6 +4389,9 @@ Base64-encoded plain text in the configuration file.</property>
|
|||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="transient_for">record_dialog</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="calendar_dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -4415,6 +4464,9 @@ Base64-encoded plain text in the configuration file.</property>
|
|||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="transient_for">pyqso</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox" id="telnet_connection_dialog_vbox">
|
||||
<property name="can_focus">False</property>
|
||||
|
@ -4658,6 +4710,106 @@ Base64-encoded plain text in the configuration file.</property>
|
|||
<action-widget response="-5">telnet_connection_ok_button</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
<object class="GtkDialog" id="update_modes_dialog">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Update ADIF Modes</property>
|
||||
<property name="destroy_with_parent">True</property>
|
||||
<property name="type_hint">dialog</property>
|
||||
<property name="transient_for">pyqso</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child internal-child="vbox">
|
||||
<object class="GtkBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">2</property>
|
||||
<child internal-child="action_area">
|
||||
<object class="GtkButtonBox">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="layout_style">end</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="update_modes_cancel_button">
|
||||
<property name="label">gtk-cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="update_modes_ok_button">
|
||||
<property name="label">gtk-ok</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="use_stock">True</property>
|
||||
<property name="always_show_image">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="adif_url_hbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="adif_url_label">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">URL of ADIF webpage</property>
|
||||
<property name="width_chars">12</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="padding">2</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEntry" id="adif_url_entry">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<action-widgets>
|
||||
<action-widget response="-6">update_modes_cancel_button</action-widget>
|
||||
<action-widget response="-5">update_modes_ok_button</action-widget>
|
||||
</action-widgets>
|
||||
</object>
|
||||
<object class="GtkMenu" id="popup">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2019 Christian Thomas Jacobs.
|
||||
|
||||
# This file is part of PyQSO.
|
||||
|
||||
# PyQSO is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# PyQSO is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with PyQSO. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
|
||||
|
||||
class UpdateModesDialog:
|
||||
|
||||
""" A handler for the Gtk.Dialog through which a user can specify a URL to an ADIF specification. """
|
||||
|
||||
def __init__(self, application):
|
||||
""" Create and show the dialog to the user.
|
||||
|
||||
:arg application: The PyQSO application containing the main Gtk window, etc.
|
||||
"""
|
||||
|
||||
self.builder = application.builder
|
||||
glade_file_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), "res", "pyqso.glade")
|
||||
self.builder.add_objects_from_file(glade_file_path, ("update_modes_dialog",))
|
||||
self.dialog = self.builder.get_object("update_modes_dialog")
|
||||
self.sources = {"URL": self.builder.get_object("adif_url_entry")}
|
||||
|
||||
self.dialog.show_all()
|
||||
|
||||
return
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
""" Return the URL of the ADIF specification's webpage.
|
||||
|
||||
:returns: The URL of the ADIF specification's webpage.
|
||||
:rtype: str
|
||||
"""
|
||||
return self.sources["URL"].get_text()
|
||||
|
Ładowanie…
Reference in New Issue