[hamradio-commits] [pyqso] 02/09: Imported Upstream version 0.1+git2015020701

Iain Learmonth irl-guest at moszumanska.debian.org
Sat Feb 7 23:49:40 UTC 2015


This is an automated email from the git hooks/post-receive script.

irl-guest pushed a commit to branch master
in repository pyqso.

commit e2da7ee2444f0520d418dcad2619de28eba4fbe9
Author: Iain R. Learmonth <irl at fsfe.org>
Date:   Sat Feb 7 23:23:44 2015 +0000

    Imported Upstream version 0.1+git2015020701
---
 .gitignore                  |   1 +
 bin/pyqso                   |  39 +++++----
 doc/manual.tex              |   2 +-
 pyqso/adif.py               |  25 ++++--
 pyqso/callsign_lookup.py    |  42 ++++++++-
 pyqso/grey_line.py          |   1 +
 pyqso/logbook.py            |  83 +++++++++++-------
 pyqso/menu.py               |  21 +++--
 pyqso/preferences_dialog.py | 201 ++++++++++++++++++++++++++++++++++----------
 pyqso/record_dialog.py      | 143 ++++++++++++++++++++++++-------
 pyqso/toolbar.py            |  15 +++-
 setup.py                    |   2 +-
 12 files changed, 434 insertions(+), 141 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0d20b64
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/bin/pyqso b/bin/pyqso
index 599a02e..c412fb9 100755
--- a/bin/pyqso
+++ b/bin/pyqso
@@ -19,8 +19,7 @@
 #    along with PyQSO.  If not, see <http://www.gnu.org/licenses/>.
 
 from gi.repository import Gtk, GObject, Gdk, GdkPixbuf
-import logging
-import optparse
+import argparse
 import ConfigParser
 import os
 import os.path
@@ -32,6 +31,9 @@ import signal
 pyqso_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), os.pardir)
 sys.path.insert(0, pyqso_path)
 
+import logging
+logging.basicConfig(level=logging.INFO)
+      
 # PyQSO modules
 from pyqso.adif import *
 from pyqso.logbook import *
@@ -47,7 +49,7 @@ class PyQSO(Gtk.Window):
       """ Set up the main (root) window, start the event loop, and open a logbook (if the logbook's path is specified by the user in the command line). """
          
       # Call the constructor of the super class (Gtk.Window)
-      Gtk.Window.__init__(self, title="PyQSO 0.1")
+      Gtk.Window.__init__(self, title="PyQSO 0.2a-dev")
 
       # Get any application-specific preferences from the configuration file      
       config = ConfigParser.ConfigParser()
@@ -113,7 +115,7 @@ class PyQSO(Gtk.Window):
       about.set_modal(True)
       about.set_transient_for(parent=self)
       about.set_program_name("PyQSO")
-      about.set_version("0.1")
+      about.set_version("0.2a-dev")
       about.set_authors(["Christian Jacobs"])
       about.set_license("""This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -149,18 +151,23 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.""")
 
 if(__name__ == "__main__"):
    # Get any command line arguments
