- Added a "+" ("New Log") tab, and a "Delete Log" button after each tab's title.

- Added a new dialog so the user can enter the new log's name.
pull/17/head
Christian Jacobs 2013-04-16 23:53:24 +01:00
rodzic 38a7c65e09
commit d0246a3f2c
6 zmienionych plików z 185 dodań i 90 usunięć

Wyświetl plik

@ -60,7 +60,7 @@ class PyQSO(Gtk.Window):
# Create a Logbook so we can add/remove/edit logs and records,
# once connected to the SQLite database.
self.logbook = Logbook(logbook_path)
self.logbook = Logbook(self, logbook_path)
# Set up menu and tool bars
# These classes depend on the Logbook class,
@ -75,7 +75,7 @@ class PyQSO(Gtk.Window):
self.statusbar = Gtk.Statusbar()
context_id = self.statusbar.get_context_id("Status")
if(self.logbook.connection):
self.statusbar.push(context_id, "Connected to Logbook: %s" % logbook_path)
self.statusbar.push(context_id, "Logbook: %s" % logbook_path)
vbox_outer.pack_start(self.statusbar, False, False, 0)
self.show_all()

Wyświetl plik

@ -51,13 +51,9 @@ class Log(Gtk.ListStore):
Gtk.ListStore.__init__(self, *data_types)
self.connection = connection
self.name = name
with self.connection:
# Set up the new log table in the database
# This is a new log/table, so create it in the database
c = self.connection.cursor()
query = "CREATE TABLE %s (id INTEGER PRIMARY KEY" % name
for field_name in self.SELECTED_FIELD_NAMES_ORDERED:
@ -81,44 +77,30 @@ class Log(Gtk.ListStore):
def add_record(self, fields_and_data):
self.set_modified(True)
return
def delete_record(self, index, iter):
# Get the selected row in the logbook
#self.records.pop(index)
self.remove(iter)
self.set_modified(True)
return
def edit_record(self, index, field_name, data):
#self.records[index].set_data(field_name, data)
self.set_modified(True)
return
def get_record_by_index(self, index):
c = self.connection.cursor()
c.execute("SELECT * FROM ? WHERE id=?", (TABLE_NAME, index))
c.execute("SELECT * FROM ? WHERE id=?", (self.name, index))
return c.fetchone()
def get_all_records(self):
c = self.connection.cursor()
c.execute("SELECT * FROM %s" % TABLE_NAME)
c.execute("SELECT * FROM %s" % self.name)
return c.fetchall()
def get_number_of_records(self):
c = self.connection.cursor()
c.execute("SELECT Count(*) FROM %s" % TABLE_NAME)
c.execute("SELECT Count(*) FROM %s" % self.name)
return c.fetchone()[0]
def set_modified(self, modified):
if(modified and self.modified):
return # Already modified. Nothing to do here.
elif(modified and (not self.modified)):
self.modified = True
self.name = self.name + "*"
return
else:
self.modified = modified
return

Wyświetl plik

