Beispiel #1
0
	def __init__( self, parent, id=wx.ID_ANY, addEditButton=True ):
		super(Properties, self).__init__(parent, id)
		
		self.state = RaceInputState()
		
		self.SetBackgroundColour( wx.WHITE )
		
		mainSizer = wx.BoxSizer( wx.VERTICAL )
		self.SetSizer( mainSizer )
		
		bookStyle = (
			  flatnotebook.FNB_NO_NAV_BUTTONS
			| flatnotebook.FNB_NO_X_BUTTON
			| flatnotebook.FNB_VC8
			| flatnotebook.FNB_NODRAG
		)
		self.notebook = flatnotebook.FlatNotebook( self, agwStyle=bookStyle )
		self.notebook.SetBackgroundColour( wx.WHITE )
		self.notebook.SetTabAreaColour( wx.WHITE )
		self.notebook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.onPageChanging )

		self.propClassName = [
			('generalInfoProperties',	GeneralInfoProperties,		_('General Info') ),
			('raceOptionsProperties',	RaceOptionsProperties,		_('Race Options') ),
			('notesProperties',			NotesProperties,			_('Notes') ),
			('rfidProperties',			RfidProperties,				_('RFID') ),
			('cameraProperties',		CameraProperties,			_('Camera') ),
			('animationProperties',		AnimationProperties,		_('Animation') ),
			('filesProperties',			FilesProperties,			_('Files') ),
		]
		for prop, PropClass, name in self.propClassName:
			setattr( self, prop, PropClass(self.notebook) )
			self.notebook.AddPage( getattr(self, prop), name )
		
		self.updateFileName()
		
		mainSizer.Add( self.notebook, 1, flag=wx.ALL|wx.EXPAND, border=4 )
		
		if addEditButton:
			hs = wx.BoxSizer( wx.HORIZONTAL )
			
			self.commitButton = wx.Button(self, label=_('Commit'))
			self.commitButton.Bind( wx.EVT_BUTTON, self.commitButtonCallback )
			hs.Add( self.commitButton, flag=wx.TOP|wx.BOTTOM, border=8 )
			
			self.excelButton = wx.Button(self, label=_('Link External Excel Sheet...'))
			self.excelButton.Bind( wx.EVT_BUTTON, self.excelButtonCallback )
			hs.Add( self.excelButton, flag=wx.LEFT|wx.TOP|wx.BOTTOM, border=8 )

			mainSizer.Add( hs, flag=wx.ALL, border=4 )
			
		self.setEditable()
		mainSizer.Fit(self)
		self.Layout()
Beispiel #2
0
	def __init__( self, parent, id=wx.ID_ANY, size=wx.DefaultSize ):
		super(TeamResults, self).__init__( parent, id, size=size )
		
		self.state = RaceInputState()
		
		vsOverall = wx.BoxSizer( wx.VERTICAL )
		
		#---------------------------------------------------------------
		self.colnames = (
			_('Pos'),
			_('Team'),
			_('Time'),
			_('Gap'),
		)
		self.grid = ReorderableGrid( self )
		self.grid.CreateGrid( 0, len(self.colnames) )
		self.grid.SetRowLabelSize( 0 )
		self.grid.SetMargins( 0, 0 )
		self.grid.AutoSizeColumns( True )
		self.grid.DisableDragColSize()
		self.grid.DisableDragRowSize()
		
		self.grid.AutoSizeColumns( False )
		self.grid.AutoSizeRows( False )

		#---------------------------------------------------------------
		self.hbs = wx.BoxSizer(wx.HORIZONTAL)
		
		self.categoryLabel = wx.StaticText( self, label = _('Category:') )
		self.categoryChoice = wx.Choice( self )
		self.Bind(wx.EVT_CHOICE, self.doChooseCategory, self.categoryChoice)
		
		self.exportButton = wx.Button( self, label='{} {}/{}'.format(_('Export'), _('Excel'), _('PDF')) )
		self.exportButton.Bind( wx.EVT_BUTTON, self.doExport )
		
		self.hbs.Add( self.categoryLabel, flag=wx.TOP|wx.BOTTOM|wx.LEFT|wx.ALIGN_CENTRE_VERTICAL, border=4 )
		self.hbs.Add( self.categoryChoice, flag=wx.ALL|wx.ALIGN_CENTRE_VERTICAL, border=4 )
		self.hbs.Add( self.exportButton, flag=wx.ALL|wx.ALIGN_CENTRE_VERTICAL, border=4 )
		
		#---------------------------------------------------------------
		
		vsOverall.Add( self.hbs, flag=wx.ALL, border=4 )
		vsOverall.Add( self.grid, 1, flag=wx.EXPAND|wx.ALL, border=4 )
		self.SetSizer( vsOverall )
Beispiel #3
0
    def __init__(self, parent, id=wx.ID_ANY):
        wx.Panel.__init__(self, parent, id)

        self.state = RaceInputState()

        vs = wx.BoxSizer(wx.VERTICAL)

        self.ignoreColour = wx.Colour(80, 80, 80)
        self.inactiveColour = wx.Colour(200, 200, 200)

        border = 4
        flag = wx.ALL

        hs = wx.BoxSizer(wx.HORIZONTAL)

        self.activateAllButton = wx.Button(self,
                                           label=_('Activate All'),
                                           style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onActivateAll, self.activateAllButton)
        hs.Add(self.activateAllButton, 0, border=border, flag=flag)

        hs.AddSpacer(6)

        self.newCategoryButton = wx.Button(self,
                                           label=_('New'),
                                           style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onNewCategory, self.newCategoryButton)
        hs.Add(self.newCategoryButton, 0, border=border, flag=flag)

        self.delCategoryButton = wx.Button(self,
                                           label=_('Delete'),
                                           style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onDelCategory, self.delCategoryButton)
        hs.Add(self.delCategoryButton,
               0,
               border=border,
               flag=(flag & ~wx.LEFT))

        hs.AddSpacer(6)

        self.upCategoryButton = wx.Button(self,
                                          label=u'\u2191',
                                          style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onUpCategory, self.upCategoryButton)
        hs.Add(self.upCategoryButton, 0, border=border, flag=flag)

        self.downCategoryButton = wx.Button(self,
                                            label=u'\u2193',
                                            style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onDownCategory, self.downCategoryButton)
        hs.Add(self.downCategoryButton,
               0,
               border=border,
               flag=(flag & ~wx.LEFT))

        hs.AddSpacer(6)

        self.setGpxDistanceButton = wx.Button(self,
                                              label=_('Set Gpx Distance'),
                                              style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onSetGpxDistance,
                  self.setGpxDistanceButton)
        hs.Add(self.setGpxDistanceButton, 0, border=border, flag=flag)

        hs.AddSpacer(6)

        self.addExceptionsButton = wx.Button(self,
                                             label=_('Bib Exceptions'),
                                             style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onAddExceptions,
                  self.addExceptionsButton)
        hs.Add(self.addExceptionsButton, 0, border=border, flag=flag)

        hs.AddSpacer(6)
        '''
		self.updateStartWaveNumbersButton = wx.Button(self, label=_('Update Start Wave Bibs'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onUpdateStartWaveNumbers, self.updateStartWaveNumbersButton )
		hs.Add( self.updateStartWaveNumbersButton, 0, border = border, flag = flag )
		'''

        self.normalizeButton = wx.Button(self,
                                         label=_('Normalize'),
                                         style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onNormalize, self.normalizeButton)
        hs.Add(self.normalizeButton, 0, border=border, flag=flag)

        hs.AddStretchSpacer()

        self.printButton = wx.Button(self,
                                     label=u'{}...'.format(_('Print')),
                                     style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onPrint, self.printButton)
        hs.Add(self.printButton, 0, border=border, flag=flag)

        self.excelButton = wx.Button(self,
                                     label=u'{}...'.format(_('Excel')),
                                     style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onExcel, self.excelButton)
        hs.Add(self.excelButton, 0, border=border, flag=flag)

        self.grid = ReorderableGrid(self)
        self.colNameFields = [
            (u'', None),
            (_('Category Type'), 'catType'),
            (_('Active'), 'active'),
            (_('Name'), 'name'),
            (_('Gender'), 'gender'),
            (_('Numbers'), 'catStr'),
            (_('Start\nOffset'), 'startOffset'),
            (_('Race\nLaps'), 'numLaps'),
            (_('Race\nMinutes'), 'raceMinutes'),
            (_('Lapped\nRiders\nContinue'), 'lappedRidersMustContinue'),
            (_('Distance'), 'distance'),
            (_('Dist.\nBy'), 'distanceType'),
            (_('First\nLap\nDist.'), 'firstLapDistance'),
            (_('80%\nLap\nTime'), 'rule80Time'),
            (_('CrossMgr\nEstimated\nLaps'), 'suggestedLaps'),
            (_('Publish'), 'publishFlag'),
            (_('Upload'), 'uploadFlag'),
            (_('Series'), 'seriesFlag'),
        ]
        self.computedFields = {'rule80Time', 'suggestedLaps'}
        self.colnames = [
            colName if not colName.startswith('_') else _('Name Copy')
            for colName, fieldName in self.colNameFields
        ]
        self.iCol = {
            fieldName: i
            for i, (colName, fieldName) in enumerate(self.colNameFields)
            if fieldName and not colName.startswith('_')
        }

        self.activeColumn = self.iCol['active']
        self.genderColumn = self.iCol['gender']
        self.numbersColumn = self.iCol['catStr']
        self.grid.CreateGrid(0, len(self.colnames))
        self.grid.SetRowLabelSize(32)
        self.grid.SetMargins(0, 0)
        for col, name in enumerate(self.colnames):
            self.grid.SetColLabelValue(col, name)

        self.cb = None

        self.boolCols = set()
        self.choiceCols = set()
        self.readOnlyCols = set()
        self.dependentCols = set()

        # Set column attributes for the table.
        for col, (colName, fieldName) in enumerate(self.colNameFields):
            attr = gridlib.GridCellAttr()

            if fieldName is None:
                attr.SetRenderer(CategoryIconRenderer())
                attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
                attr.SetReadOnly(True)
                self.readOnlyCols.add(col)

            elif fieldName == 'catType':
                self.catTypeWidth = 64
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(self.CategoryTypeChoices,
                                                 False))
                attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
                self.choiceCols.add(col)

            elif fieldName in {
                    'active', 'lappedRidersMustContinue', 'publishFlag',
                    'uploadFlag', 'seriesFlag'
            }:
                boolEditor = gridlib.GridCellBoolEditor()
                boolEditor.UseStringValues('1', '0')
                attr.SetEditor(boolEditor)
                attr.SetRenderer(gridlib.GridCellBoolRenderer())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.boolCols.add(col)
                if fieldName == 'lappedRidersMustContinue':
                    self.dependentCols.add(col)

            elif fieldName == 'gender':
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(
                        [_('Open'), _('Men'), _('Women')], False))
                self.choiceCols.add(col)

            elif fieldName == 'startOffset':
                attr.SetEditor(TimeEditor())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName == 'numLaps':
                attr.SetEditor(wx.grid.GridCellNumberEditor())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName == 'raceMinutes':
                attr.SetEditor(wx.grid.GridCellNumberEditor())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName in ['rule80Time', 'suggestedLaps']:
                attr.SetReadOnly(True)
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.readOnlyCols.add(col)
                self.dependentCols.add(col)

            elif fieldName in ['distance', 'firstLapDistance']:
                attr.SetEditor(gridlib.GridCellFloatEditor(7, 3))
                attr.SetRenderer(gridlib.GridCellFloatRenderer(7, 3))
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName == 'distanceType':
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(self.DistanceTypeChoices,
                                                 False))
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
                self.choiceCols.add(col)
                self.dependentCols.add(col)

            elif colName == '_name2':
                attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
                attr.SetBackgroundColour(wx.Colour(240, 240, 240))
                attr.SetReadOnly(True)

            self.grid.SetColAttr(col, attr)

        self.Bind(gridlib.EVT_GRID_CELL_LEFT_CLICK, self.onGridLeftClick)
        self.Bind(gridlib.EVT_GRID_SELECT_CELL, self.onCellSelected)
        self.Bind(gridlib.EVT_GRID_CELL_CHANGED, self.onCellChanged)
        self.Bind(gridlib.EVT_GRID_EDITOR_CREATED, self.onEditorCreated)

        vs.Add(hs, 0, flag=wx.EXPAND | wx.ALL, border=4)
        vs.Add(self.grid, 1, flag=wx.GROW | wx.ALL | wx.EXPAND)

        self.rowCur = 0
        self.colCur = 0
        self.SetSizer(vs)