-   parser = optparse.OptionParser()
-   parser.add_option("-d", "--debug", action="store_true", default=False, help="Enable debugging. All debugging messages will be written to pyqso.debug.")
-   parser.add_option("-l", "--logbook", action="store", type="string", dest="logbook", default=None, help="Path to the Logbook file.")
-   (options, args) = parser.parse_args()
-
-   # Set up debugging output
-   if(options.debug):
-      logging.basicConfig(level=logging.DEBUG, filename="pyqso.debug", 
-                        format="%(asctime)s %(levelname)s: %(message)s", 
-                        datefmt="%Y-%m-%d %H:%M:%S")
-
+   parser = argparse.ArgumentParser()
+   parser.add_argument("-d", "--debug", action="store_true", default=False, help="Enable debugging. All debugging messages will be written to pyqso.debug.")
+   parser.add_argument("-l", "--logbook", action="store", type=str, metavar="/path/to/my_logbook_file.db", default=None, help="Path to a Logbook file. If this file does not already exist, then it will be created.")
+   args = parser.parse_args()
+
+   # Output debugging messages to a file
+   if(args.debug):
+      # Get the root logger
+      logger = logging.getLogger()
+      logger.setLevel(logging.DEBUG)
+      # Add a file handler
+      handler = logging.FileHandler("pyqso.debug", mode="w")
+      formatter = logging.Formatter(fmt="%(asctime)s %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
+      handler.setFormatter(formatter)
+      logger.addHandler(handler)
+      
    signal.signal(signal.SIGINT, signal.SIG_DFL) # Exit PyQSO if a SIGINT signal is captured.
-   application = PyQSO(options.logbook) # Populate the main window and show it
+   application = PyQSO(args.logbook) # Populate the main window and show it
    Gtk.main() # Start up the event loop!
 
diff --git a/doc/manual.tex b/doc/manual.tex
index e7fca5e..e31f1c3 100644
--- a/doc/manual.tex
+++ b/doc/manual.tex
@@ -34,7 +34,7 @@
 \begin{center}
 \vspace*{5cm}
 \huge{PyQSO User Manual}\\\vspace*{5cm}
-\LARGE{Version 0.1}
+\LARGE{Version 0.2a-dev}
 \end{center}
 \end{titlepage}
 
diff --git a/pyqso/adif.py b/pyqso/adif.py
index b200338..151df5a 100644
--- a/pyqso/adif.py
+++ b/pyqso/adif.py
@@ -54,7 +54,7 @@ AVAILABLE_FIELD_NAMES_ORDERED = ["CALL", "QSO_DATE", "TIME_ON", "FREQ", "BAND",
 AVAILABLE_FIELD_NAMES_FRIENDLY = {"CALL":"Callsign",
                                   "QSO_DATE":"Date",
                                   "TIME_ON":"Time",
-                                  "FREQ":"Frequency (MHz)",
+                                  "FREQ":"Frequency",
                                   "BAND":"Band",
                                   "MODE":"Mode",
                                   "TX_PWR":"TX Power (W)",
@@ -84,6 +84,14 @@ AVAILABLE_FIELD_NAMES_FRIENDLY = {"CALL":"Callsign",
 # L: Location
 DATA_TYPES = ["A", "B", "N", "S", "I", "D", "T", "M", "G", "L", "E"]
 
+# All the modes listed in the ADIF specification
+MODES = ["", "AM", "AMTORFEC", "ASCI", "ATV", "CHIP64", "CHIP128", "CLO", "CONTESTI", "CW", "DSTAR", "DOMINO", "DOMINOF", "FAX", "FM", "FMHELL", "FSK31", "FSK441", "GTOR", "HELL", "HELL80", "HFSK", "ISCAT", "JT44", "JT4A", "JT4B", "JT4C", "JT4D", "JT4E", "JT4F", "JT4G", "JT65", "JT65A", "JT65B", "JT65C", "JT6M", "MFSK8", "MFSK16", "MT63", "OLIVIA", "PAC", "PAC2", "PAC3", "PAX", "PAX2", "PCW", "PKT", "PSK10", "PSK31", "PSK63", "PSK63F", "PSK125", "PSKAM10", "PSKAM31", "PSKAM50", "PSKFEC31 [...]
+
+# All the bands listed in the ADIF specification.
+BANDS = ["", "2190m", "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.
+BANDS_RANGES = [(None, None), (0.136, 0.137), (0.501, 0.504), (1.8, 2.0), (3.5, 4.0), (5.102, 5.404), (7.0, 7.3), (10.0, 10.15), (14.0, 14.35), (18.068, 18.168), (21.0, 21.45), (24.890, 24.99), (28.0, 29.7), (50.0, 54.0), (70.0, 71.0), (144.0, 148.0), (222.0, 225.0), (420.0, 450.0), (902.0, 928.0), (1240.0, 1300.0), (2300.0, 2450.0), (3300.0, 3500.0), (5650.0, 5925.0), (10000.0, 10500.0), (24000.0, 24250.0), (47000.0, 47200.0), (75500.0, 81000.0), (119980.0, 120020.0), (142000.0, 149000. [...]
+
 ADIF_VERSION = "1.0"
 
 class ADIF:
@@ -170,6 +178,9 @@ class ADIF:
                   field_data = field_data.lower()
                elif(field_name == "MODE"):
                   field_data = field_data.upper()
+               elif(field_name == "CALL"):
+                  # Also force all the callsigns to be in upper case.
+                  field_data = field_data.upper()
 
                if(field_name in AVAILABLE_FIELD_NAMES_ORDERED):
                   field_data_type = AVAILABLE_FIELD_NAMES_TYPES[field_name]
@@ -201,7 +212,7 @@ class ADIF:
          
 <adif_ver:%d>%s
 <programid:5>PyQSO
-<programversion:3>0.1
+<programversion:8>0.2a-dev
 <eoh>\n""" % (dt, len(records), len(str(ADIF_VERSION)), ADIF_VERSION))
          
          # Then write each log to the file.
@@ -373,11 +384,9 @@ class ADIF:
       elif(data_type == "E" or data_type == "A"):
          # Enumeration, AwardList.
          if(field_name == "MODE"):
-            modes = ["", "AM", "AMTORFEC", "ASCI", "ATV", "CHIP64", "CHIP128", "CLO", "CONTESTI", "CW", "DSTAR", "DOMINO", "DOMINOF", "FAX", "FM", "FMHELL", "FSK31", "FSK441", "GTOR", "HELL", "HELL80", "HFSK", "ISCAT", "JT44", "JT4A", "JT4B", "JT4C", "JT4D", "JT4E", "JT4F", "JT4G", "JT65", "JT65A", "JT65B", "JT65C", "JT6M", "MFSK8", "MFSK16", "MT63", "OLIVIA", "PAC", "PAC2", "PAC3", "PAX", "PAX2", "PCW", "PKT", "PSK10", "PSK31", "PSK63", "PSK63F", "PSK125", "PSKAM10", "PSKAM31", "PSKAM50 [...]
-            return (data in modes)
+            return (data in MODES)
          elif(field_name == "BAND"):
-            bands = ["", "2190m", "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"]
-            return (data in bands)
+            return (data in BANDS)
          else:
             return True
 
@@ -417,7 +426,7 @@ class TestADIF(unittest.TestCase):
       assert("""        
 <adif_ver:3>1.0
 <programid:5>PyQSO
-<programversion:3>0.1
+<programversion:8>0.2a-dev
 <eoh>
 <call:7>TEST123
 <qso_date:8>20120402
@@ -456,7 +465,7 @@ class TestADIF(unittest.TestCase):
       assert("""        
 <adif_ver:3>1.0
 <programid:5>PyQSO
-<programversion:3>0.1
+<programversion:8>0.2a-dev
 <eoh>
 <call:7>TEST123
 <qso_date:8>20120402
diff --git a/pyqso/callsign_lookup.py b/pyqso/callsign_lookup.py
index 833003d..13986b2 100644
--- a/pyqso/callsign_lookup.py
+++ b/pyqso/callsign_lookup.py
@@ -62,10 +62,19 @@ class CallsignLookup():
 
       return connected
 
-   def lookup(self, callsign):
+   def lookup(self, full_callsign, ignore_prefix_suffix = True):
       """ Parse the XML tree that is returned from the qrz.com XML server to obtain the NAME, ADDRESS, STATE, COUNTRY, DXCC, CQZ, ITUZ, and IOTA field data (if present),
       and return the data in the dictionary called fields_and_data. """
-      logging.debug("Performing a callsign lookup...")
+      
+      logging.debug("Looking up callsign. The full callsign (with a prefix and/or suffix) is %s" % full_callsign)
+      
+      # Remove any prefix or suffix from the callsign before performing the lookup.
+      if(ignore_prefix_suffix):
+         callsign = self.strip(full_callsign)
+      else:
+         callsign = full_callsign
+                          
+      # Commence lookup.
       fields_and_data = {"NAME":"", "ADDRESS":"", "STATE":"", "COUNTRY":"", "DXCC":"", "CQZ":"", "ITUZ":"", "IOTA":""}
       if(self.session_key):
          request = '/xml/current/?s=%s;callsign=%s' % (self.session_key, callsign)
@@ -126,3 +135,32 @@ class CallsignLookup():
          logging.debug("Callsign lookup complete. Returning data...")
       return fields_and_data
 
+   def strip(self, full_callsign):
+      components = full_callsign.split("/") # We assume that prefixes or suffixes come before/after a forward slash character "/".
+      suffixes = ["P", "M", "A", "PM", "MM", "AM", "QRP"]
+      try:
+         if(len(components) == 3):
+            # We have both a prefix and a suffix.
+            callsign = components[1]
+            
+         elif(len(components) == 2):
+            if(components[1].upper() in suffixes or components[1].lower() in suffixes):
+               # If the last part of the full_callsign is a valid suffix, then use the part before that.
+               callsign = components[0]
+               logging.debug("Suffix %s found. Callsign to lookup is %s" % (components[1], callsign))
+            else:
+               # We have a prefix, so take the part after the first "/".
+               callsign = components[1]
+               logging.debug("Prefix %s found. Callsign to lookup is %s" % (components[0], callsign))
+               
+         elif(len(components) == 1):
+            # We have neither a prefix nor a suffix, so use the full_callsign.
+            callsign = full_callsign
+            logging.debug("No prefix or suffix found. Callsign to lookup is %s" % callsign)
+            
+         else:
+            raise ValueError
+      except ValueError:
+         callsign = full_callsign
+      return callsign
+      
diff --git a/pyqso/grey_line.py b/pyqso/grey_line.py
index 5aff836..b56cd25 100644
--- a/pyqso/grey_line.py
+++ b/pyqso/grey_line.py
@@ -24,6 +24,7 @@ from datetime import datetime
 try:
    import numpy
    import matplotlib
+   matplotlib.use('Agg')
    matplotlib.rcParams['font.size'] = 10.0
    from mpl_toolkits.basemap import Basemap
    from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg as FigureCanvas
diff --git a/pyqso/logbook.py b/pyqso/logbook.py
index 705f00d..31c67ce 100644
--- a/pyqso/logbook.py
+++ b/pyqso/logbook.py
@@ -21,7 +21,7 @@
 from gi.repository import Gtk, GObject, Pango, PangoCairo
 import logging
 import sqlite3 as sqlite
-from os.path import basename, getctime, getmtime, expanduser
+from os.path import basename, getctime, getmtime, expanduser, exists
 import datetime
 import ConfigParser
 
@@ -44,15 +44,61 @@ class Logbook(Gtk.Notebook):
       logging.debug("New Logbook instance created!")
       return
    
+   def new(self, widget=None):
+      """ Create a new logbook, and open it. """
+      
+      # Get the new file's path from a dialog.
+      dialog = Gtk.FileChooserDialog("Create a New SQLite Database File",
+                                 None,
+                                 Gtk.FileChooserAction.SAVE,
+                                 (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+                                 Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
+      dialog.set_do_overwrite_confirmation(True)
+
+      response = dialog.run()
+      if(response == Gtk.ResponseType.OK):
+         path = dialog.get_filename()
+      else:
+         path = None
+      dialog.destroy()
+
+      if(path is None): # If the Cancel button has been clicked, path will still be None
+         logging.debug("No file path specified.")
+         return
+      else:
+         # Clear the contents of the file, in case the file exists already.
+         open(path, 'w').close()
+         # Open the new logbook, ready for use.
+         self.open(path=path)
+   
    def open(self, widget=None, path=None):
       """ Open a logbook, and render all the logs within it. 
       An optional 'path' argument can be specified if the database file location is known.
       Otherwise, a file selection dialog will appear. """
 
-      connected = self.db_connect(path)
+      if(path is None):
+         # If no path has been provided, get one from a "File Open" dialog.
+         dialog = Gtk.FileChooserDialog("Open SQLite Database File",
+                                    None,
+                                    Gtk.FileChooserAction.OPEN,
+                                    (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
+                                    Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
+         
+         response = dialog.run()
+         if(response == Gtk.ResponseType.OK):
+            path = dialog.get_filename()
+         dialog.destroy()
+         
+         if(path is None): # If the Cancel button has been clicked, path will still be None
+            logging.debug("No file path specified.")
+            return
+         
+      connected = self.db_connect(path=path)
       if(connected):
          # If the connection setup was successful, then open all the logs in the database
-
+         
+         self.path = path
+         
          logging.debug("Trying to retrieve all the logs in the logbook...")
          self.logs = [] # A fresh stack of Log objects
          try:
@@ -131,32 +177,10 @@ class Logbook(Gtk.Notebook):
       """ Create an SQL database connection to the Logbook's data source """
 
       logging.debug("Attempting to connect to the logbook database...")
-      if(path is None):
-         # If no path has been provided, get one from a "File Open" dialog.
-         dialog = Gtk.FileChooserDialog("Open SQLite Database File",
-                                    None,
-                                    Gtk.FileChooserAction.OPEN,
-                                    (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
-                                    Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
-         
-         response = dialog.run()
-         if(response == Gtk.ResponseType.OK):
-            path = dialog.get_filename()
-         dialog.destroy()
-         
-         if(path is None): # If the Cancel button has been clicked, path will still be None
-            logging.debug("No file path specified.")
-            return False
-         else:
-            self.path = path
-      else:
-         # Use existing user input
-         self.path = path
-
       # Try setting up the SQL database connection
       try:
          self.db_disconnect() # Destroy any existing connections first.
-         self.connection = sqlite.connect(self.path)
+         self.connection = sqlite.connect(path)
          self.connection.row_factory = sqlite.Row
       except sqlite.Error as e:
          # PyQSO can't connect to the database.
@@ -182,7 +206,7 @@ class Logbook(Gtk.Notebook):
 
    def _create_dummy_page(self):
       """ Create a blank page in the Gtk.Notebook for the "+" (New Log) tab. """
-      blank_treeview = Gtk.TreeView([])
+      blank_treeview = Gtk.TreeView()
       # Allow the Log to be scrolled up/down
       sw = Gtk.ScrolledWindow()
       sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
@@ -439,8 +463,9 @@ class Logbook(Gtk.Notebook):
 
          config = ConfigParser.ConfigParser()
          have_config = (config.read(expanduser('~/.pyqso.ini')) != [])
-         if(have_config):
-            column.set_visible(config.get("view", AVAILABLE_FIELD_NAMES_ORDERED[i].lower()) == "True")
+         (section, option) = ("view", AVAILABLE_FIELD_NAMES_ORDERED[i].lower())
+         if(have_config and config.has_option(section, option)):
+            column.set_visible(config.get(section, option) == "True")
          self.treeview[index].append_column(column)
 
       self.show_all()
diff --git a/pyqso/menu.py b/pyqso/menu.py
index fb1e297..4750d5b 100644
--- a/pyqso/menu.py
+++ b/pyqso/menu.py
@@ -42,9 +42,18 @@ class Menu(Gtk.MenuBar):
       self.append(mitem_logbook)  
       subm_logbook = Gtk.Menu()
       mitem_logbook.set_submenu(subm_logbook)
-    
-      # Create/open logbook
-      mitem_connect = Gtk.ImageMenuItem("Open New or Existing Logbook...")
+
+      # Create logbook
+      mitem_connect = Gtk.ImageMenuItem("Create a New Logbook...")
+      icon = Gtk.Image()
+      icon.set_from_stock(Gtk.STOCK_NEW, Gtk.IconSize.MENU)
+      mitem_connect.set_image(icon)
+      mitem_connect.connect("activate", parent.logbook.new)
+      subm_logbook.append(mitem_connect)
+      self.items["NEW_LOGBOOK"] = mitem_connect
+          
+      # Open logbook
+      mitem_connect = Gtk.ImageMenuItem("Open an Existing Logbook...")
       icon = Gtk.Image()
       icon.set_from_stock(Gtk.STOCK_OPEN, Gtk.IconSize.MENU)
       mitem_connect.set_image(icon)
@@ -197,8 +206,9 @@ class Menu(Gtk.MenuBar):
       mitem_toolbox = Gtk.CheckMenuItem("Toolbox")
       config = ConfigParser.ConfigParser()
       have_config = (config.read(os.path.expanduser('~/.pyqso.ini')) != [])
-      if(have_config):
-         mitem_toolbox.set_active(config.get("general", "show_toolbox") == "True")
+      (section, option) = ("general", "show_toolbox")
+      if(have_config and config.has_option(section, option)):
+         mitem_toolbox.set_active(config.get(section, option) == "True")
       else:
          mitem_toolbox.set_active(False) # Don't show the toolbox by default
       mitem_toolbox.connect("activate", parent.toolbox.toggle_visible_callback)
@@ -238,6 +248,7 @@ class Menu(Gtk.MenuBar):
       
    def set_logbook_item_sensitive(self, sensitive):
       logging.debug("Setting the 'Create/Open Logbook' menu item's sensitivity to: %s..." % sensitive) 
+      self.items["NEW_LOGBOOK"].set_sensitive(sensitive)
       self.items["OPEN_LOGBOOK"].set_sensitive(sensitive)
       self.items["CLOSE_LOGBOOK"].set_sensitive(not sensitive)
       logging.debug("Set the 'Create/Open Logbook' menu item's sensitivity to: %s." % sensitive) 
diff --git a/pyqso/preferences_dialog.py b/pyqso/preferences_dialog.py
index a01c997..7d32404 100644
--- a/pyqso/preferences_dialog.py
+++ b/pyqso/preferences_dialog.py
@@ -32,6 +32,7 @@ except ImportError:
    have_hamlib = False
 
 from pyqso.adif import AVAILABLE_FIELD_NAMES_FRIENDLY, AVAILABLE_FIELD_NAMES_ORDERED
+FREQUENCY_UNITS = ["Hz", "kHz", "MHz", "GHz"]
 
 class PreferencesDialog(Gtk.Dialog):
    
@@ -51,6 +52,9 @@ class PreferencesDialog(Gtk.Dialog):
       self.hamlib = HamlibPage()
       self.preferences.insert_page(self.hamlib, Gtk.Label("Hamlib"), 2)
 
+      self.records = RecordsPage()
+      self.preferences.insert_page(self.records, Gtk.Label("Records"), 2)
+
       self.vbox.pack_start(self.preferences, True, True, 2)
       self.show_all()
 
@@ -64,6 +68,7 @@ class PreferencesDialog(Gtk.Dialog):
       general_data = self.general.get_data()
       view_data = self.view.get_data()
       hamlib_data = self.hamlib.get_data()
+      records_data = self.records.get_data()
 
       config = ConfigParser.ConfigParser()
 
@@ -82,6 +87,11 @@ class PreferencesDialog(Gtk.Dialog):
       for key in hamlib_data.keys():
          config.set("hamlib", key.lower(), hamlib_data[key])
       
+      # Records
+      config.add_section("records")
+      for key in records_data.keys():
+         config.set("records", key.lower(), records_data[key])
+
       with open(os.path.expanduser('~/.pyqso.ini'), 'w') as f:
          config.write(f)
 
@@ -105,47 +115,15 @@ class GeneralPage(Gtk.VBox):
       frame.set_label("Startup")
       hbox = Gtk.HBox()
       self.sources["SHOW_TOOLBOX"] = Gtk.CheckButton("Show toolbox by default")
-      if(have_config):
-         self.sources["SHOW_TOOLBOX"].set_active(config.get("general", "show_toolbox") == "True")
+      (section, option) = ("general", "show_toolbox")
+      if(have_config and config.has_option(section, option)):
+         self.sources["SHOW_TOOLBOX"].set_active(config.get(section, option) == "True")
       else:
          self.sources["SHOW_TOOLBOX"].set_active(False)
       hbox.pack_start(self.sources["SHOW_TOOLBOX"], False, False, 2)
       frame.add(hbox)
       self.pack_start(frame, False, False, 2)
 
-      frame = Gtk.Frame()
-      frame.set_label("Login details (qrz.com)")
-      inner_vbox = Gtk.VBox()
-
-      hbox = Gtk.HBox()
-      label = Gtk.Label("Username: ")
-      label.set_width_chars(9)
-      label.set_alignment(0, 0.5)
-      hbox.pack_start(label, False, False, 2)
-      self.sources["QRZ_USERNAME"] = Gtk.Entry()
-      if(have_config):
-         self.sources["QRZ_USERNAME"].set_text(config.get("general", "qrz_username"))
-      hbox.pack_start(self.sources["QRZ_USERNAME"], False, False, 2)
-      inner_vbox.pack_start(hbox, False, False, 2)
-
-      hbox = Gtk.HBox()
-      label = Gtk.Label("Password: ")
-      label.set_width_chars(9)
-      label.set_alignment(0, 0.5)
-      hbox.pack_start(label, False, False, 2)
-      self.sources["QRZ_PASSWORD"] = Gtk.Entry()
-      self.sources["QRZ_PASSWORD"].set_visibility(False) # Mask the password with the "*" character.
-      if(have_config):
-         self.sources["QRZ_PASSWORD"].set_text(base64.b64decode(config.get("general", "qrz_password")))
-      hbox.pack_start(self.sources["QRZ_PASSWORD"], False, False, 2)
-      inner_vbox.pack_start(hbox, False, False, 2)
-
-      label = Gtk.Label("Warning: Login details are currently stored as\nBase64-encoded plain text in the configuration file.")
-      inner_vbox.pack_start(label, False, False, 2)
-
-      frame.add(inner_vbox)
-      self.pack_start(frame, False, False, 2)
-
       logging.debug("General page of the preferences dialog ready!")
       return
 
@@ -153,8 +131,6 @@ class GeneralPage(Gtk.VBox):
       logging.debug("Retrieving data from the General page of the preferences dialog...")
       data = {}
       data["SHOW_TOOLBOX"] = self.sources["SHOW_TOOLBOX"].get_active()
-      data["QRZ_USERNAME"] = self.sources["QRZ_USERNAME"].get_text()
-      data["QRZ_PASSWORD"] = base64.b64encode(self.sources["QRZ_PASSWORD"].get_text())
       return data
 
 class ViewPage(Gtk.VBox):
@@ -169,8 +145,12 @@ class ViewPage(Gtk.VBox):
 
       self.sources = {}
 
+      # Visible fields frame
+      frame = Gtk.Frame()
+      frame.set_label("Visible fields")
+
       # Divide the list of available field names up into multiple columns (of maximum length 'max_buttons_per_column')
-      # so we don't make the Preferences dialog too long. 
+      # so we don't make the Preferences dialog too long.      
       hbox = Gtk.HBox(spacing=2)
       max_buttons_per_column = 6
       number_of_columns = int( len(AVAILABLE_FIELD_NAMES_ORDERED)/max_buttons_per_column ) + 1 # Number of check buttons per column
@@ -181,14 +161,40 @@ class ViewPage(Gtk.VBox):
                break
             field_name = AVAILABLE_FIELD_NAMES_ORDERED[i*max_buttons_per_column + j]
             button = Gtk.CheckButton(AVAILABLE_FIELD_NAMES_FRIENDLY[field_name ])
-            if(have_config):
+            if(have_config and config.has_option("view", field_name.lower())):
                button.set_active(config.get("view", field_name.lower()) == "True")
             else:
                button.set_active(True)
             self.sources[field_name] = button
             vbox.pack_start(button, False, False, 2)
          hbox.pack_start(vbox, False, False, 2)
-      self.pack_start(hbox, False, False, 2)
+      frame.add(hbox)
+      self.pack_start(frame, False, False, 2)
+
+      # Default values
+      frame = Gtk.Frame()
+      frame.set_label("Default values")
+      vbox = Gtk.VBox()
+      
+      hbox_temp = Gtk.HBox()
+      label = Gtk.Label("Default unit of frequency: ")
+      label.set_width_chars(17)
+      label.set_alignment(0, 0.5)
+      hbox_temp.pack_start(label, False, False, 2)
+      
+      self.sources["DEFAULT_FREQ_UNIT"] = Gtk.ComboBoxText()
+      for unit in FREQUENCY_UNITS:
+         self.sources["DEFAULT_FREQ_UNIT"].append_text(unit)
+      (section, option) = ("view", "default_freq_unit")
+      if(have_config and config.has_option(section, option)):
+         self.sources["DEFAULT_FREQ_UNIT"].set_active(FREQUENCY_UNITS.index(config.get(section, option)))
+      else:
+         self.sources["DEFAULT_FREQ_UNIT"].set_active(FREQUENCY_UNITS.index("MHz")) # Set to MHz as the default option.
+      hbox_temp.pack_start(self.sources["DEFAULT_FREQ_UNIT"], False, False, 2)
+      vbox.pack_start(hbox_temp, False, False, 2)
+      
+      frame.add(vbox)
+      self.pack_start(frame, False, False, 2)
 
       self.label = Gtk.Label("Note: View-related changes will not take effect\nuntil PyQSO is restarted.")
       self.pack_start(self.label, False, False, 2)
@@ -201,6 +207,7 @@ class ViewPage(Gtk.VBox):
       data = {}
       for field_name in AVAILABLE_FIELD_NAMES_ORDERED:
          data[field_name] = self.sources[field_name].get_active()
+      data["DEFAULT_FREQ_UNIT"] = self.sources["DEFAULT_FREQ_UNIT"].get_active_text()
       return data
 
 class HamlibPage(Gtk.VBox):
@@ -221,8 +228,9 @@ class HamlibPage(Gtk.VBox):
       vbox_inner = Gtk.VBox(spacing=2)
 
       self.sources["AUTOFILL"] = Gtk.CheckButton("Auto-fill Frequency field")
-      if(have_config):
-         self.sources["AUTOFILL"].set_active(config.get("hamlib", "autofill") == "True")
+      (section, option) = ("hamlib", "autofill")
+      if(have_config and config.has_option(section, option)):
+         self.sources["AUTOFILL"].set_active(config.get(section, option) == "True")
       else:
          self.sources["AUTOFILL"].set_active(False)
       vbox_inner.pack_start(self.sources["AUTOFILL"], False, False, 2)
@@ -248,7 +256,8 @@ class HamlibPage(Gtk.VBox):
       self.sources["RIG_MODEL"] = Gtk.ComboBoxText()
       for model in models:
          self.sources["RIG_MODEL"].append_text(model)
-      if(have_config):
+      (section, option) = ("hamlib", "rig_model")
+      if(have_config and config.has_option("hamlib", "rig_model")):
          self.sources["RIG_MODEL"].set_active(models.index(config.get("hamlib", "rig_model")))
       else:
          self.sources["RIG_MODEL"].set_active(models.index("RIG_MODEL_NONE")) # Set to RIG_MODEL_NONE as the default option.
@@ -262,8 +271,9 @@ class HamlibPage(Gtk.VBox):
       label.set_alignment(0, 0.5)
       hbox_temp.pack_start(label, False, False, 2)
       self.sources["RIG_PATHNAME"] = Gtk.Entry()
-      if(have_config):
-         self.sources["RIG_PATHNAME"].set_text(config.get("hamlib", "rig_pathname"))
+      (section, option) = ("hamlib", "rig_pathname")
+      if(have_config and config.has_option(section, option)):
+         self.sources["RIG_PATHNAME"].set_text(config.get(section, option))
       hbox_temp.pack_start(self.sources["RIG_PATHNAME"], True, True, 2)
       vbox_inner.pack_start(hbox_temp, False, False, 2)
 
@@ -281,3 +291,104 @@ class HamlibPage(Gtk.VBox):
       data["RIG_MODEL"] = self.sources["RIG_MODEL"].get_active_text()
       return data
 
+class RecordsPage(Gtk.VBox):
+   
+   def __init__(self):
+      logging.debug("Setting up the Records page of the preferences dialog...")
+
+      Gtk.VBox.__init__(self, spacing=2)
+
+      # Remember that the have_config conditional in the PyQSO class may be out-of-date the next time the user opens up the preferences dialog
+      # because a configuration file may have been created after launching the application. Let's check to see if one exists again...
+      config = ConfigParser.ConfigParser()
+      have_config = (config.read(os.path.expanduser('~/.pyqso.ini')) != [])
+
+      self.sources = {}
+
+      # Autocomplete frame
+      frame = Gtk.Frame()
+      frame.set_label("Autocomplete")
+      vbox = Gtk.VBox()
+      self.sources["AUTOCOMPLETE_BAND"] = Gtk.CheckButton("Autocomplete the Band field")
+      (section, option) = ("records", "autocomplete_band")
+      if(have_config and config.has_option(section, option)):
+         self.sources["AUTOCOMPLETE_BAND"].set_active(config.get(section, option) == "True")
+      else:
+         self.sources["AUTOCOMPLETE_BAND"].set_active(True)
+      vbox.pack_start(self.sources["AUTOCOMPLETE_BAND"], False, False, 2)
+
+      self.sources["USE_UTC"] = Gtk.CheckButton("Use UTC when autocompleting the Date and Time")
+      (section, option) = ("records", "use_utc")
+      if(have_config and config.has_option(section, option)):
+         self.sources["USE_UTC"].set_active(config.get(section, option) == "True")
+      else:
+         self.sources["USE_UTC"].set_active(True)
+      vbox.pack_start(self.sources["USE_UTC"], False, False, 2)
+
+      frame.add(vbox)
+      self.pack_start(frame, False, False, 2)
+
+      # Callsign lookup frame
+      frame = Gtk.Frame()
+      frame.set_label("Callsign lookup")
+      vbox = Gtk.VBox()
+
+      subframe = Gtk.Frame()
+      subframe.set_label("Login details (qrz.com)")
+      inner_vbox = Gtk.VBox()
+
+      hbox = Gtk.HBox()
+      label = Gtk.Label("Username: ")
+      label.set_width_chars(9)
+      label.set_alignment(0, 0.5)
+      hbox.pack_start(label, False, False, 2)
+      self.sources["QRZ_USERNAME"] = Gtk.Entry()
+      (section, option) = ("records", "qrz_username")
+      if(have_config and config.has_option(section, option)):
+         self.sources["QRZ_USERNAME"].set_text(config.get(section, option))
+      hbox.pack_start(self.sources["QRZ_USERNAME"], False, False, 2)
+      inner_vbox.pack_start(hbox, False, False, 2)
+
+      hbox = Gtk.HBox()
+      label = Gtk.Label("Password: ")
+      label.set_width_chars(9)
+      label.set_alignment(0, 0.5)
+      hbox.pack_start(label, False, False, 2)
+      self.sources["QRZ_PASSWORD"] = Gtk.Entry()
+      self.sources["QRZ_PASSWORD"].set_visibility(False) # Mask the password with the "*" character.
+      (section, option) = ("records", "qrz_password")
+      if(have_config and config.has_option(section, option)):
+         self.sources["QRZ_PASSWORD"].set_text(base64.b64decode(config.get(section, option)))
+      hbox.pack_start(self.sources["QRZ_PASSWORD"], False, False, 2)
+      inner_vbox.pack_start(hbox, False, False, 2)
+
+      label = Gtk.Label("Warning: Login details are currently stored as\nBase64-encoded plain text in the configuration file.")
+      inner_vbox.pack_start(label, False, False, 2)
+
+      subframe.add(inner_vbox)
+      vbox.pack_start(subframe, False, False, 2)
+
+      self.sources["IGNORE_PREFIX_SUFFIX"] = Gtk.CheckButton("Ignore callsign prefixes and/or suffixes")
+      (section, option) = ("records", "ignore_prefix_suffix")
+      if(have_config and config.has_option(section, option)):
+         self.sources["IGNORE_PREFIX_SUFFIX"].set_active(config.get(section, option) == "True")
+      else:
+         self.sources["IGNORE_PREFIX_SUFFIX"].set_active(True)
+      vbox.pack_start(self.sources["IGNORE_PREFIX_SUFFIX"], False, False, 2)
+      
+      frame.add(vbox)
+      self.pack_start(frame, False, False, 2)
+      
+      logging.debug("Records page of the preferences dialog ready!")
+      return
+
+   def get_data(self):
+      logging.debug("Retrieving data from the Records page of the preferences dialog...")
+      data = {}
+      data["AUTOCOMPLETE_BAND"] = self.sources["AUTOCOMPLETE_BAND"].get_active()
+      data["USE_UTC"] = self.sources["USE_UTC"].get_active()
+      data["QRZ_USERNAME"] = self.sources["QRZ_USERNAME"].get_text()
+      data["QRZ_PASSWORD"] = base64.b64encode(self.sources["QRZ_PASSWORD"].get_text())
+      data["IGNORE_PREFIX_SUFFIX"] = self.sources["IGNORE_PREFIX_SUFFIX"].get_active()
+      return data
+
diff --git a/pyqso/record_dialog.py b/pyqso/record_dialog.py
index 8a647a4..6df2bfc 100644
--- a/pyqso/record_dialog.py
+++ b/pyqso/record_dialog.py
@@ -31,7 +31,7 @@ except ImportError:
    logging.error("Could not import the Hamlib module!")
    have_hamlib = False
 
-from adif import AVAILABLE_FIELD_NAMES_FRIENDLY, AVAILABLE_FIELD_NAMES_ORDERED
+from adif import AVAILABLE_FIELD_NAMES_FRIENDLY, AVAILABLE_FIELD_NAMES_ORDERED, MODES, BANDS, BANDS_RANGES
 from callsign_lookup import *
 from auxiliary_dialogs import *
 
@@ -51,6 +51,10 @@ class RecordDialog(Gtk.Dialog):
          title = "Add Record"
       Gtk.Dialog.__init__(self, title=title, parent=parent, flags=Gtk.DialogFlags.DESTROY_WITH_PARENT, buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK))
 
+      # Check if a configuration file is present, since we might need it to set up the rest of the dialog.
+      config = ConfigParser.ConfigParser()
+      have_config = (config.read(expanduser('~/.pyqso.ini')) != [])
+      
       ## QSO DATA FRAME
       qso_frame = Gtk.Frame()
       qso_frame.set_label("QSO Information")
@@ -109,11 +113,23 @@ class RecordDialog(Gtk.Dialog):
       self.sources["TIME_ON"] = Gtk.Entry()
       self.sources["TIME_ON"].set_width_chars(15)
       hbox_temp.pack_start(self.sources["TIME_ON"], False, False, 2)
+      icon = Gtk.Image()
+      icon.set_from_stock(Gtk.STOCK_MEDIA_PLAY, Gtk.IconSize.MENU)
+      button = Gtk.Button()
+      button.add(icon)
+      button.connect("clicked", self.set_current_datetime_callback)
+      button.set_tooltip_text("Use the current time and date")
+      hbox_temp.pack_start(button, True, True, 2)
       vbox_inner.pack_start(hbox_temp, False, False, 2)
 
       # FREQ
       hbox_temp = Gtk.HBox(spacing=0)
-      label = Gtk.Label(AVAILABLE_FIELD_NAMES_FRIENDLY["FREQ"], halign=Gtk.Align.START)
+      (section, option) = ("view", "default_freq_unit")
+      if(have_config and config.has_option(section, option)):
+         frequency_unit = config.get(section, option)
+      else:
+         frequency_unit = "MHz"
+      label = Gtk.Label(AVAILABLE_FIELD_NAMES_FRIENDLY["FREQ"] + " (" + frequency_unit + ")", halign=Gtk.Align.START)
       label.set_alignment(0, 0.5)
       label.set_width_chars(15)
       hbox_temp.pack_start(label, False, False, 2)
@@ -128,9 +144,9 @@ class RecordDialog(Gtk.Dialog):
       label.set_alignment(0, 0.5)
       label.set_width_chars(15)
       hbox_temp.pack_start(label, False, False, 2)
-      bands = ["", "2190m", "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"]
+
       self.sources["BAND"] = Gtk.ComboBoxText()
-      for band in bands:
+      for band in BANDS:
          self.sources["BAND"].append_text(band)
       self.sources["BAND"].set_active(0) # Set an empty string as the default option.
       hbox_temp.pack_start(self.sources["BAND"], False, False, 2)
@@ -142,9 +158,9 @@ class RecordDialog(Gtk.Dialog):
       label.set_alignment(0, 0.5)
       label.set_width_chars(15)
       hbox_temp.pack_start(label, False, False, 2)
-      modes = ["", "AM", "AMTORFEC", "ASCI", "ATV", "CHIP64", "CHIP128", "CLO", "CONTESTI", "CW", "DSTAR", "DOMINO", "DOMINOF", "FAX", "FM", "FMHELL", "FSK31", "FSK441", "GTOR", "HELL", "HELL80", "HFSK", "ISCAT", "JT44", "JT4A", "JT4B", "JT4C", "JT4D", "JT4E", "JT4F", "JT4G", "JT65", "JT65A", "JT65B", "JT65C", "JT6M", "MFSK8", "MFSK16", "MT63", "OLIVIA", "PAC", "PAC2", "PAC3", "PAX", "PAX2", "PCW", "PKT", "PSK10", "PSK31", "PSK63", "PSK63F", "PSK125", "PSKAM10", "PSKAM31", "PSKAM50", "PS [...]
+
       self.sources["MODE"] = Gtk.ComboBoxText()
-      for mode in modes:
+      for mode in MODES:
          self.sources["MODE"].append_text(mode)
       self.sources["MODE"].set_active(0) # Set an empty string as the default option.
       hbox_temp.pack_start(self.sources["MODE"], False, False, 2)
@@ -337,6 +353,7 @@ class RecordDialog(Gtk.Dialog):
 
       station_frame.add(hbox_inner)
 
+      # Populate various fields, if possible.
       if(index is not None):
          # The record already exists, so display its current data in the input boxes.
          record = log.get_record_by_index(index)
@@ -346,9 +363,9 @@ class RecordDialog(Gtk.Dialog):
             if(data is None):
                data = ""
             if(field_names[i] == "BAND"):
-               self.sources[field_names[i]].set_active(bands.index(data))
+               self.sources[field_names[i]].set_active(BANDS.index(data))
             elif(field_names[i] == "MODE"):
-               self.sources[field_names[i]].set_active(modes.index(data))
+               self.sources[field_names[i]].set_active(MODES.index(data))
             elif(field_names[i] == "QSL_SENT" or field_names[i] == "QSL_RCVD"):
                self.sources[field_names[i]].set_active(qsl_options.index(data))
             elif(field_names[i] == "NOTES"):
@@ -359,28 +376,11 @@ class RecordDialog(Gtk.Dialog):
                self.sources[field_names[i]].set_text(data)
       else:
          # Automatically fill in the current date and time
-         dt = datetime.now()
-         (year, month, day) = (dt.year, dt.month, dt.day)
-         (hour, minute) = (dt.hour, dt.minute)
-         # If necessary, add on leading zeros so the YYYYMMDD and HHMM format is followed.
-         if(month < 10):
-            month = "0" + str(month) # Note: Unlike the calendar widget, the months start from an index of 1 here.
-         if(day < 10):
-            day = "0" + str(day)
-         if(hour < 10):
-            hour = "0" + str(hour)
-         if(minute < 10):
-            minute = "0" + str(minute)
-         date = str(year) + str(month) + str(day)
-         time = str(hour) + str(minute)
-         self.sources["QSO_DATE"].set_text(date)
-         self.sources["TIME_ON"].set_text(time)
+         self.set_current_datetime_callback()
 
          if(have_hamlib):
             # If the Hamlib module is present, then use it to fill in the Frequency field if desired.
-            config = ConfigParser.ConfigParser()
-            have_config = (config.read(expanduser('~/.pyqso.ini')) != [])
-            if(have_config):
+            if(have_config and config.has_option("hamlib", "autofill") and config.has_option("hamlib", "rig_model") and config.has_option("hamlib", "rig_pathname")):
                autofill = (config.get("hamlib", "autofill") == "True")
                rig_model = config.get("hamlib", "rig_model")
                rig_pathname = config.get("hamlib", "rig_pathname")
@@ -397,6 +397,16 @@ class RecordDialog(Gtk.Dialog):
                   except:
                      logging.error("Could not obtain Frequency data via Hamlib!")
 
+      # Do we want PyQSO to autocomplete the Band field based on the Frequency field?
+      (section, option) = ("records", "autocomplete_band")
+      if(have_config and config.get(section, option)):
+         autocomplete_band = (config.get(section, option) == "True")
+         if(autocomplete_band):
+            self.sources["FREQ"].connect("changed", self._autocomplete_band)
+      else:
+         # If no configuration file exists, autocomplete the Band field by default.
+         self.sources["FREQ"].connect("changed", self._autocomplete_band)
+
       self.show_all()
 
       logging.debug("Record dialog ready!")
@@ -406,7 +416,10 @@ class RecordDialog(Gtk.Dialog):
    def get_data(self, field_name):
       """ Return the data for a specified field (with name 'field_name') from the Gtk.Entry/Gtk.ComboBoxText/etc boxes in the record dialog. """
       logging.debug("Retrieving the data in field %s from the record dialog..." % field_name)
-      if(field_name == "BAND" or field_name == "MODE" or field_name == "QSL_SENT" or field_name == "QSL_RCVD"):
+      if(field_name == "CALL"):
+         # Always show the callsigns in upper case.
+         return self.sources[field_name].get_text().upper()
+      elif(field_name == "BAND" or field_name == "MODE" or field_name == "QSL_SENT" or field_name == "QSL_RCVD"):
          return self.sources[field_name].get_active_text()
       elif(field_name == "NOTES"):
          (start, end) = self.sources[field_name].get_bounds()
@@ -418,15 +431,37 @@ class RecordDialog(Gtk.Dialog):
       else:
          return self.sources[field_name].get_text()
 
+
+   def _autocomplete_band(self, widget=None):
+      """ If a value for the Frequency is entered, this function autocompletes the Band field. """
+
+      frequency = self.sources["FREQ"].get_text()
+      # Check whether we actually have a (valid) value to use. If not, set the BAND field to an empty string ("").
+      try:
+         frequency = float(frequency)
+      except ValueError:
+         self.sources["BAND"].set_active(0)
+         return
+      
+      # Find which band the frequency lies in.
+      for i in range(1, len(BANDS)):
+         if(frequency >= BANDS_RANGES[i][0] and frequency <= BANDS_RANGES[i][1]):
+            self.sources["BAND"].set_active(i)
+            return
+
+      self.sources["BAND"].set_active(0) # If we've reached this, then the frequency does not lie in any of the specified bands.
+      return
+
+
    def lookup_callback(self, widget=None):
       """ Get the callsign-related data from the qrz.com database and store it in the relevant Gtk.Entry boxes, but return None. """
       callsign_lookup = CallsignLookup(parent = self)
 
       config = ConfigParser.ConfigParser()
       have_config = (config.read(expanduser('~/.pyqso.ini')) != [])
-      if(have_config):
-         username = config.get("general", "qrz_username")
-         password = base64.b64decode(config.get("general", "qrz_password"))
+      if(have_config and config.has_option("records", "qrz_username") and config.has_option("records", "qrz_password")):
+         username = config.get("records", "qrz_username")
+         password = base64.b64decode(config.get("records", "qrz_password"))
          if(username == "" or password == ""):
             details_given = False
          else:
@@ -439,7 +474,15 @@ class RecordDialog(Gtk.Dialog):
 
       connected = callsign_lookup.connect(username, password)
       if(connected):
-         fields_and_data = callsign_lookup.lookup(self.sources["CALL"].get_text())
+         full_callsign = self.sources["CALL"].get_text()
+         # Check whether we want to ignore any prefixes (e.g. "IA/") or suffixes "(e.g. "/M") in the callsign
+         # before performing the lookup.
+         if(have_config and config.has_option("records", "ignore_prefix_suffix")):
+            ignore_prefix_suffix = config.get("records", "ignore_prefix_suffix")
+         else:
+            ignore_prefix_suffix = True
+            
+         fields_and_data = callsign_lookup.lookup(full_callsign, ignore_prefix_suffix=ignore_prefix_suffix)
          for field_name in fields_and_data.keys():
             self.sources[field_name].set_text(fields_and_data[field_name])
       return
@@ -453,6 +496,42 @@ class RecordDialog(Gtk.Dialog):
          self.sources["QSO_DATE"].set_text(date)
       calendar.destroy()
       return
+      
+   def set_current_datetime_callback(self, widget=None):
+      """ Insert the current date and time. """
+      
+       # Check if a configuration file is present.
+      config = ConfigParser.ConfigParser()
+      have_config = (config.read(expanduser('~/.pyqso.ini')) != [])
+      
+      # Do we want to use UTC or the computer's local time?
+      (section, option) = ("records", "use_utc")
+      if(have_config and config.has_option(section, option)):
+         use_utc = (config.get(section, option) == "True")
+         if(use_utc):
+            dt = datetime.utcnow()
+         else:
+            dt = datetime.now()
+      else:
+         dt = datetime.utcnow() # Use UTC by default, since this is expected by ADIF.
+
+      (year, month, day) = (dt.year, dt.month, dt.day)
+      (hour, minute) = (dt.hour, dt.minute)
+      # If necessary, add on leading zeros so the YYYYMMDD and HHMM format is followed.
+      if(month < 10):
+         month = "0" + str(month) # Note: Unlike the calendar widget, the months start from an index of 1 here.
+      if(day < 10):
+         day = "0" + str(day)
+      if(hour < 10):
+         hour = "0" + str(hour)
+      if(minute < 10):
+         minute = "0" + str(minute)
+      date = str(year) + str(month) + str(day)
+      time = str(hour) + str(minute)
+      self.sources["QSO_DATE"].set_text(date)
+      self.sources["TIME_ON"].set_text(time)
+      
+      return
 
 class CalendarDialog(Gtk.Dialog):
    """ A simple dialog containing a Gtk.Calendar widget. Using this ensures the date is in the correct YYYYMMDD format required by ADIF. """ 
diff --git a/pyqso/toolbar.py b/pyqso/toolbar.py
index 2c14661..02bc9c1 100644
--- a/pyqso/toolbar.py
+++ b/pyqso/toolbar.py
@@ -30,12 +30,22 @@ class Toolbar(Gtk.HBox):
 
       self.buttons = {}
 
-      # Create/open logbook
+      # Create logbook
+      icon = Gtk.Image()
+      icon.set_from_stock(Gtk.STOCK_NEW, Gtk.IconSize.BUTTON)
+      button = Gtk.Button()
+      button.add(icon)
+      button.set_tooltip_text('Create a New Logbook')
+      button.connect("clicked", parent.logbook.new)
+      self.pack_start(button, False, False, 0)
+      self.buttons["NEW_LOGBOOK"] = button
+      
+      # Open logbook
       icon = Gtk.Image()
       icon.set_from_stock(Gtk.STOCK_OPEN, Gtk.IconSize.BUTTON)
       button = Gtk.Button()
       button.add(icon)
-      button.set_tooltip_text('Open New or Existing Logbook')
+      button.set_tooltip_text('Open an Existing Logbook')
       button.connect("clicked", parent.logbook.open)
       self.pack_start(button, False, False, 0)
       self.buttons["OPEN_LOGBOOK"] = button
@@ -103,6 +113,7 @@ class Toolbar(Gtk.HBox):
 
    def set_logbook_button_sensitive(self, sensitive):
       logging.debug("Setting the 'Create/Open Logbook' toolbar item's sensitivity to: %s..." % sensitive) 
+      self.buttons["NEW_LOGBOOK"].set_sensitive(sensitive)
       self.buttons["OPEN_LOGBOOK"].set_sensitive(sensitive)
       self.buttons["CLOSE_LOGBOOK"].set_sensitive(not sensitive)
       logging.debug("Set the 'Create/Open Logbook' toolbar item's sensitivity to: %s." % sensitive) 
diff --git a/setup.py b/setup.py
index eadb276..f7c0daf 100644
--- a/setup.py
+++ b/setup.py
@@ -21,7 +21,7 @@
 from distutils.core import setup
 
 setup(name='PyQSO',
-      version='0.1',
+      version='0.2a-dev',
       description='A contact logging tool for amateur radio operators.',
       author='Christian Jacobs',
       url='https://github.com/ctjacobs/pyqso',

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-hamradio/pyqso.git



More information about the pkg-hamradio-commits mailing list