def onCopyCalib(self, event): """Creates a new calibration entry for the monitor. Note that the calibration date will reflect the save date/time """ # use time as initial guess at name calibTime = time.localtime() calibTimeStr = monitors.strFromDate(calibTime) # then use dialogue so user can override msg = _translate( 'Name of this calibration (for monitor "%(name)s") will be:)') infoStr = msg % {'name': self.currentMon.name} dlg = wx.TextEntryDialog(self, message=infoStr, defaultValue=calibTimeStr, caption=_translate('Input text')) if dlg.ShowModal() == wx.ID_OK: newCalibName = dlg.GetValue() # update the GUI to reflect new calibration self.currentMon.copyCalib(newCalibName) self.currentMon.setCalibDate(calibTime) self.onChangeCalibSelection(1, newCalibName) self.updateCalibList() self.unSavedMonitor = True dlg.Destroy()
def __init__(self, parent, levels): wx.Dialog.__init__(self, parent, -1, _translate('Recorded luminance values'), style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) pad = 5 panel = wx.Panel(self, -1) mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(self.makeCalibBox(parent=panel, levels=levels), 1, wx.EXPAND | wx.ALL, pad) butBox = wx.BoxSizer(wx.HORIZONTAL) btnOK = wx.Button(panel, wx.ID_OK, _translate(" OK ")) btnOK.SetDefault() btnCANC = wx.Button(panel, wx.ID_CANCEL, _translate(" Cancel ")) butBox.Add(btnOK, 1, wx.BOTTOM | wx.ALIGN_RIGHT, pad) butBox.Add(btnCANC, 1, wx.BOTTOM | wx.RIGHT | wx.ALIGN_RIGHT, pad) mainSizer.Add(butBox, flag=wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, border=10) # finalise panel layout panel.SetAutoLayout(True) panel.SetSizerAndFit(mainSizer) mainSizer.Layout() self.SetSize(self.GetBestSize())
def onNewMon(self, event): # open a dialogue to get the name dlg = wx.TextEntryDialog(self, _translate('New monitor name:'), caption=_translate('Input text')) if dlg.ShowModal() == wx.ID_OK: self.currentMonName = dlg.GetValue() self.ctrlMonList.Append(self.currentMonName) self.ctrlMonList.SetStringSelection(self.currentMonName) self.currentMon = monitors.Monitor( self.currentMonName, verbose=True) self.updateCalibList() self.onChangeCalibSelection(event=1) self.unSavedMonitor = True dlg.Destroy()
def __init__(self, title=_translate('PsychoPy Dialog'), pos=None, size=None, style=None, labelButtonOK=_translate(" OK "), labelButtonCancel=_translate(" Cancel "), screen=-1): global app # avoid recreating for every gui app = ensureQtApp() QtWidgets.QDialog.__init__(self, None, Qt.WindowTitleHint) self.inputFields = [] self.inputFieldTypes = [] self.inputFieldNames = [] self.data = [] self.irow = 0 # QtWidgets.QToolTip.setFont(QtGui.QFont('SansSerif', 10)) # add buttons for OK and Cancel self.buttonBox = QtWidgets.QDialogButtonBox(Qt.Horizontal, parent=self) self.okbutton = QtWidgets.QPushButton(labelButtonOK, parent=self) self.cancelbutton = QtWidgets.QPushButton(labelButtonCancel, parent=self) self.buttonBox.addButton(self.okbutton, QtWidgets.QDialogButtonBox.ActionRole) self.buttonBox.addButton(self.cancelbutton, QtWidgets.QDialogButtonBox.ActionRole) self.okbutton.clicked.connect(self.accept) self.cancelbutton.clicked.connect(self.reject) if style: raise RuntimeWarning("Dlg does not currently support the " "style kwarg.") self.pos = pos self.size = size self.screen = screen # self.labelButtonOK = labelButtonOK # self.labelButtonCancel = labelButtonCancel self.layout = QtWidgets.QGridLayout() self.layout.setColumnStretch(1, 1) self.layout.setSpacing(10) self.layout.setColumnMinimumWidth(1, 250) self.setLayout(self.layout) self.setWindowTitle(title)
def test_set(self): lang = localization.getID('En_US') assert lang == 'en' for lang in ['En_US', 'Ja_JP', 'ja_JP']: setlang = localization.getID(lang) out = _translate(welcome) assert setlang == lang.lower()[:2] assert out == trans[setlang]
def __init__(self, title=_translate('PsychoPy dialogue'), pos=None, size=wx.DefaultSize, style=wx.DEFAULT_DIALOG_STYLE | wx.DIALOG_NO_PARENT, labelButtonOK=_translate(" OK "), labelButtonCancel=_translate(" Cancel ")): style = style | wx.RESIZE_BORDER global app # avoid recreating for every gui app = ensureWxApp() super().__init__(parent=None, id=-1, title=title, style=style) self.inputFields = [] self.inputFieldTypes = [] self.inputFieldNames = [] self.data = [] # prepare a frame in which to hold objects self.sizer = wx.BoxSizer(wx.VERTICAL) # self.addText('') # insert some space at top of dialogue self.pos = pos self.labelButtonOK = labelButtonOK self.labelButtonCancel = labelButtonCancel
def onBtnFindPhotometer(self, event): # safer to get by index, but GetStringSelection will work for # nonlocalized techincal names: photName = self.ctrlPhotomType.GetStringSelection() # not sure how photPort = self.ctrlPhotomPort.GetValue().strip() # [0] == Scan all ports if not photPort or photPort == self._photomChoices[0]: photPort = None elif photPort.isdigit(): photPort = int(photPort) # search all ports self.comPortLabel.SetLabel(_translate('Scanning ports...')) self.Update() self.photom = hardware.findPhotometer(device=photName, ports=photPort) if self.photom is not None and self.photom.OK: self.btnFindPhotometer.Disable() self.btnCalibrateGamma.Enable(True) self.btnTestGamma.Enable(True) if hasattr(self.photom, 'getLastSpectrum'): self.btnCalibrateColor.Enable(True) msg = _translate('%(photomType)s found on %(photomPort)s') self.comPortLabel.SetLabel(msg % {'photomType': self.photom.type, 'photomPort': self.photom.portString}) else: self.comPortLabel.SetLabel(_translate('No photometers found')) self.photom = None # does this device need a dark calibration? if (hasattr(self.photom, 'getNeedsCalibrateZero') and self.photom.getNeedsCalibrateZero()): # prompt user if we need a dark calibration for the device if self.photom.getNeedsCalibrateZero(): dlg = wx.Dialog(self, title=_translate( 'Dark calibration of ColorCAL')) msg = _translate('Your ColorCAL needs to be calibrated first.' ' Please block all light from getting into ' 'the lens and press OK.') while self.photom.getNeedsCalibrateZero(): txt = _translate('Dark calibration of ColorCAL') dlg = dialogs.MessageDialog(self, message=msg, title=txt, type='Info') # info dlg has only an OK button resp = dlg.ShowModal() if resp == wx.ID_CANCEL: self.photom = None self.comPortLabel.SetLabel('') return 0 elif resp == wx.ID_OK: self.photom.calibrateZero() # this failed at least once. Try again. msg = _translate('Try again. Cover the lens fully and ' 'press OK')
def summary(self, items=None): """Return a list of (item, color) for gui display. For non-fatal items """ config = {} for item in items: config[item[0]] = [item[1], item[2], item[3]] # [3] = warn or not green = '#009933' red = '#CC3300' check = u"\u2713 " summary = [(check + _translate('video card drivers'), green)] ofInterest = ('python version', 'available memory', 'openGL version', 'visual sync (refresh)', 'refresh stability (SD)', 'no dropped frames', 'internet access') # ofInterest.append('background processes') for item in ofInterest: if not item in config.keys(): continue # eg, microphone latency if config[item][2]: # warn True summary.append(("X " + _translate(item), red)) else: summary.append((check + _translate(item), green)) return summary
def fileOpenDlg(tryFilePath="", tryFileName="", prompt=_translate("Select file to open"), allowed=None): """A simple dialogue allowing read access to the file system. :parameters: tryFilePath: string default file path on which to open the dialog tryFileName: string default file name, as suggested file prompt: string (default "Select file to open") can be set to custom prompts allowed: string (available since v1.62.01) a string to specify file filters. e.g. "Text files (\*.txt) ;; Image files (\*.bmp \*.gif)" See http://pyqt.sourceforge.net/Docs/PyQt4/qfiledialog.html #getOpenFileNames for further details If tryFilePath or tryFileName are empty or invalid then current path and empty names are used to start search. If user cancels, then None is returned. """ global qtapp # avoid recreating for every gui qtapp = ensureQtApp() if allowed is None: allowed = ("All files (*.*);;" "PsychoPy Data (*.psydat);;" "txt (*.txt *.dlm *.csv);;" "pickled files (*.pickle *.pkl);;" "shelved files (*.shelf)") fdir = os.path.join(tryFilePath, tryFileName) filesToOpen = QtWidgets.QFileDialog.getOpenFileNames(parent=None, caption=prompt, directory=fdir, filter=allowed) if type(filesToOpen) == tuple: # some versions(?) of PyQt return (files, filter) filesToOpen = filesToOpen[0] filesToOpen = [str(fpath) for fpath in filesToOpen if os.path.exists(fpath)] if len(filesToOpen) == 0: return None return filesToOpen
def __init__(self, parent, monitor): self.method = 'auto' self.nPoints = 8 assert isinstance(monitor, monitors.Monitor) self.useBits = monitor.getUseBits() wx.Dialog.__init__(self, parent, -1, _translate('Gamma Calibration'), style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) mainSizer = wx.FlexGridSizer(cols=2, hgap=1, vgap=1) # select method of calib (auto, semi-auto, manual) # todo: make the input tablefor manual method self.methodChoiceBx = wx.Choice(self, -1, choices=['auto', 'semi']) self.methodChoiceBx.SetStringSelection('auto') wx.EVT_CHOICE(self, self.methodChoiceBx.GetId(), self.onMethodChange) self.ctrlUseBits = wx.CheckBox(self, -1, _translate('Use Bits++')) self.ctrlUseBits.SetValue(self.useBits) msg = _translate('Number of calibration points:') self.labelNPoints = wx.StaticText(self, -1, msg) self.ctrlNPoints = intctrl.IntCtrl(self, -1, value=8) msg = _translate('Screen number (primary is 1)') self.labelScrN = wx.StaticText(self, -1, msg) self.ctrlScrN = intctrl.IntCtrl(self, -1, value=1) msg = _translate('Patch size (fraction of screen):') self.labelStimSize = wx.StaticText(self, -1, msg) self.ctrlStimSize = wx.TextCtrl(self, -1, '0.3') pad = 5 mainSizer.Add((0, 0), 1, wx.ALL, pad) mainSizer.Add(self.methodChoiceBx, 1, wx.ALL, pad) mainSizer.Add(self.labelScrN, 1, wx.ALL, pad) mainSizer.Add(self.ctrlScrN, 1, wx.ALL, pad) mainSizer.Add(self.labelNPoints, 1, wx.ALL, pad) mainSizer.Add(self.ctrlNPoints, 1, wx.ALL, pad) mainSizer.Add(self.labelStimSize, 1, wx.ALL, pad) mainSizer.Add(self.ctrlStimSize, 1, wx.ALL, pad) mainSizer.Add((0, 0), 1, wx.ALL, pad) mainSizer.Add(self.ctrlUseBits, 1, wx.ALL, pad) btnOK = wx.Button(self, wx.ID_OK, _translate(" OK ")) btnOK.SetDefault() mainSizer.Add(btnOK, 1, wx.TOP | wx.BOTTOM | wx.ALIGN_RIGHT, pad) btnCANC = wx.Button(self, wx.ID_CANCEL, _translate(" Cancel ")) mainSizer.Add(btnCANC, 1, wx.TOP | wx.BOTTOM | wx.RIGHT | wx.ALIGN_RIGHT, pad) self.Center() # mainSizer.Fit(self) self.SetAutoLayout(True) self.SetSizerAndFit(mainSizer)
def makeCalibBox(self, parent, levels): '''do my best to make a calibration box''' gammaBox = wx.StaticBox(parent, -1, _translate('Luminance Values')) gammaBox.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.NORMAL)) gammaBoxSizer = wx.StaticBoxSizer(gammaBox, wx.VERTICAL) theCols = map(str, levels) self.gammaGrid = SimpleGrid(parent, id=-1, cols=theCols, rows=['lum', 'R', 'G', 'B']) gammaBoxSizer.Add(self.gammaGrid) grid.EVT_GRID_CELL_CHANGE(self.gammaGrid, self.onChangeGammaGrid) gammaBoxSizer.Layout() return gammaBoxSizer
def fileSaveDlg(initFilePath="", initFileName="", prompt=_translate("Select file to save"), allowed=None): """A simple dialogue allowing write access to the file system. (Useful in case you collect an hour of data and then try to save to a non-existent directory!!) :parameters: initFilePath: string default file path on which to open the dialog initFileName: string default file name, as suggested file prompt: string (default "Select file to open") can be set to custom prompts allowed: string A string to specify file filters. e.g. "BMP files (*.bmp)|*.bmp|GIF files (*.gif)|*.gif" See http://www.wxpython.org/docs/api/wx.FileDialog-class.html for further details If initFilePath or initFileName are empty or invalid then current path and empty names are used to start search. If user cancels the None is returned. """ if allowed is None: allowed = "All files (*.*)|*.*" # "txt (*.txt)|*.txt" # "pickled files (*.pickle, *.pkl)|*.pickle" # "shelved files (*.shelf)|*.shelf" global app # avoid recreating for every gui app = ensureWxApp() dlg = wx.FileDialog(None, prompt, initFilePath, initFileName, allowed, wx.SAVE) if dlg.ShowModal() == OK: # get names of images and their directory outName = dlg.GetFilename() outPath = dlg.GetDirectory() dlg.Destroy() # tmpApp.Destroy() # this causes an error message for some reason fullPath = os.path.join(outPath, outName) else: fullPath = None return fullPath
def onDeleteCalib(self, event): calToDel = self.ctrlCalibList.GetStringSelection() # warn user that data will be lost msg = _translate('Are you sure you want to delete this calibration? ' '(cannot be undone)') dlg = dialogs.MessageDialog(parent=self, message=msg, type='Warning') if dlg.ShowModal() == wx.ID_YES: # delete it self.currentMon.delCalib(calToDel) # load most recent calibration instead # this will load calibration "-1" (last calib) self.onChangeCalibSelection(event=None, newCalib=-1) self.updateCalibList() dlg.Destroy()
def onCloseWindow(self, event): if self.unSavedMonitor: # warn user that data will be lost msg = _translate( 'Save changes to monitor settings before quitting?') dlg = dialogs.MessageDialog(self, message=msg, type='Warning') resp = dlg.ShowModal() if resp == wx.ID_CANCEL: return 1 # return before quitting elif resp == wx.ID_YES: # save then quit self.currentMon.saveMon() elif resp == wx.ID_NO: pass # don't save just quit dlg.Destroy() self.onCopyMon() # save current monitor name to clipboard self.Destroy()
def plotSpectra(self, event=None): msg = _translate('%(monName)s %(calibName)s Spectra') figTitle = msg % {'monName': self.currentMonName, 'calibName': self.currentCalibName} plotWindow = PlotFrame(self, 1003, figTitle) figure = Figure(figsize=(5, 5), dpi=80) figureCanvas = FigureCanvas(plotWindow, -1, figure) plt = figure.add_subplot(111) plt.hold('off') nm, spectraRGB = self.currentMon.getSpectra() if nm != None: plt.plot(nm, spectraRGB[0, :], 'r-', linewidth=1.5) plt.hold('on') plt.plot(nm, spectraRGB[1, :], 'g-', linewidth=2) plt.plot(nm, spectraRGB[2, :], 'b-', linewidth=2) figureCanvas.draw() # update the canvas plotWindow.addCanvas(figureCanvas)
def onDeleteMon(self, event): monToDel = self.currentMonName msg = _translate('Are you sure you want to delete all details for %s? ' '(cannot be undone)') dlg = dialogs.MessageDialog(parent=self, message=msg % monToDel, type='Warning') response = dlg.ShowModal() dlg.Destroy() if response == wx.ID_YES: # delete it monitorFileName = os.path.join(monitors.monitorFolder, monToDel + ".calib") os.remove(monitorFileName) self.currentMon = None self.currentMonName = None self.updateMonList() # load most recent calibration instead # this will load calibration "-1" (last calib) self.onChangeMonSelection(event=None) self.updateCalibList()
def fileOpenDlg(tryFilePath="", tryFileName="", prompt=_translate("Select file(s) to open"), allowed=None): """A simple dialogue allowing read access to the file system. :parameters: tryFilePath: string default file path on which to open the dialog tryFileName: string default file name, as suggested file prompt: string (default "Select file to open") can be set to custom prompts allowed: string (available since v1.62.01) a string to specify file filters. e.g. "BMP files (*.bmp)|*.bmp|GIF files (*.gif)|*.gif" See http://www.wxpython.org/docs/api/wx.FileDialog-class.html for further details If tryFilePath or tryFileName are empty or invalid then current path and empty names are used to start search. If user cancels, then None is returned. """ if allowed is None: allowed = ("PsychoPy Data (*.psydat)|*.psydat|" "txt (*.txt,*.dlm,*.csv)|*.txt;*.dlm;*.csv|" "pickled files (*.pickle, *.pkl)|*.pickle|" "shelved files (*.shelf)|*.shelf|" "All files (*.*)|*.*") global app # avoid recreating for every gui app = ensureWxApp() dlg = wx.FileDialog(None, prompt, tryFilePath, tryFileName, allowed, wx.OPEN | wx.FILE_MUST_EXIST | wx.MULTIPLE) if dlg.ShowModal() == OK: # get names of images and their directory fullPaths = dlg.GetPaths() else: fullPaths = None dlg.Destroy() return fullPaths
def onChangeMonSelection(self, event): if self.unSavedMonitor: if self.currentMonName == self.ctrlMonList.GetStringSelection(): # it didnt' really change return 1 # warn user that data will be lost msg = _translate('Save changes to monitor?') dlg = dialogs.MessageDialog(self, msg, type='Warning') resp = dlg.ShowModal() dlg.Destroy() if resp == wx.ID_CANCEL: # revert and return self.ctrlMonList.SetStringSelection(self.currentMonName) return False # return before quitting elif resp == wx.ID_YES: # save then change self.currentMon.saveMon() elif resp == wx.ID_NO: pass # don't save just change self.currentMonName = self.ctrlMonList.GetStringSelection() self.loadMonitor(self.currentMonName)
def makeMenuBar(self): menuBar = wx.MenuBar() fileMenu = wx.Menu() fileMenu.Append(idMenuSave, _translate('Save\tCtrl+S'), _translate('Save the current monitor')) wx.EVT_MENU(self, idMenuSave, self.onSaveMon) _hint = _translate( 'Close Monitor Center (but not other PsychoPy windows)') fileMenu.Append(wx.ID_CLOSE, _translate('Close Monitor Center\tCtrl+W'), _hint) wx.EVT_MENU(self, wx.ID_CLOSE, self.onCloseWindow) menuBar.Append(fileMenu, _translate('&File')) # Edit editMenu = wx.Menu() id = wx.NewId() _hint = _translate("Copy the current monitor's name to clipboard") editMenu.Append(id, _translate('Copy\tCtrl+C'), _hint) wx.EVT_MENU(self, id, self.onCopyMon) menuBar.Append(editMenu, _translate('&Edit')) self.SetMenuBar(menuBar)
def fileSaveDlg(initFilePath="", initFileName="", prompt=_translate("Select file to save"), allowed=None): """A simple dialogue allowing write access to the file system. (Useful in case you collect an hour of data and then try to save to a non-existent directory!!) :parameters: initFilePath: string default file path on which to open the dialog initFileName: string default file name, as suggested file prompt: string (default "Select file to open") can be set to custom prompts allowed: string a string to specify file filters. e.g. "Text files (\*.txt) ;; Image files (\*.bmp \*.gif)" See http://pyqt.sourceforge.net/Docs/PyQt4/qfiledialog.html #getSaveFileName for further details If initFilePath or initFileName are empty or invalid then current path and empty names are used to start search. If user cancels the None is returned. """ if allowed is None: allowed = ("All files (*.*);;" "txt (*.txt);;" "pickled files (*.pickle *.pkl);;" "shelved files (*.shelf)") global qtapp # avoid recreating for every gui qtapp = ensureQtApp() fdir = os.path.join(initFilePath, initFileName) r = QtWidgets.QFileDialog.getSaveFileName(parent=None, caption=prompt, directory=fdir, filter=allowed) return str(r) or None
def OnInit(self): frame = MainFrame(None, _translate('PsychoPy Monitor Center')) frame.Show(True) self.SetTopWindow(frame) return True
def runDiagnostics(self, win, verbose=False): """Return list of (key, val, msg, warn) tuple, set self.warnings All tuple elements will be of <type str>. msg can depend on val; warn==True indicates a concern. Plain text is returned, expected to be used in html <table>. Hyperlinks can be embedded as <a href="..."> """ report = [] # add item tuples in display order # get lots of info and do quick-to-render visual (want 0 frames drop): # for me, grating draw times are: mean 0.53 ms, SD 0.77 ms items = info.RunTimeInfo(win=win, refreshTest='grating', verbose=True, userProcsDetailed=True) totalRAM = items['systemMemTotalRAM'] freeRAM = items['systemMemFreeRAM'] warn = False if freeRAM == 'unknown': if totalRAM != 'unknown': totalRAM = "%.1fG" % (totalRAM / 1024.) txt = _translate( 'could not assess available physical RAM; total %s') msg = txt % totalRAM report.append(('available memory', 'unknown', msg, warn)) else: txt = _translate( 'physical RAM available for configuration test ' '(of %.1fG total)') msg = txt % (totalRAM / 1024.) if freeRAM < 300: # in M txt = _translate( 'Warning: low available physical RAM for ' 'configuration test (of %.1fG total)') msg = txt % (totalRAM / 1024.) warn = True report.append(('available memory', unicode(freeRAM) + 'M', msg, warn)) # ----- PSYCHOPY: ----- warn = False report.append(('PsychoPy', '', '', False)) # not localized report.append(('psychopy', __version__, _translate('avoid upgrading during an experiment'), False)) msg = _translate( 'can be set in <a href="http://www.psychopy.org/general/' 'prefs.html#application-settings-app">Preferences -> App</a>') report.append(('locale', items['systemLocale'], msg, False)) msg = '' if items['pythonVersion'] < '2.5' or items['pythonVersion'] >= '3': msg = _translate('Warning: python 2.6 or 2.7 required; ' '2.5 is not supported but might work') warn = True if 'EPD' in items['pythonFullVersion']: msg += ' Enthought Python Distribution' elif 'PsychoPy2.app' in items['pythonExecutable']: msg += ' (PsychoPy StandAlone)' bits, linkage = platform.architecture() # if not bits.startswith('32'): # msg = 'Warning: 32-bit python required; ' + msg report.append( ('python version', items['pythonVersion'] + ' (%s)' % bits, msg, warn)) warn = False if verbose: msg = '' if items['pythonWxVersion'] < '2.8.10': msg = _translate('Warning: wx 2.8.10 or higher required') warn = True report.append(('wx', items['pythonWxVersion'], '', warn)) report.append( ('pyglet', items['pythonPygletVersion'][:32], '', False)) report.append(('rush', str(items['psychopyHaveExtRush']), _translate('for high-priority threads'), False)) # ----- VISUAL: ----- report.append(('Visual', '', '', False)) warn = False # openGL settings: msg = '' if items['openGLVersion'] < '2.': msg = _translate( 'Warning: <a href="http://www.psychopy.org/general/timing' '/reducingFrameDrops.html?highlight=OpenGL+2.0">OpenGL ' '2.0 or higher is ideal</a>.') warn = True report.append(('openGL version', items['openGLVersion'], msg, warn)) report.append(('openGL vendor', items['openGLVendor'], '', False)) report.append(('screen size', ' x '.join( map(str, items['windowSize_pix'])), '', False)) # report.append(('wait blanking', str(items['windowWaitBlanking']), '', # False)) warn = False msg = '' if not items['windowHaveShaders']: msg = _translate( 'Warning: <a href="http://www.psychopy.org/general/timing' '/reducingFrameDrops.html?highlight=shader">Rendering of' ' complex stimuli will be slow</a>.') warn = True report.append(('have shaders', str( items['windowHaveShaders']), msg, warn)) warn = False msg = _translate( 'during the drifting <a href="http://www.psychopy.org/api/' 'visual/gratingstim.html">GratingStim</a>') if items['windowRefreshTimeMedian_ms'] < 3.3333333: msg = _translate( "Warning: too fast? visual sync'ing with the monitor" " seems unlikely at 300+ Hz") warn = True report.append(('visual sync (refresh)', "%.2f ms/frame" % items['windowRefreshTimeMedian_ms'], msg, warn)) msg = _translate('SD < 0.5 ms is ideal (want low variability)') warn = False if items['windowRefreshTimeSD_ms'] > .5: msg = _translate( 'Warning: the refresh rate has high frame-to-frame ' 'variability (SD > 0.5 ms)') warn = True report.append(('refresh stability (SD)', "%.2f ms" % items['windowRefreshTimeSD_ms'], msg, warn)) # draw 100 dots as a minimally demanding visual test: # first get baseline frame-rate (safe as possible, no drawing): avg, sd, median = visual.getMsPerFrame(win) dots100 = visual.DotStim( win, nDots=100, speed=0.005, dotLife=12, dir=90, coherence=0.2, dotSize=8, fieldShape='circle', autoLog=False) win.recordFrameIntervals = True win.frameIntervals = [] win.flip() for i in xrange(180): dots100.draw() win.flip() msg = _translate( 'during <a href="http://www.psychopy.org/api/visual/' 'dotstim.html">DotStim</a> with 100 random dots') warn = False intervalsMS = np.array(win.frameIntervals) * 1000 nTotal = len(intervalsMS) nDropped = sum(intervalsMS > (1.5 * median)) if nDropped: msg = _translate( 'Warning: could not keep up during <a href="http://' 'www.psychopy.org/api/visual/dotstim.html">DotStim</a>' ' with 100 random dots.') warn = True report.append(('no dropped frames', '%i / %i' % (nDropped, nTotal), msg, warn)) win.recordFrameIntervals = False if verbose: report.append(('openGL max vertices', str(items['openGLmaxVerticesInVertexArray']), '', False)) keyList = ('GL_ARB_multitexture', 'GL_EXT_framebuffer_object', 'GL_ARB_fragment_program', 'GL_ARB_shader_objects', 'GL_ARB_vertex_shader', 'GL_ARB_texture_float', 'GL_ARB_texture_non_power_of_two', 'GL_STEREO') for key in keyList: val = items['openGLext.' + key] # boolean if not val: val = '<strong>' + str(val) + '</strong>' report.append((key, str(val), '', False)) # ----- AUDIO: ----- report.append(('Audio', '', '', False)) msg = '' warn = False if not 'systemPyoVersion' in items: msg = _translate( 'Warning: pyo is needed for sound and microphone.') warn = True items['systemPyoVersion'] = _translate('(missing)') # elif items['systemPyoVersion'] < '0.6.2': # msg = 'pyo 0.6.2 compiled with --no-messages will # suppress start-up messages' report.append(('pyo', items['systemPyoVersion'], msg, warn)) # TO-DO: add microphone + playback as sound test # ----- NUMERIC: ----- report.append(('Numeric', '', '', False)) report.append(('numpy', items['pythonNumpyVersion'], _translate('vector-based (fast) calculations'), False)) report.append(('scipy', items['pythonScipyVersion'], _translate('scientific / numerical'), False)) report.append(('matplotlib', items['pythonMatplotlibVersion'], _translate('plotting; fast contains(), overlaps()'), False)) # ----- SYSTEM: ----- report.append(('System', '', '', False)) report.append(('platform', items['systemPlatform'], '', False)) msg = _translate('for online help, usage statistics, software ' 'updates, and google-speech') warn = False if items['systemHaveInternetAccess'] is not True: items['systemHaveInternetAccess'] = 'False' msg = _translate('Warning: could not connect (no proxy attempted)') warn = True # TO-DO: dlg to query whether to try to auto-detect (can take a # while), or allow manual entry of proxy str, save into prefs val = str(items['systemHaveInternetAccess']) report.append(('internet access', val, msg, warn)) report.append(('auto proxy', str(self.prefs.connections['autoProxy']), _translate('try to auto-detect a proxy if needed; see' ' <a href="http://www.psychopy.org/general' '/prefs.html#connection-settings-connection' 's">Preferences -> Connections</a>'), False)) if not self.prefs.connections['proxy'].strip(): prx = ' --' else: prx = unicode(self.prefs.connections['proxy']) report.append(('proxy setting', prx, _translate('current manual proxy setting from <a ' 'href="http://www.psychopy.org/general/' 'prefs.html#connection-settings-connections' '">Preferences -> Connections</a>'), False)) txt = 'CPU speed test' report.append((txt, "%.3f s" % items['systemTimeNumpySD1000000_sec'], _translate('numpy.std() of 1,000,000 data points'), False)) # TO-DO: more speed benchmarks # - load large image file from disk # - transfer image to GPU # ----- IMPORTS (relevant for developers & non-StandAlone): ----- if verbose: # always False for a real first-run report.append((_translate('Python packages'), '', '', False)) packages = ['PIL', 'openpyxl', 'lxml', 'setuptools', 'pytest', 'sphinx', 'psignifit', 'pyserial', 'pp', 'pynetstation', 'labjack'] if sys.platform == 'win32': packages.append('pywin32') packages.append('winioport') for pkg in packages: try: if pkg == 'PIL': exec('import PIL.Image') ver = PIL.Image.VERSION # elif pkg == 'lxml': # elif pkg == 'pp': exec('import pp; ver = pp.version') elif pkg == 'pynetstation': exec('from psychopy.hardware import egi') ver = 'import ok' elif pkg == 'pyserial': exec('import serial') ver = serial.VERSION elif pkg == 'pywin32': exec('import win32api') ver = 'import ok' else: exec('import ' + pkg) try: ver = eval(pkg + '.__version__') except Exception: ver = 'import ok' report.append((pkg, ver, '', False)) except (ImportError, AttributeError): msg = _translate('could not import package %s') report.append((pkg, ' --', msg % pkg, False)) # rewrite to avoid assumption of locale en_US: self.warnings = list( set([key for key, val, msg, warn in report if warn])) return report
def plotGamma(self, event=None): msg = _translate('%(monName)s %(calibName)s Gamma Functions') figTitle = msg % {'monName': self.currentMonName, 'calibName': self.currentCalibName} plotWindow = PlotFrame(self, 1003, figTitle) figure = Figure(figsize=(5, 5), dpi=80) figureCanvas = FigureCanvas(plotWindow, -1, figure) plt = figure.add_subplot(111) plt.hold('off') gammaGrid = self.currentMon.getGammaGrid() lumsPre = self.currentMon.getLumsPre() levelsPre = self.currentMon.getLevelsPre() lumsPost = self.currentMon.getLumsPost() if lumsPre != None: colors = 'krgb' xxSmooth = monitors.numpy.arange(0, 255.5, 0.5) eq = self.currentMon.getLinearizeMethod() for gun in range(4): # includes lum gamma = gammaGrid[gun, 2] minLum = gammaGrid[gun, 0] maxLum = gammaGrid[gun, 1] if eq <= 2: # plot fitted curve curve = monitors.gammaFun(xxSmooth, minLum, maxLum, gamma, eq=eq, a=None, b=None, k=None) plt.plot(xxSmooth, curve, colors[gun] + '-', linewidth=1.5) if self.currentMon.getLinearizeMethod() == 4: a, b, k = gammaGrid[gun, 3:] # plot fitted curve curve = monitors.gammaFun(xxSmooth, minLum, maxLum, gamma, eq=eq, a=a, b=b, k=k) plt.plot(xxSmooth, curve, colors[gun] + '-', linewidth=1.5) else: pass # polyFit = self.currentMon._gammaInterpolator[gun] # curve = xxSmooth*0.0 # for expon, coeff in enumerate(polyFit): # curve += coeff*xxSmooth**expon # plt.plot(xxSmooth, curve, colors[gun]+'-', linewidth=1.5) # plot POINTS plt.plot(levelsPre, lumsPre[gun, :], colors[gun] + 'o', linewidth=1.5) lumsPost = self.currentMon.getLumsPost() levelsPost = self.currentMon.getLevelsPost() if lumsPost != None: for gun in range(4): # includes lum,r,g,b lums = lumsPost[gun, :] gamma = gammaGrid[gun, 2] gamma = gammaGrid[gun, 2] minLum = min(lums) maxLum = max(lums) # plot CURVE plt.plot([levelsPost[0], levelsPost[-1]], [minLum, maxLum], colors[gun] + '--', linewidth=1.5) # plot POINTS plt.plot(levelsPost, lums, 'o', markerfacecolor='w', markeredgecolor=colors[gun], linewidth=1.5) figureCanvas.draw() # update the canvas plotWindow.addCanvas(figureCanvas)
def __init__(self, fullscr=True, interactive=True, log=True): super(BenchmarkWizard, self).__init__(interactive=interactive) self.firstrun = False self.prefs = prefs self.appName = 'PsychoPy2' self.name = self.appName + _translate(' Benchmark Wizard') dlg = gui.Dlg(title=self.name) dlg.addText('') dlg.addText(_translate('Benchmarking takes ~20-30 seconds to gather')) dlg.addText(_translate('configuration and performance data. Begin?')) dlg.addText('') if interactive: dlg.show() if not dlg.OK: return self._prepare() win = visual.Window(fullscr=fullscr, allowGUI=False, monitor='testMonitor', autoLog=False) # do system info etc first to get fps, add to list later because # it's nicer for benchmark results to appears at top of the report: diagnostics = self.runDiagnostics(win, verbose=True) info = {} for k, v, m, w in diagnostics: # list of tuples --> dict, ignore msg m, warning w info[k] = v fps = 1000. / float(info['visual sync (refresh)'].split()[0]) itemsList = [('Benchmark', '', '', False)] itemsList.append(('benchmark version', '0.1', _translate( 'dots & configuration'), False)) itemsList.append(('full-screen', str(fullscr), _translate('visual window for drawing'), False)) if int(info['no dropped frames'].split('/')[0]) != 0: # eg, "0 / 180" start = 50 # if 100 dots had problems earlier, here start lower else: start = 200 for shape in ('circle', 'square'): # order matters: circle crashes first dotsList = self.runLotsOfDots(win, fieldShape=shape, starting=start, baseline=fps) itemsList.extend(dotsList) # start square where circle breaks down start = int(dotsList[-1][1]) itemsList.extend(diagnostics) win.close() itemsDict = {} for itm in itemsList: if 'proxy setting' in itm[0] or not itm[1]: continue itemsDict[itm[0]] = itm[1].replace('<strong>', '').replace( '</strong>', '').replace(' ', '').replace(' ', '') # present dialog, upload only if opt-in: dlg = gui.Dlg(title=self.name) dlg.addText('') dlg.addText(_translate( 'Benchmark complete! (See the Coder output window.)')) self.htmlReport(itemsList) self.reportPath = os.path.join(self.prefs.paths['userPrefsDir'], 'benchmarkReport.html') self.save() dlg = gui.Dlg(title=self.name) dlg.addText('') dlg.addText(_translate( 'Click OK to view full configuration and benchmark data.')) dlg.addText(_translate('Click Cancel to stay in PsychoPy.')) dlg.addText('') if interactive: dlg.show() if dlg.OK: url = 'file://' + self.reportPath wx.LaunchDefaultBrowser(url)
def htmlReport(self, items=None, fatal=False): """Return an html report given a list of (key, val, msg, warn) items. format triggers: 'Critical issue' in fatal gets highlighted warn == True -> highlight key and val val == msg == '' -> use key as section heading """ imgfile = os.path.join(self.prefs.paths['resources'], 'psychopySplash.png') _head = (u'<html><head><meta http-equiv="Content-Type" ' 'content="text/html; charset=utf-8"></head><body>' + '<a href="http://www.psychopy.org"><img src="%s" ' 'width=396 height=156></a>') self.header = _head % imgfile # self.iconhtml = '<a href="http://www.psychopy.org"><img src="%s" # width=48 height=48></a>' % self.iconfile _foot = _translate('This page was auto-generated by the ' 'PsychoPy configuration wizard on %s') self.footer = ('<center><font size=-1>' + _foot % data.getDateStr(format="%Y-%m-%d, %H:%M") + '</font></center>') htmlDoc = self.header if fatal: # fatal is a list of strings: htmlDoc += ('<h2><font color="red">' + _translate('Configuration problem') + '</font></h2><hr>') for item in fatal: item = item.replace('Critical issue', '<p><strong>') item += _translate('Critical issue') + '</strong>' htmlDoc += item + "<hr>" else: # items is a list of tuples: htmlDoc += ('<h2><font color="green">' + _translate('Configuration report') + '</font></h2>\n') numWarn = len(self.warnings) if numWarn == 0: htmlDoc += _translate('<p>All values seem reasonable (no ' 'warnings, but there might still be ' 'room for improvement).</p>\n') elif numWarn == 1: _warn = _translate('1 suboptimal value was detected</font>, ' 'see details below (%s).</p>\n') htmlDoc += ('<p><font color="red">' + _warn % (self.warnings[0])) elif numWarn > 1: _warn = _translate('%(num)i suboptimal values were detected' '</font>, see details below (%(warn)s).' '</p>\n') htmlDoc += ('<p><font color="red">' + _warn % {'num': numWarn, 'warn': ', '.join(self.warnings)}) htmlDoc += '''<script type="text/javascript"> // Loops through all rows in document and changes display // property of rows with a specific ID // toggle('ok', '') will display all rows // toggle('ok', 'none') hides ok rows, leaving Warning // rows shown function toggle(ID, display_value) { var tr=document.getElementsByTagName('tr'), i; for (i=0;i<tr.length;i++) { if (tr[i].id == ID) tr[i].style.display = display_value; } } </script> <p> <button onClick="toggle('ok', 'none');">''' htmlDoc += _translate('Only show suboptimal values') + \ '</button>' + \ '''<button onClick="toggle('ok', '');">''' + \ _translate('Show all information') + '</button></p>' htmlDoc += _translate('''<p>Resources: Contributed <a href="http://upload.psychopy.org/benchmark/report.html">benchmarks</a> | <a href="http://www.psychopy.org/documentation.html">On-line documentation</a> | Download <a href="http://www.psychopy.org/PsychoPyManual.pdf">PDF manual</a> | <a href="http://groups.google.com/group/psychopy-users">Search the user-group archives</a> </p>''') htmlDoc += '<hr><p></p> <table cellspacing=8 border=0>\n' htmlDoc += ' <tr><td><font size=+1><strong>' + \ _translate('Configuration test</strong> or setting') +\ '</font></td><td><font size=+1><strong>' + _translate('Version or value') +\ '</strong></font></td><td><font size=+1><em>' + \ _translate('Notes') + '</em></font></td>' for (key, val, msg, warn) in items: if val == msg == '': key = '<font color="darkblue" size="+1"><strong>' + \ _translate(key) + '</strong></font>' else: key = ' ' + _translate(key) if warn: key = '<font style=color:red><strong>' + \ _translate(key) + '</strong></font>' val = '<font style=color:red><strong>' + val + '</strong></font>' id = 'Warning' else: id = 'ok' htmlDoc += ' <tr id="%s"><td>' % id htmlDoc += key + '</td><td>' + val + '</td><td><em>' + msg + '</em></td></tr>\n' htmlDoc += ' </table><hr>' htmlDoc += self.footer if not fatal and numWarn: htmlDoc += """<script type="text/javascript">toggle('ok', 'none'); </script>""" htmlDoc += '</body></html>' self.reportText = htmlDoc
def __init__(self, firstrun=False, interactive=True, log=True): """Check drivers, show GUIs, run diagnostics, show report. """ super(ConfigWizard, self).__init__() self.firstrun = firstrun self.prefs = prefs self.appName = 'PsychoPy2' self.name = self.appName + _translate(' Configuration Wizard') self.reportPath = os.path.join( self.prefs.paths['userPrefsDir'], 'firstrunReport.html') # self.iconfile = os.path.join(self.prefs.paths['resources'], # 'psychopy.png') # dlg.SetIcon(wx.Icon(self.iconfile, wx.BITMAP_TYPE_PNG)) # no error # but no effect dlg = gui.Dlg(title=self.name) dlg.addText('') if firstrun: dlg.addText(_translate("Welcome to PsychoPy2!"), color='blue') dlg.addText('') dlg.addText(_translate("It looks like you are running PsychoPy " "for the first time.")) dlg.addText(_translate("This wizard will help you get started " "quickly and smoothly.")) else: dlg.addText(_translate("Welcome to the configuration wizard.")) # test for fatal configuration errors: fatalItemsList = [] cardInfo = gl_info.get_renderer().replace('OpenGL Engine', '').strip() if not driversOkay(): dlg.addText('') dlg.addText(_translate("The first configuration check is your " "video card's drivers. The current"), color='red') dlg.addText(_translate("drivers cannot support PsychoPy, so " "you'll need to update the drivers."), color='red') msg = _translate("""<p>Critical issue:\n</p><p>Your video card (%(card)s) has drivers that cannot support the high-performance features that PsychoPy depends on. Fortunately, it's typically free and straightforward to get new drivers directly from the manufacturer.</p> <p><strong>To update the drivers:</strong> <li> You'll need administrator privileges. <li> On Windows, don't use the windows option to check for updates - it can report that there are no updates available. <li> If your card is made by NVIDIA, go to <a href="http://www.nvidia.com/Drivers">the NVIDIA website</a> and use the 'auto detect' option. Try here for <a href="http://support.amd.com/">ATI / Radeon drivers</a>. Or try <a href="http://www.google.com/search?q=download+drivers+%(card2)s"> this google search</a> [google.com]. <li> Download and install the driver. <li> Reboot the computer. <li> Restart PsychoPy.</p> <p>If you updated the drivers and still get this message, you'll need a different video card to use PsychoPy. Click <a href="http://www.psychopy.org/installation.html#recommended-hardware">here for more information</a> [psychopy.org].</p> """) fatalItemsList.append(msg % {'card': cardInfo, 'card2': cardInfo.replace(' ', '+')}) if not cardOkay(): msg = _translate("""<p>Critical issue:\n</p>""") msg += cardInfo fatalItemsList.append(msg) pass # other fatal conditions? append a 'Critical issue' msg to itemsList if not fatalItemsList: dlg.addText(_translate("We'll go through a series of configura" "tion checks in about 10 seconds. ")) dlg.addText('') if firstrun: # explain things more dlg.addText(_translate('Note: The display will switch to ' 'full-screen mode and will ')) dlg.addText(_translate("then switch back. You don't need " "to do anything.")) dlg.addText(_translate('Optional: For best results, please quit' ' all email programs, web-browsers, ')) dlg.addText(_translate( 'Dropbox, backup or sync services, and others.')) dlg.addText('') dlg.addText(_translate('Click OK to start, or Cancel to skip.')) if not self.firstrun: dlg.addField(label=_translate('Full details'), initial=self.prefs.app['debugMode']) else: dlg.addText('') dlg.addText(_translate( 'Click OK for more information, or Cancel to skip.')) # show the first dialog: dlg.addText('') if interactive: dlg.show() if fatalItemsList: self.htmlReport(fatal=fatalItemsList) self.save() # user ends up in browser: url = 'file://' + self.reportPath if interactive: wx.LaunchDefaultBrowser(url) return if interactive and not dlg.OK: return # no configuration tests run # run the diagnostics: verbose = interactive and not self.firstrun and dlg.data[0] win = visual.Window(fullscr=interactive, allowGUI=False, monitor='testMonitor', autoLog=log) itemsList = self.runDiagnostics(win, verbose) # sets self.warnings win.close() self.htmlReport(itemsList) self.save() # display summary & options: dlg = gui.Dlg(title=self.name) dlg.addText('') dlg.addText(_translate('Configuration testing complete!')) summary = self.summary(items=itemsList) numWarn = len(self.warnings) if numWarn == 0: msg = _translate('All values seem reasonable (no warnings).') elif numWarn == 1: txt = _translate('1 suboptimal value was detected (%s)') msg = txt % self.warnings[0] else: txt = _translate( '%(num)i suboptimal values were detected (%(warn)s, ...)') msg = txt % {'num': len(self.warnings), 'warn': self.warnings[0]} dlg.addText(msg) for item in summary: dlg.addText(item[0], item[1]) # (key, color) dlg.addText('') dlg.addText(_translate( 'Click OK for full details (will open in a web-browser),')) dlg.addText(_translate('or Cancel to stay in PsychoPy.')) dlg.addText('') if interactive: dlg.show() if dlg.OK: url = 'file://' + self.reportPath wx.LaunchDefaultBrowser(url) return
def __init__(self, parent, title): # create a default monitor with no name self.currentMon = monitors.Monitor('', verbose=False) self.currentMonName = None # use to test if monitor is placeholder self.currentCalibName = None self.unSavedMonitor = False self.comPort = 1 self.photom = None # start building the frame wx.Frame.__init__(self, parent, -1, title, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE) self.makeMenuBar() if NOTEBOOKSTYLE: # make the notebook self.noteBook = wx.Notebook(self, -1) # add the info page self.infoPanel = wx.Panel(self.noteBook, -1) self.noteBook.AddPage(self.infoPanel, _translate('Monitor Info')) infoSizer = wx.BoxSizer(wx.HORIZONTAL) infoSizer.Add(self.makeAdminBox(self.infoPanel), 1, wx.EXPAND) infoSizer.Add(self.makeInfoBox(self.infoPanel), 1, wx.EXPAND) self.infoPanel.SetAutoLayout(True) self.infoPanel.SetSizerAndFit(infoSizer) # add the calibration page self.calibPanel = wx.Panel(self.noteBook, -1) self.noteBook.AddPage(self.calibPanel, _translate('Calibration')) calibSizer = self.makeCalibBox(self.calibPanel) self.calibPanel.SetAutoLayout(True) self.calibPanel.SetSizerAndFit(calibSizer) self.noteBookSizer.Layout() self.noteBookSizer.Fit(self) else: # just one page self.infoPanel = wx.Panel(self, -1) mainSizer = wx.BoxSizer(wx.HORIZONTAL) leftSizer = wx.BoxSizer(wx.VERTICAL) rightSizer = wx.BoxSizer(wx.VERTICAL) _style = wx.EXPAND | wx.ALL leftSizer.Add(self.makeAdminBox(self.infoPanel), 1, _style, 2) leftSizer.Add(self.makeInfoBox(self.infoPanel), 1, _style, 2) rightSizer.Add(self.makeCalibBox(self.infoPanel), 1, _style, 2) # mainSizer.Add(leftSizer, 1, _style, 2) mainSizer.Add(rightSizer, 1, _style, 2) # finalise panel layout mainSizer.Layout() self.infoPanel.SetAutoLayout(True) self.infoPanel.SetSizerAndFit(mainSizer) # if wx version 2.5+: self.SetSize(self.GetBestSize()) # self.CreateStatusBar() # self.SetStatusText("Maybe put tooltips down here one day") if os.path.isfile('psychopy.ico'): try: self.SetIcon(wx.Icon('psychopy.ico', wx.BITMAP_TYPE_ICO)) except Exception: pass wx.EVT_CLOSE(self, self.onCloseWindow) self.updateMonList()
def makeAdminBox(self, parent): # make the box for the controls boxLabel = wx.StaticBox(parent, -1, _translate('Choose Monitor')) boxLabel.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.NORMAL)) adminBox = wx.StaticBoxSizer(boxLabel) # build the controls self.ctrlMonList = wx.ListBox(parent, idCtrlMonList, choices=['iiyama571', 'sonyG500'], size=(350, 100)) wx.EVT_LISTBOX(self, idCtrlMonList, self.onChangeMonSelection) monButtonsBox = wx.BoxSizer(wx.VERTICAL) self.btnNewMon = wx.Button(parent, idBtnNewMon, _translate('New...')) wx.EVT_BUTTON(self, idBtnNewMon, self.onNewMon) monButtonsBox.Add(self.btnNewMon) self.btnNewMon.SetToolTipString( _translate("Create a new monitor")) self.btnSaveMon = wx.Button(parent, idBtnSaveMon, _translate('Save')) wx.EVT_BUTTON(self, idBtnSaveMon, self.onSaveMon) monButtonsBox.Add(self.btnSaveMon) msg = _translate("Save all calibrations for this monitor") self.btnSaveMon.SetToolTipString(msg) self.btnDeleteMon = wx.Button(parent, idBtnDeleteMon, _translate('Delete')) wx.EVT_BUTTON(self, idBtnDeleteMon, self.onDeleteMon) monButtonsBox.Add(self.btnDeleteMon) msg = _translate("Delete this monitor entirely") self.btnDeleteMon.SetToolTipString(msg) self.ctrlCalibList = wx.ListBox(parent, idCtrlCalibList, choices=[''], size=(350, 100)) wx.EVT_LISTBOX(self, idCtrlCalibList, self.onChangeCalibSelection) calibButtonsBox = wx.BoxSizer(wx.VERTICAL) self.btnCopyCalib = wx.Button(parent, idBtnCopyCalib, _translate('Copy...')) wx.EVT_BUTTON(self, idBtnCopyCalib, self.onCopyCalib) calibButtonsBox.Add(self.btnCopyCalib) msg = _translate("Creates a new calibration entry for this monitor") self.btnCopyCalib.SetToolTipString(msg) self.btnDeleteCalib = wx.Button( parent, idBtnDeleteCalib, _translate('Delete')) wx.EVT_BUTTON(self, idBtnDeleteCalib, self.onDeleteCalib) calibButtonsBox.Add(self.btnDeleteCalib) msg = _translate("Remove this calibration entry (finalized when " "monitor is saved)") self.btnDeleteCalib.SetToolTipString(msg) # add controls to box adminBoxMainSizer = wx.FlexGridSizer(cols=2, hgap=6, vgap=6) adminBoxMainSizer.AddMany([(1, 10), (1, 10), # 2 empty boxes 1x10pix self.ctrlMonList, monButtonsBox, self.ctrlCalibList, calibButtonsBox]) adminBox.Add(adminBoxMainSizer) return adminBox
def makeInfoBox(self, parent): # create the box infoBox = wx.StaticBox(parent, -1, _translate('Monitor Info')) infoBox.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.NORMAL)) infoBoxSizer = wx.StaticBoxSizer(infoBox, wx.VERTICAL) # scr distance labelScrDist = wx.StaticText(parent, -1, _translate("Screen Distance (cm):"), style=wx.ALIGN_RIGHT) self.ctrlScrDist = wx.TextCtrl(parent, idCtrlScrDist, "") wx.EVT_TEXT(self, idCtrlScrDist, self.onChangeScrDist) # scr width labelScrWidth = wx.StaticText(parent, -1, _translate("Screen Width (cm):"), style=wx.ALIGN_RIGHT) self.ctrlScrWidth = wx.TextCtrl(parent, idCtrlScrWidth, "") wx.EVT_TEXT(self, idCtrlScrWidth, self.onChangeScrWidth) # scr pixels _size = _translate("Size (pixels; Horiz,Vert):") labelScrPixels = wx.StaticText(parent, -1, _size, style=wx.ALIGN_RIGHT) self.ctrlScrPixHoriz = wx.TextCtrl(parent, -1, "", size=(50, 20)) wx.EVT_TEXT(self, self.ctrlScrPixHoriz.GetId(), self.onChangeScrPixHoriz) self.ctrlScrPixVert = wx.TextCtrl(parent, -1, '', size=(50, 20)) wx.EVT_TEXT(self, self.ctrlScrPixVert.GetId(), self.onChangeScrPixVert) ScrPixelsSizer = wx.BoxSizer(wx.HORIZONTAL) ScrPixelsSizer.AddMany([self.ctrlScrPixHoriz, self.ctrlScrPixVert]) # date labelCalibDate = wx.StaticText(parent, -1, _translate("Calibration Date:"), style=wx.ALIGN_RIGHT) self.ctrlCalibDate = wx.TextCtrl(parent, idCtrlCalibDate, "", size=(150, 20)) self.ctrlCalibDate.Disable() # notes labelCalibNotes = wx.StaticText(parent, -1, _translate("Notes:"), style=wx.ALIGN_RIGHT) self.ctrlCalibNotes = wx.TextCtrl(parent, idCtrlCalibNotes, "", size=(150, 150), style=wx.TE_MULTILINE) wx.EVT_TEXT(self, idCtrlCalibNotes, self.onChangeCalibNotes) # bits++ self.ctrlUseBits = wx.CheckBox(parent, -1, _translate('Use Bits++')) wx.EVT_CHECKBOX(self, self.ctrlUseBits.GetId(), self.onChangeUseBits) infoBoxGrid = wx.FlexGridSizer(cols=2, hgap=6, vgap=6) infoBoxGrid.AddMany([ (1, 10), (1, 10), # a pair of empty boxes each 1x10pix (1, 10), self.ctrlUseBits, labelScrDist, self.ctrlScrDist, labelScrPixels, ScrPixelsSizer, labelScrWidth, self.ctrlScrWidth, labelCalibDate, self.ctrlCalibDate ]) infoBoxGrid.Layout() infoBoxSizer.Add(infoBoxGrid) # put the notes box below the main grid sizer infoBoxSizer.Add(labelCalibNotes) infoBoxSizer.Add(self.ctrlCalibNotes, 1, wx.EXPAND) return infoBoxSizer
def makeCalibBox(self, parent): boxLabel = wx.StaticBox(parent, -1, _translate('Calibration')) boxLabel.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.NORMAL)) calibBox = wx.StaticBoxSizer(boxLabel) photometerBox = wx.FlexGridSizer(cols=2, hgap=6, vgap=6) # com port entry number self.comPortLabel = wx.StaticText(parent, -1, " ", size=(150, 20)) # photometer button # photom type choices should not need localization: _choices = list([p.longName for p in hardware.getAllPhotometers()]) self.ctrlPhotomType = wx.Choice(parent, -1, name="Type:", choices=_choices) _ports = list(hardware.getSerialPorts()) self._photomChoices = [_translate("Scan all ports")] + _ports _size = self.ctrlPhotomType.GetSize() + [0, 5] self.ctrlPhotomPort = wx.ComboBox(parent, -1, name="Port:", value=self._photomChoices[0], choices=self._photomChoices, size=_size) # wx.EVT_CHOICE(self, self.ctrlPhotomType.GetId(), # self.onChangePhotomType) # not needed? self.btnFindPhotometer = wx.Button(parent, -1, _translate("Get Photometer")) wx.EVT_BUTTON(self, self.btnFindPhotometer.GetId(), self.onBtnFindPhotometer) # gamma controls self.btnCalibrateGamma = wx.Button( parent, -1, _translate("Gamma Calibration...")) wx.EVT_BUTTON(self, self.btnCalibrateGamma.GetId(), self.onCalibGammaBtn) self.btnTestGamma = wx.Button( parent, -1, _translate("Gamma Test...")) self.btnTestGamma.Enable(False) # color controls wx.EVT_BUTTON(self, self.btnTestGamma.GetId(), self.onCalibTestBtn) self.btnCalibrateColor = wx.Button( parent, -1, _translate("Chromatic Calibration...")) self.btnCalibrateColor.Enable(False) wx.EVT_BUTTON(self, self.btnCalibrateColor.GetId(), self.onCalibColorBtn) self.btnPlotGamma = wx.Button( parent, -1, _translate("Plot gamma")) wx.EVT_BUTTON(self, self.btnPlotGamma.GetId(), self.plotGamma) self.btnPlotSpectra = wx.Button( parent, -1, _translate("Plot spectra")) wx.EVT_BUTTON(self, self.btnPlotSpectra.GetId(), self.plotSpectra) photometerBox.AddMany([self.ctrlPhotomType, self.btnFindPhotometer, self.ctrlPhotomPort, (0, 0), self.comPortLabel, (0, 0), self.btnCalibrateGamma, (0, 0), self.btnTestGamma, self.btnPlotGamma, self.btnCalibrateColor, self.btnPlotSpectra]) # ----GAMMA------------ # calibration grid gammaBox = wx.StaticBox(parent, -1, _translate('Linearization')) gammaBox.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.NORMAL)) gammaBoxSizer = wx.StaticBoxSizer(gammaBox, wx.VERTICAL) # don't localize the choices _choices = ['easy: a+kx^g', 'full: a+(b+kx)^g'] self.choiceLinearMethod = wx.Choice(parent, -1, name='formula:', choices=_choices) if self.currentMon.getLinearizeMethod() == 4: self.choiceLinearMethod.SetSelection(1) else: self.choiceLinearMethod.SetSelection(0) wx.EVT_CHOICE(self, self.choiceLinearMethod.GetId(), self.onChangeLinearMethod) gammaBoxSizer.Add(self.choiceLinearMethod, 1, wx.ALL, 2) self.gammaGrid = SimpleGrid(parent, id=-1, cols=['Min', 'Max', 'Gamma', 'a', 'b', 'k'], rows=['lum', 'R', 'G', 'B']) gammaBoxSizer.Add(self.gammaGrid) grid.EVT_GRID_CELL_CHANGE(self.gammaGrid, self.onChangeGammaGrid) gammaBoxSizer.Layout() # LMS grid LMSbox = wx.StaticBox(parent, -1, 'LMS->RGB') LMSboxSizer = wx.StaticBoxSizer(LMSbox, wx.VERTICAL) self.LMSgrid = SimpleGrid(parent, id=-1, cols=['L', 'M', 'S'], rows=['R', 'G', 'B']) LMSboxSizer.Add(self.LMSgrid) LMSboxSizer.Layout() grid.EVT_GRID_CELL_CHANGE(self.LMSgrid, self.onChangeLMSgrid) # DKL grid DKLbox = wx.StaticBox(parent, -1, 'DKL->RGB') DKLboxSizer = wx.StaticBoxSizer(DKLbox, wx.VERTICAL) self.DKLgrid = SimpleGrid(parent, id=-1, cols=['Lum', 'L-M', 'L+M-S'], rows=['R', 'G', 'B']) DKLboxSizer.Add(self.DKLgrid) DKLboxSizer.Layout() grid.EVT_GRID_CELL_CHANGE(self.DKLgrid, self.onChangeDKLgrid) calibBoxMainSizer = wx.BoxSizer(wx.VERTICAL) calibBoxMainSizer.AddMany([photometerBox, gammaBoxSizer, LMSboxSizer, DKLboxSizer]) calibBoxMainSizer.Layout() if NOTEBOOKSTYLE: return calibBoxMainSizer else: # put the main sizer into a labeled box calibBox.Add(calibBoxMainSizer) return calibBox