Beispiel #4
0
class Categories(wx.Panel):
    CategoryTypeChoices = [
        _('Start Wave'), u'    ' + _('Component'),
        _('Custom')
    ]
    DistanceTypeChoices = [_('Lap'), _('Race')]

    def __init__(self, parent, id=wx.ID_ANY):
        wx.Panel.__init__(self, parent, id)

        self.state = RaceInputState()

        vs = wx.BoxSizer(wx.VERTICAL)

        self.ignoreColour = wx.Colour(80, 80, 80)
        self.inactiveColour = wx.Colour(200, 200, 200)

        border = 4
        flag = wx.ALL

        hs = wx.BoxSizer(wx.HORIZONTAL)

        self.activateAllButton = wx.Button(self,
                                           label=_('Activate All'),
                                           style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onActivateAll, self.activateAllButton)
        hs.Add(self.activateAllButton, 0, border=border, flag=flag)

        hs.AddSpacer(6)

        self.newCategoryButton = wx.Button(self,
                                           label=_('New'),
                                           style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onNewCategory, self.newCategoryButton)
        hs.Add(self.newCategoryButton, 0, border=border, flag=flag)

        self.delCategoryButton = wx.Button(self,
                                           label=_('Delete'),
                                           style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onDelCategory, self.delCategoryButton)
        hs.Add(self.delCategoryButton,
               0,
               border=border,
               flag=(flag & ~wx.LEFT))

        hs.AddSpacer(6)

        self.upCategoryButton = wx.Button(self,
                                          label=u'\u2191',
                                          style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onUpCategory, self.upCategoryButton)
        hs.Add(self.upCategoryButton, 0, border=border, flag=flag)

        self.downCategoryButton = wx.Button(self,
                                            label=u'\u2193',
                                            style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onDownCategory, self.downCategoryButton)
        hs.Add(self.downCategoryButton,
               0,
               border=border,
               flag=(flag & ~wx.LEFT))

        hs.AddSpacer(6)

        self.setGpxDistanceButton = wx.Button(self,
                                              label=_('Set Gpx Distance'),
                                              style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onSetGpxDistance,
                  self.setGpxDistanceButton)
        hs.Add(self.setGpxDistanceButton, 0, border=border, flag=flag)

        hs.AddSpacer(6)

        self.addExceptionsButton = wx.Button(self,
                                             label=_('Bib Exceptions'),
                                             style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onAddExceptions,
                  self.addExceptionsButton)
        hs.Add(self.addExceptionsButton, 0, border=border, flag=flag)

        hs.AddSpacer(6)
        '''
		self.updateStartWaveNumbersButton = wx.Button(self, label=_('Update Start Wave Bibs'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onUpdateStartWaveNumbers, self.updateStartWaveNumbersButton )
		hs.Add( self.updateStartWaveNumbersButton, 0, border = border, flag = flag )
		'''

        self.normalizeButton = wx.Button(self,
                                         label=_('Normalize'),
                                         style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onNormalize, self.normalizeButton)
        hs.Add(self.normalizeButton, 0, border=border, flag=flag)

        hs.AddStretchSpacer()

        self.printButton = wx.Button(self,
                                     label=u'{}...'.format(_('Print')),
                                     style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onPrint, self.printButton)
        hs.Add(self.printButton, 0, border=border, flag=flag)

        self.excelButton = wx.Button(self,
                                     label=u'{}...'.format(_('Excel')),
                                     style=wx.BU_EXACTFIT)
        self.Bind(wx.EVT_BUTTON, self.onExcel, self.excelButton)
        hs.Add(self.excelButton, 0, border=border, flag=flag)

        self.grid = ReorderableGrid(self)
        self.colNameFields = [
            (u'', None),
            (_('Category Type'), 'catType'),
            (_('Active'), 'active'),
            (_('Name'), 'name'),
            (_('Gender'), 'gender'),
            (_('Numbers'), 'catStr'),
            (_('Start\nOffset'), 'startOffset'),
            (_('Race\nLaps'), 'numLaps'),
            (_('Race\nMinutes'), 'raceMinutes'),
            (_('Lapped\nRiders\nContinue'), 'lappedRidersMustContinue'),
            (_('Distance'), 'distance'),
            (_('Dist.\nBy'), 'distanceType'),
            (_('First\nLap\nDist.'), 'firstLapDistance'),
            (_('80%\nLap\nTime'), 'rule80Time'),
            (_('CrossMgr\nEstimated\nLaps'), 'suggestedLaps'),
            (_('Publish'), 'publishFlag'),
            (_('Upload'), 'uploadFlag'),
            (_('Series'), 'seriesFlag'),
        ]
        self.computedFields = {'rule80Time', 'suggestedLaps'}
        self.colnames = [
            colName if not colName.startswith('_') else _('Name Copy')
            for colName, fieldName in self.colNameFields
        ]
        self.iCol = {
            fieldName: i
            for i, (colName, fieldName) in enumerate(self.colNameFields)
            if fieldName and not colName.startswith('_')
        }

        self.activeColumn = self.iCol['active']
        self.genderColumn = self.iCol['gender']
        self.numbersColumn = self.iCol['catStr']
        self.grid.CreateGrid(0, len(self.colnames))
        self.grid.SetRowLabelSize(32)
        self.grid.SetMargins(0, 0)
        for col, name in enumerate(self.colnames):
            self.grid.SetColLabelValue(col, name)

        self.cb = None

        self.boolCols = set()
        self.choiceCols = set()
        self.readOnlyCols = set()
        self.dependentCols = set()

        # Set column attributes for the table.
        for col, (colName, fieldName) in enumerate(self.colNameFields):
            attr = gridlib.GridCellAttr()

            if fieldName is None:
                attr.SetRenderer(CategoryIconRenderer())
                attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
                attr.SetReadOnly(True)
                self.readOnlyCols.add(col)

            elif fieldName == 'catType':
                self.catTypeWidth = 64
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(self.CategoryTypeChoices,
                                                 False))
                attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
                self.choiceCols.add(col)

            elif fieldName in {
                    'active', 'lappedRidersMustContinue', 'publishFlag',
                    'uploadFlag', 'seriesFlag'
            }:
                boolEditor = gridlib.GridCellBoolEditor()
                boolEditor.UseStringValues('1', '0')
                attr.SetEditor(boolEditor)
                attr.SetRenderer(gridlib.GridCellBoolRenderer())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.boolCols.add(col)
                if fieldName == 'lappedRidersMustContinue':
                    self.dependentCols.add(col)

            elif fieldName == 'gender':
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(
                        [_('Open'), _('Men'), _('Women')], False))
                self.choiceCols.add(col)

            elif fieldName == 'startOffset':
                attr.SetEditor(TimeEditor())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName == 'numLaps':
                attr.SetEditor(wx.grid.GridCellNumberEditor())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName == 'raceMinutes':
                attr.SetEditor(wx.grid.GridCellNumberEditor())
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName in ['rule80Time', 'suggestedLaps']:
                attr.SetReadOnly(True)
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.readOnlyCols.add(col)
                self.dependentCols.add(col)

            elif fieldName in ['distance', 'firstLapDistance']:
                attr.SetEditor(gridlib.GridCellFloatEditor(7, 3))
                attr.SetRenderer(gridlib.GridCellFloatRenderer(7, 3))
                attr.SetAlignment(wx.ALIGN_CENTRE, wx.ALIGN_CENTRE)
                self.dependentCols.add(col)

            elif fieldName == 'distanceType':
                attr.SetEditor(
                    gridlib.GridCellChoiceEditor(self.DistanceTypeChoices,
                                                 False))
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
                self.choiceCols.add(col)
                self.dependentCols.add(col)

            elif colName == '_name2':
                attr.SetAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
                attr.SetBackgroundColour(wx.Colour(240, 240, 240))
                attr.SetReadOnly(True)

            self.grid.SetColAttr(col, attr)

        self.Bind(gridlib.EVT_GRID_CELL_LEFT_CLICK, self.onGridLeftClick)
        self.Bind(gridlib.EVT_GRID_SELECT_CELL, self.onCellSelected)
        self.Bind(gridlib.EVT_GRID_CELL_CHANGED, self.onCellChanged)
        self.Bind(gridlib.EVT_GRID_EDITOR_CREATED, self.onEditorCreated)

        vs.Add(hs, 0, flag=wx.EXPAND | wx.ALL, border=4)
        vs.Add(self.grid, 1, flag=wx.GROW | wx.ALL | wx.EXPAND)

        self.rowCur = 0
        self.colCur = 0
        self.SetSizer(vs)

    def onPrint(self, event):
        self.commit()
        PrintCategories()

    def onExcel(self, event):
        self.commit()
        export = getExportGrid()
        xlFName = Utils.getMainWin().getFormatFilename('excel')
        xlFName = os.path.splitext(
            xlFName)[0] + '-Categories' + os.path.splitext(xlFName)[1]

        wb = xlwt.Workbook()
        sheetCur = wb.add_sheet(_('Categories'))
        export.toExcelSheet(sheetCur)
        try:
            wb.save(xlFName)
            if Utils.getMainWin().launchExcelAfterPublishingResults:
                Utils.LaunchApplication(xlFName)
            Utils.MessageOK(
                self, u'{}:\n\n   {}'.format(_('Excel file written to'),
                                             xlFName), _('Excel Write'))
        except IOError:
            Utils.MessageOK(
                self,
                u'{} "{}"\n\n{}\n{}'.format(
                    _('Cannot write'), xlFName,
                    _('Check if this spreadsheet is already open.'),
                    _('If so, close it, and try again.')),
                _('Excel File Error'),
                iconMask=wx.ICON_ERROR)

    def onSetGpxDistance(self, event):
        race = Model.race
        geoTrack = getattr(race, 'geoTrack', None)
        if not geoTrack:
            return
        if not Utils.MessageOK(self,
                               _('Set the GPX distance for all Categories?'),
                               _('Set GPX Distance'), wx.ICON_QUESTION):
            return
        distance = geoTrack.lengthKm if race.distanceUnit == Model.Race.UnitKm else geoTrack.lengthMiles
        for category in race.getCategories():
            category.distance = distance
        race.setChanged()
        self.refresh(forceRefresh=True)

    def onNormalize(self, event):
        self.commit()
        if Model.race:
            Model.race.normalizeCategories()
            self.state.reset()
            self.refresh()

    #------------------------------------------

    def onGridLeftClick(self, event):
        if event.GetCol() in self.boolCols:
            r, c = event.GetRow(), event.GetCol()
            if c == self.iCol['active']:
                active = (self.grid.GetCellValue(r,
                                                 self.iCol['active']) == u'1')
                wx.CallAfter(
                    self.fixRow, r,
                    self.CategoryTypeChoices.index(
                        self.grid.GetCellValue(r, self.iCol['catType'])),
                    not active)
            self.grid.SetCellValue(
                r, c, '1' if self.grid.GetCellValue(r, c)[:1] != '1' else '0')
        event.Skip()

    def onCellSelected(self, event):
        self.rowCur = event.GetRow()
        self.colCur = event.GetCol()
        if self.colCur in self.choiceCols or self.colCur in self.boolCols:
            wx.CallAfter(self.grid.EnableCellEditControl)
        event.Skip()

    def onCellChanged(self, event):
        self.rowCur = event.GetRow()
        self.colCur = event.GetCol()
        if self.colCur in [1, 2]:
            wx.CallAfter(self.fixCells)
        event.Skip()

    def onEditorCreated(self, event):
        if event.GetCol() == self.numbersColumn:
            ctrl = event.GetControl()
            ctrl.Bind(wx.EVT_KEY_DOWN, self.onNumbersKeyEvent)
            ctrl.Bind(wx.EVT_TEXT_PASTE, self.onPaste)
        event.Skip()

    def getCleanClipboardText(self):
        if wx.TheClipboard.Open():
            data = wx.TextDataObject()
            if wx.TheClipboard.GetData(data):
                txt = data.GetText()
                txt = re.sub('[^0-9,-]+', ',', txt)
            wx.TheClipboard.Close()
            return txt
        return None

    def isExternalChange(self):
        if not Model.race:
            return False

        categories = Model.race.getAllCategories()

        for cat in categories:
            try:
                cat.distance = float(cat.distance)
            except:
                cat.distance = None
            try:
                cat.firstLapDistance = float(cat.firstLapDistance)
            except:
                cat.firstLapDistance = None

        if self.grid.GetNumberRows() != len(categories):
            return True

        def distanceMatches(distance, cellValue):
            try:
                value = float(cellValue)
            except ValueError:
                value = None

            if not distance and not value:
                return True
            return u'{:.3f}'.format(distance or 0.0) == cellValue

        def numLapsMatches(numLaps, cellValue):
            v = u'{}'.format(numLaps if numLaps is not None else '')
            return v == cellValue

        return any((
            cat.name != self.grid.GetCellValue(r, self.iCol['name'])
            or cat.catStr != self.grid.GetCellValue(r, self.iCol['catStr'])
            or not distanceMatches(
                cat.distance, self.grid.GetCellValue(r, self.iCol['distance']))
            or not distanceMatches(
                cat.firstLapDistance,
                self.grid.GetCellValue(r, self.iCol['firstLapDistance']))
            or not numLapsMatches(
                cat.numLaps, self.grid.GetCellValue(r, self.iCol['numLaps'])))
                   for r, cat in enumerate(categories))

    def pasteFromClipboard(self, event):
        txt = self.getCleanClipboardText()
        if txt:
            event.GetEventObject().WriteText(txt)
            return True
        return False

    def onNumbersKeyEvent(self, event):
        # Handle column pastes from Excel when there are newlines.
        if event.GetModifiers() == wx.MOD_CONTROL and event.GetKeyCode() == 86:
            if self.pasteFromClipboard(event):
                return
        event.Skip()

    def onPaste(self, event):
        self.pasteFromClipboard(event)
        event.Skip()

    #------------------------------------------

    def onUpdateStartWaveNumbers(self, event):
        self.commit()
        undo.pushState()
        with Model.LockRace() as race:
            race.adjustAllCategoryWaveNumbers()
        self.state.reset()
        wx.CallAfter(self.refresh)
        wx.CallAfter(Utils.refreshForecastHistory)

    def onAddExceptions(self, event):
        with Model.LockRace() as race:
            if not race or not race.getAllCategories():
                return

        r = self.grid.GetGridCursorRow()
        if r is None or r < 0:
            Utils.MessageOK(self, _('You must select a Category first'),
                            _('Select a Category'))
            return

        with Model.LockRace() as race:
            categories = race.getAllCategories()
            category = categories[r]

        dlg = wx.TextEntryDialog(
            self, u'{}: {}'.format(
                category.name,
                _('''Add Bib Exceptions (comma separated).
This will add the given list of Bibs to this category,
and remove them from other categories.'''),
            ), _('Add Bib Exceptions'))
        good = (dlg.ShowModal() == wx.ID_OK)
        if good:
            response = dlg.GetValue()
        dlg.Destroy()
        if not good:
            return

        undo.pushState()
        response = re.sub('[^0-9,]', '', response.replace(' ', ','))
        with Model.LockRace() as race:
            for numException in response.split(','):
                race.addCategoryException(category, numException)

        self.state.reset()
        self.refresh()

    def _setRow(
        self,
        r,
        active,
        name,
        catStr,
        startOffset='00:00:00',
        numLaps=None,
        raceMinutes=None,
        lappedRidersMustContinue=False,
        distance=None,
        distanceType=None,
        firstLapDistance=None,
        gender=None,
        catType=Model.Category.CatWave,
        publishFlag=True,
        uploadFlag=True,
        seriesFlag=True,
    ):

        if len(startOffset) < len('00:00:00'):
            startOffset = '00:' + startOffset

        GetTranslation = _
        gender = gender if gender in ['Men', 'Women'] else 'Open'
        self.grid.SetRowLabelValue(r, u'')
        self.grid.SetCellValue(r, self.iCol['active'],
                               u'1' if active else u'0')
        self.grid.SetCellValue(r, self.iCol['catType'],
                               self.CategoryTypeChoices[catType])
        self.grid.SetCellValue(r, self.iCol['name'], name)
        self.grid.SetCellValue(r, self.iCol['gender'], GetTranslation(gender))
        self.grid.SetCellValue(r, self.iCol['catStr'], catStr)
        self.grid.SetCellValue(r, self.iCol['startOffset'], startOffset)
        self.grid.SetCellValue(r, self.iCol['numLaps'],
                               u'{}'.format(numLaps) if numLaps else u'')
        self.grid.SetCellValue(
            r, self.iCol['raceMinutes'],
            u'{}'.format(raceMinutes) if raceMinutes else u'')
        self.grid.SetCellValue(r, self.iCol['lappedRidersMustContinue'],
                               u'1' if lappedRidersMustContinue else u'0')
        self.grid.SetCellValue(r, self.iCol['rule80Time'], u'')
        self.grid.SetCellValue(r, self.iCol['suggestedLaps'], u'')
        self.grid.SetCellValue(r, self.iCol['distance'],
                               ('%.3f' % distance) if distance else u'')
        self.grid.SetCellValue(
            r, self.iCol['distanceType'],
            self.DistanceTypeChoices[distanceType if distanceType else 0])
        self.grid.SetCellValue(r, self.iCol['firstLapDistance'],
                               ('%.3f' %
                                firstLapDistance) if firstLapDistance else '')
        self.grid.SetCellValue(r, self.iCol['publishFlag'],
                               u'1' if publishFlag else u'0')
        self.grid.SetCellValue(r, self.iCol['uploadFlag'],
                               u'1' if uploadFlag else u'0')
        self.grid.SetCellValue(r, self.iCol['seriesFlag'],
                               u'1' if seriesFlag else u'0')

        race = Model.race
        category = race.categories.get(u'{} ({})'.format(name.strip(), gender),
                                       None) if race else None
        if not category or category.catType != Model.Category.CatWave:
            return

        # Get the 80% time cutoff.
        if not active or not Model.race:
            return

        rule80Time = race.getRule80CountdownTime(category) if race else None
        if rule80Time:
            self.grid.SetCellValue(r, self.iCol['rule80Time'],
                                   Utils.formatTime(rule80Time))

        laps = race.getCategoryRaceLaps().get(category, 0) if race else None
        if laps:
            self.grid.SetCellValue(r, self.iCol['suggestedLaps'],
                                   u'{}'.format(laps))

    def fixRow(self, row, catType, active):
        activeColour = wx.WHITE if active else self.inactiveColour
        colour = activeColour if catType == Model.Category.CatWave else self.ignoreColour
        for colName, fieldName in self.colNameFields:
            if not fieldName:
                continue
            col = self.iCol[fieldName]
            self.grid.SetCellBackgroundColour(
                row, col,
                colour if col in self.dependentCols else activeColour)

    def fixCells(self, event=None):
        for row in xrange(self.grid.GetNumberRows()):
            active = self.grid.GetCellValue(row,
                                            self.iCol['active'])[:1] in 'TtYy1'
            catType = self.CategoryTypeChoices.index(
                self.grid.GetCellValue(row, self.iCol['catType']))
            self.fixRow(row, catType, active)

    def onActivateAll(self, event):
        self.commit()
        if Model.race:
            for c in Model.race.getAllCategories():
                if not c.active:
                    c.active = True
                    Model.race.setChanged()
        self.state.reset()
        wx.CallAfter(self.refresh)

    def onDeactivateAll(self, event):
        self.commit()
        if Model.race:
            for c in Model.race.getAllCategories():
                if c.active:
                    c.active = False
                    Model.race.setChanged()
        self.state.reset()
        wx.CallAfter(self.refresh)

    def doAutosize(self):
        self.grid.AutoSizeColumns(False)
        colWidth = self.grid.GetColSize(self.iCol['catStr'])
        maxWidth = wx.GetDisplaySize().width / 3
        if colWidth > maxWidth:
            self.grid.SetColSize(self.iCol['catStr'], maxWidth)
            self.grid.ForceRefresh()

    def onNewCategory(self, event):
        self.grid.AppendRows(1)
        self._setRow(r=self.grid.GetNumberRows() - 1,
                     active=True,
                     name=u'<{}>     '.format(_('CategoryName')),
                     catStr='100-199,504,-128')
        self.doAutosize()

    def onDelCategory(self, event):
        r = self.grid.GetGridCursorRow()
        if r is None or r < 0:
            return
        if Utils.MessageOKCancel(
                self, u'{} "{} ({})"?'.format(
                    _('Delete Category'),
                    self.grid.GetCellValue(r, 3).strip(),
                    self.grid.GetCellValue(r, 4).strip(),
                ), _('Delete Category')):
            self.grid.DeleteRows(r, 1, True)

    def onUpCategory(self, event):
        self.grid.SaveEditControlValue()
        self.grid.DisableCellEditControl()
        r = self.grid.GetGridCursorRow()
        Utils.SwapGridRows(self.grid, r, r - 1)
        if r - 1 >= 0:
            self.grid.MoveCursorUp(False)
        self.grid.ClearSelection()
        self.grid.SelectRow(max(r - 1, 0), True)

    def onDownCategory(self, event):
        self.grid.SaveEditControlValue()
        self.grid.DisableCellEditControl()
        r = self.grid.GetGridCursorRow()
        Utils.SwapGridRows(self.grid, r, r + 1)
        if r + 1 < self.grid.GetNumberRows():
            self.grid.MoveCursorDown(False)
        self.grid.ClearSelection()
        self.grid.SelectRow(min(r + 1, self.grid.GetNumberRows() - 1), True)

    def refresh(self, forceRefresh=False):
        self.setGpxDistanceButton.Enable(hasattr(Model.race, 'geoTrack'))

        if not (forceRefresh or self.isExternalChange()
                or self.state.changed()):
            return

        # Fix the height of the column labels.
        dc = wx.WindowDC(self.grid)
        dc.SetFont(self.grid.GetLabelFont())
        textHeight = dc.GetTextExtent('Label')[1]
        self.colLabelHeight = textHeight * max(
            name.count('\n') + 1 for name in self.colnames) + textHeight // 4
        self.grid.SetColLabelSize(self.colLabelHeight)

        with Model.LockRace() as race:
            self.grid.ClearGrid()
            if race is None:
                return

            for c in xrange(self.grid.GetNumberCols()):
                if self.grid.GetColLabelValue(c).startswith(_('Distance')):
                    self.grid.SetColLabelValue(
                        c,
                        u'{}\n({})'.format(_('Distance'),
                                           ['km', 'miles'
                                            ][getattr(race, 'distanceUnit',
                                                      0)]))
                    break

            categories = race.getAllCategories()

            if self.grid.GetNumberRows() > 0:
                self.grid.DeleteRows(0, self.grid.GetNumberRows())
            self.grid.AppendRows(len(categories))

            for r, cat in enumerate(categories):
                self._setRow(
                    r=r,
                    active=cat.active,
                    name=cat.name,
                    gender=getattr(cat, 'gender', None),
                    catStr=cat.catStr,
                    catType=cat.catType,
                    startOffset=cat.startOffset,
                    numLaps=cat._numLaps,
                    raceMinutes=cat.raceMinutes,
                    lappedRidersMustContinue=getattr(
                        cat, 'lappedRidersMustContinue', False),
                    distance=getattr(cat, 'distance', None),
                    distanceType=getattr(cat, 'distanceType',
                                         Model.Category.DistanceByLap),
                    firstLapDistance=getattr(cat, 'firstLapDistance', None),
                    publishFlag=cat.publishFlag,
                    uploadFlag=cat.uploadFlag,
                    seriesFlag=cat.seriesFlag,
                )

            self.doAutosize()
            self.fixCells()

            # Force the grid to the correct size.
            self.grid.FitInside()
            self.GetSizer().Layout()

    def commit(self):
        undo.pushState()
        with Model.LockRace() as race:
            self.grid.SaveEditControlValue()
            self.grid.DisableCellEditControl(
            )  # Make sure the current edit is committed.
            if race is None:
                return
            numStrTuples = []
            for r in xrange(self.grid.GetNumberRows()):
                values = {
                    name: self.grid.GetCellValue(r, c)
                    for name, c in self.iCol.iteritems()
                    if name not in self.computedFields
                }
                values['catType'] = self.CategoryTypeChoices.index(
                    values['catType'])
                values['distanceType'] = self.DistanceTypeChoices.index(
                    values['distanceType'])
                numStrTuples.append(values)
            race.setCategories(numStrTuples)
            race.adjustAllCategoryWaveNumbers()
        wx.CallAfter(Utils.refreshForecastHistory)
