# -*- coding: utf-8 -*- import wx import wx.lib.mixins.listctrl as listmix import wx.lib.masked as masked import sys import os from Hercules.workoutsql import * from Hercules.exercisesql import * from Hercules.globals import * class WorkoutsTab(wx.Panel, WorkoutSQL): """Create workouts panel for Hercules GUI""" def __init__(self, parent): # Create panel wx.Panel.__init__(self, parent) # Create workout database WorkoutSQL.__init__(self, os.path.join(hercules_dir, 'workout.db')) # create main sizer self.sizer = wx.BoxSizer(wx.VERTICAL) # create control widget self.ctrl_sizer = wx.BoxSizer(wx.HORIZONTAL) # create 'previous workout' button bmp = wx.ArtProvider_GetBitmap(wx.ART_GO_BACK, size=(16, 16)) self.prev_btn = wx.BitmapButton(self, bitmap=bmp) self.prev_btn.SetToolTipString(_("Previous workout")) self.ctrl_sizer.Add(self.prev_btn, 0) # Bind event self.Bind(wx.EVT_BUTTON, self.on_previous, self.prev_btn) # create date picker control self.dpc = wx.DatePickerCtrl(self, size=(120,-1), style=wx.DP_DROPDOWN | wx.DP_SHOWCENTURY) self.Bind(wx.EVT_DATE_CHANGED, self.on_date_change, self.dpc) self.ctrl_sizer.Add(self.dpc, 0, wx.EXPAND | wx.RIGHT | wx.LEFT, 5) # create 'next workout' button bmp = wx.ArtProvider_GetBitmap(wx.ART_GO_FORWARD, size=(16, 16)) self.next_btn = wx.BitmapButton(self, bitmap=bmp) self.next_btn.SetToolTipString(_("Next workout")) self.ctrl_sizer.Add(self.next_btn, 0) # Bind event self.Bind(wx.EVT_BUTTON, self.on_next, self.next_btn) # create 'Copy workout from...' button bmp = wx.ArtProvider_GetBitmap(wx.ART_COPY, size=(16, 16)) self.copy_workout_btn = wx.BitmapButton(self, bitmap=bmp) self.copy_workout_btn.SetToolTipString(_("Copy workout from...")) self.ctrl_sizer.Add(self.copy_workout_btn, 0, wx.RIGHT | wx.LEFT, 5) # Bind event self.Bind(wx.EVT_BUTTON, self.on_copy_workout, self.copy_workout_btn) # init variable self.copy_list = [] # create 'Workout Combo Box' self.cb = wx.ComboBox(self, choices=self.copy_list, style=wx.CB_DROPDOWN | wx.CB_READONLY) self.cb.SetToolTipString(_("Select workout to copy")) self.ctrl_sizer.Add(self.cb, 0, wx.EXPAND) # init workout list # create 'Delete workout' button bmp = wx.ArtProvider_GetBitmap(wx.ART_DELETE, size=(16, 16)) self.del_workout_btn = wx.BitmapButton(self, bitmap=bmp) self.del_workout_btn.SetToolTipString(_("Delete workout")) self.ctrl_sizer.Add(self.del_workout_btn, 0, wx.RIGHT | wx.LEFT, 5) # Bind event self.Bind(wx.EVT_BUTTON, self.on_delete_workout, self.del_workout_btn) # create workout list self.list_sizer = wx.BoxSizer(wx.HORIZONTAL) self.workout_list = WorkoutList(self) self.list_sizer.Add(self.workout_list, 2, wx.EXPAND) # Create sizer for control buttons self.btn_sizer = wx.BoxSizer(wx.HORIZONTAL) # Create 'Add', 'Edit' and 'Delete' buttons. # Also set up tool tip for this buttons. self.add_btn = wx.Button(self, label=_("Add")) self.add_btn.SetToolTipString(_("Add new set")) self.edit_btn = wx.Button(self, label=_("Edit")) self.edit_btn.SetToolTipString(_("Edit set")) self.del_btn = wx.Button(self, label=_("Delete")) self.del_btn.SetToolTipString(_("Delete set")) # set event binding self.Bind(wx.EVT_BUTTON, self.on_add, self.add_btn) self.Bind(wx.EVT_BUTTON, self.on_edit, self.edit_btn) self.Bind(wx.EVT_BUTTON, self.on_delete, self.del_btn) # Insert buttons into sizer self.btn_sizer.Add(self.add_btn, 0, wx.RIGHT, 5) self.btn_sizer.Add(self.edit_btn, 0, wx.RIGHT, 5) self.btn_sizer.Add(self.del_btn, 0, wx.RIGHT, 5) # get current date self.current_date = self.dpc.GetValue() date_id = self.current_date.FormatISODate() self.update_workout_list(date_id) # Update workout days self.update_workout_days() # Set up master sizer self.sizer.Add(self.ctrl_sizer, 0, wx.ALL, 5) self.sizer.Add(self.list_sizer, 1, wx.EXPAND | wx.ALL, 5) line = wx.StaticLine(self, style=wx.LI_HORIZONTAL) self.sizer.Add(line, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5) self.sizer.Add(self.btn_sizer, 0, wx.ALL, 5) self.SetAutoLayout(True) self.SetSizer(self.sizer) def on_date_change(self, event): """On date change event handler""" selected_date = self.dpc.GetValue() date_id = selected_date.FormatISODate() self.update_workout_list(date_id) def on_add(self, event): """Add set into workout list and database""" new_set = AddNewSetDlg(self) if new_set.get_exercise_title() != None: # get date_id selected_date = self.dpc.GetValue() date_id = selected_date.FormatISODate() # insert into database self.insert_set(new_set.get_exercise_title(), new_set.get_exercise_reps(), new_set.get_exercise_weight(), date_id) self.update_workout_list(date_id) self.update_workout_days() def on_edit(self, event): """Edit selected set""" # Get title of selected exercise exercise_title = self.workout_list.get_exercise_title() # Get reps of selected exercise exercise_reps = self.workout_list.get_exercise_reps() # Get weight of selected exercise exercise_weight = self.workout_list.get_exercise_weight() # Edit selected exercise if exercise_title != None: edit_set = EditSetDlg(self, exercise_title, exercise_reps, exercise_weight) # If exercise_title is not None, replace old exercise with new one if edit_set.get_exercise_title() != None: # get date_id selected_date = self.dpc.GetValue() date_id = selected_date.FormatISODate() # update set in the database item = self.workout_list._get_selected_indices() self.update_set(self.workout[item[0]][0], edit_set.get_exercise_title(), edit_set.get_exercise_reps(), edit_set.get_exercise_weight(), date_id) self.update_workout_list(date_id) self.update_workout_days() def on_delete(self, event): """Delete selected set""" # Get title of selected exercise exercise = self.workout_list.get_exercise_title() if exercise != None: msg = _("Are you sure you want delele this set?") dlg = wx.MessageDialog(self, msg, _("Delete set"), wx.YES_NO | wx.ICON_WARNING) ret_val = dlg.ShowModal() if ret_val == wx.ID_YES: # get date_id selected_date = self.dpc.GetValue() date_id = selected_date.FormatISODate() # delete selected set from db item = self.workout_list._get_selected_indices() self.delete_set(self.workout[item[0]][0], date_id) self.update_workout_list(date_id) self.update_workout_days() elif ret_val == wx.ID_NO : pass dlg.Destroy() def on_previous(self, event): """Switch to previous workout""" index = len(self.workout_days) - 1 curr_date = self.dpc.GetValue() while index >= 0: date = wx.DateTime() date.ParseFormat(self.workout_days[index], '%Y-%m-%d') if date.IsEarlierThan(curr_date): self.dpc.SetValue(date) date_id = date.FormatISODate() self.update_workout_list(date_id) break else: index = index - 1 def on_next(self, event): """Switch to next workout""" curr_date = self.dpc.GetValue() index = 0 while index <= len(self.workout_days) - 1: date = wx.DateTime() date.ParseFormat(self.workout_days[index], '%Y-%m-%d') if date.IsLaterThan(curr_date): self.dpc.SetValue(date) date_id = date.FormatISODate() self.update_workout_list(date_id) break else: index = index + 1 def on_delete_workout(self, event): """Delete selected workout""" # get date_id selected_date = self.dpc.GetValue() date_id = selected_date.FormatISODate() if self._is_workout_exist(date_id): msg = _("Are you sure you want delele this workout?") dlg = wx.MessageDialog(self, msg, _("Delete workout"), wx.YES_NO | wx.ICON_WARNING) ret_val = dlg.ShowModal() if ret_val == wx.ID_YES: # delete workout from db self.delete_workout(date_id) self.update_workout_list(date_id) self.update_workout_days() elif ret_val == wx.ID_NO : pass dlg.Destroy() else: msg = _("Nothing to delete!") dlg = wx.MessageDialog(self, msg, _("Error"), wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() def on_copy_workout(self, event): """Copy workout from...""" # get slected item from combo box item = self.cb.GetSelection() # get date_id selected_date = self.dpc.GetValue() date_id = selected_date.FormatISODate() if item != -1: if self._is_workout_exist(date_id): msg = _("Workout exist!") dlg = wx.MessageDialog(self, msg, _("Error"), wx.OK | wx.ICON_ERROR) dlg.ShowModal() else: copy_date_id = self.workout_days[item] # load workout for copying tmp_workout = self.load_workout(copy_date_id) # insert into database for (id, exercise, reps, weight) in tmp_workout: self.insert_set(exercise, reps, weight, date_id) self.update_workout_list(date_id) self.update_workout_days() def update_workout_list(self, date_id): """Update workout list""" # init workout list if self._is_workout_exist(date_id): self.workout = self.load_workout(date_id) else: self.workout = [] # update workout_list self.workout_list.update_list(self.workout) def update_workout_days(self): """Update workout days""" # init list of workout days self.workout_days = self.load_workout_days() # sort workout days self.workout_days.sort() # create copy list from workout days list self.copy_list = [] index = 0 while index <= len(self.workout_days) - 1: date = wx.DateTime() date.ParseFormat(self.workout_days[index], '%Y-%m-%d') self.copy_list.append(date.FormatDate()) index = index + 1 self.copy_list.reverse() # update choices in combo box self.cb.Clear() for item in self.copy_list: self.cb.Append(item) class WorkoutList(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): """Workout list class""" def __init__(self, parent): # Create list control with mixins wx.ListCtrl.__init__(self, parent, style=wx.LC_REPORT | wx.LC_SINGLE_SEL) listmix.ListCtrlAutoWidthMixin.__init__(self) # Set up colums for workout list self.InsertColumn(0, _("Exercise")) self.InsertColumn(1, _("Reps")) self.InsertColumn(2, _("Weight")) # Set column width self.SetColumnWidth(0, 250) self.SetColumnWidth(1, 45) self.SetColumnWidth(2, wx.LIST_AUTOSIZE) def update_list(self, workout): """Updating workout list""" self.DeleteAllItems() # Load workout exercises into list if workout != []: for item in workout: index = self.InsertStringItem(sys.maxint, item[1]) self.SetStringItem(index, 0, item[1]) self.SetStringItem(index, 1, item[2]) self.SetStringItem(index, 2, item[3]) def get_exercise_title(self): """Return title of selected exercise""" # Get selected item item = self._get_selected_indices() if item != []: exercise_title = self._get_column_text(item[0], 0) else: exercise_title = None return exercise_title def get_exercise_reps(self): """Return reps of selected exercise""" # Get selected item item = self._get_selected_indices() if item != []: exercise_reps = self._get_column_text(item[0], 1) else: exercise_reps = None return exercise_reps def get_exercise_weight(self): """Return weight of selected exercise""" # Get selected item item = self._get_selected_indices() if item != []: exercise_weight = self._get_column_text(item[0], 2) else: exercise_weight = None return exercise_weight def _get_selected_indices(self, state = wx.LIST_STATE_SELECTED): """get indices of selected items""" indices = [] found = 1 lastFound = -1 while found: index = self.GetNextItem(lastFound, wx.LIST_NEXT_ALL, state) if index == -1: break else: lastFound = index indices.append(index) return indices def _get_column_text(self, index, col): item = self.GetItem(index, col) return item.GetText() class SetDlg(wx.Dialog, ExerciseSQL): """Working set dialog class""" def __init__(self, parent, title): # Create dialog box wx.Dialog.__init__(self, parent, title=title) ExerciseSQL.__init__(self, os.path.join(hercules_dir, 'exercises.db')) # Sizer that holds all widgets in dialog box self.sizer = wx.BoxSizer(wx.VERTICAL) # Create sizer with exercise combobox self.exercise_sizer = wx.BoxSizer(wx.HORIZONTAL) # Create exercise label label = wx.StaticText(self, label=_("Exercise: ")) # Create exercise combobox self.exercises_list = self.load_exercises('titles') self.exercise_box = wx.ComboBox(self, size=(150,-1), choices=self.exercises_list, style=wx.CB_DROPDOWN | wx.CB_READONLY) self.exercise_sizer.Add(label, 0, wx.ALIGN_CENTER_VERTICAL) self.exercise_sizer.Add(self.exercise_box, 0, wx.ALIGN_CENTER_VERTICAL) # Create number of reps box self.reps_sizer = wx.BoxSizer(wx.HORIZONTAL) # Create label label = wx.StaticText(self, label=_("Number of reps: ")) # Create reps spin box self.reps_box = wx.SpinCtrl(self, min=1, max=30) self.reps_sizer.Add(label, 0, wx.ALIGN_CENTER_VERTICAL) self.reps_sizer.Add(self.reps_box, 1, wx.EXPAND | wx.ALIGN_CENTER_VERTICAL) # Create weight box self.weight_sizer = wx.BoxSizer(wx.HORIZONTAL) # Create label label = wx.StaticText(self, label=_("Weight: ")) # Create weight input box self.weight_box = masked.NumCtrl(self, integerWidth=3, fractionWidth=2) self.weight_sizer.Add(label, 0, wx.ALIGN_CENTER_VERTICAL) self.weight_sizer.Add(self.weight_box, 0, wx.ALIGN_CENTER_VERTICAL) self.btn_sizer = wx.StdDialogButtonSizer() # The 'OK' and 'Cancel' buttons btn = wx.Button(self, wx.ID_OK) self.btn_sizer.AddButton(btn) btn = wx.Button(self, wx.ID_CANCEL) self.btn_sizer.AddButton(btn) self.btn_sizer.Realize() # Set up the master sizer self.sizer.Add(self.exercise_sizer, 0, wx.ALL, 5) self.sizer.Add(self.reps_sizer, 1, wx.GROW | wx.ALL, 5) self.sizer.Add(self.weight_sizer, 0, wx.ALL, 5) # Insert static line between controls line = wx.StaticLine(self, style=wx.LI_HORIZONTAL) self.sizer.Add(line, 0, wx.EXPAND | wx.LEFT | wx.RIGHT, 5) self.sizer.Add(self.btn_sizer, 0, wx.ALIGN_RIGHT | wx.ALL, 5) self.SetSizer(self.sizer) self.sizer.Fit(self) class AddNewSetDlg(SetDlg): """Add new set dialog box""" def __init__(self, parent): SetDlg.__init__(self, parent, title=_("Add new set")) # init variables self.exercise_title = None self.exercise_reps = None self.exercise_weight = None # if exercises list is not empty if self.exercises_list != []: ret_val = self.ShowModal() if ret_val == wx.ID_OK: self.exercise_title = self.exercise_box.GetValue() self.exercise_reps = str(self.reps_box.GetValue()) self.exercise_weight = str(self.weight_box.GetValue()) elif ret_val == wx.ID_CANCEL: pass else: dlg = wx.MessageDialog(parent, _("Exercises list is empty!"), _("Error"), wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() self.Destroy() def get_exercise_title(self): return self.exercise_title def get_exercise_reps(self): return self.exercise_reps def get_exercise_weight(self): return self.exercise_weight class EditSetDlg(SetDlg): """Edit set dialog box""" def __init__(self, parent, exercise_title, exercise_reps, exercise_weight): SetDlg.__init__(self, parent, title=_("Edit set")) # Set default values self.exercise_box.SetValue(exercise_title) self.reps_box.SetValue(int(exercise_reps)) self.weight_box.SetValue(exercise_weight) ret_val = self.ShowModal() if ret_val == wx.ID_OK: self.exercise_title = self.exercise_box.GetValue() self.exercise_reps = str(self.reps_box.GetValue()) self.exercise_weight = str(self.weight_box.GetValue()) if self.exercise_title == '': self.exercise_title = None self.exercise_reps = None self.exercise_weight = None elif ret_val == wx.ID_CANCEL: self.exercise_title = None self.exercise_reps = None self.exercise_weight = None self.Destroy() def get_exercise_title(self): return self.exercise_title def get_exercise_reps(self): return self.exercise_reps def get_exercise_weight(self): return self.exercise_weight