def MakeRoundButton(parent, label, bType=0): btn = RoundButton(parent, wx.ID_ANY, label, size=(RoundButtonSize, RoundButtonSize)) btn.SetBackgroundColour(wx.WHITE) btn.SetForegroundColour(buttonColors[bType]) btn.SetFontToFitLabel() return btn
def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) dataDir = Utils.getHomeDir() configFileName = os.path.join(dataDir, 'CrossMgrImpinj.cfg') self.config = wx.Config(appName="CrossMgrImpinj", vendorName="SmartCyclingSolutions", localFilename=configFileName) ID_MENU_ADVANCECONFIG = wx.NewIdRef() ID_MENU_COPYLOGS = wx.NewIdRef() ID_MENU_AUTODETECT = wx.NewIdRef() self.menuBar = wx.MenuBar(wx.MB_DOCKABLE) if 'WXMAC' in wx.Platform: self.appleMenu = self.menuBar.OSXGetAppleMenu() self.appleMenu.SetTitle("CrossMgrImpinj") self.appleMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.editMenu = wx.Menu() self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_ADVANCECONFIG, "A&dvanced Configuration")) self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_COPYLOGS, "&Copy Logs to Clipboard")) self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_AUTODETECT, "&Autodetect Reader")) self.Bind(wx.EVT_MENU, self.doAdvanced, id=ID_MENU_ADVANCECONFIG) self.Bind(wx.EVT_MENU, self.doCopyToClipboard, id=ID_MENU_COPYLOGS) self.Bind(wx.EVT_MENU, self.doAutoDetect, id=ID_MENU_AUTODETECT) self.menuBar.Append(self.editMenu, "&Edit") else: self.fileMenu = wx.Menu() self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_ADVANCECONFIG, "A&dvanced Configuration")) self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_COPYLOGS, "&Copy Logs to Clipboard")) self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_AUTODETECT, "&Autodetect Reader")) self.fileMenu.Append(wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.doAdvanced, id=ID_MENU_ADVANCECONFIG) self.Bind(wx.EVT_MENU, self.doCopyToClipboard, id=ID_MENU_COPYLOGS) self.Bind(wx.EVT_MENU, self.doAutoDetect, id=ID_MENU_AUTODETECT) self.Bind(wx.EVT_MENU, self.onCloseWindow, id=wx.ID_EXIT) self.menuBar.Append(self.fileMenu, "&File") self.helpMenu = wx.Menu() self.helpMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.menuBar.Append(self.helpMenu, "&Help") self.SetMenuBar(self.menuBar) self.SetBackgroundColour(wx.Colour(232, 232, 232)) self.LightGreen = wx.Colour(153, 255, 153) self.LightRed = wx.Colour(255, 153, 153) font = self.GetFont() bigFont = wx.Font(int(font.GetPointSize() * 1.2), font.GetFamily(), font.GetStyle(), wx.FONTWEIGHT_BOLD) titleFont = wx.Font(int(bigFont.GetPointSize() * 2.2), bigFont.GetFamily(), bigFont.GetStyle(), bigFont.GetWeight()) self.vbs = wx.BoxSizer(wx.VERTICAL) bs = wx.BoxSizer(wx.HORIZONTAL) self.reset = RoundButton(self, label='Reset', size=(80, 80)) self.reset.SetBackgroundColour(wx.WHITE) self.reset.SetForegroundColour(wx.Colour(0, 128, 128)) self.reset.SetFontToFitLabel( ) # Use the button's default font, but change the font size to fit the label. self.reset.Bind(wx.EVT_BUTTON, self.doReset) self.reset.Refresh() bs.Add(self.reset, border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(titleFont, wx.StaticText(self, label='CrossMgrImpinj')), border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.AddStretchSpacer() bitmap = wx.Bitmap(clipboard_xpm) self.copyToClipboard = wx.BitmapButton(self, bitmap=bitmap) self.copyToClipboard.SetToolTip( wx.ToolTip('Copy Configuration and Logs to Clipboard...')) self.copyToClipboard.Bind(wx.EVT_BUTTON, self.doCopyToClipboard) bs.Add(self.copyToClipboard, border=32, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.tStart = datetime.datetime.now() bs.Add(setFont( bigFont, wx.StaticText(self, label='Last Reset: %s' % self.tStart.strftime('%H:%M:%S'))), border=10, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.runningTime = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.runningTime, border=20, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(bigFont, wx.StaticText(self, label=' / ')), flag=wx.ALIGN_CENTER_VERTICAL) self.time = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.time, flag=wx.ALIGN_CENTER_VERTICAL) self.vbs.Add(bs, flag=wx.ALL | wx.EXPAND, border=4) fgs = wx.FlexGridSizer(rows=2, cols=2, vgap=4, hgap=4) fgs.AddGrowableRow(1) fgs.AddGrowableCol(0) fgs.AddGrowableCol(1) fgs.SetFlexibleDirection(wx.BOTH) self.vbs.Add(fgs, flag=wx.EXPAND, proportion=5) #------------------------------------------------------------------------------------------------ # Impinj configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) iRow = 0 hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(setFont(bigFont, wx.StaticText(self, label='Impinj Configuration:')), flag=wx.ALIGN_CENTER_VERTICAL) self.autoDetectButton = wx.Button(self, label='Auto Detect') self.autoDetectButton.Bind(wx.EVT_BUTTON, self.doAutoDetect) hb.Add(self.autoDetectButton, flag=wx.LEFT, border=6) self.advancedButton = wx.Button(self, label='Advanced...') self.advancedButton.Bind(wx.EVT_BUTTON, self.doAdvanced) hb.Add(self.advancedButton, flag=wx.LEFT, border=6) gbs.Add(hb, pos=(iRow, 0), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gs = wx.GridSizer(rows=0, cols=4, vgap=0, hgap=2) self.antennaLabels = [] self.antennas = [] for i in range(4): self.antennaLabels.append( wx.StaticText(self, label='{}'.format(i + 1), style=wx.ALIGN_CENTER)) gs.Add(self.antennaLabels[-1], flag=wx.ALIGN_CENTER | wx.EXPAND) for i in range(4): cb = wx.CheckBox(self, wx.ID_ANY, '') if i < 2: cb.SetValue(True) cb.Bind(wx.EVT_CHECKBOX, lambda x: self.getAntennaStr()) gs.Add(cb, flag=wx.ALIGN_CENTER) self.antennas.append(cb) hb = wx.BoxSizer() hb.Add(gs) self.methodName = wx.StaticText(self) self.refreshMethodName() hb.Add(self.methodName, flag=wx.ALIGN_BOTTOM | wx.LEFT, border=8) gbs.Add(wx.StaticText(self, label='ANT Ports:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) iRow += 1 self.useHostName = wx.RadioButton(self, label='Host Name:', style=wx.RB_GROUP) gbs.Add(self.useHostName, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(wx.StaticText(self, label=ImpinjHostNamePrefix), flag=wx.ALIGN_CENTER_VERTICAL) if 'WXMAC' in wx.Platform: self.impinjHostName = masked.TextCtrl( self, defaultValue='00-00-00', useFixedWidthFont=True, size=(80, -1), ) else: self.impinjHostName = masked.TextCtrl( self, mask='NN-NN-NN', defaultValue='00-00-00', useFixedWidthFont=True, size=(80, -1), ) hb.Add(self.impinjHostName) hb.Add(wx.StaticText(self, label=ImpinjHostNameSuffix), flag=wx.ALIGN_CENTER_VERTICAL) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) iRow += 1 self.useStaticAddress = wx.RadioButton(self, label='IP:') gbs.Add(self.useStaticAddress, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.impinjHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.impinjHost) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) self.useHostName.SetValue(True) self.useStaticAddress.SetValue(False) iRow += 1 self.antennaReads = AntennaReads(self) gbs.Add(self.antennaReads, pos=(iRow, 0), span=(1, 3), flag=wx.ALIGN_LEFT | wx.EXPAND) iRow += 1 self.antennaReadCount = wx.StaticText( self, label='ANT Reads: 1:0 0% | 2:0 0% | 3:0 0% | 4:0 0% ' ) gbs.Add(self.antennaReadCount, pos=(iRow, 0), span=(1, 3), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, label='Backup File:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.backupFile = wx.StaticText( self, label=' ') gbs.Add(self.backupFile, pos=(iRow, 1), span=(1, 2), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # CrossMgr configuration. # cmcs = wx.BoxSizer(wx.VERTICAL) fgs.Add(cmcs, flag=wx.EXPAND | wx.ALL, border=4) cmcs.Add( setFont(bigFont, wx.StaticText(self, label='CrossMgr Configuration:'))) hh = wx.BoxSizer(wx.HORIZONTAL) hh.Add(wx.StaticText(self, label='CrossMgr Address:'), flag=wx.ALIGN_CENTER_VERTICAL) self.crossMgrHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hh.Add(self.crossMgrHost, flag=wx.ALIGN_LEFT) hh.Add(wx.StaticText(self, label=' : 53135'), flag=wx.ALIGN_CENTER_VERTICAL) cmcs.Add(hh, flag=wx.ALL, border=4) #------------------------------------------------------------------------------------------------ # Add strays # cmcs.Add(wx.StaticLine(self, style=wx.LI_HORIZONTAL), flag=wx.EXPAND | wx.TOP | wx.BOTTOM, border=2) self.strayTagsLabel = wx.StaticText(self, label='Stray Tags: ') cmcs.Add(self.strayTagsLabel, flag=wx.LEFT | wx.RIGHT, border=4) self.strays = wx.ListCtrl(self, style=wx.LC_REPORT | wx.BORDER_SUNKEN, size=(-1, 50)) self.strays.InsertColumn(0, 'Tag', wx.LIST_AUTOSIZE_USEHEADER) self.strays.InsertColumn(1, 'Time', wx.LIST_AUTOSIZE_USEHEADER) cmcs.Add(self.strays, 1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border=4) #------------------------------------------------------------------------------------------------ # Add messages # self.impinjMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.impinjMessagesText, flag=wx.EXPAND, proportion=2) self.impinjMessages = MessageManager(self.impinjMessagesText) self.crossMgrMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.crossMgrMessagesText, flag=wx.EXPAND, proportion=2) self.crossMgrMessages = MessageManager(self.crossMgrMessagesText) self.fgs = fgs #------------------------------------------------------------------------------------------------ # Create a timer to update the messages. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.updateMessages) self.timer.Start(1000, False) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizer(self.vbs) self.start()
class MainWin(wx.Frame): def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) dataDir = Utils.getHomeDir() configFileName = os.path.join(dataDir, 'CrossMgrImpinj.cfg') self.config = wx.Config(appName="CrossMgrImpinj", vendorName="SmartCyclingSolutions", localFilename=configFileName) ID_MENU_ADVANCECONFIG = wx.NewIdRef() ID_MENU_COPYLOGS = wx.NewIdRef() ID_MENU_AUTODETECT = wx.NewIdRef() self.menuBar = wx.MenuBar(wx.MB_DOCKABLE) if 'WXMAC' in wx.Platform: self.appleMenu = self.menuBar.OSXGetAppleMenu() self.appleMenu.SetTitle("CrossMgrImpinj") self.appleMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.editMenu = wx.Menu() self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_ADVANCECONFIG, "A&dvanced Configuration")) self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_COPYLOGS, "&Copy Logs to Clipboard")) self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_AUTODETECT, "&Autodetect Reader")) self.Bind(wx.EVT_MENU, self.doAdvanced, id=ID_MENU_ADVANCECONFIG) self.Bind(wx.EVT_MENU, self.doCopyToClipboard, id=ID_MENU_COPYLOGS) self.Bind(wx.EVT_MENU, self.doAutoDetect, id=ID_MENU_AUTODETECT) self.menuBar.Append(self.editMenu, "&Edit") else: self.fileMenu = wx.Menu() self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_ADVANCECONFIG, "A&dvanced Configuration")) self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_COPYLOGS, "&Copy Logs to Clipboard")) self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_AUTODETECT, "&Autodetect Reader")) self.fileMenu.Append(wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.doAdvanced, id=ID_MENU_ADVANCECONFIG) self.Bind(wx.EVT_MENU, self.doCopyToClipboard, id=ID_MENU_COPYLOGS) self.Bind(wx.EVT_MENU, self.doAutoDetect, id=ID_MENU_AUTODETECT) self.Bind(wx.EVT_MENU, self.onCloseWindow, id=wx.ID_EXIT) self.menuBar.Append(self.fileMenu, "&File") self.helpMenu = wx.Menu() self.helpMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.menuBar.Append(self.helpMenu, "&Help") self.SetMenuBar(self.menuBar) self.SetBackgroundColour(wx.Colour(232, 232, 232)) self.LightGreen = wx.Colour(153, 255, 153) self.LightRed = wx.Colour(255, 153, 153) font = self.GetFont() bigFont = wx.Font(int(font.GetPointSize() * 1.2), font.GetFamily(), font.GetStyle(), wx.FONTWEIGHT_BOLD) titleFont = wx.Font(int(bigFont.GetPointSize() * 2.2), bigFont.GetFamily(), bigFont.GetStyle(), bigFont.GetWeight()) self.vbs = wx.BoxSizer(wx.VERTICAL) bs = wx.BoxSizer(wx.HORIZONTAL) self.reset = RoundButton(self, label='Reset', size=(80, 80)) self.reset.SetBackgroundColour(wx.WHITE) self.reset.SetForegroundColour(wx.Colour(0, 128, 128)) self.reset.SetFontToFitLabel( ) # Use the button's default font, but change the font size to fit the label. self.reset.Bind(wx.EVT_BUTTON, self.doReset) self.reset.Refresh() bs.Add(self.reset, border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(titleFont, wx.StaticText(self, label='CrossMgrImpinj')), border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.AddStretchSpacer() bitmap = wx.Bitmap(clipboard_xpm) self.copyToClipboard = wx.BitmapButton(self, bitmap=bitmap) self.copyToClipboard.SetToolTip( wx.ToolTip('Copy Configuration and Logs to Clipboard...')) self.copyToClipboard.Bind(wx.EVT_BUTTON, self.doCopyToClipboard) bs.Add(self.copyToClipboard, border=32, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.tStart = datetime.datetime.now() bs.Add(setFont( bigFont, wx.StaticText(self, label='Last Reset: %s' % self.tStart.strftime('%H:%M:%S'))), border=10, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.runningTime = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.runningTime, border=20, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(bigFont, wx.StaticText(self, label=' / ')), flag=wx.ALIGN_CENTER_VERTICAL) self.time = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.time, flag=wx.ALIGN_CENTER_VERTICAL) self.vbs.Add(bs, flag=wx.ALL | wx.EXPAND, border=4) fgs = wx.FlexGridSizer(rows=2, cols=2, vgap=4, hgap=4) fgs.AddGrowableRow(1) fgs.AddGrowableCol(0) fgs.AddGrowableCol(1) fgs.SetFlexibleDirection(wx.BOTH) self.vbs.Add(fgs, flag=wx.EXPAND, proportion=5) #------------------------------------------------------------------------------------------------ # Impinj configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) iRow = 0 hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(setFont(bigFont, wx.StaticText(self, label='Impinj Configuration:')), flag=wx.ALIGN_CENTER_VERTICAL) self.autoDetectButton = wx.Button(self, label='Auto Detect') self.autoDetectButton.Bind(wx.EVT_BUTTON, self.doAutoDetect) hb.Add(self.autoDetectButton, flag=wx.LEFT, border=6) self.advancedButton = wx.Button(self, label='Advanced...') self.advancedButton.Bind(wx.EVT_BUTTON, self.doAdvanced) hb.Add(self.advancedButton, flag=wx.LEFT, border=6) gbs.Add(hb, pos=(iRow, 0), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gs = wx.GridSizer(rows=0, cols=4, vgap=0, hgap=2) self.antennaLabels = [] self.antennas = [] for i in range(4): self.antennaLabels.append( wx.StaticText(self, label='{}'.format(i + 1), style=wx.ALIGN_CENTER)) gs.Add(self.antennaLabels[-1], flag=wx.ALIGN_CENTER | wx.EXPAND) for i in range(4): cb = wx.CheckBox(self, wx.ID_ANY, '') if i < 2: cb.SetValue(True) cb.Bind(wx.EVT_CHECKBOX, lambda x: self.getAntennaStr()) gs.Add(cb, flag=wx.ALIGN_CENTER) self.antennas.append(cb) hb = wx.BoxSizer() hb.Add(gs) self.methodName = wx.StaticText(self) self.refreshMethodName() hb.Add(self.methodName, flag=wx.ALIGN_BOTTOM | wx.LEFT, border=8) gbs.Add(wx.StaticText(self, label='ANT Ports:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) iRow += 1 self.useHostName = wx.RadioButton(self, label='Host Name:', style=wx.RB_GROUP) gbs.Add(self.useHostName, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(wx.StaticText(self, label=ImpinjHostNamePrefix), flag=wx.ALIGN_CENTER_VERTICAL) if 'WXMAC' in wx.Platform: self.impinjHostName = masked.TextCtrl( self, defaultValue='00-00-00', useFixedWidthFont=True, size=(80, -1), ) else: self.impinjHostName = masked.TextCtrl( self, mask='NN-NN-NN', defaultValue='00-00-00', useFixedWidthFont=True, size=(80, -1), ) hb.Add(self.impinjHostName) hb.Add(wx.StaticText(self, label=ImpinjHostNameSuffix), flag=wx.ALIGN_CENTER_VERTICAL) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) iRow += 1 self.useStaticAddress = wx.RadioButton(self, label='IP:') gbs.Add(self.useStaticAddress, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.impinjHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.impinjHost) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) self.useHostName.SetValue(True) self.useStaticAddress.SetValue(False) iRow += 1 self.antennaReads = AntennaReads(self) gbs.Add(self.antennaReads, pos=(iRow, 0), span=(1, 3), flag=wx.ALIGN_LEFT | wx.EXPAND) iRow += 1 self.antennaReadCount = wx.StaticText( self, label='ANT Reads: 1:0 0% | 2:0 0% | 3:0 0% | 4:0 0% ' ) gbs.Add(self.antennaReadCount, pos=(iRow, 0), span=(1, 3), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, label='Backup File:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.backupFile = wx.StaticText( self, label=' ') gbs.Add(self.backupFile, pos=(iRow, 1), span=(1, 2), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # CrossMgr configuration. # cmcs = wx.BoxSizer(wx.VERTICAL) fgs.Add(cmcs, flag=wx.EXPAND | wx.ALL, border=4) cmcs.Add( setFont(bigFont, wx.StaticText(self, label='CrossMgr Configuration:'))) hh = wx.BoxSizer(wx.HORIZONTAL) hh.Add(wx.StaticText(self, label='CrossMgr Address:'), flag=wx.ALIGN_CENTER_VERTICAL) self.crossMgrHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hh.Add(self.crossMgrHost, flag=wx.ALIGN_LEFT) hh.Add(wx.StaticText(self, label=' : 53135'), flag=wx.ALIGN_CENTER_VERTICAL) cmcs.Add(hh, flag=wx.ALL, border=4) #------------------------------------------------------------------------------------------------ # Add strays # cmcs.Add(wx.StaticLine(self, style=wx.LI_HORIZONTAL), flag=wx.EXPAND | wx.TOP | wx.BOTTOM, border=2) self.strayTagsLabel = wx.StaticText(self, label='Stray Tags: ') cmcs.Add(self.strayTagsLabel, flag=wx.LEFT | wx.RIGHT, border=4) self.strays = wx.ListCtrl(self, style=wx.LC_REPORT | wx.BORDER_SUNKEN, size=(-1, 50)) self.strays.InsertColumn(0, 'Tag', wx.LIST_AUTOSIZE_USEHEADER) self.strays.InsertColumn(1, 'Time', wx.LIST_AUTOSIZE_USEHEADER) cmcs.Add(self.strays, 1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border=4) #------------------------------------------------------------------------------------------------ # Add messages # self.impinjMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.impinjMessagesText, flag=wx.EXPAND, proportion=2) self.impinjMessages = MessageManager(self.impinjMessagesText) self.crossMgrMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.crossMgrMessagesText, flag=wx.EXPAND, proportion=2) self.crossMgrMessages = MessageManager(self.crossMgrMessagesText) self.fgs = fgs #------------------------------------------------------------------------------------------------ # Create a timer to update the messages. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.updateMessages) self.timer.Start(1000, False) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizer(self.vbs) self.start() def OnAboutBox(self, e): description = """CrossMgrImpinj is an Impinj interface to CrossMgr """ licence = """CrossMgrImpinjis free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. CrossMgrImpinj is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with File Hunter; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA""" info = wx.adv.AboutDialogInfo() crossMgrPng = Utils.getImageFolder() + '/CrossMgrImpinj.png' info.SetIcon(wx.Icon(crossMgrPng, wx.BITMAP_TYPE_PNG)) info.SetName('CrossMgrImpinj') info.SetVersion(AppVerName.split(' ')[1]) info.SetDescription(description) info.SetCopyright('(C) 2020 Edward Sitarski') info.SetWebSite('http://www.sites.google.com/site/crossmgrsoftware/') info.SetLicence(licence) wx.adv.AboutBox(info, self) def readerStatusCB(self, **kwargs): # As this is called from another thread, make sure all UI updates are done from CallAfter. connectedAntennas = set(kwargs.get('connectedAntennas', [])) for i in range(4): wx.CallAfter( self.antennaLabels[i].SetBackgroundColour, self.LightGreen if (i + 1) in connectedAntennas else wx.NullColour) def refreshMethodName(self): if Impinj.ProcessingMethod == 0 and QuadReg.samplesTotal: s = u'{}: Inliers: {:.1%}'.format( MethodNames[Impinj.ProcessingMethod], float(QuadReg.inliersTotal) / float(QuadReg.samplesTotal)) else: s = MethodNames[Impinj.ProcessingMethod] self.methodName.SetLabel(s) def refreshStrays(self, strays): if self.strays.GetItemCount() != len(strays): self.strayTagsLabel.SetLabel('Stray Tags: {}'.format(len(strays))) if not strays: if self.strays.GetItemCount(): self.strays.DeleteAllItems() return if (self.strays.GetItemCount() == len(strays) and strays[0][0] == self.strays.GetItemText(0) and strays[-1][0] == self.strays.GetItemText(self.strays.GetItemCount() - 1)): return self.strays.DeleteAllItems() for tag, discovered in strays: i = self.strays.InsertItem(1000000, tag) self.strays.SetItem(i, 1, discovered.strftime('%H:%M:%S')) for c in range(self.strays.GetColumnCount()): self.strays.SetColumnWidth(c, wx.LIST_AUTOSIZE_USEHEADER) def strayHandler(self, strayQ): while 1: msg = strayQ.get() if msg[0] == 'strays': wx.CallAfter(self.refreshStrays, msg[1]) elif msg[0] == 'shutdown': break def start(self): self.dataQ = Queue() self.strayQ = Queue() self.messageQ = Queue() self.shutdownQ = Queue( ) # Queue to tell the Impinj monitor to shut down. self.readerStatusCB() if self.useHostName.GetValue(): self.impinjProcess = Process( name='ImpinjProcess', target=ImpinjServer, args=( self.dataQ, self.strayQ, self.messageQ, self.shutdownQ, ImpinjHostNamePrefix + self.impinjHostName.GetValue() + ImpinjHostNameSuffix, ImpinjInboundPort, self.getAntennaStr(), self.readerStatusCB, )) else: self.impinjProcess = Process(name='ImpinjProcess', target=ImpinjServer, args=( self.dataQ, self.strayQ, self.messageQ, self.shutdownQ, self.impinjHost.GetAddress(), ImpinjInboundPort, self.getAntennaStr(), self.readerStatusCB, )) self.impinjProcess.daemon = True self.strayProcess = Process(name='StrayProcess', target=self.strayHandler, args=(self.strayQ, )) self.strayProcess.daemon = True self.crossMgrProcess = Process(name='CrossMgrProcess', target=CrossMgrServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getCrossMgrHost(), CrossMgrPort)) self.crossMgrProcess.daemon = True self.impinjProcess.start() self.strayProcess.start() self.crossMgrProcess.start() def shutdown(self): self.impinjProcess = None self.crossMgrProcess = None self.strayProcess = None self.messageQ = None self.dataQ = None self.strayQ = None self.shutdownQ = None def doReset(self, event, confirm=True): if confirm: dlg = wx.MessageDialog(self, 'Reset CrossMgrImpinj Adapter?', 'Confirm Reset', wx.OK | wx.CANCEL | wx.ICON_WARNING) ret = dlg.ShowModal() dlg.Destroy() if ret != wx.ID_OK: return self.reset.Enable( False) # Prevent multiple clicks while shutting down. self.writeOptions() self.refreshMethodName() self.gracefulShutdown() self.impinjMessages.clear() self.crossMgrMessages.clear() self.shutdown() self.reset.Enable(True) QuadReg.ResetStats() wx.CallAfter(self.start) def doAutoDetect(self, event): wx.BeginBusyCursor() self.gracefulShutdown() self.shutdown() impinjHost, crossMgrHost = AutoDetect( ImpinjInboundPort)[0], '127.0.0.1' wx.EndBusyCursor() if impinjHost and crossMgrHost: self.useStaticAddress.SetValue(True) self.useHostName.SetValue(False) self.impinjHost.SetValue(impinjHost) self.crossMgrHost.SetValue(crossMgrHost) else: dlg = wx.MessageDialog( self, 'Auto Detect Failed.\nCheck that reader has power and is connected to the router.', 'Auto Detect Failed', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() self.doReset(event, False) def doAdvanced(self, event): dlg = AdvancedSetup(self) if dlg.ShowModal() == wx.ID_OK: if Utils.MessageOKCancel(self, 'Reset Reader Now?', 'Reset Reader'): self.doReset(event, confirm=False) dlg.Destroy() def gracefulShutdown(self): # Shutdown the CrossMgr process by sending it a shutdown command. if self.shutdownQ: self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') if self.dataQ: self.dataQ.put('shutdown') self.dataQ.put('shutdown') if self.strayQ: self.strayQ.put(('shutdown', )) if self.crossMgrProcess: self.crossMgrProcess.join() if self.impinjProcess: self.impinjProcess.join() if self.strayProcess: self.strayProcess.join() self.crossMgrProcess = None self.impinjProcess = None self.strayProcess = None def onCloseWindow(self, event): self.gracefulShutdown() wx.Exit() def doCopyToClipboard(self, event): cc = [ AppVerName, 'Configuration: CrossMgrImpinj', ' RunningTime: {}'.format(self.runningTime.GetLabel()), ' Time: {}'.format(self.time.GetLabel()), ' BackupFile: {}'.format(self.backupFile.GetLabel()), '', 'Configuration: Impinj:', ' Use Host Name: {}'.format( 'True' if self.useHostName.GetValue() else 'False'), ' HostName: {}'.format((ImpinjHostNamePrefix + self.impinjHostName.GetValue()) + ImpinjHostNameSuffix), ' ImpinjHost: {}'.format(self.impinjHost.GetAddress()), ' ImpinjPort: {}'.format(ImpinjInboundPort), ' ReportMethod: {}'.format( MethodNames[Impinj.ProcessingMethod]), ' AntennaChoice: {}'.format( AntennaChoiceNames[Impinj.AntennaChoice]), ' RemoveOutliers:{}'.format(Impinj.RepeatSeconds), '', ' ConnectionTimeoutSeconds: {}'.format( Impinj.ConnectionTimeoutSeconds), ' KeepaliveSeconds: {}'.format(Impinj.KeepaliveSeconds), ' RepeatSeconds: {}'.format(Impinj.RepeatSeconds), '', 'Configuration: CrossMgr', ' CrossMgrHost: {}'.format(self.getCrossMgrHost()), ' CrossMgrPort: {}'.format(CrossMgrPort), ] cc.append('\nLog: Impinj') log = self.impinjMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: CrossMgr') log = self.crossMgrMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: Application\n') try: with open(redirectFileName, 'r') as fp: for line in fp: cc.append(line) except: pass if wx.TheClipboard.Open(): do = wx.TextDataObject() do.SetText('\n'.join(cc)) wx.TheClipboard.SetData(do) wx.TheClipboard.Close() dlg = wx.MessageDialog( self, 'Configuration and Logs copied to the Clipboard.', 'Copy to Clipboard Succeeded', wx.OK | wx.ICON_INFORMATION) ret = dlg.ShowModal() dlg.Destroy() else: # oops... something went wrong! wx.MessageBox("Unable to open the clipboard", "Error") def getCrossMgrHost(self): return self.crossMgrHost.GetAddress() def getAntennaStr(self): selectedAntennas = [i for i in range(4) if self.antennas[i].GetValue()] # Ensure at least one antenna is selected. if not selectedAntennas: self.antennas[0].SetValue(True) selectedAntennas = [0] return ' '.join('{}'.format(a + 1) for a in selectedAntennas) def setAntennaStr(self, s): antennas = set(int(a) for a in s.split()) for i in range(4): self.antennas[i].SetValue((i + 1) in antennas) def writeOptions(self): self.config.Write('CrossMgrHost', self.getCrossMgrHost()) self.config.Write('UseHostName', 'True' if self.useHostName.GetValue() else 'False') self.config.Write( 'ImpinjHostName', ImpinjHostNamePrefix + self.impinjHostName.GetValue() + ImpinjHostNameSuffix) self.config.Write('ImpinjAddr', self.impinjHost.GetAddress()) self.config.Write('ImpinjPort', '{}'.format(ImpinjInboundPort)) self.config.Write('Antennas', self.getAntennaStr()) self.config.Write('ConnectionTimeoutSeconds', '{}'.format(Impinj.ConnectionTimeoutSeconds)) self.config.Write('KeepaliveSeconds', '{}'.format(Impinj.KeepaliveSeconds)) self.config.Write('RepeatSeconds', '{}'.format(Impinj.RepeatSeconds)) self.config.Write('PlaySounds', '{}'.format(Utils.playBell)) self.config.Write('ReceiverSensitivity', '{}'.format(Impinj.ReceiverSensitivity or 0)) self.config.Write('TransmitPower', '{}'.format(Impinj.TransmitPower or 0)) self.config.Write('TagPopulation', '{}'.format(Impinj.TagPopulation or 0)) self.config.Write('TagTransitTime', '{}'.format(Impinj.TagTransitTime or 0)) self.config.Write('ProcessingMethod', '{}'.format(Impinj.ProcessingMethod)) self.config.Write('AntennaChoice', '{}'.format(Impinj.AntennaChoice)) self.config.Write( 'RemoveOutliers', '{}'.format('True' if Impinj.RemoveOutliers else 'False')) self.config.Flush() def readOptions(self): self.crossMgrHost.SetValue( self.config.Read('CrossMgrHost', Utils.DEFAULT_HOST)) useHostName = (self.config.Read('UseHostName', 'True').upper()[:1] == 'T') self.useHostName.SetValue(useHostName) self.useStaticAddress.SetValue(not useHostName) self.impinjHostName.SetValue( self.config.Read( 'ImpinjHostName', ImpinjHostNamePrefix + '00-00-00' + ImpinjHostNameSuffix) [len(ImpinjHostNamePrefix):-len(ImpinjHostNameSuffix)]) self.impinjHost.SetValue(self.config.Read('ImpinjAddr', '0.0.0.0')) self.setAntennaStr(self.config.Read('Antennas', '1 2 3 4')) Utils.playBell = (self.config.Read('PlaySounds', 'True').upper()[:1] == 'T') Impinj.ConnectionTimeoutSeconds = int( self.config.Read('ConnectionTimeoutSeconds', '{}'.format(Impinj.ConnectionTimeoutSeconds))) Impinj.KeepaliveSeconds = int( self.config.Read('KeepaliveSeconds', '{}'.format(Impinj.KeepaliveSeconds))) Impinj.RepeatSeconds = int( self.config.Read('RepeatSeconds', '{}'.format(Impinj.RepeatSeconds))) Impinj.ReceiverSensitivity = int( self.config.Read('ReceiverSensitivity', '0')) or None Impinj.TransmitPower = int(self.config.Read('TransmitPower', '0')) or None Impinj.TagPopulation = int(self.config.Read('TagPopulation', '0')) or None Impinj.TagTransitTime = int(self.config.Read('TagTransitTime', '0')) or None Impinj.ProcessingMethod = int(self.config.Read( 'ProcessingMethod', '0')) # Default to QuadraticRegression. Impinj.AntennaChoice = int(self.config.Read( 'AntennaChoice', '1')) # Default to MaxDB antenna. Impinj.RemoveOutliers = (self.config.Read('RemoveOutliers', 'True').upper()[:1] == 'T') def updateMessages(self, event): tNow = datetime.datetime.now() running = int((tNow - self.tStart).total_seconds()) self.runningTime.SetLabel('{:02d}:{:02d}:{:02d}'.format( running // (60 * 60), (running // 60) % 60, running % 60)) self.time.SetLabel(tNow.strftime('%H:%M:%S')) def formatAntennaReadCount(arc): if arc < 1000: return arc if arc < 1000000: return '{:.1f}k'.format(arc / 1000.0) return '{:.1f}m'.format(arc / 1000000.0) if not self.messageQ: return while 1: try: d = self.messageQ.get(False) except Empty: break if isinstance(d[-1], dict): antennaReadCount = d[-1] d = d[:-1] else: antennaReadCount = None message = ' '.join(six.text_type(x) for x in d[1:]) if d[0] == 'Impinj': if 'state' in d: self.impinjMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.impinjMessages.write(message) if antennaReadCount is not None: total = max( 1, sum(antennaReadCount[i] for i in range(1, 4 + 1))) label = '{}: {} ({})'.format( 'ANT Used' if Impinj.ProcessingMethod != FirstReadMethod else 'ANT Reads', ' | '.join('{}:{} {:.1f}%'.format( i, formatAntennaReadCount(antennaReadCount[i]), antennaReadCount[i] * 100.0 / total) for i in range(1, 4 + 1)), 'Peak RSSI' if Impinj.ProcessingMethod != FirstReadMethod else 'First Read', ) self.antennaReadCount.SetLabel(label) self.antennaReads.Set( [antennaReadCount[i] for i in range(1, 4 + 1)]) self.refreshMethodName() elif d[0] == 'Impinj2JChip': if 'state' in d: self.crossMgrMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.crossMgrMessages.write(message) elif d[0] == 'BackupFile': self.backupFile.SetLabel(d[1])
def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) self.config = wx.Config( appName="CrossMgrImpinj", vendorName="SmartCyclingSolutions", #style=wx.Config.CONFIG_USE_LOCAL_FILE ) self.SetBackgroundColour(wx.Colour(232, 232, 232)) self.LightGreen = wx.Colour(153, 255, 153) self.LightRed = wx.Colour(255, 153, 153) font = self.GetFont() bigFont = wx.Font(int(font.GetPointSize() * 1.5), font.GetFamily(), font.GetStyle(), wx.FONTWEIGHT_BOLD) titleFont = wx.Font(int(bigFont.GetPointSize() * 2.2), bigFont.GetFamily(), bigFont.GetStyle(), bigFont.GetWeight()) self.vbs = wx.BoxSizer(wx.VERTICAL) bs = wx.BoxSizer(wx.HORIZONTAL) self.reset = RoundButton(self, label='Reset', size=(80, 80)) self.reset.SetBackgroundColour(wx.WHITE) self.reset.SetForegroundColour(wx.Colour(0, 128, 128)) self.reset.SetFontToFitLabel( ) # Use the button's default font, but change the font size to fit the label. self.reset.Bind(wx.EVT_BUTTON, self.doReset) self.reset.Refresh() bs.Add(self.reset, border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(titleFont, wx.StaticText(self, label='CrossMgrImpinj')), border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.AddStretchSpacer() bitmap = wx.Bitmap(clipboard_xpm) self.copyToClipboard = wx.BitmapButton(self, bitmap=bitmap) self.copyToClipboard.SetToolTip( wx.ToolTip('Copy Configuration and Logs to Clipboard...')) self.copyToClipboard.Bind(wx.EVT_BUTTON, self.doCopyToClipboard) bs.Add(self.copyToClipboard, border=32, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.tStart = datetime.datetime.now() bs.Add(setFont( bigFont, wx.StaticText(self, label='Last Reset: %s' % self.tStart.strftime('%H:%M:%S'))), border=10, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.runningTime = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.runningTime, border=20, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(bigFont, wx.StaticText(self, label=' / ')), flag=wx.ALIGN_CENTER_VERTICAL) self.time = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.time, flag=wx.ALIGN_CENTER_VERTICAL) self.vbs.Add(bs, flag=wx.ALL | wx.EXPAND, border=4) fgs = wx.FlexGridSizer(rows=2, cols=2, vgap=4, hgap=4) fgs.AddGrowableRow(1) fgs.AddGrowableCol(0) fgs.AddGrowableCol(1) fgs.SetFlexibleDirection(wx.BOTH) self.vbs.Add(fgs, flag=wx.EXPAND, proportion=5) #------------------------------------------------------------------------------------------------ # Impinj configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) iRow = 0 hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(setFont(bigFont, wx.StaticText(self, label='Impinj Configuration:')), flag=wx.ALIGN_CENTER_VERTICAL) self.autoDetectButton = wx.Button(self, label='Auto Detect') self.autoDetectButton.Bind(wx.EVT_BUTTON, self.doAutoDetect) hb.Add(self.autoDetectButton, flag=wx.LEFT, border=6) self.advancedButton = wx.Button(self, label='Advanced...') self.advancedButton.Bind(wx.EVT_BUTTON, self.doAdvanced) hb.Add(self.advancedButton, flag=wx.LEFT, border=6) gbs.Add(hb, pos=(iRow, 0), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gs = wx.GridSizer(rows=0, cols=4, vgap=0, hgap=2) self.antennaLabels = [] self.antennas = [] for i in xrange(4): self.antennaLabels.append( wx.StaticText(self, label='{}'.format(i + 1), style=wx.ALIGN_CENTER)) gs.Add(self.antennaLabels[-1], flag=wx.ALIGN_CENTER | wx.EXPAND) for i in xrange(4): cb = wx.CheckBox(self, wx.ID_ANY, '') if i < 2: cb.SetValue(True) cb.Bind(wx.EVT_CHECKBOX, lambda x: self.getAntennaStr()) gs.Add(cb, flag=wx.ALIGN_CENTER) self.antennas.append(cb) gbs.Add(wx.StaticText(self, label='ANT Ports:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) gbs.Add(gs, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) iRow += 1 self.useHostName = wx.RadioButton(self, label='Host Name:', style=wx.RB_GROUP) gbs.Add(self.useHostName, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(wx.StaticText(self, label=ImpinjHostNamePrefix), flag=wx.ALIGN_CENTER_VERTICAL) self.impinjHostName = masked.TextCtrl( self, mask='NN-NN-NN', defaultValue='00-00-00', useFixedWidthFont=True, size=(80, -1), ) hb.Add(self.impinjHostName) hb.Add(wx.StaticText(self, label=ImpinjHostNameSuffix), flag=wx.ALIGN_CENTER_VERTICAL) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) iRow += 1 self.useStaticAddress = wx.RadioButton(self, label='IP:') gbs.Add(self.useStaticAddress, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.impinjHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.impinjHost) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) self.useHostName.SetValue(True) self.useStaticAddress.SetValue(False) iRow += 1 gbs.Add(wx.StaticText(self, label='ANT Reads:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.antennaReadCount = wx.StaticText( self, label='1:0 0% | 2:0 0% | 3:0 0% | 4:0 0% ') gbs.Add(self.antennaReadCount, pos=(iRow, 1), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, label='Backup File:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.backupFile = wx.StaticText( self, label=' ') gbs.Add(self.backupFile, pos=(iRow, 1), span=(1, 2), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # CrossMgr configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) gbs.Add(setFont(bigFont, wx.StaticText(self, label='CrossMgr Configuration:')), pos=(0, 0), span=(1, 2), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL) gbs.Add(wx.StaticText(self, label='CrossMgr Address:'), pos=(1, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.crossMgrHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.crossMgrHost, flag=wx.ALIGN_LEFT) hb.Add(wx.StaticText(self, label=' : 53135'), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(1, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # Add messages # self.impinjMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.impinjMessagesText, flag=wx.EXPAND, proportion=2) self.impinjMessages = MessageManager(self.impinjMessagesText) self.crossMgrMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.crossMgrMessagesText, flag=wx.EXPAND, proportion=2) self.crossMgrMessages = MessageManager(self.crossMgrMessagesText) self.fgs = fgs #------------------------------------------------------------------------------------------------ # Create a timer to update the messages. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.updateMessages) self.timer.Start(1000, False) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizer(self.vbs) self.start()
def __init__(self, parent, id=wx.ID_ANY): wx.Panel.__init__(self, parent, id) self.SetBackgroundColour(wx.Colour(255, 255, 255)) ps = wx.BoxSizer(wx.VERTICAL) self.splitter = wx.SplitterWindow(self, wx.VERTICAL) ps.Add(self.splitter, 1, flag=wx.EXPAND) self.SetSizer(ps) #--------------------------------------------------------------------------------------------- self.leftPanel = wx.Panel(self.splitter) bs = wx.BoxSizer(wx.VERTICAL) self.leftPanel.SetSizer(bs) self.leftPanel.SetBackgroundColour(wx.Colour(255, 255, 255)) self.leftPanel.Bind(wx.EVT_SIZE, self.setWrappedRaceInfo) buttonSize = 220 self.button = RoundButton(self.leftPanel, size=(buttonSize, buttonSize)) self.button.SetLabel(FinishText) self.button.SetFontToFitLabel() self.button.SetForegroundColour(wx.Colour(128, 128, 128)) self.Bind(wx.EVT_BUTTON, self.onPress, self.button) self.clock = Clock(self, size=(190, 190), checkFunc=self.updateClock) self.clock.SetBackgroundColour(wx.WHITE) self.raceIntro = wx.StaticText(self.leftPanel, label=u'') self.raceIntro.SetFont(wx.Font(20, wx.DEFAULT, wx.NORMAL, wx.NORMAL)) self.chipTimingOptions = wx.RadioBox( self.leftPanel, label=_("Chip Timing Options"), majorDimension=1, choices=Properties.RfidProperties.choices, style=wx.RA_SPECIFY_COLS) self.Bind(wx.EVT_RADIOBOX, self.onChipTimingOptions, self.chipTimingOptions) self.settingsButton = wx.BitmapButton( self.leftPanel, bitmap=Utils.GetPngBitmap('settings-icon.png')) self.settingsButton.SetToolTip(wx.ToolTip(_('Properties Shortcut'))) self.settingsButton.Bind(wx.EVT_BUTTON, self.onShowProperties) self.startRaceTimeCheckBox = wx.CheckBox( self.leftPanel, label=_('Start Race Automatically at Future Time')) hsSettings = wx.BoxSizer(wx.HORIZONTAL) hsSettings.Add(self.settingsButton, flag=wx.ALIGN_CENTER_VERTICAL) hsSettings.Add(self.startRaceTimeCheckBox, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border=12) border = 8 hs = wx.BoxSizer(wx.HORIZONTAL) hs.Add(self.button, border=border, flag=wx.LEFT | wx.TOP) hs.Add(self.raceIntro, 1, border=border, flag=wx.LEFT | wx.TOP | wx.RIGHT | wx.EXPAND) bs.Add(hs, border=border, flag=wx.ALL) hsClock = wx.BoxSizer(wx.HORIZONTAL) hsClock.AddSpacer(26) hsClock.Add(self.clock) hsClock.Add(hsSettings, border=4, flag=wx.LEFT) bs.Add(hsClock, border=4, flag=wx.ALL) bs.Add(self.chipTimingOptions, border=border, flag=wx.ALL) #--------------------------------------------------------------------------------------------- self.rightPanel = wx.Panel(self.splitter) self.rightPanel.SetBackgroundColour(wx.Colour(255, 255, 255)) checklistTitle = wx.StaticText(self.rightPanel, label=_('Checklist:')) checklistTitle.SetFont( wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) self.checklist = Checklist.Checklist(self.rightPanel) hsSub = wx.BoxSizer(wx.VERTICAL) hsSub.Add(checklistTitle, 0, flag=wx.ALL, border=4) hsSub.Add(self.checklist, 1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=4) self.rightPanel.SetSizer(hsSub) #--------------------------------------------------------------------------------------------- self.splitter.SplitVertically(self.leftPanel, self.rightPanel) self.splitter.SetMinimumPaneSize(100) wx.CallAfter(self.refresh) wx.CallAfter(self.splitter.SetSashPosition, 650) wx.CallAfter(self.GetSizer().Layout)
class MainWin(wx.Frame): def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) self.config = wx.Config( appName="CrossMgrImpinj", vendorName="SmartCyclingSolutions", #style=wx.Config.CONFIG_USE_LOCAL_FILE ) self.SetBackgroundColour(wx.Colour(232, 232, 232)) self.LightGreen = wx.Colour(153, 255, 153) self.LightRed = wx.Colour(255, 153, 153) font = self.GetFont() bigFont = wx.Font(int(font.GetPointSize() * 1.5), font.GetFamily(), font.GetStyle(), wx.FONTWEIGHT_BOLD) titleFont = wx.Font(int(bigFont.GetPointSize() * 2.2), bigFont.GetFamily(), bigFont.GetStyle(), bigFont.GetWeight()) self.vbs = wx.BoxSizer(wx.VERTICAL) bs = wx.BoxSizer(wx.HORIZONTAL) self.reset = RoundButton(self, label='Reset', size=(80, 80)) self.reset.SetBackgroundColour(wx.WHITE) self.reset.SetForegroundColour(wx.Colour(0, 128, 128)) self.reset.SetFontToFitLabel( ) # Use the button's default font, but change the font size to fit the label. self.reset.Bind(wx.EVT_BUTTON, self.doReset) self.reset.Refresh() bs.Add(self.reset, border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(titleFont, wx.StaticText(self, label='CrossMgrImpinj')), border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.AddStretchSpacer() bitmap = wx.Bitmap(clipboard_xpm) self.copyToClipboard = wx.BitmapButton(self, bitmap=bitmap) self.copyToClipboard.SetToolTip( wx.ToolTip('Copy Configuration and Logs to Clipboard...')) self.copyToClipboard.Bind(wx.EVT_BUTTON, self.doCopyToClipboard) bs.Add(self.copyToClipboard, border=32, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.tStart = datetime.datetime.now() bs.Add(setFont( bigFont, wx.StaticText(self, label='Last Reset: %s' % self.tStart.strftime('%H:%M:%S'))), border=10, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.runningTime = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.runningTime, border=20, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(bigFont, wx.StaticText(self, label=' / ')), flag=wx.ALIGN_CENTER_VERTICAL) self.time = setFont(bigFont, wx.StaticText(self, label='00:00:00')) bs.Add(self.time, flag=wx.ALIGN_CENTER_VERTICAL) self.vbs.Add(bs, flag=wx.ALL | wx.EXPAND, border=4) fgs = wx.FlexGridSizer(rows=2, cols=2, vgap=4, hgap=4) fgs.AddGrowableRow(1) fgs.AddGrowableCol(0) fgs.AddGrowableCol(1) fgs.SetFlexibleDirection(wx.BOTH) self.vbs.Add(fgs, flag=wx.EXPAND, proportion=5) #------------------------------------------------------------------------------------------------ # Impinj configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) iRow = 0 hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(setFont(bigFont, wx.StaticText(self, label='Impinj Configuration:')), flag=wx.ALIGN_CENTER_VERTICAL) self.autoDetectButton = wx.Button(self, label='Auto Detect') self.autoDetectButton.Bind(wx.EVT_BUTTON, self.doAutoDetect) hb.Add(self.autoDetectButton, flag=wx.LEFT, border=6) self.advancedButton = wx.Button(self, label='Advanced...') self.advancedButton.Bind(wx.EVT_BUTTON, self.doAdvanced) hb.Add(self.advancedButton, flag=wx.LEFT, border=6) gbs.Add(hb, pos=(iRow, 0), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gs = wx.GridSizer(rows=0, cols=4, vgap=0, hgap=2) self.antennaLabels = [] self.antennas = [] for i in xrange(4): self.antennaLabels.append( wx.StaticText(self, label='{}'.format(i + 1), style=wx.ALIGN_CENTER)) gs.Add(self.antennaLabels[-1], flag=wx.ALIGN_CENTER | wx.EXPAND) for i in xrange(4): cb = wx.CheckBox(self, wx.ID_ANY, '') if i < 2: cb.SetValue(True) cb.Bind(wx.EVT_CHECKBOX, lambda x: self.getAntennaStr()) gs.Add(cb, flag=wx.ALIGN_CENTER) self.antennas.append(cb) gbs.Add(wx.StaticText(self, label='ANT Ports:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) gbs.Add(gs, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) iRow += 1 self.useHostName = wx.RadioButton(self, label='Host Name:', style=wx.RB_GROUP) gbs.Add(self.useHostName, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) hb.Add(wx.StaticText(self, label=ImpinjHostNamePrefix), flag=wx.ALIGN_CENTER_VERTICAL) self.impinjHostName = masked.TextCtrl( self, mask='NN-NN-NN', defaultValue='00-00-00', useFixedWidthFont=True, size=(80, -1), ) hb.Add(self.impinjHostName) hb.Add(wx.StaticText(self, label=ImpinjHostNameSuffix), flag=wx.ALIGN_CENTER_VERTICAL) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) iRow += 1 self.useStaticAddress = wx.RadioButton(self, label='IP:') gbs.Add(self.useStaticAddress, pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.impinjHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.impinjHost) hb.Add(wx.StaticText(self, label=' : ' + '{}'.format(ImpinjInboundPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) self.useHostName.SetValue(True) self.useStaticAddress.SetValue(False) iRow += 1 gbs.Add(wx.StaticText(self, label='ANT Reads:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.antennaReadCount = wx.StaticText( self, label='1:0 0% | 2:0 0% | 3:0 0% | 4:0 0% ') gbs.Add(self.antennaReadCount, pos=(iRow, 1), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, label='Backup File:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.backupFile = wx.StaticText( self, label=' ') gbs.Add(self.backupFile, pos=(iRow, 1), span=(1, 2), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # CrossMgr configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) gbs.Add(setFont(bigFont, wx.StaticText(self, label='CrossMgr Configuration:')), pos=(0, 0), span=(1, 2), flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL) gbs.Add(wx.StaticText(self, label='CrossMgr Address:'), pos=(1, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.crossMgrHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.crossMgrHost, flag=wx.ALIGN_LEFT) hb.Add(wx.StaticText(self, label=' : 53135'), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(1, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # Add messages # self.impinjMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.impinjMessagesText, flag=wx.EXPAND, proportion=2) self.impinjMessages = MessageManager(self.impinjMessagesText) self.crossMgrMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.crossMgrMessagesText, flag=wx.EXPAND, proportion=2) self.crossMgrMessages = MessageManager(self.crossMgrMessagesText) self.fgs = fgs #------------------------------------------------------------------------------------------------ # Create a timer to update the messages. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.updateMessages) self.timer.Start(1000, False) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizer(self.vbs) self.start() def readerStatusCB(self, **kwargs): # As this is called from another thread, make sure all UI updates are done from CallAfter. connectedAntennas = set(kwargs.get('connectedAntennas', [])) for i in xrange(4): wx.CallAfter( self.antennaLabels[i].SetBackgroundColour, self.LightGreen if (i + 1) in connectedAntennas else wx.NullColour) def start(self): self.dataQ = Queue() self.messageQ = Queue() self.shutdownQ = Queue( ) # Queue to tell the Impinj monitor to shut down. self.readerStatusCB() if self.useHostName.GetValue(): self.impinjProcess = Process( name='ImpinjProcess', target=ImpinjServer, args=( self.dataQ, self.messageQ, self.shutdownQ, ImpinjHostNamePrefix + self.impinjHostName.GetValue() + ImpinjHostNameSuffix, ImpinjInboundPort, self.getAntennaStr(), self.readerStatusCB, )) else: self.impinjProcess = Process(name='ImpinjProcess', target=ImpinjServer, args=( self.dataQ, self.messageQ, self.shutdownQ, self.impinjHost.GetAddress(), ImpinjInboundPort, self.getAntennaStr(), self.readerStatusCB, )) self.impinjProcess.daemon = True self.crossMgrProcess = Process(name='CrossMgrProcess', target=CrossMgrServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getCrossMgrHost(), CrossMgrPort)) self.crossMgrProcess.daemon = True self.impinjProcess.start() self.crossMgrProcess.start() def shutdown(self): self.impinjProcess = None self.crossMgrProcess = None self.messageQ = None self.dataQ = None self.shutdownQ = None def doReset(self, event, confirm=True): if confirm: dlg = wx.MessageDialog(self, 'Reset CrossMgrImpinj Adapter?', 'Confirm Reset', wx.OK | wx.CANCEL | wx.ICON_WARNING) ret = dlg.ShowModal() dlg.Destroy() if ret != wx.ID_OK: return self.reset.Enable( False) # Prevent multiple clicks while shutting down. self.writeOptions() self.gracefulShutdown() self.impinjMessages.clear() self.crossMgrMessages.clear() self.shutdown() self.reset.Enable(True) wx.CallAfter(self.start) def doAutoDetect(self, event): wx.BeginBusyCursor() self.gracefulShutdown() self.shutdown() impinjHost, crossMgrHost = AutoDetect( ImpinjInboundPort)[0], '127.0.0.1' wx.EndBusyCursor() if impinjHost and crossMgrHost: self.useStaticAddress.SetValue(True) self.useHostName.SetValue(False) self.impinjHost.SetValue(impinjHost) self.crossMgrHost.SetValue(crossMgrHost) else: dlg = wx.MessageDialog( self, 'Auto Detect Failed.\nCheck that reader has power and is connected to the router.', 'Auto Detect Failed', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() self.doReset(event, False) def doAdvanced(self, event): dlg = AdvancedSetup(self) dlg.ShowModal() dlg.Destroy() def gracefulShutdown(self): # Shutdown the CrossMgr process by sending it a shutdown command. if self.shutdownQ: self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') if self.dataQ: self.dataQ.put('shutdown') self.dataQ.put('shutdown') if self.crossMgrProcess: self.crossMgrProcess.join() if self.impinjProcess: self.impinjProcess.join() self.crossMgrProcess = None self.impinjProcess = None def onCloseWindow(self, event): self.gracefulShutdown() wx.Exit() def doCopyToClipboard(self, event): cc = [ 'Configuration: CrossMgrImpinj', ' RunningTime: {}'.format(self.runningTime.GetLabel()), ' Time: {}'.format(self.time.GetLabel()), ' BackupFile: {}'.format(self.backupFile.GetLabel()), '', 'Configuration: Impinj:', ' Use Host Name: {}'.format( 'True' if self.useHostName.GetValue() else 'False'), ' HostName: {}'.format((ImpinjHostNamePrefix + self.impinjHostName.GetValue()) + ImpinjHostNameSuffix), ' ImpinjHost: {}'.format(self.impinjHost.GetAddress()), ' ImpinjPort: {}'.format(ImpinjInboundPort), '' ' ConnectionTimeoutSeconds: {}'.format( Impinj.ConnectionTimeoutSeconds), ' KeepaliveSeconds: {}'.format(Impinj.KeepaliveSeconds), ' RepeatSeconds: {}'.format(Impinj.RepeatSeconds), '', 'Configuration: CrossMgr', ' CrossMgrHost: {}'.format(self.getCrossMgrHost()), ' CrossMgrPort: {}'.format(CrossMgrPort), ] cc.append('\nLog: Impinj') log = self.impinjMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: CrossMgr') log = self.crossMgrMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: Application\n') try: with open(redirectFileName, 'r') as fp: for line in fp: cc.append(line) except: pass if wx.TheClipboard.Open(): do = wx.TextDataObject() do.SetText('\n'.join(cc)) wx.TheClipboard.SetData(do) wx.TheClipboard.Close() dlg = wx.MessageDialog( self, 'Configuration and Logs copied to the Clipboard.', 'Copy to Clipboard Succeeded', wx.OK | wx.ICON_INFORMATION) ret = dlg.ShowModal() dlg.Destroy() else: # oops... something went wrong! wx.MessageBox("Unable to open the clipboard", "Error") def getCrossMgrHost(self): return self.crossMgrHost.GetAddress() def getAntennaStr(self): selectedAntennas = [ i for i in xrange(4) if self.antennas[i].GetValue() ] # Ensure at least one antenna is selected. if not selectedAntennas: self.antennas[0].SetValue(True) selectedAntennas = [0] return ' '.join('{}'.format(a + 1) for a in selectedAntennas) def setAntennaStr(self, s): antennas = set(int(a) for a in s.split()) for i in xrange(4): self.antennas[i].SetValue((i + 1) in antennas) def writeOptions(self): self.config.Write('CrossMgrHost', self.getCrossMgrHost()) self.config.Write('UseHostName', 'True' if self.useHostName.GetValue() else 'False') self.config.Write( 'ImpinjHostName', ImpinjHostNamePrefix + self.impinjHostName.GetValue() + ImpinjHostNameSuffix) self.config.Write('ImpinjAddr', self.impinjHost.GetAddress()) self.config.Write('ImpinjPort', '{}'.format(ImpinjInboundPort)) self.config.Write('Antennas', self.getAntennaStr()) self.config.Write('ConnectionTimeoutSeconds', '{}'.format(Impinj.ConnectionTimeoutSeconds)) self.config.Write('KeepaliveSeconds', '{}'.format(Impinj.KeepaliveSeconds)) self.config.Write('RepeatSeconds', '{}'.format(Impinj.RepeatSeconds)) self.config.Write('PlaySounds', '{}'.format(Utils.playBell)) self.config.Write('ReceiverSensitivity', '{}'.format(Impinj.ReceiverSensitivity or 0)) self.config.Write('TransmitPower', '{}'.format(Impinj.TransmitPower or 0)) self.config.Write('TagPopulation', '{}'.format(Impinj.TagPopulation or 0)) self.config.Write('TagTransitTime', '{}'.format(Impinj.TagTransitTime or 0)) self.config.Flush() def readOptions(self): self.crossMgrHost.SetValue( self.config.Read('CrossMgrHost', Utils.DEFAULT_HOST)) useHostName = (self.config.Read('UseHostName', 'True').upper()[:1] == 'T') self.useHostName.SetValue(useHostName) self.useStaticAddress.SetValue(not useHostName) self.impinjHostName.SetValue( self.config.Read( 'ImpinjHostName', ImpinjHostNamePrefix + '00-00-00' + ImpinjHostNameSuffix) [len(ImpinjHostNamePrefix):-len(ImpinjHostNameSuffix)]) self.impinjHost.SetValue(self.config.Read('ImpinjAddr', '0.0.0.0')) self.setAntennaStr(self.config.Read('Antennas', '1 2 3 4')) Utils.playBell = (self.config.Read('PlaySounds', 'True').upper()[:1] == 'T') Impinj.ConnectionTimeoutSeconds = int( self.config.Read('ConnectionTimeoutSeconds', '{}'.format(Impinj.ConnectionTimeoutSeconds))) Impinj.KeepaliveSeconds = int( self.config.Read('KeepaliveSeconds', '{}'.format(Impinj.KeepaliveSeconds))) Impinj.RepeatSeconds = int( self.config.Read('RepeatSeconds', '{}'.format(Impinj.RepeatSeconds))) Impinj.ReceiverSensitivity = int( self.config.Read('ReceiverSensitivity', '0')) or None Impinj.TransmitPower = int(self.config.Read('TransmitPower', '0')) or None Impinj.TagPopulation = int(self.config.Read('TagPopulation', '0')) or None Impinj.TagTransitTime = int(self.config.Read('TagTransitTime', '0')) or None def updateMessages(self, event): tNow = datetime.datetime.now() running = int((tNow - self.tStart).total_seconds()) self.runningTime.SetLabel('{:02d}:{:02d}:{:02d}'.format( running // (60 * 60), (running // 60) % 60, running % 60)) self.time.SetLabel(tNow.strftime('%H:%M:%S')) if not self.messageQ: return while 1: try: d = self.messageQ.get(False) except Empty: break if isinstance(d[-1], dict): antennaReadCount = d[-1] d = d[:-1] else: antennaReadCount = None message = ' '.join(unicode(x) for x in d[1:]) if d[0] == 'Impinj': if 'state' in d: self.impinjMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.impinjMessages.write(message) if antennaReadCount is not None: total = max( 1, sum(antennaReadCount[i] for i in xrange(1, 4 + 1))) self.antennaReadCount.SetLabel(' | '.join( '{}:{} {:.1f}%'.format( i, antennaReadCount[i], antennaReadCount[i] * 100.0 / total) for i in xrange(1, 4 + 1))) elif d[0] == 'Impinj2JChip': if 'state' in d: self.crossMgrMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.crossMgrMessages.write(message) elif d[0] == 'BackupFile': self.backupFile.SetLabel(d[1])
def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) self.SetBackgroundColour(wx.Colour(240, 240, 240)) self.fname = None self.updated = False self.firstTime = True self.lastUpdateTime = None self.comments = [] self.filehistory = wx.FileHistory(16) self.config = wx.Config(appName="StageRaceGC", vendorName="*****@*****.**", style=wx.CONFIG_USE_LOCAL_FILE) self.filehistory.Load(self.config) inputBox = wx.StaticBox(self, label=_('Input')) inputBoxSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL) self.fileBrowse = filebrowse.FileBrowseButtonWithHistory( self, labelText=_('Excel File'), buttonText=('Browse...'), startDirectory=os.path.expanduser('~'), fileMask= 'Excel Spreadsheet (*.xlsx; *.xlsm; *.xls)|*.xlsx; *.xlsml; *.xls', size=(400, -1), history=lambda: [ self.filehistory.GetHistoryFile(i) for i in xrange(self.filehistory.GetCount()) ], changeCallback=self.doChangeCallback, ) inputBoxSizer.Add(self.fileBrowse, 0, flag=wx.EXPAND | wx.ALL, border=4) horizontalControlSizer = wx.BoxSizer(wx.HORIZONTAL) self.updateButton = RoundButton(self, size=(96, 96)) self.updateButton.SetLabel(_('Update')) self.updateButton.SetFontToFitLabel() self.updateButton.SetForegroundColour(wx.Colour(0, 100, 0)) self.updateButton.Bind(wx.EVT_BUTTON, self.doUpdate) horizontalControlSizer.Add(self.updateButton, flag=wx.ALL, border=4) horizontalControlSizer.AddSpacer(48) vs = wx.BoxSizer(wx.VERTICAL) self.tutorialButton = wx.Button(self, label=_('Help/Tutorial...')) self.tutorialButton.Bind(wx.EVT_BUTTON, self.onTutorial) vs.Add(self.tutorialButton, flag=wx.ALL, border=4) branding = wx.adv.HyperlinkCtrl( self, id=wx.ID_ANY, label=u"Powered by CrossMgr", url=u"http://www.sites.google.com/site/crossmgrsoftware/") vs.Add(branding, flag=wx.ALL, border=4) horizontalControlSizer.Add(vs) self.openExcel = wx.Button(self, label=_('Open Excel File...')) self.openExcel.Bind(wx.EVT_BUTTON, self.onOpenExcel) horizontalControlSizer.AddSpacer(48) horizontalControlSizer.Add(self.openExcel, flag=wx.ALIGN_RIGHT | wx.ALL, border=4) inputBoxSizer.Add(horizontalControlSizer, flag=wx.EXPAND) self.stageList = ListMixCtrl(self, style=wx.LC_REPORT, size=(-1, 160)) self.stageList.InsertColumn(0, "Sheet") self.stageList.InsertColumn(1, "Bibs", wx.LIST_FORMAT_RIGHT) self.stageList.InsertColumn(2, "Errors/Warnings") self.stageList.setResizeColumn(2) bookStyle = (flatnotebook.FNB_NO_X_BUTTON | flatnotebook.FNB_FF2 | flatnotebook.FNB_NODRAG | flatnotebook.FNB_DROPDOWN_TABS_LIST | flatnotebook.FNB_NO_NAV_BUTTONS | flatnotebook.FNB_BOTTOM) self.notebook = flatnotebook.FlatNotebook(self, 1000, agwStyle=bookStyle) self.notebook.SetBackgroundColour(wx.WHITE) self.saveAsExcelButton = wx.Button(self, label=u'Save as Excel') self.saveAsExcelButton.Bind(wx.EVT_BUTTON, self.saveAsExcel) mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(inputBoxSizer, flag=wx.EXPAND | wx.ALL, border=4) mainSizer.Add(self.stageList, flag=wx.EXPAND | wx.ALL, border=4) mainSizer.Add(self.notebook, 1, flag=wx.EXPAND | wx.ALL, border=4) mainSizer.Add(self.saveAsExcelButton, flag=wx.ALL, border=4) self.SetSizer(mainSizer)
class Actions(wx.Panel): iResetStartClockOnFirstTag = 1 iSkipFirstTagRead = 2 def __init__(self, parent, id=wx.ID_ANY): wx.Panel.__init__(self, parent, id) self.SetBackgroundColour(wx.Colour(255, 255, 255)) ps = wx.BoxSizer(wx.VERTICAL) self.splitter = wx.SplitterWindow(self, wx.VERTICAL) ps.Add(self.splitter, 1, flag=wx.EXPAND) self.SetSizer(ps) #--------------------------------------------------------------------------------------------- self.leftPanel = wx.Panel(self.splitter) bs = wx.BoxSizer(wx.VERTICAL) self.leftPanel.SetSizer(bs) self.leftPanel.SetBackgroundColour(wx.Colour(255, 255, 255)) self.leftPanel.Bind(wx.EVT_SIZE, self.setWrappedRaceInfo) buttonSize = 220 self.button = RoundButton(self.leftPanel, size=(buttonSize, buttonSize)) self.button.SetLabel(FinishText) self.button.SetFontToFitLabel() self.button.SetForegroundColour(wx.Colour(128, 128, 128)) self.Bind(wx.EVT_BUTTON, self.onPress, self.button) self.clock = Clock(self, size=(190, 190), checkFunc=self.updateClock) self.clock.SetBackgroundColour(wx.WHITE) self.raceIntro = wx.StaticText(self.leftPanel, label=u'') self.raceIntro.SetFont(wx.Font(20, wx.DEFAULT, wx.NORMAL, wx.NORMAL)) self.chipTimingOptions = wx.RadioBox( self.leftPanel, label=_("Chip Timing Options"), majorDimension=1, choices=Properties.RfidProperties.choices, style=wx.RA_SPECIFY_COLS) self.Bind(wx.EVT_RADIOBOX, self.onChipTimingOptions, self.chipTimingOptions) self.settingsButton = wx.BitmapButton( self.leftPanel, bitmap=Utils.GetPngBitmap('settings-icon.png')) self.settingsButton.SetToolTip(wx.ToolTip(_('Properties Shortcut'))) self.settingsButton.Bind(wx.EVT_BUTTON, self.onShowProperties) self.startRaceTimeCheckBox = wx.CheckBox( self.leftPanel, label=_('Start Race Automatically at Future Time')) hsSettings = wx.BoxSizer(wx.HORIZONTAL) hsSettings.Add(self.settingsButton, flag=wx.ALIGN_CENTER_VERTICAL) hsSettings.Add(self.startRaceTimeCheckBox, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border=12) border = 8 hs = wx.BoxSizer(wx.HORIZONTAL) hs.Add(self.button, border=border, flag=wx.LEFT | wx.TOP) hs.Add(self.raceIntro, 1, border=border, flag=wx.LEFT | wx.TOP | wx.RIGHT | wx.EXPAND) bs.Add(hs, border=border, flag=wx.ALL) hsClock = wx.BoxSizer(wx.HORIZONTAL) hsClock.AddSpacer(26) hsClock.Add(self.clock) hsClock.Add(hsSettings, border=4, flag=wx.LEFT) bs.Add(hsClock, border=4, flag=wx.ALL) bs.Add(self.chipTimingOptions, border=border, flag=wx.ALL) #--------------------------------------------------------------------------------------------- self.rightPanel = wx.Panel(self.splitter) self.rightPanel.SetBackgroundColour(wx.Colour(255, 255, 255)) checklistTitle = wx.StaticText(self.rightPanel, label=_('Checklist:')) checklistTitle.SetFont( wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) self.checklist = Checklist.Checklist(self.rightPanel) hsSub = wx.BoxSizer(wx.VERTICAL) hsSub.Add(checklistTitle, 0, flag=wx.ALL, border=4) hsSub.Add(self.checklist, 1, flag=wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border=4) self.rightPanel.SetSizer(hsSub) #--------------------------------------------------------------------------------------------- self.splitter.SplitVertically(self.leftPanel, self.rightPanel) self.splitter.SetMinimumPaneSize(100) wx.CallAfter(self.refresh) wx.CallAfter(self.splitter.SetSashPosition, 650) wx.CallAfter(self.GetSizer().Layout) def setWrappedRaceInfo(self, event=None): wrapWidth = self.leftPanel.GetClientSize( )[0] - self.button.GetClientSize()[0] - 20 dc = wx.WindowDC(self.raceIntro) dc.SetFont(self.raceIntro.GetFont()) label = wordwrap(Model.race.getRaceIntro() if Model.race else u'', wrapWidth, dc) self.raceIntro.SetLabel(label) self.leftPanel.GetSizer().Layout() if event: event.Skip() def updateChipTimingOptions(self): if not Model.race: return iSelection = self.chipTimingOptions.GetSelection() race = Model.race race.resetStartClockOnFirstTag = bool( iSelection == self.iResetStartClockOnFirstTag) race.skipFirstTagRead = bool(iSelection == self.iSkipFirstTagRead) def updateClock(self): mainWin = Utils.getMainWin() return not mainWin or mainWin.isShowingPage(self) def onShowProperties(self, event): if not Model.race: Utils.MessageOK( self, _("You must have a valid race. Open or New a race first."), _("No Valid Race"), iconMask=wx.ICON_ERROR) return if not hasattr(self, 'propertiesDialog'): self.propertiesDialog = PropertiesDialog(self, showFileFields=False, updateProperties=True, size=(600, 400)) else: self.propertiesDialog.properties.refresh(forceUpdate=True) self.propertiesDialog.properties.setPage('raceOptionsProperties') if self.propertiesDialog.ShowModal() == wx.ID_OK: self.propertiesDialog.properties.doCommit() Utils.refresh() def onChipTimingOptions(self, event): if not Model.race: return self.updateChipTimingOptions() def onPress(self, event): if not Model.race: return with Model.LockRace() as race: running = race.isRunning() if running: self.onFinishRace(event) return self.updateChipTimingOptions() if getattr(Model.race, 'enableJChipIntegration', False): try: externalFields = race.excelLink.getFields() externalInfo = race.excelLink.read() except: externalFields = [] externalInfo = {} if not externalInfo: Utils.MessageOK( self, u'\n\n'.join([ _('Cannot Start. Excel Sheet read failure.'), _('The Excel file is either unconfigured or unreadable.' ) ]), _('Excel Sheet Read '), wx.ICON_ERROR) return try: i = next((i for i, field in enumerate(externalFields) if field.startswith('Tag'))) except StopIteration: Utils.MessageOK( self, u'\n\n'.join([ _('Cannot Start. Excel Sheet missing Tag column.'), _('The Excel file must contain a Tag column to use RFID.' ) ]), _('Excel Sheet missing Tag column'), wx.ICON_ERROR) return if self.startRaceTimeCheckBox.IsChecked(): self.onStartRaceTime(event) else: self.onStartRace(event) def onStartRace(self, event): if Model.race and Utils.MessageOKCancel(self, _('Start Race Now?\n\n'), _('Start Race')): StartRaceNow() def onStartRaceTime(self, event): if Model.race is None: return dlg = StartRaceAtTime(self) dlg.ShowModal() dlg.Destroy() def onFinishRace(self, event): if Model.race is None or not Utils.MessageOKCancel( self, _('Finish Race Now?'), _('Finish Race')): return with Model.LockRace() as race: race.finishRaceNow() if race.numLaps is None: race.numLaps = race.getMaxLap() SetNoDataDNS() Model.resetCache() Utils.writeRace() self.refresh() mainWin = Utils.getMainWin() if mainWin: mainWin.refresh() OutputStreamer.writeRaceFinish() OutputStreamer.StopStreamer() try: ChipReader.chipReaderCur.StopListener() except: pass if getattr(Model.race, 'ftpUploadDuringRace', False): realTimeFtpPublish.publishEntry(True) def commit(self): self.checklist.commit() def refresh(self): self.clock.Start() self.button.Enable(False) self.startRaceTimeCheckBox.Enable(False) self.settingsButton.Enable(False) self.button.SetLabel(StartText) self.button.SetForegroundColour(wx.Colour(100, 100, 100)) self.chipTimingOptions.SetSelection(0) self.chipTimingOptions.Enable(False) with Model.LockRace() as race: if race: self.settingsButton.Enable(True) # Adjust the chip recording options for TT. if getattr(race, 'isTimeTrial', False): race.resetStartClockOnFirstTag = False race.skipFirstTagRead = False if getattr(race, 'resetStartClockOnFirstTag', True): self.chipTimingOptions.SetSelection( self.iResetStartClockOnFirstTag) elif getattr(race, 'skipFirstTagRead', False): self.chipTimingOptions.SetSelection(self.iSkipFirstTagRead) if race.startTime is None: self.button.Enable(True) self.button.SetLabel(StartText) self.button.SetForegroundColour(wx.Colour(0, 128, 0)) self.startRaceTimeCheckBox.Enable(True) self.startRaceTimeCheckBox.Show(True) self.chipTimingOptions.Enable( getattr(race, 'enableJChipIntegration', False)) self.chipTimingOptions.Show( getattr(race, 'enableJChipIntegration', False)) elif race.isRunning(): self.button.Enable(True) self.button.SetLabel(FinishText) self.button.SetForegroundColour(wx.Colour(128, 0, 0)) self.startRaceTimeCheckBox.Enable(False) self.startRaceTimeCheckBox.Show(False) self.chipTimingOptions.Enable(False) self.chipTimingOptions.Show(False) # Adjust the time trial display options. if getattr(race, 'isTimeTrial', False): self.chipTimingOptions.Enable(False) self.chipTimingOptions.Show(False) self.GetSizer().Layout() self.setWrappedRaceInfo() self.checklist.refresh() mainWin = Utils.getMainWin() if mainWin is not None: mainWin.updateRaceClock()
def CreateCaptureButtons( parent ): snapshot = RoundButton( parent, label="SNAPSHOT", size=(90,90) ) snapshot.SetBackgroundColour( wx.WHITE ) snapshot.SetForegroundColour( snapshotEnableColour ) snapshot.SetFontToFitLabel( wx.Font(wx.FontInfo(10).Bold()) ) snapshot.SetToolTip( _('Record a Single Frame') ) autoCapture = RoundButton( parent, label="AUTO\nCAPTURE", size=(90,90) ) autoCapture.SetBackgroundColour( wx.WHITE ) autoCapture.SetForegroundColour( autoCaptureEnableColour ) autoCapture.SetFontToFitLabel( wx.Font(wx.FontInfo(10).Bold()) ) autoCapture.SetToolTip( _('Capture Video for an Automatic Interval\nSet in "Config Auto Capture"') ) capture = RoundButton( parent, label="CAPTURE", size=(90,90) ) capture.SetBackgroundColour( wx.WHITE ) capture.SetForegroundColour( captureEnableColour ) capture.SetFontToFitLabel( wx.Font(wx.FontInfo(10).Bold()) ) capture.SetToolTip( _('Capture Video\nwhile the Button is held down') ) return snapshot, autoCapture, capture
class MainWin(wx.Frame): def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) self.SetBackgroundColour(wx.Colour(240, 240, 240)) self.fname = None self.updated = False self.firstTime = True self.lastUpdateTime = None self.comments = [] self.filehistory = wx.FileHistory(16) self.config = wx.Config(appName="StageRaceGC", vendorName="*****@*****.**", style=wx.CONFIG_USE_LOCAL_FILE) self.filehistory.Load(self.config) inputBox = wx.StaticBox(self, label=_('Input')) inputBoxSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL) self.fileBrowse = filebrowse.FileBrowseButtonWithHistory( self, labelText=_('Excel File'), buttonText=('Browse...'), startDirectory=os.path.expanduser('~'), fileMask= 'Excel Spreadsheet (*.xlsx; *.xlsm; *.xls)|*.xlsx; *.xlsml; *.xls', size=(400, -1), history=lambda: [ self.filehistory.GetHistoryFile(i) for i in xrange(self.filehistory.GetCount()) ], changeCallback=self.doChangeCallback, ) inputBoxSizer.Add(self.fileBrowse, 0, flag=wx.EXPAND | wx.ALL, border=4) horizontalControlSizer = wx.BoxSizer(wx.HORIZONTAL) self.updateButton = RoundButton(self, size=(96, 96)) self.updateButton.SetLabel(_('Update')) self.updateButton.SetFontToFitLabel() self.updateButton.SetForegroundColour(wx.Colour(0, 100, 0)) self.updateButton.Bind(wx.EVT_BUTTON, self.doUpdate) horizontalControlSizer.Add(self.updateButton, flag=wx.ALL, border=4) horizontalControlSizer.AddSpacer(48) vs = wx.BoxSizer(wx.VERTICAL) self.tutorialButton = wx.Button(self, label=_('Help/Tutorial...')) self.tutorialButton.Bind(wx.EVT_BUTTON, self.onTutorial) vs.Add(self.tutorialButton, flag=wx.ALL, border=4) branding = wx.adv.HyperlinkCtrl( self, id=wx.ID_ANY, label=u"Powered by CrossMgr", url=u"http://www.sites.google.com/site/crossmgrsoftware/") vs.Add(branding, flag=wx.ALL, border=4) horizontalControlSizer.Add(vs) self.openExcel = wx.Button(self, label=_('Open Excel File...')) self.openExcel.Bind(wx.EVT_BUTTON, self.onOpenExcel) horizontalControlSizer.AddSpacer(48) horizontalControlSizer.Add(self.openExcel, flag=wx.ALIGN_RIGHT | wx.ALL, border=4) inputBoxSizer.Add(horizontalControlSizer, flag=wx.EXPAND) self.stageList = ListMixCtrl(self, style=wx.LC_REPORT, size=(-1, 160)) self.stageList.InsertColumn(0, "Sheet") self.stageList.InsertColumn(1, "Bibs", wx.LIST_FORMAT_RIGHT) self.stageList.InsertColumn(2, "Errors/Warnings") self.stageList.setResizeColumn(2) bookStyle = (flatnotebook.FNB_NO_X_BUTTON | flatnotebook.FNB_FF2 | flatnotebook.FNB_NODRAG | flatnotebook.FNB_DROPDOWN_TABS_LIST | flatnotebook.FNB_NO_NAV_BUTTONS | flatnotebook.FNB_BOTTOM) self.notebook = flatnotebook.FlatNotebook(self, 1000, agwStyle=bookStyle) self.notebook.SetBackgroundColour(wx.WHITE) self.saveAsExcelButton = wx.Button(self, label=u'Save as Excel') self.saveAsExcelButton.Bind(wx.EVT_BUTTON, self.saveAsExcel) mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(inputBoxSizer, flag=wx.EXPAND | wx.ALL, border=4) mainSizer.Add(self.stageList, flag=wx.EXPAND | wx.ALL, border=4) mainSizer.Add(self.notebook, 1, flag=wx.EXPAND | wx.ALL, border=4) mainSizer.Add(self.saveAsExcelButton, flag=wx.ALL, border=4) self.SetSizer(mainSizer) def onOpenExcel(self, event): filename = self.fileBrowse.GetValue() if not filename: return wait = wx.BusyCursor() Utils.LaunchApplication(filename) def onTutorial(self, event): if not Utils.MessageOKCancel( self, u"\n".join([ _("Launch the StageRaceGC Tutorial."), _("This open a sample Excel input file created into your home folder." ), _("This data in this sheet is made-up, although it does include some current rider's names." ), u"", _("It will also open the Tutorial page in your browser. If you can't see your browser, make sure you bring to the front." ), u"", _("Continue?"), ])): return try: fname_excel = MakeExampleExcel() except Exception as e: traceback.print_exc() Utils.MessageOK( self, u'{}\n\n{}\n\n{}'.format( u'Problem creating Excel sheet.', e, _('If the Excel file is open, please close it and try again' ))) return self.fileBrowse.SetValue(fname_excel) self.doUpdate(event) Utils.LaunchApplication(fname_excel) Utils.LaunchApplication( os.path.join(Utils.getHtmlDocFolder(), 'Tutorial.html')) def doChangeCallback(self, event): fname = event.GetString() if not fname: self.setUpdated(False) return if fname != self.fname: wx.CallAfter(self.doUpdate, fnameNew=fname) def setUpdated(self, updated=True): self.updated = updated for w in [self.stageList, self.saveAsExcelButton]: w.Enable(updated) if not updated: self.stageList.DeleteAllItems() self.notebook.DeleteAllPages() def updateStageList(self, registration=None, stages=None): self.stageList.DeleteAllItems() registration = (registration or (Model.model and Model.model.registration) or None) if not registration: return stages = (stages or (Model.model and Model.model.stages) or []) def insert_stage_info(stage): idx = self.stageList.InsertItem(sys.maxint, stage.sheet_name) self.stageList.SetItem(idx, 1, unicode(len(stage))) if stage.errors: self.stageList.SetItem( idx, 2, u'{}: {}'.format( len(stage.errors), u' '.join(u'[{}]'.format(e) for e in stage.errors))) else: self.stageList.SetItem( idx, 2, u' ' ) insert_stage_info(registration) for stage in stages: insert_stage_info(stage) for col in xrange(3): self.stageList.SetColumnWidth(col, wx.LIST_AUTOSIZE) self.stageList.SetColumnWidth(1, 52) self.stageList.Refresh() def callbackUpdate(self, message): pass def doUpdate(self, event=None, fnameNew=None): try: self.fname = (fnameNew or event.GetString() or self.fileBrowse.GetValue()) except: self.fname = u'' if not self.fname: Utils.MessageOK( self, _('Missing Excel file. Please select an Excel file.'), _('Missing Excel File')) self.setUpdated(False) return if self.lastUpdateTime and (datetime.datetime.now() - self.lastUpdateTime).total_seconds() < 1.0: return try: with open(self.fname, 'rb') as f: pass except Exception as e: traceback.print_exc() Utils.MessageOK( self, u'{}:\n\n {}\n\n{}'.format(_('Cannot Open Excel file'), self.fname, e), _('Cannot Open Excel File')) self.setUpdated(False) return self.filehistory.AddFileToHistory(self.fname) self.filehistory.Save(self.config) self.fileBrowse.SetValue(self.fname) wait = wx.BusyCursor() labelSave, backgroundColourSave = self.updateButton.GetLabel( ), self.updateButton.GetForegroundColour() try: Model.read(self.fname, callbackfunc=self.updateStageList) except Exception as e: traceback.print_exc() Utils.MessageOK( self, u'{}:\n\n {}\n\n{}'.format(_('Excel File Error'), self.fname, e), _('Excel File Error')) self.setUpdated(False) return Model.model.getGCs() self.setUpdated(True) self.updateStageList() StageRaceGCToGrid(self.notebook) self.lastUpdateTime = datetime.datetime.now() def getOutputExcelName(self): fname_base, fname_suffix = os.path.splitext(self.fname) fname_excel = '{}-{}{}'.format(fname_base, 'GC', '.xlsx') return fname_excel def saveAsExcel(self, event): fname_excel = self.getOutputExcelName() if os.path.isfile(fname_excel): if not Utils.MessageOKCancel( self, u'"{}"\n\n{}'.format(fname_excel, _('File exists. Replace?')), _('Output Excel File Exists'), ): return try: StageRaceGCToExcel(fname_excel, Model.model) except Exception as e: Utils.MessageOK( self, u'{}: "{}"\n\n{}\n\n"{}"'.format( _("Write Failed"), e, _("If you have this file open, close it and try again."), fname_excel), _("Excel Write Failed."), iconMask=wx.ICON_ERROR, ) return wait = wx.BusyCursor() Utils.LaunchApplication(fname_excel)
def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) self.config = wx.Config( appName="CrossMgrAlien", vendorName="SmartCyclingSolutions", #style=wx.CONFIG_USE_LOCAL_FILE ) self.SetBackgroundColour(wx.Colour(232, 232, 232)) self.LightGreen = wx.Colour(153, 255, 153) self.LightRed = wx.Colour(255, 153, 153) font = self.GetFont() bigFont = wx.Font(font.GetPointSize() * 1.5, font.GetFamily(), font.GetStyle(), wx.FONTWEIGHT_BOLD) titleFont = wx.Font(bigFont.GetPointSize() * 2.2, bigFont.GetFamily(), bigFont.GetStyle(), bigFont.GetWeight()) self.vbs = wx.BoxSizer(wx.VERTICAL) bs = wx.BoxSizer(wx.HORIZONTAL) self.reset = RoundButton(self, wx.ID_ANY, 'Reset', size=(80, 80)) self.reset.SetBackgroundColour(wx.WHITE) self.reset.SetForegroundColour(wx.Colour(0, 128, 128)) self.reset.SetFontToFitLabel( ) # Use the button's default font, but change the font size to fit the label. self.reset.Bind(wx.EVT_BUTTON, self.doReset) self.reset.Refresh() bs.Add(self.reset, border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(titleFont, wx.StaticText(self, wx.ID_ANY, 'CrossMgrAlien')), border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.AddStretchSpacer() bitmap = wx.Bitmap(clipboard_xpm) self.copyToClipboard = wx.BitmapButton(self, wx.ID_ANY, bitmap) self.copyToClipboard.SetToolTip( wx.ToolTip('Copy Configuration and Logs to Clipboard...')) self.copyToClipboard.Bind(wx.EVT_BUTTON, self.doCopyToClipboard) bs.Add(self.copyToClipboard, border=32, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.tStart = datetime.datetime.now() bs.Add(setFont( bigFont, wx.StaticText( self, wx.ID_ANY, 'Last Reset: {}'.format(self.tStart.strftime('%H:%M:%S')))), border=10, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.runningTime = setFont(bigFont, wx.StaticText(self, wx.ID_ANY, '00:00:00')) bs.Add(self.runningTime, border=20, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(bigFont, wx.StaticText(self, wx.ID_ANY, ' / ')), flag=wx.ALIGN_CENTER_VERTICAL) self.time = setFont(bigFont, wx.StaticText(self, wx.ID_ANY, '00:00:00')) bs.Add(self.time, flag=wx.ALIGN_CENTER_VERTICAL) self.vbs.Add(bs, flag=wx.ALL | wx.EXPAND, border=4) fgs = wx.FlexGridSizer(rows=0, cols=2, vgap=4, hgap=4) fgs.AddGrowableRow(1) fgs.AddGrowableCol(0) fgs.AddGrowableCol(1) fgs.SetFlexibleDirection(wx.BOTH) self.vbs.Add(fgs, flag=wx.EXPAND, proportion=5) #------------------------------------------------------------------------------------------------ # Alien configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) iRow = 0 hs = wx.BoxSizer(wx.HORIZONTAL) hs.Add(setFont(bigFont, wx.StaticText(self, wx.ID_ANY, 'Alien Configuration:')), flag=wx.ALIGN_CENTER_VERTICAL) self.autoDetectButton = wx.Button(self, wx.ID_ANY, 'Auto Detect') self.autoDetectButton.Bind(wx.EVT_BUTTON, self.doAutoDetect) hs.Add(self.autoDetectButton, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=6) self.advancedButton = wx.Button(self, wx.ID_ANY, 'Advanced...') self.advancedButton.Bind(wx.EVT_BUTTON, self.doAdvanced) hs.Add(self.advancedButton, flag=wx.LEFT, border=6) gbs.Add(hs, pos=(iRow, 0), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, wx.ID_ANY, 'Antennas:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) gs = wx.GridSizer(rows=0, cols=4, hgap=2, vgap=2) self.antennas = [] for i in range(4): gs.Add(wx.StaticText(self, wx.ID_ANY, '{}'.format(i)), flag=wx.ALIGN_CENTER) for i in range(4): cb = wx.CheckBox(self, wx.ID_ANY, '') if i < 2: cb.SetValue(True) cb.Bind(wx.EVT_CHECKBOX, lambda x: self.getAntennaStr()) gs.Add(cb, flag=wx.ALIGN_CENTER) self.antennas.append(cb) gbs.Add(gs, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) iRow += 1 gbs.Add(wx.StaticText(self, wx.ID_ANY, 'Notify Address:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) ips = Utils.GetAllIps() self.notifyHost = wx.Choice(self, choices=ips) hb.Add(self.notifyHost) hb.Add(wx.StaticText(self, label=' : {}'.format(NotifyPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1)) iRow += 1 self.listenForHeartbeat = wx.CheckBox( self, label='Listen for Alien Heartbeat on Port: {}'.format( HeartbeatPort), style=wx.ALIGN_LEFT) self.listenForHeartbeat.SetValue(True) gbs.Add(self.listenForHeartbeat, pos=(iRow, 0), span=(1, 2)) iRow += 1 gbs.Add(wx.StaticText(self, label='Alien Cmd Address:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.cmdHost = IpAddrCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_TAB) hb.Add(self.cmdHost) hb.Add(wx.StaticText(self, label=' : '), flag=wx.ALIGN_CENTER_VERTICAL) self.cmdPort = intctrl.IntCtrl(self, size=(50, -1), min=0, max=999999) hb.Add(self.cmdPort, flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, label='Backup File:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.backupFile = wx.StaticText(self, label='') gbs.Add(self.backupFile, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # CrossMgr configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) gbs.Add(setFont(bigFont, wx.StaticText(self, label='CrossMgr Configuration:')), pos=(0, 0), span=(1, 2), flag=wx.ALIGN_LEFT) gbs.Add(wx.StaticText(self, label='CrossMgr Address:'), pos=(1, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.crossMgrHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.crossMgrHost, flag=wx.ALIGN_LEFT) hb.Add(wx.StaticText(self, label=' : 53135'), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(1, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # Add messages # self.alienMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.alienMessagesText, flag=wx.EXPAND, proportion=2) self.alienMessages = MessageManager(self.alienMessagesText) self.crossMgrMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.crossMgrMessagesText, flag=wx.EXPAND, proportion=2) self.crossMgrMessages = MessageManager(self.crossMgrMessagesText) self.fgs = fgs #------------------------------------------------------------------------------------------------ # Create a timer to update the messages. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.updateMessages) self.timer.Start(1000, False) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizer(self.vbs) self.start()
class MainWin(wx.Frame): def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) self.config = wx.Config( appName="CrossMgrAlien", vendorName="SmartCyclingSolutions", #style=wx.CONFIG_USE_LOCAL_FILE ) self.SetBackgroundColour(wx.Colour(232, 232, 232)) self.LightGreen = wx.Colour(153, 255, 153) self.LightRed = wx.Colour(255, 153, 153) font = self.GetFont() bigFont = wx.Font(font.GetPointSize() * 1.5, font.GetFamily(), font.GetStyle(), wx.FONTWEIGHT_BOLD) titleFont = wx.Font(bigFont.GetPointSize() * 2.2, bigFont.GetFamily(), bigFont.GetStyle(), bigFont.GetWeight()) self.vbs = wx.BoxSizer(wx.VERTICAL) bs = wx.BoxSizer(wx.HORIZONTAL) self.reset = RoundButton(self, wx.ID_ANY, 'Reset', size=(80, 80)) self.reset.SetBackgroundColour(wx.WHITE) self.reset.SetForegroundColour(wx.Colour(0, 128, 128)) self.reset.SetFontToFitLabel( ) # Use the button's default font, but change the font size to fit the label. self.reset.Bind(wx.EVT_BUTTON, self.doReset) self.reset.Refresh() bs.Add(self.reset, border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(titleFont, wx.StaticText(self, wx.ID_ANY, 'CrossMgrAlien')), border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.AddStretchSpacer() bitmap = wx.Bitmap(clipboard_xpm) self.copyToClipboard = wx.BitmapButton(self, wx.ID_ANY, bitmap) self.copyToClipboard.SetToolTip( wx.ToolTip('Copy Configuration and Logs to Clipboard...')) self.copyToClipboard.Bind(wx.EVT_BUTTON, self.doCopyToClipboard) bs.Add(self.copyToClipboard, border=32, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.tStart = datetime.datetime.now() bs.Add(setFont( bigFont, wx.StaticText( self, wx.ID_ANY, 'Last Reset: {}'.format(self.tStart.strftime('%H:%M:%S')))), border=10, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.runningTime = setFont(bigFont, wx.StaticText(self, wx.ID_ANY, '00:00:00')) bs.Add(self.runningTime, border=20, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(bigFont, wx.StaticText(self, wx.ID_ANY, ' / ')), flag=wx.ALIGN_CENTER_VERTICAL) self.time = setFont(bigFont, wx.StaticText(self, wx.ID_ANY, '00:00:00')) bs.Add(self.time, flag=wx.ALIGN_CENTER_VERTICAL) self.vbs.Add(bs, flag=wx.ALL | wx.EXPAND, border=4) fgs = wx.FlexGridSizer(rows=0, cols=2, vgap=4, hgap=4) fgs.AddGrowableRow(1) fgs.AddGrowableCol(0) fgs.AddGrowableCol(1) fgs.SetFlexibleDirection(wx.BOTH) self.vbs.Add(fgs, flag=wx.EXPAND, proportion=5) #------------------------------------------------------------------------------------------------ # Alien configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) iRow = 0 hs = wx.BoxSizer(wx.HORIZONTAL) hs.Add(setFont(bigFont, wx.StaticText(self, wx.ID_ANY, 'Alien Configuration:')), flag=wx.ALIGN_CENTER_VERTICAL) self.autoDetectButton = wx.Button(self, wx.ID_ANY, 'Auto Detect') self.autoDetectButton.Bind(wx.EVT_BUTTON, self.doAutoDetect) hs.Add(self.autoDetectButton, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=6) self.advancedButton = wx.Button(self, wx.ID_ANY, 'Advanced...') self.advancedButton.Bind(wx.EVT_BUTTON, self.doAdvanced) hs.Add(self.advancedButton, flag=wx.LEFT, border=6) gbs.Add(hs, pos=(iRow, 0), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, wx.ID_ANY, 'Antennas:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) gs = wx.GridSizer(rows=0, cols=4, hgap=2, vgap=2) self.antennas = [] for i in range(4): gs.Add(wx.StaticText(self, wx.ID_ANY, '{}'.format(i)), flag=wx.ALIGN_CENTER) for i in range(4): cb = wx.CheckBox(self, wx.ID_ANY, '') if i < 2: cb.SetValue(True) cb.Bind(wx.EVT_CHECKBOX, lambda x: self.getAntennaStr()) gs.Add(cb, flag=wx.ALIGN_CENTER) self.antennas.append(cb) gbs.Add(gs, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) iRow += 1 gbs.Add(wx.StaticText(self, wx.ID_ANY, 'Notify Address:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) ips = Utils.GetAllIps() self.notifyHost = wx.Choice(self, choices=ips) hb.Add(self.notifyHost) hb.Add(wx.StaticText(self, label=' : {}'.format(NotifyPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1)) iRow += 1 self.listenForHeartbeat = wx.CheckBox( self, label='Listen for Alien Heartbeat on Port: {}'.format( HeartbeatPort), style=wx.ALIGN_LEFT) self.listenForHeartbeat.SetValue(True) gbs.Add(self.listenForHeartbeat, pos=(iRow, 0), span=(1, 2)) iRow += 1 gbs.Add(wx.StaticText(self, label='Alien Cmd Address:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.cmdHost = IpAddrCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_TAB) hb.Add(self.cmdHost) hb.Add(wx.StaticText(self, label=' : '), flag=wx.ALIGN_CENTER_VERTICAL) self.cmdPort = intctrl.IntCtrl(self, size=(50, -1), min=0, max=999999) hb.Add(self.cmdPort, flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, label='Backup File:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.backupFile = wx.StaticText(self, label='') gbs.Add(self.backupFile, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # CrossMgr configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) gbs.Add(setFont(bigFont, wx.StaticText(self, label='CrossMgr Configuration:')), pos=(0, 0), span=(1, 2), flag=wx.ALIGN_LEFT) gbs.Add(wx.StaticText(self, label='CrossMgr Address:'), pos=(1, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.crossMgrHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.crossMgrHost, flag=wx.ALIGN_LEFT) hb.Add(wx.StaticText(self, label=' : 53135'), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(1, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # Add messages # self.alienMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.alienMessagesText, flag=wx.EXPAND, proportion=2) self.alienMessages = MessageManager(self.alienMessagesText) self.crossMgrMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.crossMgrMessagesText, flag=wx.EXPAND, proportion=2) self.crossMgrMessages = MessageManager(self.crossMgrMessagesText) self.fgs = fgs #------------------------------------------------------------------------------------------------ # Create a timer to update the messages. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.updateMessages) self.timer.Start(1000, False) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizer(self.vbs) self.start() def start(self): self.dataQ = Queue() self.messageQ = Queue() self.shutdownQ = Queue( ) # Queue to tell the Alien monitor to shut down. self.alienProcess = Process( name='AlienProcess', target=AlienServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getNotifyHost(), NotifyPort, HeartbeatPort, self.getAntennaStr(), self.listenForHeartbeat.GetValue(), self.cmdHost.GetAddress(), self.cmdPort.GetValue())) self.alienProcess.daemon = True self.crossMgrProcess = Process(name='CrossMgrProcess', target=CrossMgrServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getCrossMgrHost(), CrossMgrPort)) self.crossMgrProcess.daemon = True self.alienProcess.start() self.crossMgrProcess.start() def doAdvanced(self, event): dlg = AdvancedSetup(self) dlg.ShowModal() dlg.Destroy() self.writeOptions() def shutdown(self): self.alienProcess = None self.crossMgrProcess = None self.messageQ = None self.dataQ = None self.shutdownQ = None def gracefulShutdown(self): # Shutdown the CrossMgr process by sending it a shutdown command. if self.shutdownQ: self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') if self.dataQ: self.dataQ.put('shutdown') if self.crossMgrProcess: self.crossMgrProcess.join() if self.alienProcess: self.alienProcess.join() self.crossMgrProcess = None self.alienProcess = None def doReset(self, event, confirm=True): if confirm: dlg = wx.MessageDialog(self, 'Reset CrossMgrAlien Adapter?', 'Confirm Reset', wx.OK | wx.CANCEL | wx.ICON_WARNING) ret = dlg.ShowModal() dlg.Destroy() if ret != wx.ID_OK: return self.reset.Enable( False) # Prevent multiple clicks while shutting down. self.writeOptions() self.gracefulShutdown() self.alienMessages.clear() self.crossMgrMessages.clear() self.shutdown() self.reset.Enable(True) wx.CallAfter(self.start) def doAutoDetect(self, event): wx.BeginBusyCursor() self.gracefulShutdown() self.shutdown() alienHost, crossmgrHost = AutoDetect() wx.EndBusyCursor() if alienHost and crossmgrHost: self.setNotifyHost( crossmgrHost ) # Assumes CrossMgr is on the same computer as CrossMgrAlien. self.cmdHost.SetValue(alienHost) self.cmdPort.SetValue(str(DefaultAlienCmdPort)) self.crossMgrHost.SetValue(crossmgrHost) self.listenForHeartbeat.SetValue(False) else: dlg = wx.MessageDialog( self, 'Auto Detect Failed.\nCheck that reader has power and is connected to the router.', 'Auto Detect Failed', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() self.doReset(event, False) def onCloseWindow(self, event): wx.Exit() def doCopyToClipboard(self, event): cc = [ 'Configuration: CrossMgrAlien', ' NotifyHost: {}'.format(self.getNotifyHost()), ' NotifyPort: {}'.format(NotifyPort), ' RunningTime: {}'.format(self.runningTime.GetLabel()), ' Time: {}'.format(self.time.GetLabel()), ' BackupFile: {}'.format(self.backupFile.GetLabel()), '', 'Configuration: Alien:', ' ListenForAlienHeartbeat: {}'.format( 'True' if self.listenForHeartbeat.GetValue() else 'False'), ' Antennas: {}'.format(self.getAntennaStr()), ' HeartbeatPort: {}'.format(HeartbeatPort), ' AlienCmdHost: {}'.format(self.cmdHost.GetAddress()), ' AlienCmdPort: {}'.format(self.cmdPort.GetValue()), '', ' RepeatSeconds: {}'.format(Alien.RepeatSeconds), '', 'Configuration: CrossMgr', ' CrossMgrHost: {}'.format(self.getCrossMgrHost()), ' CrossMgrPort: {}'.format(CrossMgrPort), ] cc.append('\nLog: Alien') log = self.alienMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: CrossMgr') log = self.crossMgrMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: Application\n') try: with open(redirectFileName, 'r') as fp: for line in fp: cc.append(line) except: pass if wx.TheClipboard.Open(): do = wx.TextDataObject() do.SetText('\n'.join(cc)) wx.TheClipboard.SetData(do) wx.TheClipboard.Close() dlg = wx.MessageDialog( self, 'Configuration and Logs copied to the Clipboard.', 'Copy to Clipboard Succeeded', wx.OK | wx.ICON_INFORMATION) ret = dlg.ShowModal() dlg.Destroy() else: # oops... something went wrong! wx.MessageBox("Unable to open the clipboard", "Error") def getNotifyHost(self): s = self.notifyHost.GetSelection() return self.notifyHost.GetString(s) if s != wx.NOT_FOUND else None def setNotifyHost(self, notifyHost): for i, s in enumerate(self.notifyHost.GetItems()): if s == notifyHost: self.notifyHost.SetSelection(i) return self.notifyHost.SetSelection(0) def getCrossMgrHost(self): return self.crossMgrHost.GetAddress() def getAntennaStr(self): s = [] for i in range(4): if self.antennas[i].GetValue(): s.append('%d' % i) if not s: # Ensure that at least one antenna is selected. self.antennas[0].SetValue(True) s.append('0') return ' '.join(s) def setAntennaStr(self, s): antennas = set(int(a) for a in s.split()) for i in range(4): self.antennas[i].SetValue(i in antennas) def writeOptions(self): self.config.Write('CrossMgrHost', self.getCrossMgrHost()) self.config.Write( 'ListenForAlienHeartbeat', 'True' if self.listenForHeartbeat.GetValue() else 'False') self.config.Write('AlienCmdAddr', self.cmdHost.GetAddress()) self.config.Write('AlienCmdPort', str(self.cmdPort.GetValue())) self.config.Write('Antennas', self.getAntennaStr()) self.config.Write('RepeatSeconds', '{}'.format(Alien.RepeatSeconds)) self.config.Write('PlaySounds', '{}'.format(Utils.playBell)) s = self.notifyHost.GetSelection() if s != wx.NOT_FOUND: self.config.Write('NotifyHost', self.notifyHost.GetString(s)) self.config.Flush() def readOptions(self): self.crossMgrHost.SetValue( self.config.Read('CrossMgrHost', Utils.DEFAULT_HOST)) self.listenForHeartbeat.SetValue( self.config.Read('ListenForAlienHeartbeat', 'True').upper()[:1] == 'T') Utils.playBell = (self.config.Read('PlaySounds', 'True').upper()[:1] == 'T') self.cmdHost.SetValue(self.config.Read('AlienCmdAddr', '0.0.0.0')) self.cmdPort.SetValue(int(self.config.Read('AlienCmdPort', '0'))) self.setAntennaStr(self.config.Read('Antennas', '0 1')) Alien.RepeatSeconds = int( self.config.Read('RepeatSeconds', '{}'.format(Alien.RepeatSeconds))) notifyHost = self.config.Read('NotifyHost', Utils.DEFAULT_HOST) self.setNotifyHost(notifyHost) def updateMessages(self, event): tNow = datetime.datetime.now() running = int((tNow - self.tStart).total_seconds()) self.runningTime.SetLabel('{:02d}:{:02d}:{:02d}'.format( running // (60 * 60), (running // 60) % 60, running % 60)) self.time.SetLabel(tNow.strftime('%H:%M:%S')) if not self.messageQ: return while 1: try: d = self.messageQ.get(False) except Empty: break message = ' '.join(str(x) for x in d[1:]) if d[0] == 'Alien': if 'state' in d: self.alienMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.alienMessages.write(message) elif d[0] == 'Alien2JChip': if 'state' in d: self.crossMgrMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.crossMgrMessages.write(message) elif d[0] == 'CmdHost': cmdHost, cmdPort = d[1].split(':') self.cmdHost.SetValue(cmdHost) self.cmdPort.SetValue(int(cmdPort)) elif d[0] == 'BackupFile': self.backupFile.SetLabel(d[1])
def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) self.SetBackgroundColour(wx.Colour(240, 240, 240)) self.fname = None self.updated = False self.firstTime = True self.lastUpdateTime = None self.sources = [] self.errors = [] self.filehistory = wx.FileHistory(16) dataDir = Utils.getHomeDir() configFileName = os.path.join(dataDir, 'CallupSeedingMgr.cfg') self.config = wx.Config(appName="CallupSeedingMgr", vendorName="SmartCyclingSolutions", localFilename=configFileName) self.filehistory.Load(self.config) ID_MENU_UPDATE = wx.NewIdRef() ID_MENU_HELP = wx.NewIdRef() self.menuBar = wx.MenuBar(wx.MB_DOCKABLE) if 'WXMAC' in wx.Platform: self.appleMenu = self.menuBar.OSXGetAppleMenu() self.appleMenu.SetTitle("CallupSeedingMgr") self.appleMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.editMenu = wx.Menu() self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_UPDATE, "&Update")) self.Bind(wx.EVT_MENU, self.doUpdate, id=ID_MENU_UPDATE) self.menuBar.Append(self.editMenu, "&Edit") self.helpMenu = wx.Menu() self.helpMenu.Append( wx.MenuItem(self.helpMenu, ID_MENU_HELP, "&Help")) self.menuBar.Append(self.helpMenu, "&Help") self.Bind(wx.EVT_MENU, self.onTutorial, id=ID_MENU_HELP) else: self.fileMenu = wx.Menu() self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_UPDATE, "&Update")) self.fileMenu.Append(wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.doUpdate, id=ID_MENU_UPDATE) self.Bind(wx.EVT_MENU, self.onClose, id=wx.ID_EXIT) self.menuBar.Append(self.fileMenu, "&File") self.helpMenu = wx.Menu() self.helpMenu.Insert(0, wx.ID_ABOUT, "&About") self.helpMenu.Insert(1, ID_MENU_HELP, "&Help") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.Bind(wx.EVT_MENU, self.onTutorial, id=ID_MENU_HELP) self.menuBar.Append(self.helpMenu, "&Help") self.SetMenuBar(self.menuBar) inputBox = wx.StaticBox(self, label=_('Input')) inputBoxSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL) self.fileBrowse = filebrowse.FileBrowseButtonWithHistory( self, labelText=_('Excel File'), buttonText=('Browse...'), startDirectory=os.path.expanduser('~'), fileMask= 'Excel Spreadsheet (*.xlsx; *.xlsm; *.xls)|*.xlsx; *.xlsml; *.xls', size=(400, -1), history=lambda: [ self.filehistory.GetHistoryFile(i) for i in range(self.filehistory.GetCount()) ], changeCallback=self.doChangeCallback, ) inputBoxSizer.Add(self.fileBrowse, 0, flag=wx.EXPAND | wx.ALL, border=4) horizontalControlSizer = wx.BoxSizer(wx.HORIZONTAL) #------------------------------------------------------------------------------------------- verticalControlSizer = wx.BoxSizer(wx.VERTICAL) self.useUciIdCB = wx.CheckBox(self, label=_("Use UCI ID (assume no errors)")) self.useUciIdCB.SetValue(True) verticalControlSizer.Add(self.useUciIdCB, flag=wx.ALL, border=4) self.useLicenseCB = wx.CheckBox( self, label=_("Use License (assume no errors)")) self.useLicenseCB.SetValue(True) verticalControlSizer.Add(self.useLicenseCB, flag=wx.ALL, border=4) self.soundalikeCB = wx.CheckBox( self, label=_("Match misspelled names with Sound-Alike")) self.soundalikeCB.SetValue(True) verticalControlSizer.Add(self.soundalikeCB, flag=wx.ALL, border=4) self.callupSeedingRB = wx.RadioBox( self, style=wx.RA_SPECIFY_COLS, majorDimension=1, label=_("Sequence"), choices=[ _("Callups: Highest ranked FIRST (Cyclo-cross, MTB)"), _("Seeding: Highest ranked LAST (Time Trials)"), ], ) verticalControlSizer.Add(self.callupSeedingRB, flag=wx.EXPAND | wx.ALL, border=4) verticalControlSizer.Add(wx.StaticText( self, label=_('Riders with no criteria will be sequenced randomly.')), flag=wx.ALL, border=4) horizontalControlSizer.Add(verticalControlSizer, flag=wx.EXPAND) self.updateButton = RoundButton(self, size=(96, 96)) self.updateButton.SetLabel(_('Update')) self.updateButton.SetFontToFitLabel() self.updateButton.SetForegroundColour(wx.Colour(0, 100, 0)) self.updateButton.Bind(wx.EVT_BUTTON, self.doUpdate) horizontalControlSizer.Add(self.updateButton, flag=wx.ALL, border=4) horizontalControlSizer.AddSpacer(48) vs = wx.BoxSizer(wx.VERTICAL) self.tutorialButton = wx.Button(self, label=_('Help/Tutorial...')) self.tutorialButton.Bind(wx.EVT_BUTTON, self.onTutorial) vs.Add(self.tutorialButton, flag=wx.ALL, border=4) branding = wx.adv.HyperlinkCtrl( self, id=wx.ID_ANY, label=u"Powered by CrossMgr", url=u"http://www.sites.google.com/site/crossmgrsoftware/") vs.Add(branding, flag=wx.ALL, border=4) horizontalControlSizer.Add(vs) inputBoxSizer.Add(horizontalControlSizer, flag=wx.EXPAND) self.sourceList = wx.ListCtrl(self, style=wx.LC_REPORT, size=(-1, 100)) inputBoxSizer.Add(self.sourceList, flag=wx.ALL | wx.EXPAND, border=4) self.sourceList.InsertColumn(0, "Sheet") self.sourceList.InsertColumn(1, "Data Columns and Derived Information") self.sourceList.InsertColumn(2, "Key Fields") self.sourceList.InsertColumn(3, "Rows", wx.LIST_FORMAT_RIGHT) self.sourceList.InsertColumn(4, "Errors/Warnings", wx.LIST_FORMAT_RIGHT) self.sourceList.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelected) instructions = [ _('Drag-and-Drop the row numbers on the Left to change the sequence.' ), _('Click on Points or Position cells for details.'), _('Orange Cells: Multiple Matches. Click on the cell to see what you need to fix in the spreadsheet.' ), _('Yellow Cells: Soundalike Matches. Click on the cell to validate if the names are matched correctly.' ), ] self.grid = ReorderableGrid(self) self.grid.CreateGrid(0, 1) self.grid.SetColLabelValue(0, u'') self.grid.EnableDragRowSize(False) self.grid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.onGridCellClick) #self.grid.Bind( wx.EVT_MOTION, self.onMouseOver ) outputBox = wx.StaticBox(self, label=_('Output')) outputBoxSizer = wx.StaticBoxSizer(outputBox, wx.VERTICAL) hs = wx.BoxSizer(wx.HORIZONTAL) self.excludeUnrankedCB = wx.CheckBox( self, label=_("Exclude riders with no ranking info")) hs.Add(self.excludeUnrankedCB, flag=wx.ALL | wx.ALIGN_CENTRE_VERTICAL, border=4) hs.AddSpacer(24) hs.Add(wx.StaticText(self, label=_("Output:")), flag=wx.ALL | wx.ALIGN_CENTRE_VERTICAL, border=4) self.topRiders = wx.Choice(self, choices=[ _('All Riders'), _('Top 5'), _('Top 10'), _('Top 15'), _('Top 20'), _('Top 25') ]) self.topRiders.SetSelection(0) hs.Add(self.topRiders, flag=wx.ALIGN_CENTRE_VERTICAL) self.saveAsExcel = wx.Button(self, label=_('Save as Excel...')) self.saveAsExcel.Bind(wx.EVT_BUTTON, self.doSaveAsExcel) hs.AddSpacer(48) hs.Add(self.saveAsExcel, flag=wx.ALL, border=4) outputBoxSizer.Add(hs) mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(inputBoxSizer, flag=wx.EXPAND | wx.ALL, border=4) for i, instruction in enumerate(instructions): flag = wx.LEFT | wx.RIGHT if i == len(instructions) - 1: flag |= wx.BOTTOM mainSizer.Add(wx.StaticText(self, label=instruction), flag=flag, border=8) mainSizer.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=4) mainSizer.Add(outputBoxSizer, flag=wx.EXPAND | wx.ALL, border=4) self.SetSizer(mainSizer)
class MainWin(wx.Frame): def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) self.SetBackgroundColour(wx.Colour(240, 240, 240)) self.fname = None self.updated = False self.firstTime = True self.lastUpdateTime = None self.sources = [] self.errors = [] self.filehistory = wx.FileHistory(16) dataDir = Utils.getHomeDir() configFileName = os.path.join(dataDir, 'CallupSeedingMgr.cfg') self.config = wx.Config(appName="CallupSeedingMgr", vendorName="SmartCyclingSolutions", localFilename=configFileName) self.filehistory.Load(self.config) ID_MENU_UPDATE = wx.NewIdRef() ID_MENU_HELP = wx.NewIdRef() self.menuBar = wx.MenuBar(wx.MB_DOCKABLE) if 'WXMAC' in wx.Platform: self.appleMenu = self.menuBar.OSXGetAppleMenu() self.appleMenu.SetTitle("CallupSeedingMgr") self.appleMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.editMenu = wx.Menu() self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_UPDATE, "&Update")) self.Bind(wx.EVT_MENU, self.doUpdate, id=ID_MENU_UPDATE) self.menuBar.Append(self.editMenu, "&Edit") self.helpMenu = wx.Menu() self.helpMenu.Append( wx.MenuItem(self.helpMenu, ID_MENU_HELP, "&Help")) self.menuBar.Append(self.helpMenu, "&Help") self.Bind(wx.EVT_MENU, self.onTutorial, id=ID_MENU_HELP) else: self.fileMenu = wx.Menu() self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_UPDATE, "&Update")) self.fileMenu.Append(wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.doUpdate, id=ID_MENU_UPDATE) self.Bind(wx.EVT_MENU, self.onClose, id=wx.ID_EXIT) self.menuBar.Append(self.fileMenu, "&File") self.helpMenu = wx.Menu() self.helpMenu.Insert(0, wx.ID_ABOUT, "&About") self.helpMenu.Insert(1, ID_MENU_HELP, "&Help") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.Bind(wx.EVT_MENU, self.onTutorial, id=ID_MENU_HELP) self.menuBar.Append(self.helpMenu, "&Help") self.SetMenuBar(self.menuBar) inputBox = wx.StaticBox(self, label=_('Input')) inputBoxSizer = wx.StaticBoxSizer(inputBox, wx.VERTICAL) self.fileBrowse = filebrowse.FileBrowseButtonWithHistory( self, labelText=_('Excel File'), buttonText=('Browse...'), startDirectory=os.path.expanduser('~'), fileMask= 'Excel Spreadsheet (*.xlsx; *.xlsm; *.xls)|*.xlsx; *.xlsml; *.xls', size=(400, -1), history=lambda: [ self.filehistory.GetHistoryFile(i) for i in range(self.filehistory.GetCount()) ], changeCallback=self.doChangeCallback, ) inputBoxSizer.Add(self.fileBrowse, 0, flag=wx.EXPAND | wx.ALL, border=4) horizontalControlSizer = wx.BoxSizer(wx.HORIZONTAL) #------------------------------------------------------------------------------------------- verticalControlSizer = wx.BoxSizer(wx.VERTICAL) self.useUciIdCB = wx.CheckBox(self, label=_("Use UCI ID (assume no errors)")) self.useUciIdCB.SetValue(True) verticalControlSizer.Add(self.useUciIdCB, flag=wx.ALL, border=4) self.useLicenseCB = wx.CheckBox( self, label=_("Use License (assume no errors)")) self.useLicenseCB.SetValue(True) verticalControlSizer.Add(self.useLicenseCB, flag=wx.ALL, border=4) self.soundalikeCB = wx.CheckBox( self, label=_("Match misspelled names with Sound-Alike")) self.soundalikeCB.SetValue(True) verticalControlSizer.Add(self.soundalikeCB, flag=wx.ALL, border=4) self.callupSeedingRB = wx.RadioBox( self, style=wx.RA_SPECIFY_COLS, majorDimension=1, label=_("Sequence"), choices=[ _("Callups: Highest ranked FIRST (Cyclo-cross, MTB)"), _("Seeding: Highest ranked LAST (Time Trials)"), ], ) verticalControlSizer.Add(self.callupSeedingRB, flag=wx.EXPAND | wx.ALL, border=4) verticalControlSizer.Add(wx.StaticText( self, label=_('Riders with no criteria will be sequenced randomly.')), flag=wx.ALL, border=4) horizontalControlSizer.Add(verticalControlSizer, flag=wx.EXPAND) self.updateButton = RoundButton(self, size=(96, 96)) self.updateButton.SetLabel(_('Update')) self.updateButton.SetFontToFitLabel() self.updateButton.SetForegroundColour(wx.Colour(0, 100, 0)) self.updateButton.Bind(wx.EVT_BUTTON, self.doUpdate) horizontalControlSizer.Add(self.updateButton, flag=wx.ALL, border=4) horizontalControlSizer.AddSpacer(48) vs = wx.BoxSizer(wx.VERTICAL) self.tutorialButton = wx.Button(self, label=_('Help/Tutorial...')) self.tutorialButton.Bind(wx.EVT_BUTTON, self.onTutorial) vs.Add(self.tutorialButton, flag=wx.ALL, border=4) branding = wx.adv.HyperlinkCtrl( self, id=wx.ID_ANY, label=u"Powered by CrossMgr", url=u"http://www.sites.google.com/site/crossmgrsoftware/") vs.Add(branding, flag=wx.ALL, border=4) horizontalControlSizer.Add(vs) inputBoxSizer.Add(horizontalControlSizer, flag=wx.EXPAND) self.sourceList = wx.ListCtrl(self, style=wx.LC_REPORT, size=(-1, 100)) inputBoxSizer.Add(self.sourceList, flag=wx.ALL | wx.EXPAND, border=4) self.sourceList.InsertColumn(0, "Sheet") self.sourceList.InsertColumn(1, "Data Columns and Derived Information") self.sourceList.InsertColumn(2, "Key Fields") self.sourceList.InsertColumn(3, "Rows", wx.LIST_FORMAT_RIGHT) self.sourceList.InsertColumn(4, "Errors/Warnings", wx.LIST_FORMAT_RIGHT) self.sourceList.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelected) instructions = [ _('Drag-and-Drop the row numbers on the Left to change the sequence.' ), _('Click on Points or Position cells for details.'), _('Orange Cells: Multiple Matches. Click on the cell to see what you need to fix in the spreadsheet.' ), _('Yellow Cells: Soundalike Matches. Click on the cell to validate if the names are matched correctly.' ), ] self.grid = ReorderableGrid(self) self.grid.CreateGrid(0, 1) self.grid.SetColLabelValue(0, u'') self.grid.EnableDragRowSize(False) self.grid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.onGridCellClick) #self.grid.Bind( wx.EVT_MOTION, self.onMouseOver ) outputBox = wx.StaticBox(self, label=_('Output')) outputBoxSizer = wx.StaticBoxSizer(outputBox, wx.VERTICAL) hs = wx.BoxSizer(wx.HORIZONTAL) self.excludeUnrankedCB = wx.CheckBox( self, label=_("Exclude riders with no ranking info")) hs.Add(self.excludeUnrankedCB, flag=wx.ALL | wx.ALIGN_CENTRE_VERTICAL, border=4) hs.AddSpacer(24) hs.Add(wx.StaticText(self, label=_("Output:")), flag=wx.ALL | wx.ALIGN_CENTRE_VERTICAL, border=4) self.topRiders = wx.Choice(self, choices=[ _('All Riders'), _('Top 5'), _('Top 10'), _('Top 15'), _('Top 20'), _('Top 25') ]) self.topRiders.SetSelection(0) hs.Add(self.topRiders, flag=wx.ALIGN_CENTRE_VERTICAL) self.saveAsExcel = wx.Button(self, label=_('Save as Excel...')) self.saveAsExcel.Bind(wx.EVT_BUTTON, self.doSaveAsExcel) hs.AddSpacer(48) hs.Add(self.saveAsExcel, flag=wx.ALL, border=4) outputBoxSizer.Add(hs) mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(inputBoxSizer, flag=wx.EXPAND | wx.ALL, border=4) for i, instruction in enumerate(instructions): flag = wx.LEFT | wx.RIGHT if i == len(instructions) - 1: flag |= wx.BOTTOM mainSizer.Add(wx.StaticText(self, label=instruction), flag=flag, border=8) mainSizer.Add(self.grid, 1, flag=wx.EXPAND | wx.ALL, border=4) mainSizer.Add(outputBoxSizer, flag=wx.EXPAND | wx.ALL, border=4) self.SetSizer(mainSizer) def onClose(self, event): wx.Exit() def OnAboutBox(self, e): description = """CallupSeedingMgr is an Seeding Manager for CrossMgr """ licence = """CallupSeedingMgr free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. CallupSeedingMgr is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with File Hunter; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA""" info = wx.adv.AboutDialogInfo() crossMgrPng = Utils.getImageFolder() + '/CallupSeedingMgr.png' info.SetIcon(wx.Icon(crossMgrPng, wx.BITMAP_TYPE_PNG)) info.SetName('CallupSeedingMgr') info.SetVersion(AppVerName.split(' ')[1]) info.SetDescription(description) info.SetCopyright('(C) 2020 Edward Sitarski') info.SetWebSite('http://www.sites.google.com/site/crossmgrsoftware/') info.SetLicence(licence) wx.adv.AboutBox(info, self) def onTutorial(self, event): if not Utils.MessageOKCancel( self, u"\n".join([ _("Launch the CallupSeedingMgr Tutorial."), _("This open a sample Excel input file created into your home folder." ), _("This data in this sheet is made-up, although it does include some current rider's names." ), u"", _("It will also open the Tutorial page in your browser. If you can't see your browser, make sure you bring to the front." ), u"", _("Continue?"), ])): return try: fname_excel = MakeExampleExcel() self.fileBrowse.SetValue(fname_excel) except Exception as e: Utils.MessageOK( self, u'{}\n\n{}\n\n{}'.format( u'Problem creating Excel sheet.', e, _('If the Excel file is open, please close it and try again' ))) self.doUpdate(event) Utils.LaunchApplication([ fname_excel, os.path.join(Utils.getHtmlDocFolder(), 'Tutorial.html') ]) def onItemSelected(self, event): currentItem = event.GetIndex() errors = self.errors[(currentItem + len(self.errors) - 1) % len(self.errors)] if not errors: return dialog = ErrorDialog(self, errors=errors) dialog.ShowModal() dialog.Destroy() event.Skip() def onMouseOver(self, event): ''' Method to calculate where the mouse is pointing and then set the tooltip dynamically. ''' # Use CalcUnscrolledPosition() to get the mouse position # within the # entire grid including what's offscreen x, y = self.grid_area.CalcUnscrolledPosition(event.GetX(), event.GetY()) coords = self.grid_area.XYToCell(x, y) # you only need these if you need the value in the cell row = coords[0] col = coords[1] iRecord = int( self.grid.GetCellValue(row, self.grid.GetNumberCols() - 1)) iSource = self.grid.GetNumberCols() - col try: v = self.callup_results[iRecord][-iSource + 1] except IndexError: event.Skip() return try: status = v.get_status() except AttributeError: event.Skip() return if status == v.NoMatch: event.Skip() return message = u'{}\n\n{}'.format( v.get_message(), _('Make changes in the Spreadsheet (if necessary), then press "Update" to refresh the screen.' ), ) event.GetEventObject().SetToolTipString(message) event.Skip() def onGridCellClick(self, event): row = event.GetRow() col = event.GetCol() iRecord = int( self.grid.GetCellValue(row, self.grid.GetNumberCols() - 1)) iSource = self.grid.GetNumberCols() - col try: v = self.callup_results[iRecord][-iSource + 1] except IndexError: return try: status = v.get_status() except AttributeError: return if status == v.NoMatch: return message = u'{}\n\n{}'.format( v.get_message(), _('Make changes in the Spreadsheet (if necessary), then press "Update" to refresh the screen.' ), ) Utils.MessageOK( self, message, _('Soundalike Match') if status == v.SuccessSoundalike else _('Multiple Matches') if status == v.MultiMatch else _('Match Success')) def getTopRiders(self): i = self.topRiders.GetSelection() return 5 * i if i > 0 else 999999 def getIsCallup(self): return self.callupSeedingRB.GetSelection() == 0 def getIsSoundalike(self): return self.soundalikeCB.GetValue() def getUseUciId(self): return self.useUciIdCB.GetValue() def getUseLicense(self): return self.useLicenseCB.GetValue() def getOutputExcelName(self): fname = os.path.abspath(self.fname) dirname, basename = os.path.dirname(fname), os.path.basename(fname) fname_base, fname_suffix = os.path.splitext(basename) dirchild = 'CallupsOutput' if self.getIsCallup() else 'SeedingOutput' try: os.makedirs(os.path.join(dirname, dirchild)) except Exception as e: pass fname_excel = os.path.join( dirname, dirchild, '{}{}{}'.format(fname_base, '_Callups' if self.getIsCallup() else '_Seeding', '.xlsx')) return fname_excel def doChangeCallback(self, event): fname = event.GetString() if not fname: self.setUpdated(False) return if fname != self.fname: wx.CallAfter(self.doUpdate, fnameNew=fname) def setUpdated(self, updated=True): self.updated = updated for w in [self.sourceList, self.grid, self.saveAsExcel]: w.Enable(updated) if not updated: self.sourceList.DeleteAllItems() Utils.DeleteAllGridRows(self.grid) def updateSourceList(self, sources=None, errors=None): self.sourceList.DeleteAllItems() sources = (sources or self.sources) errors = (errors or self.errors) if not sources: return def insert_source_info(source, errors, add_value_field=True): idx = self.sourceList.InsertItem(999999, source.sheet_name) fields = source.get_ordered_fields() if add_value_field and source.get_cmp_policy_field(): fields = [source.get_cmp_policy_field()] + list(fields) self.sourceList.SetItem(idx, 1, u', '.join(make_title(f) for f in fields)) match_fields = source.get_match_fields( sources[-1]) if source != sources[-1] else [] self.sourceList.SetItem( idx, 2, u', '.join(make_title(f) for f in match_fields)) self.sourceList.SetItem(idx, 3, u'{}'.format(len(source.results))) self.sourceList.SetItem(idx, 4, u'{}'.format(len(errors))) insert_source_info(sources[-1], errors[-1], False) for i, source in enumerate(sources[:-1]): insert_source_info(source, errors[i]) for col in range(3): self.sourceList.SetColumnWidth(col, wx.LIST_AUTOSIZE) self.sourceList.SetColumnWidth(3, 52) self.sourceList.Refresh() def callbackUpdate(self, message): pass def doUpdate(self, event=None, fnameNew=None): try: self.fname = (fnameNew or event.GetString() or self.fileBrowse.GetValue()) except: self.fname = u'' if not self.fname: Utils.MessageOK( self, _('Missing Excel file. Please select an Excel file.'), _('Missing Excel File')) self.setUpdated(False) return if self.lastUpdateTime and (datetime.datetime.now() - self.lastUpdateTime).total_seconds() < 1.0: return try: with open(self.fname, 'rb') as f: pass except Exception as e: Utils.MessageOK( self, u'{}:\n\n {}\n\n{}'.format(_('Cannot Open Excel file'), self.fname, e), _('Cannot Open Excel File')) self.setUpdated(False) return self.filehistory.AddFileToHistory(self.fname) self.filehistory.Save(self.config) wait = wx.BusyCursor() labelSave, backgroundColourSave = self.updateButton.GetLabel( ), self.updateButton.GetForegroundColour() try: self.registration_headers, self.callup_headers, self.callup_results, self.sources, self.errors = GetCallups( self.fname, soundalike=self.getIsSoundalike(), useUciId=self.getUseUciId(), useLicense=self.getUseLicense(), callbackfunc=self.updateSourceList, callbackupdate=self.callbackUpdate, ) except Exception as e: traceback.print_exc() Utils.MessageOK( self, u'{}:\n\n {}\n\n{}'.format(_('Excel File Error'), self.fname, e), _('Excel File Error')) self.setUpdated(False) return self.setUpdated(True) self.updateSourceList() CallupResultsToGrid( self.grid, self.registration_headers, self.callup_headers, self.callup_results, is_callup=(self.callupSeedingRB.GetSelection() == 0), top_riders=self.getTopRiders(), exclude_unranked=self.excludeUnrankedCB.GetValue(), ) self.GetSizer().Layout() self.lastUpdateTime = datetime.datetime.now() def doSaveAsExcel(self, event): if self.grid.GetNumberRows() == 0: return fname_excel = self.getOutputExcelName() if os.path.isfile(fname_excel): if not Utils.MessageOKCancel( self, u'"{}"\n\n{}'.format(fname_excel, _('File exists. Replace?')), _('Output Excel File Exists'), ): return user_sequence = [ int(self.grid.GetCellValue(row, self.grid.GetNumberCols() - 1)) for row in range(self.grid.GetNumberRows()) ] user_callup_results = [self.callup_results[i] for i in user_sequence] try: CallupResultsToExcel( fname_excel, self.registration_headers, self.callup_headers, user_callup_results, is_callup=self.getIsCallup(), top_riders=self.getTopRiders(), exclude_unranked=self.excludeUnrankedCB.GetValue(), ) except Exception as e: traceback.print_exc() Utils.MessageOK( self, u'{}: "{}"\n\n{}\n\n"{}"'.format( _("Write Failed"), e, _("If you have this file open, close it and try again."), fname_excel), _("Excel Write Failed."), iconMask=wx.ICON_ERROR, ) return webbrowser.open(fname_excel, new=2, autoraise=True)
class MainWin(wx.Frame): def __init__(self, parent, id=wx.ID_ANY, title='', size=(200, 200)): wx.Frame.__init__(self, parent, id, title, size=size) dataDir = Utils.getHomeDir() configFileName = os.path.join(dataDir, 'CrossMgrAlien.cfg') self.config = wx.Config(appName="CrossMgrAlien", vendorName="SmartCyclingSolutions", localFilename=configFileName) ID_MENU_ADVANCECONFIG = wx.NewIdRef() ID_MENU_COPYLOGS = wx.NewIdRef() ID_MENU_AUTODETECT = wx.NewIdRef() self.menuBar = wx.MenuBar(wx.MB_DOCKABLE) if 'WXMAC' in wx.Platform: self.appleMenu = self.menuBar.OSXGetAppleMenu() self.appleMenu.SetTitle("CrossMgrAlien") self.appleMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.editMenu = wx.Menu() self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_ADVANCECONFIG, "A&dvanced Configuration")) self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_COPYLOGS, "&Copy Logs to Clipboard")) self.editMenu.Append( wx.MenuItem(self.editMenu, ID_MENU_AUTODETECT, "&Autodetect Reader")) self.Bind(wx.EVT_MENU, self.doAdvanced, id=ID_MENU_ADVANCECONFIG) self.Bind(wx.EVT_MENU, self.doCopyToClipboard, id=ID_MENU_COPYLOGS) self.Bind(wx.EVT_MENU, self.doAutoDetect, id=ID_MENU_AUTODETECT) self.menuBar.Append(self.editMenu, "&Edit") else: self.fileMenu = wx.Menu() self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_ADVANCECONFIG, "A&dvanced Configuration")) self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_COPYLOGS, "&Copy Logs to Clipboard")) self.fileMenu.Append( wx.MenuItem(self.fileMenu, ID_MENU_AUTODETECT, "&Autodetect Reader")) self.fileMenu.Append(wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.doAdvanced, id=ID_MENU_ADVANCECONFIG) self.Bind(wx.EVT_MENU, self.doCopyToClipboard, id=ID_MENU_COPYLOGS) self.Bind(wx.EVT_MENU, self.doAutoDetect, id=ID_MENU_AUTODETECT) self.Bind(wx.EVT_MENU, self.onCloseWindow, id=wx.ID_EXIT) self.menuBar.Append(self.fileMenu, "&File") self.helpMenu = wx.Menu() self.helpMenu.Insert(0, wx.ID_ABOUT, "&About") self.Bind(wx.EVT_MENU, self.OnAboutBox, id=wx.ID_ABOUT) self.menuBar.Append(self.helpMenu, "&Help") self.SetMenuBar(self.menuBar) self.SetBackgroundColour(wx.Colour(232, 232, 232)) self.LightGreen = wx.Colour(153, 255, 153) self.LightRed = wx.Colour(255, 153, 153) font = self.GetFont() bigFont = wx.Font(font.GetPointSize() * 1.5, font.GetFamily(), font.GetStyle(), wx.FONTWEIGHT_BOLD) titleFont = wx.Font(bigFont.GetPointSize() * 2.2, bigFont.GetFamily(), bigFont.GetStyle(), bigFont.GetWeight()) self.vbs = wx.BoxSizer(wx.VERTICAL) bs = wx.BoxSizer(wx.HORIZONTAL) self.reset = RoundButton(self, wx.ID_ANY, 'Reset', size=(80, 80)) self.reset.SetBackgroundColour(wx.WHITE) self.reset.SetForegroundColour(wx.Colour(0, 128, 128)) self.reset.SetFontToFitLabel( ) # Use the button's default font, but change the font size to fit the label. self.reset.Bind(wx.EVT_BUTTON, self.doReset) self.reset.Refresh() bs.Add(self.reset, border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(titleFont, wx.StaticText(self, wx.ID_ANY, 'CrossMgrAlien')), border=8, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.AddStretchSpacer() bitmap = wx.Bitmap(clipboard_xpm) self.copyToClipboard = wx.BitmapButton(self, wx.ID_ANY, bitmap) self.copyToClipboard.SetToolTip( wx.ToolTip('Copy Configuration and Logs to Clipboard...')) self.copyToClipboard.Bind(wx.EVT_BUTTON, self.doCopyToClipboard) bs.Add(self.copyToClipboard, border=32, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.tStart = datetime.datetime.now() bs.Add(setFont( bigFont, wx.StaticText( self, wx.ID_ANY, 'Last Reset: {}'.format(self.tStart.strftime('%H:%M:%S')))), border=10, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) self.runningTime = setFont(bigFont, wx.StaticText(self, wx.ID_ANY, '00:00:00')) bs.Add(self.runningTime, border=20, flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) bs.Add(setFont(bigFont, wx.StaticText(self, wx.ID_ANY, ' / ')), flag=wx.ALIGN_CENTER_VERTICAL) self.time = setFont(bigFont, wx.StaticText(self, wx.ID_ANY, '00:00:00')) bs.Add(self.time, flag=wx.ALIGN_CENTER_VERTICAL) self.vbs.Add(bs, flag=wx.ALL | wx.EXPAND, border=4) fgs = wx.FlexGridSizer(rows=0, cols=2, vgap=4, hgap=4) fgs.AddGrowableRow(1) fgs.AddGrowableCol(0) fgs.AddGrowableCol(1) fgs.SetFlexibleDirection(wx.BOTH) self.vbs.Add(fgs, flag=wx.EXPAND, proportion=5) #------------------------------------------------------------------------------------------------ # Alien configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) iRow = 0 hs = wx.BoxSizer(wx.HORIZONTAL) hs.Add(setFont(bigFont, wx.StaticText(self, wx.ID_ANY, 'Alien Configuration:')), flag=wx.ALIGN_CENTER_VERTICAL) self.autoDetectButton = wx.Button(self, wx.ID_ANY, 'Auto Detect') self.autoDetectButton.Bind(wx.EVT_BUTTON, self.doAutoDetect) hs.Add(self.autoDetectButton, flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=6) self.advancedButton = wx.Button(self, wx.ID_ANY, 'Advanced...') self.advancedButton.Bind(wx.EVT_BUTTON, self.doAdvanced) hs.Add(self.advancedButton, flag=wx.LEFT, border=6) gbs.Add(hs, pos=(iRow, 0), span=(1, 2), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, wx.ID_ANY, 'Antennas:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) gs = wx.GridSizer(rows=0, cols=4, hgap=2, vgap=2) self.antennas = [] for i in range(4): gs.Add(wx.StaticText(self, wx.ID_ANY, '{}'.format(i)), flag=wx.ALIGN_CENTER) for i in range(4): cb = wx.CheckBox(self, wx.ID_ANY, '') if i < 2: cb.SetValue(True) cb.Bind(wx.EVT_CHECKBOX, lambda x: self.getAntennaStr()) gs.Add(cb, flag=wx.ALIGN_CENTER) self.antennas.append(cb) gbs.Add(gs, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_CENTER_VERTICAL) iRow += 1 gbs.Add(wx.StaticText(self, wx.ID_ANY, 'Notify Address:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) ips = Utils.GetAllIps() self.notifyHost = wx.Choice(self, choices=ips) hb.Add(self.notifyHost) hb.Add(wx.StaticText(self, label=' : {}'.format(NotifyPort)), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1)) iRow += 1 self.listenForHeartbeat = wx.CheckBox( self, label='Listen for Alien Heartbeat on Port: {}'.format( HeartbeatPort), style=wx.ALIGN_LEFT) self.listenForHeartbeat.SetValue(True) gbs.Add(self.listenForHeartbeat, pos=(iRow, 0), span=(1, 2)) iRow += 1 gbs.Add(wx.StaticText(self, label='Alien Cmd Address:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.cmdHost = IpAddrCtrl(self, wx.ID_ANY, style=wx.TE_PROCESS_TAB) hb.Add(self.cmdHost) hb.Add(wx.StaticText(self, label=' : '), flag=wx.ALIGN_CENTER_VERTICAL) self.cmdPort = intctrl.IntCtrl(self, size=(50, -1), min=0, max=999999) hb.Add(self.cmdPort, flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) iRow += 1 gbs.Add(wx.StaticText(self, label='Backup File:'), pos=(iRow, 0), span=(1, 1), flag=wx.ALIGN_RIGHT) self.backupFile = wx.StaticText(self, label='') gbs.Add(self.backupFile, pos=(iRow, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # CrossMgr configuration. # gbs = wx.GridBagSizer(4, 4) fgs.Add(gbs, flag=wx.EXPAND | wx.ALL, border=4) gbs.Add(setFont(bigFont, wx.StaticText(self, label='CrossMgr Configuration:')), pos=(0, 0), span=(1, 2), flag=wx.ALIGN_LEFT) gbs.Add(wx.StaticText(self, label='CrossMgr Address:'), pos=(1, 0), span=(1, 1), flag=wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL) hb = wx.BoxSizer(wx.HORIZONTAL) self.crossMgrHost = IpAddrCtrl(self, style=wx.TE_PROCESS_TAB) hb.Add(self.crossMgrHost, flag=wx.ALIGN_LEFT) hb.Add(wx.StaticText(self, label=' : 53135'), flag=wx.ALIGN_CENTER_VERTICAL) gbs.Add(hb, pos=(1, 1), span=(1, 1), flag=wx.ALIGN_LEFT) #------------------------------------------------------------------------------------------------ # Add messages # self.alienMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.alienMessagesText, flag=wx.EXPAND, proportion=2) self.alienMessages = MessageManager(self.alienMessagesText) self.crossMgrMessagesText = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_MULTILINE | wx.HSCROLL, size=(-1, 400)) fgs.Add(self.crossMgrMessagesText, flag=wx.EXPAND, proportion=2) self.crossMgrMessages = MessageManager(self.crossMgrMessagesText) self.fgs = fgs #------------------------------------------------------------------------------------------------ # Create a timer to update the messages. # self.timer = wx.Timer() self.timer.Bind(wx.EVT_TIMER, self.updateMessages) self.timer.Start(1000, False) self.Bind(wx.EVT_CLOSE, self.onCloseWindow) self.readOptions() self.SetSizer(self.vbs) self.start() def OnAboutBox(self, e): description = """CrossMgrAlien is an Impinj interface to CrossMgr """ licence = """CrossMgrAlien is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. CrossMgrImpinj is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with File Hunter; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA""" info = wx.adv.AboutDialogInfo() crossMgrPng = Utils.getImageFolder() + '/CrossMgrAlien.png' info.SetIcon(wx.Icon(crossMgrPng, wx.BITMAP_TYPE_PNG)) info.SetName('CrossMgrAlien') info.SetVersion(AppVerName.split(' ')[1]) info.SetDescription(description) info.SetCopyright('(C) 2020 Edward Sitarski') info.SetWebSite('http://www.sites.google.com/site/crossmgrsoftware/') info.SetLicence(licence) wx.adv.AboutBox(info, self) def start(self): self.dataQ = Queue() self.messageQ = Queue() self.shutdownQ = Queue( ) # Queue to tell the Alien monitor to shut down. self.alienProcess = Process( name='AlienProcess', target=AlienServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getNotifyHost(), NotifyPort, HeartbeatPort, self.getAntennaStr(), self.listenForHeartbeat.GetValue(), self.cmdHost.GetAddress(), self.cmdPort.GetValue())) self.alienProcess.daemon = True self.crossMgrProcess = Process(name='CrossMgrProcess', target=CrossMgrServer, args=(self.dataQ, self.messageQ, self.shutdownQ, self.getCrossMgrHost(), CrossMgrPort)) self.crossMgrProcess.daemon = True self.alienProcess.start() self.crossMgrProcess.start() def doAdvanced(self, event): dlg = AdvancedSetup(self) dlg.ShowModal() dlg.Destroy() self.writeOptions() def shutdown(self): self.alienProcess = None self.crossMgrProcess = None self.messageQ = None self.dataQ = None self.shutdownQ = None def gracefulShutdown(self): # Shutdown the CrossMgr process by sending it a shutdown command. if self.shutdownQ: self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') self.shutdownQ.put('shutdown') if self.dataQ: self.dataQ.put('shutdown') if self.crossMgrProcess: self.crossMgrProcess.join() if self.alienProcess: self.alienProcess.join() self.crossMgrProcess = None self.alienProcess = None def doReset(self, event, confirm=True): if confirm: dlg = wx.MessageDialog(self, 'Reset CrossMgrAlien Adapter?', 'Confirm Reset', wx.OK | wx.CANCEL | wx.ICON_WARNING) ret = dlg.ShowModal() dlg.Destroy() if ret != wx.ID_OK: return self.reset.Enable( False) # Prevent multiple clicks while shutting down. self.writeOptions() self.gracefulShutdown() self.alienMessages.clear() self.crossMgrMessages.clear() self.shutdown() self.reset.Enable(True) wx.CallAfter(self.start) def doAutoDetect(self, event): wx.BeginBusyCursor() self.gracefulShutdown() self.shutdown() alienHost, crossmgrHost = AutoDetect() wx.EndBusyCursor() if alienHost and crossmgrHost: self.setNotifyHost( crossmgrHost ) # Assumes CrossMgr is on the same computer as CrossMgrAlien. self.cmdHost.SetValue(alienHost) self.cmdPort.SetValue(str(DefaultAlienCmdPort)) self.crossMgrHost.SetValue(crossmgrHost) self.listenForHeartbeat.SetValue(False) else: dlg = wx.MessageDialog( self, 'Auto Detect Failed.\nCheck that reader has power and is connected to the router.', 'Auto Detect Failed', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() self.doReset(event, False) def onCloseWindow(self, event): wx.Exit() def doCopyToClipboard(self, event): cc = [ 'Configuration: CrossMgrAlien', ' NotifyHost: {}'.format(self.getNotifyHost()), ' NotifyPort: {}'.format(NotifyPort), ' RunningTime: {}'.format(self.runningTime.GetLabel()), ' Time: {}'.format(self.time.GetLabel()), ' BackupFile: {}'.format(self.backupFile.GetLabel()), '', 'Configuration: Alien:', ' ListenForAlienHeartbeat: {}'.format( 'True' if self.listenForHeartbeat.GetValue() else 'False'), ' Antennas: {}'.format(self.getAntennaStr()), ' HeartbeatPort: {}'.format(HeartbeatPort), ' AlienCmdHost: {}'.format(self.cmdHost.GetAddress()), ' AlienCmdPort: {}'.format(self.cmdPort.GetValue()), '', ' RepeatSeconds: {}'.format(Alien.RepeatSeconds), '', 'Configuration: CrossMgr', ' CrossMgrHost: {}'.format(self.getCrossMgrHost()), ' CrossMgrPort: {}'.format(CrossMgrPort), ] cc.append('\nLog: Alien') log = self.alienMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: CrossMgr') log = self.crossMgrMessagesText.GetValue() cc.extend([' ' + line for line in log.split('\n')]) cc.append('\nLog: Application\n') try: with open(redirectFileName, 'r') as fp: for line in fp: cc.append(line) except: pass if wx.TheClipboard.Open(): do = wx.TextDataObject() do.SetText('\n'.join(cc)) wx.TheClipboard.SetData(do) wx.TheClipboard.Close() dlg = wx.MessageDialog( self, 'Configuration and Logs copied to the Clipboard.', 'Copy to Clipboard Succeeded', wx.OK | wx.ICON_INFORMATION) ret = dlg.ShowModal() dlg.Destroy() else: # oops... something went wrong! wx.MessageBox("Unable to open the clipboard", "Error") def getNotifyHost(self): s = self.notifyHost.GetSelection() return self.notifyHost.GetString(s) if s != wx.NOT_FOUND else None def setNotifyHost(self, notifyHost): for i, s in enumerate(self.notifyHost.GetItems()): if s == notifyHost: self.notifyHost.SetSelection(i) return self.notifyHost.SetSelection(0) def getCrossMgrHost(self): return self.crossMgrHost.GetAddress() def getAntennaStr(self): s = [] for i in range(4): if self.antennas[i].GetValue(): s.append('%d' % i) if not s: # Ensure that at least one antenna is selected. self.antennas[0].SetValue(True) s.append('0') return ' '.join(s) def setAntennaStr(self, s): antennas = set(int(a) for a in s.split()) for i in range(4): self.antennas[i].SetValue(i in antennas) def writeOptions(self): self.config.Write('CrossMgrHost', self.getCrossMgrHost()) self.config.Write( 'ListenForAlienHeartbeat', 'True' if self.listenForHeartbeat.GetValue() else 'False') self.config.Write('AlienCmdAddr', self.cmdHost.GetAddress()) self.config.Write('AlienCmdPort', str(self.cmdPort.GetValue())) self.config.Write('Antennas', self.getAntennaStr()) self.config.Write('RepeatSeconds', '{}'.format(Alien.RepeatSeconds)) self.config.Write('PlaySounds', '{}'.format(Utils.playBell)) s = self.notifyHost.GetSelection() if s != wx.NOT_FOUND: self.config.Write('NotifyHost', self.notifyHost.GetString(s)) self.config.Flush() def readOptions(self): self.crossMgrHost.SetValue( self.config.Read('CrossMgrHost', Utils.DEFAULT_HOST)) self.listenForHeartbeat.SetValue( self.config.Read('ListenForAlienHeartbeat', 'True').upper()[:1] == 'T') Utils.playBell = (self.config.Read('PlaySounds', 'True').upper()[:1] == 'T') self.cmdHost.SetValue(self.config.Read('AlienCmdAddr', '0.0.0.0')) self.cmdPort.SetValue(int(self.config.Read('AlienCmdPort', '0'))) self.setAntennaStr(self.config.Read('Antennas', '0 1')) Alien.RepeatSeconds = int( self.config.Read('RepeatSeconds', '{}'.format(Alien.RepeatSeconds))) notifyHost = self.config.Read('NotifyHost', Utils.DEFAULT_HOST) self.setNotifyHost(notifyHost) def updateMessages(self, event): tNow = datetime.datetime.now() running = int((tNow - self.tStart).total_seconds()) self.runningTime.SetLabel('{:02d}:{:02d}:{:02d}'.format( running // (60 * 60), (running // 60) % 60, running % 60)) self.time.SetLabel(tNow.strftime('%H:%M:%S')) if not self.messageQ: return while 1: try: d = self.messageQ.get(False) except Empty: break message = ' '.join(str(x) for x in d[1:]) if d[0] == 'Alien': if 'state' in d: self.alienMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.alienMessages.write(message) elif d[0] == 'Alien2JChip': if 'state' in d: self.crossMgrMessages.messageList.SetBackgroundColour( self.LightGreen if d[2] else self.LightRed) else: self.crossMgrMessages.write(message) elif d[0] == 'CmdHost': cmdHost, cmdPort = d[1].split(':') self.cmdHost.SetValue(cmdHost) self.cmdPort.SetValue(int(cmdPort)) elif d[0] == 'BackupFile': self.backupFile.SetLabel(d[1])