Beispiel #5
0
class Properties( wx.Panel ):
	badFileCharsRE = re.compile( '[^a-zA-Z0-9_ ]+' )
	dateFormat = '%Y-%m-%d'

	def __init__( self, parent, id=wx.ID_ANY, addEditButton=True ):
		super(Properties, self).__init__(parent, id)
		
		self.state = RaceInputState()
		
		self.SetBackgroundColour( wx.WHITE )
		
		mainSizer = wx.BoxSizer( wx.VERTICAL )
		self.SetSizer( mainSizer )
		
		bookStyle = (
			  flatnotebook.FNB_NO_NAV_BUTTONS
			| flatnotebook.FNB_NO_X_BUTTON
			| flatnotebook.FNB_VC8
			| flatnotebook.FNB_NODRAG
		)
		self.notebook = flatnotebook.FlatNotebook( self, agwStyle=bookStyle )
		self.notebook.SetBackgroundColour( wx.WHITE )
		self.notebook.SetTabAreaColour( wx.WHITE )
		self.notebook.Bind( wx.EVT_NOTEBOOK_PAGE_CHANGED, self.onPageChanging )

		self.propClassName = [
			('generalInfoProperties',	GeneralInfoProperties,		_('General Info') ),
			('raceOptionsProperties',	RaceOptionsProperties,		_('Race Options') ),
			('notesProperties',			NotesProperties,			_('Notes') ),
			('rfidProperties',			RfidProperties,				_('RFID') ),
			('cameraProperties',		CameraProperties,			_('Camera') ),
			('animationProperties',		AnimationProperties,		_('Animation') ),
			('filesProperties',			FilesProperties,			_('Files') ),
		]
		for prop, PropClass, name in self.propClassName:
			setattr( self, prop, PropClass(self.notebook) )
			self.notebook.AddPage( getattr(self, prop), name )
		
		self.updateFileName()
		
		mainSizer.Add( self.notebook, 1, flag=wx.ALL|wx.EXPAND, border=4 )
		
		if addEditButton:
			hs = wx.BoxSizer( wx.HORIZONTAL )
			
			self.commitButton = wx.Button(self, label=_('Commit'))
			self.commitButton.Bind( wx.EVT_BUTTON, self.commitButtonCallback )
			hs.Add( self.commitButton, flag=wx.TOP|wx.BOTTOM, border=8 )
			
			self.excelButton = wx.Button(self, label=_('Link External Excel Sheet...'))
			self.excelButton.Bind( wx.EVT_BUTTON, self.excelButtonCallback )
			hs.Add( self.excelButton, flag=wx.LEFT|wx.TOP|wx.BOTTOM, border=8 )

			mainSizer.Add( hs, flag=wx.ALL, border=4 )
			
		self.setEditable()
		mainSizer.Fit(self)
		self.Layout()
		
	def onJChipIntegration( self, event ):
		self.rfidProperties.autocorrectLapsDefault.SetValue( not self.rfidProperties.jchip.GetValue() )
	
	def onPageChanging( self, event ):
		'''
		if Model.race:
			notebook = event.GetEventObject()
			notebook.GetPage( event.GetOldSelection() ).commit()
			notebook.GetPage( event.GetSelection() ).refresh()
			self.updateFileName()
		'''
		if hasattr(self, 'cameraProperties'):
			notebook = event.GetEventObject()
			if notebook.GetPage(event.GetOldSelection()) == self.cameraProperties:
				self.cameraProperties.commit()
			if notebook.GetPage(event.GetSelection()) == self.cameraProperties:
				self.cameraProperties.refresh()
		event.Skip()	# Required to properly repaint the screen.
	
	def excelButtonCallback( self, event ):
		mainWin = Utils.getMainWin()
		if mainWin:
			mainWin.menuLinkExcel()
	
	def commitButtonCallback( self, event ):
		mainWin = Utils.getMainWin()
		if Model.race:
			wx.CallAfter( self.commit )
		else:
			Utils.MessageOK( self,
				_('You must have a valid race File|Open...') + u'\n' + _('Or create one with File|New....'), _('Valid Race Required'),
				wx.ICON_WARNING )
	
	def setEditable( self, editable = True ):
		pass
	
	def incNext( self ):
		if not hasattr(self,'generalInfoProperties'):
			return ''

		gi = self.generalInfoProperties
		
		gi.raceNum.SetValue( gi.raceNum.GetValue() + 1 )
		gi.memo.SetValue( '' )
		if	 gi.scheduledStart.GetValue() == '10:00' and gi.minutes.GetValue() == 40 and gi.raceNum.GetValue() == 2:
			gi.scheduledStart.SetValue( '11:30' )
			gi.minutes.SetValue( 50 )
		elif gi.scheduledStart.GetValue() == '11:30' and gi.minutes.GetValue() == 50 and gi.raceNum.GetValue() == 3:
			gi.scheduledStart.SetValue( '13:00' )
			gi.minutes.SetValue( 60 )
		else:
			sStr = '{}'.format(gi.scheduledStart.GetValue())
			fields = sStr.split(':')
			if len(fields) == 2:
				mins = int(fields[0],10) * 60 + int(fields[1],10)
				mins += gi.minutes.GetValue()
				mins += 15	# Add time for a break.
				if (mins/60) >= 24:
					mins = 0
				sNew = '%02d:%02d:00' % (int(mins/60), mins%60)
				gi.scheduledStart.SetValue( sNew )
	
	def onChanged( self, event ):
		self.updateFileName()
	
	def updateFileName( self ):
		if not hasattr(self,'filesProperties') or not hasattr(self,'generalInfoProperties'):
			return ''
		
		gi = self.generalInfoProperties
		
		rDate = gi.date.GetValue().Format(Properties.dateFormat)
		rName = Properties.badFileCharsRE.sub( ' ', gi.raceName.GetValue() ).strip()
		rNum = gi.raceNum.GetValue()
		rMemo = Properties.badFileCharsRE.sub( ' ', gi.memo.GetValue() ).strip()
		
		fname = u'%s-%s-r%d-%s.cmn' % (rDate, rName, rNum, rMemo )
		self.filesProperties.fileName.SetLabel( fname )
		return fname
	
	def saveFileNameFields( self ):
		if not hasattr(self,'generalInfoProperties'):
			return ''
		gi = self.generalInfoProperties
		
		for f in ['date', 'raceName', 'raceNum', 'memo']:
			setattr(self, f + 'Original', getattr(gi, f).GetValue())
		
	def restoreFileNameFields( self ):
		if not hasattr(self,'generalInfoProperties'):
			return ''
		gi = self.generalInfoProperties
		for f in ['date', 'raceName', 'raceNum', 'memo']:
			getattr(gi, f).SetValue( getattr(self, f + 'Original') )
	
	def getFileName( self ):
		return self.updateFileName()
	
	def refresh( self ):
		if not self.state.changed():
			return

		with Model.LockRace() as race:
			self.setEditable( False )
			if race is None:
				return
			
			for prop, PropClass, name in self.propClassName:
				getattr(self, prop).refresh()
			
			self.saveFileNameFields()
			
		self.GetSizer().Layout()
		
	def doCommit( self ):
		undo.pushState()
		with Model.LockRace() as race:
			if race is None:
				return
			for prop, PropClass, name in self.propClassName:
				getattr(self, prop).commit()
			race.setChanged()
			
		if Utils.getMainWin():
			Utils.getMainWin().record.setTimeTrialInput( race.isTimeTrial )
		
	def commit( self ):
		success = SetNewFilename( self, self )
		self.doCommit()
		Model.resetCache()
		mainWin = Utils.getMainWin()
		if mainWin:
			wx.CallAfter( mainWin.writeRace, False )
		wx.CallAfter( Utils.refreshForecastHistory )
		if not success and mainWin:
			wx.CallAfter( mainWin.showPageName, _("Properties") )
