def fetchPsychoPy(self, v='latest'): msg = _translate("Attempting to fetch PsychoPy %s...") self.statusMessage.SetLabel(msg % self.latest['version']) info = "" if v == 'latest': v = self.latest['version'] # open page URL = "https://github.com/psychopy/psychopy/releases/download/%s/PsychoPy-%s.zip" page = urllib.request.urlopen(URL % v) # download in chunks so that we can monitor progress and abort mid-way chunk = 4096 read = 0 fileSize = int(page.info()['Content-Length']) buffer = io.StringIO() self.progressBar.SetRange(fileSize) while read < fileSize: ch = page.read(chunk) buffer.write(ch) read += chunk self.progressBar.SetValue(read) txt = _translate( "Fetched %(done)i of %(total)i kb of PsychoPy-%(version)s.zip") msg = txt % {'done': read // 1000, 'total': fileSize // 1000, 'version': v} self.statusMessage.SetLabel(msg) self.Update() info += _translate('Successfully downloaded PsychoPy-%s.zip') % v page.close() zfile = zipfile.ZipFile(buffer) # buffer.close() return zfile, info
def check(self, parent): """checks namespace, return error-msg (str), enable (bool) """ control = self.GetWindow() newName = control.GetValue() msg, OK = '', True # until we find otherwise if newName == '': msg = _translate("Missing name") OK = False else: namespace = parent.frame.exp.namespace used = namespace.exists(newName) sameAsOldName = bool(newName == parent.params['name'].val) if used and not sameAsOldName: msg = _translate("That name is in use (by %s). Try another name.") % used OK = False elif not namespace.isValid(newName): # valid as a var name msg = _translate("Name must be alpha-numeric or _, no spaces") OK = False # warn but allow, chances are good that its actually ok elif namespace.isPossiblyDerivable(newName): msg = _translate(namespace.isPossiblyDerivable(newName)) OK = True parent.warningsDict['name'] = msg return msg, OK
def __init__(self, parent, ID, app): """Latest is optional extra. If not given it will be fetched. """ self.app = app # get latest version info if poss if app.updater in [False, None]: # user has turned off check for updates in prefs so check now app.updater = updater = Updater(app=self.app) # don't need a warning - we'll provide one ourselves self.latest = updater.getLatestInfo(warnMsg=False) else: self.latest = app.updater.latest self.runningVersion = app.updater.runningVersion wx.Dialog.__init__(self, parent, ID, title=_translate('PsychoPy Updates'), size=(100, 200)) mainSizer = wx.BoxSizer(wx.VERTICAL) # set the actual content of status msg later in self.updateStatus() self.statusMessage = wx.StaticText( self, -1, "msg", style=wx.ALIGN_CENTER) mainSizer.Add(self.statusMessage, flag=wx.EXPAND | wx.ALL, border=5) # ctrls for auto-update from web msg = _translate(" Auto-update (will fetch latest version)") self.useLatestBtn = wx.RadioButton(self, -1, msg, style=wx.RB_GROUP) self.Bind(wx.EVT_RADIOBUTTON, self.onRadioSelect, self.useLatestBtn) self.progressBar = wx.Gauge(self, -1, 100, size=(250, 25)) mainSizer.Add(self.useLatestBtn, flag=wx.ALIGN_LEFT | wx.ALL, border=5) mainSizer.Add(self.progressBar, flag=wx.EXPAND | wx.ALL, border=5) # ctrls for updating from specific zip file msg = _translate(" Use zip file below (download a PsychoPy release " "file ending .zip)") self.useZipBtn = wx.RadioButton(self, -1, msg) self.Bind(wx.EVT_RADIOBUTTON, self.onRadioSelect, self.useZipBtn) self.fileBrowseCtrl = wx.lib.filebrowsebutton.FileBrowseButton( self, -1, size=(450, -1), changeCallback=self.onFileBrowse, fileMask='*.zip') mainSizer.Add(self.useZipBtn, flag=wx.ALIGN_LEFT | wx.ALL, border=5) mainSizer.Add(self.fileBrowseCtrl, flag=wx.ALIGN_LEFT | wx.ALL, border=5) # ctrls for buttons (install/cancel) self.installBtn = wx.Button(self, -1, _translate('Install')) self.Bind(wx.EVT_BUTTON, self.onInstall, self.installBtn) self.installBtn.SetDefault() self.cancelBtn = wx.Button(self, -1, _translate('Close')) self.Bind(wx.EVT_BUTTON, self.onCancel, self.cancelBtn) btnSizer = wx.BoxSizer(wx.HORIZONTAL) btnSizer.Add(self.installBtn, flag=wx.ALIGN_RIGHT) btnSizer.Add(self.cancelBtn, flag=wx.ALIGN_RIGHT | wx.LEFT, border=5) mainSizer.Add(btnSizer, flag=wx.ALIGN_RIGHT | wx.ALL, border=5) self.SetSizerAndFit(mainSizer) self.SetAutoLayout(True) # positioning and sizing self.updateStatus() self.Center() self.ShowModal()
def onLogin(self, event): """ Check credentials and login """ if not havePyosf: dialogs.MessageDialog(parent=self.parent, type='Warning', title=_translate("pyosf not found"), message=_translate("You need pyosf to " "log in to Open Science Framework"), ) return None username = self.username.GetValue() pword = self.password.GetValue() rememberMe = bool(self.rememberMe.GetValue()) try: session = pyosf.Session(username=username, password=pword, remember_me=rememberMe) self.app.osf_session = session self.updateStatus(_translate("Successful authentication"), color=(0, 170, 0)) time.sleep(0.5) self.Destroy() except pyosf.AuthError: self.updateStatus(_translate("Failed to Authenticate. " "Check username/password"), color=(255, 0, 0))
def setPrefsFromCtrls(self): # extract values, adjust as needed: # a) strip() to remove whitespace # b) case-insensitive match for Cmd+ at start of string # c) reverse-map locale display names to canonical names (ja_JP) re_cmd2ctrl = re.compile('^Cmd\+', re.I) for sectionName in self.prefsCfg: for prefName in self.prefsSpec[sectionName]: if prefName in ['version']: # any other prefs not to show? continue ctrlName = sectionName + '.' + prefName ctrl = self.ctrls[ctrlName] thisPref = ctrl.getValue() # remove invisible trailing whitespace: if hasattr(thisPref, 'strip'): thisPref = thisPref.strip() # regularize the display format for keybindings if sectionName == 'keyBindings': thisPref = thisPref.replace(' ', '') thisPref = '+'.join([part.capitalize() for part in thisPref.split('+')]) if platform.system() == 'Darwin': # key-bindings were displayed as 'Cmd+O', revert to # 'Ctrl+O' internally thisPref = re_cmd2ctrl.sub('Ctrl+', thisPref) self.prefsCfg[sectionName][prefName] = thisPref # make sure list values are converted back to lists (from str) if self.prefsSpec[sectionName][prefName].startswith('list'): try: # if thisPref is not a null string, do eval() to get a # list. if thisPref == '' or type(thisPref) == list: newVal = thisPref else: newVal = eval(thisPref) except Exception: # if eval() failed, show warning dialog and return try: pLabel = _localized[prefName] sLabel = _localized[sectionName] except Exception: pLabel = prefName sLabel = sectionName txt = _translate( 'Invalid value in "%(pref)s" ("%(section)s" Tab)') msg = txt % {'pref': pLabel, 'section': sLabel} title = _translate('Error') warnDlg = dialogs.MessageDialog(parent=self, message=msg, type='Info', title=title) resp = warnDlg.ShowModal() return if type(newVal) != list: self.prefsCfg[sectionName][prefName] = [newVal] else: self.prefsCfg[sectionName][prefName] = newVal self.app.prefs.saveUserPrefs() # includes a validation
def save(self, event=None): """save header + row x col data to a pickle file """ self.getData(True) # update self.data adjustedNames = False for i, paramName in enumerate(self.data[0]): newName = paramName # ensure its legal as a var name, including namespace check: if self.parent: msg, enable = self.parent._checkName(name=paramName) if msg: # msg not empty means a namespace issue newName = self.parent.exp.namespace.makeValid( paramName, prefix='param') adjustedNames = True elif not valid_var_re.match(paramName): msg, enable = _translate( "Name must be alpha-numeric or _, no spaces"), False newName = _nonalphanumeric_re.sub('_', newName) adjustedNames = True else: msg, enable = "", True # try to ensure its unique: while newName in self.data[0][:i]: adjustedNames = True newName += 'x' # might create a namespace conflict? self.data[0][i] = newName self.header[i].SetValue(newName) # displayed value if adjustedNames: self.tmpMsg.SetLabel(_translate( 'Param name(s) adjusted to be legal. Look ok?')) return False if hasattr(self, 'fileName') and self.fileName: fname = self.fileName else: self.newFile = True fname = self.defaultFileName if self.newFile or not os.path.isfile(fname): fullPath = gui.fileSaveDlg(initFilePath=os.path.split(fname)[0], initFileName=os.path.basename(fname), allowed="Pickle files (*.pkl)|*.pkl") else: fullPath = fname if fullPath: # None if user canceled if not fullPath.endswith('.pkl'): fullPath += '.pkl' f = open(fullPath, 'w') pickle.dump(self.data, f) f.close() self.fileName = fullPath self.newFile = False # ack, sometimes might want relative path if self.parent: self.parent.conditionsFile = fullPath return True
def onOpenFile(self, event): """Open project file from dialog """ dlg = wx.FileDialog(parent=None, message=_translate("Open local project file"), style=wx.FD_OPEN, wildcard=_translate( "Project files (*.psyproj)|*.psyproj")) if dlg.ShowModal() == wx.ID_OK: projFile = dlg.GetPath() self.openProj(projFile)
def getProject(filename): """Will try to find (locally synced) pavlovia Project for the filename """ try: git haveGit = True except ImportError: haveGit = False if not haveGit: logging.error( "You need to install git to connect with Pavlovia projects") return None gitRoot = getGitRoot(filename) if gitRoot in knownProjects: return knownProjects[gitRoot] elif gitRoot: # Existing repo but not in our knownProjects. Investigate logging.info("Investigating repo at {}".format(gitRoot)) localRepo = git.Repo(gitRoot) proj = None for remote in localRepo.remotes: for url in remote.urls: if "gitlab.pavlovia.org/" in url: namespaceName = url.split('gitlab.pavlovia.org/')[1] namespaceName = namespaceName.replace('.git', '') pavSession = getCurrentSession() if pavSession.user: proj = pavSession.getProject(namespaceName, repo=localRepo) if proj.pavlovia == 0: logging.warning( _translate( "We found a repository pointing to {} " "but ") + _translate("no project was found there (" "deleted?)") .format(url)) else: logging.warning( _translate( "We found a repository pointing to {} " "but ") + _translate( "no user is logged in for us to check " "it") .format(url)) return proj if proj == None: logging.warning("We found a repository at {} but it " "doesn't point to gitlab.pavlovia.org. " "You could create that as a remote to " "sync from PsychoPy.".format(gitRoot))
def getLatestInfo(self, warnMsg=False): # open page latest = getLatestVersionInfo() if latest == -1: m1 = _translate("Couldn't connect to psychopy.org to check for " "updates. \n") m2 = _translate("Check internet settings (and proxy setting in " "PsychoPy Preferences).") confirmDlg = dialogs.MessageDialog( parent=None, message=m1 + m2, type='Info', title=_translate('PsychoPy updates')) confirmDlg.ShowModal() return latest
def onInsertRoutineSelect(self, event): """User has selected a routine to be entered so bring up the entrypoint marker and await mouse button press. see self.insertRoutine() for further info """ self.mode = 'routine' self.btnInsertRoutine.SetLabel(_translate('CANCEL Insert')) self.btnInsertRoutine.SetLabelColor(**self.labelTextRed) self.frame.SetStatusText(_translate( 'Click where you want to insert the Routine, or CANCEL insert.')) self.insertingRoutine = self.routinesFromID[event.GetId()] x = self.getNearestGapPoint(0) self.drawEntryPoints([x])
def updateUserProjs(self): if self.app.osf_session.user_id is None: self.myProjectsPanel.setContents( _translate("No user logged in. \n\n" "Go to menu item Projects>Users>")) else: self.myProjectsPanel.setContents( _translate("Searching projects for user {} ...") .format(self.app.osf_session.username)) self.Update() wx.Yield() myProjs = self.app.osf_session.find_user_projects() self.myProjectsPanel.setContents(myProjs)
def check(self, parent): """Checks python syntax of code snippets, and for self-reference. Note: code snippets simply use existing names in the namespace, like from condition-file headers. They do not add to the namespace (unlike Name fields). Code snippets in param fields will often be user-defined vars, especially condition names. Can also be expressions like random(1,100). Hard to know what will be problematic. But its always the case that self-reference is wrong. """ # first check if there's anything to validate (and return if not) control = self.GetWindow() if not hasattr(control, 'GetValue'): return '', True val = control.GetValue() # same as parent.params[self.fieldName].val if not isinstance(val, basestring): return '', True field = self.fieldName msg, OK = '', True # until we find otherwise codeWanted = psychopy.experiment.utils.unescapedDollarSign_re.search(val) isCodeField = bool(parent.params[self.fieldName].valType == 'code') if codeWanted or isCodeField: # get var names from val, check against namespace: code = experiment.getCodeFromParamStr(val) try: names = compile(code, '', 'eval').co_names except SyntaxError: # empty '' compiles to a syntax error, ignore if not code.strip() == '': msg = _translate('Python syntax error in field `{}`: {}') msg = msg.format(self.displayName, code) OK = False else: # namespace = parent.frame.exp.namespace # parent.params['name'].val is not in namespace for new params # and is not fixed as .val until dialog closes. Use getvalue() # to handle dynamic changes to Name field: if 'name' in parent.paramCtrls: # some components don't have names parentName = parent.paramCtrls['name'].getValue() for name in names: # `name` means a var name within a compiled code snippet if name == parentName: msg = _translate( 'Python var `{}` in `{}` is same as Name') msg = msg.format(name, self.displayName) OK = False parent.warningsDict[field] = msg return msg, OK
def __init__(self, project, parent, *args, **kwargs): wx.Dialog.__init__(self, parent, *args, **kwargs) self.parent = parent self.project = project existingName = project.name msgText = _translate("points to a remote that doesn't exist (deleted?).") msgText += (" "+_translate("What shall we do?")) msg = wx.StaticText(self, label="{} {}".format(existingName, msgText)) choices = [_translate("(Re)create a project"), "{} ({})".format(_translate("Point to an different location"), _translate("not yet supported")), _translate("Forget the local git repository (deletes history keeps files)")] self.radioCtrl = wx.RadioBox(self, label='RadioBox', choices=choices, majorDimension=1) self.radioCtrl.EnableItem(1, False) self.radioCtrl.EnableItem(2, False) mainSizer = wx.BoxSizer(wx.VERTICAL) buttonSizer = wx.BoxSizer(wx.HORIZONTAL) buttonSizer.Add(wx.Button(self, id=wx.ID_OK, label=_translate("OK")), 1, wx.ALL | wx.ALIGN_RIGHT, 5) buttonSizer.Add(wx.Button(self, id=wx.ID_CANCEL, label=_translate("Cancel")), 1, wx.ALL | wx.ALIGN_RIGHT, 5) mainSizer.Add(msg, 1, wx.ALL, 5) mainSizer.Add(self.radioCtrl, 1, wx.ALL | wx.ALIGN_CENTER_HORIZONTAL, 5) mainSizer.Add(buttonSizer, 1, wx.ALL | wx.ALIGN_RIGHT, 1) self.SetSizer(mainSizer) self.Layout()
def addPavloviaTools(self, buttons=[]): rc = self.frame.app.prefs.paths['resources'] info = {} info['pavloviaRun'] = { 'emblem': 'run16.png', 'func': self.frame.onPavloviaRun, 'label': _translate('Run online'), 'tip': _translate('Run the study online (with pavlovia.org)')} info['pavloviaSync'] = { 'emblem': 'sync_green16.png', 'func': self.frame.onPavloviaSync, 'label': _translate('Sync online'), 'tip': _translate('Sync with web project (at pavlovia.org)')} info['pavloviaSearch'] = { 'emblem': 'magnifier16.png', 'func': self.onPavloviaSearch, 'label': _translate('Search Pavlovia.org'), 'tip': _translate('Find existing studies online (at pavlovia.org)')} info['pavloviaUser'] = { 'emblem': 'user22.png', 'func': self.onPavloviaUser, 'label': _translate('Log in to Pavlovia'), 'tip': _translate('Log in to (or create user at) Pavlovia.org')} info['pavloviaProject'] = { 'emblem': 'info16.png', 'func': self.onPavloviaProject, 'label': _translate('View project'), 'tip': _translate('View details of this project')} if not buttons: # allows panels to select subsets buttons = info.keys() for buttonName in buttons: emblem = info[buttonName]['emblem'] btnFunc = info[buttonName]['func'] label = info[buttonName]['label'] tip = info[buttonName]['tip'] btnImage = icons.combineImageEmblem( main=join(rc, 'globe%i.png' % self.tbSize), emblem=join(rc, emblem), pos='bottom_right') if 'phoenix' in wx.PlatformInfo: self.btnHandles[buttonName] = self.toolbar.AddTool( wx.ID_ANY, label, btnImage, tip) else: self.btnHandles[buttonName] = self.toolbar.AddSimpleTool( wx.ID_ANY, btnImage, label, tip) self.toolbar.Bind(wx.EVT_TOOL, btnFunc, self.btnHandles[buttonName])
def __init__(self, parent, id, project, *args, **kwargs): wx.Panel.__init__(self, parent, id, *args, **kwargs) self.project = project self.sizer = wx.FlexGridSizer(rows=2, cols=2, vgap=5, hgap=5) self.sizer.AddGrowableCol(1) upLabel = wx.StaticText(self, -1, _translate("Uploading:")) self.upProg = wx.Gauge(self, -1, range=1, size=(200, -1)) downLabel = wx.StaticText(self, -1, _translate("Downloading:")) self.downProg = wx.Gauge(self, -1, range=1, size=(200, -1)) self.sizer.AddMany([upLabel, self.upProg, downLabel, self.downProg]) self.SetSizerAndFit(self.sizer)
def removeComponent(self, component, compID): """Remove either a Routine or a Loop from the Flow """ flow = self.frame.exp.flow if component.getType() == 'Routine': # check whether this will cause a collapsed loop # prev and next elements on flow are a loop init/end prevIsLoop = nextIsLoop = False if compID > 0: # there is at least one preceding prevIsLoop = (flow[compID - 1]).getType() == 'LoopInitiator' if len(flow) > (compID + 1): # there is at least one more compon nextIsLoop = (flow[compID + 1]).getType() == 'LoopTerminator' if prevIsLoop and nextIsLoop: # because flow[compID+1] is a terminator loop = flow[compID + 1].loop msg = _translate('The "%s" Loop is about to be deleted as ' 'well (by collapsing). OK to proceed?') title = _translate('Impending Loop collapse') warnDlg = dialogs.MessageDialog( parent=self.frame, message=msg % loop.params['name'], type='Warning', title=title) resp = warnDlg.ShowModal() if resp in [wx.ID_CANCEL, wx.ID_NO]: return # abort elif resp == wx.ID_YES: # make recursive calls to this same method until success # remove the loop first self.removeComponent(loop, compID) # because the loop has been removed ID is now one less self.removeComponent(component, compID - 1) return # have done the removal in final successful call # remove name from namespace only if it's a loop; # loops exist only in the flow elif 'conditionsFile' in component.params: conditionsFile = component.params['conditionsFile'].val if conditionsFile and conditionsFile not in ['None', '']: try: trialList, fieldNames = data.importConditions( conditionsFile, returnFieldNames=True) for fname in fieldNames: self.frame.exp.namespace.remove(fname) except Exception: msg = ("Conditions file %s couldn't be found so names not" " removed from namespace") logging.debug(msg % conditionsFile) self.frame.exp.namespace.remove(component.params['name'].val) # perform the actual removal flow.removeComponent(component, id=compID) self.draw()
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 self.pos = pos # 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.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 update(self, status=None): """Update to a particular status if given or deduce status msg if not """ if status is None: if not self.OSFproject: status = _translate("No remote project set") self.syncButton.Enable(False) elif not self.localPath or not self.localPath.GetLabel(): status = _translate("No local folder to sync with") self.syncButton.Enable(False) else: status = _translate("Ready") self.syncButton.Enable(True) self.status.SetLabel(_translate("Status: ") + status) self.Layout() self.Update()
def preview(self, event=None): self.getData(typeSelected=True) # in theory, self.data is also ok, because fixed previewData = self.data[:] # is supposed to never change anything, but bugs would be very subtle DlgConditions(previewData, parent=self.parent, title=_translate('PREVIEW'), fixed=True)
def makeFileMenu(self): fileMenu = wx.Menu() app = wx.GetApp() keyCodes = app.keys # add items to file menu fileMenu.Append(wx.ID_CLOSE, _translate("&Close View\t%s") % keyCodes['close'], _translate("Close current window")) self.Bind(wx.EVT_MENU, self.closeFrame, id=wx.ID_CLOSE) # -------------quit fileMenu.AppendSeparator() fileMenu.Append(wx.ID_EXIT, _translate("&Quit\t%s") % keyCodes['quit'], _translate("Terminate the program")) self.Bind(wx.EVT_MENU, app.quit, id=wx.ID_EXIT) return fileMenu
def __init__(self, app, parent=None, style=None, pos=wx.DefaultPosition, project=None): if style is None: style = (wx.DEFAULT_DIALOG_STYLE | wx.CENTER | wx.TAB_TRAVERSAL | wx.RESIZE_BORDER) if project: title = project.title else: title = _translate("Project info") self.frameType = 'ProjectInfo' wx.Dialog.__init__(self, parent, -1, title=title, style=style, size=(700, 500), pos=pos) self.app = app self.project = project self.parent = parent # on the right self.detailsPanel = DetailsPanel(parent=self, project=self.project) self.mainSizer = wx.BoxSizer(wx.HORIZONTAL) self.mainSizer.Add(self.detailsPanel, 1, wx.EXPAND | wx.ALL, 5) self.SetSizerAndFit(self.mainSizer) if self.parent: self.CenterOnParent() self.Layout()
def __do_layout(self): for paramName in self.order: if paramName.lower() != 'name': guikey = paramName.replace(' ', '_') paramGuiDict = self.codeGuiElements.get(guikey) asizer = paramGuiDict.setdefault( guikey + '_sizer', wx.BoxSizer(wx.VERTICAL)) asizer.Add(paramGuiDict.get( guikey + '_codebox'), 1, wx.EXPAND, 0) paramGuiDict.get(guikey + '_panel').SetSizer(asizer) tabLabel = _translate(paramName) # Add a visual indicator when tab contains code if self.params.get(guikey.replace('_',' ')).val: tabLabel += ' *' self.codeSections.AddPage(paramGuiDict.get( guikey + '_panel'), tabLabel) nameSizer = wx.BoxSizer(wx.HORIZONTAL) nameSizer.Add(self.nameLabel, 0, wx.ALL | wx.ALIGN_CENTER_VERTICAL, 10) nameSizer.Add(self.componentName, flag=wx.EXPAND | wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=10, proportion=1) nameSizer.Add(self.nameOKlabel, 0, wx.ALL, 10) mainSizer = wx.BoxSizer(wx.VERTICAL) buttonSizer = wx.BoxSizer(wx.HORIZONTAL) mainSizer.Add(nameSizer) mainSizer.Add(self.codeSections, 1, wx.EXPAND | wx.ALL, 10) buttonSizer.Add(self.helpButton, 0, wx.RIGHT, 10) buttonSizer.Add(self.okButton, 0, wx.LEFT, 10) buttonSizer.Add(self.cancelButton, 0, 0, 0) mainSizer.Add(buttonSizer, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.SetSizer(mainSizer) self.Layout() self.Center()
def noGitWarning(parent): """Raise a simpler warning dialog that the user needs to install git first""" dlg = wx.Dialog(parent=parent, style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP) errorBitmap = wx.ArtProvider.GetBitmap( wx.ART_ERROR, wx.ART_MESSAGE_BOX ) errorBitmapCtrl = wx.StaticBitmap(dlg, -1) errorBitmapCtrl.SetBitmap(errorBitmap) msg = wx.StaticText(dlg, label=_translate("You need to install git to use Pavlovia projects")) link = wxhl.HyperlinkCtrl(dlg, url="https://git-scm.com/") OK = wx.Button(dlg, wx.ID_OK, label="OK") msgsSizer = wx.BoxSizer(wx.VERTICAL) msgsSizer.Add(msg, 1, flag=wx.ALIGN_RIGHT | wx.ALL | wx.EXPAND, border=5) msgsSizer.Add(link, 1, flag=wx.ALIGN_RIGHT | wx.ALL | wx.EXPAND, border=5) msgsAndIcon = wx.BoxSizer(wx.HORIZONTAL) msgsAndIcon.Add(errorBitmapCtrl, 0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5) msgsAndIcon.Add(msgsSizer, 1, flag=wx.ALIGN_RIGHT | wx.ALL | wx.EXPAND, border=5) mainSizer = wx.BoxSizer(wx.VERTICAL) mainSizer.Add(msgsAndIcon, 0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5) mainSizer.Add(OK, 0, flag=wx.ALIGN_RIGHT | wx.ALL, border=5) dlg.SetSizerAndFit(mainSizer) dlg.Layout() dlg.ShowModal()
def setLocalPath(parent, project=None, path=""): """Open a DirDialog and set the project local folder to that specified Returns ---------- None for no change and newPath if this has changed from previous """ if path: origPath = path elif project and 'localRoot' in project: origPath = project.localRoot else: origPath = "" # create the dialog dlg = wx.DirDialog( parent, defaultPath=origPath, message=_translate( "Choose/create the root location for the synced project")) if dlg.ShowModal() == wx.ID_OK: newPath = dlg.GetPath() if os.path.isfile(newPath): newPath = os.path.split(newPath)[0] if newPath != origPath: if project: project.localRoot = newPath return newPath
def onInsertRoutine(self, evt): """For when the insert Routine button is pressed - bring up dialog and present insertion point on flow line. see self.insertRoutine() for further info """ if self.mode.startswith('loopPoint'): self.clearMode() elif self.mode == 'routine': # clicked again with label now being "Cancel..." self.clearMode() return self.frame.SetStatusText(_translate( "Select a Routine to insert (Esc to exit)")) menu = wx.Menu() self.routinesFromID = {} id = wx.NewIdRef() menu.Append(id, '(new)') self.routinesFromID[id] = '(new)' menu.Bind(wx.EVT_MENU, self.insertNewRoutine, id=id) for routine in self.frame.exp.routines: id = wx.NewIdRef() menu.Append(id, routine) self.routinesFromID[id] = routine menu.Bind(wx.EVT_MENU, self.onInsertRoutineSelect, id=id) btnPos = self.btnInsertRoutine.GetRect() menuPos = (btnPos[0], btnPos[1] + btnPos[3]) self.PopupMenu(menu, menuPos) menu.Bind(wx.EVT_MENU_CLOSE, self.clearMode) menu.Destroy() # destroy to avoid mem leak
def setLoopPoint1(self, evt=None): """Someone pushed the insert loop button. Fetch the dialog """ if self.mode == 'routine': self.clearMode() # clicked again, label is "Cancel..." elif self.mode.startswith('loopPoint'): self.clearMode() return self.btnInsertLoop.SetLabel(_translate('CANCEL insert')) self.btnInsertLoop.SetLabelColor(**self.labelTextRed) self.mode = 'loopPoint1' self.frame.SetStatusText(_translate( 'Click where you want the loop to start/end, or CANCEL insert.')) x = self.getNearestGapPoint(0) self.drawEntryPoints([x])
def clearMode(self, event=None): """If we were in middle of doing something (like inserting routine) then end it, allowing user to cancel """ self.mode = 'normal' self.insertingRoutine = None for id in self.entryPointIDlist: self.pdc.RemoveId(id) self.entryPointPosList = [] self.entryPointIDlist = [] self.gapsExcluded = [] self.draw() self.frame.SetStatusText("") self.btnInsertRoutine.SetLabel(_translate('Insert Routine')) self.btnInsertLoop.SetLabel(_translate('Insert Loop')) self.btnInsertRoutine.SetLabelColor(**self.labelTextBlack) self.btnInsertLoop.SetLabelColor(**self.labelTextBlack)
def doAutoInstall(self, v='latest'): if v == 'latest': v = self.latest['version'] msg = _translate("Downloading PsychoPy v%s") % v self.statusMessage.SetLabel(msg) try: zipFile, info = self.fetchPsychoPy(v) except Exception: msg = _translate('Failed to fetch PsychoPy release.\n' 'Check proxy setting in preferences') self.statusMessage.SetLabel(msg) return -1 self.statusMessage.SetLabel(info) self.Fit() # got a download - try to install it info = self.installZipFile(zipFile, v) return info
def onBrowseLocalFolder(self, evt): self.localFolder = setLocalPath(self, self.project) if self.localFolder: self.localFolderCtrl.SetLabel( label=_translate("Local root: {}").format(self.localFolder)) self.localFolderCtrl.Wrap(self.GetSize().width) self.Layout() self.parent.Raise()
def __init__(self, *args, **kwargs): wx.Frame.__init__(self, *args, **kwargs) self.Center() # set up menu bar self.menuBar = wx.MenuBar() self.fileMenu = self.makeFileMenu() self.menuBar.Append(self.fileMenu, _translate('&File')) self.SetMenuBar(self.menuBar)
def __init__(self, app, pos=wx.DefaultPosition, size=wx.DefaultSize, style=defaultStyle): wx.Dialog.__init__( self, None, title=_translate("Log in to Open Science Framework")) self.session = app.osf_session self.app = app self.fieldsSizer = wx.GridBagSizer(vgap=5, hgap=5) if web.haveInternetAccess(): self.status = wx.StaticText(self, label=_translate("Status: Ready")) else: self.status = wx.StaticText(self, label=_translate("No internet access")) self.fieldsSizer.Add(self.status, pos=(0, 0), span=(1, 2), flag=wx.ALIGN_CENTER, border=10) # user info self.fieldsSizer.Add(wx.StaticText( self, label=_translate("OSF Username (email)")), pos=(1, 0), flag=wx.ALIGN_RIGHT) self.username = wx.TextCtrl(self) self.username.SetToolTip( wx.ToolTip( _translate("Your username on OSF " "(the email address you used)"))) self.fieldsSizer.Add(self.username, pos=(1, 1), flag=wx.ALIGN_LEFT) # pass info self.fieldsSizer.Add(wx.StaticText(self, label=_translate("Password")), pos=(2, 0), flag=wx.ALIGN_RIGHT) self.password = wx.TextCtrl(self, style=wx.TE_PASSWORD | wx.TE_PROCESS_ENTER) self.password.SetToolTip( wx.ToolTip( _translate("Your password on OSF " "(will be checked securely with https)"))) self.fieldsSizer.Add(self.password, pos=(2, 1), flag=wx.ALIGN_LEFT) # remember me self.fieldsSizer.Add(wx.StaticText(self, label=_translate("Remember me")), pos=(3, 0), flag=wx.ALIGN_RIGHT) self.rememberMe = wx.CheckBox(self, True) self.rememberMe.SetToolTip( _translate("We won't store your password - " "just an authorisation token")) self.fieldsSizer.Add(self.rememberMe, pos=(3, 1), flag=wx.ALIGN_LEFT) # buttons (Log in, Cancel) btnSizer = wx.BoxSizer(wx.HORIZONTAL) self.cancelBtn = wx.Button(self, wx.ID_CANCEL, _translate('Cancel')) self.Bind(wx.EVT_BUTTON, self.onCancel, id=wx.ID_CANCEL) btnSizer.Add(self.cancelBtn, wx.ALIGN_RIGHT) self.okBtn = wx.Button(self, wx.ID_OK, _translate("Login")) self.okBtn.SetDefault() self.Bind(wx.EVT_BUTTON, self.onLogin, id=wx.ID_OK) btnSizer.Add(self.okBtn, wx.ALIGN_RIGHT) self.main_sizer = wx.BoxSizer(wx.VERTICAL) self.main_sizer.Add(self.fieldsSizer, 0, wx.ALL, 5) self.main_sizer.Add(btnSizer, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.SetSizerAndFit(self.main_sizer)
def rename(self): """Rename a file or directory.""" if os.path.isdir(self.selectedItem.abspath): # rename a directory dlg = wx.TextEntryDialog( self, _translate('Rename folder `{}` to:').format(self.selectedItem.name), _translate('Rename Folder'), self.selectedItem.name) if dlg.ShowModal() == wx.ID_OK: newName = dlg.GetValue() try: os.rename(self.selectedItem.abspath, os.path.join(self.selectedItem.basename, newName)) except OSError: dlg2 = wx.MessageDialog( self, _translate("Cannot rename `{}` to `{}`.").format( self.selectedItem.name, newName), style=wx.ICON_ERROR | wx.OK) dlg2.ShowModal() dlg2.Destroy() dlg.Destroy() return self.gotoDir(self.currentPath) # refresh for idx, item in enumerate(self.dirData): abspath = os.path.join(self.currentPath, newName) if item.abspath == abspath: self.fileList.Select(idx, True) self.fileList.EnsureVisible(idx) self.selectedItem = self.dirData[idx] self.fileList.SetFocus() break dlg.Destroy() elif os.path.isfile(self.selectedItem.abspath): # rename a directory dlg = wx.TextEntryDialog( self, _translate('Rename file `{}` to:').format(self.selectedItem.name), _translate('Rename file'), self.selectedItem.name) if dlg.ShowModal() == wx.ID_OK: newName = dlg.GetValue() try: newPath = os.path.join(self.selectedItem.basename, newName) os.rename(self.selectedItem.abspath, newPath) except OSError: dlgError = wx.MessageDialog( self, _translate("Cannot rename `{}` to `{}`.").format( self.selectedItem.name, newName), style=wx.ICON_ERROR | wx.OK) dlgError.ShowModal() dlgError.Destroy() dlg.Destroy() return self.gotoDir(self.currentPath) # refresh for idx, item in enumerate(self.dirData): if newPath == item.abspath: self.fileList.Select(idx, True) self.fileList.EnsureVisible(idx) self.selectedItem = self.dirData[idx] self.fileList.SetFocus() break dlg.Destroy()
def populatePrefs(self): """Populate pages with property items for each preference.""" # clear pages for sectionName in self.prefsSpec.keys(): prefsSection = self.prefsCfg[sectionName] specSection = self.prefsSpec[sectionName] for prefName in specSection: if prefName in ['version']: # any other prefs not to show? continue # allowModuleImports pref is handled by generateSpec.py # NB if something is in prefs but not in spec then it won't be # shown (removes outdated prefs) thisPref = prefsSection[prefName] thisSpec = specSection[prefName] # for keybindings replace Ctrl with Cmd on Mac if platform.system() == 'Darwin' and \ sectionName == 'keyBindings': if thisSpec.startswith('string'): thisPref = thisPref.replace('Ctrl+', 'Cmd+') # can we translate this pref? try: pLabel = _localized[prefName] except Exception: pLabel = prefName # get tooltips from comment lines from the spec, as parsed by # configobj helpText = '' hints = self.prefsSpec[sectionName].comments[prefName] # a list if len(hints): # use only one comment line, from right above the pref hint = hints[-1].lstrip().lstrip('#').lstrip() helpText = _translate(hint) if type(thisPref) == bool: # only True or False - use a checkbox self.proPrefs.addBoolItem( sectionName, pLabel, prefName, thisPref, helpText=helpText) # # properties for fonts, dropdown gives a list of system fonts elif prefName in ('codeFont', 'commentFont', 'outputFont'): try: default = self.fontList.index(thisPref) except ValueError: default = 0 self.proPrefs.addEnumItem( sectionName, pLabel, prefName, labels=self.fontList, values=[i for i in range(len(self.fontList))], value=default, helpText=helpText) elif prefName == 'locale': thisPref = self.app.prefs.app['locale'] locales = self.app.localization.available try: default = locales.index(thisPref) except ValueError: # default to US english default = locales.index('en_US') self.proPrefs.addEnumItem( sectionName, pLabel, prefName, labels=[_localized[i] for i in locales], values=[i for i in range(len(locales))], value=default, helpText=helpText) # # single directory elif prefName in ('unpackedDemosDir',): self.proPrefs.addDirItem( sectionName, pLabel, prefName, thisPref, helpText=helpText) # single file elif prefName in ('flac',): self.proPrefs.addFileItem( sectionName, pLabel, prefName, thisPref, helpText=helpText) # # audio latency mode for the PTB driver elif prefName == 'audioLatencyMode': # get the labels from above labels = [] for val, labl in audioLatencyLabels.items(): labels.append(u'{}: {}'.format(val, labl)) # get the options from the config file spec vals = thisSpec.replace("option(", "").replace("'", "") # item -1 is 'default=x' from spec vals = vals.replace(", ", ",").split(',') try: # set the field to the value in the pref default = int(thisPref) except ValueError: try: # use first if default not in list default = int(vals[-1].strip('()').split('=')[1]) except (IndexError, TypeError, ValueError): # no default default = 0 self.proPrefs.addEnumItem( sectionName, pLabel, prefName, labels=labels, values=[i for i in range(len(labels))], value=default, helpText=helpText) # # option items are given a dropdown, current value is shown # # in the box elif thisSpec.startswith('option') or prefName == 'audioDevice': if prefName == 'audioDevice': options = self.audioDevNames try: default = self.audioDevNames.index( self.audioDevDefault) except ValueError: default = 0 else: vals = thisSpec.replace("option(", "").replace("'", "") # item -1 is 'default=x' from spec vals = vals.replace(", ", ",").split(',') options = vals[:-1] try: # set the field to the value in the pref default = options.index(thisPref) except ValueError: try: # use first if default not in list default = vals[-1].strip('()').split('=')[1] except IndexError: # no default default = 0 labels = [] # display only for opt in options: try: labels.append(_localized[opt]) except Exception: labels.append(opt) self.proPrefs.addEnumItem( sectionName, pLabel, prefName, labels=labels, values=[i for i in range(len(labels))], value=default, helpText=helpText) # # lists are given a property that can edit and reorder items elif thisSpec.startswith('list'): # list self.proPrefs.addStringArrayItem( sectionName, pLabel, prefName, [str(i) for i in thisPref], helpText) # integer items elif thisSpec.startswith('integer'): # integer self.proPrefs.addIntegerItem( sectionName, pLabel, prefName, thisPref, helpText) # # all other items just use a string field else: self.proPrefs.addStringItem( sectionName, pLabel, prefName, thisPref, helpText) self.proPrefs.populateGrid()
def __init__(self, exp, parentName, name='image', image='', mask='', interpolate='linear', units='from exp settings', color='$[1,1,1]', colorSpace='rgb', pos=(0, 0), size=(0.5, 0.5), ori=0, texRes='128', flipVert=False, flipHoriz=False, startType='time (s)', startVal=0.0, stopType='duration (s)', stopVal=1.0, startEstim='', durationEstim=''): super(ImageComponent, self).__init__(exp, parentName, name=name, units=units, color=color, colorSpace=colorSpace, pos=pos, size=size, ori=ori, startType=startType, startVal=startVal, stopType=stopType, stopVal=stopVal, startEstim=startEstim, durationEstim=durationEstim) self.type = 'Image' self.targets = ['PsychoPy', 'PsychoJS'] self.url = "https://www.psychopy.org/builder/components/image.html" self.exp.requirePsychopyLibs(['visual']) # params self.order += [ 'image', # Basic tab 'mask', 'texture resolution', # Texture tab ] msg = _translate( "The image to be displayed - a filename, including path") self.params['image'] = Param( image, valType='file', inputType="file", allowedTypes=[], categ='Basic', updates='constant', allowedUpdates=['constant', 'set every repeat', 'set every frame'], hint=msg, label=_localized["image"]) msg = _translate( "An image to define the alpha mask through which the image is " "seen - gauss, circle, None or a filename (including path)") self.params['mask'] = Param( mask, valType='str', inputType="file", allowedTypes=[], categ='Texture', updates='constant', allowedUpdates=['constant', 'set every repeat', 'set every frame'], hint=msg, label=_localized["mask"]) msg = _translate("Resolution of the mask if one is used.") self.params['texture resolution'] = Param( texRes, valType='num', inputType="choice", categ='Texture', allowedVals=['32', '64', '128', '256', '512'], updates='constant', allowedUpdates=[], hint=msg, label=_localized["texture resolution"]) msg = _translate( "How should the image be interpolated if/when rescaled") self.params['interpolate'] = Param(interpolate, valType='str', inputType="choice", allowedVals=['linear', 'nearest'], categ='Texture', updates='constant', allowedUpdates=[], hint=msg, label=_localized["interpolate"]) msg = _translate( "Should the image be flipped vertically (top to bottom)?") self.params['flipVert'] = Param(flipVert, valType='bool', inputType="bool", categ='Layout', updates='constant', allowedUpdates=[], hint=msg, label=_localized["flipVert"]) msg = _translate( "Should the image be flipped horizontally (left to right)?") self.params['flipHoriz'] = Param(flipHoriz, valType='bool', inputType="bool", categ='Layout', updates='constant', allowedUpdates=[], hint=msg, label=_localized["flipHoriz"]) del self.params['fillColor'] del self.params['borderColor']
#!/usr/bin/env python # -*- coding: utf-8 -*- # This file was generated by generateHints.py. # Following strings are used to localize hints in Preference Dialog of # the PsychoPy application. # Rebuild this file if comments in *.spec files are modified. from __future__ import absolute_import, print_function from psychopy.localization import _translate # baseNoArch.spec,[general],line25 _translate("which system to use as a backend for drawing") # baseNoArch.spec,[general],line27 _translate("the default units for windows and visual stimuli") # baseNoArch.spec,[general],line29 _translate("full screen is best for accurate timing") # baseNoArch.spec,[general],line31 _translate("enable subjects to use the mouse and GUIs during experiments") # baseNoArch.spec,[general],line33 _translate("'version' is for internal usage, not for the user") # baseNoArch.spec,[general],line35 _translate("Add paths here to your custom Python modules") # baseNoArch.spec,[general],line37 _translate("choice of audio library")
def __init__(self, parent=None, message='', type='Warning', title=None, timeout=None): # select and localize a title if not title: title = type labels = {'Warning': _translate('Warning'), 'Info': _translate('Info'), 'Query': _translate('Query')} try: label = labels[title] except Exception: label = title wx.Dialog.__init__(self, parent, -1, title=label) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(wx.StaticText(self, -1, message), flag=wx.ALL, border=15) # add buttons btnSizer = wx.BoxSizer(wx.HORIZONTAL) if type == 'Warning': # we need Yes,No,Cancel self.yesBtn = wx.Button(self, wx.ID_YES, _translate('Yes')) self.yesBtn.SetDefault() self.cancelBtn = wx.Button( self, wx.ID_CANCEL, _translate('Cancel')) self.noBtn = wx.Button(self, wx.ID_NO, _translate('No')) self.Bind(wx.EVT_BUTTON, self.onButton, id=wx.ID_CANCEL) self.Bind(wx.EVT_BUTTON, self.onButton, id=wx.ID_YES) self.Bind(wx.EVT_BUTTON, self.onButton, id=wx.ID_NO) # self.Bind(wx.EVT_CLOSE, self.onEscape) btnSizer.Add(self.cancelBtn, 0, wx.ALL | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, border=3) btnSizer.AddStretchSpacer() btnSizer.Add(self.noBtn, 0, wx.ALL | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border=3) btnSizer.Add(self.yesBtn, 0, wx.ALL | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border=3) elif type == 'Query': # we need Yes,No self.yesBtn = wx.Button(self, wx.ID_YES, _translate('Yes')) self.yesBtn.SetDefault() self.noBtn = wx.Button(self, wx.ID_NO, _translate('No')) self.Bind(wx.EVT_BUTTON, self.onButton, id=wx.ID_YES) self.Bind(wx.EVT_BUTTON, self.onButton, id=wx.ID_NO) # self.Bind(wx.EVT_CLOSE, self.onEscape) btnSizer.Add(self.noBtn, 0, wx.ALL | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border=3) btnSizer.Add(self.yesBtn, 0, wx.ALL | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border=3) elif type == 'Info': # just an OK button self.okBtn = wx.Button(self, wx.ID_OK, _translate('OK')) self.okBtn.SetDefault() self.Bind(wx.EVT_BUTTON, self.onButton, id=wx.ID_OK) btnSizer.Add(self.okBtn, 0, wx.ALL | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border=3) else: raise NotImplementedError('Message type %s unknown' % type) # configure sizers and fit sizer.Add(btnSizer, flag=wx.ALL | wx.EXPAND, border=5) self.Center() self.SetSizerAndFit(sizer) self.timeout = timeout
def __init__(self, exp, name, nReps='50', stairType='simple', switchStairs='random', conditions=(), conditionsFile='', endPoints=(0, 1), isTrials=True): """ @param name: name of the loop e.g. trials @type name: string @param nReps: number of reps (for all conditions) @type nReps:int """ super(MultiStairHandler, self).__init__() self.type = 'MultiStairHandler' self.exp = exp self.order = ['name'] # make name come first self.params = {} self.params['name'] = Param( name, valType='code', label=_localized['Name'], hint=_translate("Name of this loop")) self.params['nReps'] = Param( nReps, valType='code', label=_localized['nReps'], hint=_translate("(Minimum) number of trials in *each* staircase")) self.params['stairType'] = Param( stairType, valType='str', allowedVals=['simple', 'QUEST', 'quest'], label=_localized['stairType'], hint=_translate("How to select the next staircase to run")) self.params['switchMethod'] = Param( switchStairs, valType='str', allowedVals=['random', 'sequential', 'fullRandom'], label=_localized['switchMethod'], hint=_translate("How to select the next staircase to run")) # these two are really just for making the dialog easier (they won't # be used to generate code) self.params['loopType'] = Param( 'staircase', valType='str', allowedVals=['random', 'sequential', 'fullRandom', 'staircase', 'interleaved staircases'], label=_localized['loopType'], hint=_translate("How should the next trial value(s) be chosen?")) self.params['endPoints'] = Param( list(endPoints), valType='num', label=_localized['endPoints'], hint=_translate('Where to loop from and to (see values currently' ' shown in the flow view)')) self.params['conditions'] = Param( list(conditions), valType='str', updates=None, allowedUpdates=None, label=_localized['conditions'], hint=_translate("A list of dictionaries describing the " "differences between each staircase")) self.params['conditionsFile'] = Param( conditionsFile, valType='str', updates=None, allowedUpdates=None, label=_localized['conditions'], hint=_translate("An xlsx or csv file specifying the parameters " "for each condition")) self.params['isTrials'] = Param( isTrials, valType='bool', updates=None, allowedUpdates=None, label=_localized["Is trials"], hint=_translate("Indicates that this loop generates TRIALS, " "rather than BLOCKS of trials or stimuli within " "a trial. It alters how data files are output")) pass # don't initialise at start of exp, create when needed
def __init__(self, exp, name, loopType='random', nReps=5, conditions=(), conditionsFile='', endPoints=(0, 1), randomSeed='', selectedRows='', isTrials=True): """ @param name: name of the loop e.g. trials @type name: string @param loopType: @type loopType: string ('rand', 'seq') @param nReps: number of reps (for all conditions) @type nReps:int @param conditions: list of different trial conditions to be used @type conditions: list (of dicts?) @param conditionsFile: filename of the .csv file that contains conditions info @type conditions: string (filename) """ super(TrialHandler, self).__init__() self.type = 'TrialHandler' self.exp = exp self.order = ['name'] # make name come first (others don't matter) self.params = {} self.params['name'] = Param( name, valType='code', updates=None, allowedUpdates=None, label=_localized['Name'], hint=_translate("Name of this loop")) self.params['nReps'] = Param( nReps, valType='code', updates=None, allowedUpdates=None, label=_localized['nReps'], hint=_translate("Number of repeats (for each condition)")) self.params['conditions'] = Param( list(conditions), valType='str', updates=None, allowedUpdates=None, label=_localized['conditions'], hint=_translate("A list of dictionaries describing the " "parameters in each condition")) self.params['conditionsFile'] = Param( conditionsFile, valType='str', updates=None, allowedUpdates=None, label=_localized['conditions'], hint=_translate("Name of a file specifying the parameters for " "each condition (.csv, .xlsx, or .pkl). Browse " "to select a file. Right-click to preview file " "contents, or create a new file.")) self.params['endPoints'] = Param( list(endPoints), valType='num', updates=None, allowedUpdates=None, label=_localized['endPoints'], hint=_translate("The start and end of the loop (see flow " "timeline)")) self.params['Selected rows'] = Param( selectedRows, valType='str', updates=None, allowedUpdates=None, label=_localized['Selected rows'], hint=_translate("Select just a subset of rows from your condition" " file (the first is 0 not 1!). Examples: 0, " "0:5, 5:-1")) # NB staircase is added for the sake of the loop properties dialog: self.params['loopType'] = Param( loopType, valType='str', allowedVals=['random', 'sequential', 'fullRandom', 'staircase', 'interleaved staircases'], label=_localized['loopType'], hint=_translate("How should the next condition value(s) be " "chosen?")) self.params['random seed'] = Param( randomSeed, valType='code', updates=None, allowedUpdates=None, label=_localized['random seed'], hint=_translate("To have a fixed random sequence provide an " "integer of your choosing here. Leave blank to " "have a new random sequence on each run of the " "experiment.")) self.params['isTrials'] = Param( isTrials, valType='bool', updates=None, allowedUpdates=None, label=_localized["Is trials"], hint=_translate("Indicates that this loop generates TRIALS, " "rather than BLOCKS of trials or stimuli within " "a trial. It alters how data files are output"))
def __init__(self, exp, name, nReps='50', startVal='', nReversals='', nUp=1, nDown=3, minVal=0, maxVal=1, stepSizes='[4,4,2,2,1]', stepType='db', endPoints=(0, 1), isTrials=True): """ @param name: name of the loop e.g. trials @type name: string @param nReps: number of reps (for all conditions) @type nReps:int """ super(StairHandler, self).__init__() self.type = 'StairHandler' self.exp = exp self.order = ['name'] # make name come first (others don't matter) self.children = [] self.params = {} self.params['name'] = Param( name, valType='code', hint=_translate("Name of this loop"), label=_localized['Name']) self.params['nReps'] = Param( nReps, valType='code', label=_localized['nReps'], hint=_translate("(Minimum) number of trials in the staircase")) self.params['start value'] = Param( startVal, valType='code', label=_localized['start value'], hint=_translate("The initial value of the parameter")) self.params['max value'] = Param( maxVal, valType='code', label=_localized['max value'], hint=_translate("The maximum value the parameter can take")) self.params['min value'] = Param( minVal, valType='code', label=_localized['min value'], hint=_translate("The minimum value the parameter can take")) self.params['step sizes'] = Param( stepSizes, valType='code', label=_localized['step sizes'], hint=_translate("The size of the jump at each step (can change" " on each 'reversal')")) self.params['step type'] = Param( stepType, valType='str', allowedVals=['lin', 'log', 'db'], label=_localized['step type'], hint=_translate("The units of the step size (e.g. 'linear' will" " add/subtract that value each step, whereas " "'log' will ad that many log units)")) self.params['N up'] = Param( nUp, valType='code', label=_localized['N up'], hint=_translate("The number of 'incorrect' answers before the " "value goes up")) self.params['N down'] = Param( nDown, valType='code', label=_localized['N down'], hint=_translate("The number of 'correct' answers before the " "value goes down")) self.params['N reversals'] = Param( nReversals, valType='code', label=_localized['N reversals'], hint=_translate("Minimum number of times the staircase must " "change direction before ending")) # these two are really just for making the dialog easier (they won't # be used to generate code) self.params['loopType'] = Param( 'staircase', valType='str', allowedVals=['random', 'sequential', 'fullRandom', 'staircase', 'interleaved staircases'], label=_localized['loopType'], hint=_translate("How should the next trial value(s) be chosen?")) # NB this is added for the sake of the loop properties dialog self.params['endPoints'] = Param( list(endPoints), valType='num', label=_localized['endPoints'], hint=_translate('Where to loop from and to (see values currently' ' shown in the flow view)')) self.params['isTrials'] = Param( isTrials, valType='bool', updates=None, allowedUpdates=None, label=_localized["Is trials"], hint=_translate("Indicates that this loop generates TRIALS, " "rather than BLOCKS of trials or stimuli within" " a trial. It alters how data files are output"))
def __init__(self, parent, id, size=(400, 300), *args, **kwargs): BaseFrame.__init__(self, parent=None, id=id, size=size, *args, **kwargs) self.frameType = 'project' self.app = wx.GetApp() self.app.trackFrame(self) self.OSFproject = None self.project = None self.syncStatus = None # title self.title = wx.StaticText(self, -1, _translate("No project opened"), style=wx.BOLD | wx.ALIGN_CENTER) font = wx.Font(18, family=wx.NORMAL, style=wx.NORMAL, weight=wx.BOLD) self.title.SetFont(font) self.title.SetMinSize((300, -1)) self.title.Wrap(300) # name box nameBox = wx.StaticBox(self, -1, _translate("Name (for PsychoPy use):")) nameSizer = wx.StaticBoxSizer(nameBox, wx.VERTICAL) self.nameCtrl = wx.TextCtrl(self, -1, "", style=wx.TE_LEFT) nameSizer.Add(self.nameCtrl, flag=wx.EXPAND | wx.ALL, border=5) # local files localsBox = wx.StaticBox(self, -1, _translate("Local Info")) localsSizer = wx.StaticBoxSizer(localsBox, wx.VERTICAL) localBrowseBtn = wx.Button(self, -1, _translate("Browse...")) localBrowseBtn.Bind(wx.EVT_BUTTON, self.onBrowseLocal) self.localPath = wx.StaticText(self, -1, "") filesSizer = wx.BoxSizer(wx.HORIZONTAL) filesSizer.Add(wx.StaticText(self, -1, _translate("Local files:"))) filesSizer.Add(localBrowseBtn, flag=wx.ALL, border=5) localsSizer.Add(filesSizer, flag=wx.ALL, border=5) localsSizer.Add(self.localPath, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, proportion=1, border=5) # sync controls syncBox = wx.StaticBox(self, -1, _translate("Sync")) self.syncButton = wx.Button(self, -1, _translate("Sync Now")) self.syncButton.Bind(wx.EVT_BUTTON, self.onSyncBtn) self.syncStatus = SyncStatusPanel(self, id=-1, project=self.project) self.status = wx.StaticText(self, -1, "put status updates here") syncSizer = wx.StaticBoxSizer(syncBox, wx.VERTICAL) syncSizer.Add(self.syncButton, flag=wx.EXPAND | wx.ALL, proportion=1, border=5) syncSizer.Add(self.syncStatus, flag=wx.EXPAND | wx.ALL, proportion=1, border=5) syncSizer.Add(self.status, flag=wx.EXPAND | wx.ALL, proportion=1, border=5) projBox = wx.StaticBox(self, -1, _translate("Project Info")) projSizer = wx.StaticBoxSizer(projBox, wx.VERTICAL) self.projDetails = DetailsPanel(parent=self, noTitle=True) projSizer.Add(self.projDetails, flag=wx.EXPAND | wx.ALL, proportion=1, border=5) # mainSizer with title, then two columns self.mainSizer = wx.BoxSizer(wx.VERTICAL) self.mainSizer.Add(self.title, flag=wx.ALIGN_CENTER | wx.ALL, border=20) # set contents for left and right sizers leftSizer = wx.BoxSizer(wx.VERTICAL) leftSizer.Add(projSizer, flag=wx.EXPAND | wx.ALL, proportion=1, border=5) rightSizer = wx.BoxSizer(wx.VERTICAL) rightSizer.Add(nameSizer, flag=wx.EXPAND | wx.ALL, proportion=0, border=5) rightSizer.Add(localsSizer, flag=wx.EXPAND | wx.ALL, proportion=0, border=5) rightSizer.Add(syncSizer, flag=wx.ALL, border=5) columnSizer = wx.BoxSizer(wx.HORIZONTAL) columnSizer.Add(leftSizer, border=5, flag=wx.EXPAND | wx.ALL, proportion=1) columnSizer.Add(rightSizer, border=5, flag=wx.EXPAND | wx.ALL, proportion=1) self.mainSizer.Add(columnSizer, proportion=1, flag=wx.EXPAND | wx.ALL, border=5) self.SetSizerAndFit(self.mainSizer) self.SetAutoLayout(True) self.update() self.app.trackFrame(self) self.Show()
def __init__(self, parent=None, id=wx.ID_ANY, project=None, localRoot="", *args, **kwargs): wx.Dialog.__init__(self, parent, id, *args, **kwargs) panel = wx.Panel(self, wx.ID_ANY, style=wx.TAB_TRAVERSAL) # when a project is successfully created these will be populated if hasattr(parent, 'filename'): self.filename = parent.filename else: self.filename = None self.project = project # type: pavlovia.PavloviaProject self.projInfo = None self.parent = parent if project: # edit existing project self.isNew = False if project.localRoot and not localRoot: localRoot = project.localRoot else: self.isNew = True # create the controls nameLabel = wx.StaticText(panel, -1, _translate("Name:")) self.nameBox = wx.TextCtrl(panel, -1, size=(400, -1)) # Path can contain only letters, digits, '_', '-' and '.'. # Cannot start with '-', end in '.git' or end in '.atom'] pavSession = pavlovia.getCurrentSession() try: username = pavSession.user.username except AttributeError as e: raise pavlovia.NoUserError( "{}: Tried to create project with no user logged in.".format( e)) gpChoices = [username] gpChoices.extend(pavSession.listUserGroups()) groupLabel = wx.StaticText(panel, -1, _translate("Group/owner:")) self.groupBox = wx.Choice(panel, -1, size=(400, -1), choices=gpChoices) descrLabel = wx.StaticText(panel, -1, _translate("Description:")) self.descrBox = wx.TextCtrl(panel, -1, size=(400, 200), style=wx.TE_MULTILINE | wx.SUNKEN_BORDER) localLabel = wx.StaticText(panel, -1, _translate("Local folder:")) self.localBox = wx.TextCtrl(panel, -1, size=(400, -1), value=localRoot) self.btnLocalBrowse = wx.Button(panel, wx.ID_ANY, _translate("Browse...")) self.btnLocalBrowse.Bind(wx.EVT_BUTTON, self.onBrowseLocal) localPathSizer = wx.BoxSizer(wx.HORIZONTAL) localPathSizer.Add(self.localBox) localPathSizer.Add(self.btnLocalBrowse) tagsLabel = wx.StaticText(panel, -1, _translate("Tags (comma separated):")) self.tagsBox = wx.TextCtrl(panel, -1, size=(400, 100), value="PsychoPy, Builder, Coder", style=wx.TE_MULTILINE | wx.SUNKEN_BORDER) publicLabel = wx.StaticText(panel, -1, _translate("Public:")) self.publicBox = wx.CheckBox(panel, -1) # buttons if self.isNew: buttonMsg = _translate("Create project on Pavlovia") else: buttonMsg = _translate("Submit changes to Pavlovia") updateBtn = wx.Button(panel, -1, buttonMsg) updateBtn.Bind(wx.EVT_BUTTON, self.submitChanges) cancelBtn = wx.Button(panel, -1, _translate("Cancel")) cancelBtn.Bind(wx.EVT_BUTTON, self.onCancel) btnSizer = wx.BoxSizer(wx.HORIZONTAL) if sys.platform == "win32": btns = [updateBtn, cancelBtn] else: btns = [cancelBtn, updateBtn] btnSizer.AddMany(btns) # do layout fieldsSizer = wx.FlexGridSizer(cols=2, rows=6, vgap=5, hgap=5) fieldsSizer.AddMany([(nameLabel, 0, wx.ALIGN_RIGHT), self.nameBox, (groupLabel, 0, wx.ALIGN_RIGHT), self.groupBox, (localLabel, 0, wx.ALIGN_RIGHT), localPathSizer, (descrLabel, 0, wx.ALIGN_RIGHT), self.descrBox, (tagsLabel, 0, wx.ALIGN_RIGHT), self.tagsBox, (publicLabel, 0, wx.ALIGN_RIGHT), self.publicBox]) border = wx.BoxSizer(wx.VERTICAL) border.Add(fieldsSizer, 0, wx.ALL, 5) border.Add(btnSizer, 0, wx.ALIGN_RIGHT | wx.ALL, 5) panel.SetSizerAndFit(border) self.Fit()
def __init__(self, app, pos=wx.DefaultPosition, size=wx.DefaultSize, style=defaultStyle): title = _translate("Search OSF (Open Science Framework)") self.frameType = 'OSFsearch' BaseFrame.__init__(self, None, -1, title, pos, size, style) self.app = app self.currentOSFProject = None # to show detail of current selection self.detailsPanel = DetailsPanel(parent=self) # create list of my projects (no search?) self.myProjectsPanel = ProjectListPanel(self, self.detailsPanel) # create list of searchable public projects self.publicProjectsPanel = ProjectListPanel(self, self.detailsPanel) self.publicProjectsPanel.setContents('') # sizers: on the left we have search boxes leftSizer = wx.BoxSizer(wx.VERTICAL) leftSizer.Add(wx.StaticText(self, -1, _translate("My Projects")), flag=wx.EXPAND | wx.ALL, border=5) leftSizer.Add(self.myProjectsPanel, proportion=1, flag=wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT, border=10) searchSizer = wx.BoxSizer(wx.HORIZONTAL) searchSizer.Add(wx.StaticText(self, -1, _translate("Search Public:"))) self.searchTextCtrl = wx.TextCtrl(self, -1, "", style=wx.TE_PROCESS_ENTER) self.searchTextCtrl.Bind(wx.EVT_TEXT_ENTER, self.onSearch) searchSizer.Add(self.searchTextCtrl, flag=wx.EXPAND) leftSizer.Add(searchSizer) tagsSizer = wx.BoxSizer(wx.HORIZONTAL) tagsSizer.Add(wx.StaticText(self, -1, _translate("Tags:"))) self.tagsTextCtrl = wx.TextCtrl(self, -1, "psychopy,", style=wx.TE_PROCESS_ENTER) self.tagsTextCtrl.Bind(wx.EVT_TEXT_ENTER, self.onSearch) tagsSizer.Add(self.tagsTextCtrl, flag=wx.EXPAND) leftSizer.Add(tagsSizer) leftSizer.Add(self.publicProjectsPanel, proportion=1, flag=wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT, border=10) # sizers: on the right we have detail rightSizer = wx.BoxSizer(wx.VERTICAL) rightSizer.Add(wx.StaticText(self, -1, _translate("Project Info")), flag=wx.ALL, border=5) self.syncButton = wx.Button(self, -1, _translate("Sync...")) self.syncButton.Enable(False) rightSizer.Add(self.syncButton, flag=wx.ALL, border=5) self.syncButton.Bind(wx.EVT_BUTTON, self.onSyncButton) rightSizer.Add(self.detailsPanel, proportion=1, flag=wx.EXPAND | wx.ALL, border=10) self.mainSizer = wx.BoxSizer(wx.HORIZONTAL) self.mainSizer.Add(leftSizer, flag=wx.EXPAND, proportion=1, border=5) self.mainSizer.Add(rightSizer, flag=wx.EXPAND, proportion=1, border=5) self.SetSizerAndFit(self.mainSizer) aTable = wx.AcceleratorTable([ (0, wx.WXK_ESCAPE, wx.ID_CANCEL), ]) self.SetAcceleratorTable(aTable) self.Show() # show the window before doing search/updates self.updateUserProjs() # update the info in myProjectsPanel
def _getXMLparam(self, params, paramNode, componentNode=None): """params is the dict of params of the builder component (e.g. stimulus) into which the parameters will be inserted (so the object to store the params should be created first) paramNode is the parameter node fetched from the xml file """ name = paramNode.get('name') valType = paramNode.get('valType') val = paramNode.get('val') # many components need web char newline replacement if not name == 'advancedParams': val = val.replace(" ", "\n") # custom settings (to be used when if valType == 'fixedList': # convert the string to a list try: params[name].val = eval('list({})'.format(val)) except NameError: # if val is a single string it will look like variable params[name].val = [val] elif name == 'storeResponseTime': return # deprecated in v1.70.00 because it was redundant elif name == 'nVertices': # up to 1.85 there was no shape param # if no shape param then use "n vertices" only if _findParam('shape', componentNode) is None: if val == '2': params['shape'].val = "line" elif val == '3': params['shape'].val = "triangle" elif val == '4': params['shape'].val = "rectangle" else: params['shape'].val = "regular polygon..." params['nVertices'].val = val elif name == 'startTime': # deprecated in v1.70.00 params['startType'].val = "{}".format('time (s)') params['startVal'].val = "{}".format(val) return # times doesn't need to update its type or 'updates' rule elif name == 'forceEndTrial': # deprecated in v1.70.00 params['forceEndRoutine'].val = bool(val) return # forceEndTrial doesn't need to update type or 'updates' elif name == 'forceEndTrialOnPress': # deprecated in v1.70.00 params['forceEndRoutineOnPress'].val = bool(val) return # forceEndTrial doesn't need to update type or 'updates' elif name == 'forceEndRoutineOnPress': if val == 'True': val = "any click" elif val == 'False': val = "never" params['forceEndRoutineOnPress'].val = val return elif name == 'trialList': # deprecated in v1.70.00 params['conditions'].val = eval(val) return # forceEndTrial doesn't need to update type or 'updates' elif name == 'trialListFile': # deprecated in v1.70.00 params['conditionsFile'].val = "{}".format(val) return # forceEndTrial doesn't need to update type or 'updates' elif name == 'duration': # deprecated in v1.70.00 params['stopType'].val = u'duration (s)' params['stopVal'].val = "{}".format(val) return # times doesn't need to update its type or 'updates' rule elif name == 'allowedKeys' and valType == 'str': # changed v1.70.00 # ynq used to be allowed, now should be 'y','n','q' or # ['y','n','q'] if len(val) == 0: newVal = val elif val[0] == '$': newVal = val[1:] # they were using code (which we can reuse) elif val.startswith('[') and val.endswith(']'): # they were using code (slightly incorrectly!) newVal = val[1:-1] elif val in ['return', 'space', 'left', 'right', 'escape']: newVal = val # they were using code else: # convert string to list of keys then represent again as a # string! newVal = repr(list(val)) params['allowedKeys'].val = newVal params['allowedKeys'].valType = 'code' elif name == 'correctIf': # deprecated in v1.60.00 corrIf = val corrAns = corrIf.replace( 'resp.keys==unicode(', '').replace(')', '') params['correctAns'].val = corrAns name = 'correctAns' # then we can fetch other aspects below elif 'olour' in name: # colour parameter was Americanised v1.61.00 name = name.replace('olour', 'olor') params[name].val = val elif name == 'times': # deprecated in v1.60.00 times = eval('%s' % val) params['startType'].val = "{}".format('time (s)') params['startVal'].val = "{}".format(times[0]) params['stopType'].val = "{}".format('time (s)') params['stopVal'].val = "{}".format(times[1]) return # times doesn't need to update its type or 'updates' rule elif name in ('Before Experiment', 'Begin Experiment', 'Begin Routine', 'Each Frame', 'End Routine', 'End Experiment', 'Before JS Experiment', 'Begin JS Experiment', 'Begin JS Routine', 'Each JS Frame', 'End JS Routine', 'End JS Experiment'): # up to version 1.78.00 and briefly in 2021.1.0-1.1 these were 'code' params[name].val = val params[name].valType = 'extendedCode' return # so that we don't update valType again below elif name == 'Saved data folder': # deprecated in 1.80 for more complete data filename control params[name] = Param( val, valType='code', allowedTypes=[], hint=_translate("Name of the folder in which to save data" " and log files (blank defaults to the " "builder pref)"), categ='Data') elif name == 'channel': # was incorrectly set to be valType='str' until 3.1.2 params[name].val = val params[name].valType = 'code' # override elif 'val' in list(paramNode.keys()): if val == 'window units': # changed this value in 1.70.00 params[name].val = 'from exp settings' # in v1.80.00, some RatingScale API and Param fields were changed # Try to avoid a KeyError in these cases so can load the expt elif name in ('choiceLabelsAboveLine', 'lowAnchorText', 'highAnchorText'): # not handled, just ignored; want labels=[lowAnchor, # highAnchor] return elif name == 'customize_everything': # Try to auto-update the code: v = val # python code, not XML v = v.replace('markerStyle', 'marker').replace( 'customMarker', 'marker') v = v.replace('stretchHoriz', 'stretch').replace( 'displaySizeFactor', 'size') v = v.replace('textSizeFactor', 'textSize') v = v.replace('ticksAboveLine=False', 'tickHeight=-1') v = v.replace('showScale=False', 'scale=None').replace( 'allowSkip=False', 'skipKeys=None') v = v.replace('showAnchors=False', 'labels=None') # lowAnchorText highAnchorText will trigger obsolete error # when run the script params[name].val = v elif name == 'storeResponseTime': return # deprecated in v1.70.00 because it was redundant elif name == 'Resources': # if the xml import hasn't automatically converted from string? if type(val) == str: resources = data.utils.listFromString(val) if self.psychopyVersion == '2020.2.5': # in 2020.2.5 only, problems were: # a) resources list was saved as a string and # b) with wrong root folder resList = [] for resourcePath in resources: # doing this the blunt way but should we check for existence? resourcePath = resourcePath.replace("../", "") # it was created using wrong root resourcePath = resourcePath.replace("\\", "/") # created using windows \\ resList.append(resourcePath) resources = resList # push our new list back to resources params[name].val = resources else: if name in params: params[name].val = val else: # we found an unknown parameter (probably from the future) params[name] = Param( val, valType=paramNode.get('valType'), inputType="inv", allowedTypes=[], label=_translate(name), hint=_translate( "This parameter is not known by this version " "of PsychoPy. It might be worth upgrading, otherwise " "press the X button to remove this parameter.")) params[name].allowedTypes = paramNode.get('allowedTypes') if params[name].allowedTypes is None: params[name].allowedTypes = [] if name not in legacyParams + ['JS libs', 'OSF Project ID']: # don't warn people if we know it's OK (e.g. for params # that have been removed msg = _translate( "Parameter %r is not known to this version of " "PsychoPy but has come from your experiment file " "(saved by a future version of PsychoPy?). This " "experiment may not run correctly in the current " "version.") logging.warn(msg % name) logging.flush() # get the value type and update rate if 'valType' in list(paramNode.keys()): params[name].valType = paramNode.get('valType') # compatibility checks: if name in ['allowedKeys'] and paramNode.get('valType') == 'str': # these components were changed in v1.70.00 params[name].valType = 'code' elif name == 'Selected rows': # changed in 1.81.00 from 'code' to 'str': allow string or var params[name].valType = 'str' # conversions based on valType if params[name].valType == 'bool': params[name].val = eval("%s" % params[name].val) if 'updates' in list(paramNode.keys()): params[name].updates = paramNode.get('updates')
#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function from os import path from psychopy.experiment.components import BaseComponent, Param, getInitVals from psychopy.localization import _translate # the absolute path to the folder containing this path thisFolder = path.abspath(path.dirname(__file__)) iconFile = path.join(thisFolder, 'image.png') tooltip = _translate('Image: present images (bmp, jpg, tif...)') # only use _localized values for label values, nothing functional: _localized = { 'image': _translate('Image'), 'ipd': _translate('IPD'), 'mask': _translate('Mask'), 'texture resolution': _translate('Texture resolution'), 'flipVert': _translate('Flip vertically'), 'flipHoriz': _translate('Flip horizontally'), 'interpolate': _translate('Interpolate') } class VRDisplayComponent(BaseComponent): """An event class for presenting image-based stimuli""" categories = ['Stimuli']
#!/usr/bin/env python # -*- coding: utf-8 -*- # This file was generated by generateAlertmsg.py. # Following strings are used to localize alerts. # Rebuild this file if alert messages in *.yaml files are modified. from __future__ import absolute_import, print_function from psychopy.localization import _translate # Alert 2115 _translate( "Your stimulus size exceeds the {dimension} dimension of your window.") # Alert 2120 _translate( "Your stimulus size is smaller than 1 pixel ({dimension} dimension).") # Alert 2155 _translate( "Your stimulus position exceeds the {dimension} dimension of your window.") # Alert 3110 _translate( "Your stimulus {type} time of {time} is less than a screen refresh for a {Hz}Hz monitor." ) # Alert 3115 _translate( "Your stimulus {type} time of {time} seconds cannot be accurately presented for {time} on a {Hz}Hz monitor." )
def getProject(filename): """Will try to find (locally synced) pavlovia Project for the filename """ if not haveGit: raise exceptions.DependencyError( "gitpython and a git installation required for getProject()") gitRoot = getGitRoot(filename) if gitRoot in knownProjects: return knownProjects[gitRoot] elif gitRoot: # Existing repo but not in our knownProjects. Investigate logging.info("Investigating repo at {}".format(gitRoot)) localRepo = git.Repo(gitRoot) proj = None for remote in localRepo.remotes: for url in remote.urls: if "gitlab.pavlovia.org" in url: # could be 'https://gitlab.pavlovia.org/NameSpace/Name.git' # or may be '[email protected]:NameSpace/Name.git' namespaceName = url.split('gitlab.pavlovia.org')[1] # remove the first char (: or /) if namespaceName[0] in ['/', ':']: namespaceName = namespaceName[1:] # remove the .git at the end if present namespaceName = namespaceName.replace('.git', '') pavSession = getCurrentSession() if not pavSession.user: nameSpace = namespaceName.split('/')[0] if nameSpace in knownUsers: # Log in if user is known login(nameSpace, rememberMe=True) else: # Check whether project repo is found in any of the known users accounts for user in knownUsers: try: login(user) except requests.exceptions.ConnectionError: break foundProject = False for repo in pavSession.findUserProjects(): if namespaceName in repo['id']: foundProject = True logging.info( "Logging in as {}".format(user)) break if not foundProject: logging.warning( "Could not find {namespace} in your Pavlovia accounts. " "Logging in as {user}.".format( namespace=namespaceName, user=user)) if pavSession.user: proj = pavSession.getProject(namespaceName, repo=localRepo) if proj.pavlovia and proj.pavlovia.get_id() == 0: logging.warning( _translate( "We found a repository pointing to {} " "but no project was found there (deleted?)" ).format(url)) else: logging.warning( _translate( "We found a repository pointing to {} " "but no user is logged in for us to check it". format(url))) return proj if proj == None: logging.warning("We found a repository at {} but it " "doesn't point to gitlab.pavlovia.org. " "You could create that as a remote to " "sync from PsychoPy.".format(gitRoot))
def __init__(self, app, pos=wx.DefaultPosition, size=dlgSize, style=defaultStyle): title = _translate("PsychoPy Preferences") wx.Dialog.__init__(self, None, -1, title, pos, size, style) self.app = app self.Center() self.prefsCfg = self.app.prefs.userPrefsCfg self.prefsSpec = self.app.prefs.prefsSpec sizer = wx.BoxSizer(wx.VERTICAL) line = wx.StaticLine(self, -1, size=(20, -1), style=wx.LI_HORIZONTAL) lineStyle = wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.TOP sizer.Add(line, 0, lineStyle, 5) # notebook, flatnotebook or something else? self.nb = fnb.FlatNotebook(self, style=fnb.FNB_NO_X_BUTTON | fnb.FNB_NO_NAV_BUTTONS) # self.nb = wx.Notebook(self) # notebook not nice with lots of pages self.ctrls = {} sectionOrdering = [ 'general', 'app', 'builder', 'coder', 'hardware', 'connections', 'keyBindings' ] for section in sectionOrdering: prefsPage = self.makePrefPage(parent=self.nb, sectionName=section, prefsSection=self.prefsCfg[section], specSection=self.prefsSpec[section]) self.nb.AddPage(prefsPage, _localized[section]) self.nb.SetSelection(self.app.prefs.pageCurrent) sizer.Add(self.nb, 1, wx.EXPAND) aTable = wx.AcceleratorTable([ (wx.ACCEL_NORMAL, wx.WXK_ESCAPE, wx.ID_CANCEL), (wx.ACCEL_NORMAL, wx.WXK_RETURN, wx.ID_OK), ]) self.SetAcceleratorTable(aTable) # create buttons line = wx.StaticLine(self, -1, size=(20, -1), style=wx.LI_HORIZONTAL) sizer.Add(line, 0, wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.TOP, 5) btnsizer = wx.StdDialogButtonSizer() # ok btn = wx.Button(self, wx.ID_OK, _translate('OK')) btn.SetHelpText( _translate("Save prefs (in all sections) and close " "window")) btn.Bind(wx.EVT_BUTTON, self.onOK) btn.SetDefault() btnsizer.AddButton(btn) # cancel btn = wx.Button(self, wx.ID_CANCEL, _translate('Cancel')) btn.SetHelpText(_translate("Cancel any changes (to any panel)")) btn.Bind(wx.EVT_BUTTON, self.onCancel) btnsizer.AddButton(btn) # help btn = wx.Button(self, wx.ID_HELP, _translate('Help')) btn.SetHelpText(_translate("Get help on prefs")) btn.Bind(wx.EVT_BUTTON, self.onHelp) btnsizer.AddButton(btn) btnsizer.Realize() # add buttons to dlg sizer.Add(btnsizer, 0, wx.BOTTOM | wx.ALL, 5) self.SetSizerAndFit(sizer) self.SetAutoLayout(True) sizer.Fit(self)
from . import dialogs from psychopy import localization from psychopy.localization import _translate from pkg_resources import parse_version from psychopy import sound from psychopy.app.utils import getSystemFonts import collections # this will be overridden by the size of the scrolled panel making the prefs dlgSize = (600, 500) # labels mappings for display: _localized = { # section labels: 'general': _translate('General'), 'app': _translate('App'), 'builder': "Builder", # not localized 'coder': "Coder", # not localized 'hardware': _translate('Hardware'), 'connections': _translate('Connections'), 'keyBindings': _translate('Key bindings'), # pref labels: 'winType': _translate("window type"), 'units': _translate("units"), 'fullscr': _translate("full-screen"), 'allowGUI': _translate("allow GUI"), 'paths': _translate('paths'), 'audioLib': _translate("audio library"), 'audioDriver': _translate("audio driver"), 'audioDevice': _translate("audio device"),
def applyPrefs(self): """Write preferences to the current configuration.""" if not self.proPrefs.isModified(): return if platform.system() == 'Darwin': re_cmd2ctrl = re.compile('^Cmd\+', re.I) for sectionName in self.prefsSpec: for prefName in self.prefsSpec[sectionName]: if prefName in ['version']: # any other prefs not to show? continue thisPref = self.proPrefs.getPrefVal(sectionName, prefName) # handle special cases if prefName in ('codeFont', 'commentFont', 'outputFont'): self.prefsCfg[sectionName][prefName] = \ self.fontList[thisPref] continue elif prefName == 'audioDevice': self.prefsCfg[sectionName][prefName] = \ self.audioDevNames[thisPref] continue elif prefName == 'locale': # fake spec -> option: use available locale info not spec file self.app.prefs.app['locale'] = \ self.app.localization.available[thisPref] self.prefsCfg[sectionName][prefName] = \ self.app.localization.available[thisPref] continue # remove invisible trailing whitespace: if hasattr(thisPref, 'strip'): thisPref = thisPref.strip() # regularize the display format for keybindings if sectionName == 'keyBindings': thisPref = thisPref.replace(' ', '') thisPref = '+'.join([part.capitalize() for part in thisPref.split('+')]) if platform.system() == 'Darwin': # key-bindings were displayed as 'Cmd+O', revert to # 'Ctrl+O' internally thisPref = re_cmd2ctrl.sub('Ctrl+', thisPref) self.prefsCfg[sectionName][prefName] = thisPref # make sure list values are converted back to lists (from str) if self.prefsSpec[sectionName][prefName].startswith('list'): try: # if thisPref is not a null string, do eval() to get a # list. if thisPref == '' or type(thisPref) == list: newVal = thisPref else: newVal = eval(thisPref) except Exception: # if eval() failed, show warning dialog and return try: pLabel = _localized[prefName] sLabel = _localized[sectionName] except Exception: pLabel = prefName sLabel = sectionName txt = _translate( 'Invalid value in "%(pref)s" ("%(section)s" Tab)') msg = txt % {'pref': pLabel, 'section': sLabel} title = _translate('Error') warnDlg = dialogs.MessageDialog(parent=self, message=msg, type='Info', title=title) resp = warnDlg.ShowModal() return if type(newVal) != list: self.prefsCfg[sectionName][prefName] = [newVal] else: self.prefsCfg[sectionName][prefName] = newVal elif self.prefsSpec[sectionName][prefName].startswith('option'): vals = self.prefsSpec[sectionName][prefName].replace( "option(", "").replace("'", "") # item -1 is 'default=x' from spec options = vals.replace(", ", ",").split(',')[:-1] self.prefsCfg[sectionName][prefName] = options[thisPref] self.app.prefs.saveUserPrefs() # includes a validation # maybe then go back and set GUI from prefs again, because validation # may have changed vals? # > sure, why not? - mdc self.populatePrefs() # after validation, update the UI self.updateCoderUI()
def __init__(self, parent, noTitle=False, style=wx.VSCROLL | wx.NO_BORDER, project={}): scrlpanel.ScrolledPanel.__init__(self, parent, -1, style=style) self.parent = parent self.project = project # type: pavlovia.PavloviaProject self.noTitle = noTitle self.localFolder = '' self.syncPanel = None if not noTitle: self.title = wx.StaticText(parent=self, id=-1, label="", style=wx.ALIGN_CENTER) font = wx.Font(18, wx.DECORATIVE, wx.NORMAL, wx.BOLD) self.title.SetFont(font) # if we've synced before we should know the local location self.localFolderCtrl = wx.StaticText(parent=self, id=wx.ID_ANY, label=_translate("Local root: ")) self.browseLocalBtn = wx.Button(parent=self, id=wx.ID_ANY, label=_translate("Browse...")) self.browseLocalBtn.Bind(wx.EVT_BUTTON, self.onBrowseLocalFolder) # remote attributes self.url = wxhl.HyperLinkCtrl( parent=self, id=-1, label="https://pavlovia.org", URL="https://pavlovia.org", ) self.description = wx.StaticText( parent=self, id=-1, label=_translate("Select a project for details")) self.tags = wx.StaticText(parent=self, id=-1, label="") self.visibility = wx.StaticText(parent=self, id=-1, label="") self.syncButton = wx.Button(self, -1, _translate("Sync...")) self.syncButton.Enable(False) self.syncButton.Bind(wx.EVT_BUTTON, self.onSyncButton) self.syncPanel = sync.SyncStatusPanel(parent=self, id=wx.ID_ANY) # layout # sizers: on the right we have detail self.sizer = wx.BoxSizer(wx.VERTICAL) # self.sizer.Add(wx.StaticText(self, -1, _translate("Project Info")), # flag=wx.ALL, # border=5) if not noTitle: self.sizer.Add(self.title, border=5, flag=wx.ALL | wx.CENTER) self.sizer.Add(self.url, border=5, flag=wx.ALL | wx.CENTER) self.sizer.Add(self.localFolderCtrl, border=5, flag=wx.ALL | wx.EXPAND), self.sizer.Add(self.browseLocalBtn, border=5, flag=wx.ALL | wx.LEFT) self.sizer.Add(self.tags, border=5, flag=wx.ALL | wx.EXPAND) self.sizer.Add(self.visibility, border=5, flag=wx.ALL | wx.EXPAND) self.sizer.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), flag=wx.ALL | wx.EXPAND) self.sizer.Add(self.description, border=10, flag=wx.ALL | wx.EXPAND) self.sizer.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), flag=wx.ALL | wx.EXPAND) self.sizer.Add(self.syncButton, flag=wx.ALL | wx.RIGHT, border=5) self.sizer.Add(self.syncPanel, border=5, proportion=1, flag=wx.ALL | wx.RIGHT | wx.EXPAND) if self.project: self.setProject(self.project) self.syncPanel.setStatus(_translate("Ready to sync")) else: self.syncPanel.setStatus( _translate("This file doesn't belong to a project yet")) self.SetAutoLayout(True) self.SetSizerAndFit(self.sizer) self.SetupScrolling() self.Bind(wx.EVT_SIZE, self.onResize)
def onInit(self, showSplash=True, testMode=False): """This is launched immediately *after* the app initialises with wx :Parameters: testMode: bool """ self.SetAppName('PsychoPy3') if False: # show splash screen splashFile = os.path.join(self.prefs.paths['resources'], 'psychopySplash.png') splashImage = wx.Image(name=splashFile) splashImage.ConvertAlphaToMask() splash = AS.AdvancedSplash( None, bitmap=splashImage.ConvertToBitmap(), timeout=3000, agwStyle=AS.AS_TIMEOUT | AS.AS_CENTER_ON_SCREEN, ) # transparency? w, h = splashImage.GetSize() splash.SetTextPosition((int(w - 130), h - 20)) splash.SetText(_translate("Loading libraries...")) wx.Yield() else: splash = None # SLOW IMPORTS - these need to be imported after splash screen starts # but then that they end up being local so keep track in self if splash: splash.SetText(_translate("Loading PsychoPy3...")) wx.Yield() from psychopy.compatibility import checkCompatibility # import coder and builder here but only use them later from psychopy.app import coder, builder, dialogs if '--firstrun' in sys.argv: del sys.argv[sys.argv.index('--firstrun')] self.firstRun = True if 'lastVersion' not in self.prefs.appData: # must be before 1.74.00 last = self.prefs.appData['lastVersion'] = '1.73.04' self.firstRun = True else: last = self.prefs.appData['lastVersion'] if self.firstRun and not self.testMode: pass # setup links for URLs # on a mac, don't exit when the last frame is deleted, just show menu if sys.platform == 'darwin': self.menuFrame = MenuFrame(parent=None, app=self) # get preferred view(s) from prefs and previous view if self.prefs.app['defaultView'] == 'last': mainFrame = self.prefs.appData['lastFrame'] else: # configobjValidate should take care of this situation allowed = ['last', 'coder', 'builder', 'both'] if self.prefs.app['defaultView'] in allowed: mainFrame = self.prefs.app['defaultView'] else: self.prefs.app['defaultView'] = 'both' mainFrame = 'both' # fetch prev files if that's the preference if self.prefs.coder['reloadPrevFiles']: scripts = self.prefs.appData['coder']['prevFiles'] else: scripts = [] appKeys = list(self.prefs.appData['builder'].keys()) if self.prefs.builder['reloadPrevExp'] and ('prevFiles' in appKeys): exps = self.prefs.appData['builder']['prevFiles'] else: exps = [] # then override the prev files by command options and passed files if len(sys.argv) > 1: if sys.argv[1] == __name__: # program was executed as "python.exe psychopyApp.py %1' args = sys.argv[2:] else: # program was executed as "psychopyApp.py %1' args = sys.argv[1:] # choose which frame to start with if args[0] in ['builder', '--builder', '-b']: mainFrame = 'builder' args = args[1:] # can remove that argument elif args[0] in ['coder', '--coder', '-c']: mainFrame = 'coder' args = args[1:] # can remove that argument # did we get .py or .psyexp files? elif args[0][-7:] == '.psyexp': mainFrame = 'builder' exps = [args[0]] elif args[0][-3:] == '.py': mainFrame = 'coder' scripts = [args[0]] else: args = [] self.dpi = int(wx.GetDisplaySize()[0] / float(wx.GetDisplaySizeMM()[0]) * 25.4) if not (50 < self.dpi < 120): self.dpi = 80 # dpi was unreasonable, make one up if sys.platform == 'win32': # wx.SYS_DEFAULT_GUI_FONT is default GUI font in Win32 self._mainFont = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT) else: self._mainFont = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT) try: self._codeFont = wx.SystemSettings.GetFont(wx.SYS_ANSI_FIXED_FONT) except wx._core.wxAssertionError: # if no SYS_ANSI_FIXED_FONT then try generic FONTFAMILY_MODERN self._codeFont = wx.Font(self._mainFont.GetPointSize(), wx.FONTFAMILY_MODERN, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL) self._codeFont.SetFaceName(self.prefs.coder['codeFont']) # removed Aug 2017: on newer versions of wx (at least on mac) # this looks too big # if hasattr(self._mainFont, 'Larger'): # # Font.Larger is available since wyPython version 2.9.1 # # PsychoPy still supports 2.8 (see ensureMinimal above) # self._mainFont = self._mainFont.Larger() # self._codeFont.SetPointSize( # self._mainFont.GetPointSize()) # unify font size # create both frame for coder/builder as necess if splash: splash.SetText(_translate(" Creating frames...")) if mainFrame in ['both', 'coder']: self.showCoder(fileList=scripts) if mainFrame in ['both', 'builder']: self.showBuilder(fileList=exps) # send anonymous info to www.psychopy.org/usage.php # please don't disable this, it's important for PsychoPy's development self._latestAvailableVersion = None self.updater = None self.news = None self.tasks = None prefsConn = self.prefs.connections ok, msg = checkCompatibility(last, self.version, self.prefs, fix=True) # tell the user what has changed if not ok and not self.firstRun and not self.testMode: title = _translate("Compatibility information") dlg = dialogs.MessageDialog(parent=None, message=msg, type='Info', title=title) dlg.ShowModal() if (self.prefs.app['showStartupTips'] and not self.testMode and not blockTips): tipFile = os.path.join(self.prefs.paths['resources'], _translate("tips.txt")) tipIndex = self.prefs.appData['tipIndex'] if parse_version(wx.__version__) >= parse_version('4.0.0a1'): tp = wx.adv.CreateFileTipProvider(tipFile, tipIndex) showTip = wx.adv.ShowTip(None, tp) else: tp = wx.CreateFileTipProvider(tipFile, tipIndex) showTip = wx.ShowTip(None, tp) self.prefs.appData['tipIndex'] = tp.GetCurrentTip() self.prefs.saveAppData() self.prefs.app['showStartupTips'] = showTip self.prefs.saveUserPrefs() self.Bind(wx.EVT_IDLE, self.onIdle) # doing this once subsequently enables the app to open & switch among # wx-windows on some platforms (Mac 10.9.4) with wx-3.0: v = parse_version if sys.platform == 'darwin': if v('3.0') <= v(wx.version()) < v('4.0'): _Showgui_Hack() # returns ~immediately, no display # focus stays in never-land, so bring back to the app: if mainFrame in ['both', 'builder']: self.showBuilder() else: self.showCoder() # after all windows are created (so errors flushed) create output self._appLoaded = True if self.coder: self.coder.setOutputWindow() # takes control of sys.stdout return True
def __init__(self, exp, parentName, name='', units='from exp settings', color='$[1,1,1]', borderColor="", fillColor="", pos=(0, 0), size=(0, 0), ori=0, colorSpace='rgb', opacity=1, startType='time (s)', startVal='', stopType='duration (s)', stopVal='', startEstim='', durationEstim='', saveStartStop=True, syncScreenRefresh=True): super(BaseVisualComponent, self).__init__(exp, parentName, name, startType=startType, startVal=startVal, stopType=stopType, stopVal=stopVal, startEstim=startEstim, durationEstim=durationEstim, saveStartStop=saveStartStop, syncScreenRefresh=syncScreenRefresh) self.exp.requirePsychopyLibs(['visual' ]) # needs this psychopy lib to operate msg = _translate("Units of dimensions for this stimulus") self.params['units'] = Param(units, valType='str', categ='Layout', allowedVals=[ 'from exp settings', 'deg', 'cm', 'pix', 'norm', 'height', 'degFlatPos', 'degFlat' ], hint=msg, label=_localized['units']) msg = _translate( "Foreground color of this stimulus (e.g. $[1,1,0], red );" " Right-click to bring up a color-picker (rgb only)") self.params['color'] = Param( color, valType='str', allowedTypes=[], categ='Appearance', updates='constant', allowedUpdates=['constant', 'set every repeat', 'set every frame'], hint=msg, label=_localized['color']) msg = _translate("In what format (color space) have you specified " "the foreground color? (rgb, dkl, lms, hsv)") self.params['colorSpace'] = Param( colorSpace, valType='str', categ='Appearance', allowedVals=['rgb', 'dkl', 'lms', 'hsv'], updates='constant', hint=msg, label=_localized['colorSpace']) msg = _translate("Fill color of this stimulus (e.g. $[1,1,0], red );" " Right-click to bring up a color-picker (rgb only)") self.params['fillColor'] = Param( fillColor, valType='str', allowedTypes=[], categ='Appearance', updates='constant', allowedUpdates=['constant', 'set every repeat', 'set every frame'], hint=msg, label=_localized['fillColor']) msg = _translate("In what format (color space) have you specified " "the fill color? (rgb, dkl, lms, hsv)") self.params['fillColorSpace'] = Param( colorSpace, valType='str', categ='Appearance', allowedVals=['rgb', 'dkl', 'lms', 'hsv'], updates='constant', hint=msg, label=_localized['fillColorSpace']) msg = _translate("Color of this stimulus (e.g. $[1,1,0], red );" " Right-click to bring up a color-picker (rgb only)") self.params['borderColor'] = Param( borderColor, valType='str', allowedTypes=[], categ='Appearance', updates='constant', allowedUpdates=['constant', 'set every repeat', 'set every frame'], hint=msg, label=_localized['borderColor']) msg = _translate("In what format (color space) have you specified " "the border color? (rgb, dkl, lms, hsv)") self.params['borderColorSpace'] = Param( colorSpace, valType='str', categ='Appearance', allowedVals=['rgb', 'dkl', 'lms', 'hsv'], updates='constant', hint=msg, label=_localized['borderColorSpace']) msg = _translate("Opacity of the stimulus (1=opaque, 0=fully " "transparent, 0.5=translucent)") self.params['opacity'] = Param( opacity, valType='code', allowedTypes=[], categ='Appearance', updates='constant', allowedUpdates=['constant', 'set every repeat', 'set every frame'], hint=msg, label=_localized['opacity']) msg = _translate("Position of this stimulus (e.g. [1,2] )") self.params['pos'] = Param( pos, valType='code', allowedTypes=[], categ='Layout', updates='constant', allowedUpdates=['constant', 'set every repeat', 'set every frame'], hint=msg, label=_localized['pos']) msg = _translate("Size of this stimulus (either a single value or " "x,y pair, e.g. 2.5, [1,2] ") self.params['size'] = Param( size, valType='code', allowedTypes=[], categ='Layout', updates='constant', allowedUpdates=['constant', 'set every repeat', 'set every frame'], hint=msg, label=_localized['size']) self.params['ori'] = Param( ori, valType='code', allowedTypes=[], categ='Layout', updates='constant', allowedUpdates=['constant', 'set every repeat', 'set every frame'], hint=_translate("Orientation of this stimulus (in deg)"), label=_localized['ori']) self.params['syncScreenRefresh'].readOnly = True
def makeTools(self): iconCache = self.app.iconCache iconSize = 16 # Load in icons for toolbars # Redraw toolbar buttons self.newFolderTool = iconCache.makeBitmapButton( self, filename='foldernew', name='foldernew', label=_translate( 'New Folder'), toolbar=self.toolBar, tip=_translate( "Create a new folder in the current folder"), size=iconSize) self.renameTool = iconCache.makeBitmapButton( self, filename='rename', name='rename', label=_translate('Rename'), toolbar=self.toolBar, tip=_translate( "Rename the selected folder or file"), size=iconSize) self.deleteTool = iconCache.makeBitmapButton( self, filename='delete', name='delete', label=_translate('Delete'), toolbar=self.toolBar, tip=_translate( "Delete the selected folder or file"), size=iconSize) self.gotoTool = iconCache.makeBitmapButton( self, filename='goto', name='goto', label=_translate('Goto'), toolbar=self.toolBar, tip=_translate( "Jump to another folder"), size=iconSize, tbKind=wx.ITEM_DROPDOWN) # create the dropdown menu for goto self.gotoMenu = wx.Menu() item = self.gotoMenu.Append( wx.ID_ANY, _translate("Browse ..."), _translate("Browse the file system for a directory to open")) self.Bind(wx.EVT_MENU, self.OnBrowse, id=item.GetId()) self.gotoMenu.AppendSeparator() item = self.gotoMenu.Append( wx.ID_ANY, _translate("Current working directory"), _translate("Open the current working directory")) self.Bind(wx.EVT_MENU, self.OnGotoCWD, id=item.GetId()) item = self.gotoMenu.Append( wx.ID_ANY, _translate("Editor file location"), _translate("Open the directory the current editor file is located")) self.Bind(wx.EVT_MENU, self.OnGotoFileLocation, id=item.GetId()) # Bind toolbar buttons self.Bind(wx.EVT_TOOL, self.OnBrowse, self.gotoTool) self.Bind(aui.EVT_AUITOOLBAR_TOOL_DROPDOWN, self.OnGotoMenu, self.gotoTool) self.Bind(wx.EVT_TOOL, self.OnNewFolderTool, self.newFolderTool) self.Bind(wx.EVT_TOOL, self.OnDeleteTool, self.deleteTool) self.Bind(wx.EVT_TOOL, self.OnRenameTool, self.renameTool) self.gotoTool.SetDropdownMenu(self.gotoMenu) # Realise self.toolBar.Realize()
def __init__(self, exp, parentName, name='', startType='time (s)', startVal='', stopType='duration (s)', stopVal='', startEstim='', durationEstim='', saveStartStop=True, syncScreenRefresh=False, disabled=False): self.type = 'Base' self.exp = exp # so we can access the experiment if necess self.parentName = parentName # to access the routine too if needed self.params = {} self.depends = [] # allows params to turn each other off/on """{ "dependsOn": "shape", "condition": "=='n vertices", "param": "n vertices", "true": "enable", # what to do with param if condition is True "false": "disable", # permitted: hide, show, enable, disable }""" msg = _translate( "Name of this component (alpha-numeric or _, no spaces)") self.params['name'] = Param(name, valType='code', categ='Basic', hint=msg, label=_localized['name']) msg = _translate("How do you want to define your start point?") self.params['startType'] = Param( startType, valType='str', categ='Basic', allowedVals=['time (s)', 'frame N', 'condition'], hint=msg, label=_localized['startType']) msg = _translate("How do you want to define your end point?") self.params['stopType'] = Param(stopType, valType='str', categ='Basic', allowedVals=[ 'duration (s)', 'duration (frames)', 'time (s)', 'frame N', 'condition' ], hint=msg, label=_localized['stopType']) self.params['startVal'] = Param( startVal, valType='code', allowedTypes=[], categ='Basic', hint=_translate("When does the component start?"), label=_localized['startVal']) self.params['stopVal'] = Param( stopVal, valType='code', allowedTypes=[], categ='Basic', updates='constant', allowedUpdates=[], hint=_translate("When does the component end? (blank is endless)"), label=_localized['stopVal']) msg = _translate("(Optional) expected start (s), purely for " "representing in the timeline") self.params['startEstim'] = Param(startEstim, valType='code', allowedTypes=[], categ='Basic', hint=msg, label=_localized['startEstim']) msg = _translate("(Optional) expected duration (s), purely for " "representing in the timeline") self.params['durationEstim'] = Param(durationEstim, valType='code', allowedTypes=[], categ='Basic', hint=msg, label=_localized['durationEstim']) msg = _translate("Store the onset/offset times in the data file " "(as well as in the log file).") self.params['saveStartStop'] = Param( saveStartStop, valType='bool', allowedTypes=[], categ='Data', hint=msg, label=_translate('Save onset/offset times')) msg = _translate("Synchronize times with screen refresh (good for " "visual stimuli and responses based on them)") self.params['syncScreenRefresh'] = Param( syncScreenRefresh, valType='bool', allowedTypes=[], hint=msg, categ="Data", label=_translate('Sync timing with screen refresh')) msg = _translate("Disable this component") self.params['disabled'] = Param(disabled, valType='bool', allowedTypes=[], hint=msg, categ="Testing", label=_translate('Disable component')) self.order = ['name'] # name first, then timing, then others
def __init__(self, frame, title, params, order, helpUrl=None, suppressTitles=True, size=wx.DefaultSize, style=_style, editing=False, depends=[], timeout=None): # translate title localizedTitle = title.replace(' Properties', _translate(' Properties')) wx.Dialog.__init__(self, None, -1, localizedTitle, size=size, style=style) self.frame = frame self.app = frame.app self.helpUrl = helpUrl self.params = params # dict self.order = order self.title = title self.timeout = timeout self.warningsDict = {} # to store warnings for all fields # keep localized title to update dialog's properties later. self.localizedTitle = localizedTitle self.codeBoxes = {} self.tabs = OrderedDict() if not editing and 'name' in self.params: # then we're adding a new component so ensure a valid name: makeValid = self.frame.exp.namespace.makeValid self.params['name'].val = makeValid(params['name'].val) agwStyle = flatnotebook.FNB_NO_X_BUTTON if hasattr(flatnotebook, "FNB_NAV_BUTTONS_WHEN_NEEDED"): # not available in wxPython 2.8 agwStyle |= flatnotebook.FNB_NAV_BUTTONS_WHEN_NEEDED if hasattr(flatnotebook, "FNB_NO_TAB_FOCUS"): # not available in wxPython 2.8.10 agwStyle |= flatnotebook.FNB_NO_TAB_FOCUS self.codeNotebook = flatnotebook.FlatNotebook(self, wx.ID_ANY, style=agwStyle) openToPage = None tabN = -1 for paramN, paramName in enumerate(self.order): param = self.params.get(paramName) if paramName == 'name': self.nameLabel = wx.StaticText(self, wx.ID_ANY, _translate(param.label)) _style = wx.TE_PROCESS_ENTER | wx.TE_PROCESS_TAB self.componentName = wx.TextCtrl(self, wx.ID_ANY, str(param.val), style=_style) self.componentName.SetToolTip( wx.ToolTip(_translate(param.hint))) self.componentName.SetValidator(validators.NameValidator()) self.nameOKlabel = wx.StaticText(self, -1, '', style=wx.ALIGN_RIGHT) self.nameOKlabel.SetForegroundColour(wx.RED) elif paramName == 'Code Type': # Create code type choice menu _codeTypes = self.params['Code Type'].allowedVals _selectedCodeType = self.params['Code Type'].val _selectedCodeTypeIndex = _codeTypes.index(_selectedCodeType) self.codeTypeMenu = wx.Choice(self, choices=_codeTypes) # If user does not have metapensiero but codetype is auto-js, revert to (Py?) if not hasMetapensiero and _selectedCodeType.lower( ) == 'auto->js': _selectedCodeTypeIndex -= 1 # Set selection to value stored in self params self.codeTypeMenu.SetSelection(_selectedCodeTypeIndex) self.codeTypeMenu.Bind(wx.EVT_CHOICE, self.onCodeChoice) self.codeTypeName = wx.StaticText(self, wx.ID_ANY, _translate(param.label)) else: codeType = ["Py", "JS"]["JS" in paramName] # Give CodeBox a code type tabName = paramName.replace("JS ", "") if tabName in self.tabs: _panel = self.tabs[tabName] else: _panel = wx.Panel(self.codeNotebook, wx.ID_ANY) self.tabs[tabName] = _panel tabN += 1 self.codeBoxes[paramName] = CodeBox(_panel, wx.ID_ANY, pos=wx.DefaultPosition, style=0, prefs=self.app.prefs, params=params, codeType=codeType) self.codeBoxes[paramName].AddText(param.val) self.codeBoxes[paramName].Bind( wx.EVT_KEY_UP, self.onKeyUp) # For real time translation if len(param.val.strip()) and openToPage is None: # index of first non-blank page openToPage = tabN if self.helpUrl is not None: self.helpButton = wx.Button(self, wx.ID_HELP, _translate(" Help ")) tip = _translate("Go to online help about this component") self.helpButton.SetToolTip(wx.ToolTip(tip)) self.okButton = wx.Button(self, wx.ID_OK, _translate(" OK ")) self.okButton.SetDefault() self.cancelButton = wx.Button(self, wx.ID_CANCEL, _translate(" Cancel ")) self.__set_properties() self.__do_layout() if openToPage is None: openToPage = 0 self.codeNotebook.SetSelection(openToPage) self.Update() self.Bind(wx.EVT_BUTTON, self.helpButtonHandler, self.helpButton) if self.timeout: timeout = wx.CallLater(self.timeout, self.onEnter) timeout.Start() # do show and process return ret = self.ShowModal() if ret == wx.ID_OK: self.checkName() self.OK = True self.params = self.getParams() # get new vals from dlg self.Validate() else: self.OK = False
def __init__(self, app, parent=None, pos=wx.DefaultPosition, style=None): if style is None: style = (wx.DEFAULT_DIALOG_STYLE | wx.CENTER | wx.TAB_TRAVERSAL | wx.RESIZE_BORDER) title = _translate("Search for projects online") self.frameType = 'ProjectSearch' wx.Dialog.__init__(self, parent, -1, title=title, style=style, size=(700, 500)) self.app = app self.project = None self.parent = parent # info about last search (NB None means no search but [] or '' means empty) self.lastSearchStr = None self.lastSearchOwn = None self.lastSearchGp = None self.lastSearchPub = None # self.mainPanel = wx.Panel(self, wx.ID_ANY) self.searchLabel = wx.StaticText(self, wx.ID_ANY, _translate('Search:')) self.searchCtrl = wx.TextCtrl(self, wx.ID_ANY) self.searchCtrl.Bind(wx.EVT_BUTTON, self.onSearch) self.searchBtn = wx.Button(self, wx.ID_ANY, _translate("Search")) self.searchBtn.Bind(wx.EVT_BUTTON, self.onSearch) self.searchBtn.SetDefault() self.searchInclPublic = wx.CheckBox(self, wx.ID_ANY, label="Public") self.searchInclPublic.Bind(wx.EVT_CHECKBOX, self.onSearch) self.searchInclPublic.SetValue(True) self.searchInclGroup = wx.CheckBox(self, wx.ID_ANY, label="My groups") self.searchInclGroup.Bind(wx.EVT_CHECKBOX, self.onSearch) self.searchInclGroup.SetValue(True) self.searchBuilderOnly = wx.CheckBox(self, wx.ID_ANY, label="Only Builder") self.searchBuilderOnly.Bind(wx.EVT_CHECKBOX, self.onSearch) # then the search results self.searchResults = ProjectListCtrl(self) # on the right self.detailsPanel = DetailsPanel(parent=self) # sizers layout self.searchBtnSizer = wx.BoxSizer(wx.HORIZONTAL) self.searchBtnSizer.Add(self.searchCtrl, 1, wx.EXPAND | wx.ALL, 5) self.searchBtnSizer.Add(self.searchBtn, 0, wx.EXPAND | wx.ALL, 5) self.optionsSizer = wx.WrapSizer() self.optionsSizer.AddMany([ self.searchInclGroup, self.searchInclPublic, self.searchBuilderOnly ]) self.leftSizer = wx.BoxSizer(wx.VERTICAL) self.leftSizer.Add(self.searchLabel, 0, wx.EXPAND | wx.ALL, 5) self.leftSizer.Add(self.optionsSizer) self.leftSizer.Add(self.searchBtnSizer, 0, wx.EXPAND | wx.ALL, 5) self.leftSizer.Add(self.searchResults, 1, wx.EXPAND | wx.ALL, 5) self.mainSizer = wx.BoxSizer(wx.HORIZONTAL) self.mainSizer.Add(self.leftSizer, 1, wx.EXPAND | wx.ALL, 5) self.mainSizer.Add(self.detailsPanel, 1, wx.EXPAND | wx.ALL, 5) self.SetSizer(self.mainSizer) if self.parent: self.CenterOnParent()
import numpy as np import platform import codecs from pkg_resources import parse_version if parse_version(wx.__version__) < parse_version('2.9'): tmpApp = wx.PySimpleApp() else: tmpApp = wx.App(False) from psychopy.localization import _translate from psychopy import (info, data, visual, gui, core, __version__, prefs, event, constants) # set values, using a form that poedit can discover: _localized = { 'Benchmark': _translate('Benchmark'), 'benchmark version': _translate('benchmark version'), 'full-screen': _translate('full-screen'), 'dots_circle': _translate('dots_circle'), 'dots_square': _translate('dots_square'), 'available memory': _translate('available memory'), 'python version': _translate('python version'), 'locale': _translate('locale'), 'Visual': _translate('Visual'), 'openGL version': _translate('openGL version'), 'openGL vendor': _translate('openGL vendor'), 'screen size': _translate('screen size'), 'have shaders': _translate('have shaders'), 'refresh stability (SD)': _translate('refresh stability (SD)'), 'no dropped frames': _translate('no dropped frames'), 'pyglet avbin': _translate('pyglet avbin'),
Discover all _localized strings from all Builder components, etc. Mainly used by validators.py -- need access to _translate()d field names. """ from __future__ import absolute_import, print_function import copy import os import glob from psychopy.localization import _localized as _localizedBase from psychopy.localization import _translate _localizedDialogs = { # strings for all allowedVals (from all components) go here: # interpolation 'linear': _translate('linear'), 'nearest': _translate('nearest'), # color spaces not translated: 'rgb': 'rgb', 'dkl': 'dkl', 'lms': 'lms', 'hsv': 'hsv', 'last key': _translate('last key'), 'first key': _translate('first key'), 'all keys': _translate('all keys'), 'nothing': _translate('nothing'), 'last button': _translate('last button'), 'first button': _translate('first button'), 'all buttons': _translate('all buttons'), 'final': _translate('final'), 'on click': _translate('on click'), 'every frame': _translate('every frame'), 'never': _translate('never'), 'from exp settings': _translate('from exp settings'),
def __init__(self, parent): wx.Menu.__init__(self) self.parent = parent ProjectsMenu.app = parent.app keys = self.app.keys # from prefs fetch info about prev usernames and projects ProjectsMenu.appData = self.app.prefs.appData['projects'] global projHistory self.projHistory = projHistory # so we can treat as local from here global usersList self.userList = usersList item = self.Append(wx.ID_ANY, _translate("Tell me more...")) parent.Bind(wx.EVT_MENU, self.onAbout, id=item.GetId()) if not havePyosf: self.Append(wx.ID_ANY, _translate("Requires pyosf (not installed)")) ProjectsMenu.knownUsers = {} else: if self.app.osf_session is None: # create a default (anonymous) session with osf self.app.osf_session = pyosf.Session() ProjectsMenu.knownUsers = pyosf.TokenStorage() # a dict name:token # sub-menu to open previous or new projects self.projsSubMenu = wx.Menu() item = self.projsSubMenu.Append( wx.ID_ANY, _translate("From file...\t{}").format(keys['projectsOpen'])) parent.Bind(wx.EVT_MENU, self.onOpenFile, id=item.GetId()) self.projsSubMenu.AppendSeparator() self.projHistory.UseMenu(self.projsSubMenu) try: self.projHistory.AddFilesToMenu(self.projsSubMenu) except: self.projHistory.AddFilesToThisMenu(self.projsSubMenu) parent.Bind(wx.EVT_MENU_RANGE, self.onProjFromHistory, id=self.projHistory.idBase, id2=self.projHistory.idBase + 9) self.AppendSubMenu(self.projsSubMenu, _translate("Open")) # sub-menu for usernames and login self.userMenu = wx.Menu() # if a user was previously logged in then set them as current if ProjectsMenu.appData['user'] and \ ProjectsMenu.appData['user'] in self.knownUsers: self.setUser(ProjectsMenu.appData['user']) for name in self.knownUsers: self.addToSubMenu(name, self.userMenu, self.onSetUser) self.userMenu.AppendSeparator() item = self.userMenu.Append( wx.ID_ANY, _translate("Log in...\t{}").format(keys['projectsLogIn'])) parent.Bind(wx.EVT_MENU, self.onLogIn, id=item.GetId()) self.AppendSubMenu(self.userMenu, _translate("User")) # search item = self.Append( wx.ID_ANY, _translate("Search OSF\t{}").format(keys['projectsFind'])) parent.Bind(wx.EVT_MENU, self.onSearch, id=item.GetId()) # new item = self.Append( wx.ID_ANY, _translate("New...\t{}").format(keys['projectsNew'])) parent.Bind(wx.EVT_MENU, self.onNew, id=item.GetId())
def syncProject(parent, project=None, closeFrameWhenDone=False): """A function to sync the current project (if there is one) Returns ----------- 1 for success 0 for fail -1 for cancel at some point in the process """ if not pavlovia.haveGit: noGitWarning(parent) return 0 isCoder = hasattr(parent, 'currentDoc') # Test and reject sync from invalid folders if isCoder: currentPath = os.path.dirname(parent.currentDoc.filename) else: currentPath = os.path.dirname(parent.filename) currentPath = os.path.normcase(os.path.expanduser(currentPath)) invalidFolders = [ os.path.normcase(os.path.expanduser('~/Desktop')), os.path.normcase(os.path.expanduser('~/My Documents')) ] if currentPath in invalidFolders: wx.MessageBox( ("You cannot sync projects from:\n\n" " - Desktop\n" " - My Documents\n\n" "Please move your project files to another folder, and try again." ), "Project Sync Error", wx.ICON_QUESTION | wx.OK) return -1 if not project and "BuilderFrame" in repr(parent): # try getting one from the frame project = parent.project # type: pavlovia.PavloviaProject if not project: # ask the user to create one # if we're going to create a project we need user to be logged in pavSession = pavlovia.getCurrentSession() try: username = pavSession.user.username except: username = logInPavlovia(parent) if not username: return -1 # never logged in # create project dialog msg = _translate("This file doesn't belong to any existing project.") style = wx.OK | wx.CANCEL | wx.CENTER dlg = wx.MessageDialog(parent=parent, message=msg, style=style) dlg.SetOKLabel(_translate("Create a project")) if dlg.ShowModal() == wx.ID_OK: if isCoder: if parent.currentDoc: localRoot = os.path.dirname(parent.currentDoc.filename) else: localRoot = '' else: localRoot = os.path.dirname(parent.filename) # open the project editor (with no project to create one) editor = ProjectEditor(parent=parent, localRoot=localRoot) if editor.ShowModal() == wx.ID_OK: project = editor.project else: project = None else: return -1 # user pressed cancel if not project: # we did our best for them. Give up! return 0 # if project.localRoot doesn't exist, or is empty if 'localRoot' not in project or not project.localRoot: # we first need to choose a location for the repository setLocalPath(parent, project) parent.Raise() # make sure that frame is still visible #check that the project does exist remotely if not project.pavlovia: # project didn't exist at Pavlovia (deleted?) recreatorDlg = ProjectRecreator(parent=parent, project=project) ok = recreatorDlg.ShowModal() if ok > 0: project = recreatorDlg.project else: logging.error("Failed to recreate project to sync with") return 0 # a sync will be necessary so set the target to Runner stdout parent.app.showRunner() syncFrame = parent.app.runner.stdOut if project._newRemote: # new remote so this will be a first push if project.getRepo(forceRefresh=True) is None: # no local repo yet so create one project.newRepo(syncFrame) # add the local files and commit them ok = showCommitDialog(parent=parent, project=project, initMsg="First commit", infoStream=syncFrame) if ok == -1: # cancelled syncFrame.Destroy() return -1 syncFrame.setStatus("Pushing files to Pavlovia") wx.Yield() time.sleep(0.001) # git push -u origin master try: project.firstPush(infoStream=syncFrame) project._newRemote = False except Exception as e: closeFrameWhenDone = False syncFrame.statusAppend(traceback.format_exc()) else: # existing remote which we should sync (or clone) try: ok = project.getRepo(syncFrame) if not ok: closeFrameWhenDone = False except Exception as e: closeFrameWhenDone = False syncFrame.statusAppend(traceback.format_exc()) # check for anything to commit before pull/push outcome = showCommitDialog(parent, project, infoStream=syncFrame) # 0=nothing to do, 1=OK, -1=cancelled if outcome == -1: # user cancelled return -1 try: status = project.sync(syncFrame) if status == -1: syncFrame.statusAppend("Couldn't sync") except Exception: # not yet sure what errors might occur # send the error to panel syncFrame.statusAppend(traceback.format_exc()) return 0 wx.Yield() project._lastKnownSync = time.time() if closeFrameWhenDone: pass return 1