def __init__(self, set_projecttype_cb, parent): '''Creates new project window''' super(NewProjectWin, self).__init__(Gtk.WindowType.TOPLEVEL) self.modify_bg(Gtk.StateType.NORMAL, fstimer.gui.bgcolor) self.set_transient_for(parent) self.set_modal(True) self.set_title('fsTimer - New project') self.set_position(Gtk.WindowPosition.CENTER) self.set_border_width(20) self.connect('delete_event', lambda b, jnk_unused: self.hide()) # Now create the vbox. vbox = Gtk.VBox(False, 10) self.add(vbox) # Now add the text. label_0 = Gtk.Label('Enter a name for the new project.\nOnly letters, numbers, and underscore.') # And an error, if needed.. self.label_1 = Gtk.Label() self.label_1.set_line_wrap(True) # And the text entry self.entry = Gtk.Entry() self.entry.set_max_length(32) # Select a file to import, if desired label_2 = Gtk.Label('Select a project to import settings from (optional)') # A combobox to select the project combobox = Gtk.ComboBoxText() projectlist = ["-- No import --"] rootdir = normpath(join(dirname(abspath(__file__)),'../../')) projectlist.extend([i for i in os.listdir(rootdir) if os.path.isdir(join(rootdir,i)) and os.path.exists(join(rootdir,i+'/'+i+'.reg'))]) #List the folders in pwd that contain a .reg registration file projectlist.sort() for project in projectlist: combobox.append_text(project) combobox.set_active(0) # And an hbox with 2 buttons hbox_1 = Gtk.HBox(False, 0) btnCANCEL = GtkStockButton('close',"Close") btnCANCEL.connect('clicked', lambda btn: self.hide()) alignCANCEL = Gtk.Alignment.new(0, 0, 0, 0) alignCANCEL.add(btnCANCEL) btnNEXT = GtkStockButton('forward',"Next") btnNEXT.connect('clicked', self.nextClicked, set_projecttype_cb, projectlist, combobox) alignNEXT = Gtk.Alignment.new(1, 0, 1, 0) alignNEXT.add(btnNEXT) btnNEXT.set_sensitive(False) # And populate self.entry.connect("changed", self.lock_btn_title, btnNEXT) hbox_1.pack_start(alignCANCEL, True, True, 0) hbox_1.pack_start(alignNEXT, False, False, 0) vbox.pack_start(label_0, False, False, 0) vbox.pack_start(self.entry, False, False, 0) vbox.pack_start(self.label_1, False, False, 0) vbox.pack_start(label_2, False, False, 0) vbox.pack_start(combobox, False, False, 0) vbox.pack_start(hbox_1, False, False, 0) self.show_all()
def __init__(self, load_project_cb, create_project_cb): '''Builds and display the introduction window''' super(IntroWin, self).__init__(Gtk.WindowType.TOPLEVEL) self.modify_bg(Gtk.StateType.NORMAL, fstimer.gui.bgcolor) icon_fname = normpath(join(dirname(abspath(__file__)),'../data/icon.png')) self.set_icon_from_file(icon_fname) self.set_title('fsTimer') self.set_position(Gtk.WindowPosition.CENTER) self.set_border_width(20) self.connect('delete_event', Gtk.main_quit) # Create the vbox that will contain everything vbox = Gtk.VBox(False, 10) self.add(vbox) # Main logo logo = Gtk.Image() logo.set_from_file(normpath(join(dirname(abspath(__file__)),'../data/fstimer_logo.png'))) # Welcome text label0 = Gtk.Label(label='') label = Gtk.Label('Select an existing project, or begin a new project.') # A combobox to select the project combobox = Gtk.ComboBoxText() projectlist = [' -- Select an existing project --'] rootdir = normpath(join(dirname(abspath(__file__)),'../../')) projectlist.extend([i for i in os.listdir(rootdir) if os.path.isdir(join(rootdir,i)) and os.path.exists(join(rootdir,i+'/'+i+'.reg'))]) #List the folders in pwd that contain a .reg registration file projectlist.sort() for project in projectlist: combobox.append_text(project) combobox.set_active(0) #An hbox for the buttons. hbox = Gtk.HBox(False, 0) #And build the buttons btnNEW = GtkStockButton('new','New') btnNEW.connect('clicked', create_project_cb) btnOK = GtkStockButton('ok','OK') btnOK.connect('clicked', load_project_cb, combobox, projectlist) btnOK.set_sensitive(False) #Set combobox to lock btnOK, so we can't press OK until we have selected a project combobox.connect('changed', self.lock_btnOK, combobox, btnOK) btnCANCEL = GtkStockButton('close','Close') btnCANCEL.connect('clicked', Gtk.main_quit) #Now fill the hbox. hbox.pack_start(btnCANCEL, True, True, 0) hbox.pack_start(btnNEW, True, True, 50) hbox.pack_start(btnOK, True, True, 0) #Now build the vbox vbox.pack_start(logo, False, False, 0) vbox.pack_start(label0, False, False, 0) vbox.pack_start(label, False, False, 0) vbox.pack_start(combobox, False, False, 0) vbox.pack_start(hbox, False, False, 0) #And show everything. self.show_all()
class RegistrationWin(Gtk.Window): '''Handling of the window dedicated to registration''' def __init__(self, path, fields, fieldsdic, prereg, projecttype, save_registration_cb, parent_win=None, autosave=True, save_label=''): '''Builds and display the registration window''' super(RegistrationWin, self).__init__(Gtk.WindowType.TOPLEVEL) if parent_win: self.set_transient_for(parent_win) self.set_modal(True) self.fields = fields self.fieldsdic = fieldsdic self.prereg = prereg self.ids = set() self.projecttype = projecttype self.save_registration_cb = save_registration_cb self.autosave = autosave self.editreg_win = None self.editregfields = None # First we define the registration model. # We will setup a liststore that is wrapped in a treemodelfilter # that is wrapped in a treemodelsort that is put in a treeview # that is put in a scrolled window. Eesh. self.regmodel = Gtk.ListStore(*[str for field in self.fields]) self.modelfilter = self.regmodel.filter_new() self.modelfiltersorted = Gtk.TreeModelSort(self.modelfilter) self.treeview = Gtk.TreeView() # Now we define each column in the treeview for (colid, field) in enumerate(fields): column = Gtk.TreeViewColumn(field, Gtk.CellRendererText(), text=colid) column.set_sort_column_id(colid) self.treeview.append_column(column) # Now we populate the model with the pre-registration info, if any for reg in prereg: self.regmodel.append([reg[field] for field in fields]) if reg['ID']: self.ids.add(reg['ID']) # This is the string that we filter based on. self.searchstr = '' self.modelfilter.set_visible_func(self.visible_filter) self.treeview.set_model(self.modelfiltersorted) self.treeview.set_enable_search(False) # Now let us actually build the window self.modify_bg(Gtk.StateType.NORMAL, fstimer.gui.bgcolor) fname = os.path.abspath( os.path.join( os.path.dirname(os.path.abspath(__file__)), '../data/icon.png')) self.set_icon_from_file(fname) self.set_title('fsTimer - ' + os.path.basename(path)) self.set_position(Gtk.WindowPosition.CENTER) self.connect('delete_event', lambda b, jnk: self.close_clicked(jnk)) self.set_border_width(10) self.set_size_request(850, 450) #Now the filter entrybox filterbox = Gtk.HBox(False, 8) filterbox.pack_start(Gtk.Label('Filter by ', True, True, 0), False, False, 0) self.filter_combo = Gtk.ComboBoxText() for field in self.fields: self.filter_combo.append_text(field) self.filter_combo.set_active(0) filterbox.pack_start(self.filter_combo, False, False, 0) filterbox.pack_start(Gtk.Label(':'), False, False, 0) self.filterentry = Gtk.Entry() self.filterentry.set_max_length(40) self.filterentry.connect('changed', self.filter_apply) self.filterbtnCLEAR = GtkStockButton('clear',"Clear") self.filterbtnCLEAR.connect('clicked', self.filter_clear) self.filterbtnCLEAR.set_sensitive(False) filterbox.pack_start(self.filterentry, False, False, 0) filterbox.pack_start(self.filterbtnCLEAR, False, False, 0) # Now the scrolled window that contains the treeview regsw = Gtk.ScrolledWindow() regsw.set_shadow_type(Gtk.ShadowType.ETCHED_IN) regsw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) regsw.add(self.treeview) # And a message that says if we have saved or not. self.regstatus = Gtk.Label(label=save_label) # Some boxes for all the stuff on the left regvbox1 = Gtk.VBox(False, 8) regvbox1.pack_start(filterbox, False, False, 0) regvbox1.pack_start(regsw, True, True, 0) regvbox1.pack_start(self.regstatus, False, False, 0) vbox1align = Gtk.Alignment.new(0, 0, 1, 1) vbox1align.add(regvbox1) # And boxes/table for the buttons on the right regtable = Gtk.Table(2, 1, False) regtable.set_row_spacings(5) regtable.set_col_spacings(5) regtable.set_border_width(5) btnEDIT = GtkStockButton('edit',"Edit") btnEDIT.connect('clicked', self.edit_clicked) btnREMOVE = GtkStockButton('remove',"Remove") btnREMOVE.connect('clicked', self.rm_clicked) btnNEW = GtkStockButton('new',"New") btnNEW.connect('clicked', self.new_clicked) btnSAVE = GtkStockButton('save',"Save") btnSAVE.connect('clicked', self.save_clicked) btnOK = GtkStockButton('close',"Close") btnOK.connect('clicked', self.close_clicked) vsubbox = Gtk.VBox(False, 8) vsubbox.pack_start(btnSAVE, False, False, 0) regvspacer = Gtk.Alignment.new(1, 1, 0, 0) regvspacer.add(vsubbox) regtable.attach(regvspacer, 0, 1, 1, 2) regvbox2 = Gtk.VBox(False, 8) regvbox2.pack_start(btnNEW, False, False, 0) regvbox2.pack_start(btnEDIT, False, False, 0) regvbox2.pack_start(btnREMOVE, False, False, 30) regvbalign = Gtk.Alignment.new(1, 0, 0, 0) regvbalign.add(regvbox2) regtable.attach(regvbalign, 0, 1, 0, 1) #Now we pack everything together reghbox = Gtk.HBox(False, 8) reghbox.pack_start(vbox1align, True, True, 0) reghbox.pack_start(regtable, False, False, 0) # Add in the close button on the bottom vbox_all = Gtk.VBox(False, 0) vbox_all.pack_start(reghbox, True, True, 0) donespacer = Gtk.Alignment.new(0, 0, 0, 0) donespacer.add(btnOK) vbox_all.pack_start(donespacer, False, False, 5) self.add(vbox_all) # And show. self.show_all() def visible_filter(self, model, titer, data): '''This is the filter function. It checks if self.searchstr is contained in column matching self.filter_combo case insensitive''' if self.searchstr: col_idx = self.filter_combo.get_active() if not model.get_value(titer, col_idx): return False else: return self.searchstr.lower() in model.get_value(titer, col_idx).lower() else: return True def filter_apply(self, jnk_unused): ''' handles modification of the content of the filter box sets self.searchstr to the current entrybox contents and refilter''' self.searchstr = self.filterentry.get_text() self.filterbtnCLEAR.set_sensitive(True) self.modelfilter.refilter() def filter_clear(self, jnk_unused): '''handles clearing of the filter box. Clears self.searchstr and refilter''' self.searchstr = '' self.filterentry.set_text('') self.filterbtnCLEAR.set_sensitive(False) self.modelfilter.refilter() def edit_clicked(self, jnk_unused): '''handles click on the 'edit' button on the registration window''' selection = self.treeview.get_selection() treeiter = selection.get_selected()[1] # if no selection, do nothing. if treeiter: # Grab the current information. current_info = {} for (colid, field) in enumerate(self.fields): current_info[field] = self.modelfiltersorted.get_value(treeiter, colid) # Find where this is in self.prereg preregiter = self.prereg.index(current_info) # Generate the window self.edit_registration(treeiter, preregiter, current_info) def rm_clicked(self, jnk_unused): '''Handling click on the 'remove' button of the registration window Throws up an 'are you sure' dialog box, and delete if yes''' selection = self.treeview.get_selection() treeiter = selection.get_selected()[1] # if nothing is selected, do nothing. if treeiter: rmreg_dialog = MsgDialog(self, 'warning', ['yes', 'no'], 'Really delete?', 'Are you sure you want to delete this entry?\nThis cannot be undone.') rmreg_dialog.set_default_response(Gtk.ResponseType.NO) response = rmreg_dialog.run() rmreg_dialog.destroy() if response == Gtk.ResponseType.YES: # Grab the current information. current_info = {} for (colid, field) in enumerate(self.fields): current_info[field] = self.modelfiltersorted.get_value(treeiter, colid) # Find where this is in self.prereg preregiter = self.prereg.index(current_info) # converts the treeiter from sorted to filter to model, and remove self.regmodel.remove(self.modelfilter.convert_iter_to_child_iter(self.modelfiltersorted.convert_iter_to_child_iter(treeiter))) try: self.ids.remove(current_info['ID']) except: pass self.prereg.pop(preregiter) # The latest stuff has no longer been saved. self.regstatus.set_markup('') def new_clicked(self, jnk_unused): '''Handles click on the 'new' button on the registration window Creates the editreg window with a None treeiter and clear initial values.''' self.edit_registration(None, None, None) def save_clicked(self, jnk_unused): '''Handles click on the 'save' button on the registration window. We do a json dump of self.prereg''' filename, success = self.save_registration_cb() if success: self.regstatus.set_markup('<span color="blue">Registration saved to %s</span>' % filename) return True else: self.regstatus.set_markup('<span color="red">Registration NOT saved: %s</span>' % filename) return False def close_clicked(self, jnk_unused): '''Handles click on the 'close' button on the registration window. Throws up a 'do you want to save' dialog, and close the window''' okreg_dialog = MsgDialog(self, 'question', ['yes', 'no'], 'Save?', 'Do you want to save before finishing?\nUnsaved data will be lost.') okreg_dialog.set_default_response(Gtk.ResponseType.YES) response = okreg_dialog.run() okreg_dialog.destroy() if response == Gtk.ResponseType.YES: # this will save save_res = self.save_clicked(None) if not save_res: return self.hide() # Clear the file setting from pre-reg, in case pre-reg is # re-run without selecting a file del self.prereg[:] def edit_registration(self, treeiter, preregiter, current_info): '''handles creation/modification of a registration entry. Converts the treeiter from the treemodelsort to the liststore.''' if treeiter: treeiter = self.modelfilter.convert_iter_to_child_iter(self.modelfiltersorted.convert_iter_to_child_iter(treeiter)) # Define the window self.editreg_win = Gtk.Window(Gtk.WindowType.TOPLEVEL) self.editreg_win.modify_bg(Gtk.StateType.NORMAL, fstimer.gui.bgcolor) self.editreg_win.set_title('Registration entry') self.editreg_win.set_transient_for(self) self.editreg_win.set_modal(True) self.editreg_win.set_position(Gtk.WindowPosition.CENTER) self.editreg_win.connect('delete_event', lambda b, jnk_unused: self.editreg_win.hide()) self.editreg_win.set_border_width(10) #An hbox for the buttons editreghbox = Gtk.HBox(False, 8) editregbtnOK = GtkStockButton('ok',"OK") editregbtnCANCEL = GtkStockButton('close',"Cancel") editregbtnCANCEL.connect('clicked', lambda b: self.editreg_win.hide()) editreghbox.pack_start(editregbtnOK, False, False, 5) editreghbox.pack_start(editregbtnCANCEL, False, False, 5) # fill in current_info if available. self.editregfields = {} for field in self.fields: # Determine which type of entry is appropriate, create it and fill it. # Entrybox if self.fieldsdic[field]['type'] in ['entrybox', 'entrybox_int']: self.editregfields[field] = Gtk.Entry() self.editregfields[field].set_max_length(self.fieldsdic[field]['max']) if current_info: self.editregfields[field].set_text(current_info[field]) # Combobox elif self.fieldsdic[field]['type'] == 'combobox': self.editregfields[field] = Gtk.ComboBoxText() self.editregfields[field].append_text('') for val in self.fieldsdic[field]['options']: self.editregfields[field].append_text(val) if current_info: try: indx = self.fieldsdic[field]['options'].index(current_info[field]) self.editregfields[field].set_active(indx+1) except ValueError: self.editregfields[field].set_active(0) #this catches if current_inf[field] is not a valid value. It is probably blank. Otherwise this is an issue, but we will force it blank. else: self.editregfields[field].set_active(0) # Set up the vbox editregvbox = Gtk.VBox(False, 8) # We will make a smaller hbox for each of the fields. hboxes = {} for field in self.fields: hboxes[field] = Gtk.HBox(False, 15) hboxes[field].pack_start(Gtk.Label(field+':', True, True, 0), False, False, 0) #Pack the label hboxes[field].pack_start(self.editregfields[field], False, False, 0) #Pack the button/entry/.. if self.projecttype == 'handicap' and field == 'Handicap': label_hd = Gtk.Label(label='hh:mm:ss') hboxes[field].pack_start(label_hd, False, False, 0) if field == 'ID': label_id = Gtk.Label('Must be unique') hboxes[field].pack_start(label_id, False, False, 0) editregvbox.pack_start(hboxes[field], False, False, 0) #Pack this hbox into the big vbox. #Pack and show if self.projecttype == 'handicap': editregbtnOK.connect('clicked', self.validate_entry, treeiter, preregiter, label_hd, label_id) else: editregbtnOK.connect('clicked', self.validate_entry, treeiter, preregiter, None, label_id) editregvbox.pack_start(editreghbox, False, False, 5) self.editreg_win.add(editregvbox) self.editreg_win.show_all() def validate_entry(self, jnk_unused, treeiter, preregiter, label_hd, label_id): '''Handles a click on the 'ok' button of the entry edition window. Reads out the input information, and writes the changes to the treemodel''' #First check if we have entered a handicap, and if so, make sure it is valid if self.projecttype == 'handicap': sduration = self.editregfields['Handicap'].get_text() if sduration != '': try: timePattern = r'((?P<days>-?\d+) day(s)?, )?((?P<hours>\d+):)?'r'(?P<minutes>\d+):(?P<seconds>\d+)' re.match(timePattern, sduration).groupdict(0) except AttributeError: label_hd.set_markup('<span color="red">hh:mm:ss</span>') return # If that was OK, we go through each field and grab the new value. new_vals = {} for field in self.fields: #Entrybox if self.fieldsdic[field]['type'] in ['entrybox', 'entrybox_int']: new_vals[field] = self.editregfields[field].get_text() #Combobox elif self.fieldsdic[field]['type'] == 'combobox': indx = self.editregfields[field].get_active() if indx == 0: new_vals[field] = '' else: new_vals[field] = self.fieldsdic[field]['options'][indx-1] # Make sure we don't have a duplicate ID, unless we are editing and leave it the same. if treeiter and new_vals['ID'] == self.prereg[preregiter]['ID']: pass # No need for an ID check, it was already done. elif new_vals['ID'] in self.ids: label_id.set_markup('<span color="red">{} has already been used</span>'.format(new_vals['ID'])) return # Now we replace or append in the treemodel and in prereg if treeiter: # Remove the old ID from the id store, and add the new value if self.prereg[preregiter]['ID']: self.ids.remove(self.prereg[preregiter]['ID']) # Update the tree and prereg for (colid, field) in enumerate(self.fields): self.regmodel.set_value(treeiter, colid, new_vals[field]) self.prereg[preregiter] = new_vals else: self.regmodel.append([new_vals[field] for field in self.fields]) self.prereg.append(new_vals) # Add the new ID to the id store if new_vals['ID']: self.ids.add(new_vals['ID']) # The saved status is unsaved self.regstatus.set_markup('') # Filter results by the current filter field, for this value self.filterentry.set_text(new_vals[self.filter_combo.get_active_text()]) # Save if self.autosave: self.save_clicked(None) # we're done self.editreg_win.hide()
def __init__(self, fields, fieldsdic, divisions, back_clicked_cb, next_clicked_cb, parent, edit): '''Creates divisions window''' super(DivisionsWin, self).__init__(Gtk.WindowType.TOPLEVEL) self.divisions = divisions self.fields = fields self.fieldsdic = fieldsdic self.winnewdiv = None self.modify_bg(Gtk.StateType.NORMAL, fstimer.gui.bgcolor) self.set_transient_for(parent) self.set_modal(True) self.set_title('fsTimer - Divisions') self.set_position(Gtk.WindowPosition.CENTER) self.set_border_width(20) self.set_size_request(800, 500) self.connect('delete_event', lambda b, jnk_unused: self.hide()) # Now create the vbox. vbox = Gtk.VBox(False, 10) self.add(vbox) # Now add the text. label2_0 = Gtk.Label( "Specify the divisions for reporting divisional places.\n" "Press 'Forward' to continue with the default settings, or make " "edits below.\n\nDivisions can be any combination of number entry" " and selection box fields.") # Make the liststore, with columns: # name | (... combobox and entrybox_int fields...) # To do this we first count the number of fields ndivfields = len([field for field in fields if fieldsdic[field]['type'] in ['combobox', 'entrybox_int']]) self.divmodel = Gtk.ListStore(*[str for i_unused in range(ndivfields + 1)]) #We will put the liststore in a treeview self.divview = Gtk.TreeView() #Add each of the columns Columns = {} Columns[1] = Gtk.TreeViewColumn( 'Division name', Gtk.CellRendererText(), text=0) self.divview.append_column(Columns[1]) # The fields textcount = 1 for field in fields: if fieldsdic[field]['type'] in ['combobox', 'entrybox_int']: Columns[field] = Gtk.TreeViewColumn( field, Gtk.CellRendererText(), text=textcount) textcount += 1 self.divview.append_column(Columns[field]) #Now we populate the model with the default fields divmodelrows = {} for div in self.divisions: divmodelrow = self.get_divmodelrow(div) self.divmodel.append(divmodelrow) #Done there. self.divview.set_model(self.divmodel) selection = self.divview.get_selection() #And put it in a scrolled window, in an alignment divsw = Gtk.ScrolledWindow() divsw.set_shadow_type(Gtk.ShadowType.ETCHED_IN) divsw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) divsw.add(self.divview) divalgn = Gtk.Alignment.new(0, 0, 1, 1) divalgn.add(divsw) #Now we put the buttons on the side. vbox2 = Gtk.VBox(False, 10) btnUP = GtkStockButton('up','Up') btnUP.connect('clicked', self.div_up, selection) vbox2.pack_start(btnUP, False, False, 0) btnDOWN = GtkStockButton('down','Down') btnDOWN.connect('clicked', self.div_down, selection) vbox2.pack_start(btnDOWN, False, False, 0) btnEDIT = GtkStockButton('edit','Edit') btnEDIT.connect('clicked', self.div_edit, selection) vbox2.pack_start(btnEDIT, False, False, 0) btnREMOVE = GtkStockButton('remove','Remove') btnREMOVE.connect('clicked', self.div_remove, selection) vbox2.pack_start(btnREMOVE, False, False, 0) btnNEW = GtkStockButton('new','New') btnNEW.connect('clicked', self.div_new, ('', {}), None) vbox2.pack_start(btnNEW, False, False, 0) btnCOPY = GtkStockButton('copy','Copy') btnCOPY.connect('clicked', self.div_copy, selection) vbox2.pack_start(btnCOPY, False, False, 0) #And an hbox for the fields and the buttons hbox4 = Gtk.HBox(False, 0) hbox4.pack_start(divalgn, True, True, 10) hbox4.pack_start(vbox2, False, False, 0) ##And an hbox with 3 buttons hbox3 = Gtk.HBox(False, 0) btnCANCEL = GtkStockButton('close', 'Close') btnCANCEL.connect('clicked', lambda btn: self.hide()) alignCANCEL = Gtk.Alignment.new(0, 0, 0, 0) alignCANCEL.add(btnCANCEL) btnBACK = GtkStockButton('back', 'Back') if edit: btnBACK.set_sensitive(False) else: btnBACK.connect('clicked', back_clicked_cb) btnNEXT = GtkStockButton('forward', 'Next') btnNEXT.connect('clicked', next_clicked_cb, edit) ##And populate hbox3.pack_start(alignCANCEL, True, True, 0) hbox3.pack_start(btnBACK, False, False, 2) hbox3.pack_start(btnNEXT, False, False, 0) alignText = Gtk.Alignment.new(0, 0, 0, 0) alignText.add(label2_0) vbox.pack_start(alignText, False, False, 0) vbox.pack_start(hbox4, True, True, 0) vbox.pack_start(hbox3, False, False, 10) self.show_all()