Beispiel #6
0
	def __init__( self, parent, id=wx.ID_ANY, size=wx.DefaultSize ):
		super(Primes, self).__init__( parent, id, size=size )
		
		self.state = RaceInputState()
		
		vsOverall = wx.BoxSizer( wx.VERTICAL )
		
		#---------------------------------------------------------------
		self.colNameFields = (
			(_('Prime For'),			'effortType',	's'),
			(_('or Custom'),			'effortCustom',	's'),
			(_('Position'),				'position',		'i'),
			(_('Laps\nTo Go'),			'lapsToGo',		'i'),
			(_('Sponsor'),				'sponsor', 		's'),
			(_('Cash'),					'cash', 		'f'),
			(_('Points'),				'points', 		'i'),
			(_('Merchandise'),			'merchandise', 	's'),
			(_('Winner\nBib'),			'winnerBib',	'i'),
			(u'',						'winnerInfo',	's'),
		)
		self.colnames = [colName for colName, fieldName, dataType in self.colNameFields]
		self.iCol = dict( (fieldName, i) for i, (colName, fieldName, dataType) in enumerate(self.colNameFields) if fieldName )
		self.grid = ReorderableGrid( self )
		self.grid.CreateGrid( 0, len(self.colNameFields) )
		GetTranslation = _
		for col, (colName, fieldName, dataType) in enumerate(self.colNameFields):
			self.grid.SetColLabelValue( col, colName )
			attr = wx.grid.GridCellAttr()
			if fieldName == 'effortType':
				attr.SetEditor( wx.grid.GridCellChoiceEditor(choices=[GetTranslation(name) for code, name in EffortChoices]) )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_TOP )
			elif fieldName == 'position':
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_TOP )
			elif fieldName == 'winnerInfo':
				attr.SetReadOnly( True )
			elif dataType == 'i':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
				attr.SetEditor( wx.grid.GridCellFloatEditor(precision=0) )
				attr.SetRenderer( wx.grid.GridCellFloatRenderer(precision=0) )
			elif dataType == 'f':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
				attr.SetEditor( wx.grid.GridCellFloatEditor(precision=2) )
				attr.SetRenderer( wx.grid.GridCellFloatRenderer(precision=2) )
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
			self.grid.SetColAttr( col, attr )
			if fieldName == 'lapsToGo':
				self.lapsToGoCol = col
		
		self.grid.Bind( wx.grid.EVT_GRID_CELL_CHANGE, self.onCellChange )
		self.grid.AutoSizeColumns( False )
		self.grid.AutoSizeRows( False )

		#---------------------------------------------------------------
		self.photosButton = wx.Button( self, label=u'{}...'.format(_('Photos')) )
		self.photosButton.Bind( wx.EVT_BUTTON, self.onPhotos )
		self.finishStrip = wx.Button( self, label=u'{}...'.format(_('Finish Strip')) )
		self.finishStrip.Bind( wx.EVT_BUTTON, self.onFinishStrip )
		self.history = wx.Button( self, label=u'{}...'.format(_('History')) )
		self.history.Bind( wx.EVT_BUTTON, self.onHistory )
		
		self.newButton = wx.Button( self, id=wx.ID_NEW )
		self.newButton.SetToolTip( wx.ToolTip(_('Create a new Prime')) )
		self.newButton.Bind( wx.EVT_BUTTON, self.onNew )
		self.nextPositionButton = wx.Button( self, label=('Next Position') )
		self.nextPositionButton.SetToolTip( wx.ToolTip(_('Create a Prime from an Existing Prime for the Next Position')) )
		self.nextPositionButton.Bind( wx.EVT_BUTTON, self.onNextPosition )
		self.nextPrimeButton = wx.Button( self, label=('Next Prime') )
		self.nextPrimeButton.SetToolTip( wx.ToolTip(_('Create a Prime from an Existing Prime')) )
		self.nextPrimeButton.Bind( wx.EVT_BUTTON, self.onNextPrime )
		self.deleteButton = wx.Button( self, id=wx.ID_DELETE )
		self.deleteButton.SetToolTip( wx.ToolTip(_('Delete a Prime')) )
		self.deleteButton.Bind( wx.EVT_BUTTON, self.onDelete )
		hsButtons = wx.BoxSizer( wx.HORIZONTAL )
		hsButtons.Add( self.photosButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.finishStrip, flag=wx.ALL, border=4 )
		hsButtons.Add( self.history, flag=wx.ALL, border=4 )
		hsButtons.AddStretchSpacer()
		hsButtons.Add( self.newButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.nextPositionButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.nextPrimeButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.deleteButton, flag=wx.ALL, border=4 )
		
		#---------------------------------------------------------------
		
		vsOverall.Add( self.grid, 1, flag=wx.EXPAND|wx.ALL, border=4 )
		vsOverall.Add( hsButtons, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, border=4 )
		self.SetSizer( vsOverall )
Beispiel #7
0
class Primes( wx.Panel ):
	def __init__( self, parent, id=wx.ID_ANY, size=wx.DefaultSize ):
		super(Primes, self).__init__( parent, id, size=size )
		
		self.state = RaceInputState()
		
		vsOverall = wx.BoxSizer( wx.VERTICAL )
		
		#---------------------------------------------------------------
		self.colNameFields = (
			(_('Prime For'),			'effortType',	's'),
			(_('or Custom'),			'effortCustom',	's'),
			(_('Position'),				'position',		'i'),
			(_('Laps\nTo Go'),			'lapsToGo',		'i'),
			(_('Sponsor'),				'sponsor', 		's'),
			(_('Cash'),					'cash', 		'f'),
			(_('Points'),				'points', 		'i'),
			(_('Merchandise'),			'merchandise', 	's'),
			(_('Winner\nBib'),			'winnerBib',	'i'),
			(u'',						'winnerInfo',	's'),
		)
		self.colnames = [colName for colName, fieldName, dataType in self.colNameFields]
		self.iCol = dict( (fieldName, i) for i, (colName, fieldName, dataType) in enumerate(self.colNameFields) if fieldName )
		self.grid = ReorderableGrid( self )
		self.grid.CreateGrid( 0, len(self.colNameFields) )
		GetTranslation = _
		for col, (colName, fieldName, dataType) in enumerate(self.colNameFields):
			self.grid.SetColLabelValue( col, colName )
			attr = wx.grid.GridCellAttr()
			if fieldName == 'effortType':
				attr.SetEditor( wx.grid.GridCellChoiceEditor(choices=[GetTranslation(name) for code, name in EffortChoices]) )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_TOP )
			elif fieldName == 'position':
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_TOP )
			elif fieldName == 'winnerInfo':
				attr.SetReadOnly( True )
			elif dataType == 'i':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
				attr.SetEditor( wx.grid.GridCellFloatEditor(precision=0) )
				attr.SetRenderer( wx.grid.GridCellFloatRenderer(precision=0) )
			elif dataType == 'f':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
				attr.SetEditor( wx.grid.GridCellFloatEditor(precision=2) )
				attr.SetRenderer( wx.grid.GridCellFloatRenderer(precision=2) )
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
			self.grid.SetColAttr( col, attr )
			if fieldName == 'lapsToGo':
				self.lapsToGoCol = col
		
		self.grid.Bind( wx.grid.EVT_GRID_CELL_CHANGE, self.onCellChange )
		self.grid.AutoSizeColumns( False )
		self.grid.AutoSizeRows( False )

		#---------------------------------------------------------------
		self.photosButton = wx.Button( self, label=u'{}...'.format(_('Photos')) )
		self.photosButton.Bind( wx.EVT_BUTTON, self.onPhotos )
		self.finishStrip = wx.Button( self, label=u'{}...'.format(_('Finish Strip')) )
		self.finishStrip.Bind( wx.EVT_BUTTON, self.onFinishStrip )
		self.history = wx.Button( self, label=u'{}...'.format(_('History')) )
		self.history.Bind( wx.EVT_BUTTON, self.onHistory )
		
		self.newButton = wx.Button( self, id=wx.ID_NEW )
		self.newButton.SetToolTip( wx.ToolTip(_('Create a new Prime')) )
		self.newButton.Bind( wx.EVT_BUTTON, self.onNew )
		self.nextPositionButton = wx.Button( self, label=('Next Position') )
		self.nextPositionButton.SetToolTip( wx.ToolTip(_('Create a Prime from an Existing Prime for the Next Position')) )
		self.nextPositionButton.Bind( wx.EVT_BUTTON, self.onNextPosition )
		self.nextPrimeButton = wx.Button( self, label=('Next Prime') )
		self.nextPrimeButton.SetToolTip( wx.ToolTip(_('Create a Prime from an Existing Prime')) )
		self.nextPrimeButton.Bind( wx.EVT_BUTTON, self.onNextPrime )
		self.deleteButton = wx.Button( self, id=wx.ID_DELETE )
		self.deleteButton.SetToolTip( wx.ToolTip(_('Delete a Prime')) )
		self.deleteButton.Bind( wx.EVT_BUTTON, self.onDelete )
		hsButtons = wx.BoxSizer( wx.HORIZONTAL )
		hsButtons.Add( self.photosButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.finishStrip, flag=wx.ALL, border=4 )
		hsButtons.Add( self.history, flag=wx.ALL, border=4 )
		hsButtons.AddStretchSpacer()
		hsButtons.Add( self.newButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.nextPositionButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.nextPrimeButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.deleteButton, flag=wx.ALL, border=4 )
		
		#---------------------------------------------------------------
		
		vsOverall.Add( self.grid, 1, flag=wx.EXPAND|wx.ALL, border=4 )
		vsOverall.Add( hsButtons, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, border=4 )
		self.SetSizer( vsOverall )
	
	def onCellChange( self, event ):
		row, col = event.GetRow(), event.GetCol()
		colName = self.colNameFields[col][1]
		GetTranslation = _
		if colName == 'effortCustom':
			if self.grid.GetCellValue(row, col).strip():
				self.grid.SetCellValue( row, col-1, GetTranslation('Custom') )
		elif colName == 'effortType':
			if self.grid.GetCellValue(row, col) != 'Custom':
				self.grid.SetCellValue( row, col+1, u'' )
		elif colName == 'winnerBib':
			bib = int( u''.join(c for c in self.grid.GetCellValue(row, col) if c.isdigit()) )
			self.grid.SetCellValue( row, col+1, getWinnerInfo(bib) )
		
		wx.CallAfter( self.grid.AutoSizeColumns, False )
	
	def getT( self ):
		race = Model.race
		if not race:
			return 0
		row = self.grid.GetGridCursorRow()
		if row is None or row < 0:
			return
		lapsToGo = int( u''.join(c for c in self.grid.GetCellValue(row, self.lapsToGoCol) if c.isdigit()) )
		tMax = 0.0
		for rr in GetResults(None):
			try:
				tMax = min( tMax, rr.raceTimes[-1-lapsToGo] )
			except IndexError:
				pass
		return tMax
		
	def onPhotos( self, event ):
		mainWin = Utils.getMainWin()
		if not mainWin:
			return
		mainWin.photoDialog.SetT( self.getT() )
		mainWin.photoDialog.Show()
		
	def onFinishStrip( self, event ):
		ShowFinishStrip( self, self.getT() )
	
	def onHistory( self, event ):
		mainWin = Utils.getMainWin()
		if not mainWin:
			return
		mainWin.openMenuWindow( 'history' )
	
	def selectGridRow( self, row ):
		self.grid.SelectRow( row )
		self.grid.SetGridCursor( row, 0 )
		self.grid.ShowCellEditControl()
	
	def onNew( self, event ):
		race = Model.race
		if not race:
			Utils.MessageOK( self, _('You must have a Race to create a Prime.'), _('Missing Race') )
			return
		self.commit()
		race.primes = getattr(race, 'primes', [])
		rowNew = len( race.primes )
		race.primes.append( {} )
		self.updateGrid()
		self.selectGridRow( rowNew )
	
	def onNextPosition( self, event ):
		rowNext = self.grid.GetGridCursorRow()
		if rowNext is None or rowNext < 0:
			return
		self.commit()
		race = Model.race
		if not race:
			Utils.MessageOK( self, _('You must have a Race to create a next Prime.'), _('Missing Race') )
			return
		
		nextPrime = race.primes[rowNext].copy()
		
		nextPoints = {
			(1, 5):	3,
			(2, 3): 2,
			(3, 2): 1,
		}.get( (nextPrime['position'], nextPrime['points']), None )
		
		if nextPoints is not None:
			nextPrime['points'] = nextPoints
		
		try:
			nextPrime['position'] += 1
		except:
			pass
		nextPrime['winnerBib'] = None
		race.primes = race.primes[:rowNext+1] + [nextPrime] + race.primes[rowNext+1:]
		self.updateGrid()
		self.selectGridRow( rowNext + 1 )
	
	def onNextPrime( self, event ):
		rowNext = self.grid.GetGridCursorRow()
		if rowNext is None or rowNext < 0:
			return
		self.commit()
		race = Model.race
		if not race:
			Utils.MessageOK( self, _('You must have a Race to create a next Prime.'), _('Missing Race') )
			return
		nextPrime = race.primes[rowNext].copy()
		nextPrime['position'] = 1
		if nextPrime['points']:
			nextPrime['points'] = 5
		if nextPrime['lapsToGo'] > 0:
			nextPrime['lapsToGo'] -= 1
		nextPrime['winnerBib'] = None
		race.primes = race.primes[:rowNext+1] + [nextPrime] + race.primes[rowNext+1:]
		self.updateGrid()
		self.selectGridRow( rowNext + 1 )
		
	def onDelete( self, event ):
		rowDelete = self.grid.GetGridCursorRow()
		if rowDelete is None or rowDelete < 0:
			return
		self.commit()
		race = Model.race
		if race and Utils.MessageOKCancel( self, u'{}: {} ?'.format(_('Delete Prime'), rowDelete+1), _('Confirm Delete Primes') ):
			race.primes = getattr(race, 'primes', [])
			try:
				del race.primes[rowDelete]
			except Exception as e:
				return
			self.updateGrid()
			if race.primes:
				self.grid.SetGridCursor( rowDelete, 0 )
		
	def getSponsors( self ):
		race = Model.race
		if not race:
			return []
		sponsors = [prime.get('sponsor', u'') for prime in getattr(race, 'primes', [])] + [race.organizer]
		sponsors = [s for s in sponsors if s]
		return sponsors
		
	def getMerchandise( self ):
		race = Model.race
		if not race:
			return []
		merchandise = [prime.get('merchandise', u'') for prime in getattr(race, 'primes', [])]
		merchandise = [m for m in merchandise if m]
		return merchandise
	
	def setRow( self, prime, row, updateGrid=True ):
		GetTranslation = _
		
		data = []
		for col, (name, attr, dataType) in enumerate(self.colNameFields):
			if attr == 'effortType':
				effortType = prime.get('effortType', 'Pack')
				v = GetTranslation(effortType)
			elif attr == 'position':
				position = prime.get('position', 1)
				v = u'' if position == 0 else unicode(position)
			elif attr == 'winnerBib':
				winnerBib = prime.get('winnerBib', None)
				v = u'' if not winnerBib else unicode(winnerBib)
			elif attr == 'winnerInfo':
				v = getWinnerInfo(winnerBib)
			elif attr == 'cash':
				cash = prime.get('cash', 0.0)
				v = u'{:.2f}'.format( prime.get('cash', 0.0) ) if cash else u''
			else:
				v = unicode(prime.get(attr, u''))
			if updateGrid:
				self.grid.SetCellValue( row, col, v )
			data.append( v )
		
		return data
	
	def getRow( self, row ):
		values = {}
		for col, (name, attr, dataType) in enumerate(self.colNameFields):
			v = self.grid.GetCellValue( row, col ).strip()
			if dataType == 'i':
				v = u''.join( c for c in v if c.isdigit() )
				v = int( v or 0 )
			elif dataType == 'f':
				v = u''.join( c for c in v if c.isdigit() or c == '.')
				v = float( v or 0.0 )
			values[attr] = v
		
		GetTranslation = _
		for code, name in EffortChoices:
			if values['effortType'] == GetTranslation(name):
				values['effortType'] = name
				break
		
		if values['effortCustom']:
			values['effortType'] = 'Custom'
		return values
	
	def updateGrid( self ):
		race = Model.race
		if not race or not getattr(race, 'primes', None):
			self.grid.ClearGrid()
			return
		
		Utils.AdjustGridSize( self.grid, len(race.primes) )
		for row, prime in enumerate(race.primes):
			self.setRow( prime, row )
		
		self.grid.AutoSizeColumns( False )								# Resize to fit the column name.
		self.grid.AutoSizeRows( False )
	
	def refresh( self ):
		if self.state.changed():
			self.updateGrid()
		
	def commit( self ):
		self.grid.SaveEditControlValue()	# Make sure the current edit is committed.
		self.grid.DisableCellEditControl()
		race = Model.race
		if not race:
			return
		race.primes = [self.getRow(row) for row in xrange(self.grid.GetNumberRows())]
