def __init__(self, host, port, callFunc, timeLim=5, pollInterval=0.2): """Start waiting for a TCP server to accept a connection @param[in] host: host address of server @param[in] port: port number of server @param[in] callFunc: function to call when server ready or wait times out; receives one parameter: this object @param[in] timeLim: approximate maximum wait time (sec); the actual wait time may be up to pollInterval longer @param[in] pollInterval: interval at which to poll (sec) Useful attributes: - isDone: the wait is over - didFail: the wait failed """ self.host = host self.port = port self.isDone = False self.didFail = False self._callFunc = callFunc self._pollInterval = float(pollInterval) self._timeLim = float(timeLim) self._pollTimer = Timer() self._startTime = time.time() self._tryConnection() self._timeoutTimer = Timer(timeLim, self._finish)
def setMsg(self, msgStr, severity=RO.Constants.sevNormal, isTemp=False, duration=None): """Display a new message. Inputs: - msgStr the new string to display - severity one of RO.Constants.sevNormal (default), sevWarning or sevError - isTemp if true, message is temporary and can be cleared with clearTempMsg; if false, any existing temp info is ditched - duration the amount of time (msec) to leave a temporary message; if omitted, there is no time limit; ignored if isTemp false Returns None if a permanent message, else a unique positive message ID. """ self.displayWdg.set(msgStr, severity=severity) if isTemp: self.currID = next(self.tempIDGen) if duration is not None: Timer(duration / 1000.0, self.clearTempMsg, self.currID) else: self.permMsg = msgStr self.permSeverity = severity self.currID = None return self.currID
def addRandomValues(line, interval=0.1): """Add random values to the specified strip chart line Inputs: - line: strip chart line - interval: interval between updates (sec) """ var = varDict[line] line.addPoint(next(var)) Timer(interval, addRandomValues, line, interval)
def runTest(): global clientConn try: testStr = next(strIter) print("Client writing %r" % (testStr,)) clientConn.writeLine(testStr) Timer(0.001, runTest) except StopIteration: pass
def displayNext(): global ind, testData val = testData[ind] print("\nvalue = %r, isCurrent = %s" % tuple(val)) for wdg in wdgSet: wdg.set(*val) ind += 1 if ind < len(testData): Timer(1.2, displayNext)
def __init__( self, prefVar, master, row=0, column=0, ): self.master = master self.prefVar = prefVar # create and set a variable to contain the edited value self.editVar = Tkinter.StringVar() self.editVar.set(self.prefVar.getValueStr()) # save initial value, in case we have to restore it self.initialValue = self.getCurrentValue() # save last good value, for use if a typed char is rejected self.mostRecentValidValue = self.editVar.get() # a list (in grid order) of (widget name, sticky setting) wdgInfo = ( ("labelWdg", "e"), ("changedWdg", ""), ("editWdg", "w"), ("unitsWdg", "w"), ) self.labelWdg = Tkinter.Label(self.master, text=self.prefVar.name) self._addCtxMenu(self.labelWdg) self.changedVar = Tkinter.StringVar() self.changedWdg = Tkinter.Label(self.master, width=1, textvariable=self.changedVar) self._addCtxMenu(self.changedWdg) self.editWdg = self._getEditWdg() # self.rangeWdg = self._getRangeWdg() if self.prefVar.units: self.unitsWdg = Tkinter.Label(self.master, text=self.prefVar.name) self._addCtxMenu(self.unitsWdg) else: self.unitsWdg = None # grid the widgets for wdgName, sticky in wdgInfo: wdg = getattr(self, wdgName) if wdg: wdg.grid(row=row, column=column, sticky=sticky) column += 1 self.timer = Timer() self._setupCallbacks()
def _dispatchIter(self, dataDictIter): """Dispatch and iterator over dataDictSet; see runDataDictSet for details """ try: dataDict = dataDictIter.next() delay = dataDict.pop("delay") except StopIteration: print "Test finished" return self.dispatch(**dataDict) Timer(delay, self._dispatchIter, dataDictIter)
def __init__(self, delayMS=600): """Construct a _BalloonHelp Inputs: - delayMS: delay time before help is shown """ self._isShowing = False self._delayMS = delayMS self._showTimer = Timer() self._leaveTimer = Timer() self._msgWin = tkinter.Toplevel() self._msgWin.overrideredirect(True) self._msgWdg = tkinter.Message(self._msgWin, bg="light yellow") self._msgWdg.pack() self._msgWin.withdraw() self._msgWdg.bind_all('<Motion>', self._start) self._msgWdg.bind_all('<Leave>', self._leave) self._msgWdg.bind_all('<ButtonPress>', self._stop) self._msgWdg.bind_all('<KeyPress>', self._stop) self._msgWdg.bind_all('<Tab>', self._stop, add=True) self._msgWin.bind("<Configure>", self._configure)
def runTest(): global clientSocket try: testStr = next(strIter) print("Client writing %r" % (testStr,)) if binary: clientSocket.write(testStr) else: clientSocket.writeLine(testStr) Timer(0.001, runTest) except StopIteration: pass
def __init__(self, master=None, retainSec=300, height=10, width=50, **kargs): Tkinter.Frame.__init__(self, master, **kargs) hubModel = TUI.Models.HubModel.getModel() self.tuiModel = TUI.TUIModel.getModel() # entries are commanders (prog.user) self._cmdrList = [] # entries are (cmdr, time deleted); time is from time.time() self._delCmdrTimeList = [] # time to show deleted users self._retainSec = retainSec # dictionary of user name: User object self.userDict = dict() self._updateTimer = Timer() self.yscroll = Tkinter.Scrollbar( master=self, orient="vertical", ) self.text = Tkinter.Text( master=self, yscrollcommand=self.yscroll.set, wrap="none", tabs="1.6c 5.0c 6.7c 8.5c", height=height, width=width, ) self.yscroll.configure(command=self.text.yview) self.text.grid(row=0, column=0, sticky="nsew") self.yscroll.grid(row=0, column=1, sticky="ns") RO.Wdg.Bindings.makeReadOnly(self.text) RO.Wdg.addCtxMenu( wdg=self.text, helpURL=_HelpPage, ) self.rowconfigure(0, weight=1) self.columnconfigure(0, weight=1) self.text.tag_configure("del", overstrike=True) self.text.tag_configure("me", underline=True) hubModel.user.addCallback(self.updUser, callNow=False) hubModel.users.addCallback(self.updUsers)
def dispatchNext(): try: newDataDict = dataIter.next() except StopIteration: return msgDict = { "cmdr": ".hub", "cmdID": 11, "actor": "hub", "msgType": "i", "data": newDataDict } kd.dispatch(msgDict) Timer(1.0, dispatchNext)
def __init__(self, scriptPath, title=None, initialText=None, **keyArgs): """Construct and run a DropletRunner Inputs: - scriptPath: path to script to run when files are dropped on the application - title: title for log window; if None then generated from scriptPath - initialText: initial text to display in log window **keyArgs: all other keyword arguments are sent to the RO.Wdg.LogWdg constructor """ self.isRunning = False self.scriptPath = os.path.abspath(scriptPath) if not os.path.isfile(scriptPath): raise RuntimeError("Cannot find script %r" % (self.scriptPath, )) self.tkRoot = tkinter.Tk() self._timer = Timer() if title == None: title = os.path.splitext(os.path.basename(scriptPath))[0] self.tkRoot.title(title) if RO.OS.PlatformName == "mac": self.tkRoot.createcommand('::tk::mac::OpenDocument', self._macOpenDocument) # the second argument is a process ID (approximately) if run as an Applet; # the conditional handles operation from the command line if len(sys.argv) > 1 and sys.argv[1].startswith("-"): filePathList = sys.argv[2:] else: filePathList = sys.argv[1:] else: filePathList = sys.argv[1:] self.logWdg = LogWdg.LogWdg(self.tkRoot, **keyArgs) self.logWdg.grid(row=0, column=0, sticky="nsew") self.tkRoot.grid_rowconfigure(0, weight=1) self.tkRoot.grid_columnconfigure(0, weight=1) if initialText: self.logWdg.addOutput(initialText) if filePathList: self.runFiles(filePathList) self.tkRoot.mainloop()
def __init__(self, master, countUp=False, valueFormat=("%3.0f sec", "??? sec"), autoStop=False, updateInterval=0.1, **kargs): ProgressBar.__init__(self, master=master, valueFormat=valueFormat, **kargs) self._autoStop = bool(autoStop) self._countUp = bool(countUp) self._updateInterval = updateInterval self._updateTimer = Timer() self._startTime = None if "value" in kargs: self.start(kargs["value"])
def _processNextFile(self, filePathList): """Helper for processFileList The main purpose of this helper is to yield some time between each file so the log window can update (without using update_idletasks). """ if filePathList: filePath = filePathList[0] try: self.processFile(filePath) except Exception as e: self.logWdg.addOutput("%s failed: %s\n" % (filePath, e), severity=RO.Constants.sevError) if self.printTraceback: traceback.print_exc(file=sys.stderr) remFilePathList = filePathList[1:] if remFilePathList: Timer(0.001, self._processNextFile, remFilePathList) elif self.doneMsg: self.logWdg.addOutput(self.doneMsg, severity=RO.Constants.sevNormal)
def _nextStar(starInfo, delaySec): starInfo.update() keyVarStr = starInfo.getKeyVarStr() testDispatcher.dispatch(keyVarStr, actor="gcam") Timer(delaySec, _nextStar, starInfo, delaySec)
def _nextGuideOffset(guideOffInfo, delaySec): guideOffInfo.update() keyVarStr = guideOffInfo.getKeyVarStr() testDispatcher.dispatch(keyVarStr, actor="tcc") Timer(delaySec, _nextGuideOffset, guideOffInfo, delaySec)
def __init__(self, master, maxTransfers = 1, maxLines = 500, helpURL = None, **kargs): tkinter.Frame.__init__(self, master = master, **kargs) self._memDebugDict = {} self.maxLines = maxLines self.maxTransfers = maxTransfers self.selFTPGet = None # selected getter, for displaying details; None if none self.dispList = [] # list of displayed ftpGets self.getQueue = [] # list of unfinished (ftpGet, stateLabel, ftpCallback) triples self._timer = Timer() self.yscroll = tkinter.Scrollbar ( master = self, orient = "vertical", ) self.text = tkinter.Text ( master = self, yscrollcommand = self.yscroll.set, wrap = "none", tabs = (8,), height = 4, width = 50, ) self.yscroll.configure(command=self.text.yview) self.text.grid(row=0, column=0, sticky="nsew") self.yscroll.grid(row=0, column=1, sticky="ns") Bindings.makeReadOnly(self.text) if helpURL: CtxMenu.addCtxMenu( wdg = self.text, helpURL = helpURL + "#LogDisplay", ) self.rowconfigure(0, weight=1) self.columnconfigure(0, weight=1) detFrame = tkinter.Frame(self) gr = RO.Wdg.Gridder(detFrame, sticky="ew") self.fromWdg = RO.Wdg.StrEntry( master = detFrame, readOnly = True, helpURL = helpURL and helpURL + "#From", borderwidth = 0, ) gr.gridWdg("From", self.fromWdg, colSpan=3) self.toWdg = RO.Wdg.StrEntry( master = detFrame, readOnly = True, helpURL = helpURL and helpURL + "#To", borderwidth = 0, ) gr.gridWdg("To", self.toWdg, colSpan=2) self.stateWdg = RO.Wdg.StrEntry( master = detFrame, readOnly = True, helpURL = helpURL and helpURL + "#State", borderwidth = 0, ) self.abortWdg = RO.Wdg.Button( master = detFrame, text = "Abort", command = self._abort, helpURL = helpURL and helpURL + "#Abort", ) gr.gridWdg("State", self.stateWdg, colSpan=2) self.abortWdg.grid(row=1, column=2, rowspan=2, sticky="s") detFrame.columnconfigure(1, weight=1) detFrame.grid(row=1, column=0, columnspan=2, sticky="ew") self.text.bind("<ButtonPress-1>", self._selectEvt) self.text.bind("<B1-Motion>", self._selectEvt) self._updAllStatus() atexit.register(self._abortAll)
def __init__(self, master=None, **kargs): """Displays miscellaneous information, such as current time and az/alt Inputs: - master master Tk widget -- typically a frame or window """ Tkinter.Frame.__init__(self, master=master, **kargs) self.tccModel = TUI.Models.getModel("tcc") self.guiderModel = TUI.Models.getModel("guider") self.mcpModel = TUI.Models.getModel("mcp") self.plateDBModel = TUI.Models.getModel("platedb") self._cartridgeInfo = [None] * 3 # (cartID, plateID, pointing) self._clockTimer = Timer() gr = RO.Wdg.Gridder(self, sticky="e") self.haWdg = RO.Wdg.DMSLabel( master=self, precision=0, nFields=3, cvtDegToHrs=1, width=8, helpText="Hour angle of the object", helpURL=_HelpURL, ) gr.gridWdg("HA", self.haWdg, "hms") self.designHAWdg = RO.Wdg.DMSLabel( master=self, precision=0, nFields=3, cvtDegToHrs=1, width=8, helpText="Hour angle the plate was designed for (from platedb)", helpURL=_HelpURL, ) gr.gridWdg("Design HA", self.designHAWdg, "hms") self.deltaHAWdg = RO.Wdg.DMSLabel( master=self, precision=0, nFields=3, cvtDegToHrs=1, width=8, helpText="Design - current hour angle", helpURL=_HelpURL, ) gr.gridWdg("Des-Curr HA", self.deltaHAWdg, "hms") self.taiWdg = RO.Wdg.StrLabel( master=self, width=19, helpText="International Atomic Time", helpURL=_HelpURL, ) gr.gridWdg("TAI", self.taiWdg, colSpan=2) # secondary focus self.secFocusWdg = RO.Wdg.FloatLabel( master=self, precision=0, width=5, helpText="Secondary mirror focus", helpURL=_HelpURL, ) gr.gridWdg( label="Focus", dataWdg=self.secFocusWdg, units=u"\N{MICRO SIGN}m", ) self.tccModel.secFocus.addValueCallback(self.secFocusWdg.set) # start the second column of widgets gr.startNewCol(spacing=1) gr._nextCol -= 2 # allow overlap with widget to the right self.airmassWdg = RO.Wdg.FloatLabel( master=self, precision=3, width=5, helpText="Airmass", helpURL=_HelpURL, ) gr.gridWdg("Airmass", self.airmassWdg) self.zdWdg = RO.Wdg.FloatLabel( master=self, precision=1, helpText="Zenith distance (90 - altitude)", helpURL=_HelpURL, width=5, ) gr.gridWdg("ZD", self.zdWdg, RO.StringUtil.DegStr) self.lmstWdg = RO.Wdg.DMSLabel( master=self, precision=0, nFields=3, width=8, justify="right", helpText="Local mean sidereal time at APO", helpURL=_HelpURL, ) gr.gridWdg("LMST", self.lmstWdg, "hms") self.sjdWdg = RO.Wdg.IntLabel( master=self, helpText="SDSS MJD (rolls over at TAI MJD-0.3)", helpURL=_HelpURL, width=6, ) gr.gridWdg("SJD", self.sjdWdg, "days") self.scaleWdg = RO.Wdg.FloatLabel( master=self, precision=1, width=8, helpText= "scale ((plate/nominal - 1) * 1e6); larger is higher resolution", helpURL=_HelpURL, ) gr.gridWdg( label="Scale", dataWdg=self.scaleWdg, units="1e6", ) self.tccModel.scaleFac.addCallback(self._scaleFacCallback) # start the third column of widgets gr.startNewCol(spacing=1) self.instNameWdg = RO.Wdg.StrLabel( master=self, width=10, helpText="Current instrument (from the TCC)", helpURL=_HelpURL, ) gr.gridWdg("Inst", self.instNameWdg, units=False) self.tccModel.inst.addValueCallback(self.instNameWdg.set) self.cartridgeIDWdg = RO.Wdg.StrLabel( master=self, width=13, helpText="currently mounted cartridge (from MCP and guider)", helpURL=_HelpURL, ) gr.gridWdg("Cartridge", self.cartridgeIDWdg) self.plateIDWdg = RO.Wdg.IntLabel( master=self, width=8, helpText="currently mounted plug plate (from the guider)", helpURL=_HelpURL, ) gr.gridWdg("Plate", self.plateIDWdg) self.platePointingWdg = RO.Wdg.StrLabel( master=self, width=8, helpText="plug-plate pointing (from the guider)", helpURL=_HelpURL, ) gr.gridWdg("Pointing", self.platePointingWdg) # state of guiding self.guideWdg = RO.Wdg.StrLabel( master=self, anchor="e", helpText="State of guiding", helpURL=_HelpURL, ) gr.gridWdg( label="Guiding", dataWdg=self.guideWdg, units=False, sticky="ew", ) # all widgets are gridded gr.allGridded() # add callbacks self.tccModel.axePos.addCallback(self._setAxePos) self.guiderModel.cartridgeLoaded.addCallback(self.setCartridgeInfo) self.mcpModel.instrumentNum.addCallback(self.setCartridgeInfo) self.plateDBModel.pointingInfo.addCallback(self._setAxePos) self.guiderModel.guideState.addCallback(self._guideStateCallback) # start clock updates self._updateClock() # allow the last+1 column to grow to fill the available space self.columnconfigure(gr.getMaxNextCol(), weight=1)
def __init__(self, master=None, userModel=None, **kargs): RO.Wdg.InputContFrame.__init__(self, master, **kargs) gr = RO.Wdg.Gridder(self, sticky="w") # start out by not checking object position # set this true after all widgets are painted # and the formatting functions have had their test run self.checkObjPos = 0 self._azAltRefreshTimer = Timer() self.objNameWdg = RO.Wdg.StrEntry( self, helpText="Object name (optional)", helpURL=_HelpPrefix + "NameWdg", width=25, ) self.objName = gr.gridWdg( label="Name", dataWdg=self.objNameWdg, colSpan=3, ) lastCol = gr.getNextCol() - 2 self.columnconfigure(lastCol, weight=1) objPos1UnitsVar = Tkinter.StringVar() self.objPos1 = gr.gridWdg( label="", dataWdg=RO.Wdg.DMSEntry( self, minValue=0, maxValue=359.99999999, defValue=None, unitsVar=objPos1UnitsVar, isHours= 0, # this will vary so no initial value is actually needed helpText="Object position", helpURL=_HelpPrefix + "PosWdg", ), units=objPos1UnitsVar, ) objPos2UnitsVar = Tkinter.StringVar() self.objPos2 = gr.gridWdg( label="", dataWdg=RO.Wdg.DMSEntry( self, minValue=0, maxValue=90, defValue=None, unitsVar=objPos2UnitsVar, isHours=0, # always in degrees helpText="Object position", helpURL=_HelpPrefix + "PosWdg", ), units=objPos2UnitsVar, ) self.coordSysWdg = CoordSysWdg.CoordSysWdg( master=self, userModel=userModel, ) gr.gridWdg( label="CSys", dataWdg=self.coordSysWdg, colSpan=3, ) self.rotWdg = RotWdg.RotWdg( master=self, userModel=userModel, ) gr.gridWdg( label="Rot", dataWdg=self.rotWdg, colSpan=3, ) azAltFrame = Tkinter.Frame(self) self.azWdg = RO.Wdg.FloatLabel( master=azAltFrame, precision=2, width=6, helpText="azimuth for proposed object", helpURL=_HelpPrefix + "Azimuth", ) self.azWdg.pack(side="left") Tkinter.Label(azAltFrame, text="%s Alt" % (RO.StringUtil.DegStr, )).pack(side="left") self.altWdg = RO.Wdg.FloatLabel( master=azAltFrame, precision=2, width=6, helpText="altitude for proposed object", helpURL=_HelpPrefix + "Altitude", ) self.altWdg.pack(side="left") Tkinter.Label(azAltFrame, text=RO.StringUtil.DegStr).pack(side="left") gr.gridWdg( label="Az", dataWdg=azAltFrame, colSpan=3, ) self.airmassWdg = RO.Wdg.FloatLabel( master=self, precision=3, width=6, helpText="airmass for proposed object", helpURL=_HelpPrefix + "Airmass", ) gr.gridWdg( label="Airmass", dataWdg=self.airmassWdg, ) # create a set of input widget containers # this makes it easy to retrieve a command # and also to get and set all data using a value dictionary # note: the coordsys widget must be FIRST # because it has to be set (when restoring from a value dict) # before pos1 is set, to set the isHours flag correctly def formatObjPos(inputCont): wdgList = inputCont.getWdgList() # format data using the widgets valList = [] for wdg in wdgList: if wdg.getString() == '': raise ValueError, "must specify position" val = wdg.getNum() if wdg.getIsHours(): val = val * 15.0 valList.append(val) return 'track %.7f, %.7f' % tuple(valList) def formatAll(inputCont): # container order is coordsys, objpos, rotator, name (optional) strList = inputCont.getStringList() return strList[1] + ' ' + strList[0] + ''.join(strList[2:]) def vmsQuoteStr(astr): return RO.StringUtil.quoteStr(astr, '"') self.inputCont = RO.InputCont.ContList( conts=[ self.coordSysWdg.inputCont, RO.InputCont.WdgCont( name="ObjPos", wdgs=(self.objPos1.dataWdg, self.objPos2.dataWdg), formatFunc=formatObjPos, ), RO.InputCont.WdgCont( name="Name", wdgs=self.objNameWdg, formatFunc=RO.InputCont.VMSQualFmt(vmsQuoteStr), ), self.rotWdg.inputCont, ], formatFunc=formatAll, ) self.userModel = userModel or TUI.TCC.UserModel.getModel() self.userModel.coordSysName.addCallback(self._coordSysChanged) self.userModel.potentialTarget.addCallback(self.setAzAltAirmass) self.tccModel = TUI.TCC.TCCModel.getModel() self.tccModel.azLim.addCallback(self._azLimChanged) self.tccModel.altLim.addCallback(self._altLimChanged) # initialize display self.restoreDefault() self.objNameWdg.focus_set()
def __init__(self, master=None, **kargs): """Displays miscellaneous information, such as current time and az/alt Inputs: - master master Tk widget -- typically a frame or window """ Tkinter.Frame.__init__(self, master=master, **kargs) self.tccModel = TUI.TCC.TCCModel.getModel() self.gmechModel = TUI.Guide.GMechModel.getModel() self._clockTimer = Timer() gr = RO.Wdg.Gridder(self, sticky="e") # magic numbers AzAltRotPrec = 1 # number of digits past decimal point self.haWdg = RO.Wdg.DMSLabel( master=self, precision=0, nFields=3, cvtDegToHrs=1, width=8, helpText="Hour angle of the object", helpURL=_HelpURL, ) gr.gridWdg( label="HA", dataWdg=self.haWdg, units="hms", ) self.lmstWdg = RO.Wdg.DMSLabel( master=self, precision=0, nFields=3, width=8, justify="right", helpText="Local mean sidereal time at APO", helpURL=_HelpURL, ) gr.gridWdg( label="LMST", dataWdg=self.lmstWdg, units="hms", ) self.utcWdg = RO.Wdg.StrLabel( master=self, width=19, helpText="Coordinated universal time", helpURL=_HelpURL, ) gr.gridWdg( label="UTC", dataWdg=self.utcWdg, colSpan=2, ) # start the second column of widgets gr.startNewCol(spacing=1) self.guideWdg = RO.Wdg.StrLabel( master=self, width=13, anchor="w", helpText="State of guiding", helpURL=_HelpURL, ) gr.gridWdg( label="Guiding", dataWdg=self.guideWdg, colSpan=4, units=False, sticky="ew", ) gr._nextCol -= 2 # allow overlap with widget to the right self.guideModelDict = {} # guide camera name: guide model for guideModel in TUI.Guide.GuideModel.modelIter(): gcamName = guideModel.gcamName if gcamName.endswith("focus"): continue self.guideModelDict[guideModel.gcamName] = guideModel guideModel.locGuideStateSummary.addIndexedCallback( self._updGuideStateSummary, callNow=False) self._updGuideStateSummary() # airmass and zenith distance self.airmassWdg = RO.Wdg.FloatLabel( master=self, precision=3, width=5, helpURL=_HelpURL, ) gr.gridWdg( label="Airmass", dataWdg=self.airmassWdg, units="", ) # self.tccModel.axePos.addCallback(self.setAxePos) self.zdWdg = RO.Wdg.FloatLabel( master=self, precision=AzAltRotPrec, helpText="Zenith distance", helpURL=_HelpURL, width=5, ) gr.gridWdg( label="ZD", dataWdg=self.zdWdg, units=RO.StringUtil.DegStr, ) # start the third column of widgets gr.startNewCol(spacing=1) self.instNameWdg = RO.Wdg.StrLabel( master=self, width=10, anchor="w", helpText="Current instrument", helpURL=_HelpURL, ) gr.gridWdg( label="Inst", dataWdg=self.instNameWdg, colSpan=3, units=False, sticky="w", ) self.tccModel.instName.addCallback(self.updateInstName) self.secFocusWdg = RO.Wdg.FloatLabel( master=self, precision=0, width=5, helpText="Secondary mirror focus", helpURL=_HelpURL, ) gr.gridWdg( label="Focus", dataWdg=self.secFocusWdg, units=u"\N{MICRO SIGN}m", ) self.tccModel.secFocus.addROWdg(self.secFocusWdg) self.gcFocusWdg = RO.Wdg.FloatLabel( master=self, precision=0, width=5, helpText="NA2 guide camera focus", helpURL=_HelpURL, ) gr.gridWdg( label="GC Focus", dataWdg=self.gcFocusWdg, units=u"\N{MICRO SIGN}m", ) self.gmechModel.focus.addROWdg(self.gcFocusWdg) # all widgets are gridded gr.allGridded() # add callbacks that deal with multiple widgets self.tccModel.axePos.addCallback(self.setAxePos) # start clock updates self.updateClock() # allow the last+1 column to grow to fill the available space self.columnconfigure(gr.getMaxNextCol(), weight=1)
def __init__(self, master, stateTracker, **kargs): """Create a new widget to show status for and configure GIFS Inputs: - master: parent widget - stateTracker: an RO.Wdg.StateTracker """ RO.Wdg.InputContFrame.__init__(self, master=master, stateTracker=stateTracker, **kargs) self.model = GIFSModel.getModel() self.tuiModel = TUI.TUIModel.getModel() self.updateStdPresetsTimer = Timer() self.gridder = RO.Wdg.StatusConfigGridder( master=self, sticky="w", numStatusCols=3, ) blankLabel = Tkinter.Label(self, width=_DataWidth) blankLabel.grid(row=0, column=1, columnspan=2) self.magnifier = StageControls( gridder=self.gridder, label="Magnifier", configKey=self.model.magnifierConfig, statusKey=self.model.magnifierStatus, ) self.lenslets = StageControls(gridder=self.gridder, label="Lenslets", configKey=self.model.lensletsConfig, statusKey=self.model.lensletsStatus, descr="lenslet array") self.calMirror = CalMirrorControls( gridder=self.gridder, statusKey=self.model.calMirrorStatus, ) self.filter = FilterControls( gridder=self.gridder, label="Filter Wheel", configKey=self.model.filterNames, statusKey=self.model.filterStatus, ) self.collimator = StageControls( gridder=self.gridder, label="Collimator", configKey=self.model.collimatorConfig, statusKey=self.model.collimatorStatus, showOther=True, ) self.disperser = StageControls( gridder=self.gridder, label="Disperser", configKey=self.model.disperserConfig, statusKey=self.model.disperserStatus, ) self.ccdTempWdg = RO.Wdg.FloatLabel(master=self, formatStr="%0.1f K", helpText="CCD temperature (K)") self.heaterPowerWdg = RO.Wdg.FloatLabel( master=self, formatStr="%0.1f %%", helpText="CCD heater power (%)", ) self.gridder.gridWdg( label="CCD Temp", dataWdg=(self.ccdTempWdg, self.heaterPowerWdg), ) self.model.ccdTemp.addROWdg(self.ccdTempWdg) self.model.heaterPower.addROWdg(self.heaterPowerWdg) moveFmtFunc = RO.InputCont.BasicFmt(nameSep=" move=", ) # set up the input container set self.inputCont = RO.InputCont.ContList(conts=[ RO.InputCont.WdgCont( name="calmirror", wdgs=self.calMirror.userWdg, formatFunc=RO.InputCont.BasicFmt(nameSep=" "), ), RO.InputCont.WdgCont( name="collimator", wdgs=self.collimator.userWdg, formatFunc=moveFmtFunc, ), RO.InputCont.WdgCont( name="disperser", wdgs=self.disperser.userWdg, formatFunc=moveFmtFunc, ), RO.InputCont.WdgCont( name="filter", wdgs=self.filter.userWdg, formatFunc=moveFmtFunc, ), RO.InputCont.WdgCont( name="lenslets", wdgs=self.lenslets.userWdg, formatFunc=moveFmtFunc, ), RO.InputCont.WdgCont( name="magnifier", wdgs=self.magnifier.userWdg, formatFunc=moveFmtFunc, ), ], ) self._inputContNameKeyVarDict = dict( calmirror=self.model.calMirrorPresets, collimator=self.model.collimatorPresets, disperser=self.model.disperserPresets, filter=self.model.filterPresets, lenslets=self.model.lensletPresets, magnifier=self.model.magnifierPresets, ) self.presetsWdg = RO.Wdg.InputContPresetsWdg( master=self, sysName="%sConfig" % (self.InstName, ), userPresetsDict=self.tuiModel.userPresetsDict, inputCont=self.inputCont, helpText="use and manage named presets", helpURL=self.HelpPrefix + "Presets", ) self.gridder.gridWdg( "Presets", cfgWdg=self.presetsWdg, ) self.gridder.allGridded() # for presets data use a timer to increase the chance that all keywords have been seen # before the method is called def callUpdPresets(*args, **kwargs): self.updateStdPresetsTimer.start(0.1, self.updateStdPresets) for keyVar in self._inputContNameKeyVarDict.itervalues(): keyVar.addCallback(callUpdPresets) def repaint(evt): self.restoreDefault() self.bind("<Map>", repaint)
def addCatalog(self, catalog): """Add a new catalog with a given name. If the catalog already exists, it is deleted. """ # print "addCatalog %r" % (catalog.name,) catName = catalog.name if catName in self.catDict: self.removeCatalogByName(catName) self.catDict[catName] = catalog self.catPixPosObjDict[catName] = [] self.catRedrawTimerDict[catName] = Timer() self.catColorDict[catName] = catalog.getDispColor() def updateCat(sr, self=self, catalog=catalog): catName = catalog.name catTag = "cat_%s" % (catName, ) if not catalog.getDoDisplay(): self.catPixPosObjDict[catName] = [] self.cnv.delete(catTag) return # if color has changed, update it color = catalog.getDispColor() oldColor = self.catColorDict.get(catName) if color != oldColor: self.cnv.itemconfigure(catTag, fill=color, outline=color) self.catColorDict[catName] = color # print "compute %s thread starting" % catName yield sr.waitThread(_UpdateCatalog, catalog.objList, self.center, self.azAltScale) pixPosObjList = sr.value # print "compute %s thread done" % catName catName = catalog.name catTag = "cat_%s" % (catName, ) self.catPixPosObjDict[catName] = [] self.cnv.delete(catTag) color = catalog.getDispColor() rad = 2 # for now, eventually may wish to vary by magnitude or window size or...? for pixPos, obj in pixPosObjList: self.cnv.create_oval( pixPos[0] - rad, pixPos[1] - rad, pixPos[0] + rad + 1, pixPos[1] + rad + 1, tag=(SkyWdg.CATOBJECT, catTag), fill=color, outline=color, ) self.catPixPosObjDict[catName] = pixPosObjList self.catRedrawTimerDict[catName].start(_CatRedrawDelay, self._drawCatalog, catalog) sr = RO.ScriptRunner.ScriptRunner( runFunc=updateCat, name="updateCatalog", ) self.catSRDict[catName] = sr catalog.addCallback(self._drawCatalog, callNow=True)
def __init__(self, master, width=201, height=201): Tkinter.Frame.__init__(self, master) self.tuiModel = TUI.TUIModel.getModel() self.tccModel = TUI.TCC.TCCModel.getModel() self.userModel = TUI.TCC.UserModel.getModel() # instance variables: # center: position of center of canvas, in pixels # size: size of canvas, in pixels # scale: scale of canvas, in pixels per deg self.currCatObjID = None self._telPotentialAnimTimer = Timer() self.eastLabelPos = AzAltTarget(azAlt=(90, 0)) self.northLabelPos = AzAltTarget(azAlt=(180, 0)) # pane on which to display current star info self.currStarDisp = RO.Wdg.StatusBar(master=self) self.currStarDisp.grid(row=1, column=0, sticky="ew") self.currStarMsgID = None # canvas on which to display stars self.cnv = Tkinter.Canvas( master=self, width=width, height=height, # background='black', selectborderwidth=0, highlightthickness=0) self.cnv.grid(row=0, column=0, sticky="nsew") self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) RO.Wdg.addCtxMenu( wdg=self.cnv, helpURL=_HelpURL, ) # thickness of canvas border; # drawable width/height = winfo_width/height - (2 * border) self.cnvBorderWidth = int(self.cnv["highlightthickness"]) + int( self.cnv["selectborderwidth"]) self.cnv.bind('<Configure>', self._configureEvt) # self.cnv.tag_bind('star', '<Enter>', self._enterStar) # self.cnv.tag_bind('star', '<Leave>', self._leaveStar) self.cnv.bind('<Motion>', self._enterStar) self.cnv.bind('<Leave>', self._leaveStar) # the following prevents the display from blanking # when the button is pressed once (I tried trapping and # discarding <Button>, as a faster solutionn, but it didn't work) self.cnv.bind('<Button>', self._enterStar) self.cnv.bind('<Double-Button-1>', self._setPotential) self.center = [None, None] self.size = [None, None] self.azAltRad = None self.azAltScale = None self.sizeDeg = [180.0, 180.0] # various dictionaries whose keys are catalog name # note: if a catalog is deleted, it is removed from catDict # and catPixPosObjDict, but not necessarily the others self.catDict = {} # key=catalog name, value = catalog self.catRedrawTimerDict = {} # key=catalog name, value = tk after id self.catColorDict = {} # key=catalog name, value = color self.catPixPosObjDict = { } # key=catalog name, value = list of (pix pos, obj) pairs self.catSRDict = { } # key=catalog name, value = scriptrunner script to redisplay catalog self.telCurrent = None self.telTarget = None self.telPotential = None self.azWrapGauge = RO.CanvasUtil.Spiral( cnv=self.cnv, xctr=1, yctr=1, begRad=0, endRad=0, # not yet ready to draw; canvas size unknown angScale=-1.0, angOff=-90.0, ) self._setSize() # set up automatic update of current and target telescope position self.tccModel.axePos.addCallback(self.setTelCurrent) self.tccModel.tccPos.addCallback(self.setTelTarget) self.tccModel.azLim.addCallback(self.setAzLim) self.userModel.potentialTarget.addCallback(self.setTelPotential) self.userModel.userCatDict.addCallback(self._updUserCatDict)
def __init__ (self, master=None, **kargs): """Displays information about the axes Inputs: - master master Tk widget -- typically a frame or window """ Tkinter.Frame.__init__(self, master=master, **kargs) self.tccModel = TUI.Models.getModel("tcc") self.prevSounds = [None]*3 # sounds played last time we received AxisCmdState self.prevCtrlStatusOK = [None]*3 self.ctrlBadTime = 0 # time of last "controller bad" sound self._soundTimer = Timer() # magic numbers PosPrec = 3 # number of digits past decimal point PosWidth = 5 + PosPrec # assumes -999.99... degrees is longest field AxisCmdStateWidth = 8 AxisErrCodeWidth = 13 CtrlStatusWidth = 25 # commanded state dictionary: # - keys are axis commanded state keywords, cast to lowercase # - values are the severity self._CmdStateDict = { "Drifting": RO.Constants.sevWarning, "Halted": RO.Constants.sevError, "Halting": RO.Constants.sevError, "Slewing": RO.Constants.sevWarning, "Tracking": RO.Constants.sevNormal, "NotAvailable": RO.Constants.sevNormal, } self.axisInd = range(len(self.tccModel.axisNames)) # actual axis position widget set self.axePosWdgSet = [ RO.Wdg.FloatLabel( master = self, precision = PosPrec, width = PosWidth, helpText = "Current axis position, as reported by the controller", helpURL = _HelpURL, ) for axis in self.axisInd ] self.tccModel.axePos.addValueListCallback([wdg.set for wdg in self.axePosWdgSet]) # target axis position widget set self.tccPosWdgSet = [ RO.Wdg.FloatLabel( master = self, precision = PosPrec, width = PosWidth, helpText = "Target axis position", helpURL = _HelpURL, ) for axis in self.axisInd ] self.tccModel.tccPos.addValueListCallback([wdg.set for wdg in self.tccPosWdgSet]) # TCC status widget set (e.g. tracking or halted) self.axisCmdStateWdgSet = [ RO.Wdg.StrLabel( master = self, width = AxisCmdStateWidth, helpText = "What the TCC is telling the axis to do", helpURL = _HelpURL, anchor = "nw", ) for axis in self.axisInd ] self.tccModel.axisCmdState.addCallback(self._axisCmdStateCallback) self.tccModel.pleaseSlew.addCallback(self._pleaseSlewStateCallback) self.tccModel.rotExists.addCallback(self._rotExistsCallback) # axis error code widet set (why the TCC is not moving the axis) self.axisErrCodeWdgSet = [ RO.Wdg.StrLabel( master = self, width = AxisErrCodeWidth, helpText = "Why the TCC halted the axis", helpURL = _HelpURL, anchor = "nw", ) for axis in self.axisInd ] self.tccModel.axisErrCode.addValueListCallback([wdg.set for wdg in self.axisErrCodeWdgSet]) # controller status widget set (the status word) self.ctrlStatusWdgSet = [ RO.Wdg.StrLabel( master = self, width = CtrlStatusWidth, helpText = "Status reported by the axis controller", helpURL = _HelpURL, anchor = "nw", ) for axis in self.axisInd ] # handle Az/Alt/RotCtrlStatus for axisInd, axisName in enumerate(self.tccModel.axisNames): statusVar = getattr(self.tccModel, axisName.lower() + "Stat") statusVar.addCallback(RO.Alg.GenericCallback(self._ctrlStatusCallback, axisInd)) # grid the axis widgets gr = RO.Wdg.Gridder(self, sticky="w") for axis in self.axisInd: unitsLabel1 = Tkinter.Label(self, text=RO.StringUtil.DegStr) unitsLabel2 = Tkinter.Label(self, text=RO.StringUtil.DegStr) if axis == 2: self.rotUnitsLabel1 = unitsLabel1 self.rotUnitsLabel2 = unitsLabel2 gr.gridWdg ( label = self.tccModel.axisNames[axis], dataWdg = ( self.axePosWdgSet[axis], unitsLabel1, self.tccPosWdgSet[axis], unitsLabel2, self.axisCmdStateWdgSet[axis], # self.axisErrCodeWdgSet[axis], # self.ctrlStatusWdgSet[axis], ) ) # widen rotator commanded state widget # so there's room to display "NotAvailable" # (note that the error code widget will be hidden when this occurs # so the text will not overlap anything). rotCmdWdg = self.axisCmdStateWdgSet[2] rotCmdWdg.grid_configure(columnspan=2) rotCmdWdg["width"] = 12 # allow the last column to grow to fill the available space self.columnconfigure(gr.getMaxNextCol(), weight=1)
def __init__(self, testDispatcher, master): Tkinter.Frame.__init__(self, master) self.testDispatcher = testDispatcher random.seed(0) self.tuiModel = self.testDispatcher.tuiModel self.pollTimer = Timer() self.oldPendingCmd = None self.fileNum = 0 gr = RO.Wdg.Gridder(self, sticky="ew") self.guideWdg = AgileGuideWindow.AgileGuideWdg(self) gr.gridWdg(False, self.guideWdg, colSpan=10) self.imageAvailWdg = RO.Wdg.Button( master = self, text = "Image is Available", callFunc = self.dispatchFileData, ) gr.gridWdg(None, self.imageAvailWdg) self.starPosWdgSet = [] for ii in range(2): letter = ("X", "Y")[ii] starPosWdg = RO.Wdg.FloatEntry( master = self, label = "Star Pos %s" % (letter,), minValue = 0, defValue = 100 * (ii + 1), maxValue = 5000, autoIsCurrent = True, autoSetDefault = True, helpText = "Star %s position in binned pixels" % (letter,), ) self.starPosWdgSet.append(starPosWdg) gr.gridWdg("Star Pos", self.starPosWdgSet, "pix") self.centroidRadWdg = RO.Wdg.IntEntry( master = self, label = "Centroid Rad", minValue = 5, maxValue = 1024, defValue = 10, defMenu = "Default", autoIsCurrent = True, autoSetDefault = True, helpText = "Radius of region to centroid in binned pixels; don't skimp", ) gr.gridWdg(self.centroidRadWdg.label, self.centroidRadWdg, "arcsec", sticky="ew") self.numToFindWdg = RO.Wdg.IntEntry( master = self, label = "Num To Find", minValue = 0, maxValue = 100, defValue = 5, defMenu = "Default", autoIsCurrent = True, autoSetDefault = True, helpText = "Number of stars to find (0 for findstars to fail)", ) gr.gridWdg(self.numToFindWdg.label, self.numToFindWdg) self.centroidOKWdg = RO.Wdg.Checkbutton( master = self, text = "Centroid OK", defValue = True, helpText = "Should centroid command succeed?", ) gr.gridWdg(None, self.centroidOKWdg) self.offsetOKWdg = RO.Wdg.Checkbutton( master = self, text = "Offset OK", defValue = True, helpText = "Should offset command succeed?", ) gr.gridWdg(None, self.offsetOKWdg) self.axesTrackingWdg = RO.Wdg.Checkbutton( master = self, text = "Axes Tracking", defValue = True, callFunc = self.axesTrackingCallback, helpText = "Are axes tracking?", ) gr.gridWdg(None, self.axesTrackingWdg) self.isInstAgileWdg = RO.Wdg.Checkbutton( master = self, text = "Is Curr Inst Agile?", defValue = True, callFunc = self.isInstAgileCallback, helpText = "Is the current instrument Agile?", ) gr.gridWdg(None, self.isInstAgileWdg) self.useWrongCmdrWdg = RO.Wdg.Checkbutton( master = self, text = "Use Wrong Cmdr", defValue = False, helpText = "Should replies be for a different cmdr?", ) gr.gridWdg(None, self.useWrongCmdrWdg) self.useWrongCmdIDWdg = RO.Wdg.Checkbutton( master = self, text = "Use Wrong Cmd ID", defValue = False, helpText = "Should replies be for a different command?", ) gr.gridWdg(None, self.useWrongCmdIDWdg) self.useWrongActorWdg = RO.Wdg.Checkbutton( master = self, text = "Use Wrong Actor", defValue = False, helpText = "Should replies be for a different actor?", ) gr.gridWdg(None, self.useWrongActorWdg) self.grid_columnconfigure(9, weight=1) tccData = ( "inst=Agile", "iimScale=-27784.4, 27569.0", "axisCmdState=Tracking, Tracking, Tracking", ) self.testDispatcher.dispatch(tccData, actor="tcc") self.testDispatcher.dispatch("bin=1", actor="agile") self.pollPendingCmd()
def __init__( self, master, timeRange=3600, numSubplots=1, width=8, height=2, showGrid=True, dateFormat="%H:%M:%S", updateInterval=None, cnvTimeFunc=None, ): """Construct a StripChartWdg with the specified time range Inputs: - master: Tk parent widget - timeRange: range of time displayed (seconds) - width: width of graph in inches - height: height of graph in inches - numSubplots: the number of subplots - showGrid: if True a grid is shown - dateFormat: format for major axis labels, using time.strftime format - updateInterval: now often the time axis is updated (seconds); if None a value is calculated - cnvTimeFunc: a function that takes a POSIX timestamp (e.g. time.time()) and returns matplotlib days; typically an instance of TimeConverter; defaults to TimeConverter(useUTC=False) """ tkinter.Frame.__init__(self, master) self._timeRange = timeRange self._isVisible = self.winfo_ismapped() self._isFirst = True if updateInterval is None: updateInterval = max(0.1, min(5.0, timeRange / 2000.0)) self.updateInterval = float(updateInterval) # print "updateInterval=", self.updateInterval if cnvTimeFunc is None: cnvTimeFunc = TimeConverter(useUTC=False) self._cnvTimeFunc = cnvTimeFunc # how many time axis updates occur before purging old data self._maxPurgeCounter = max(1, int(0.5 + (5.0 / self.updateInterval))) self._purgeCounter = 0 self.figure = matplotlib.figure.Figure(figsize=(width, height), frameon=True) self.canvas = FigureCanvasTkAgg(self.figure, self) self.canvas.get_tk_widget().grid(row=0, column=0, sticky="news") self.canvas.mpl_connect('draw_event', self._handleDrawEvent) self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) bottomSubplot = self.figure.add_subplot(numSubplots, 1, numSubplots) self.subplotArr = [self.figure.add_subplot(numSubplots, 1, n+1, sharex=bottomSubplot) \ for n in range(numSubplots-1)] + [bottomSubplot] if showGrid: for subplot in self.subplotArr: subplot.grid(True) self.xaxis = bottomSubplot.xaxis bottomSubplot.xaxis_date() self.xaxis.set_major_formatter( matplotlib.dates.DateFormatter(dateFormat)) # dictionary of constant line name: (matplotlib Line2D, matplotlib Subplot) self._constLineDict = dict() for subplot in self.subplotArr: subplot._scwLines = [] # a list of contained _Line objects; # different than the standard lines property in that: # - lines contains Line2D objects # - lines contains constant lines as well as data lines subplot._scwBackground = None # background for animation subplot.label_outer( ) # disable axis labels on all but the bottom subplot subplot.set_ylim(auto=True) # set auto scaling for the y axis self.bind("<Map>", self._handleMap) self.bind("<Unmap>", self._handleUnmap) self._timeAxisTimer = Timer() self._updateTimeAxis()
def _nextSecFocus(secFocus, delaySec): keyVarStr = "SecFocus=%0.1f" % (next(secFocus), ) testDispatcher.dispatch(keyVarStr, actor="tcc") Timer(delaySec, _nextSecFocus, secFocus, delaySec)
("invalid text", False), (0, True), ("", True), (False, True), (1, True), (1234567890, True), (1234567890, False), (1.1, True), (1.9, True), (-1.1, True), (-1.9, True), (-0.001, True), (-1.9, False), ] ind = 0 def displayNext(): global ind, testData val = testData[ind] print("\nvalue = %r, isCurrent = %s" % tuple(val)) for wdg in wdgSet: wdg.set(*val) ind += 1 if ind < len(testData): Timer(1.2, displayNext) Timer(1.2, displayNext) root.mainloop()
def _nextSecPiston(secPiston, delaySec): keyVarStr = "SecOrient=%0.1f, 0, 0, 0, 0" % (next(secPiston), ) testDispatcher.dispatch(keyVarStr, actor="tcc") Timer(delaySec, _nextSecPiston, secPiston, delaySec)
def __init__ (self, master=None, **kargs): """Displays information about the axes Inputs: - master master Tk widget -- typically a frame or window """ Tkinter.Frame.__init__(self, master=master, **kargs) self.tccModel = TUI.TCC.TCCModel.getModel() self.prevSounds = [None]*3 # sounds played last time we received AxisCmdState self.prevCtrlStatusOK = [None]*3 self.ctrlBadTime = 0 # time of last "controller bad" sound self._soundTimer = Timer() # magic numbers PosPrec = 1 # number of digits past decimal point PosWidth = 5 + PosPrec # assumes -999.99... degrees is longest field AxisCmdStateWidth = 8 AxisErrCodeWidth = 13 CtrlStatusWidth = 25 self.axisInd = range(len(self.tccModel.axisNames)) # actual axis position widget set self.axePosWdgSet = [ RO.Wdg.FloatLabel( master = self, precision = PosPrec, width = PosWidth, helpText = "Current axis position, as reported by the controller", helpURL = _HelpURL, ) for axis in self.axisInd ] self.tccModel.axePos.addROWdgSet(self.axePosWdgSet) # target axis position widget set self.tccPosWdgSet = [ RO.Wdg.FloatLabel( master = self, precision = PosPrec, width = PosWidth, helpText = "Target axis position", helpURL = _HelpURL, ) for axis in self.axisInd ] self.tccModel.tccPos.addROWdgSet(self.tccPosWdgSet) # TCC status widget set (e.g. tracking or halted) self.axisCmdStateWdgSet = [ RO.Wdg.StrLabel( master = self, width = AxisCmdStateWidth, helpText = "What the TCC is telling the axis to do", helpURL = _HelpURL, anchor = "nw", ) for axis in self.axisInd ] self.tccModel.axisCmdState.addCallback(self.setAxisCmdState) self.tccModel.rotExists.addIndexedCallback(self.setRotExists) # axis error code widet set (why the TCC is not moving the axis) self.axisErrCodeWdgSet = [ RO.Wdg.StrLabel( master = self, width = AxisErrCodeWidth, helpText = "Why the TCC halted the axis", helpURL = _HelpURL, anchor = "nw", ) for axis in self.axisInd ] self.tccModel.axisErrCode.addROWdgSet(self.axisErrCodeWdgSet) # controller status widget set (the status word) self.ctrlStatusWdgSet = [ RO.Wdg.StrLabel( master = self, width = CtrlStatusWidth, helpText = "Status reported by the axis controller", helpURL = _HelpURL, anchor = "nw", ) for axis in self.axisInd ] for axis in self.axisInd: self.tccModel.ctrlStatusSet[axis].addIndexedCallback( RO.Alg.GenericCallback(self.setCtrlStatus, axis), 3) # grid the axis widgets gr = RO.Wdg.Gridder(self, sticky="w") for axis in self.axisInd: unitsLabel1 = Tkinter.Label(self, text=RO.StringUtil.DegStr) unitsLabel2 = Tkinter.Label(self, text=RO.StringUtil.DegStr) if axis == 2: self.rotUnitsLabel1 = unitsLabel1 self.rotUnitsLabel2 = unitsLabel2 gr.gridWdg ( label = self.tccModel.axisNames[axis], dataWdg = ( self.axePosWdgSet[axis], unitsLabel1, self.tccPosWdgSet[axis], unitsLabel2, self.axisCmdStateWdgSet[axis], self.axisErrCodeWdgSet[axis], self.ctrlStatusWdgSet[axis], ) ) # widen rotator commanded state widget # so there's room to display "NotAvailable" # (note that the error code widget will be hidden when this occurs # so the text will not overlap anything). rotCmdWdg = self.axisCmdStateWdgSet[2] rotCmdWdg.grid_configure(columnspan=2) rotCmdWdg["width"] = 12 # allow the last column to grow to fill the available space self.columnconfigure(gr.getMaxNextCol(), weight=1)