kopia lustrzana https://github.com/ctjacobs/pyqso
264 wiersze
9.5 KiB
Python
264 wiersze
9.5 KiB
Python
#!/usr/bin/env python
|
|
# File: pyqso.py
|
|
|
|
# Copyright (C) 2012 Christian 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/>.
|
|
|
|
from gi.repository import Gtk, GObject
|
|
import logging
|
|
import optparse
|
|
|
|
# PyQSO modules
|
|
from adif import *
|
|
from logbook import *
|
|
from menu import *
|
|
|
|
# The PyQSO application class
|
|
class PyQSO(Gtk.Window):
|
|
|
|
def __init__(self):
|
|
|
|
# Call the constructor of the super class (Gtk.Window)
|
|
Gtk.Window.__init__(self, title="PyQSO (development version)")
|
|
|
|
self.set_size_request(500, 300)
|
|
self.set_position(Gtk.WindowPosition.CENTER)
|
|
# Kills the application if the close button is clicked
|
|
# on the main window itself.
|
|
self.connect("delete-event", Gtk.main_quit)
|
|
|
|
# Vertically aligned packing layout
|
|
vbox = Gtk.VBox(spacing=6)
|
|
self.add(vbox)
|
|
|
|
# Set up menu bar and populate it
|
|
menu = Menu(self, vbox)
|
|
|
|
# SCROLLED WINDOW to host the list box defined below.
|
|
sw = Gtk.ScrolledWindow()
|
|
sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
|
|
sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
|
vbox.pack_start(sw, True, True, 0)
|
|
|
|
# LIST BOX for the radio log entries
|
|
self.logbook = Logbook()
|
|
self.treeview = Gtk.TreeView(self.logbook)
|
|
self.treeview.set_grid_lines(Gtk.TreeViewGridLines.BOTH)
|
|
sw.add(self.treeview)
|
|
|
|
# The first column of the logbook will always be the unique record index.
|
|
# Let's append this separately to the field names.
|
|
renderer = Gtk.CellRendererText()
|
|
renderer.set_property("editable", False)
|
|
column = Gtk.TreeViewColumn("INDEX", renderer, text=0)
|
|
column.set_resizable(True)
|
|
column.set_min_width(50)
|
|
self.treeview.append_column(column)
|
|
|
|
# Set up column names for each selected field
|
|
field_names = self.logbook.SELECTED_FIELD_NAMES_TYPES.keys()
|
|
data_types = self.logbook.SELECTED_FIELD_NAMES_TYPES.values()
|
|
for i in range(0, len(self.logbook.SELECTED_FIELD_NAMES_TYPES)):
|
|
|
|
if(data_types[i] == "S"):
|
|
renderer = Gtk.CellRendererText()
|
|
renderer.set_property("editable", True)
|
|
renderer.connect("edited", self.textcell_edited_callback, self.treeview, i+1)
|
|
column = Gtk.TreeViewColumn(field_names[i], renderer, text=i+1)
|
|
|
|
elif(data_types[i] == "B"):
|
|
choices = Gtk.ListStore(str)
|
|
for item in ["", "Yes", "No"]:
|
|
choices.append([item])
|
|
|
|
renderer = Gtk.CellRendererCombo()
|
|
renderer.set_property("editable", True)
|
|
renderer.set_property("model", choices)
|
|
renderer.set_property("text-column", 0)
|
|
renderer.connect("edited", self.booleancell_edited_callback, self.treeview, i+1)
|
|
column = Gtk.TreeViewColumn(field_names[i], renderer, text=i+1)
|
|
|
|
else:
|
|
# Default to a text-based cell if the field name is unknown.
|
|
renderer = Gtk.CellRendererText()
|
|
renderer.set_property("editable", True)
|
|
renderer.connect("edited", self.textcell_edited_callback, self.treeview, i+1)
|
|
column = Gtk.TreeViewColumn(field_names[i], renderer, text=i+1)
|
|
|
|
column.set_resizable(True)
|
|
column.set_min_width(50)
|
|
self.treeview.append_column(column)
|
|
|
|
self.show_all()
|
|
|
|
return
|
|
|
|
def new_log(self, widget):
|
|
self.logbook.records = []
|
|
self.logbook.populate()
|
|
return
|
|
|
|
def open_log(self, widget):
|
|
dialog = Gtk.FileChooserDialog("Open File",
|
|
None,
|
|
Gtk.FileChooserAction.OPEN,
|
|
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
|
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
|
|
filter = Gtk.FileFilter()
|
|
filter.set_name("All ADIF files")
|
|
filter.add_pattern("*.adi")
|
|
filter.add_pattern("*.adx")
|
|
dialog.add_filter(filter)
|
|
|
|
response = dialog.run()
|
|
if(response == Gtk.ResponseType.OK):
|
|
path = dialog.get_filename()
|
|
else:
|
|
path = None
|
|
dialog.destroy()
|
|
|
|
if(path is None):
|
|
logging.debug("No file path specified.")
|
|
return
|
|
|
|
adif = ADIF()
|
|
self.logbook.records = adif.read(path)
|
|
self.logbook.populate()
|
|
|
|
return
|
|
|
|
def save_log(self, widget):
|
|
dialog = Gtk.FileChooserDialog("Save File",
|
|
None,
|
|
Gtk.FileChooserAction.SAVE,
|
|
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
|
Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
|
|
|
|
response = dialog.run()
|
|
if(response == Gtk.ResponseType.OK):
|
|
path = dialog.get_filename()
|
|
else:
|
|
path = None
|
|
dialog.destroy()
|
|
|
|
if(path is None):
|
|
logging.debug("No file path specified.")
|
|
return
|
|
|
|
adif = ADIF()
|
|
adif.write(self.logbook.records, path)
|
|
|
|
return
|
|
|
|
def add_record_callback(self, widget):
|
|
self.logbook.add_record()
|
|
return
|
|
|
|
def delete_record_callback(self, widget):
|
|
# Get the selected row in the logbook and
|
|
# delete it.
|
|
selection = self.treeview.get_selection()
|
|
selection.set_mode(Gtk.SelectionMode.SINGLE)
|
|
(model, path) = selection.get_selected_rows()
|
|
try:
|
|
iter = model.get_iter(path[0])
|
|
index = model.get_value(iter,0)
|
|
except IndexError:
|
|
logging.debug("Trying to delete a record, but there are no records in the logbook!")
|
|
return
|
|
|
|
dialog = Gtk.MessageDialog(self, Gtk.DialogFlags.DESTROY_WITH_PARENT,
|
|
Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO,
|
|
"Are you sure you want to delete record %d?" % index)
|
|
response = dialog.run()
|
|
if(response == Gtk.ResponseType.YES):
|
|
self.logbook.delete_record(iter, index)
|
|
|
|
dialog.destroy()
|
|
return
|
|
|
|
def textcell_edited_callback(self, widget, path, text, treeview, column_index):
|
|
|
|
#TODO: Validate user input!
|
|
|
|
# First update the Record object. In this case, path is the row number.
|
|
column_name = treeview.get_column(column_index).get_title()
|
|
self.logbook.records[int(path)].set_field(column_name, text)
|
|
|
|
# And then the GTK TreeView (i.e. the logbook)
|
|
self.logbook[path][column_index] = text
|
|
|
|
return
|
|
|
|
def booleancell_edited_callback(self, widget, path, text, treeview, column_index):
|
|
|
|
# Boolean combo boxes require special treatment:
|
|
# ADIF specification denotes True by the letter "Y" and False by the
|
|
# letter "N"...
|
|
if(text == "Yes"):
|
|
text_for_record = "Y"
|
|
elif(text == "No"):
|
|
text_for_record = "N"
|
|
else:
|
|
text_for_record = ""
|
|
column_name = treeview.get_column(column_index).get_title()
|
|
self.logbook.records[int(path)].set_field(column_name, text_for_record)
|
|
|
|
# Update the logbook using the actual text from the combo box.
|
|
iter = self.logbook.get_iter(path)
|
|
self.logbook[path][column_index] = text
|
|
|
|
return
|
|
|
|
def show_about(self, widget):
|
|
about = Gtk.AboutDialog()
|
|
about.set_program_name("PyQSO")
|
|
about.set_version("Development version")
|
|
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
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.''')
|
|
about.set_comments("PyQSO: A Python-based contact logging tool for amateur radio operators")
|
|
about.run()
|
|
about.destroy()
|
|
return
|
|
|
|
|
|
if(__name__ == '__main__'):
|
|
# Get any command line arguments
|
|
parser = optparse.OptionParser()
|
|
parser.add_option('-l', '--log', action="store_true", default=False, help='Enable logging. All log messages will be written to pyqso.log.')
|
|
(options, args) = parser.parse_args()
|
|
|
|
# Set up debugging output
|
|
if(options.log):
|
|
logging.basicConfig(level=logging.DEBUG, filename="pyqso.log",
|
|
format="%(asctime)s %(levelname)s: %(message)s",
|
|
datefmt="%Y-%m-%d %H:%M:%S")
|
|
|
|
application = PyQSO() # Populate the main window and show it
|
|
Gtk.main() # Start up the event loop!
|