Beispiel #8
0
	def __init__( self, parent, id = wx.ID_ANY ):
		wx.Panel.__init__(self, parent, id)
		
		self.state = RaceInputState()
		
		vs = wx.BoxSizer( wx.VERTICAL )
		
		self.ignoreColour = wx.Colour( 80, 80, 80 )
		self.inactiveColour = wx.Colour( 220, 220, 220 )
		
		border = 4
		flag = wx.ALL
		
		hs = wx.BoxSizer( wx.HORIZONTAL )
		
		self.activateAllButton = wx.Button(self, label=_('&Activate All'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onActivateAll, self.activateAllButton )
		hs.Add( self.activateAllButton, 0, border = border, flag = flag )

		self.deactivateAllButton = wx.Button(self, label=_('&Deactivate All'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onDeactivateAll, self.deactivateAllButton )
		hs.Add( self.deactivateAllButton, 0, border = border, flag = (flag & ~wx.LEFT) )

		hs.AddSpacer( 8 )
		
		self.newCategoryButton = wx.Button(self, label=_('&New Category'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onNewCategory, self.newCategoryButton )
		hs.Add( self.newCategoryButton, 0, border = border, flag = flag )
		
		self.delCategoryButton = wx.Button(self, label=_('&Delete Category'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onDelCategory, self.delCategoryButton )
		hs.Add( self.delCategoryButton, 0, border = border, flag = flag )

		hs.AddSpacer( 8 )
		
		self.upCategoryButton = wx.Button(self, label=_('Move &Up'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onUpCategory, self.upCategoryButton )
		hs.Add( self.upCategoryButton, 0, border = border, flag = flag )

		self.downCategoryButton = wx.Button(self, label=_('Move D&own'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onDownCategory, self.downCategoryButton )
		hs.Add( self.downCategoryButton, 0, border = border, flag = (flag & ~wx.LEFT) )

		hs.AddSpacer( 8 )
		
		self.addExceptionsButton = wx.Button(self, label=_('&Add Bib Exceptions'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onAddExceptions, self.addExceptionsButton )
		hs.Add( self.addExceptionsButton, 0, border = border, flag = (flag & ~wx.LEFT) )

		hs.AddSpacer( 8 )
		
		self.updateStartWaveNumbersButton = wx.Button(self, label=_('&Update Start Wave Numbers'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onUpdateStartWaveNumbers, self.updateStartWaveNumbersButton )
		hs.Add( self.updateStartWaveNumbersButton, 0, border = border, flag = (flag & ~wx.LEFT) )

		self.normalizeButton = wx.Button(self, label=_('N&ormalize'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onNormalize, self.normalizeButton )
		hs.Add( self.normalizeButton, 0, border = border, flag = (flag & ~wx.LEFT) )

		hs.AddStretchSpacer()
		
		self.printButton = wx.Button( self, label=u'{}...'.format(_('Print')), style=wx.BU_EXACTFIT )
		self.Bind( wx.EVT_BUTTON, self.onPrint, self.printButton )
		hs.Add( self.printButton, 0, border = border, flag = (flag & ~wx.LEFT) )
		
		self.grid = ReorderableGrid( self )
		self.colNameFields = [
			(u'',						None),
			(_('Category Type'),		'catType'),
			(_('Active'),				'active'),
			(_('Name'),					'name'),
			(_('Gender'),				'gender'),
			(_('Numbers'),				'catStr'),
			(_('Start\nOffset'),		'startOffset'),
			(_('Race\nLaps'),			'numLaps'),
			(_('Lapped\nRiders\nContinue'),	'lappedRidersMustContinue'),
			(_('Distance'),				'distance'),
			(_('Dist.\nBy'),			'distanceType'),
			(_('First\nLap\nDist.'),	'firstLapDistance'),
			(_('80%\nLap\nTime'),		'rule80Time'),
			(_('CrossMgr\nEstimated\nLaps'),	'suggestedLaps'),
			(_('Publish'),				'publishFlag'),
			(_('Upload'),				'uploadFlag'),
			(_('Series'),				'seriesFlag'),
		]
		self.computedFields = {'rule80Time', 'suggestedLaps'}
		self.colnames = [colName if not colName.startswith('_') else _('Name Copy') for colName, fieldName in self.colNameFields]
		self.iCol = { fieldName:i for i, (colName, fieldName) in enumerate(self.colNameFields) if fieldName and not colName.startswith('_') }
		
		self.activeColumn = self.iCol['active']
		self.genderColumn = self.iCol['gender']
		self.numbersColumn = self.iCol['catStr']
		self.grid.CreateGrid( 0, len(self.colnames) )
		self.grid.SetRowLabelSize(32)
		self.grid.SetMargins(0,0)
		for col, name in enumerate(self.colnames):
			self.grid.SetColLabelValue( col, name )
			
		self.cb = None
		
		self.boolCols = set()
		self.choiceCols = set()
		self.readOnlyCols = set()
		self.dependentCols = set()
		
		# Set column attributes for the table.
		for col, (colName, fieldName) in enumerate(self.colNameFields):
			attr = gridlib.GridCellAttr()
			
			if fieldName is None:
				attr.SetRenderer( CategoryIconRenderer() )
				attr.SetAlignment( wx.ALIGN_LEFT, wx.ALIGN_CENTRE )
				attr.SetReadOnly( True )
				self.readOnlyCols.add( col )
				
			elif fieldName == 'catType':
				self.catTypeWidth = 64
				attr.SetEditor( gridlib.GridCellChoiceEditor(self.CategoryTypeChoices, False) )
				attr.SetAlignment( wx.ALIGN_LEFT, wx.ALIGN_CENTRE )
				self.choiceCols.add( col )
				
			elif fieldName in {'active', 'lappedRidersMustContinue', 'publishFlag', 'uploadFlag', 'seriesFlag'}:
				boolEditor = gridlib.GridCellBoolEditor()
				boolEditor.UseStringValues( '1', '0' )
				attr.SetEditor( boolEditor )
				attr.SetRenderer( gridlib.GridCellBoolRenderer() )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				self.boolCols.add( col )
				if fieldName == 'lappedRidersMustContinue':
					self.dependentCols.add( col )
				
			elif fieldName == 'gender':
				attr.SetEditor( gridlib.GridCellChoiceEditor([_('Open'),_('Men'),_('Women')], False) )
				self.choiceCols.add( col )
				
			elif fieldName == 'startOffset':
				attr.SetEditor( TimeEditor() )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				self.dependentCols.add( col )
				
			elif fieldName == 'numLaps':
				attr.SetEditor( wx.grid.GridCellNumberEditor() )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				self.dependentCols.add( col )
				
			elif fieldName in ['rule80Time', 'suggestedLaps']:
				attr.SetReadOnly( True )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				self.readOnlyCols.add( col )
				self.dependentCols.add( col )
				
			elif fieldName in ['distance', 'firstLapDistance'] :
				attr.SetEditor( gridlib.GridCellFloatEditor(7, 3) )
				attr.SetRenderer( gridlib.GridCellFloatRenderer(7, 3) )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				self.dependentCols.add( col )
				
			elif fieldName == 'distanceType':
				attr.SetEditor( gridlib.GridCellChoiceEditor(self.DistanceTypeChoices, False) )
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_CENTRE )
				self.choiceCols.add( col )
				self.dependentCols.add( col )
				
			elif colName == '_name2':
				attr.SetAlignment( wx.ALIGN_LEFT, wx.ALIGN_CENTRE )
				attr.SetBackgroundColour( wx.Colour(240,240,240) )
				attr.SetReadOnly( True )
				
			self.grid.SetColAttr( col, attr )
		
		self.Bind( gridlib.EVT_GRID_CELL_LEFT_CLICK, self.onGridLeftClick )
		self.Bind( gridlib.EVT_GRID_SELECT_CELL, self.onCellSelected )
		self.Bind( gridlib.EVT_GRID_CELL_CHANGE, self.onCellChanged )
		self.Bind( gridlib.EVT_GRID_EDITOR_CREATED, self.onEditorCreated )
		
		vs.Add( hs, 0, flag=wx.EXPAND|wx.ALL, border = 4 )
		vs.Add( self.grid, 1, flag=wx.GROW|wx.ALL|wx.EXPAND )
		
		self.rowCur = 0
		self.colCur = 0
		self.SetSizer(vs)
Beispiel #9
0
class Categories( wx.Panel ):
	CategoryTypeChoices = [_('Start Wave'),u'    ' + _('Component'),_('Custom')]
	DistanceTypeChoices = [_('Lap'),_('Race')]
	
	def __init__( self, parent, id = wx.ID_ANY ):
		wx.Panel.__init__(self, parent, id)
		
		self.state = RaceInputState()
		
		vs = wx.BoxSizer( wx.VERTICAL )
		
		self.ignoreColour = wx.Colour( 80, 80, 80 )
		self.inactiveColour = wx.Colour( 220, 220, 220 )
		
		border = 4
		flag = wx.ALL
		
		hs = wx.BoxSizer( wx.HORIZONTAL )
		
		self.activateAllButton = wx.Button(self, label=_('&Activate All'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onActivateAll, self.activateAllButton )
		hs.Add( self.activateAllButton, 0, border = border, flag = flag )

		self.deactivateAllButton = wx.Button(self, label=_('&Deactivate All'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onDeactivateAll, self.deactivateAllButton )
		hs.Add( self.deactivateAllButton, 0, border = border, flag = (flag & ~wx.LEFT) )

		hs.AddSpacer( 8 )
		
		self.newCategoryButton = wx.Button(self, label=_('&New Category'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onNewCategory, self.newCategoryButton )
		hs.Add( self.newCategoryButton, 0, border = border, flag = flag )
		
		self.delCategoryButton = wx.Button(self, label=_('&Delete Category'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onDelCategory, self.delCategoryButton )
		hs.Add( self.delCategoryButton, 0, border = border, flag = flag )

		hs.AddSpacer( 8 )
		
		self.upCategoryButton = wx.Button(self, label=_('Move &Up'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onUpCategory, self.upCategoryButton )
		hs.Add( self.upCategoryButton, 0, border = border, flag = flag )

		self.downCategoryButton = wx.Button(self, label=_('Move D&own'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onDownCategory, self.downCategoryButton )
		hs.Add( self.downCategoryButton, 0, border = border, flag = (flag & ~wx.LEFT) )

		hs.AddSpacer( 8 )
		
		self.addExceptionsButton = wx.Button(self, label=_('&Add Bib Exceptions'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onAddExceptions, self.addExceptionsButton )
		hs.Add( self.addExceptionsButton, 0, border = border, flag = (flag & ~wx.LEFT) )

		hs.AddSpacer( 8 )
		
		self.updateStartWaveNumbersButton = wx.Button(self, label=_('&Update Start Wave Numbers'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onUpdateStartWaveNumbers, self.updateStartWaveNumbersButton )
		hs.Add( self.updateStartWaveNumbersButton, 0, border = border, flag = (flag & ~wx.LEFT) )

		self.normalizeButton = wx.Button(self, label=_('N&ormalize'), style=wx.BU_EXACTFIT)
		self.Bind( wx.EVT_BUTTON, self.onNormalize, self.normalizeButton )
		hs.Add( self.normalizeButton, 0, border = border, flag = (flag & ~wx.LEFT) )

		hs.AddStretchSpacer()
		
		self.printButton = wx.Button( self, label=u'{}...'.format(_('Print')), style=wx.BU_EXACTFIT )
		self.Bind( wx.EVT_BUTTON, self.onPrint, self.printButton )
		hs.Add( self.printButton, 0, border = border, flag = (flag & ~wx.LEFT) )
		
		self.grid = ReorderableGrid( self )
		self.colNameFields = [
			(u'',						None),
			(_('Category Type'),		'catType'),
			(_('Active'),				'active'),
			(_('Name'),					'name'),
			(_('Gender'),				'gender'),
			(_('Numbers'),				'catStr'),
			(_('Start\nOffset'),		'startOffset'),
			(_('Race\nLaps'),			'numLaps'),
			(_('Lapped\nRiders\nContinue'),	'lappedRidersMustContinue'),
			(_('Distance'),				'distance'),
			(_('Dist.\nBy'),			'distanceType'),
			(_('First\nLap\nDist.'),	'firstLapDistance'),
			(_('80%\nLap\nTime'),		'rule80Time'),
			(_('CrossMgr\nEstimated\nLaps'),	'suggestedLaps'),
			(_('Publish'),				'publishFlag'),
			(_('Upload'),				'uploadFlag'),
			(_('Series'),				'seriesFlag'),
		]
		self.computedFields = {'rule80Time', 'suggestedLaps'}
		self.colnames = [colName if not colName.startswith('_') else _('Name Copy') for colName, fieldName in self.colNameFields]
		self.iCol = { fieldName:i for i, (colName, fieldName) in enumerate(self.colNameFields) if fieldName and not colName.startswith('_') }
		
		self.activeColumn = self.iCol['active']
		self.genderColumn = self.iCol['gender']
		self.numbersColumn = self.iCol['catStr']
		self.grid.CreateGrid( 0, len(self.colnames) )
		self.grid.SetRowLabelSize(32)
		self.grid.SetMargins(0,0)
		for col, name in enumerate(self.colnames):
			self.grid.SetColLabelValue( col, name )
			
		self.cb = None
		
		self.boolCols = set()
		self.choiceCols = set()
		self.readOnlyCols = set()
		self.dependentCols = set()
		
		# Set column attributes for the table.
		for col, (colName, fieldName) in enumerate(self.colNameFields):
			attr = gridlib.GridCellAttr()
			
			if fieldName is None:
				attr.SetRenderer( CategoryIconRenderer() )
				attr.SetAlignment( wx.ALIGN_LEFT, wx.ALIGN_CENTRE )
				attr.SetReadOnly( True )
				self.readOnlyCols.add( col )
				
			elif fieldName == 'catType':
				self.catTypeWidth = 64
				attr.SetEditor( gridlib.GridCellChoiceEditor(self.CategoryTypeChoices, False) )
				attr.SetAlignment( wx.ALIGN_LEFT, wx.ALIGN_CENTRE )
				self.choiceCols.add( col )
				
			elif fieldName in {'active', 'lappedRidersMustContinue', 'publishFlag', 'uploadFlag', 'seriesFlag'}:
				boolEditor = gridlib.GridCellBoolEditor()
				boolEditor.UseStringValues( '1', '0' )
				attr.SetEditor( boolEditor )
				attr.SetRenderer( gridlib.GridCellBoolRenderer() )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				self.boolCols.add( col )
				if fieldName == 'lappedRidersMustContinue':
					self.dependentCols.add( col )
				
			elif fieldName == 'gender':
				attr.SetEditor( gridlib.GridCellChoiceEditor([_('Open'),_('Men'),_('Women')], False) )
				self.choiceCols.add( col )
				
			elif fieldName == 'startOffset':
				attr.SetEditor( TimeEditor() )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				self.dependentCols.add( col )
				
			elif fieldName == 'numLaps':
				attr.SetEditor( wx.grid.GridCellNumberEditor() )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				self.dependentCols.add( col )
				
			elif fieldName in ['rule80Time', 'suggestedLaps']:
				attr.SetReadOnly( True )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				self.readOnlyCols.add( col )
				self.dependentCols.add( col )
				
			elif fieldName in ['distance', 'firstLapDistance'] :
				attr.SetEditor( gridlib.GridCellFloatEditor(7, 3) )
				attr.SetRenderer( gridlib.GridCellFloatRenderer(7, 3) )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				self.dependentCols.add( col )
				
			elif fieldName == 'distanceType':
				attr.SetEditor( gridlib.GridCellChoiceEditor(self.DistanceTypeChoices, False) )
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_CENTRE )
				self.choiceCols.add( col )
				self.dependentCols.add( col )
				
			elif colName == '_name2':
				attr.SetAlignment( wx.ALIGN_LEFT, wx.ALIGN_CENTRE )
				attr.SetBackgroundColour( wx.Colour(240,240,240) )
				attr.SetReadOnly( True )
				
			self.grid.SetColAttr( col, attr )
		
		self.Bind( gridlib.EVT_GRID_CELL_LEFT_CLICK, self.onGridLeftClick )
		self.Bind( gridlib.EVT_GRID_SELECT_CELL, self.onCellSelected )
		self.Bind( gridlib.EVT_GRID_CELL_CHANGE, self.onCellChanged )
		self.Bind( gridlib.EVT_GRID_EDITOR_CREATED, self.onEditorCreated )
		
		vs.Add( hs, 0, flag=wx.EXPAND|wx.ALL, border = 4 )
		vs.Add( self.grid, 1, flag=wx.GROW|wx.ALL|wx.EXPAND )
		
		self.rowCur = 0
		self.colCur = 0
		self.SetSizer(vs)

	def onPrint( self, event ):
		self.commit()
		PrintCategories()
	
	def onNormalize( self, event ):
		self.commit()
		if Model.race:
			Model.race.normalizeCategories()
			self.refresh()
			
	#------------------------------------------
	
	def onGridLeftClick( self, event ):
		if event.GetCol() in self.boolCols:
			r, c = event.GetRow(), event.GetCol()
			if c == self.iCol['active']:
				active = (self.grid.GetCellValue(r, self.iCol['active']) == u'1')
				wx.CallAfter( self.fixRow, r, self.CategoryTypeChoices.index(self.grid.GetCellValue(r, self.iCol['catType'])), not active )
			self.grid.SetCellValue( r, c, '1' if self.grid.GetCellValue(r, c) != '1' else '0' )
		event.Skip()
	
	def onCellSelected( self, event ):
		self.rowCur = event.GetRow()
		self.colCur = event.GetCol()
		if self.colCur in self.choiceCols or self.colCur in self.boolCols:
			wx.CallAfter( self.grid.EnableCellEditControl )
		event.Skip()

	def onCellChanged( self, event ):
		self.rowCur = event.GetRow()
		self.colCur = event.GetCol()
		if self.colCur in [1, 2]:
			self.fixCells()
		event.Skip()

	def onEditorCreated( self, event ):
		if event.GetCol() == self.numbersColumn:
			ctrl = event.GetControl()
			ctrl.Bind( wx.EVT_KEY_DOWN, self.onNumbersKeyEvent )
			ctrl.Bind( wx.EVT_TEXT_PASTE, self.onPaste )
		event.Skip()
	
	def getCleanClipboardText( self ):
		if wx.TheClipboard.Open():
			data = wx.TextDataObject()
			if wx.TheClipboard.GetData(data):
				txt = data.GetText()
				txt = re.sub( '[^0-9,-]+', ',', txt )
			wx.TheClipboard.Close()
			return txt
		return None
	
	def pasteFromClipboard( self, event ):
		txt = self.getCleanClipboardText()
		if txt:
			event.GetEventObject().WriteText( txt )
			return True
		return False
		
	def onNumbersKeyEvent( self, event ):
		# Handle column pastes from Excel when there are newlines.
		if event.GetModifiers() == wx.MOD_CONTROL and event.GetKeyCode() == 86:
			if self.pasteFromClipboard( event ):
				return
		event.Skip()

	def onPaste( self, event ):
		self.pasteFromClipboard( event )
		event.Skip()
		
	#------------------------------------------

	def onUpdateStartWaveNumbers( self, event ):
		self.commit()
		undo.pushState()
		with Model.LockRace() as race:
			race.adjustAllCategoryWaveNumbers()
		wx.CallAfter( self.refresh )
		wx.CallAfter( Utils.refreshForecastHistory )

	def onAddExceptions( self, event ):
		with Model.LockRace() as race:
			if not race or not race.getAllCategories():
				return
				
		r = self.grid.GetGridCursorRow()
		if r is None or r < 0:
			Utils.MessageOK( self, _('You must select a Category first'), _('Select a Category') )
			return
		
		with Model.LockRace() as race:
			categories = race.getAllCategories()
			category = categories[r]
			
		dlg = wx.TextEntryDialog(
			self,
			u'{}: {}'.format(
				category.name,
				_('''Add Bib Exceptions (comma separated).
This will add the given list of Bibs to this category,
and remove them from other categories.'''),
			),
			_('Add Bib Exceptions')
		)
		good = (dlg.ShowModal() == wx.ID_OK)
		if good:
			response = dlg.GetValue()
		dlg.Destroy()
		if not good:
			return

		undo.pushState()
		response = re.sub( '[^0-9,]', '', response.replace(' ', ',') )
		with Model.LockRace() as race:
			for numException in response.split(','):
				race.addCategoryException( category, numException )

		self.refresh()
		
	def _setRow( self, r, active, name, catStr, startOffset = '00:00:00',
					numLaps = None,
					lappedRidersMustContinue = False,
					distance = None, distanceType = None,
					firstLapDistance = None, gender = None,
					catType = Model.Category.CatWave,
					publishFlag = True, uploadFlag = True, seriesFlag = True, ):
					
		if len(startOffset) < len('00:00:00'):
			startOffset = '00:' + startOffset
			
		GetTranslation = _
		gender = gender if gender in ['Men', 'Women'] else 'Open'
		self.grid.SetRowLabelValue( r, u'' )
		self.grid.SetCellValue( r, self.iCol['active'], u'1' if active else u'0' )
		self.grid.SetCellValue( r, self.iCol['catType'], self.CategoryTypeChoices[catType] )
		self.grid.SetCellValue( r, self.iCol['name'], name )
		self.grid.SetCellValue( r, self.iCol['gender'], GetTranslation(gender) )
		self.grid.SetCellValue( r, self.iCol['catStr'], catStr )
		self.grid.SetCellValue( r, self.iCol['startOffset'], startOffset )
		self.grid.SetCellValue( r, self.iCol['numLaps'], u'{}'.format(numLaps) if numLaps else '' )
		self.grid.SetCellValue( r, self.iCol['lappedRidersMustContinue'], u'1' if lappedRidersMustContinue else u'0' )
		self.grid.SetCellValue( r, self.iCol['rule80Time'], '' )
		self.grid.SetCellValue( r, self.iCol['suggestedLaps'], '' )
		self.grid.SetCellValue( r, self.iCol['distance'], ('%.3f' % distance) if distance else '' )
		self.grid.SetCellValue( r, self.iCol['distanceType'], self.DistanceTypeChoices[distanceType if distanceType else 0] )
		self.grid.SetCellValue( r, self.iCol['firstLapDistance'], ('%.3f' % firstLapDistance) if firstLapDistance else '' )
		self.grid.SetCellValue( r, self.iCol['publishFlag'], u'1' if publishFlag else u'0' )
		self.grid.SetCellValue( r, self.iCol['uploadFlag'], u'1' if uploadFlag else u'0' )
		self.grid.SetCellValue( r, self.iCol['seriesFlag'], u'1' if seriesFlag else u'0' )
		
		race = Model.race
		category = race.categories.get(u'{} ({})'.format(name.strip(), gender), None) if race else None
		if not category or category.catType != Model.Category.CatWave:
			return
			
		# Get the 80% time cutoff.
		if not active or not Model.race:
			return
			
		rule80Time = race.getRule80CountdownTime( category ) if race else None
		if rule80Time:
			self.grid.SetCellValue( r, self.iCol['rule80Time'], Utils.formatTime(rule80Time) )
		
		laps = race.getCategoryRaceLaps().get(category, 0) if race else None
		if laps:
			self.grid.SetCellValue( r, self.iCol['suggestedLaps'], '{}'.format(laps) )
	
	def fixRow( self, row, catType, active ):
		activeColour = wx.WHITE if active else self.inactiveColour
		colour = activeColour if catType == Model.Category.CatWave else self.ignoreColour
		for colName, fieldName in self.colNameFields:
			if not fieldName:
				continue
			col = self.iCol[fieldName]
			if col in self.dependentCols:
				self.grid.SetCellBackgroundColour( row, col, colour )
			else:
				self.grid.SetCellBackgroundColour( row, col, activeColour )
		
	def fixCells( self, event = None ):
		for row in xrange(self.grid.GetNumberRows()):
			active = self.grid.GetCellValue( row, self.iCol['active'] )[:1] in 'TtYy1'
			catType = self.CategoryTypeChoices.index(self.grid.GetCellValue(row, self.iCol['catType']) )
			self.fixRow( row, catType, active )
	
	def onActivateAll( self, event ):
		if Model.race:
			for c in Model.race.getAllCategories():
				if not c.active:
					c.active = True
					Model.race.setChanged()
		wx.CallAfter( self.refresh )
		
	def onDeactivateAll( self, event ):
		if Model.race:
			for c in Model.race.getAllCategories():
				if c.active:
					c.active = False
					Model.race.setChanged()
		wx.CallAfter( self.refresh )
	
	def doAutosize( self ):
		self.grid.AutoSizeColumns( False )
		colWidth = self.grid.GetColSize( self.iCol['catStr'] )
		maxWidth = wx.GetDisplaySize().width / 3
		if colWidth > maxWidth:
			self.grid.SetColSize( self.iCol['catStr'], maxWidth )
			self.grid.ForceRefresh()
	
	def onNewCategory( self, event ):
		self.grid.AppendRows( 1 )
		self._setRow( r=self.grid.GetNumberRows() - 1, active=True, name=u'<{}>     '.format(_('CategoryName')), catStr='100-199,504,-128' )
		self.doAutosize()
		
	def onDelCategory( self, event ):
		r = self.grid.GetGridCursorRow()
		if r is None or r < 0:
			return
		if Utils.MessageOKCancel(
					self,
					u'{} "{} ({})"?'.format(
						_('Delete Category'),
						self.grid.GetCellValue(r, 3).strip(),
						self.grid.GetCellValue(r, 4).strip(),
					),
					_('Delete Category') ):
			self.grid.DeleteRows( r, 1, True )
		
	def onUpCategory( self, event ):
		self.grid.SaveEditControlValue()
		self.grid.DisableCellEditControl()
		r = self.grid.GetGridCursorRow()
		Utils.SwapGridRows( self.grid, r, r-1 )
		if r-1 >= 0:
			self.grid.MoveCursorUp( False )
		self.grid.ClearSelection()
		self.grid.SelectRow( max(r-1, 0), True )
		
	def onDownCategory( self, event ):
		self.grid.SaveEditControlValue()
		self.grid.DisableCellEditControl()
		r = self.grid.GetGridCursorRow()
		Utils.SwapGridRows( self.grid, r, r+1 )
		if r+1 < self.grid.GetNumberRows():
			self.grid.MoveCursorDown( False )
		self.grid.ClearSelection()
		self.grid.SelectRow( min(r+1, self.grid.GetNumberRows()-1), True )
		
	def refresh( self ):
		if not self.state.changed():
			return
			
		# Fix the height of the column labels.
		dc = wx.WindowDC( self.grid )
		dc.SetFont( self.grid.GetLabelFont() )
		textHeight = dc.GetTextExtent( 'Label' )[1]
		self.colLabelHeight = textHeight * max(name.count('\n') + 1 for name in self.colnames) + textHeight // 4
		self.grid.SetColLabelSize( self.colLabelHeight )
			
		with Model.LockRace() as race:
			self.grid.ClearGrid()
			if race is None:
				return
			
			for c in xrange(self.grid.GetNumberCols()):
				if self.grid.GetColLabelValue(c).startswith(_('Distance')):
					self.grid.SetColLabelValue( c, u'{}\n({})'.format(_('Distance'), ['km', 'miles'][getattr(race, 'distanceUnit', 0)]) )
					break
			
			categories = race.getAllCategories()
			
			if self.grid.GetNumberRows() > 0:
				self.grid.DeleteRows( 0, self.grid.GetNumberRows() )
			self.grid.AppendRows( len(categories) )

			for r, cat in enumerate(categories):
				self._setRow(	r=r,
								active				= cat.active,
								name				= cat.name,
								gender				= getattr(cat, 'gender', None),
								catStr				= cat.catStr,
								catType				= cat.catType,
								startOffset			= cat.startOffset,
								numLaps				= cat.numLaps,
								lappedRidersMustContinue = getattr(cat, 'lappedRidersMustContinue', False),
								distance			= getattr(cat, 'distance', None),
								distanceType		= getattr(cat, 'distanceType', Model.Category.DistanceByLap),
								firstLapDistance	= getattr(cat, 'firstLapDistance', None),
								publishFlag			= cat.publishFlag,
								uploadFlag			= cat.uploadFlag,
								seriesFlag			= cat.seriesFlag,
							)
				
			self.doAutosize()
			self.fixCells()
			
			# Force the grid to the correct size.
			self.grid.FitInside()
			self.GetSizer().Layout()

	def commit( self ):
		undo.pushState()
		with Model.LockRace() as race:
			self.grid.SaveEditControlValue()
			self.grid.DisableCellEditControl()	# Make sure the current edit is committed.
			if race is None:
				return
			numStrTuples = []
			for r in xrange(self.grid.GetNumberRows()):
				values = { name:self.grid.GetCellValue(r, c) for name, c in self.iCol.iteritems()
																			if name not in self.computedFields }
				values['catType'] = self.CategoryTypeChoices.index(values['catType'])
				values['distanceType'] = self.DistanceTypeChoices.index(values['distanceType'])
				numStrTuples.append( values )
			race.setCategories( numStrTuples )
			race.adjustAllCategoryWaveNumbers()
		wx.CallAfter( Utils.refreshForecastHistory )
Beispiel #10
0
    def __init__(self, parent, id=wx.ID_ANY, size=wx.DefaultSize):
        super(Pulled, self).__init__(parent, id, size=size)

        self.state = RaceInputState()

        vsOverall = wx.BoxSizer(wx.VERTICAL)

        self.hbs = wx.BoxSizer(wx.HORIZONTAL)
        self.showingCategoryLabel = wx.StaticText(self,
                                                  label=u'{}:'.format(
                                                      _('Start Wave')))
        self.showingCategory = wx.StaticText(self)
        self.showingCategory.SetFont(self.showingCategory.GetFont().Bold())
        self.categoryLabel = wx.StaticText(self, label=_('Category:'))
        self.categoryChoice = wx.Choice(self)
        self.Bind(wx.EVT_CHOICE, self.doChooseCategory, self.categoryChoice)
        self.useTableToPullRidersCkBox = wx.CheckBox(
            self, label=_('Use this Table to Pull Riders'))
        self.useTableToPullRidersCkBox.SetToolTip(
            wx.ToolTip(
                _('Also requires Laps to be set in Categories screen.')))
        self.commitBtn = wx.Button(self, label=_('Commit'))
        self.commitBtn.Bind(wx.EVT_BUTTON, self.doCommit)
        self.hbs.Add(self.showingCategoryLabel,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=0)
        self.hbs.Add(self.showingCategory,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=2)
        self.hbs.Add(self.categoryLabel,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=18)
        self.hbs.Add(self.categoryChoice,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=2)
        self.hbs.Add(self.useTableToPullRidersCkBox,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=18)
        self.hbs.Add(self.commitBtn,
                     flag=wx.LEFT | wx.ALIGN_CENTRE_VERTICAL,
                     border=32)

        #---------------------------------------------------------------
        self.colNameFields = (
            (_('Laps to Go'), 'lapsToGo', 'i'),
            (u'    ' + _('Bib'), 'pulledBib', 'i'),
            (u'Name', 'pulledName', 's'),
            (u'Team', 'pulledTeam', 's'),
            (u'Component', 'pulledComponent', 's'),
            (u'Error', 'pulledError', 's'),
        )
        self.colnames = [
            colName for colName, fieldName, dataType in self.colNameFields
        ]
        self.iCol = dict((fieldName, i)
                         for i, (colName, fieldName,
                                 dataType) in enumerate(self.colNameFields)
                         if fieldName)
        self.grid = ReorderableGrid(self)
        self.grid.CreateGrid(0, len(self.colNameFields))
        GetTranslation = _
        for col, (colName, fieldName,
                  dataType) in enumerate(self.colNameFields):
            self.grid.SetColLabelValue(col, colName)
            attr = wx.grid.GridCellAttr()
            if dataType == 'i':
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_TOP)
                attr.SetEditor(wx.grid.GridCellFloatEditor(precision=0))
                attr.SetRenderer(wx.grid.GridCellFloatRenderer(precision=0))
            elif dataType == 'f':
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_TOP)
                attr.SetEditor(wx.grid.GridCellFloatEditor(precision=2))
                attr.SetRenderer(wx.grid.GridCellFloatRenderer(precision=2))
            elif dataType == 't':
                attr.SetAlignment(wx.ALIGN_RIGHT, wx.ALIGN_CENTRE)
                attr.SetEditor(TimeEditor())

            self.grid.SetColAttr(col, attr)

        self.grid.Bind(wx.grid.EVT_GRID_CELL_CHANGED, self.onCellChange)
        self.grid.AutoSizeColumns(False)
        self.grid.AutoSizeRows(False)

        #---------------------------------------------------------------

        vsOverall.Add(self.hbs, 0, flag=wx.EXPAND | wx.ALL, border=4)
        vsOverall.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=4)
        self.SetSizer(vsOverall)
Beispiel #11
0
	def __init__( self, parent, id=wx.ID_ANY, size=wx.DefaultSize ):
		super(Primes, self).__init__( parent, id, size=size )
		
		self.state = RaceInputState()
		
		vsOverall = wx.BoxSizer( wx.VERTICAL )
		
		#---------------------------------------------------------------
		self.colNameFields = (
			(_('Prime For'),			'effortType',	's'),
			(_('or Custom'),			'effortCustom',	's'),
			(_('Position'),				'position',		'i'),
			(_('Laps\nTo Go'),			'lapsToGo',		'i'),
			(_('Sponsor'),				'sponsor', 		's'),
			(_('Cash'),					'cash', 		'f'),
			(_('Merchandise'),			'merchandise', 	's'),
			(_('Points'),				'points', 		'i'),
			(_('Time\nBonus'),			'timeBonus', 	't'),
			(_('Winner\nBib'),			'winnerBib',	'i'),
			(u'',						'winnerInfo',	's'),
		)
		self.colnames = [colName for colName, fieldName, dataType in self.colNameFields]
		self.iCol = dict( (fieldName, i) for i, (colName, fieldName, dataType) in enumerate(self.colNameFields) if fieldName )
		self.grid = ReorderableGrid( self )
		self.grid.CreateGrid( 0, len(self.colNameFields) )
		GetTranslation = _
		for col, (colName, fieldName, dataType) in enumerate(self.colNameFields):
			self.grid.SetColLabelValue( col, colName )
			attr = wx.grid.GridCellAttr()
			if fieldName == 'effortType':
				attr.SetEditor( wx.grid.GridCellChoiceEditor(choices=[GetTranslation(name) for code, name in EffortChoices]) )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_TOP )
			elif fieldName == 'position':
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_TOP )
			elif fieldName == 'winnerInfo':
				attr.SetReadOnly( True )
			elif dataType == 'i':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
				attr.SetEditor( wx.grid.GridCellFloatEditor(precision=0) )
				attr.SetRenderer( wx.grid.GridCellFloatRenderer(precision=0) )
			elif dataType == 'f':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
				attr.SetEditor( wx.grid.GridCellFloatEditor(precision=2) )
				attr.SetRenderer( wx.grid.GridCellFloatRenderer(precision=2) )
			elif dataType == 't':
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				attr.SetEditor( TimeEditor() )
			
			self.grid.SetColAttr( col, attr )
			if fieldName == 'lapsToGo':
				self.lapsToGoCol = col
		
		self.grid.Bind( wx.grid.EVT_GRID_CELL_CHANGE, self.onCellChange )
		self.grid.AutoSizeColumns( False )
		self.grid.AutoSizeRows( False )

		#---------------------------------------------------------------
		self.photosButton = wx.Button( self, label=u'{}...'.format(_('Photos')) )
		self.photosButton.Bind( wx.EVT_BUTTON, self.onPhotos )
		self.finishStrip = wx.Button( self, label=u'{}...'.format(_('Finish Strip')) )
		self.finishStrip.Bind( wx.EVT_BUTTON, self.onFinishStrip )
		self.history = wx.Button( self, label=u'{}...'.format(_('Passings')) )
		self.history.Bind( wx.EVT_BUTTON, self.onHistory )
		
		self.newButton = wx.Button( self, id=wx.ID_NEW )
		self.newButton.SetToolTip( wx.ToolTip(_('Create a new Prime')) )
		self.newButton.Bind( wx.EVT_BUTTON, self.onNew )
		self.nextPositionButton = wx.Button( self, label=('Next Position') )
		self.nextPositionButton.SetToolTip( wx.ToolTip(_('Create a Prime from an Existing Prime for the Next Position')) )
		self.nextPositionButton.Bind( wx.EVT_BUTTON, self.onNextPosition )
		self.nextPrimeButton = wx.Button( self, label=('Next Prime') )
		self.nextPrimeButton.SetToolTip( wx.ToolTip(_('Create a Prime from an Existing Prime')) )
		self.nextPrimeButton.Bind( wx.EVT_BUTTON, self.onNextPrime )
		self.deleteButton = wx.Button( self, id=wx.ID_DELETE )
		self.deleteButton.SetToolTip( wx.ToolTip(_('Delete a Prime')) )
		self.deleteButton.Bind( wx.EVT_BUTTON, self.onDelete )
		hsButtons = wx.BoxSizer( wx.HORIZONTAL )
		hsButtons.Add( self.photosButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.finishStrip, flag=wx.ALL, border=4 )
		hsButtons.Add( self.history, flag=wx.ALL, border=4 )
		hsButtons.AddStretchSpacer()
		hsButtons.Add( self.newButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.nextPositionButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.nextPrimeButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.deleteButton, flag=wx.ALL, border=4 )
		
		#---------------------------------------------------------------
		
		vsOverall.Add( self.grid, 1, flag=wx.EXPAND|wx.ALL, border=4 )
		vsOverall.Add( hsButtons, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, border=4 )
		self.SetSizer( vsOverall )
Beispiel #12
0
class Primes( wx.Panel ):
	def __init__( self, parent, id=wx.ID_ANY, size=wx.DefaultSize ):
		super(Primes, self).__init__( parent, id, size=size )
		
		self.state = RaceInputState()
		
		vsOverall = wx.BoxSizer( wx.VERTICAL )
		
		#---------------------------------------------------------------
		self.colNameFields = (
			(_('Prime For'),			'effortType',	's'),
			(_('or Custom'),			'effortCustom',	's'),
			(_('Position'),				'position',		'i'),
			(_('Laps\nTo Go'),			'lapsToGo',		'i'),
			(_('Sponsor'),				'sponsor', 		's'),
			(_('Cash'),					'cash', 		'f'),
			(_('Merchandise'),			'merchandise', 	's'),
			(_('Points'),				'points', 		'i'),
			(_('Time\nBonus'),			'timeBonus', 	't'),
			(_('Winner\nBib'),			'winnerBib',	'i'),
			(u'',						'winnerInfo',	's'),
		)
		self.colnames = [colName for colName, fieldName, dataType in self.colNameFields]
		self.iCol = dict( (fieldName, i) for i, (colName, fieldName, dataType) in enumerate(self.colNameFields) if fieldName )
		self.grid = ReorderableGrid( self )
		self.grid.CreateGrid( 0, len(self.colNameFields) )
		GetTranslation = _
		for col, (colName, fieldName, dataType) in enumerate(self.colNameFields):
			self.grid.SetColLabelValue( col, colName )
			attr = wx.grid.GridCellAttr()
			if fieldName == 'effortType':
				attr.SetEditor( wx.grid.GridCellChoiceEditor(choices=[GetTranslation(name) for code, name in EffortChoices]) )
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_TOP )
			elif fieldName == 'position':
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_TOP )
			elif fieldName == 'winnerInfo':
				attr.SetReadOnly( True )
			elif dataType == 'i':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
				attr.SetEditor( wx.grid.GridCellFloatEditor(precision=0) )
				attr.SetRenderer( wx.grid.GridCellFloatRenderer(precision=0) )
			elif dataType == 'f':
				attr.SetAlignment( wx.ALIGN_RIGHT, wx.ALIGN_TOP )
				attr.SetEditor( wx.grid.GridCellFloatEditor(precision=2) )
				attr.SetRenderer( wx.grid.GridCellFloatRenderer(precision=2) )
			elif dataType == 't':
				attr.SetAlignment( wx.ALIGN_CENTRE, wx.ALIGN_CENTRE )
				attr.SetEditor( TimeEditor() )
			
			self.grid.SetColAttr( col, attr )
			if fieldName == 'lapsToGo':
				self.lapsToGoCol = col
		
		self.grid.Bind( wx.grid.EVT_GRID_CELL_CHANGE, self.onCellChange )
		self.grid.AutoSizeColumns( False )
		self.grid.AutoSizeRows( False )

		#---------------------------------------------------------------
		self.photosButton = wx.Button( self, label=u'{}...'.format(_('Photos')) )
		self.photosButton.Bind( wx.EVT_BUTTON, self.onPhotos )
		self.finishStrip = wx.Button( self, label=u'{}...'.format(_('Finish Strip')) )
		self.finishStrip.Bind( wx.EVT_BUTTON, self.onFinishStrip )
		self.history = wx.Button( self, label=u'{}...'.format(_('Passings')) )
		self.history.Bind( wx.EVT_BUTTON, self.onHistory )
		
		self.newButton = wx.Button( self, id=wx.ID_NEW )
		self.newButton.SetToolTip( wx.ToolTip(_('Create a new Prime')) )
		self.newButton.Bind( wx.EVT_BUTTON, self.onNew )
		self.nextPositionButton = wx.Button( self, label=('Next Position') )
		self.nextPositionButton.SetToolTip( wx.ToolTip(_('Create a Prime from an Existing Prime for the Next Position')) )
		self.nextPositionButton.Bind( wx.EVT_BUTTON, self.onNextPosition )
		self.nextPrimeButton = wx.Button( self, label=('Next Prime') )
		self.nextPrimeButton.SetToolTip( wx.ToolTip(_('Create a Prime from an Existing Prime')) )
		self.nextPrimeButton.Bind( wx.EVT_BUTTON, self.onNextPrime )
		self.deleteButton = wx.Button( self, id=wx.ID_DELETE )
		self.deleteButton.SetToolTip( wx.ToolTip(_('Delete a Prime')) )
		self.deleteButton.Bind( wx.EVT_BUTTON, self.onDelete )
		hsButtons = wx.BoxSizer( wx.HORIZONTAL )
		hsButtons.Add( self.photosButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.finishStrip, flag=wx.ALL, border=4 )
		hsButtons.Add( self.history, flag=wx.ALL, border=4 )
		hsButtons.AddStretchSpacer()
		hsButtons.Add( self.newButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.nextPositionButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.nextPrimeButton, flag=wx.ALL, border=4 )
		hsButtons.Add( self.deleteButton, flag=wx.ALL, border=4 )
		
		#---------------------------------------------------------------
		
		vsOverall.Add( self.grid, 1, flag=wx.EXPAND|wx.ALL, border=4 )
		vsOverall.Add( hsButtons, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM, border=4 )
		self.SetSizer( vsOverall )
	
	def onCellChange( self, event ):
		row, col = event.GetRow(), event.GetCol()
		colName = self.colNameFields[col][1]
		GetTranslation = _
		if colName == 'effortCustom':
			if self.grid.GetCellValue(row, col).strip():
				self.grid.SetCellValue( row, col-1, GetTranslation('Custom') )
		elif colName == 'effortType':
			if self.grid.GetCellValue(row, col) != 'Custom':
				self.grid.SetCellValue( row, col+1, u'' )
		elif colName == 'winnerBib':
			bib = int( u''.join(c for c in self.grid.GetCellValue(row, col) if c.isdigit()) )
			self.grid.SetCellValue( row, col+1, getWinnerInfo(bib) )
		
		wx.CallAfter( self.grid.AutoSizeColumns, False )
	
	def getT( self ):
		race = Model.race
		if not race:
			return 0
		row = self.grid.GetGridCursorRow()
		if row is None or row < 0:
			return
		lapsToGo = int( u''.join(c for c in self.grid.GetCellValue(row, self.lapsToGoCol) if c.isdigit()) )
		tMax = 0.0
		for rr in GetResults(None):
			try:
				tMax = min( tMax, rr.raceTimes[-1-lapsToGo] )
			except IndexError:
				pass
		return tMax
		
	def onPhotos( self, event ):
		mainWin = Utils.getMainWin()
		if not mainWin:
			return
		mainWin.photoDialog.SetT( self.getT() )
		mainWin.photoDialog.Show()
		
	def onFinishStrip( self, event ):
		ShowFinishStrip( self, self.getT() )
	
	def onHistory( self, event ):
		mainWin = Utils.getMainWin()
		if not mainWin:
			return
		mainWin.openMenuWindow( 'Passings' )
	
	def selectGridRow( self, row ):
		self.grid.SelectRow( row )
		self.grid.SetGridCursor( row, 0 )
		self.grid.ShowCellEditControl()
	
	def onNew( self, event ):
		race = Model.race
		if not race:
			Utils.MessageOK( self, _('You must have a Race to create a Prime.'), _('Missing Race') )
			return
		self.commit()
		race.primes = getattr(race, 'primes', [])
		rowNew = len( race.primes )
		race.primes.append( {} )
		self.updateGrid()
		self.selectGridRow( rowNew )
	
	def onNextPosition( self, event ):
		rowNext = self.grid.GetGridCursorRow()
		if rowNext is None or rowNext < 0:
			return
		self.commit()
		race = Model.race
		if not race:
			Utils.MessageOK( self, _('You must have a Race to create a next Prime.'), _('Missing Race') )
			return
		
		nextPrime = race.primes[rowNext].copy()
		
		nextPoints = {
			(1, 5):	3,
			(2, 3): 2,
			(3, 2): 1,
		}.get( (nextPrime['position'], nextPrime['points']), None )
		
		if nextPoints is not None:
			nextPrime['points'] = nextPoints
		
		try:
			nextPrime['position'] += 1
		except:
			pass
		nextPrime['winnerBib'] = None
		race.primes = race.primes[:rowNext+1] + [nextPrime] + race.primes[rowNext+1:]
		self.updateGrid()
		self.selectGridRow( rowNext + 1 )
	
	def onNextPrime( self, event ):
		rowNext = self.grid.GetGridCursorRow()
		if rowNext is None or rowNext < 0:
			return
		self.commit()
		race = Model.race
		if not race:
			Utils.MessageOK( self, _('You must have a Race to create a next Prime.'), _('Missing Race') )
			return
		nextPrime = race.primes[rowNext].copy()
		nextPrime['position'] = 1
		if nextPrime['points']:
			nextPrime['points'] = 5
		if nextPrime['lapsToGo'] > 0:
			nextPrime['lapsToGo'] -= 1
		nextPrime['winnerBib'] = None
		race.primes = race.primes[:rowNext+1] + [nextPrime] + race.primes[rowNext+1:]
		self.updateGrid()
		self.selectGridRow( rowNext + 1 )
		
	def onDelete( self, event ):
		rowDelete = self.grid.GetGridCursorRow()
		if rowDelete is None or rowDelete < 0:
			return
		self.commit()
		race = Model.race
		if race and Utils.MessageOKCancel( self, u'{}: {} ?'.format(_('Delete Prime'), rowDelete+1), _('Confirm Delete Primes') ):
			race.primes = getattr(race, 'primes', [])
			try:
				del race.primes[rowDelete]
			except Exception as e:
				return
			self.updateGrid()
			if race.primes:
				self.grid.SetGridCursor( rowDelete, 0 )
		
	def getSponsors( self ):
		race = Model.race
		if not race:
			return []
		sponsors = [prime.get('sponsor', u'') for prime in getattr(race, 'primes', [])] + [race.organizer]
		sponsors = [s for s in sponsors if s]
		return sponsors
		
	def getMerchandise( self ):
		race = Model.race
		if not race:
			return []
		merchandise = [prime.get('merchandise', u'') for prime in getattr(race, 'primes', [])]
		merchandise = [m for m in merchandise if m]
		return merchandise
	
	def setRow( self, prime, row, updateGrid=True ):
		GetTranslation = _
		
		data = []
		for col, (name, attr, dataType) in enumerate(self.colNameFields):
			if attr == 'effortType':
				effortType = prime.get('effortType', 'Pack')
				v = GetTranslation(effortType)
			elif attr == 'position':
				position = prime.get('position', 1)
				v = u'' if position == 0 else unicode(position)
			elif attr == 'points':
				points = prime.get('points', 0)
				v = u'' if points == 0 else unicode(points)
			elif attr == 'winnerBib':
				winnerBib = prime.get('winnerBib', None)
				v = u'' if not winnerBib else unicode(winnerBib)
			elif attr == 'winnerInfo':
				v = getWinnerInfo(winnerBib)
			elif dataType == 'f':
				f = prime.get(attr, 0.0)
				v = u'{:.2f}'.format(f) if f else u''
			elif dataType == 't':
				t = prime.get(attr, 0.0)
				v = Utils.formatTime(t, forceHours=True, twoDigitHours=True) if t != 0 else u''
			else:
				v = unicode(prime.get(attr, u''))
			if updateGrid:
				self.grid.SetCellValue( row, col, v )
			data.append( v )
		
		return data
	
	def getRow( self, row ):
		values = {}
		for col, (name, attr, dataType) in enumerate(self.colNameFields):
			v = self.grid.GetCellValue( row, col ).strip()
			if dataType == 'i':
				v = u''.join( c for c in v if c.isdigit() )
				v = int( v or 0 )
			elif dataType == 'f':
				v = u''.join( c for c in v if c.isdigit() or c == '.')
				v = float( v or 0.0 )
			elif dataType == 't':
				v = Utils.StrToSeconds( v )
				
			if attr == 'position' and not v:
				v = 1
			
			values[attr] = v
		
		GetTranslation = _
		for code, name in EffortChoices:
			if values['effortType'] == GetTranslation(name):
				values['effortType'] = name
				break
		
		if values['effortCustom']:
			values['effortType'] = 'Custom'
		return values
	
	def updateGrid( self ):
		race = Model.race
		if not race or not getattr(race, 'primes', None):
			self.grid.ClearGrid()
			return
		
		Utils.AdjustGridSize( self.grid, len(race.primes) )
		for row, prime in enumerate(race.primes):
			self.setRow( prime, row )
		
		self.grid.AutoSizeColumns( False )								# Resize to fit the column name.
		self.grid.AutoSizeRows( False )
	
	def refresh( self ):
		if self.state.changed():
			self.updateGrid()
		
	def commit( self ):
		self.grid.SaveEditControlValue()	# Make sure the current edit is committed.
		self.grid.DisableCellEditControl()
		race = Model.race
		if not race:
			return
		race.primes = [self.getRow(row) for row in xrange(self.grid.GetNumberRows())]