Beispiel #1
0
	def add_cyclevar(self):

		"""Presents a dialog and add a variable,"""

		var_name, ok = QtGui.QInputDialog.getText(self.loop_table,
			_(u'New variable'),
			_(u'Enter a variable name, optionally followed by a default value (i.e., \"varname defaultvalue\")'))

		if not ok:
			return
		l = self.cyclevar_list()
		var_name = str(var_name)
		# Split by space, because a name may be followed by a default value
		_l = var_name.split()
		if len(_l) > 1:
			default = _l[1]
			var_name = _l[0]
		else:
			default = u""
		if not self.name_valid_and_unique(var_name):
			return
		for i in range(self.var.cycles):
			if i not in self.matrix:
				self.matrix[i] = {}
			self.matrix[i][var_name] = default
		self.refresh_loop_table()
		self.apply_edit_changes()
Beispiel #2
0
	def name_valid_and_unique(self, name):

		"""
		desc:
			Checks if a variable name is syntactically valid and unique.

		arguments:
			name:	The name to check.

		returns:
			True if the name is valid and unique, False otherwise.
		"""

		if not self.syntax.valid_var_name(name):
			self.experiment.notify(
				_(u'"%s" is not a valid variable name. Valid names consist of '
				u'letters, numbers, and underscores, and do not start with a '
				u'number.') % name)
			return False
		var_list = self.cyclevar_list()
		if var_list is not None and name in var_list:
			self.experiment.notify(
				_(u"A variable with the name '%s' already exists") % \
				name)
			return False
		return True
    def purge_unused(self):

        """Remove all unused items from the items list"""

        # Ask confirmation
        resp = QtGui.QMessageBox.question(
            self.main_window.ui.centralwidget,
            _(u"Permanently delete items?"),
            _(u"Are you sure you want to permanently delete all unused items? This action cannot be undone."),
            QtGui.QMessageBox.Yes,
            QtGui.QMessageBox.No,
        )
        if resp == QtGui.QMessageBox.No:
            return

            # We need a loop, because items may become unused
            # by deletion of their unused parent items
        while len(self.main_window.experiment.unused_items) > 0:
            for item in self.main_window.experiment.unused_items:
                if item in self.main_window.experiment.items:
                    del self.main_window.experiment.items[item]
            self.main_window.experiment.build_item_tree()

            # Notify dispatch
        self.main_window.dispatch.event_structure_change.emit(u"")
        self.main_window.ui.tabwidget.close_all()
        self.main_window.ui.tabwidget.open_general()
	def _apply(self):

		"""
		desc:
			Confirms and applies the script changes.
		"""

		resp = QtGui.QMessageBox.question(self.main_window, _(u'Apply?'),
			_(u'Are you sure you want to apply the changes to the general script?'),
			QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
		if resp == QtGui.QMessageBox.No:
			return
		try:
			exp = experiment(self.main_window, name=self.experiment.title,
				string=self.ui.qprogedit.text(),
				pool_folder=self.experiment.pool_folder,
				experiment_path=self.experiment.experiment_path,
				resources=self.experiment.resources)
		except osexception as e:
			self.notify(e.html())
			self.main_window.print_debug_window(e)
			return
		self.main_window.experiment = exp
		self.main_window.tabwidget.close_all()
		self.main_window.tabwidget.open_general()
		self.experiment.build_item_tree()
		self.extension_manager.fire(u'regenerate')
Beispiel #5
0
	def contextMenuEvent(self, event):

		"""
		desc:
			Presents a context menu.

		arguments:
			event:
			 	type:	QMouseClickEvent
		"""

		item = self.ui.list_pool.itemAt(self.ui.list_pool.mapFromGlobal(
			event.globalPos()))
		if item is None:
			return
		path = item.path
		pm = popup_menu(self, [
			(0, _(u'Open'), item.icon),
			(1, _(u'Remove from pool'), u'delete'),
			(2, _(u'Rename'), u'rename'),
			])
		action = pm.show()
		if action == 0:
			self.open_file(path)
		elif action == 1:
			self.delete_selected_files()
		elif action == 2:
			self.ui.list_pool.editItem(item)
Beispiel #6
0
	def set_cycle_count(self, cycles, confirm=True):

		"""
		Sets the nr of cycles and truncates data if necessary.

		Arguments:
		cycles		--	The number of cycles.

		Keyword arguments:
		confirm		--	Indicates whether confirmation is required before data
						is removed from the table. (default=True)
		"""

		if self.lock_cycles:
			return
		self.lock_cycles = True
		debug.msg(u"cycles = %s" % cycles)
		# Ask for confirmation before reducing the number of cycles.
		if len(self.matrix) > cycles and confirm:
			resp = QtGui.QMessageBox.question(self.experiment.ui.centralwidget,
				_(u"Remove cycles?"),
				_(u"By reducing the number of cycles, data will be lost from the table. Do you wish to continue?"),
				QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
			if resp == QtGui.QMessageBox.No:
				self.loop_widget.ui.spin_cycles.setValue(self.cycles)
				self.lock_cycles = False
				return
		for i in self.matrix.keys():
			if i >= cycles:
				del self.matrix[i]		
		self.lock_cycles = False
		self.set(u"cycles", cycles)
	def closeEvent(self, e):

		"""
		desc:
			Process a request to close the application.

		arguments:
			e:
				type:	QCloseEvent
		"""

		if debug.enabled or self.devmode:
			libopensesame.experiment.clean_up(debug.enabled)
			self.save_state()
			e.accept()
			return
		resp = QtGui.QMessageBox.question(self.ui.centralwidget, _(u"Quit?"),
			_(u"Are you sure you want to quit OpenSesame?"),
			QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
		if resp == QtGui.QMessageBox.No:
			if not isinstance(e, bool):
				e.ignore()
				return
		if not self.save_unsaved_changes():
			e.ignore()
			return
		self.extension_manager.fire(u'close')
		self.save_state()
		libopensesame.experiment.clean_up(debug.enabled)
		e.accept()
	def __init__(self, main_window):

		"""
		desc:
			Constructor.

		arguments:
			main_window:	The main-window object.
		"""

		self.setup(main_window)
		self.main_window.set_status(_(u'Loading extensions'), timeout=None,
			status=u'busy')
		QtGui.QApplication.processEvents()
		self._extensions = []
		self.events = {}
		for ext_name in plugins.list_plugins(_type=u'extensions'):
			try:
				ext = plugins.load_extension(ext_name, self.main_window)
			except Exception as e:
				if not isinstance(e, osexception):
					e = osexception(msg=u'Extension error', exception=e)
				self.notify(
					u'Failed to load extension %s (see debug window for stack trace)' \
					% ext_name)
				self.main_window.print_debug_window(e)
			self._extensions.append(ext)
			for event in ext.supported_events():
				if event not in self.events:
					self.events[event] = []
				self.events[event].append(ext)
		self.main_window.set_status(_(u'Done'), status=u'ready')
	def permanently_delete(self):

		"""
		desc:
			Permanently deletes the item, if possible.
		"""

		if not self.is_deletable() and not self.is_unused():
			return
		if QtGui.QMessageBox.question(self.treeWidget(),
			_(u'Permanently delete item'),
			_(u'Are you sure you want to permanently delete <b>%s</b>? All linked copies of <b>%s</b> will be deleted. You will not be able to undo this.') \
			% (self.name, self.name),
			buttons=(QtGui.QMessageBox.Yes|QtGui.QMessageBox.No),
			defaultButton=QtGui.QMessageBox.No) \
			!= QtGui.QMessageBox.Yes:
			return
		del self.item_store[self.name]
		self.close_tab()
		try:
			# Sometimes the treeWidget has already been deleted, in which case
			# this gives an Exception. But it when it hasn't been deleted, a
			# structure_change has to be emitted, otherwise the GUI doesn't
			# update properly.
			self.treeWidget().structure_change.emit()
		except:
			pass
	def __init__(self, main_window):

		super(backend_settings, self).__init__(main_window,
			ui=u'widgets.backend_settings')
		self.tab_name = u'__backend_settings__'

		for backend_type in backend._backend_types:
			try:
				_backend = backend.get_backend_class(self.experiment,
					backend_type)
			except:
				_backend = None
				
			layout = getattr(self.ui, u'layout_%s' % backend_type)
			label = getattr(self.ui, u'label_%s' % backend_type)
			# Horribly ugly way to clear the previous settings
			while layout.count() > 1:
				w = layout.itemAt(1)
				layout.removeItem(w)
				w.widget().hide()
				sip.delete(w)
			if _backend is None:
				label.setText(_(u"Failed to load backend"))				
			elif not hasattr(_backend, u"settings") or _backend.settings == \
				None:
				label.setText(_(u"No settings for %s") % _backend.__name__)
			else:
				label.setText(_(u"Settings for %s:") % _backend.__name__)
				layout.addWidget(settings_widget(self.main_window,
					_backend.settings))
Beispiel #11
0
	def copy_to_pool(self, fname):

		"""
		Copy a file to the file pool

		Arguments:
		fname -- full path to file
		"""

		import shutil

		renamed = False
		_fname = os.path.basename(fname)
		while os.path.exists(os.path.join(self.experiment.pool_folder, _fname)):
			_fname = u"_" + _fname
			renamed = True

		if renamed:
			QtGui.QMessageBox.information(self.ui.centralwidget, \
				_(u"File renamed"), \
				_(u"The file has been renamed to '%s', because the file pool already contains a file named '%s'.") \
				% (_fname, os.path.basename(fname)))

		shutil.copyfile(fname, os.path.join(self.experiment.pool_folder, _fname))
		self.refresh_pool(True)
Beispiel #12
0
def select_from_pool(main_window):

	"""
	A static function that presents the select from pool dialog

	Arguments:
	main_window -- the GUI main window
	"""

	d = QtGui.QDialog(main_window.ui.centralwidget)
	widget = pool_widget(main_window)
	widget.refresh()
	bbox = QtGui.QDialogButtonBox(d)
	bbox.addButton(_("Cancel"), QtGui.QDialogButtonBox.RejectRole)
	bbox.addButton(_("Select"), QtGui.QDialogButtonBox.AcceptRole)
	bbox.accepted.connect(d.accept)
	bbox.rejected.connect(d.reject)
	vbox = QtGui.QVBoxLayout()
	vbox.addWidget(widget)
	vbox.addWidget(bbox)
	d.setLayout(vbox)
	d.setWindowTitle(_("Select file from pool"))
	res = d.exec_()
	main_window.refresh_pool()

	if res == QtGui.QDialog.Rejected:
		return ""

	selected = widget.ui.list_pool.currentItem()
	if selected == None:
		return ""
	return unicode(selected.text())
	def __init__(self, tree_overview, target_treeitem=None):

		"""
		desc:
			Constructor.

		arguments:
			tree_overview:
				desc:	The tree_overview with which this menu is associated.
				type:	tree_overview

		keywords:
			target_treeitem:
				desc:	The treewidget item which corresponds to the sequence to
						which the append operation should be applied. If `None`,
						the top-level item from the tree_overview is used.
				type:	tree_item_item
		"""

		super(tree_append_menu, self).__init__(tree_overview)
		self.setup(tree_overview)
		self.target_treeitem = target_treeitem
		self.tree_overview = tree_overview
		self.action_new_items = QtGui.QAction(self.theme.qicon(u'list-add'),
			_(u'Append new item'), self)
		self.addAction(self.action_new_items)
		self.action_linked_copy = QtGui.QAction(self.theme.qicon(u'edit-copy'),
			_(u'Append existing item (linked)'), self)
		self.addAction(self.action_linked_copy)
		self.aboutToShow.connect(self.refresh)
		self.triggered.connect(self.append_item)
		self._new_items_menu = None
Beispiel #14
0
	def __init__(self, item):

		"""
		desc:
			Constructor.

		arguments:
			item:
				desc:	An item.
				type:	qtitem.
		"""

		super(item_view_button, self).__init__(item.main_window)
		self.item = item
		self.setup(item)
		self.set_view_icon(u'controls')
		self.setIconSize(QtCore.QSize(16,16))
		self.menu_view = QtGui.QMenu()
		self.menu_view.addAction(self.view_controls_icon(),
			_(u'View controls'), self.item.set_view_controls)
		self.menu_view.addAction(self.view_script_icon(), _(u'View script'),
			self.item.set_view_script)
		self.menu_view.addAction(self.view_split_icon(), _(u'Split view'),
			self.item.set_view_split)
		self.setMenu(self.menu_view)
		self.setToolTip(_(u'Select view'))
Beispiel #15
0
	def rename_file(self, item):

		"""
		desc:
			Starts a rename action for an item.

		arguments:
			item:
				type:	QListWidgetItem
		"""

		old_name = item.path
		new_name = item.text().strip()
		if new_name in self.pool:
			self.notify(
				_(u"There already is a file named '%s' in the file pool") \
				% new_name)
			new_name = old_name
		elif new_name in (old_name, u''):
			new_name = old_name
		else:
			try:
				self.pool.rename(old_name, new_name)
			except:
				self.notify(_(u'Failed to rename "%s" to "%s".') \
					% (old_name, new_name))
		self.refresh()
		self.select(new_name)
	def __init__(self, main_window):
	
		self.main_window = main_window
		QtGui.QWidget.__init__(self, main_window)
		self.ui = Ui_widget_backend_settings()
		self.ui.setupUi(self)
		self.main_window.theme.apply_theme(self)
		self.tab_name = '__backend_settings__'
		
		for backend_type in ["canvas", "keyboard", "mouse", "synth", \
			"sampler"]:
			backend = self.main_window.experiment.get("%s_backend" \
				% backend_type)
			backend_module = __import__(u'openexp._%s.%s' % (backend_type, \
				backend), fromlist=[u'dummy'])
			_backend = getattr(backend_module, backend)
			group = getattr(self.ui, u'group_%s' % backend_type)
			layout = getattr(self.ui, u'layout_%s' % backend_type)
			label = getattr(self.ui, u'label_%s' % backend_type)
			# Horribly ugly way to clear the previous settings
			while layout.count() > 1:
				w = layout.itemAt(1)
				layout.removeItem(w)
				w.widget().hide()
				sip.delete(w)

			if not hasattr(_backend, "settings") or _backend.settings == \
				None:
				label.setText(_("No settings for %s") % backend)
			else:
				label.setText(_("Settings for %s:") % backend)
				layout.addWidget(settings_widget( \
					self.main_window.experiment, _backend.settings, self))
Beispiel #17
0
	def show_context_menu(self, pos):

		"""
		desc:
			Shows a context menu for the element.

		arguments:
			pos:
				type:	QPoint
		"""

		from libqtopensesame._input.popup_menu import popup_menu
		pm = popup_menu(self.main_window, [
			(0, _(u'Edit script'), u'utilities-terminal'),
			(1, _(u'Raise to front'), u'go-top'),
			(2, _(u'Lower to bottom'), u'go-bottom'),
			(3, _(u'Delete'), u'edit-delete'),
			])
		resp = pm.show()
		if resp is None:
			return
		if resp == 0:
			self.show_script_edit_dialog()
		elif resp == 1:
			self.properties[u'z_index'] = self.sketchpad.min_z_index() - 1
		elif resp == 2:
			self.properties[u'z_index'] = self.sketchpad.max_z_index() + 1
		elif resp == 3:
			self.sketchpad.remove_elements([self])
		self.sketchpad.draw()
Beispiel #18
0
	def handle_exception(self, e):

		"""
		desc:
			Shows a summary when the experiment was aborted.

		arguments:
			e:
				desc:	The Exception that caused the experiment to stop.
				type:	Exception
		"""

		if not isinstance(e, osexception):
			e = osexception(msg=u'Unexpected error', exception=e)
		if e.user_triggered:
			icon = u'os-finished-user-interrupt'
			title = _(u'Aborted')
			md = _(u'# Aborted\n\n- ') + e.markdown()
		else:
			icon = u'os-finished-error'
			title = _(u'Stopped')
			md = _(u'# Stopped\n\nThe experiment did not finish normally for the '
				u'following reason:\n\n- ') + e.markdown()
		self.console.write(e)
		self.tabwidget.open_markdown(md, icon, title)
Beispiel #19
0
	def add(self, files, rename=False):

		"""
		desc:
			Adds a list of files to the pool.

		arguments:
			files:
			 	desc:	A list of paths.
				type:	list
		"""

		if not files:
			return
		for path in files:
			basename = os.path.basename(path)
			if self.pool.in_folder(basename):
				if rename:
					while self.pool.in_folder(basename):
						basename = u'_' + basename
				else:
					c = confirmation(self.main_window,
						_(u"A file named '%s' already exists in the pool. Do you want to overwrite this file?") \
						% basename)
					if not c.show():
						continue
			try:
				self.pool.add(path, new_name=basename)
			except IOError as e:
				self.notify(_(u'Failed to copy %s to file pool') % path)
				self.console.write(safe_decode(e, errors=u'ignore'))
		self.refresh()
		self.select(basename)
Beispiel #20
0
	def sanitize_check(self, s, strict=False, allow_vars=True, notify=True):
		
		"""
		Checks whether a string is sane (i.e. unchanged by sanitize()) and
		optionally presents a warning if it's notably
		
		Arguments: 
		s -- the string to check
		
		Keyword arguments:
		strict -- see sanitize()
		allow_vars -- see sanitize()
		notify -- indicates whether a notification should be presented if the 
				  string is not sane.
		
		Returns:
		True if s is sane, False otherwise
		"""
		
		sane = s == self.sanitize(s, strict=strict, allow_vars=allow_vars)
		if not sane and notify:
			if strict:
				self.experiment.notify(
					_('All non-alphanumeric characters except underscores have been stripped'))
			else:
				self.experiment.notify(
					_('The following characters are not allowed and have been stripped: double-quote ("), backslash (\), and newline'))
		return sane						
Beispiel #21
0
	def add_control(self, label, widget, tooltip=None, min_width=None):

		"""
		Adds a generic control QWidget.

		Arguments:
		label		--	A text label.
		widget		--	A control QWidget.

		Keyword arguments:
		tooltip		--	A tooltip text. (default=None)
		min_width	--	A minimum width for the widget. (default=None)
		"""

		if tooltip is not None:
			try:
				widget.setToolTip(_(tooltip, context=self.name))
			except:
				pass
		if type(min_width) == int:
			widget.setMinimumWidth(min_width)
		row = self.edit_grid.rowCount()
		self.edit_grid.addWidget(QtGui.QLabel(_(label, context=self.name)), row, 0)
		self.edit_grid.addWidget(widget, row, 1)
		self.set_focus_widget(widget)
Beispiel #22
0
    def on_success(self, quick=False):

        """
		Is called when an experiment has successfully ended, and gives the user
		the opportunity to copy the logfile to the file pool.

		Keyword arguments:
		quick		--	Indicates whether we are quickrunning the experiment.
						(default=False)
		"""

        if quick:
            return
        resp = QtGui.QMessageBox.question(
            self.main_window.ui.centralwidget,
            _(u"Finished!"),
            _(
                u"The experiment is finished and data has been logged to '%s'. Do you want to copy the logfile to the file pool?"
            )
            % self.experiment.logfile,
            QtGui.QMessageBox.Yes,
            QtGui.QMessageBox.No,
        )
        if resp == QtGui.QMessageBox.Yes:
            self.main_window.copy_to_pool(self.experiment.logfile)
Beispiel #23
0
	def add(self, files):

		"""
		Add a list of files to the pool.

		Arguments:
		files - A list of paths
		"""

		basename = ""
		for path in files:
			path = unicode(path)
			debug.msg(path)
			basename = os.path.basename(path)
			if not os.path.isfile(path):
				self.main_window.experiment.notify( \
					"'%s' is not a regular file and could not be added to the file pool." \
					% path)
			else:
				# If a similar file already exists in the pool, ask before overwriting
				if os.path.exists(os.path.join( \
					self.main_window.experiment.pool_folder, basename)):
					resp = QtGui.QMessageBox.question(self, _("Overwrite"), \
						_("A file named '%s' already exists in the pool. Do you want to overwrite this file?") \
						% basename, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
					if resp == QtGui.QMessageBox.Yes:
						shutil.copyfile(path, os.path.join( \
							self.main_window.experiment.pool_folder, basename))
				else:
					shutil.copyfile(path, os.path.join( \
						self.main_window.experiment.pool_folder, basename))

		self.refresh()
		self.select(basename)
Beispiel #24
0
	def __init__(self, main_window, overview_mode=True):

		"""
		desc:
			Constructor.

		arguments:
			main_window:
				desc:		The main window object.
				type:		qtopensesame

		keywords:
			overview_mode:
				desc:		Indicates whether the tree should be overview-area
							style (True) or sequence style (False).
				type:		int
		"""

		super(tree_overview, self).__init__(main_window)
		self.overview_mode = overview_mode
		self.setAcceptDrops(True)
		if self.overview_mode:
			self.setHeaderHidden(True)
		else:
			self.setHeaderHidden(False)
			self.setHeaderLabels([_(u'Item name'), _(u'Run if')])
		self.setAlternatingRowColors(True)
		self.itemChanged.connect(self.text_edited)
		self.pending_drag_data = None
		self.drag_timer = None
		if not self.overview_mode:
			self.shortcut_edit_runif = QtGui.QShortcut(
				QtGui.QKeySequence(cfg.shortcut_edit_runif), self,
				self.start_edit_runif,
				context=QtCore.Qt.WidgetWithChildrenShortcut)
		self.shortcut_rename = QtGui.QShortcut(
			QtGui.QKeySequence(cfg.shortcut_rename), self,
			self.start_rename,
			context=QtCore.Qt.WidgetWithChildrenShortcut)
		self.shortcut_copy_item = QtGui.QShortcut(
			QtGui.QKeySequence(cfg.shortcut_copy_clipboard), self,
			self.copy_item, context=QtCore.Qt.WidgetWithChildrenShortcut)
		self.shortcut_paste_item = QtGui.QShortcut(
			QtGui.QKeySequence(cfg.shortcut_paste_clipboard), self,
			self.paste_item, context=QtCore.Qt.WidgetWithChildrenShortcut)
		self.shortcut_delete_item = QtGui.QShortcut(
			QtGui.QKeySequence(cfg.shortcut_delete), self, self.delete_item,
			context=QtCore.Qt.WidgetWithChildrenShortcut)
		self.shortcut_delete_item = QtGui.QShortcut(
			QtGui.QKeySequence(cfg.shortcut_permanently_delete), self,
			self.permanently_delete_item,
			context=QtCore.Qt.WidgetWithChildrenShortcut)
		self.shortcut_linked_copy = QtGui.QShortcut(
			QtGui.QKeySequence(cfg.shortcut_linked_copy), self,
			self.create_linked_copy,
			context=QtCore.Qt.WidgetWithChildrenShortcut)
		self.shortcut_unlinked_copy = QtGui.QShortcut(
			QtGui.QKeySequence(cfg.shortcut_unlinked_copy), self,
			self.create_unlinked_copy,
			context=QtCore.Qt.WidgetWithChildrenShortcut)
Beispiel #25
0
    def drop_event_item_move(self, data, e):

        """
		desc:
			Handles drop events for item moves.

		arguments:
			data:
				desc:	A drop-data dictionary.
				type:	dict:
			e:
				desc:	A drop event.
				type:	QDropEvent
		"""

        if not drag_and_drop.matches(data, [u"item-existing"]):
            e.ignore()
            return
        target_treeitem = self.itemAt(e.pos())
        if not self.droppable(target_treeitem, data):
            debug.msg(u"Drop ignored: target not droppable")
            self.main_window.set_status(_(u"Drop cancelled: Target not droppable"))
            e.ignore()
            return
            # If the drop comes from this application, check for recursion etc.
        if data[u"application-id"] == self.main_window._id():
            target_item_name, target_item_ancestry = target_treeitem.ancestry()
            item_name = data[u"item-name"]
            item = self.experiment.items[item_name]
            if target_item_name in item.children():
                debug.msg(u"Drop ignored: recursion prevented")
                self.main_window.set_status(_(u"Drop cancelled: Recursion prevented"))
                e.ignore()
                return
            parent_item_name, index = self.parent_from_ancestry(data[u"ancestry"])
            if parent_item_name == None:
                debug.msg(u"Drop ignored: no parent")
                e.ignore()
                return
                # The logic below is a bit complicated, but works as follows:
                # - If we're in a move action, remove the dragged item from its parent,
                #   and set need_restore so that we know this happened.
                # - Try to drop the dragged item onto the target item
                # - If the drop action was unsuccesful, and if need_restore is set,
                #   re-add the dragged item to its former parent.
        need_restore = False
        if not QtCore.Qt.ControlModifier & e.keyboardModifiers() and data[u"application-id"] == self.main_window._id():
            if parent_item_name not in self.experiment.items:
                debug.msg(u"Don't know how to remove item from %s" % parent_item_name)
            else:
                self.locked = True
                need_restore = True
                self.experiment.items[parent_item_name].remove_child_item(item_name, index)
                self.locked = False
        if self.drop_event_item_new(data, e, target_treeitem=target_treeitem):
            return
        if need_restore:
            self.experiment.items[parent_item_name].insert_child_item(item_name, index)
            self.experiment.build_item_tree()
Beispiel #26
0
	def drop_hint(self):

		if self.treeWidget().overview_mode or self.parent() == None:
			if self.item.item_type == u'loop':
				return _(u'Set as item to run for %s') % self.name
			if self.item.item_type == u'sequence':
				return _(u'Insert into %s') % self.name
		return _(u'Drop below %s') % self.name
	def __init__(self, main_window, treeitem):

		"""
		desc:
			Constructor.

		arguments:
			main_window:
				desc:	The main-window object.
				type:	qtopensesame
			treeitem:
				desc:	The tree item.
				type:	tree_item_item
		"""

		super(item_context_menu, self).__init__(main_window)
		self.setup(main_window)
		self.treeitem = treeitem
		self.addAction(self.theme.qicon(self.item.item_type), _('Open'),
			self.item.open_tab)
		self.addSeparator()
		self.add_action(u"accessories-text-editor", _("Rename"),
			self.treeitem.start_rename, cfg.shortcut_rename)
		if not self.treewidget.overview_mode and self.treeitem.parent() is not None:
			self.add_action(u"accessories-text-editor",
				_("Edit run-if statement"),
				self.treeitem.start_edit_runif, cfg.shortcut_edit_runif)
		self.addSeparator()
		self.add_action(u"edit-copy", _("Copy (unlinked)"),
			self.treeitem.copy_unlinked, cfg.shortcut_copy_clipboard_unlinked)
		self.add_action(u"edit-copy", _("Copy (linked)"),
			self.treeitem.copy_linked, cfg.shortcut_copy_clipboard_linked)
		if self.treeitem.clipboard_data() is not None:
			self.add_action(u"edit-paste", _("Paste"),
				self.treeitem.paste, cfg.shortcut_paste_clipboard)
		if self.treeitem.is_deletable():
			self.addSeparator()
			self.add_action(u"list-remove", _("Delete"),
				self.treeitem.delete, cfg.shortcut_delete)
			self.add_action(u"list-remove",
				_("Permanently delete all linked copies"),
				self.treeitem.permanently_delete,
				cfg.shortcut_permanently_delete)
		elif self.treeitem.is_unused():
			self.addSeparator()
			self.add_action(u"list-remove", _("Permanently delete"),
				self.treeitem.permanently_delete,
				cfg.shortcut_permanently_delete)
		if self.treeitem.has_append_menu():
			# An append menu for sequence items
			menu = tree_append_menu(self.treeitem.treeWidget(), self.treeitem)
			action = QtGui.QAction(self.theme.qicon(u'list-add'),
				u'Append item', self)
			action.setMenu(menu)
			self.addSeparator()
			self.addAction(action)
		self.addSeparator()
		self.add_action(u"help", _("Help"), self.item.open_help_tab)
Beispiel #28
0
	def check_for_updates(self, always=True):

		import urllib

		if not always and not cfg.auto_update_check:
			debug.msg(u"skipping update check")
			return

		debug.msg(u"opening %s" % cfg.version_check_url)

		try:
			fd = urllib.urlopen(cfg.version_check_url)
			mrv = float(fd.read().strip())
		except Exception as e:
			if always:
				self.update_dialog( \
					_(u"... and is sorry to say that the attempt to check for updates has failed. Please make sure that you are connected to the internet and try again later. If this problem persists, please visit <a href='http://www.cogsci.nl/opensesame'>http://www.cogsci.nl/opensesame</a> for more information."))
			return

		# The most recent version as downloaded is always a float. Therefore, we
		# must convert the various possible version numbers to analogous floats.
		# We do this by dividing each subversion number by 100. The only
		# exception is that prereleases should be counted as older than stable
		# releases, so for pre-release we substract one bugfix version.
		# 0.27			->	0.27.0.0			->	0.27
		# 0.27.1 		-> 	0.27.1.0			->	0.2701
		# 0.27~pre1		->	0.27.0.1 - .0001	-> 	0.269901
		# 0.27.1~pre1	->	0.27.1.1 - .0001	-> 	0.270001
		v = self.main_window.version
		l = v.split(u"~pre")
		if len(l) == 2:
			lastSubVer = l[1]
			v = l[0]
			ver = -.0001
		else:
			lastSubVer = 0
			ver = .0
		lvl = 0
		fct = .01
		for subVer in v.split(u'.') + [lastSubVer]:
			try:
				_subVer = int(subVer)
			except:
				debug.msg(u'Failed to process version segment %s' % subVer, \
					reason=u'warning')
				return
			ver += fct**lvl * _subVer
			lvl += 1
		debug.msg(u'identifying as version %s' % ver)
		debug.msg(u'latest stable version is %s' % mrv)
		if mrv > ver:
			self.update_dialog( \
				_(u"... and is happy to report that a new version of OpenSesame (%s) is available at <a href='http://www.cogsci.nl/opensesame'>http://www.cogsci.nl/opensesame</a>!") % mrv)
		else:
			if always:
				self.update_dialog( \
					_(u" ... and is happy to report that you are running the most recent version of OpenSesame."))
Beispiel #29
0
	def init_edit_widget(self):

		"""Construct the GUI controls"""

		qtitem.qtitem.init_edit_widget(self, False)

		tabwidget_script = QtGui.QTabWidget(self._edit_widget)
		py_ver = "Python %d.%d.%d" % (sys.version_info[0], \
			sys.version_info[1], sys.version_info[2])
			
		# Construct prepare editor
		self.textedit_prepare = inline_editor.inline_editor(self.experiment, \
			notification=py_ver, syntax="python")
		self.textedit_prepare.apply.clicked.connect(self.apply_edit_changes)
		QtCore.QObject.connect(self.textedit_prepare.edit, QtCore.SIGNAL( \
			"focusLost"), self.apply_edit_changes)
		hbox = QtGui.QHBoxLayout()
		hbox.addStretch()
		hbox.setContentsMargins(0, 0, 0, 0)
		hbox_widget = QtGui.QWidget()
		hbox_widget.setLayout(hbox)
		vbox = QtGui.QVBoxLayout()
		vbox.addWidget(self.textedit_prepare)
		vbox.addWidget(hbox_widget)
		widget = QtGui.QWidget()
		widget.setLayout(vbox)
		tabwidget_script.addTab(widget, self.experiment.icon("inline_script"), \
			_("Prepare phase"))

		# Construct run editor
		self.textedit_run = inline_editor.inline_editor(self.experiment, \
			notification=py_ver, syntax="python")
		self.textedit_run.apply.clicked.connect(self.apply_edit_changes)
		QtCore.QObject.connect(self.textedit_run.edit, QtCore.SIGNAL( \
			"focusLost"), self.apply_edit_changes)
		hbox = QtGui.QHBoxLayout()
		hbox.addStretch()
		hbox.setContentsMargins(0, 0, 0, 0)
		hbox_widget = QtGui.QWidget()
		hbox_widget.setLayout(hbox)
		vbox = QtGui.QVBoxLayout()
		vbox.addWidget(self.textedit_run)
		vbox.addWidget(hbox_widget)
		widget = QtGui.QWidget()
		widget.setLayout(vbox)
		tabwidget_script.addTab(widget, self.experiment.icon("inline_script"), \
			_("Run phase"))

		# Switch to the run script by default, unless there is only content for
		# the prepare script.
		if self._run == "" and self._prepare != "":
			tabwidget_script.setCurrentIndex(0)
		else:
			tabwidget_script.setCurrentIndex(1)
			
		# Add the tabwidget to the controls
		self.edit_vbox.addWidget(tabwidget_script)
	def context_menu(self, e):

		"""
		Show the context menu

		Arguments:
		e -- a QMouseEvent
		"""

		# First clear the grid, so we don't select the grid items
		for l in self.grid_list:
			self.removeItem(l)
		self.grid_list = []


		# Get the item under the cursor
		item = self.itemAt(e.scenePos())
		if item != None:

			# Get the corresponding item from the sketchpad_widget
			for g, _item in self.sketchpad_widget.item_list:
				if g == item:

					# Draw a highlight box
					brush = QtGui.QBrush()
					brush.setColor(QtGui.QColor(self.grid_color))
					brush.setStyle(QtCore.Qt.Dense2Pattern)

					# For some items the boundingrect does not appear to work
					# correctly, so we have to use the pos() function
					x = max(item.pos().x(), item.boundingRect().x()) - 4
					y = max(item.pos().y(), item.boundingRect().y()) - 4
					w = item.boundingRect().width() + 8
					h = item.boundingRect().height() + 8
					l = self.addRect(x, y, w, h, brush = brush)
					l.setOpacity(0.25)

					# Show the context menu
					menu = QtGui.QMenu()
					menu.addAction( \
						self.sketchpad_widget.sketchpad.experiment.icon( \
						"delete"), _("Delete"))
					menu.addAction( \
						self.sketchpad_widget.sketchpad.experiment.icon( \
						"edit"), _("Edit"))
					action = menu.exec_(e.screenPos())

					# Execute the chosen action
					if action != None:
						if unicode(action.text()) == _("Delete"):
							self.delete_item(_item)
						elif unicode(action.text()) == _("Edit"):
							self.edit_item(_item)

		self.sketchpad_widget.sketchpad.apply_edit_changes()
		self.sketchpad_widget.refresh()
Beispiel #31
0
	def event_startup(self):

		"""
		desc:
			On startup, a widget is created with a menu that copies all actions
			from the main menu bar.
		"""

		self.menu = QtGui.QMenu()
		for action in self.menubar.actions():
			self.menu.addAction(action)
		self.stretch = QtGui.QWidget()
		self.stretch.setSizePolicy(QtGui.QSizePolicy.Expanding,
			QtGui.QSizePolicy.Expanding)
		self.button = QtGui.QPushButton(self.theme.qicon(self.icon()),
			_(u'Menu'))
		self.button.setMenu(self.menu)
		self.button.setIconSize(QtCore.QSize(cfg.toolbar_size,
			cfg.toolbar_size))
		self.button.setFlat(True)
		self.toolbar.addWidget(self.stretch)
		self.menu_action = self.toolbar.addWidget(self.button)
		if cfg.toolbar_menu_active:
			self.activate_toolbar_menu()
			self.set_checked(True)
		else:
			self.deactivate_toolbar_menu()
Beispiel #32
0
    def add_editor_control(self, var, label, syntax=False, tooltip=None, \
     default=None):
        """
		Adds a QProgEdit that is linked to a variable.

		Arguments:
		var			--	Name of the associated variable.
		label 		--	Label text.

		Keyword arguments:
		syntax 		--	A boolean indicating whether Python syntax highlighting
						should be activated. (default=False)
		tooltip		--	A tooltip text. (default=None)
		default		--	DEPRECATED

		Returns:
		A QProgEdit widget.
		"""

        from QProgEdit import QTabManager
        if syntax:
            lang = u'python'
        else:
            lang = u'text'
        qprogedit = QTabManager(cfg=cfg)
        qprogedit.focusLost.connect(self.apply_edit_changes)
        qprogedit.handlerButtonClicked.connect(self.apply_edit_changes)
        qprogedit.addTab(_(label, context=self.name)).setLang(lang)

        if var is not None:
            self.auto_editor[var] = qprogedit
        self.edit_vbox.addWidget(qprogedit)
        self.set_focus_widget(qprogedit)
        return qprogedit
Beispiel #33
0
	def show_edit_dialog(self):

		"""
		desc:
			The show-edit dialog for the textline only edits the text, not the
			full element script.
		"""

		text = self.experiment.text_input(_(u'Edit text'),
			message=_(u'Please enter a text for the textline'),
			content=self.properties[u'text'].replace(u'<br />', u'\n'),
			parent=self.sketchpad._edit_widget)
		if text is None:
			return
		self.properties[u'text'] = self.clean_text(text)
		self.sketchpad.draw()
Beispiel #34
0
    def regenerate(self, script):
        """
		Handles a full regeneration of the experiment.
		
		Arguments:
		script 			--	A definition Unicode string / QString.
		"""

        self.main_window.set_busy(True)
        script = self.main_window.experiment.unistr(script)
        try:
            # Generate the new experiment
            tmp = experiment.experiment(self.main_window, \
             self.main_window.experiment.title, script, \
             self.main_window.experiment.pool_folder)
        except Exception as error:
            # If something is wrong with the script, notify the user and print
            # a traceback to the debug window
            self.main_window.experiment.notify( \
             _(u'Failed to parse script (see traceback in debug window): %s') \
             % error)
            self.main_window.print_debug_window(error)
            return
        # Apply the new experiment
        self.main_window.experiment = tmp
        self.main_window.experiment.build_item_tree()
        self.main_window.ui.tabwidget.close_all()
        self.main_window.ui.tabwidget.open_general_script()
        self.main_window.set_busy(False)
        self.main_window.set_unsaved()
Beispiel #35
0
    def apply_button(self, label=u'Apply', icon=u'apply', \
     tooltip=u'Apply changes'):
        """
		Returns a right-outlined apply QPushButton. The widget is not added
		automatically to the controls. I.e. you need to implement your own
		connections to make the button functional.

		Keyword arguments:
		label		-- A label text. (default=u'Apply')
		icon		-- An icon name. (default=u'Apply')
		tooltip		-- A tooltip text. (default=u'Apply changes')

		Returns:
		A QPushButton widget.
		"""

        button_apply = QtGui.QPushButton(_(label))
        button_apply.setIcon(self.experiment.icon(icon))
        button_apply.setIconSize(QtCore.QSize(16, 16))
        button_apply.clicked.connect(self.apply_edit_changes)
        button_apply.setToolTip(tooltip)
        hbox = QtGui.QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.addStretch()
        hbox.addWidget(button_apply)
        widget = QtGui.QWidget()
        widget.setLayout(hbox)
        return widget
Beispiel #36
0
    def draw(self, e):
        """
		Handle drawing operations

		Arguments:
		e -- a QMouseEvent
		"""

        x, y = self.cursor_pos(e)
        if x == None:
            return

        if self.sketchpad_widget.sketchpad.get("coordinates") == "relative":
            x += 0.5 * self.sketchpad_widget.sketchpad.get("width")
            y += 0.5 * self.sketchpad_widget.sketchpad.get("height")

        if x < 0 or y < 0 or x >= self.sketchpad_widget.sketchpad.get("width") \
         or y >= self.sketchpad_widget.sketchpad.get("height"):
            self.sketchpad_widget.ui.label_mouse_pos.setText( \
             _("(Out of sketchpad)"))
            return

        if self.from_pos == None and not self.oneshot:
            self.addEllipse(x - self.r, y - self.r, 2 * self.r, 2 * self.r, \
             self.pen)
            self.from_pos = x, y
        else:
            pos = self.from_pos
            self.from_pos = None
            self.sketchpad_widget.add(pos, (x, y))
Beispiel #37
0
    def init_script_widget(self):
        """Build the script tab"""

        from QProgEdit import QTabManager
        self.script_qprogedit = QTabManager(handler= \
         self.apply_script_and_close, defaultLang=u'OpenSesame', \
         handlerButtonText=_(u'Apply and close script editor'), \
         focusOutHandler=self.apply_script_changes, cfg=cfg)
        self.script_qprogedit.addTab(u'Script')

        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(self.experiment.label_image(self.item_type))
        self.script_header = QtGui.QLabel()
        hbox.addWidget(self.script_header)
        hbox.addStretch()
        hbox.setContentsMargins(0, 0, 0, 0)
        hwidget = QtGui.QWidget()
        hwidget.setLayout(hbox)

        vbox = QtGui.QVBoxLayout()
        vbox.addWidget(hwidget)
        vbox.addWidget(self.script_qprogedit)
        self._script_widget = QtGui.QWidget()
        self._script_widget.setLayout(vbox)
        self._script_widget.__script_item__ = self.name
Beispiel #38
0
    def open_markdown(self,
                      md,
                      icon=u'dialog-information',
                      title=u'Attention!'):
        """
		desc:
			Opens a Markdown-formatted tab.

		arguments:
			md:
				desc:	If it ends with `.md`, this is interpreted as a file
						name. Otherwise, it is interpreted as markdown text.
				type:	str

		keywords:
			icon:
				desc:	The name of the tab icon.
				type:	str
			title:
				desc:	The tab title.
				type:	str
		"""

        from libqtopensesame.widgets.webbrowser import webbrowser
        wb = webbrowser(self.main_window)
        if md.endswith(u'.md'):
            wb.load(md)
        else:
            wb.load_markdown(md)
        self.main_window.tabwidget.add(wb, icon, _(title))
Beispiel #39
0
	def update_recent_files(self):

		"""Recreate the list with recent documents"""

		from libqtopensesame.actions import recent_action

		# Add the current path to the front of the list
		if self.current_path != None and os.path.exists(self.current_path):
			if self.current_path in self.recent_files:
				self.recent_files.remove(self.current_path)
			self.recent_files.insert(0, self.current_path)

		# Trim the list
		self.recent_files = self.recent_files[:5]

		# Build the menu
		self.ui.menu_recent_files.clear()
		if len(self.recent_files) == 0:
			a = QtGui.QAction(_(u"(No recent files)"), \
				self.ui.menu_recent_files)
			a.setDisabled(True)
			self.ui.menu_recent_files.addAction(a)
		else:
			for path in self.recent_files:
				self.ui.menu_recent_files.addAction( \
					recent_action.recent_action(path, self, \
					self.ui.menu_recent_files))
Beispiel #40
0
    def item_tree_widget(self, toplevel, icon=None, name=None, tooltip=None):
        """
		Create a single item tree widget

		Arguments:
		toplevel -- the toplevel item

		Keyword arguments:
		icon -- an icon name or None for default (default=None)
		name -- the name of the item or None for default (default=None)
		tooltip -- the tooltip or None for default (default=None)

		Returns:
		A QTreeWidgetItem
		"""

        if name == None:
            name = self.name
        if icon == None:
            icon = self.item_type
        if tooltip == None:
            tooltip = _(u"Type: %s\nDescription: %s") % (self.item_type, \
             self.description)
        font = QtGui.QFont()
        font.setPointSize(8)
        font.setItalic(True)
        widget = QtGui.QTreeWidgetItem(toplevel)
        widget.setText(0, name)
        widget.setIcon(0, self.experiment.icon(icon))
        widget.name = name
        widget.setToolTip(0, tooltip)
        return widget
Beispiel #41
0
    def __init__(self, parent, item, pixmap):
        """
		desc:
			Constructor.

		arguments:
			parent:
				desc:	The parent.
				type:	QWidget
			item:
				desc:	An item.
				type:	qtitem
			pixmap:
				desc:	A pixmap for the icon.
				type:	QPixmap
		"""

        super(toolbar_items_item, self).__init__(parent)
        self.setup(parent)
        self.item = item
        self.pixmap = pixmap
        self.setMargin(6)
        self.setToolTip(
            _("Drag this <b>%s</b> item to the intended location in the overview "
              "area or into the item list of a sequence tab") % self.item)
        self.setCursor(QtGui.QCursor(QtCore.Qt.OpenHandCursor))
        self.setPixmap(self.pixmap)
Beispiel #42
0
	def regenerate(self, script):
	
		"""
		Handles a full regeneration of the experiment
		
		Arguments:
		script -- a definition string (unicode/ QString)
		"""
				
		self.main_window.set_busy(True)		
		script = self.main_window.experiment.unistr(script)
		try:
			# Generate the new experiment
			tmp = experiment.experiment(self.main_window, \
				self.main_window.experiment.title, script, \
				self.main_window.experiment.pool_folder)
		except libopensesame.exceptions.script_error as error:		
			# If something is wrong with the script, notify the user
			self.main_window.experiment.notify(_("Could not parse script: %s") \
				% error)
			return
		# Apply the new experiment
		self.main_window.experiment = tmp
		self.main_window.experiment.build_item_tree()			
		self.main_window.ui.tabwidget.close_all()
		self.main_window.ui.tabwidget.open_general_script()
		self.main_window.set_busy(False)
		self.main_window.set_unsaved()	
Beispiel #43
0
    def image(self, path, center, x, y, scale):
        """Draw image"""

        pixmap = QtGui.QPixmap(path)

        if pixmap.isNull():
            # Qt4 cannot handle certain funky bitmaps that PyGame can. So if
            # loading the image directly fails, we fall back to loading the
            # image with PyGame and converting it to a QPixmap. In addition, we
            # notify the user that he/ she is using a funky bitmap.
            import pygame
            im = pygame.image.load(path)
            data = pygame.image.tostring(im, "RGBA")
            size = im.get_size()
            image = QtGui.QImage(data, size[0], size[1], \
             QtGui.QImage.Format_ARGB32)
            pixmap = QtGui.QPixmap.fromImage(image)
            self.notifications.append( \
             _("Funky image alert: '%s' has a non-standard format. It is recommended to convert this image to .png format, for example with Gimp <http://www.gimp.org/>.") \
             % os.path.basename(path))

        w = pixmap.width() * scale
        pixmap = pixmap.scaledToWidth(w)
        _item = self.scene.addPixmap(pixmap)
        if center:
            _item.setPos(x - 0.5 * pixmap.width(), y - 0.5 * pixmap.height())
        else:
            _item.setPos(x, y)
        return _item
Beispiel #44
0
	def regenerate(self, script):

		"""
		desc:
			Regenerates the current experiment from script, and updates the GUI.

		argument:
			script:
				desc:	The new experiment script.
				type:	str
		"""

		try:
			exp = experiment.experiment(self, name=self.experiment.var.title,
				string=script, pool_folder=self.experiment.pool.folder(),
				experiment_path=self.experiment.experiment_path,
				resources=self.experiment.resources)
		except osexception as e:
			md = _(u'# Parsing error\n\nFailed to parse the script for the '
				u'following reason:\n\n- ') + e.markdown()
			self.tabwidget.open_markdown(md)
			self.console.write(e)
			return
		self.extension_manager.fire(u'prepare_regenerate')
		self.experiment = exp
		self.tabwidget.close_all()
		self.experiment.build_item_tree()
		self.extension_manager.fire(u'regenerate')
Beispiel #45
0
	def insert_child_item(self, item_name, index=0):

		if not self.is_coroutine(item_name):
			self.experiment.notify(
				_(u'"%s" does not support coroutines.') % item_name)
			return
		sequence.insert_child_item(self, item_name, index=index)
Beispiel #46
0
    def sanity_check(self):
        """
		Checks whether all variables match prespecified criteria and fall back
		to the script editor otherwise. This is usefull to check that certain
		variables are numeric, etc.
		"""

        debug.msg()
        errors = []
        for var_name, criteria in self.sanity_criteria.items():
            msg = _(u"Invalid or missing value for variable '%s' (edit script to fix this)") \
             % var_name
            if u'msg' in criteria:
                msg += u': ' + criteria[u'msg']
            if not self.has(var_name) and u'required' in criteria and \
             criteria[u'required']:
                self.experiment.notify(msg)
                return False
            else:
                var = self.get(var_name, _eval=False)
                if u'type' in criteria:
                    _type = criteria[u'type']
                    if type(_type) != list:
                        _type = [_type]
                    if type(var) not in _type:
                        self.experiment.notify(msg)
                        return False
                if u'func' in criteria:
                    if not criteria[u'func'](var):
                        self.experiment.notify(msg)
                        return False
        return True
Beispiel #47
0
	def prepare(self):

		"""Prepare for the run phase"""

		item.item.prepare(self)

		# Prepare the form
		try:
			cols = [float(i) for i in unicode(self.cols).split(';')]
			rows = [float(i) for i in unicode(self.rows).split(';')]
			margins = [float(i) for i in unicode(self.margins).split(';')]
		except:
			raise exceptions.runtime_error( \
				_('cols, rows, and margins should be numeric values separated by a semi-colon'))
		self._form = widgets.form(self.experiment, cols=cols, rows=rows, \
			margins=margins, spacing=self.spacing, theme=self.theme, item=self)

		# Prepare the widgets
		for _w in self._widgets:

			# Evaluate all keyword arguments
			w = {}
			for var, val in _w.iteritems():
				w[var] = self.eval_text(val)

			_type = w['type']
			col = w['col']
			row = w['row']
			colspan = w['colspan']
			rowspan = w['rowspan']
			del w['type']
			del w['col']
			del w['row']
			del w['colspan']
			del w['rowspan']

			# Translate paths into full file names
			if 'path' in w:
				w['path'] = self.experiment.get_file(w['path'])

			# Process focus keyword
			if 'focus' in w:
				focus = True
				del w['focus']
			else:
				focus = False

			# Create the widget and add it to the form
			try:
				_w = eval('widgets.%s(self._form, **w)' % _type)
			except Exception as e:
				raise exceptions.runtime_error( \
					'Failed to create widget "%s": %s' % (_type, e))
			self._form.set_widget(_w, (col, row), colspan=colspan, \
					rowspan=rowspan)

			# Add as focus widget
			if focus:
				self.focus_widget = _w
		return True
Beispiel #48
0
    def apply_button(self, label="Apply", icon="apply", \
     tooltip="Apply changes"):
        """
		Returns a right-outlined apply button. The widget is not added
		automatically to the controls.

		Keyword arguments:
		label -- a button label (default="Apply")
		icon -- an icon name (default="Apply")
		tooltip -- a tooltip (default="Apply changes")

		Returns:
		A QPushButton
		"""

        button_apply = QtGui.QPushButton(_(label))
        button_apply.setIcon(self.experiment.icon(icon))
        button_apply.setIconSize(QtCore.QSize(16, 16))
        button_apply.clicked.connect(self.apply_edit_changes)
        button_apply.setToolTip(tooltip)
        hbox = QtGui.QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.addStretch()
        hbox.addWidget(button_apply)
        widget = QtGui.QWidget()
        widget.setLayout(hbox)

        return widget
Beispiel #49
0
	def parse_line(self, line):

		"""
		Allows for arbitrary line parsing, for item-specific requirements

		Arguments:
		line -- a single definition line
		"""

		l = self.split(line.strip())
		if len(l) < 6 or l[0] != 'widget':
			return

		# Verify that the position variables are integers
		try:
			col = int(l[1])
			row = int(l[2])
			colspan = int(l[3])
			rowspan = int(l[4])
		except:
			raise exceptions.script_error( \
				_('Widget column and row should be numeric'))

		# Parse the widget into a dictionary and store it in the list of
		# widgets
		w = self.parse_keywords(line)
		w['type'] = l[5]
		w['col'] = col
		w['row'] = row
		w['colspan'] = colspan
		w['rowspan'] = rowspan
		self._widgets.append(w)
Beispiel #50
0
	def __init__(self, item):

		"""
		Constructor

		Arguments:
		item -- the item to provide a header for
		"""

		QtGui.QWidget.__init__(self)
		self.setCursor(QtCore.Qt.IBeamCursor)
		self.setToolTip(_(u"Click to edit"))
		self.item = item
		self.label_name = QtGui.QLabel()
		self.label_name.id = u"name"
		self.edit_name = QtGui.QLineEdit()
		self.edit_name.editingFinished.connect(self.restore_name)
		self.edit_name.hide()
		self.label_desc = QtGui.QLabel()
		self.label_desc.id = u"desc"
		self.edit_desc = QtGui.QLineEdit()
		self.edit_desc.editingFinished.connect(self.restore_desc)
		self.edit_desc.hide()
			
		vbox = QtGui.QVBoxLayout()
		vbox.setContentsMargins(8, 0, 0, 0)
		vbox.setSpacing(0)
		vbox.addWidget(self.label_name)
		vbox.addWidget(self.edit_name)
		vbox.addWidget(self.label_desc)
		vbox.addWidget(self.edit_desc)		
		self.refresh()
		self.setLayout(vbox)
Beispiel #51
0
	def __init__(self, item):

		"""
		desc:
			Constructor.

		arguments:
			item: 			A qtitem object.
		"""

		super(header_widget, self).__init__(item.main_window)
		self.setCursor(QtCore.Qt.IBeamCursor)
		self.setToolTip(_(u"Click to edit"))
		self.item = item
		self.label_name = QtGui.QLabel()
		self.edit_name = QtGui.QLineEdit()
		self.edit_name.editingFinished.connect(self.apply_name)
		self.edit_name.hide()
		self.label_desc = QtGui.QLabel()
		self.label_desc.setWordWrap(True)
		self.edit_desc = QtGui.QLineEdit()
		self.edit_desc.editingFinished.connect(self.apply_desc)
		self.edit_desc.hide()

		vbox = QtGui.QVBoxLayout()
		vbox.setContentsMargins(8, 0, 0, 0)
		vbox.setSpacing(0)
		vbox.addWidget(self.label_name)
		vbox.addWidget(self.edit_name)
		vbox.addWidget(self.label_desc)
		vbox.addWidget(self.edit_desc)
		self.refresh()
		self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
		self.setLayout(vbox)
Beispiel #52
0
    def save_file_as(self):
        """
		desc:
			Saves the current experiment after asking for a file name.
		"""

        # Choose a default file name based on the experiment title
        if self.current_path is None:
            cfg.file_dialog_path = os.path.join(
                self.home_folder,
                self.experiment.syntax.sanitize(self.experiment.var.title,
                                                strict=True,
                                                allow_vars=False))
        else:
            cfg.file_dialog_path = self.current_path
        path, file_type = QtGui.QFileDialog.getSaveFileNameAndFilter(
            self.ui.centralwidget,
            _(u'Save file as ...'),
            directory=cfg.file_dialog_path,
            filter=self.save_file_filter)
        if path is None or path == u"":
            return
        if not path.lower().endswith(u'.osexp'):
            path += u'.osexp'
        cfg.file_dialog_path = os.path.dirname(path)
        self.current_path = path
        cfg.default_logfile_folder = os.path.dirname(self.current_path)
        self.save_file()
Beispiel #53
0
    def list_keys(self):
        """Show a dialog with available key names"""

        my_keyboard = keyboard(self.experiment)
        s = _('The following key names are valid:<br />') \
         + '<br />'.join(my_keyboard.valid_keys())
        self.experiment.notify(s)
Beispiel #54
0
	def __init__(self, item, extra_info=None, parent_item=None, index=None):

		"""
		desc:
			Constructor.

		arguments:
			item:
				desc:	An item.
				type:	qtitem

		keywords:
			extra_info:
				desc:	Extra info that is shown in the second column. Not shown
						in overview mode.
				type:	[NoneType, unicode]
		"""

		super(tree_item_item, self).__init__()
		self.setup(item.main_window)
		self.item = item
		tooltip = _(u"Type: %s\nDescription: %s") % (item.item_type,
			item.var.description)
		self.setText(0, item.name)
		if extra_info is not None:
			self.setText(1, extra_info)
		self.setFlags(QtCore.Qt.ItemIsEditable | self.flags())
		self.setIcon(0, self.theme.qicon(item.item_icon()))
		self.name = item.name
		# Only allow drops on used items
		self._droppable = self.item.name in self.item.experiment.items.used()
		self._draggable = True
		self._lock = False
		self.setToolTip(0, tooltip)
Beispiel #55
0
    def save_file(self):
        """
		desc:
			Saves the current experiment.

		keywords:
			dummy:		A dummy argument passed by the signal handler.
			remember:
				desc:	Indicates whether the file should be included in the
						list of recent files.
				type:	bool
			catch:
			 	desc:	Indicates whether exceptions should be caught and
				 		displayed in a notification.
				type:	bool
		"""

        if self.current_path is None:
            self.save_file_as()
            return
        self.extension_manager.fire(u'save_experiment', path=self.current_path)
        # Indicate that we're busy
        self.set_busy(True)
        QtGui.QApplication.processEvents()
        # Get ready
        try:
            self.get_ready()
        except osexception as e:
            self.ui.console.write(e)
            self.experiment.notify(
             _(u"The following error occured while trying to save:<br/>%s") \
             % e)
            self.set_busy(False)
            return
        # Try to save the experiment if it doesn't exist already
        try:
            self.experiment.save(self.current_path, overwrite=True)
            self.set_busy(False)
        except Exception as e:
            self.ui.console.write(e)
            self.experiment.notify(_(u"Failed to save file. Error: %s") % e)
            self.set_busy(False)
            return
        self.update_recent_files()
        self.set_unsaved(False)
        self.window_message(self.current_path)
        self.set_busy(False)
Beispiel #56
0
    def rename(self, from_name, to_name):
        """
		desc:
			Renames an item and updates the interface. This function may show
			a notification dialog.

		arguments:
			from_name:
				desc:	The old item name.
				type:	unicode
			to_name:
				desc:	The desired new item name.
				type:	unicode

		returns:
			desc:	The new name of the item, if renaming was successful, None
					otherwise.
			type:	[NoneType, unicode]
		"""

        if from_name not in self:
            self.experiment.notify(_(u'Item "%s" doesn\'t exist' % from_name))
            return None
        if from_name == to_name:
            return None
        to_name = self.valid_name(self[from_name].item_type,
                                  suggestion=to_name)
        if to_name in self:
            self.experiment.notify(
                _(u'An item with that name already exists.'))
            return None
        if to_name == u'':
            self.experiment.notify(_(u'An item name cannot be empty.'))
            return None
        # Copy the item in the __items__dictionary
        self.__items__[to_name] = self.__items__[from_name]
        del self.__items__[from_name]
        # Give all items a chance to update
        for item in self.values():
            item.rename(from_name, to_name)
        self.experiment.rename(from_name, to_name)
        self.itemtree.rename(from_name, to_name)
        self.main_window.set_unsaved(True)
        self.extension_manager.fire(u'rename_item',
                                    from_name=from_name,
                                    to_name=to_name)
        return to_name
Beispiel #57
0
	def wizard(self):

		"""Present the variable wizard dialog"""
		
		icons = {}
		icons["cut"] = self.experiment.icon("cut")
		icons["copy"] = self.experiment.icon("copy")
		icons["paste"] = self.experiment.icon("paste")
		icons["clear"] = self.experiment.icon("clear")		

		# Set up the wizard dialog
		a = QtGui.QDialog(self.experiment.main_window.ui.centralwidget)
		a.ui = loop_wizard_dialog_ui.Ui_loop_wizard_dialog()
		a.ui.setupUi(a)		
		self.experiment.main_window.theme.apply_theme(a)
		a.ui.table_example.build_context_menu(icons)
		a.ui.table_wizard.build_context_menu(icons)
		a.ui.table_example.hide()
		a.ui.table_wizard.setRowCount(255)
		a.ui.table_wizard.setColumnCount(255)
		
		a.ui.table_wizard.set_contents(cfg.loop_wizard)		
		if a.exec_() == QtGui.QDialog.Accepted:		
			cfg.loop_wizard = a.ui.table_wizard.get_contents()
			debug.msg("filling loop table")
			# First read the table into a dictionary of variables
			var_dict = {}
			for col in range(a.ui.table_wizard.columnCount()):
				var = None
				for row in range(a.ui.table_wizard.rowCount()):
					item = a.ui.table_wizard.item(row, col)
					if item == None:
						break
					s = unicode(item.text())
					if s == u'':
						break
					if row == 0:
						var = self.experiment.sanitize(s, True)
						var_dict[var] = []
					elif var != None:
						var_dict[var].append(s)
						
			# If the variable wizard was not parsed correctly, provide a
			# notification and do nothin
			if len(var_dict) == 0:
				self.experiment.notify(
					_('You provided an empty or invalid variable definition. For an example of a valid variable definition, open the variable wizard and select "Show example".'))
				return	
						
			# Then fill the loop table
			self.i = 0
			self.matrix = {}
			self.wizard_process(var_dict)
			self.set_cycle_count(len(self.matrix))
			self.lock = True		
			self.loop_widget.ui.spin_cycles.setValue(self.cycle_count())
			self.lock = False
			self.refresh_loop_table()		
			self.refresh_summary()
Beispiel #58
0
    def execute(self):
        """See base_runner.execute()."""

        import multiprocessing
        from libqtopensesame.misc import process, _
        from libopensesame import misc, debug
        from StringIO import StringIO
        if os.name == u'nt':
            # Under Windows, the multiprocess runner assumes that there is a
            # file called `opensesame.py` or `opensesame.pyc`. If this file does
            # not exist, try to copy it from the main script (`opensesame`). If
            # this fails, provide an informative error message.
            os_folder = misc.opensesame_folder()
            if not os.path.exists(os.path.join(os_folder, u'opensesame.pyc')) \
             and not os.path.exists(os.path.join(os_folder, u'opensesame.py')):
                import shutil
                try:
                    shutil.copyfile(os.path.join(os_folder, u'opensesame'), \
                     os.path.join(os_folder, u'opensesame.py'))
                except Exception as e:
                    return osexception( \
                     _(u'Failed to copy `opensesame` to `opensesame.py`, which is required for the multiprocess runner. Please copy the file manually, or select a different runner under Preferences.'), exception=e)
        self.channel = multiprocessing.Queue()
        self.exp_process = process.ExperimentProcess(self.experiment, \
         self.channel)
        # Start process!
        self.exp_process.start()
        # Variables used for ugly hack to suppress 'None' print by Queue.get()
        _stdout = sys.stdout
        _pit = StringIO()
        # Wait for experiment to finish.
        # Listen for incoming messages in the meantime.
        while self.exp_process.is_alive() or not self.channel.empty():
            QtGui.QApplication.processEvents()
            # Make sure None is not printed. Ugly hack for a bug in the Queue
            # class?
            sys.stdout = _pit
            # Wait for messages. Will throw Exception if no message is received
            # before timeout.
            try:
                msg = self.channel.get(True, 0.05)
            except:
                msg = None
            # Restore connection to stdout
            sys.stdout = _stdout
            # For standard print statements
            if isinstance(msg, basestring):
                sys.stdout.write(msg)
            # Errors arrive as a tuple with (Error object, traceback)
            elif isinstance(msg, Exception):
                return msg
            # Anything that is not a string, not an Exception, and not None is
            # unexpected
            elif msg != None:
                return osexception( \
                 u"Illegal message type received from child process: %s (%s)" \
                 % (msg, type(msg)))
        # Return None if experiment finished without problems
        return None
Beispiel #59
0
    def handle_exception(self, e):
        """
		desc:
			Shows a summary when the experiment was aborted.

		arguments:
			e:
				desc:	The Exception that caused the experiment to stop.
				type:	Exception
		"""

        if not isinstance(e, osexception):
            e = osexception(msg=u'Unexpected error', exception=e)
        self.console.write(e)
        md = _(u'# Stopped\n\nThe experiment did not finish normally for the '
               u'following reason:\n\n- ') + e.markdown()
        self.tabwidget.open_markdown(md, u'process-stop', _(u'Stopped'))
Beispiel #60
0
    def event_startup(self):
        """
		desc:
			Build the menu on startup.
		"""

        self.menu = self.menubar.addMenu(_(u'Help'))
        menu = self.online_help_menu()
        if menu != None:
            self.action_online_help = self.menu.addMenu(menu)
        menu = self.psychopy_help_menu()
        if menu != None:
            self.action_psychopy_help = self.menu.addMenu(menu)
        self.action_offline_help = self.menu.addAction(
            self.theme.qicon(u'help-contents'), _(u'Offline help'))
        self.action_offline_help.triggered.connect(self.open_offline_help)
        self.menu.addSeparator()