@ -26,14 +26,17 @@ from os.path import basename
from adif import *
from log import *
from new_log_dialog import *
class Logbook(Gtk.Notebook):
''' A Logbook object can store multiple Log objects. '''
def __init__(self, path):
def __init__(self, root_window, path):
Gtk.Notebook.__init__(self)
self.root_window = root_window
# A stack of Log objects
self.logs = []
# SQL database connection to the Logbook's data source
@ -43,6 +46,14 @@ class Logbook(Gtk.Notebook):
self.treeview = []
self.treeselection = []
self._create_new_log_tab()
# FIXME: This is an unfortunate work-around. If the area around the "+/New Log" button
# is clicked, PyQSO will change to an empty page. This signal is used to stop this from happening.
self.connect("switch-page", self._on_switch_page)
self.show_all()
logging.debug("New Logbook instance created!")
def _connect(self, path):
@ -66,23 +77,76 @@ class Logbook(Gtk.Notebook):
logging.error("Already disconnected. Nothing to do here.")
return True
def new_log(self, widget=None):
def _create_new_log_tab(self):
# Create a blank page in the Notebook for the "+" (New Log) tab
blank_treeview = Gtk.TreeView([])
# Allow the Log to be scrolled up/down
sw = Gtk.ScrolledWindow()
sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
sw.add(blank_treeview)
vbox = Gtk.VBox()
vbox.pack_start(sw, True, True, 0)
# Add a "+" button to the tab
hbox = Gtk.HBox(False, 0)
icon = Gtk.Image.new_from_stock(Gtk.STOCK_ADD, Gtk.IconSize.MENU)
button = Gtk.Button()
button.set_relief(Gtk.ReliefStyle.NONE)
button.set_focus_on_click(False)
button.connect("clicked", self.new_log)
button.add(icon)
hbox.pack_start(button, False, False, 0)
hbox.show_all()
vbox.show_all()
self.insert_page(vbox, hbox, 0)
l = Log(self.connection, log_name) # Empty log
self.logs.append(l)
self.render_log(l)
return
def delete_log(self, widget, parent):
current = self.get_current_page() # Gets the index of the selected tab in the logbook
def _on_switch_page(self, widget, label, new_page):
if(new_page == self.get_n_pages()-1): # The last (right-most) tab is the "New Log" tab.
self.stop_emission("switch-page")
return
def new_log(self, widget=None):
exists = True
dialog = NewLogDialog(self.root_window)
while(exists):
response = dialog.run()
if(response == Gtk.ResponseType.OK):
log_name = dialog.get_log_name()
if(not self.log_name_exists(log_name) and log_name != ""):
l = Log(self.connection, log_name) # Empty log
self.logs.append(l)
self.render_log(self.get_number_of_logs()-1)
exists = False
else:
logging.error("Log with name %s already exists." % log_name)
# Data is not valid - inform the user.
message = Gtk.MessageDialog(self.root_window, Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
"Log with name %s already exists. Please choose another name." % log_name)
message.run()
message.destroy()
else:
dialog.destroy()
return
dialog.destroy()
self.set_current_page(self.get_number_of_logs()-1)
return
def delete_log(self, widget):
current = self.get_current_page() - 1 # Gets the index of the selected tab in the logbook
if(current == -1):
logging.debug("No logs to delete!")
return
log = self.logs[current]
dialog = Gtk.MessageDialog(parent, Gtk.DialogFlags.DESTROY_WITH_PARENT,
dialog = Gtk.MessageDialog(self.root_window, Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.QUESTION, Gtk.ButtonsType.YES_NO,
"Are you sure you want to delete log %s?" % log.name)
response = dialog.run()
@ -97,26 +161,38 @@ class Logbook(Gtk.Notebook):
return
def render_log(self, log):
def render_log(self, index):
# Render the Log
current = self.get_number_of_logs()-1
#sorter = Gtk.TreeModelSort(model=log) #FIXME: Get sorted columns working!
#sorter.set_sort_column_id(1, Gtk.SortType.ASCENDING)
#self.treeview.append(Gtk.TreeView(sorter))
self.treeview.append(Gtk.TreeView(log))
self.treeview[current].set_grid_lines(Gtk.TreeViewGridLines.BOTH)
#FIXME: Ideally we want the parent window to be passed in to self.edit_record_callback.
self.treeview[current].connect("row-activated", self.edit_record_callback, None)
self.treeselection.append(self.treeview[current].get_selection())
self.treeselection[current].set_mode(Gtk.SelectionMode.SINGLE)
self.treeview.append(Gtk.TreeView(self.logs[index]))
self.treeview[index].set_grid_lines(Gtk.TreeViewGridLines.BOTH)
self.treeview[index].connect("row-activated", self.edit_record_callback, self.root_window)
self.treeselection.append(self.treeview[index].get_selection())
self.treeselection[index].set_mode(Gtk.SelectionMode.SINGLE)
# Allow the Log to be scrolled up/down
sw = Gtk.ScrolledWindow()
sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
sw.add(self.treeview[current])
sw.add(self.treeview[index])
vbox = Gtk.VBox()
vbox.pack_start(sw, True, True, 0)
self.append_page(vbox, Gtk.Label(self.logs[current].name)) # Append the new log as a new tab
# Add a close button to the tab
hbox = Gtk.HBox(False, 0)
label = Gtk.Label(self.logs[index].name)
hbox.pack_start(label, False, False, 0)
icon = Gtk.Image.new_from_stock(Gtk.STOCK_CLOSE, Gtk.IconSize.MENU)
button = Gtk.Button()
button.set_relief(Gtk.ReliefStyle.NONE)
button.set_focus_on_click(False)
button.connect("clicked", self.delete_log)
button.add(icon)
hbox.pack_start(button, False, False, 0)
hbox.show_all()
self.insert_page(vbox, hbox, index) # Append the new log as a new tab
# The first column of the logbook will always be the unique record index.
# Let's append this separately to the field names.
@ -124,21 +200,21 @@ class Logbook(Gtk.Notebook):
column = Gtk.TreeViewColumn("Index", renderer, text=0)
column.set_resizable(True)
column.set_min_width(50)
self.treeview[current].append_column(column)
self.treeview[index].append_column(column)
# Set up column names for each selected field
field_names = self.logs[current].SELECTED_FIELD_NAMES_ORDERED
field_names = self.logs[index].SELECTED_FIELD_NAMES_ORDERED
for i in range(0, len(field_names)):
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(self.logs[current].SELECTED_FIELD_NAMES_FRIENDLY[field_names[i]], renderer, text=i+1)
column = Gtk.TreeViewColumn(self.logs[index].SELECTED_FIELD_NAMES_FRIENDLY[field_names[i]], renderer, text=i+1)
column.set_resizable(True)
column.set_min_width(50)
self.treeview[current].append_column(column)
self.treeview[index].append_column(column)
self.show_all()
def import_log(self, widget, parent):
def import_log(self, widget):
dialog = Gtk.FileChooserDialog("Import ADIF Log File",
None,
Gtk.FileChooserAction.OPEN,
@ -162,7 +238,7 @@ class Logbook(Gtk.Notebook):
for log in self.logs:
if(log.path == path):
dialog = Gtk.MessageDialog(parent, Gtk.DialogFlags.DESTROY_WITH_PARENT,
dialog = Gtk.MessageDialog(self.root_window, Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
"Log %s is already open." % path)
response = dialog.run()
@ -214,14 +290,14 @@ class Logbook(Gtk.Notebook):
log.set_modified(False)
return
def add_record_callback(self, widget, parent):
def add_record_callback(self, widget):
current = self.get_current_page() # Gets the index of the selected tab in the logbook
current = self.get_current_page() - 1 # Gets the index of the selected tab in the logbook
if(current == -1):
logging.debug("Tried to add a record, but no log present!")
return
log = self.logs[current]
dialog = RecordDialog(root_window=parent, log=log, index=None)
dialog = RecordDialog(root_window=self.root_window, log=log, index=None)
all_valid = False # Are all the field entries valid?
while(not all_valid):
@ -238,7 +314,7 @@ class Logbook(Gtk.Notebook):
fields_and_data[field_names[i]] = dialog.get_data(field_names[i])
if(not(dialog.is_valid(field_names[i], fields_and_data[field_names[i]], log.SELECTED_FIELD_NAMES_TYPES[field_names[i]]))):
# Data is not valid - inform the user.
message = Gtk.MessageDialog(parent, Gtk.DialogFlags.DESTROY_WITH_PARENT,
message = Gtk.MessageDialog(self.root_window, Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
"The data in field \"%s\" is not valid!" % field_names[i])
message.run()
@ -261,8 +337,8 @@ class Logbook(Gtk.Notebook):
dialog.destroy()
return
def delete_record_callback(self, widget, parent):
current = self.get_current_page() # Get the selected log
def delete_record_callback(self, widget):
current = self.get_current_page() - 1 # Get the selected log
if(current == -1):
logging.debug("Tried to delete a record, but no log present!")
return
@ -274,7 +350,7 @@ class Logbook(Gtk.Notebook):
logging.debug("Trying to delete a record, but there are no records in the log!")
return
dialog = Gtk.MessageDialog(parent, Gtk.DialogFlags.DESTROY_WITH_PARENT,
dialog = Gtk.MessageDialog(self.root_window, 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()
@ -287,11 +363,11 @@ class Logbook(Gtk.Notebook):
self.set_tab_label_text(self.get_nth_page(current), self.logs[current].name)
return
def edit_record_callback(self, widget, path, view_column, parent):
def edit_record_callback(self, widget, path, view_column):
# Note: the path and view_column arguments need to be passed in
# since they associated with the row-activated signal.
current = self.get_current_page() # Get the selected log
current = self.get_current_page() - 1 # Get the selected log
if(current == -1):
logging.debug("Tried to edit a record, but no log present!")
return
@ -306,7 +382,7 @@ class Logbook(Gtk.Notebook):
logging.debug("Could not find the selected row's index!")
return
dialog = RecordDialog(root_window=parent, log=self.logs[current], index=row_index)
dialog = RecordDialog(root_window=self.root_window, log=self.logs[current], index=row_index)
all_valid = False # Are all the field entries valid?
while(not all_valid):
@ -323,7 +399,7 @@ class Logbook(Gtk.Notebook):
fields_and_data[field_names[i]] = dialog.get_data(field_names[i])
if(not(dialog.is_valid(field_names[i], fields_and_data[field_names[i]], self.logs[current].SELECTED_FIELD_NAMES_TYPES[field_names[i]]))):
# Data is not valid - inform the user.
message = Gtk.MessageDialog(parent, Gtk.DialogFlags.DESTROY_WITH_PARENT,
message = Gtk.MessageDialog(self.root_window, Gtk.DialogFlags.DESTROY_WITH_PARENT,
Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
"The data in field \"%s\" is not valid!" % field_names[i])
message.run()
@ -350,4 +426,13 @@ class Logbook(Gtk.Notebook):
def get_number_of_logs(self):
return len(self.logs)
def log_name_exists(self, table_name):
with self.connection:
c = self.connection.cursor()
c.execute("SELECT name FROM sqlite_master WHERE type='table'")
names = c.fetchall()
for name in names:
if(table_name in name):
return True
return False

Wyświetl plik

@ -47,7 +47,7 @@ class Menu(Gtk.MenuBar):
# Delete the current log
mitem_delete = Gtk.MenuItem("Delete Log")
mitem_delete.connect("activate", parent.logbook.delete_log, parent)
mitem_delete.connect("activate", parent.logbook.delete_log)
key, mod = Gtk.accelerator_parse("<Control>D")
mitem_delete.add_accelerator("activate", agrp, key, mod, Gtk.AccelFlags.VISIBLE)
subm_logbook.append(mitem_delete)
@ -69,19 +69,19 @@ class Menu(Gtk.MenuBar):
mitem_records.set_submenu(subm_records)
mitem_addrecord = Gtk.MenuItem("Add Record...")
mitem_addrecord.connect("activate", parent.logbook.add_record_callback, parent)
mitem_addrecord.connect("activate", parent.logbook.add_record_callback)
key, mod = Gtk.accelerator_parse("<Control>R")
mitem_addrecord.add_accelerator("activate", agrp, key, mod, Gtk.AccelFlags.VISIBLE)
subm_records.append(mitem_addrecord)
mitem_editrecord = Gtk.MenuItem("Edit Selected Record...")
mitem_editrecord.connect("activate", parent.logbook.edit_record_callback, None, None, parent)
mitem_editrecord.connect("activate", parent.logbook.edit_record_callback, None, None)
key, mod = Gtk.accelerator_parse("<Control>E")
mitem_editrecord.add_accelerator("activate", agrp, key, mod, Gtk.AccelFlags.VISIBLE)
subm_records.append(mitem_editrecord)
mitem_deleterecord = Gtk.MenuItem("Delete Selected Record...")
mitem_deleterecord.connect("activate", parent.logbook.delete_record_callback, parent)
mitem_deleterecord.connect("activate", parent.logbook.delete_record_callback)
key, mod = Gtk.accelerator_parse("Delete")
mitem_deleterecord.add_accelerator("activate", agrp, key, mod, Gtk.AccelFlags.VISIBLE)
subm_records.append(mitem_deleterecord)

Wyświetl plik

@ -0,0 +1,48 @@
#!/usr/bin/env python
# File: new_log_dialog.py
# Copyright (C) 2013 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 re
import calendar
from callsign_lookup import *
class NewLogDialog(Gtk.Dialog):
def __init__(self, root_window):
logging.debug("New NewLogDialog instance created!")
Gtk.Dialog.__init__(self, title="New Log", parent=root_window, flags=Gtk.DialogFlags.DESTROY_WITH_PARENT, buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OK, Gtk.ResponseType.OK))
hbox_temp = Gtk.HBox(spacing=0)
label = Gtk.Label("Log Name:")
label.set_alignment(0, 0.5)
hbox_temp.pack_start(label, False, False, 6)
self.entry = Gtk.Entry()
hbox_temp.pack_start(self.entry, False, False, 6)
self.vbox.pack_start(hbox_temp, False, False, 6)
self.show_all()
return
def get_log_name(self):
return self.entry.get_text()

Wyświetl plik

@ -28,33 +28,13 @@ class Toolbar(Gtk.HBox):
Gtk.HBox.__init__(self, spacing=2)
# New Log
icon = Gtk.Image()
icon.set_from_stock(Gtk.STOCK_NEW, Gtk.IconSize.BUTTON)
button = Gtk.Button()
button.add(icon)
button.set_tooltip_text('New Log')
button.connect("clicked", parent.logbook.new_log)
self.pack_start(button, False, False, 0)
# Delete Log
icon = Gtk.Image()
icon.set_from_stock(Gtk.STOCK_DELETE, Gtk.IconSize.BUTTON)
button = Gtk.Button()
button.add(icon)
button.set_tooltip_text('Delete Log')
button.connect("clicked", parent.logbook.delete_log, parent)
self.pack_start(button, False, False, 0)
self.pack_start(Gtk.SeparatorMenuItem(), False, False, 0)
# Add record
icon = Gtk.Image()
icon.set_from_stock(Gtk.STOCK_ADD, Gtk.IconSize.BUTTON)
button = Gtk.Button()
button.add(icon)
button.set_tooltip_text('Add record')
button.connect("clicked", parent.logbook.add_record_callback, parent)
button.connect("clicked", parent.logbook.add_record_callback)
self.pack_start(button, False, False, 0)
# Edit record
@ -63,7 +43,7 @@ class Toolbar(Gtk.HBox):
button = Gtk.Button()
button.add(icon)
button.set_tooltip_text('Edit record')
button.connect("clicked", parent.logbook.edit_record_callback, None, None, parent)
button.connect("clicked", parent.logbook.edit_record_callback, None, None)
self.pack_start(button, False, False, 0)
# Delete record
@ -72,7 +52,7 @@ class Toolbar(Gtk.HBox):
button = Gtk.Button()
button.add(icon)
button.set_tooltip_text('Delete record')
button.connect("clicked", parent.logbook.delete_record_callback, parent)
button.connect("clicked", parent.logbook.delete_record_callback)
self.pack_start(button, False, False, 0)
self.pack_start(Gtk.SeparatorMenuItem(), False, False, 0)