dwtt/Window.py
2011-03-19 22:22:28 -04:00

416 lines
15 KiB
Python

import gtk
import gobject
from datetime import *
from AboutWindow import AboutWindow
from Config import *
DT_FORMAT = '%Y-%m-%d %H:%M:%S'
class Window:
def __init__(self, progName, ds, config):
self.ds = ds
self.config = config
self.mark = None
if self.config.get('mark') is not None:
self.mark = datetime.strptime(self.config.get('mark'), DT_FORMAT)
self.shown_projects = []
self.taskStart = datetime.now()
self.monday = \
self.taskStart.date() - timedelta(self.taskStart.weekday())
# Top-level Window creation
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title(progName)
self.window.set_geometry_hints()
self.window.connect("delete_event", self.delete_event)
self.window.connect("destroy", self.destroy_event)
self.window.connect("key-press-event", self.window_key_press_event)
# Menu Bar
self.menubar = gtk.MenuBar()
m = gtk.Menu()
mi = gtk.MenuItem('E_xit')
mi.connect("activate", self.destroy_event)
m.append(mi)
mi = gtk.MenuItem('_File')
mi.set_submenu(m)
self.menubar.append(mi)
mi = gtk.MenuItem('_Report')
self.menubar.append(mi)
m = gtk.Menu()
mi = gtk.MenuItem('_About')
mi.connect("activate", self.show_about_event)
m.append(mi)
mi = gtk.MenuItem('_Help')
mi.set_submenu(m)
self.menubar.append(mi)
# Projects Table
self.projects_present = False
self.currProject = 0
self.currTask = 0
self.projects_container = gtk.VBox()
self.updateProjects()
# Bottom Control Bar
self.status_label = gtk.Label()
self.status_label.set_alignment(0.0, 0.5)
self.status_label.set_padding(5, 0)
adjust_button = gtk.Button('Adjust')
in_button = gtk.Button('In')
in_button.connect("clicked", self.in_event)
out_button = gtk.Button('Out')
out_button.connect("clicked", self.out_event)
exit_button = gtk.Button('Exit')
exit_button.connect("clicked", self.destroy_event)
hbox = gtk.HBox()
hbox.pack_start(self.status_label, expand = True)
hbox.pack_start(adjust_button, expand = False)
hbox.pack_start(in_button, expand = False)
hbox.pack_start(out_button, expand = False)
hbox.pack_start(exit_button, expand = False)
vbox = gtk.VBox()
vbox.pack_start(self.menubar, expand = False)
vbox.pack_start(self.projects_container, expand = False)
vbox.pack_end(hbox, expand = False)
vbox.pack_end(gtk.HSeparator(), expand = False)
self.update_status_event()
gobject.timeout_add(1000, self.update_status_event)
self.window.add(vbox)
def updateProjects(self):
def projTblCell(text, pnum):
lbl = gtk.Label(text)
e = gtk.EventBox()
e.add(lbl)
if pnum == self.currProject:
e.set_state(gtk.STATE_SELECTED)
return e
if self.projects_present:
self.projects_container.remove(self.projects_table)
self.projects = self.ds.getProjects()
self.project_hour_labels = {}
projects_to_show = {}
for p in self.shown_projects:
projects_to_show[p] = 1
project_week_hours = self.getProjectWeekHours()
for p in project_week_hours:
projects_to_show[p] = 1
self.tasks = self.ds.getTasks(self.currProject)
self.task_hour_labels = {}
# Header row
col_headers = ('Project',
'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Tot')
rows = 3 + len(projects_to_show) + len(self.tasks)
columns = len(col_headers)
self.projects_table = gtk.Table(rows = rows, columns = columns)
row = 0
for i in range(len(col_headers)):
l = gtk.Label()
l.set_markup('<b>%s</b>' % col_headers[i])
if i == 0:
l.set_alignment(0.0, 0.0)
l.set_size_request(60, -1)
else:
l.set_size_request(40, -1)
self.projects_table.attach(l, i, i + 1, row, row + 1)
row += 1
pnames_to_show = map(lambda x: self.projects[x], projects_to_show)
pnames_to_show.sort()
for pname in pnames_to_show:
pnum = self.getProjectNum(pname)
e = projTblCell(pname, pnum)
e.get_child().set_alignment(0.0, 0.0)
e.add_events(gtk.gdk.BUTTON_PRESS_MASK)
e.connect("button-press-event", self.project_click_event)
self.projects_table.attach(e, 0, 1, row, row + 1)
self.project_hour_labels[pnum] = {}
for i in range(8):
e = projTblCell(" ", pnum)
self.projects_table.attach(e, i + 1, i + 2, row, row + 1)
self.project_hour_labels[pnum][i] = e.get_child()
row += 1
# Totals row
l = gtk.Label()
l.set_markup('<b>Total</b>')
l.set_alignment(0.0, 0.0)
self.projects_table.attach(l, 0, 1, row, row + 1)
row += 1
# Add project row
self.new_project_combobox = gtk.combo_box_entry_new_text()
self.new_project_combobox.set_size_request(250, -1)
self.new_project_combobox.connect("key-release-event",
self.project_key_press_event)
project_names = self.projects.values()
project_names.sort()
for project in project_names:
self.new_project_combobox.append_text(project)
self.add_project_button = gtk.Button('Add')
self.add_project_button.connect('clicked', self.add_project_event)
hbox = gtk.HBox()
hbox.pack_start(self.new_project_combobox, expand = False)
hbox.pack_start(self.add_project_button, expand = False)
self.projects_table.attach(hbox, 0, columns, row, row + 1)
self.projects_table.set_row_spacings(3)
row += 1
# Add Tasks information if we have a current project
if self.currProject > 0:
l = gtk.Label()
l.set_markup('<b>Tasks</b>')
l.set_alignment(0.0, 0.0)
self.projects_table.attach(l, 0, 1, row, row + 1)
row += 1
for tnum in self.tasks:
l = gtk.Label(self.tasks[tnum])
l.set_alignment(0.0, 0.0)
e = gtk.EventBox()
e.add(l)
e.add_events(gtk.gdk.BUTTON_PRESS_MASK)
e.connect("button-press-event", self.task_click_event)
self.projects_table.attach(e, 0, 1, row, row + 1)
self.task_hour_labels[tnum] = {}
for i in range(8):
l = gtk.Label()
self.projects_table.attach(l, i + 1, i + 2, row, row + 1)
self.task_hour_labels[tnum][i] = l
row += 1
# Totals row
l = gtk.Label()
l.set_markup('<b>Total</b>')
l.set_alignment(0.0, 0.0)
self.projects_table.attach(l, 0, 1, row, row + 1)
row += 1
# Add task row
self.new_task_combobox = gtk.combo_box_entry_new_text()
self.new_task_combobox.set_size_request(250, -1)
self.new_task_combobox.connect("key-release-event",
self.task_key_press_event)
task_names = self.tasks.values()
task_names.sort()
for task in task_names:
self.new_task_combobox.append_text(task)
self.add_task_button = gtk.Button('Add')
self.add_task_button.connect('clicked', self.add_task_event)
hbox = gtk.HBox()
hbox.pack_start(self.new_task_combobox, expand = False)
hbox.pack_start(self.add_task_button, expand = False)
self.projects_table.attach(hbox, 0, columns, row, row + 1)
self.projects_table.set_row_spacings(3)
row += 1
self.projects_container.pack_start(self.projects_table, expand = False)
self.projects_present = True
self.projects_table.show_all()
self.updateProjectHours()
self.updateTaskHours()
def updateProjectHours(self):
totals = {}
for day in range(7):
dt = str(self.monday + timedelta(day))
day_hours = self.ds.getProjectDailyHours(dt)
print day
print day_hours
for p in day_hours:
if p in self.project_hour_labels:
hrs = self.hoursFromSeconds(day_hours[p])
if not p in totals:
totals[p] = 0
totals[p] += float(hrs)
self.project_hour_labels[p][day].set_text(hrs)
print totals
for p in totals:
if p in self.project_hour_labels:
self.project_hour_labels[p][7].set_text(str(totals[p]))
def updateTaskHours(self):
totals = {}
for day in range(7):
dt = str(self.monday + timedelta(day))
day_hours = self.ds.getDailyHours(dt)
if self.currProject in day_hours:
for t in day_hours[self.currProject]:
if t in self.task_hour_labels:
hrs = self.hoursFromSeconds(
day_hours[self.currProject][t])
if not t in totals:
totals[t] = 0
totals[t] += float(hrs)
self.task_hour_labels[t][day].set_text(hrs)
for t in totals:
if t in self.task_hour_labels:
self.task_hour_labels[t][7].set_text(str(totals[t]))
def getProjectWeekHours(self):
proj_week_hours = {}
for day in range(7):
dt = str(self.monday + timedelta(day))
day_hours = self.ds.getProjectDailyHours(dt)
for p in day_hours:
if not p in proj_week_hours:
proj_week_hours[p] = 0
proj_week_hours[p] += float(day_hours[p])
return proj_week_hours
def hoursFromSeconds(self, secs):
if secs < 0.01 * 60 * 60:
secs = 0.01 * 60 * 60;
return "%.2f" % (secs / 60.0 / 60.0)
def main(self):
self.window.show_all()
gtk.main()
def getProjectNum(self, pname):
for p in self.projects:
if self.projects[p] == pname:
return p
return 0
def getTaskNum(self, tname):
for t in self.tasks:
if self.tasks[t] == tname:
return t
return 0
def add_project_event(self, button, data=None):
pname = self.new_project_combobox.get_active_text()
if pname in self.projects.values():
pnum = self.getProjectNum(pname)
elif len(pname.strip()):
pnum = self.ds.createProject(pname)
else:
pnum = 0
if pnum:
self.shown_projects.append(pnum)
self.project_select_event(pname)
self.updateProjects()
def project_key_press_event(self, widget, event, data=None):
if event.keyval == gtk.gdk.keyval_from_name("Return"):
self.add_project_event(1)
return True
return False
def project_click_event(self, ebox, event, data=None):
lbl = ebox.get_child()
if lbl:
if event.button == 1:
return self.project_select_event(lbl.get_text())
elif event.button == 3:
print "Right Click"
return True
return False
def project_select_event(self, projectName):
pnum = self.getProjectNum(projectName)
if pnum:
print "Selected project '%s' (%d)" % (projectName, pnum)
self.currProject = pnum
self.updateProjects()
return True
return False
def add_task_event(self, button, data=None):
tname = self.new_task_combobox.get_active_text()
if tname in self.tasks.values():
tnum = self.getTaskNum(tname)
elif len(tname.strip()):
tnum = self.ds.createTask(self.currProject, tname)
else:
tnum = 0
if tnum:
self.tasks = self.ds.getTasks(self.currProject)
self.task_select_event(tname)
self.updateProjects()
def task_key_press_event(self, widget, event, data=None):
if event.keyval == gtk.gdk.keyval_from_name("Return"):
self.add_task_event(1)
return True
return False
def task_click_event(self, ebox, event, data=None):
lbl = ebox.get_child()
if lbl:
if event.button == 1:
return self.task_select_event(lbl.get_text())
elif event.button == 3:
print "Right Click"
return True
return False
def task_select_event(self, taskName):
tnum = self.getTaskNum(taskName)
if tnum:
self.updateCurrTaskHours()
self.currTask = tnum
print "Selected task '%s' (%d)" % (taskName, tnum)
self.updateProjects()
return True
return False
def updateCurrTaskHours(self):
nst = datetime.now()
delta = nst - self.taskStart
self.ds.addTaskHours(self.currTask, nst.date(), delta.seconds)
self.taskStart = nst
def delete_event(self, widget, event, data=None):
return False
def destroy_event(self, widget, data=None):
# could be called by window or exit button
# save any config changes
if self.mark is not None:
self.config.set('mark', self.mark.strftime(DT_FORMAT))
else:
self.config.clear('mark')
self.config.write()
gtk.main_quit()
def window_key_press_event(self, widget, event, data=None):
if event.keyval == gtk.gdk.keyval_from_name("Escape"):
gtk.main_quit()
def show_about_event(self, widget, data=None):
about_window = AboutWindow()
def update_status_event(self):
if self.mark is not None:
td = datetime.now() - self.mark
days = td.days
hours = td.seconds / 60 / 60
mins = (td.seconds - hours * 60 * 60) / 60
secs = (td.seconds - hours * 60 * 60 - mins * 60)
# TODO: smarter / shorter status text
self.status_label.set_text('Mark: %s; Elapsed: %dd %dh %dm %ds' % \
(self.mark.strftime(DT_FORMAT), days, hours, mins, secs))
else:
self.status_label.set_text('No mark set')
return True
def in_event(self, widget, data=None):
if self.mark is None:
self.mark = datetime.now()
def out_event(self, widget, data=None):
self.mark = None