Exemple #1
0
    def add_editor_control(self, var, label, syntax=False):
        """
		Adds a QProgEdit that is linked to a variable.

		arguments:
			var:		The associated variable.
			label:		The label text.

		keywords:
			syntax:		A boolean indicating whether Python syntax highlighting
						should be activated.

		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).setLang(lang)
        if var is not None:
            self.auto_editor[var] = qprogedit
        self.edit_vbox.addWidget(qprogedit)
        self.set_focus_widget(qprogedit)
        return qprogedit
Exemple #2
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
Exemple #3
0
	def add_editor_control(self, var, label, syntax=False):

		"""
		Adds a QProgEdit that is linked to a variable.

		arguments:
			var:		The associated variable.
			label:		The label text.

		keywords:
			syntax:		A boolean indicating whether Python syntax highlighting
						should be activated.

		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).setLang(lang)
		if var is not None:
			self.auto_editor[var] = qprogedit
		self.edit_vbox.addWidget(qprogedit)
		self.set_focus_widget(qprogedit)
		return qprogedit
Exemple #4
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
Exemple #5
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
Exemple #6
0
def main():

	"""Runs a simple QProgEdit demonstration."""

	print(u'API: %s' % os.environ[u'QT_API'])
	validate.addPythonBuiltins([u'builtin_var'])
	validate.setPyFlakesFilter(lambda msg: msg.message == u'undefined name %r')
	app = QtWidgets.QApplication(sys.argv)

	treeWidgetItem1 = QtWidgets.QTreeWidgetItem([u'Tab 1'])
	treeWidgetItem3 = QtWidgets.QTreeWidgetItem([u'Tab 3'])
	symbolTree = QtWidgets.QTreeWidget()
	symbolTree.addTopLevelItem(treeWidgetItem1)
	symbolTree.addTopLevelItem(treeWidgetItem3)
	symbolTree.itemActivated.connect(activateSymbolTree)

	tabManager = QTabManager(handlerButtonText=u'apply', runButton=True)
	tabManager.setWindowIcon(QtGui.QIcon.fromTheme(u'accessories-text-editor'))
	tabManager.setWindowTitle(u'QProgEdit')
	tabManager.resize(800, 600)

	tabManager.cursorRowChanged.connect(cursorRowChanged)
	tabManager.focusLost.connect(focusLost)
	tabManager.focusReceived.connect(focusReceived)
	tabManager.handlerButtonClicked.connect(handlerButtonClicked)
	tabManager.execute.connect(runSelectedText)

	tab = tabManager.addTab(u'Tab 1')
	tab.setLang(u'Python')
	tab.setSymbolTree(treeWidgetItem1)
	tab.setText(open(__file__).read())

	tab = tabManager.addTab(u'Tab 2')
	tab.setText(u'Some plain text')

	tab = tabManager.addTab(u'Tab 3')
	tab.setLang(u'Python')
	tab.setSymbolTree(treeWidgetItem3)
	if os.path.exists(u'content.txt'):
		tab.setText(open(u'content.txt').read())

	layout = QtWidgets.QHBoxLayout()
	layout.addWidget(symbolTree)
	layout.addWidget(tabManager)
	container = QtWidgets.QWidget()
	container.setLayout(layout)
	container.show()

	res = app.exec_()
	open(u'content.txt', u'w').write(tab.text())
	sys.exit(res)
Exemple #7
0
def main():
	
	"""Runs a simple QProgEdit demonstration."""

	validate.addPythonBuiltins(['builtin_var'])
	app = QtGui.QApplication(sys.argv)

	treeWidgetItem1 = QtGui.QTreeWidgetItem([u'Tab 1'])
	treeWidgetItem3 = QtGui.QTreeWidgetItem([u'Tab 3'])
	symbolTree = QtGui.QTreeWidget()
	symbolTree.addTopLevelItem(treeWidgetItem1)
	symbolTree.addTopLevelItem(treeWidgetItem3)
	symbolTree.itemActivated.connect(activateSymbolTree)

	tabManager = QTabManager(handlerButtonText=u'apply')
	tabManager.setWindowIcon(QtGui.QIcon.fromTheme(u'accessories-text-editor'))
	tabManager.setWindowTitle(u'QProgEdit')
	tabManager.resize(800, 600)

	tabManager.cursorRowChanged.connect(cursorRowChanged)
	tabManager.focusLost.connect(focusLost)
	tabManager.focusReceived.connect(focusReceived)
	tabManager.handlerButtonClicked.connect(handlerButtonClicked)

	tab = tabManager.addTab(u'Tab 1')
	tab.setLang(u'Python')
	tab.setSymbolTree(treeWidgetItem1)
	tab.setText(open(__file__).read())
	print tab.symbols()

	tab = tabManager.addTab(u'Tab 2')
	tab.setText(u'Some plain text')

	tab = tabManager.addTab(u'Tab 3')
	tab.setLang(u'Python')
	tab.setSymbolTree(treeWidgetItem3)
	if os.path.exists(u'content.txt'):
		tab.setText(open(u'content.txt').read().decode(u'utf-8'))
	print tab.symbols()

	layout = QtGui.QHBoxLayout()
	layout.addWidget(symbolTree)
	layout.addWidget(tabManager)
	container = QtGui.QWidget()
	container.setLayout(layout)
	container.show()

	res = app.exec_()
	open(u'content.txt', u'w').write(tab.text().encode(u'utf-8'))
	sys.exit(res)
Exemple #8
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'), callHandlerOnFocusOut=False,
			cfg=cfg)
		self.script_qprogedit.addTab(u'Script')
		
		hbox = QtGui.QHBoxLayout()
		hbox.addWidget(self.experiment.label_image(u"%s" % 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
Exemple #9
0
    def init_edit_widget(self):
        """Constructs the GUI controls."""

        from QProgEdit import QTabManager
        qtitem.qtitem.init_edit_widget(self, False)
        self.qprogedit = QTabManager(handler=self.apply_edit_changes, \
         defaultLang=u'Python', cfg=cfg, focusOutHandler= \
         self.apply_edit_changes)
        self.qprogedit.addTab(u'Prepare')
        self.qprogedit.addTab(u'Run')
        # Switch to the run phase, unless there is only content for the prepare
        # phase.
        if self._run == u'' and self._prepare != u'':
            self.qprogedit.setCurrentIndex(0)
        else:
            self.qprogedit.setCurrentIndex(1)
        self.edit_vbox.addWidget(self.qprogedit)
Exemple #10
0
    def init_edit_widget(self):
        """See qtitem."""

        from QProgEdit import QTabManager
        super(inline_script, self).init_edit_widget(stretch=False)
        self.qprogedit = QTabManager(cfg=cfg)
        self.qprogedit.handlerButtonClicked.connect(self.apply_edit_changes)
        self.qprogedit.focusLost.connect(self.apply_edit_changes)
        self.qprogedit.cursorRowChanged.connect(self.apply_edit_changes)
        self.qprogedit.addTab(u'Prepare').setLang(u'Python')
        self.qprogedit.addTab(u'Run').setLang(u'Python')
        # Switch to the run phase, unless there is only content for the prepare
        # phase.
        if self._run == u'' and self._prepare != u'':
            self.qprogedit.setCurrentIndex(0)
        else:
            self.qprogedit.setCurrentIndex(1)
        self.edit_vbox.addWidget(self.qprogedit)
Exemple #11
0
    def init_edit_widget(self):
        """See qtitem."""

        from QProgEdit import QTabManager

        qtplugin.init_edit_widget(self, stretch=False)
        self.qprogedit = QTabManager(cfg=cfg, runButton=True)
        self.qprogedit.execute.connect(self.main_window.console.execute)
        self.qprogedit.handlerButtonClicked.connect(self.apply_edit_changes)
        self.qprogedit.focusLost.connect(self.apply_edit_changes)
        self.qprogedit.cursorRowChanged.connect(self.apply_edit_changes)
        self.qprogedit.addTab(_(u'Prepare')).setLang(u'Python')
        self.qprogedit.addTab(_(u'Run')).setLang(u'Python')
        # Switch to the run phase, unless there is only content for the prepare
        # phase.
        if self.var._run == u'' and self.var._prepare != u'':
            self.qprogedit.setCurrentIndex(0)
        else:
            self.qprogedit.setCurrentIndex(1)
        self.edit_vbox.addWidget(self.qprogedit)
Exemple #12
0
    def settings_widget(self):
        """
		desc:
			returns the QProgEdit settings widget.

		returns:
			type:	QWidget
		"""

        # Create a temporary QProgEdit instance, add one tab, and wrap the
        # preferences widget for this tab in a group box.
        from QProgEdit import QTabManager
        tm = QTabManager(cfg=cfg)
        tm.addTab()
        w = tm.tab(0).prefs
        w.show()
        w.refresh()
        group = QtGui.QGroupBox(_(u'Editor preferences (QProgEdit)'),
                                self.main_window)
        layout = QtGui.QHBoxLayout(group)
        layout.addWidget(w)
        group.setLayout(layout)
        return group
Exemple #13
0
	def settings_widget(self):

		"""
		desc:
			returns the QProgEdit settings widget.

		returns:
			type:	QWidget
		"""

		# Create a temporary QProgEdit instance, add one tab, and wrap the
		# preferences widget for this tab in a group box.
		from QProgEdit import QTabManager
		tm = QTabManager(cfg=cfg)
		tm.addTab()
		w = tm.tab(0).prefs
		w.show()
		w.refresh()
		group = QtGui.QGroupBox(_(u'Editor preferences (QProgEdit)'),
			self.main_window)
		layout = QtGui.QHBoxLayout(group)
		layout.addWidget(w)
		group.setLayout(layout)
		return group
	def init_edit_widget(self):

		"""Constructs the GUI controls."""
		
		from QProgEdit import QTabManager
		qtitem.qtitem.init_edit_widget(self, False)
		self.qprogedit = QTabManager(handler=self.apply_edit_changes, \
			defaultLang=u'Python', cfg=cfg)
		self.qprogedit.addTab(u'Prepare')
		self.qprogedit.addTab(u'Run')
		# Switch to the run phase, unless there is only content for the prepare
		# phase.
		if self._run == u'' and self._prepare != u'':
			self.qprogedit.setCurrentIndex(0)
		else:
			self.qprogedit.setCurrentIndex(1)
		self.edit_vbox.addWidget(self.qprogedit)
Exemple #15
0
    def __init__(self, main_window):
        """
		desc:
			Constructor.

		arguments:
			main_window:	A qtopensesame object.
		"""

        from QProgEdit import QTabManager
        super(general_script_editor,
              self).__init__(main_window, ui=u'widgets.general_script_editor')
        self.ui.qprogedit = QTabManager(handlerButtonText=u'Apply', cfg=cfg)
        self.ui.qprogedit.handlerButtonClicked.connect(self._apply)
        self.ui.qprogedit.addTab(u'General script').setLang(u'OpenSesame')
        self.ui.layout_vbox.addWidget(self.ui.qprogedit)
        self.tab_name = u'__general_script__'
Exemple #16
0
	def init_edit_widget(self):

		"""See qtitem."""

		from QProgEdit import QTabManager
		super(inline_script, self).init_edit_widget(stretch=False)
		self.qprogedit = QTabManager(cfg=cfg)
		self.qprogedit.handlerButtonClicked.connect(self.apply_edit_changes)
		self.qprogedit.focusLost.connect(self.apply_edit_changes)
		self.qprogedit.cursorRowChanged.connect(self.apply_edit_changes)
		self.qprogedit.addTab(u'Prepare').setLang(u'Python')
		self.qprogedit.addTab(u'Run').setLang(u'Python')
		# Switch to the run phase, unless there is only content for the prepare
		# phase.
		if self._run == u'' and self._prepare != u'':
			self.qprogedit.setCurrentIndex(0)
		else:
			self.qprogedit.setCurrentIndex(1)
		self.edit_vbox.addWidget(self.qprogedit)
    def __init__(self, main_window):
        """
		Constructor
		
		Arguments:
		main_window -- the main window
		"""

        from QProgEdit import QTabManager

        self.main_window = main_window
        QtGui.QWidget.__init__(self, main_window)
        self.ui = Ui_widget_general_script_editor()
        self.ui.setupUi(self)
        self.ui.qprogedit = QTabManager(handler=self._apply, defaultLang= \
         u'OpenSesame', handlerButtonText=u'Apply', cfg=cfg)
        self.ui.qprogedit.addTab(u'General script')
        self.ui.layout_vbox.addWidget(self.ui.qprogedit)
        self.main_window.theme.apply_theme(self)
        self.tab_name = '__general_script__'
Exemple #18
0
def main():
    """Runs a simple QProgEdit demonstration."""

    print(u'API: %s' % os.environ[u'QT_API'])
    validate.addPythonBuiltins([u'builtin_var'])
    validate.setPyFlakesFilter(lambda msg: msg.message == u'undefined name %r')
    app = QtWidgets.QApplication(sys.argv)

    treeWidgetItem1 = QtWidgets.QTreeWidgetItem([u'Tab 1'])
    treeWidgetItem3 = QtWidgets.QTreeWidgetItem([u'Tab 3'])
    symbolTree = QtWidgets.QTreeWidget()
    symbolTree.addTopLevelItem(treeWidgetItem1)
    symbolTree.addTopLevelItem(treeWidgetItem3)
    symbolTree.itemActivated.connect(activateSymbolTree)

    tabManager = QTabManager(handlerButtonText=u'apply', runButton=True)
    tabManager.setWindowIcon(QtGui.QIcon.fromTheme(u'accessories-text-editor'))
    tabManager.setWindowTitle(u'QProgEdit')
    tabManager.resize(800, 600)

    tabManager.cursorRowChanged.connect(cursorRowChanged)
    tabManager.focusLost.connect(focusLost)
    tabManager.focusReceived.connect(focusReceived)
    tabManager.handlerButtonClicked.connect(handlerButtonClicked)
    tabManager.execute.connect(runSelectedText)

    tab = tabManager.addTab(u'Tab 1')
    tab.setLang(u'Python')
    tab.setSymbolTree(treeWidgetItem1)
    tab.setText(open(__file__).read())

    tab = tabManager.addTab(u'Tab 2')
    tab.setText(u'Some plain text')

    tab = tabManager.addTab(u'Tab 3')
    tab.setLang(u'Python')
    tab.setSymbolTree(treeWidgetItem3)
    if os.path.exists(u'content.txt'):
        tab.setText(open(u'content.txt').read())

    layout = QtWidgets.QHBoxLayout()
    layout.addWidget(symbolTree)
    layout.addWidget(tabManager)
    container = QtWidgets.QWidget()
    container.setLayout(layout)
    container.show()

    res = app.exec_()
    open(u'content.txt', u'w').write(tab.text())
    sys.exit(res)
class inline_script(libopensesame.inline_script.inline_script, qtitem.qtitem):

	"""The inline_script GUI controls"""

	def __init__(self, name, experiment, string=None):

		"""
		Constructor.

		Arguments:
		name 		--	The item name.
		experiment	--	The experiment object.

		Keywords arguments:
		string		--	A definition string. (default=None)
		"""

		libopensesame.inline_script.inline_script.__init__(self, name, \
			experiment, string)
		qtitem.qtitem.__init__(self)
		self.lock = False
		self._var_info = None

	def apply_edit_changes(self, **args):

		"""
		Applies the controls.

		Keywords arguments:
		args	--	A dictionary to accept unused keyword arguments.
		"""

		qtitem.qtitem.apply_edit_changes(self, False)
		sp = self.qprogedit.text(index=0)
		sr = self.qprogedit.text(index=1)
		self.set(u'_prepare', sp)
		self.set(u'_run', sr)
		self.lock = True
		self._var_info = None
		self.experiment.main_window.refresh(self.name)
		self.lock = False

	def init_edit_widget(self):

		"""Constructs the GUI controls."""
		
		from QProgEdit import QTabManager
		qtitem.qtitem.init_edit_widget(self, False)
		self.qprogedit = QTabManager(handler=self.apply_edit_changes, \
			defaultLang=u'Python', cfg=cfg)
		self.qprogedit.addTab(u'Prepare')
		self.qprogedit.addTab(u'Run')
		# Switch to the run phase, unless there is only content for the prepare
		# phase.
		if self._run == u'' and self._prepare != u'':
			self.qprogedit.setCurrentIndex(0)
		else:
			self.qprogedit.setCurrentIndex(1)
		self.edit_vbox.addWidget(self.qprogedit)

	def edit_widget(self):

		"""
		Updates the GUI controls.

		Returns:
		The control QWidget.
		"""

		qtitem.qtitem.edit_widget(self, False)
		if not self.lock:
			self.qprogedit.setText(self._prepare, index=0)
			self.qprogedit.setText(self._run, index=1)
		return self._edit_widget

	def get_ready(self):

		"""Applies pending script changes."""

		if self.qprogedit.isModified():
			debug.msg(u'applying pending script changes')
			self.apply_edit_changes(catch=False)
			return True
		return qtitem.qtitem.get_ready(self)
Exemple #20
0
class qtitem(QtCore.QObject):

	"""Base class for the GUI controls of other items"""

	def __init__(self):

		"""Constructor"""

		QtCore.QObject.__init__(self)

		# The auto-widgets are stored in name -> (var, widget) dictionaries
		self.auto_line_edit = {}
		self.auto_combobox = {}
		self.auto_spinbox = {}
		self.auto_slider = {}
		self.auto_editor = {}
		self.auto_checkbox = {}
		self.sanity_criteria = {}

		self.init_edit_widget()
		self.init_script_widget()
		self.script_tab = None
		self.lock = False
		self.edit_mode = u'edit'

		debug.msg(u'created %s' % self.name)

	def open_help_tab(self, page=None):

		"""Opens a help tab."""
		
		self.experiment.main_window.ui.tabwidget.open_help(self.item_type)

	def open_tab(self):

		"""Opens the correct tab based on the current edit mode"""

		if self.edit_mode == u'edit':
			self.open_edit_tab()
		else:
			self.open_script_tab()

	def init_edit_widget(self, stretch=True):

		"""Build the GUI controls"""

		# Header widget
		self.header = header_widget.header_widget(self)
		self.user_hint_widget = user_hint_widget.user_hint_widget( \
			self.experiment.main_window, self)
		self.header_hbox = QtGui.QHBoxLayout()
		self.header_hbox.addWidget(self.experiment.label_image(self.item_type))
		self.header_hbox.addWidget(self.header)
		self.header_hbox.addStretch()
		self.header_hbox.setContentsMargins(0, 0, 0, 16)

		# Edit script button
		button = QtGui.QPushButton(self.experiment.icon(u"script"), u"")
		button.setToolTip(_(u"Edit script"))
		button.setIconSize(QtCore.QSize(16, 16))
		QtCore.QObject.connect(button, QtCore.SIGNAL(u"clicked()"), \
			self.open_script_tab)
		self.header_hbox.addWidget(button)

		# Help button
		button = QtGui.QPushButton(self.experiment.icon(u"help"), u"")
		button.setToolTip(_(u"Tell me more about the %s item") % self.item_type)
		button.setIconSize(QtCore.QSize(16, 16))
		QtCore.QObject.connect(button, QtCore.SIGNAL(u"clicked()"), \
			self.open_help_tab)
		self.header_hbox.addWidget(button)

		self.header_widget = QtGui.QWidget()
		self.header_widget.setLayout(self.header_hbox)

		# The edit_grid is the layout that contains the actual controls for the
		# items.
		self.edit_grid = QtGui.QGridLayout()
		self.edit_grid.setColumnStretch(2, 2)
		self.edit_grid_widget = QtGui.QWidget()
		self.edit_grid.setMargin(0)
		self.edit_grid_widget.setLayout(self.edit_grid)

		# The edit_vbox contains the edit_grid and the header widget
		self.edit_vbox = QtGui.QVBoxLayout()
		self.edit_vbox.setMargin(16)
		self.edit_vbox.addWidget(self.header_widget)
		self.edit_vbox.addWidget(self.user_hint_widget)
		self.edit_vbox.addWidget(self.edit_grid_widget)		
		if stretch:
			self.edit_vbox.addStretch()
		self._edit_widget = QtGui.QWidget()
		self._edit_widget.setLayout(self.edit_vbox)
		self._edit_widget.__edit_item__ = self.name

		return self._edit_widget

	def edit_widget(self, stretch=True):

		"""
		A dummy edit widget, to be overridden.

		Keywords arguments:
		stretch		--	DEPRECATED (default=True)
		"""

		if not stretch:
			debug.msg(u"passing the stretch argument is deprecated", \
				reason=u"deprecation")
		self.user_hint_widget.clear()
		self.header.restore_name(False)
		self.header.refresh()
		self._edit_widget.__edit_item__ = self.name
		if not self.sanity_check():
			self.open_script_tab()
			return
		self.auto_edit_widget()
		self.user_hint_widget.refresh()
		return self._edit_widget

	def apply_name_change(self, rebuild=True):

		"""
		Apply an item name change

		Keywords arguments:
		rebuild -- a deprecated argument (default=True)
		"""

		debug.msg()

		# Sanitize the name, check if it is new and valid, and if so, rename
		new_name = self.experiment.sanitize(self.header.edit_name.text(), \
			strict=True, allow_vars=False)
		if new_name.lower() != self.name.lower():
			valid = self.experiment.check_name(new_name)
			if valid != True:
				self.experiment.notify(valid)
				self.header.edit_name.setText(self.name)
				return
		old_name = self.name
		self.name = new_name
		self._edit_widget.__edit_item__	= new_name
		self.experiment.main_window.dispatch.event_name_change.emit(old_name, \
			new_name)

	def apply_edit_changes(self, rebuild=True):

		"""
		Applies the GUI controls.

		Keywords arguments:
		rebuild	--	Specifies whether the overview area (item list) should be
					rebuild. (default=True)
		"""

		debug.msg(self.name)
		if self.experiment.main_window.lock_refresh:
			debug.msg(u"skipping, because refresh in progress")
			return False
		self.auto_apply_edit_changes()
		self.set(u"description", \
			self.experiment.sanitize(unicode( \
				self.header.edit_desc.text()).strip()))
		if self.description == u"":
			self.description = u"No description"
		self.header.label_desc.setText(self.description)
		self.experiment.main_window.dispatch.event_simple_change.emit(self.name)
		return True

	def close_edit_tab(self, index=None):

		"""
		Closes the edit tab (does nothing by default).

		Keywords arguments:
		index	--	The index of the tab in the tab area. (default=None)
		"""

		pass

	def open_edit_tab(self, index=None, focus=True):

		"""
		Opens the GUI control tab, or switches to the tab if it was already
		open.

		Keywords arguments:
		index	--	The index of the tab (if open). (default=None)
		focus	--	Indicates whether the tab should receive focus.
					(default=True)
		"""

		debug.msg(u"%s (#%s)" % (self.name, hash(self)))

		# Switch to edit mode and close the script tab if it was open
		self.edit_mode = u"edit"
		for i in range(self.experiment.ui.tabwidget.count()):
			w = self.experiment.ui.tabwidget.widget(i)
			if hasattr(w, u"__script_item__") and w.__script_item__ == \
				self.name:
				self.experiment.ui.tabwidget.removeTab(i)
				if index == None:
					index = i
				break

		# Focus the edit tab, instead of reopening, if it was already open
		for i in range(self.experiment.ui.tabwidget.count()):
			w = self.experiment.ui.tabwidget.widget(i)
			if hasattr(w, u"__edit_item__") and w.__edit_item__ == self.name:
				index = i

		# Refresh the controls on the tab. In debug mode don't catch any errors
		if debug.enabled:
			widget = self.edit_widget()
		else:
			try:
				widget = self.edit_widget()
			except Exception as e:
				self.experiment.notify(_(u"%s (Edit the script to fix this)") \
					% e)
				self.open_script_tab()
				return

		# Open the tab or focus the tab if it was already open
		if index == None:
			self.edit_tab_index = self.experiment.ui.tabwidget.addTab(widget, \
				self.experiment.icon(self.item_type), u"%s" % self.name)
		else:
			self.experiment.ui.tabwidget.insertTab(index, widget, \
				self.experiment.icon(self.item_type), u"%s" % self.name)
			self.edit_tab_index = index
		if focus:
			self.experiment.ui.tabwidget.setCurrentIndex(self.edit_tab_index)

	def apply_script_and_close(self):

		"""Applies script changes and opens the edit tab"""

		self.apply_script_changes()
		self.experiment.main_window.select_item(self.name)

	def apply_script_changes(self, rebuild=True, catch=True):

		"""
		Apply changes to the script, by regenerating the item from the script

		Keywords arguments:
		rebuild -- specifies whether the overview area (item list) should be
				   rebuild (default=True)
		catch -- indicates if exceptions should be caught and shown in a
				 notification dialog (True) or not be caught (False)
				 (default=True)
		"""

		debug.msg(self.name)
		script = self.script_qprogedit.text()
		# Create a new item and make it a clone of the current item
		item = self.experiment.main_window.add_item(self.item_type, False, \
			name=self.name, interactive=False)
		if catch:
			try:
				self.experiment.items[item].from_string(script)
			except Exception as e:
				self.experiment.notify(unicode(e))
				return
		else:
			self.experiment.items[item].from_string(script)
		self.experiment.items[item].name = self.name
		# Replace the current item
		self.experiment.items[self.name] = self.experiment.items[item]
		del self.experiment.items[item]
		self.experiment.items[self.name].init_script_widget()
		self.experiment.main_window.dispatch.event_script_change.emit(self.name)

	def strip_script_line(self, s):

		"""
		Strips unwanted characters from a line of script

		Arguments:
		s -- a line of script

		Returns:
		A stripped line of script
		"""

		if len(s) > 0 and s[0] == u"\t":
			return s[1:] + u"\n"
		return s + u"\n"

	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'), callHandlerOnFocusOut=False,
			cfg=cfg)
		self.script_qprogedit.addTab(u'Script')
		
		hbox = QtGui.QHBoxLayout()
		hbox.addWidget(self.experiment.label_image(u"%s" % 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

	def script_widget(self):

		"""
		Update the script tab

		Returns:
		The QWidget containing the script tab
		"""

		self.script_header.setText( \
			_(u"Editing script for <b>%s</b> - %s") % (self.name, \
			self.item_type))
		script = u""
		for s in self.to_string().split(u"\n")[1:]:
			script += self.strip_script_line(s)
		self.script_qprogedit.setText(script)
		self._script_widget.__script_item__ = self.name
		return self._script_widget

	def open_script_tab(self, index=None, focus=True):

		"""
		Open/ show the script tab

		Keywords arguments:
		index -- the index of the tab (if it is already open) (default=None)
		focus -- indicates whether the tab should receive focus (default=True)
		"""

		debug.msg(u"%s (#%s)" % (self.name, hash(self)))
		self.edit_mode = u"script"

		# Close the edit tab
		for i in range(self.experiment.ui.tabwidget.count()):
			w = self.experiment.ui.tabwidget.widget(i)
			if hasattr(w, u"__edit_item__") and w.__edit_item__ == self.name:
				self.experiment.ui.tabwidget.removeTab(i)
				if index == None:
					index = i
				break

		for i in range(self.experiment.ui.tabwidget.count()):
			w = self.experiment.ui.tabwidget.widget(i)
			if hasattr(w, u"__script_item__") and w.__script_item__ == self.name:
				index = i
		if index == None:
			self.script_tab_index = self.experiment.ui.tabwidget.addTab( \
				self.script_widget(), self.experiment.icon(u"script"), u"%s" \
				% self.name)
		else:
			self.script_tab_index = index
			self.experiment.ui.tabwidget.insertTab(index, \
				self.script_widget(), self.experiment.icon(u"script"), u"%s" \
				% self.name)
		if focus:
			self.experiment.ui.tabwidget.setCurrentIndex(self.script_tab_index)

	def close_script_tab(self, index=None):

		"""
		Close the script tab (does nothing by defaut)

		Keywords arguments:
		index -- the index of the tab in the tab area (default=None)
		"""

		pass

	def rename(self, from_name, to_name):

		"""
		Handle the renaming of an item (not necesarrily the currnet item)

		Arguments:
		from_name -- the old item name
		to_name -- the new item name
		"""

		if self.name == from_name:
			self.name = to_name

	def delete(self, item_name, item_parent=None, index=None):

		"""
		Delete an item (not necessarily the current one)

		Arguments:
		item_name -- the name of the item to be deleted

		Keywords arguments:
		item_parent -- the parent item (default=None)
		index -- the index of the item in the parent (default=None)
		"""

		pass

	def rename_var(self, item, from_name, to_name):

		"""
		A notification that a variable has been renamed

		Arguments:
		item -- the item doing the renaming
		from_name -- the old variable name
		to_name -- the new variable name
		"""

		pass

	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

	def build_item_tree(self, toplevel=None, items=[]):

		"""
		Construct an item tree

		Keyword arguments:
		toplevel -- the toplevel widget (default = None)
		items -- a list of items that have been added, to prevent recursion
				 (default=[])
		"""

		toplevel.addChild(self.item_tree_widget(toplevel))

	def is_offspring(self, item):

		"""
		Checks if the item is offspring of the current item, in the sense that
		the current item is contained by the item

		Arguments:
		item -- the potential offspring

		Returns:
		True if the current item is offspring of the item, False otherwise
		"""

		return False

	def parents(self):

		"""
		Creates a list of all the items	that the current sequences is connected
		to upstream

		Returns:
		A list of item names
		"""

		l = [self.name]
		for item in self.experiment.items:
			if self.experiment.items[item].is_offspring(self.name):
				l.append(item)
		return l

	def variable_vars(self, exclude=[]):

		"""
		Determines if one of the variables of the current item is defined in
		terms of another variable

		Keywords arguments:
		exclude -- a list of variables that should not be checked

		Returns:
		True if there are variably defined variables, False otherwise
		"""

		for var in self.variables:
			if var not in exclude:
				val = self.variables[var]
				if isinstance(val, basestring) and u'[' in val:
					return True
		return False

	def get_ready(self):

		"""
		This function should be overridden to do any last-minute stuff that
		and item should do before an experiment is actually run, such as
		applying pending script changes.

		Returns:
		True if some action has been taken, False if nothing was done
		"""

		if self.script_qprogedit.isModified():
			debug.msg(u'applying pending script changes')
			self.apply_script_changes(catch=False)
			return True
		return False		
		
	def auto_edit_widget(self):

		"""Update the GUI controls based on the auto-widgets"""

		debug.msg()
		for var, edit in self.auto_line_edit.iteritems():
			edit.editingFinished.disconnect()
			if self.has(var):
				try:
					edit.setText(self.unistr(self.get(var, _eval=False)))
				except Exception as e:
					self.experiment.notify(_(u"Failed to set control '%s': %s") \
						% (var, e))
			else:
				edit.setText(u"")
			edit.editingFinished.connect(self.apply_edit_changes)

		for var, combobox in self.auto_combobox.iteritems():
			combobox.currentIndexChanged.disconnect()
			if self.has(var):
				try:
					combobox.setCurrentIndex(combobox.findText( \
						self.unistr(self.get(var, _eval=False))))
				except Exception as e:
					self.experiment.notify(_(u"Failed to set control '%s': %s") \
						% (var, e))
			combobox.currentIndexChanged.connect(self.apply_edit_changes)

		for var, spinbox in self.auto_spinbox.iteritems():
			spinbox.editingFinished.disconnect()
			if self.has(var):
				val = self.get(var, _eval=False)
				if type(val) in (float, int):						
					try:
						spinbox.setValue(val)
					except Exception as e:
						self.experiment.notify(_( \
							u"Failed to set control '%s': %s") % (var, e))
				else:
					spinbox.setDisabled(True)
					self.user_hint_widget.add_user_hint(_( \
						u'"%s" is defined using variables and can only be edited through the script.' \
						% var))
			spinbox.editingFinished.connect(self.apply_edit_changes)			

		for var, slider in self.auto_slider.iteritems():
			slider.valueChanged.disconnect()
			if self.has(var):
				val = self.get(var, _eval=False)
				if type(val) in (float, int):	
					try:
						slider.setValue(val)
					except Exception as e:
						self.experiment.notify(_( \
							u"Failed to set control '%s': %s") % (var, e))						
				else:
					slider.setDisabled(True)
					self.user_hint_widget.add_user_hint(_( \
						u'"%s" is defined using variables and can only be edited through the script.' \
						% var))
			slider.valueChanged.connect(self.apply_edit_changes)

		for var, checkbox in self.auto_checkbox.iteritems():
			checkbox.toggled.disconnect()
			if self.has(var):
				try:
					checkbox.setChecked(self.get(var, _eval=False) == u"yes")
				except Exception as e:
					self.experiment.notify(_(u"Failed to set control '%s': %s") \
						% (var, e))
			checkbox.toggled.connect(self.apply_edit_changes)

		for var, qprogedit in self.auto_editor.iteritems():
			if self.has(var):
				try:
					qprogedit.setText(self.unistr(self.get(var, _eval=False)))
				except Exception as e:
					self.experiment.notify(_(u"Failed to set control '%s': %s") \
						% (var, e))

	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.

		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(
					_(u'All non-alphanumeric characters except underscores have been stripped'))
			else:
				self.experiment.notify(
					_(u'The following characters are not allowed and have been stripped: double-quote ("), backslash (\), and newline'))
		return sane

	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

	def auto_apply_edit_changes(self, rebuild=True):

		"""
		Apply the auto-widget controls

		Keyword arguments:
		rebuild -- deprecated (does nothing) (default=True)
		"""

		debug.msg()
		for var, edit in self.auto_line_edit.iteritems():
			if edit.isEnabled() and isinstance(var, basestring):
				val = unicode(edit.text()).strip()
				if val != u"":
					self.set(var, val)

				# If the variable has no value, we assign a default value if it
				# has been specified, and unset it otherwise.
				elif hasattr(edit, u"default"):
					self.set(var, edit.default)
				else:
					self.unset(var)

		for var, combobox in self.auto_combobox.iteritems():
			if combobox.isEnabled() and isinstance(var, basestring):
				self.set(var, unicode(combobox.currentText()))

		for var, spinbox in self.auto_spinbox.iteritems():
			if spinbox.isEnabled() and isinstance(var, basestring):
				self.set(var, spinbox.value())

		for var, slider in self.auto_slider.iteritems():
			if slider.isEnabled() and isinstance(var, basestring):
				self.set(var, slider.value())

		for var, checkbox in self.auto_checkbox.iteritems():
			if checkbox.isEnabled() and isinstance(var, basestring):
				if checkbox.isChecked():
					val = u"yes"
				else:
					val = u"no"
				self.set(var, val)
				
		for var, qprogedit in self.auto_editor.iteritems():
			if isinstance(var, basestring):
				self.set(var, qprogedit.text())

		return True

	def auto_add_widget(self, widget, var=None):

		"""
		Add a widget to the list of auto-widgets

		Arguments:
		widget -- a QWidget

		Keyword arguments:
		var -- the variable to be linked to the widget (default=None)
		"""

		# Use the object id as a fallback name
		if var == None:
			var = id(widget)
		debug.msg(var)

		if isinstance(widget, QtGui.QSpinBox) or isinstance(widget, \
			QtGui.QDoubleSpinBox):
			widget.editingFinished.connect(self.apply_edit_changes)
			self.auto_spinbox[var] = widget

		elif isinstance(widget, QtGui.QComboBox):
			widget.currentIndexChanged.connect(self.apply_edit_changes)
			self.auto_combobox[var] = widget

		elif isinstance(widget, QtGui.QSlider):
			widget.editingFinished.connect(self.apply_edit_changes)
			self.auto_slider[var] = widget

		elif isinstance(widget, QtGui.QLineEdit):
			widget.editingFinished.connect(self.apply_edit_changes)
			self.auto_line_edit[var] = widget

		elif isinstance(widget, QtGui.QCheckBox):
			widget.toggled.connect(self.apply_edit_changes)
			self.auto_checkbox[var] = widget

		else:
			raise Exception(u"Cannot auto-add widget of type %s" % widget)
Exemple #21
0
class qtitem(base_qtobject):

	"""Base class for the GUI controls of other items"""

	initial_view = u'controls'
	label_align = u'right'
	help_url = None
	lazy_init = False

	def __init__(self):

		"""Constructor"""

		# The auto-widgets are stored in name -> (var, widget) dictionaries
		self.auto_line_edit = {}
		self.auto_combobox = {}
		self.auto_spinbox = {}
		self.auto_slider = {}
		self.auto_editor = {}
		self.auto_checkbox = {}
		# Lazy initialization means that the control widgets are initialized
		# only when they are shown for the first time. This dramatically speeds
		# up opening of files. However, with some items this is not currently
		# possible, because their widgets are also referred when they are not
		# shown. Currently this means the inline_script.
		if self.lazy_init:
			self.container_widget = None
		else:
			self.init_edit_widget()
		self.lock = False
		self.maximized = False
		self.set_validator()
		debug.msg(u'created %s' % self.name)

	@property
	def main_window(self):
		return self.experiment.main_window

	@property
	def overview_area(self):
		return self.experiment.main_window.ui.itemtree

	@property
	def theme(self):
		return self.experiment.main_window.theme

	@property
	def tabwidget(self):
		return self.experiment.main_window.tabwidget

	@property
	def console(self):
		return self.experiment.main_window.console

	@property
	def extension_manager(self):
		return self.experiment.main_window.extension_manager

	@property
	def default_description(self):
		return _(u'Default description')
		
	def open_tab(self, select_in_tree=True):

		"""
		desc:
			Opens the tab if it wasn't yet open, and switches to it.
		"""

		unsaved = self.main_window.unsaved_changes
		self.tabwidget.add(self.widget(), self.item_icon(), self.name)
		self.main_window.set_unsaved(unsaved)
		if select_in_tree:
			self.experiment.main_window.ui.itemtree.select_item(self.name)

	def close_tab(self):

		"""
		desc:
			Closes the tab if it was open.
		"""

		self.tabwidget.remove(self.widget())

	def set_focus(self):

		"""
		desc:
			Gives focus to the most important widget.
		"""

		if hasattr(self, u'focus_widget') and self.focus_widget is not None:
			self.focus_widget.setFocus()

	def set_focus_widget(self, widget, override=False):

		"""
		desc:
			Sets the widget that receives focus when the tab is opened.

		arguments:
			widget:
				desc:	The widget to receive focus or `None` to reset.
				type:	[QWidget, NoneType]

		keywords:
			override:
				desc:	Indicates whether the focus widget should be changed if
						there already is a focus widget.
				type:	bool
		"""

		if override or not hasattr(self, u'focus_widget') or self.focus_widget is None:
			self.focus_widget = widget

	def update_item_icon(self):

		"""
		desc:
			Updates the item icon.
		"""

		self.tabwidget.set_icon(self.widget(), self.item_icon())
		self.experiment.items.set_icon(self.name, self.item_icon())
		self.header_item_icon.setPixmap(
			self.theme.qpixmap(self.item_icon(), 32))

	def item_icon(self):

		"""
		returns:
			desc:	The name of the item icon.
			type:	unicode
		"""

		return self.item_type

	def show_tab(self):

		"""
		desc:
			Is called when the tab becomes visible, and updated the contents.
		"""

		self.extension_manager.fire(u'prepare_open_item', name=self.name)
		self.update_script()
		self.edit_widget()
		self.main_window.ui.itemtree.select_item(self.name, open_tab=False)
		self.extension_manager.fire(u'open_item', name=self.name)

	@requires_init
	def widget(self):

		"""
		returns:
			desc:	The widget that is added to the tabwidget.
			type:	QWidget
		"""

		return self.container_widget

	def init_edit_widget(self, stretch=True):

		"""
		desc:
			Builds the UI.

		keywords:
			stretch:
				desc:	Indicates whether a vertical stretch should be added to
						the bottom of the controls. This is necessary if the
						controls don't expand.
				type:	bool
		"""

		# Header widget
		self.header = header_widget.header_widget(self)
		self.header_hbox = QtWidgets.QHBoxLayout()
		self.header_item_icon = self.theme.qlabel(self.item_icon())
		self.header_hbox.addWidget(self.header_item_icon)
		self.header_hbox.addWidget(self.header)
		self.header_hbox.setContentsMargins(0, 0, 0, 0)
		self.header_hbox.setSpacing(12)

		# Maximize button
		self.button_toggle_maximize = QtWidgets.QPushButton(
			self.theme.qicon(u'view-fullscreen'), u'')
		self.button_toggle_maximize.setToolTip(_(u'Toggle pop-out'))
		self.button_toggle_maximize.setIconSize(QtCore.QSize(16, 16))
		self.button_toggle_maximize.clicked.connect(self.toggle_maximize)
		self.header_hbox.addWidget(self.button_toggle_maximize)
		# View button
		self.button_view = item_view_button(self)
		self.header_hbox.addWidget(self.button_view)
		# Help button
		self.button_help = QtWidgets.QPushButton(self.theme.qicon(u"help"), u"")
		self.button_help.setToolTip(
			_(u"Tell me more about the %s item") % self.item_type)
		self.button_help.setIconSize(QtCore.QSize(16, 16))
		self.button_help.clicked.connect(self.open_help_tab)
		self.header_hbox.addWidget(self.button_help)

		self.header_widget = QtWidgets.QWidget()
		self.header_widget.setLayout(self.header_hbox)

		# The edit_grid is the layout that contains the actual controls for the
		# items.
		self.edit_grid = QtWidgets.QFormLayout()
		if self.label_align == u'right':
			self.edit_grid.setLabelAlignment(QtCore.Qt.AlignRight)
		self.edit_grid.setFieldGrowthPolicy(
			QtWidgets.QFormLayout.FieldsStayAtSizeHint)
		self.edit_grid.setContentsMargins(0, 0, 0, 0)
		self.edit_grid.setVerticalSpacing(6)
		self.edit_grid.setHorizontalSpacing(12)
		self.edit_grid_widget = QtWidgets.QWidget()
		self.edit_grid_widget.setLayout(self.edit_grid)

		# The edit_vbox contains the edit_grid and the header widget
		self.edit_vbox = QtWidgets.QVBoxLayout()
		self.edit_vbox.addWidget(self.edit_grid_widget)
		self.edit_vbox.setContentsMargins(0, 0, 0, 0)
		self.edit_vbox.setSpacing(12)
		if stretch:
			self.edit_vbox.addStretch()
		self._edit_widget = QtWidgets.QWidget()
		self._edit_widget.setWindowIcon(self.theme.qicon(self.item_type))
		self._edit_widget.setLayout(self.edit_vbox)

		# The _script_widget contains the script editor
		from QProgEdit import QTabManager
		self._script_widget = QTabManager(
			handlerButtonText=_(u'Apply and close'), cfg=cfg)
		self._script_widget.focusLost.connect(self.apply_script_changes)
		self._script_widget.cursorRowChanged.connect(self.apply_script_changes)
		self._script_widget.handlerButtonClicked.connect(
			self.apply_script_changes_and_switch_view)
		self._script_widget.addTab(u'Script').setLang(u'OpenSesame')

		# The container_widget is the top-level widget that is actually inserted
		# into the tab widget.
		self.splitter = qtitem_splitter(self)
		if self.initial_view == u'controls':
			self.set_view_controls()
		elif self.initial_view == u'script':
			self.set_view_script()
		elif self.initial_view == u'split':
			self.set_view_split()
		else:
			debug.msg(u'Invalid initial_view: %s' % self.initial_view,
				reason=u'warning')
			self.set_view_controls()
		self.splitter.splitterMoved.connect(self.splitter_moved)
		self.container_vbox = QtWidgets.QVBoxLayout()
		self.container_vbox.setContentsMargins(12, 12, 12, 12)
		self.container_vbox.setSpacing(18)
		self.container_vbox.addWidget(self.header_widget)
		self.container_vbox.addWidget(self.splitter)
		self.container_widget = QtWidgets.QWidget()
		self.container_widget.setLayout(self.container_vbox)
		self.container_widget.on_activate = self.show_tab
		self.container_widget.__item__ = self.name

	def splitter_moved(self, pos, index):

		"""
		desc:
			Is called when the splitter handle is manually moved.

		arguments:
			pos:
				desc:	The splitter-handle position.
				type:	int
			index:
				desc:	The index of the splitter handle. Since there is only
						one handle, this is always 0.
				type:	int
		"""

		sizes = self.splitter.sizes()
		self.edit_size = sizes[0]
		self.script_size = sizes[1]
		if self.script_size == 0:
			self.button_view.set_view_icon(u'controls')
		elif self.edit_size == 0:
			self.button_view.set_view_icon(u'script')
		else:
			self.button_view.set_view_icon(u'split')

	def set_view_controls(self):

		"""
		desc:
			Puts the splitter in control view.
		"""

		self.splitter.setSizes([self.splitter.width(), 0])
		self.button_view.set_view_icon(u'controls')

	def set_view_script(self):

		"""
		desc:
			Puts the splitter in script view.
		"""

		self.splitter.setSizes([0, self.splitter.width()])
		self.button_view.set_view_icon(u'script')

	def set_view_split(self):

		"""
		desc:
			Puts the splitter in split view.
		"""

		self.splitter.setSizes([self.splitter.width()/2,
			self.splitter.width()/2])
		self.button_view.set_view_icon(u'split')

	def update(self):

		"""
		desc:
			Updates both the script and the controls.
		"""

		# Items are updated when their tab is shown, so we don't need to update
		# them if they aren't shown.
		if self.tabwidget.current_item() != self.name:
			return False
		self.update_script()
		self.edit_widget()
		return True

	def update_script(self):

		"""
		desc:
			Regenerates the script and updates the script widget.
		"""

		# Normally, the script starts with a 'define' line and is indented by
		# a tab. We want to undo this, and present only unindented content.
		import textwrap
		script = self.to_string()
		script = script[script.find(u'\t'):]
		script = textwrap.dedent(script)
		if self._script_widget.text() != script:
			self.main_window.set_unsaved()
			self._script_widget.setText(script)
			self.extension_manager.fire(u'change_item', name=self.name)

	def edit_widget(self, *deprecated, **_deprecated):

		"""
		desc:
			This function updates the controls based on the item state.
		"""

		self.auto_edit_widget()
		self.header.refresh()

	def apply_edit_changes(self, *deprecated, **_deprecated):

		"""
		desc:
			Applies changes to the graphical controls.
		"""

		self.auto_apply_edit_changes()
		self.update_script()
		return True

	def apply_script_changes(self, *deprecated, **_deprecated):

		"""
		desc:
			Applies changes to the script.
		"""

		if not self.validate_script():
			return
		new_script = self._script_widget.text()
		old_script = self.to_string()
		self.from_string(new_script)
		if old_script != new_script:
			self.main_window.set_unsaved()
		self.edit_widget()

	def apply_script_changes_and_switch_view(self, *deprecated, **_deprecated):

		"""
		desc:
			Applies changes to the script if possible. If so, switches to the
			controls view.
		"""

		if self.validate_script():
			self.set_view_controls()

	def validate_script(self):

		"""
		desc:
			Checks whether the script is syntactically valid. If not, the
			offending line is highlighted if QProgEdit suppors setInvalid().

		returns:
			type:	bool
		"""

		script = self._script_widget.text()
		# First create a dummy item to see if the string can be parsed.
		try:
			self.validator(self.name, self.experiment, script)
			return True
		except Exception as e:
			# If an error occurs, we first parse the first line, then the first
			# and second, and so on, until we find the error.
			l = script.split(u'\n')
			for line_nr, line in enumerate(l):
				test_script = u'\n'.join(l[:line_nr])
				try:
					self.validator(self.name, self.experiment, test_script)
				except Exception as e_:
					if not isinstance(e_, osexception):
						e_ = osexception(e_)
					if hasattr(self._script_widget, u'setInvalid'):
						self._script_widget.setInvalid(line_nr, e_.markdown())
					break
			self.console.write(e)
			return False

	def set_validator(self):

		"""
		desc:
			Sets the validator class, that is, the class that is used to parse
			scripts to see if they are syntactically correct. Currently, we use
			the class that defines from_string().
		"""

		import inspect
		meth = self.from_string
		for cls in inspect.getmro(self.__class__):
			if meth.__name__ in cls.__dict__:
				break
		debug.msg(u'validator: %s' % cls)
		self.validator = cls

	@requires_init
	def rename(self, from_name, to_name):

		"""
		desc:
			Handles renaming of an item (not necesarrily the current item).

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

		if self.name != from_name:
			return
		self.name = to_name
		self.container_widget.__item__ = self.name
		self.header.set_name(to_name)
		index = self.tabwidget.indexOf(self.widget())
		if index is not None:
			self.tabwidget.setTabText(index, to_name)

	def open_help_tab(self):

		"""
		desc:
			Opens a help tab.
		"""

		if self.help_url is None:
			self.tabwidget.open_help(self.item_type)
		else:
			self.tabwidget.open_osdoc(self.help_url)

	def toggle_maximize(self):

		"""
		desc:
			Toggles edit-widget maximization.
		"""

		if not self.maximized:
			# Always ignore close events. This is necessary, because otherwise
			# the pop-out widget can be closed without re-enabling the main
			# window.
			self.main_window.block_close_event = True
			self.container_widget.closeEvent = lambda e: e.ignore()
			self.container_widget.setParent(None)
			self.container_widget.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint|\
				QtCore.Qt.WindowMaximizeButtonHint|\
				QtCore.Qt.CustomizeWindowHint)
			self.container_widget.showMaximized()
			self.container_widget.show()
			self.button_toggle_maximize.setIcon(
				self.theme.qicon(u'view-restore'))
		else:
			self.main_window.block_close_event = False
			self.container_widget.setParent(self.main_window)
			self.open_tab()
			self.button_toggle_maximize.setIcon(
				self.theme.qicon(u'view-fullscreen'))
		self.maximized = not self.maximized
		self.button_help.setDisabled(self.maximized)
		self.main_window.setDisabled(self.maximized)

	def delete(self, item_name, item_parent=None, index=None):

		"""
		desc:
			Is called when an item is deleted (not necessarily the current one).

		arguments:
			item_name:
			 	desc:	The name of the item to be deleted.
				type:	str

		keywords:
			item_parent:
			 	desc:	The name of the parent item.
				type:	str
			index:
			 	desc:	The index of the item in the parent.
				type:	int
		"""

		pass

	def build_item_tree(self, toplevel=None, items=[], max_depth=-1,
		extra_info=None):

		"""
		desc:
			Constructs an item tree.

		keywords:
			toplevel:
			 	desc:	The toplevel widget.
				type:	tree_base_item
			items:
				desc:	A list of item names that have already been added, to
				 		prevent recursion.
				tyep:	list

		returns:
			type:	tree_item_item
		"""

		widget = tree_item_item(self, extra_info=extra_info)
		items.append(self.name)
		if toplevel is not None:
			toplevel.addChild(widget)
		return widget

	def parents(self):

		"""
		returns:
			desc:	A list of all parents (names) of the current item.
			type:	list
		"""

		l = [self.name]
		for item in self.experiment.items:
			if self.experiment.items[item].is_child_item(self.name):
				l.append(item)
		return l

	def get_ready(self):

		"""
		desc:
			This function should be overridden to do any last-minute stuff that
			and item should do before an experiment is actually run, such as
			applying pending script changes.

		returns:
			desc:	True if some action has been taken, False if nothing was
					done.
			type:	bool
		"""

		return False

	def auto_edit_widget(self):

		"""
		desc:
			Updates the GUI controls based on the auto-widgets.
		"""

		for var, edit in self.auto_line_edit.items():
			if isinstance(var, int):
				continue
			if var in self.var:
				val = safe_decode(self.var.get(var, _eval=False))
			else:
				val = u''
			if val != edit.text():
				edit.setText(val)

		for var, combobox in self.auto_combobox.items():
			if isinstance(var, int):
				continue
			val = self.var.get(var, _eval=False, default=u'')
			i = combobox.findText(safe_decode(val))
			# Set the combobox to the select item
			if i >= 0:
				if i != combobox.currentIndex() or not combobox.isEnabled():
					combobox.setDisabled(False)
					combobox.setCurrentIndex(i)
			# If no value was specified, set the combobox to a blank item
			elif val == u'':
				if combobox.currentIndex() >= 0 or not combobox.isEnabled():
					combobox.setDisabled(False)
					combobox.setCurrentIndex(-1)
			# If an unknown value has been specified, notify the user
			else:
				if combobox.isEnabled():
					combobox.setDisabled(True)
					self.extension_manager.fire(u'notify',
						message=_(u'"%s" is set to a variable or '
						u'unknown value and can only be edited through '
						u'the script.') % var, category=u'info')

		for var, spinbox in list(self.auto_spinbox.items()) \
			+ list(self.auto_slider.items()):
			if isinstance(var, int):
				continue
			if var in self.var:
				val = self.var.get(var, _eval=False)
				if type(val) in (float, int):
					if spinbox.value() == val and spinbox.isEnabled():
						continue
					spinbox.setDisabled(False)
					try:
						spinbox.setValue(val)
					except Exception as e:
						self.experiment.notify(
							_(u"Failed to set control '%s': %s") % (var, e))
				else:
					if not spinbox.isEnabled():
						continue
					spinbox.setDisabled(True)
					self.extension_manager.fire(u'notify',
						message=_(u'"%s" is defined using '
						'variables and can only be edited through the '
						'script.') % var, category=u'info')

		for var, checkbox in self.auto_checkbox.items():
			if isinstance(var, int):
				continue
			if var in self.var:
				val = self.var.get(var, _eval=False)
				if val in [u'yes', u'no']:
					checkbox.setDisabled(False)
					checked = val == u'yes'
					if checked != checkbox.isChecked():
						checkbox.setChecked(checked)
				else:
					checkbox.setDisabled(True)
					self.extension_manager.fire(u'notify',
						message=_(u'"%s" is defined using '
						u'variables or has an invalid value, and can only be '
						u'edited through the script.') % var,
						category=u'info')

		for var, qprogedit in self.auto_editor.items():
			if isinstance(var, int):
				continue
			if var in self.var:
				val = safe_decode(self.var.get(var, _eval=False))
				if val != qprogedit.text():
					qprogedit.setText(val)

	def auto_apply_edit_changes(self, rebuild=True):

		"""
		desc:
			Applies the auto-widget controls.

		keywords:
			rebuild:	Deprecated (does nothing).
		"""

		for var, edit in self.auto_line_edit.items():
			if isinstance(var, int):
				continue
			if edit.isEnabled() and isinstance(var, basestring):
				val = str(edit.text()).strip()
				if val != u'':
					self.var.set(var, val)
					continue
				# If no text was entered, we use a default if available ...
				if hasattr(edit, u'default'):
					self.var.set(var, edit.default)
					continue
				# ... or unset the variable if no default is available.
				self.var.unset(var)

		for var, combobox in self.auto_combobox.items():
			if isinstance(var, int):
				continue
			if combobox.isEnabled() and isinstance(var, basestring):
				self.var.set(var, str(combobox.currentText()))

		for var, spinbox in self.auto_spinbox.items():
			if isinstance(var, int):
				continue
			if spinbox.isEnabled() and isinstance(var, basestring):
				self.var.set(var, spinbox.value())

		for var, slider in self.auto_slider.items():
			if isinstance(var, int):
				continue
			if slider.isEnabled() and isinstance(var, basestring):
				self.var.set(var, slider.value())

		for var, checkbox in self.auto_checkbox.items():
			if isinstance(var, int):
				continue
			if checkbox.isEnabled() and isinstance(var, basestring):
				if checkbox.isChecked():
					val = u"yes"
				else:
					val = u"no"
				self.var.set(var, val)

		for var, qprogedit in self.auto_editor.items():
			if isinstance(var, int):
				continue
			if isinstance(var, basestring):
				self.var.set(var, qprogedit.text())

		return True

	def auto_add_widget(self, widget, var=None, apply_func=None):

		"""
		desc:
			Adds a widget to the list of auto-widgets.

		arguments:
			widget:
			 	type:	QWidget

		keywords:
			var:	The name of the experimental variable to be linked to the
					widget, or None to use an automatically chosen name.
			type:	[str, NoneType]
		"""

		# Use the object id as a fallback name
		if var is None:
			var = id(widget)
		if apply_func is None:
			apply_func = self.apply_edit_changes
		debug.msg(var)
		self.set_focus_widget(widget)
		if isinstance(widget, QtWidgets.QSpinBox) or isinstance(widget,
			QtWidgets.QDoubleSpinBox):
			widget.editingFinished.connect(apply_func)
			self.auto_spinbox[var] = widget
		elif isinstance(widget, QtWidgets.QComboBox):
			widget.activated.connect(apply_func)
			self.auto_combobox[var] = widget
		elif isinstance(widget, QtWidgets.QSlider) \
			or isinstance(widget, QtWidgets.QDial):
			widget.valueChanged.connect(apply_func)
			self.auto_slider[var] = widget
		elif isinstance(widget, QtWidgets.QLineEdit) or isinstance(widget,
			pool_select):
			widget.editingFinished.connect(apply_func)
			self.auto_line_edit[var] = widget
		elif isinstance(widget, QtWidgets.QCheckBox):
			widget.clicked.connect(apply_func)
			self.auto_checkbox[var] = widget
		else:
			raise Exception(u"Cannot auto-add widget of type %s" % widget)
			
	def children(self):

		"""
		returns:
			desc:	A list of children, including grand children, and so on.
			type:	list
		"""

		return []

	def is_child_item(self, item_name):

		"""
		desc:
			Checks if an item is somewhere downstream from the current item
			in the experimental hierarchy.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		returns:
			desc:	True if the current item is offspring of the item, False
					otherwise.
			type:	bool
		"""

		return False

	def insert_child_item(self, item_name, index=0):

		"""
		desc:
			Inserts a child item, if applicable to the item type.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		keywords:
			index:
				desc:	The index of the child item.
				type:	int
		"""

		pass

	def remove_child_item(self, item_name, index=0):

		"""
		desc:
			Removes a child item, if applicable to the item type.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		keywords:
			index:
				desc:	The index of the child item, if applicable. A negative
						value indicates all instances.
				type:	int
		"""

		pass
Exemple #22
0
	def init_edit_widget(self, stretch=True):

		"""
		desc:
			Builds the UI.

		keywords:
			stretch:
				desc:	Indicates whether a vertical stretch should be added to
						the bottom of the controls. This is necessary if the
						controls don't expand.
				type:	bool
		"""

		# Header widget
		self.header = header_widget.header_widget(self)
		self.header_hbox = QtWidgets.QHBoxLayout()
		self.header_item_icon = self.theme.qlabel(self.item_icon())
		self.header_hbox.addWidget(self.header_item_icon)
		self.header_hbox.addWidget(self.header)
		self.header_hbox.setContentsMargins(0, 0, 0, 0)
		self.header_hbox.setSpacing(12)

		# Maximize button
		self.button_toggle_maximize = QtWidgets.QPushButton(
			self.theme.qicon(u'view-fullscreen'), u'')
		self.button_toggle_maximize.setToolTip(_(u'Toggle pop-out'))
		self.button_toggle_maximize.setIconSize(QtCore.QSize(16, 16))
		self.button_toggle_maximize.clicked.connect(self.toggle_maximize)
		self.header_hbox.addWidget(self.button_toggle_maximize)
		# View button
		self.button_view = item_view_button(self)
		self.header_hbox.addWidget(self.button_view)
		# Help button
		self.button_help = QtWidgets.QPushButton(self.theme.qicon(u"help"), u"")
		self.button_help.setToolTip(
			_(u"Tell me more about the %s item") % self.item_type)
		self.button_help.setIconSize(QtCore.QSize(16, 16))
		self.button_help.clicked.connect(self.open_help_tab)
		self.header_hbox.addWidget(self.button_help)

		self.header_widget = QtWidgets.QWidget()
		self.header_widget.setLayout(self.header_hbox)

		# The edit_grid is the layout that contains the actual controls for the
		# items.
		self.edit_grid = QtWidgets.QFormLayout()
		if self.label_align == u'right':
			self.edit_grid.setLabelAlignment(QtCore.Qt.AlignRight)
		self.edit_grid.setFieldGrowthPolicy(
			QtWidgets.QFormLayout.FieldsStayAtSizeHint)
		self.edit_grid.setContentsMargins(0, 0, 0, 0)
		self.edit_grid.setVerticalSpacing(6)
		self.edit_grid.setHorizontalSpacing(12)
		self.edit_grid_widget = QtWidgets.QWidget()
		self.edit_grid_widget.setLayout(self.edit_grid)

		# The edit_vbox contains the edit_grid and the header widget
		self.edit_vbox = QtWidgets.QVBoxLayout()
		self.edit_vbox.addWidget(self.edit_grid_widget)
		self.edit_vbox.setContentsMargins(0, 0, 0, 0)
		self.edit_vbox.setSpacing(12)
		if stretch:
			self.edit_vbox.addStretch()
		self._edit_widget = QtWidgets.QWidget()
		self._edit_widget.setWindowIcon(self.theme.qicon(self.item_type))
		self._edit_widget.setLayout(self.edit_vbox)

		# The _script_widget contains the script editor
		from QProgEdit import QTabManager
		self._script_widget = QTabManager(
			handlerButtonText=_(u'Apply and close'), cfg=cfg)
		self._script_widget.focusLost.connect(self.apply_script_changes)
		self._script_widget.cursorRowChanged.connect(self.apply_script_changes)
		self._script_widget.handlerButtonClicked.connect(
			self.apply_script_changes_and_switch_view)
		self._script_widget.addTab(u'Script').setLang(u'OpenSesame')

		# The container_widget is the top-level widget that is actually inserted
		# into the tab widget.
		self.splitter = qtitem_splitter(self)
		if self.initial_view == u'controls':
			self.set_view_controls()
		elif self.initial_view == u'script':
			self.set_view_script()
		elif self.initial_view == u'split':
			self.set_view_split()
		else:
			debug.msg(u'Invalid initial_view: %s' % self.initial_view,
				reason=u'warning')
			self.set_view_controls()
		self.splitter.splitterMoved.connect(self.splitter_moved)
		self.container_vbox = QtWidgets.QVBoxLayout()
		self.container_vbox.setContentsMargins(12, 12, 12, 12)
		self.container_vbox.setSpacing(18)
		self.container_vbox.addWidget(self.header_widget)
		self.container_vbox.addWidget(self.splitter)
		self.container_widget = QtWidgets.QWidget()
		self.container_widget.setLayout(self.container_vbox)
		self.container_widget.on_activate = self.show_tab
		self.container_widget.__item__ = self.name
Exemple #23
0
class inline_script(inline_script_runtime, qtplugin):
    """The inline_script GUI controls"""
    def __init__(self, name, experiment, string=None):
        """See item."""

        inline_script_runtime.__init__(self, name, experiment, string)
        qtplugin.__init__(self)

    def apply_edit_changes(self):
        """See qtitem."""

        super(inline_script, self).apply_edit_changes(self)
        sp = self.qprogedit.text(index=0)
        sr = self.qprogedit.text(index=1)
        self.set(u'_prepare', sp)
        self.set(u'_run', sr)
        self.update_item_icon()

    def set_focus(self):
        """
		desc:
			Allows the item to focus the most important widget.
		"""

        self.qprogedit.setFocus()

    def item_icon(self):
        """
		desc:
			Determines the icon, based on whether the scripts are syntactically
			correct.

		returns:
			desc:	An icon name.
			type:	unicode
		"""

        if self.experiment.python_workspace.check_syntax(
         self.unistr(self.get(u'_prepare', _eval=False)))\
         and self.experiment.python_workspace.check_syntax(
         self.unistr(self.get(u'_run', _eval=False))):
            return u'os-inline_script'
        return u'os-inline_script-syntax-error'

    def build_item_tree(self,
                        toplevel=None,
                        items=[],
                        max_depth=-1,
                        extra_info=None):
        """See qtitem."""

        widget = tree_inline_script_item(self,
                                         extra_info=extra_info,
                                         symbols=(max_depth < 0
                                                  or max_depth > 1))
        items.append(self.name)
        if toplevel != None:
            toplevel.addChild(widget)
        return widget

    def init_edit_widget(self):
        """See qtitem."""

        from QProgEdit import QTabManager
        super(inline_script, self).init_edit_widget(stretch=False)
        self.qprogedit = QTabManager(cfg=cfg)
        self.qprogedit.handlerButtonClicked.connect(self.apply_edit_changes)
        self.qprogedit.focusLost.connect(self.apply_edit_changes)
        self.qprogedit.cursorRowChanged.connect(self.apply_edit_changes)
        self.qprogedit.addTab(u'Prepare').setLang(u'Python')
        self.qprogedit.addTab(u'Run').setLang(u'Python')
        # Switch to the run phase, unless there is only content for the prepare
        # phase.
        if self._run == u'' and self._prepare != u'':
            self.qprogedit.setCurrentIndex(0)
        else:
            self.qprogedit.setCurrentIndex(1)
        self.edit_vbox.addWidget(self.qprogedit)

    def edit_widget(self):
        """See qtitem."""

        super(inline_script, self).edit_widget()
        self.qprogedit.tab(0).setText(self.unistr(self._prepare))
        self.qprogedit.tab(1).setText(self.unistr(self._run))

    def get_ready(self):
        """See qtitem."""

        if self.qprogedit.isAnyModified():
            debug.msg(u'applying pending script changes')
            self.apply_edit_changes()
            return True
        return super(inline_script, self).get_ready()
Exemple #24
0
class qtitem(base_qtobject):

    """Base class for the GUI controls of other items"""

    initial_view = u"controls"

    def __init__(self):

        """Constructor"""

        # The auto-widgets are stored in name -> (var, widget) dictionaries
        self.auto_line_edit = {}
        self.auto_combobox = {}
        self.auto_spinbox = {}
        self.auto_slider = {}
        self.auto_editor = {}
        self.auto_checkbox = {}
        self.init_edit_widget()
        self.lock = False
        self.maximized = False
        debug.msg(u"created %s" % self.name)

    @property
    def main_window(self):
        return self.experiment.main_window

    @property
    def theme(self):
        return self.experiment.main_window.theme

    @property
    def tabwidget(self):
        return self.experiment.main_window.tabwidget

    @property
    def console(self):
        return self.experiment.main_window.console

    @property
    def extension_manager(self):
        return self.experiment.main_window.extension_manager

    def open_tab(self, select_in_tree=True):

        """
		desc:
			Opens the tab if it wasn't yet open, and switches to it.
		"""

        unsaved = self.main_window.unsaved_changes
        self.tabwidget.add(self.widget(), self.item_icon(), self.name)
        self.main_window.set_unsaved(unsaved)
        if select_in_tree:
            self.experiment.main_window.ui.itemtree.select_item(self.name)

    def close_tab(self):

        """
		desc:
			Closes the tab if it was open.
		"""

        self.tabwidget.remove(self.widget())

    def set_focus(self):

        """
		desc:
			Gives focus to the most important widget.
		"""

        if hasattr(self, u"focus_widget") and self.focus_widget is not None:
            self.focus_widget.setFocus()

    def set_focus_widget(self, widget, override=False):

        """
		desc:
			Sets the widget that receives focus when the tab is opened.

		arguments:
			widget:
				desc:	The widget to receive focus or `None` to reset.
				type:	[QWidget, NoneType]

		keywords:
			override:
				desc:	Indicates whether the focus widget should be changed if
						there already is a focus widget.
				type:	bool
		"""

        if override or not hasattr(self, u"focus_widget") or self.focus_widget is None:
            self.focus_widget = widget

    def update_item_icon(self):

        """
		desc:
			Updates the item icon.
		"""

        self.tabwidget.set_icon(self.widget(), self.item_icon())
        self.experiment.items.set_icon(self.name, self.item_icon())
        self.header_item_icon.setPixmap(self.theme.qpixmap(self.item_icon(), 32))

    def item_icon(self):

        """
		returns:
			desc:	The name of the item icon.
			type:	unicode
		"""

        return self.item_type

    def show_tab(self):

        """
		desc:
			Is called when the tab becomes visible, and updated the contents.
		"""

        self.update_script()
        self.edit_widget()
        self.main_window.ui.itemtree.select_item(self.name, open_tab=False)

    def widget(self):

        """
		returns:
			desc:	The widget that is added to the tabwidget.
			type:	QWidget
		"""

        return self.container_widget

    def init_edit_widget(self, stretch=True):

        """
		desc:
			Builds the UI.

		keywords:
			stretch:
				desc:	Indicates whether a vertical stretch should be added to
						the bottom of the controls. This is necessary if the
						controls don't expand.
				type:	bool
		"""

        # Header widget
        self.header = header_widget.header_widget(self)
        self.user_hint_widget = user_hint_widget.user_hint_widget(self.experiment.main_window, self)
        self.header_hbox = QtGui.QHBoxLayout()
        self.header_item_icon = self.theme.qlabel(self.item_icon())
        self.header_hbox.addWidget(self.header_item_icon)
        self.header_hbox.addWidget(self.header)
        self.header_hbox.setContentsMargins(0, 2, 0, 0)

        # Maximize button
        self.button_toggle_maximize = QtGui.QPushButton(self.theme.qicon(u"view-fullscreen"), u"")
        self.button_toggle_maximize.setToolTip(_(u"Toggle pop-out"))
        self.button_toggle_maximize.setIconSize(QtCore.QSize(16, 16))
        self.button_toggle_maximize.clicked.connect(self.toggle_maximize)
        self.header_hbox.addWidget(self.button_toggle_maximize)
        # View button
        self.button_view = item_view_button(self)
        self.header_hbox.addWidget(self.button_view)
        # Help button
        self.button_help = QtGui.QPushButton(self.theme.qicon(u"help"), u"")
        self.button_help.setToolTip(_(u"Tell me more about the %s item") % self.item_type)
        self.button_help.setIconSize(QtCore.QSize(16, 16))
        self.button_help.clicked.connect(self.open_help_tab)
        self.header_hbox.addWidget(self.button_help)

        self.header_widget = QtGui.QWidget()
        self.header_widget.setLayout(self.header_hbox)

        # The edit_grid is the layout that contains the actual controls for the
        # items.
        self.edit_grid = QtGui.QGridLayout()
        self.edit_grid.setColumnStretch(2, 2)
        self.edit_grid_widget = QtGui.QWidget()
        self.edit_grid_widget.setLayout(self.edit_grid)

        # The edit_vbox contains the edit_grid and the header widget
        self.edit_vbox = QtGui.QVBoxLayout()
        self.edit_vbox.addWidget(self.user_hint_widget)
        self.edit_vbox.addWidget(self.edit_grid_widget)
        self.edit_vbox.setContentsMargins(0, 0, 0, 0)
        self.edit_vbox.setSpacing(0)
        if stretch:
            self.edit_vbox.addStretch()
        self._edit_widget = QtGui.QWidget()
        self._edit_widget.setWindowIcon(self.theme.qicon(self.item_type))
        self._edit_widget.setLayout(self.edit_vbox)

        # The _script_widget contains the script editor
        from QProgEdit import QTabManager

        self._script_widget = QTabManager(handlerButtonText=_(u"Apply and close"), cfg=cfg)
        self._script_widget.focusLost.connect(self.apply_script_changes)
        self._script_widget.cursorRowChanged.connect(self.apply_script_changes)
        self._script_widget.handlerButtonClicked.connect(self.set_view_controls)
        self._script_widget.addTab(u"Script").setLang(u"OpenSesame")

        # The container_widget is the top-level widget that is actually inserted
        # into the tab widget.
        self.splitter = qtitem_splitter(self)
        if self.initial_view == u"controls":
            self.set_view_controls()
        elif self.initial_view == u"script":
            self.set_view_script()
        elif self.initial_view == u"split":
            self.set_view_split()
        else:
            debug.msg(u"Invalid initial_view: %s" % self.initial_view, reason=u"warning")
            self.set_view_controls()
        self.splitter.splitterMoved.connect(self.splitter_moved)
        self.container_vbox = QtGui.QVBoxLayout()
        self.container_vbox.setContentsMargins(4, 0, 4, 4)
        self.container_vbox.addWidget(self.header_widget)
        self.container_vbox.addWidget(self.splitter)
        self.container_widget = QtGui.QWidget()
        self.container_widget.setLayout(self.container_vbox)
        self.container_widget.on_activate = self.show_tab
        self.container_widget.__item__ = self.name

    def splitter_moved(self, pos, index):

        """
		desc:
			Is called when the splitter handle is manually moved.

		arguments:
			pos:
				desc:	The splitter-handle position.
				type:	int
			index:
				desc:	The index of the splitter handle. Since there is only
						one handle, this is always 0.
				type:	int
		"""

        sizes = self.splitter.sizes()
        self.edit_size = sizes[0]
        self.script_size = sizes[1]
        if self.script_size == 0:
            self.button_view.set_view_icon(u"controls")
        elif self.edit_size == 0:
            self.button_view.set_view_icon(u"script")
        else:
            self.button_view.set_view_icon(u"split")

    def set_view_controls(self):

        """
		desc:
			Puts the splitter in control view.
		"""

        self.splitter.setSizes([self.splitter.width(), 0])
        self.button_view.set_view_icon(u"controls")

    def set_view_script(self):

        """
		desc:
			Puts the splitter in script view.
		"""

        self.splitter.setSizes([0, self.splitter.width()])
        self.button_view.set_view_icon(u"script")

    def set_view_split(self):

        """
		desc:
			Puts the splitter in split view.
		"""

        self.splitter.setSizes([self.splitter.width() / 2, self.splitter.width() / 2])
        self.button_view.set_view_icon(u"split")

    def update(self):

        """
		desc:
			Updates both the script and the controls.
		"""

        self.update_script()
        self.edit_widget()

    def update_script(self):

        """
		desc:
			Regenerates the script and updates the script widget.
		"""

        # Normally, the script starts with a 'define' line and is indented by
        # a tab. We want to undo this, and present only unindented content.
        import textwrap

        script = self.to_string()
        script = script[script.find(u"\t") :]
        script = textwrap.dedent(script)
        if self._script_widget.text() != script:
            self.main_window.set_unsaved()
            self._script_widget.setText(script)
            self.extension_manager.fire(u"change_item", name=self.name)

    def edit_widget(self, *deprecated, **_deprecated):

        """
		desc:
			This function updates the controls based on the item state.
		"""

        self.auto_edit_widget()
        self.header.refresh()

    def apply_edit_changes(self, *deprecated, **_deprecated):

        """
		desc:
			Applies changes to the graphical controls.
		"""

        self.auto_apply_edit_changes()
        self.update_script()
        return True

    def apply_script_changes(self, *deprecated, **_deprecated):

        """
		desc:
			Applies changes to the script, by re-parsing the item from string.
		"""

        old_script = self.to_string()
        new_script = self._script_widget.text()
        try:
            self.from_string(new_script)
        except osexception as e:
            md = _(u"# Error\n\nFailed to apply script for the " u"following reason:\n\n- ") + e.markdown()
            self.tabwidget.open_markdown(md)
            self.console.write(e)
            self.from_string(old_script)
        if old_script != new_script:
            self.main_window.set_unsaved()
        self.edit_widget()

    def rename(self, from_name, to_name):

        """
		desc:
			Handles renaming of an item (not necesarrily the current item).

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

        if self.name != from_name:
            return
        self.name = to_name
        self.container_widget.__item__ = self.name
        self.header.set_name(to_name)
        index = self.tabwidget.indexOf(self.widget())
        if index is not None:
            self.tabwidget.setTabText(index, to_name)

    def open_help_tab(self):

        """
		desc:
			Opens a help tab.
		"""

        self.experiment.main_window.ui.tabwidget.open_help(self.item_type)

    def toggle_maximize(self):

        """
		desc:
			Toggles edit-widget maximization.
		"""

        if not self.maximized:
            # Always ignore close events. This is necessary, because otherwise
            # the pop-out widget can be closed without re-enabling the main
            # window.
            self.main_window.block_close_event = True
            self.container_widget.closeEvent = lambda e: e.ignore()
            self.container_widget.setParent(None)
            self.container_widget.setWindowFlags(
                QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.WindowMaximizeButtonHint | QtCore.Qt.CustomizeWindowHint
            )
            self.container_widget.showMaximized()
            self.container_widget.show()
            self.button_toggle_maximize.setIcon(self.theme.qicon(u"view-restore"))
        else:
            self.main_window.block_close_event = False
            self.container_widget.setParent(self.main_window)
            self.open_tab()
            self.button_toggle_maximize.setIcon(self.theme.qicon(u"view-fullscreen"))
        self.maximized = not self.maximized
        self.user_hint_widget.disable(self.maximized)
        self.button_help.setDisabled(self.maximized)
        self.main_window.setDisabled(self.maximized)

    def delete(self, item_name, item_parent=None, index=None):

        """
		desc:
			Is called when an item is deleted (not necessarily the current one).

		arguments:
			item_name:
			 	desc:	The name of the item to be deleted.
				type:	str

		keywords:
			item_parent:
			 	desc:	The name of the parent item.
				type:	str
			index:
			 	desc:	The index of the item in the parent.
				type:	int
		"""

        pass

    def build_item_tree(self, toplevel=None, items=[], max_depth=-1, extra_info=None):

        """
		desc:
			Constructs an item tree.

		keywords:
			toplevel:
			 	desc:	The toplevel widget.
				type:	tree_base_item
			items:
				desc:	A list of item names that have already been added, to
				 		prevent recursion.
				tyep:	list

		returns:
			type:	tree_item_item
		"""

        widget = tree_item_item(self, extra_info=extra_info)
        items.append(self.name)
        if toplevel is not None:
            toplevel.addChild(widget)
        return widget

    def parents(self):

        """
		returns:
			desc:	A list of all parents (names) of the current item.
			type:	list
		"""

        l = [self.name]
        for item in self.experiment.items:
            if self.experiment.items[item].is_child_item(self.name):
                l.append(item)
        return l

    def get_ready(self):

        """
		desc:
			This function should be overridden to do any last-minute stuff that
			and item should do before an experiment is actually run, such as
			applying pending script changes.

		returns:
			desc:	True if some action has been taken, False if nothing was
					done.
			type:	bool
		"""

        return False

    def auto_edit_widget(self):

        """
		desc:
			Updates the GUI controls based on the auto-widgets.
		"""

        for var, edit in self.auto_line_edit.items():
            if isinstance(var, int):
                continue
            if var in self.var:
                val = safe_decode(self.var.get(var, _eval=False))
            else:
                val = u""
            if val != edit.text():
                edit.setText(val)

        for var, combobox in self.auto_combobox.items():
            if isinstance(var, int):
                continue
            val = self.var.get(var, _eval=False, default=u"")
            i = combobox.findText(safe_decode(val))
            # Set the combobox to the select item
            if i >= 0:
                if i != combobox.currentIndex() or not combobox.isEnabled():
                    combobox.setDisabled(False)
                    combobox.setCurrentIndex(i)
                    # If no value was specified, set the combobox to a blank item
            elif val == u"":
                if combobox.currentIndex() >= 0 or not combobox.isEnabled():
                    combobox.setDisabled(False)
                    combobox.setCurrentIndex(-1)
                    # If an unknown value has been specified, notify the user
            else:
                if combobox.isEnabled():
                    combobox.setDisabled(True)
                    self.user_hint_widget.add(
                        _(
                            u'"%s" is set to a variable or '
                            u"unknown value and can only be edited through "
                            u"the script."
                        )
                        % var
                    )

        for var, spinbox in list(self.auto_spinbox.items()) + list(self.auto_slider.items()):
            if isinstance(var, int):
                continue
            if var in self.var:
                val = self.var.get(var, _eval=False)
                if type(val) in (float, int):
                    if spinbox.value() == val and spinbox.isEnabled():
                        continue
                    spinbox.setDisabled(False)
                    try:
                        spinbox.setValue(val)
                    except Exception as e:
                        self.experiment.notify(_(u"Failed to set control '%s': %s") % (var, e))
                else:
                    if not spinbox.isEnabled():
                        continue
                    spinbox.setDisabled(True)
                    self.user_hint_widget.add(
                        _(u'"%s" is defined using ' "variables and can only be edited through the " "script.") % var
                    )

        for var, checkbox in self.auto_checkbox.items():
            if isinstance(var, int):
                continue
            if var in self.var:
                val = self.var.get(var, _eval=False)
                if val in [u"yes", u"no"]:
                    checkbox.setDisabled(False)
                    checked = val == u"yes"
                    if checked != checkbox.isChecked():
                        checkbox.setChecked(checked)
                else:
                    checkbox.setDisabled(True)
                    self.user_hint_widget.add(
                        _(
                            u'"%s" is defined using '
                            u"variables or has an invalid value, and can only be "
                            u"edited through the script."
                        )
                        % var
                    )

        for var, qprogedit in self.auto_editor.items():
            if isinstance(var, int):
                continue
            if var in self.var:
                val = safe_decode(self.var.get(var, _eval=False))
                if val != qprogedit.text():
                    qprogedit.setText(val)

        self.user_hint_widget.refresh()

    def auto_apply_edit_changes(self, rebuild=True):

        """
		desc:
			Applies the auto-widget controls.

		keywords:
			rebuild:	Deprecated (does nothing).
		"""

        for var, edit in self.auto_line_edit.items():
            if isinstance(var, int):
                continue
            if edit.isEnabled() and isinstance(var, basestring):
                val = str(edit.text()).strip()
                if val != u"":
                    self.var.set(var, val)
                    continue
                    # If no text was entered, we use a default if available ...
                if hasattr(edit, u"default"):
                    self.var.set(var, edit.default)
                    continue
                    # ... or unset the variable if no default is available.
                self.var.unset(var)

        for var, combobox in self.auto_combobox.items():
            if isinstance(var, int):
                continue
            if combobox.isEnabled() and isinstance(var, basestring):
                self.var.set(var, str(combobox.currentText()))

        for var, spinbox in self.auto_spinbox.items():
            if isinstance(var, int):
                continue
            if spinbox.isEnabled() and isinstance(var, basestring):
                self.var.set(var, spinbox.value())

        for var, slider in self.auto_slider.items():
            if isinstance(var, int):
                continue
            if slider.isEnabled() and isinstance(var, basestring):
                self.var.set(var, slider.value())

        for var, checkbox in self.auto_checkbox.items():
            if isinstance(var, int):
                continue
            if checkbox.isEnabled() and isinstance(var, basestring):
                if checkbox.isChecked():
                    val = u"yes"
                else:
                    val = u"no"
                self.var.set(var, val)

        for var, qprogedit in self.auto_editor.items():
            if isinstance(var, int):
                continue
            if isinstance(var, basestring):
                self.var.set(var, qprogedit.text())

        return True

    def auto_add_widget(self, widget, var=None):

        """
		desc:
			Adds a widget to the list of auto-widgets.

		arguments:
			widget:
			 	type:	QWidget

		keywords:
			var:	The name of the experimental variable to be linked to the
					widget, or None to use an automatically chosen name.
			type:	[str, NoneType]
		"""

        # Use the object id as a fallback name
        if var is None:
            var = id(widget)
        debug.msg(var)
        self.set_focus_widget(widget)
        if isinstance(widget, QtGui.QSpinBox) or isinstance(widget, QtGui.QDoubleSpinBox):
            widget.editingFinished.connect(self.apply_edit_changes)
            self.auto_spinbox[var] = widget
        elif isinstance(widget, QtGui.QComboBox):
            widget.activated.connect(self.apply_edit_changes)
            self.auto_combobox[var] = widget
        elif isinstance(widget, QtGui.QSlider) or isinstance(widget, QtGui.QDial):
            widget.valueChanged.connect(self.apply_edit_changes)
            self.auto_slider[var] = widget
        elif isinstance(widget, QtGui.QLineEdit):
            widget.editingFinished.connect(self.apply_edit_changes)
            self.auto_line_edit[var] = widget
        elif isinstance(widget, QtGui.QCheckBox):
            widget.clicked.connect(self.apply_edit_changes)
            self.auto_checkbox[var] = widget
        else:
            raise Exception(u"Cannot auto-add widget of type %s" % widget)

    def clean_cond(self, cond, default=u"always"):

        """
		desc:
			Cleans a conditional statement. May raise a dialog box if problems
			are encountered.

		arguments:
			cond:
				desc:	A conditional statement.
				type:	str

		keywords:
			default:
				desc:	A default conditional statement, which is used for empty
						and invalid statements.
				type:	str

		returns:
			cond:
				desc:	A clean conditional statement.
				type:	str
		"""

        cond = self.syntax.sanitize(cond)
        if cond.strip() == u"":
            cond = default
        try:
            self.syntax.compile_cond(cond)
        except osexception as e:
            self.experiment.notify(u'Failed to compile conditional statement "%s": %s' % (cond, e))
            return default
        return cond

    def children(self):

        """
		returns:
			desc:	A list of children, including grand children, and so on.
			type:	list
		"""

        return []

    def is_child_item(self, item_name):

        """
		desc:
			Checks if an item is somewhere downstream from the current item
			in the experimental hierarchy.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		returns:
			desc:	True if the current item is offspring of the item, False
					otherwise.
			type:	bool
		"""

        return False

    def insert_child_item(self, item_name, index=0):

        """
		desc:
			Inserts a child item, if applicable to the item type.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		keywords:
			index:
				desc:	The index of the child item.
				type:	int
		"""

        pass

    def remove_child_item(self, item_name, index=0):

        """
		desc:
			Removes a child item, if applicable to the item type.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		keywords:
			index:
				desc:	The index of the child item, if applicable. A negative
						value indicates all instances.
				type:	int
		"""

        pass
Exemple #25
0
class qtitem(QtCore.QObject):
    """Base class for the GUI controls of other items"""

    initial_view = u'controls'

    def __init__(self):
        """Constructor"""

        QtCore.QObject.__init__(self)
        # The auto-widgets are stored in name -> (var, widget) dictionaries
        self.auto_line_edit = {}
        self.auto_combobox = {}
        self.auto_spinbox = {}
        self.auto_slider = {}
        self.auto_editor = {}
        self.auto_checkbox = {}
        self.init_edit_widget()
        self.lock = False
        self.maximized = False
        debug.msg(u'created %s' % self.name)

    @property
    def main_window(self):
        return self.experiment.main_window

    @property
    def theme(self):
        return self.experiment.main_window.theme

    @property
    def tabwidget(self):
        return self.experiment.main_window.tabwidget

    def open_tab(self, select_in_tree=True):
        """
		desc:
			Opens the tab if it wasn't yet open, and switches to it.
		"""

        self.tabwidget.add(self.widget(), self.item_icon(), self.name)
        if select_in_tree:
            self.experiment.main_window.ui.itemtree.select_item(self.name)

    def close_tab(self):
        """
		desc:
			Closes the tab if it was open.
		"""

        self.tabwidget.remove(self.widget())

    def set_focus(self):
        """
		desc:
			Gives focus to the most important widget.
		"""

        if hasattr(self, u'focus_widget') and self.focus_widget != None:
            self.focus_widget.setFocus()

    def set_focus_widget(self, widget, override=False):
        """
		desc:
			Sets the widget that receives focus when the tab is opened.

		arguments:
			widget:
				desc:	The widget to receive focus or `None` to reset.
				type:	[QWidget, NoneType]

		keywords:
			override:
				desc:	Indicates whether the focus widget should be changed if
						there already is a focus widget.
				type:	bool
		"""

        if override or not hasattr(
                self, u'focus_widget') or self.focus_widget == None:
            self.focus_widget = widget

    def update_item_icon(self):
        """
		desc:
			Updates the item icon.
		"""

        self.tabwidget.set_icon(self.widget(), self.item_icon())
        self.experiment.items.set_icon(self.name, self.item_icon())
        self.header_item_icon.setPixmap(
            self.theme.qpixmap(self.item_icon(), 32))

    def item_icon(self):
        """
		returns:
			desc:	The name of the item icon.
			type:	unicode
		"""

        return self.item_type

    def show_tab(self):
        """
		desc:
			Is called when the tab becomes visible, and updated the contents.
		"""

        self.update_script()
        self.edit_widget()
        self.set_focus()

    def widget(self):
        """
		returns:
			desc:	The widget that is added to the tabwidget.
			type:	QWidget
		"""

        return self.container_widget

    def init_edit_widget(self, stretch=True):
        """
		desc:
			Builds the UI.

		keywords:
			stretch:
				desc:	Indicates whether a vertical stretch should be added to
						the bottom of the controls. This is necessary if the
						controls don't expand.
				type:	bool
		"""

        # Header widget
        self.header = header_widget.header_widget(self)
        self.user_hint_widget = user_hint_widget.user_hint_widget(
            self.experiment.main_window, self)
        self.header_hbox = QtGui.QHBoxLayout()
        self.header_item_icon = self.experiment.label_image(self.item_icon())
        self.header_hbox.addWidget(self.header_item_icon)
        self.header_hbox.addWidget(self.header)
        self.header_hbox.setContentsMargins(0, 5, 0, 10)

        # Maximize button
        self.button_toggle_maximize = QtGui.QPushButton(
            self.theme.qicon(u'view-fullscreen'), u'')
        self.button_toggle_maximize.setToolTip(_(u'Toggle pop-out'))
        self.button_toggle_maximize.setIconSize(QtCore.QSize(16, 16))
        self.button_toggle_maximize.clicked.connect(self.toggle_maximize)
        self.header_hbox.addWidget(self.button_toggle_maximize)
        # View button
        self.button_view = item_view_button(self)
        self.header_hbox.addWidget(self.button_view)
        # Help button
        self.button_help = QtGui.QPushButton(self.experiment.icon(u"help"),
                                             u"")
        self.button_help.setToolTip(
            _(u"Tell me more about the %s item") % self.item_type)
        self.button_help.setIconSize(QtCore.QSize(16, 16))
        self.button_help.clicked.connect(self.open_help_tab)
        self.header_hbox.addWidget(self.button_help)

        self.header_widget = QtGui.QWidget()
        self.header_widget.setLayout(self.header_hbox)

        # The edit_grid is the layout that contains the actual controls for the
        # items.
        self.edit_grid = QtGui.QGridLayout()
        self.edit_grid.setColumnStretch(2, 2)
        self.edit_grid_widget = QtGui.QWidget()
        self.edit_grid.setMargin(0)
        self.edit_grid_widget.setLayout(self.edit_grid)

        # The edit_vbox contains the edit_grid and the header widget
        self.edit_vbox = QtGui.QVBoxLayout()
        self.edit_vbox.setMargin(5)
        self.edit_vbox.addWidget(self.user_hint_widget)
        self.edit_vbox.addWidget(self.edit_grid_widget)
        if stretch:
            self.edit_vbox.addStretch()
        self._edit_widget = QtGui.QWidget()
        self._edit_widget.setWindowIcon(self.experiment.icon(self.item_type))
        self._edit_widget.setLayout(self.edit_vbox)

        # The _script_widget contains the script editor
        from QProgEdit import QTabManager
        self._script_widget = QTabManager(
            handlerButtonText=_(u'Apply and close'), cfg=cfg)
        self._script_widget.focusLost.connect(self.apply_script_changes)
        self._script_widget.handlerButtonClicked.connect(
            self.set_view_controls)
        self._script_widget.addTab(u'Script').setLang(u'OpenSesame')

        # The container_widget is the top-level widget that is actually inserted
        # into the tab widget.
        self.splitter = qtitem_splitter(self)
        if self.initial_view == u'controls':
            self.set_view_controls()
        elif self.initial_view == u'script':
            self.set_view_script()
        elif self.initial_view == u'split':
            self.set_view_split()
        else:
            debug.msg(u'Invalid initial_view: %s' % self.initial_view,
                      reason=u'warning')
            self.set_view_controls()
        self.splitter.splitterMoved.connect(self.splitter_moved)
        self.container_vbox = QtGui.QVBoxLayout()
        self.container_vbox.addWidget(self.header_widget)
        self.container_vbox.addWidget(self.splitter)
        self.container_widget = QtGui.QWidget()
        self.container_widget.setLayout(self.container_vbox)
        self.container_widget.on_activate = self.show_tab
        self.container_widget.__item__ = self.name

    def splitter_moved(self, pos, index):
        """
		desc:
			Is called when the splitter handle is manually moved.

		arguments:
			pos:
				desc:	The splitter-handle position.
				type:	int
			index:
				desc:	The index of the splitter handle. Since there is only
						one handle, this is always 0.
				type:	int
		"""

        sizes = self.splitter.sizes()
        self.edit_size = sizes[0]
        self.script_size = sizes[1]
        if self.script_size == 0:
            self.button_view.set_view_icon(u'controls')
        elif self.edit_size == 0:
            self.button_view.set_view_icon(u'script')
        else:
            self.button_view.set_view_icon(u'split')

    def set_view_controls(self):
        """
		desc:
			Puts the splitter in control view.
		"""

        self.splitter.setSizes([self.splitter.width(), 0])
        self.button_view.set_view_icon(u'controls')

    def set_view_script(self):
        """
		desc:
			Puts the splitter in script view.
		"""

        self.splitter.setSizes([0, self.splitter.width()])
        self.button_view.set_view_icon(u'script')

    def set_view_split(self):
        """
		desc:
			Puts the splitter in split view.
		"""

        self.splitter.setSizes(
            [self.splitter.width() / 2,
             self.splitter.width() / 2])
        self.button_view.set_view_icon(u'split')

    def update(self):
        """
		desc:
			Updates both the script and the controls.
		"""

        self.update_script()
        self.edit_widget()

    def update_script(self):
        """
		desc:
			Regenerates the script and updates the script widget.
		"""

        # Normally, the script starts with a 'define' line and is indented by
        # a tab. We want to undo this, and present only unindented content.
        import textwrap
        script = self.to_string()
        script = script[script.find(u'\t'):]
        script = textwrap.dedent(script)
        self._script_widget.setText(script)

    def edit_widget(self, *deprecated, **_deprecated):
        """
		desc:
			This function updates the controls based on the item state.
		"""

        debug.msg()
        self.auto_edit_widget()
        self.header.refresh()

    def apply_edit_changes(self, *deprecated, **_deprecated):
        """
		desc:
			Applies changes to the graphical controls.
		"""

        debug.msg()
        self.auto_apply_edit_changes()
        self.update_script()
        self.main_window.set_unsaved(True)
        return True

    def apply_script_changes(self, *deprecated, **_deprecated):
        """
		desc:
			Applies changes to the script, by re-parsing the item from string.
		"""

        debug.msg()
        old_script = self.to_string()
        new_script = self._script_widget.text()
        try:
            self.from_string(new_script)
        except osexception as e:
            self.experiment.notify(e.html())
            self.main_window.print_debug_window(e)
            self.from_string(old_script)
        self.edit_widget()
        self.main_window.set_unsaved(True)

    def rename(self, from_name, to_name):
        """
		desc:
			Handles renaming of an item (not necesarrily the current item).

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

        if self.name != from_name:
            return
        self.name = to_name
        self.container_widget.__item__ = self.name
        self.header.set_name(to_name)
        index = self.tabwidget.indexOf(self.widget())
        if index != None:
            self.tabwidget.setTabText(index, to_name)

    def open_help_tab(self):
        """
		desc:
			Opens a help tab.
		"""

        self.experiment.main_window.ui.tabwidget.open_help(self.item_type)

    def toggle_maximize(self):
        """
		desc:
			Toggles edit-widget maximization.
		"""

        if not self.maximized:
            # Always ignore close events. This is necessary, because otherwise
            # the pop-out widget can be closed without re-enabling the main
            # window.
            self.container_widget.closeEvent = lambda e: e.ignore()
            self.container_widget.setParent(None)
            self.container_widget.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint|\
             QtCore.Qt.WindowMaximizeButtonHint|\
             QtCore.Qt.CustomizeWindowHint)
            self.container_widget.showMaximized()
            self.container_widget.show()
            self.button_toggle_maximize.setIcon(
                self.theme.qicon(u'view-restore'))
        else:
            self.container_widget.setParent(self.main_window)
            self.open_tab()
            self.button_toggle_maximize.setIcon(
                self.theme.qicon(u'view-fullscreen'))
        self.maximized = not self.maximized
        self.user_hint_widget.disable(self.maximized)
        self.button_help.setDisabled(self.maximized)
        self.main_window.setDisabled(self.maximized)

    def delete(self, item_name, item_parent=None, index=None):
        """
		Delete an item (not necessarily the current one)

		Arguments:
		item_name -- the name of the item to be deleted

		Keywords arguments:
		item_parent -- the parent item (default=None)
		index -- the index of the item in the parent (default=None)
		"""

        pass

    def build_item_tree(self,
                        toplevel=None,
                        items=[],
                        max_depth=-1,
                        extra_info=None):
        """
		Construct an item tree

		Keyword arguments:
		toplevel -- the toplevel widget (default = None)
		items -- a list of items that have been added, to prevent recursion
				 (default=[])
		"""

        widget = tree_item_item(self, extra_info=extra_info)
        items.append(self.name)
        if toplevel != None:
            toplevel.addChild(widget)
        return widget

    def parents(self):
        """
		Creates a list of all the items	that the current sequences is connected
		to upstream

		Returns:
		A list of item names
		"""

        l = [self.name]
        for item in self.experiment.items:
            if self.experiment.items[item].is_child_item(self.name):
                l.append(item)
        return l

    def variable_vars(self, exclude=[]):
        """
		Determines if one of the variables of the current item is defined in
		terms of another variable

		Keywords arguments:
		exclude -- a list of variables that should not be checked

		Returns:
		True if there are variably defined variables, False otherwise
		"""

        for var in self.variables:
            if var not in exclude:
                val = self.variables[var]
                if self.experiment.varref(val):
                    return True
        return False

    def get_ready(self):
        """
		This function should be overridden to do any last-minute stuff that
		and item should do before an experiment is actually run, such as
		applying pending script changes.

		Returns:
		True if some action has been taken, False if nothing was done
		"""

        return False

    def auto_edit_widget(self):
        """Update the GUI controls based on the auto-widgets"""

        debug.msg()
        for var, edit in self.auto_line_edit.iteritems():
            if self.has(var):
                edit.setText(self.unistr(self.get(var, _eval=False)))
            else:
                edit.setText(u'')

        for var, combobox in self.auto_combobox.iteritems():
            val = self.get_check(var, _eval=False, default=u'')
            i = combobox.findText(self.unistr(val))
            # Set the combobox to the select item
            if i >= 0:
                combobox.setDisabled(False)
                combobox.setCurrentIndex(i)
            # If no value was specified, set the combobox to a blank item
            elif val == u'':
                combobox.setDisabled(False)
                combobox.setCurrentIndex(-1)
            # If an unknown value has been specified, notify the user
            else:
                combobox.setDisabled(True)
                self.user_hint_widget.add(
                    _(u'"%s" is set to a '
                      u'variable or unknown value and can only be edited through '
                      u'the script.' % var))

        for var, spinbox in self.auto_spinbox.iteritems():
            if self.has(var):
                val = self.get(var, _eval=False)
                if type(val) in (float, int):
                    spinbox.setDisabled(False)
                    try:
                        spinbox.setValue(val)
                    except Exception as e:
                        self.experiment.notify(_( \
                         u"Failed to set control '%s': %s") % (var, e))
                else:
                    spinbox.setDisabled(True)
                    self.user_hint_widget.add(_( \
                     u'"%s" is defined using variables and can only be edited through the script.' \
                     % var))

        for var, slider in self.auto_slider.iteritems():
            if self.has(var):
                val = self.get(var, _eval=False)
                if type(val) in (float, int):
                    slider.setDisabled(False)
                    try:
                        slider.setValue(val)
                    except Exception as e:
                        self.experiment.notify(_( \
                         u"Failed to set control '%s': %s") % (var, e))
                else:
                    slider.setDisabled(True)
                    self.user_hint_widget.add(_( \
                     u'"%s" is defined using variables and can only be edited through the script.' \
                     % var))

        for var, checkbox in self.auto_checkbox.iteritems():
            if self.has(var):
                try:
                    checkbox.setChecked(self.get(var, _eval=False) == u"yes")
                except Exception as e:
                    self.experiment.notify(_(u"Failed to set control '%s': %s") \
                     % (var, e))

        for var, qprogedit in self.auto_editor.iteritems():
            if self.has(var):
                try:
                    qprogedit.setText(self.unistr(self.get(var, _eval=False)))
                except Exception as e:
                    self.experiment.notify(_(u"Failed to set control '%s': %s") \
                     % (var, e))

    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.

		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(
                    _(u'All non-alphanumeric characters except underscores have been stripped'
                      ))
            else:
                self.experiment.notify(
                    _(u'The following characters are not allowed and have been stripped: double-quote ("), backslash (\), and newline'
                      ))
        return sane

    def auto_apply_edit_changes(self, rebuild=True):
        """
		Apply the auto-widget controls

		Keyword arguments:
		rebuild -- deprecated (does nothing) (default=True)
		"""

        debug.msg()
        for var, edit in self.auto_line_edit.iteritems():
            if edit.isEnabled() and isinstance(var, basestring):
                val = unicode(edit.text()).strip()
                if val != u"":
                    self.set(var, val)

                # If the variable has no value, we assign a default value if it
                # has been specified, and unset it otherwise.
                elif hasattr(edit, u"default"):
                    self.set(var, edit.default)
                else:
                    self.unset(var)

        for var, combobox in self.auto_combobox.iteritems():
            if combobox.isEnabled() and isinstance(var, basestring):
                self.set(var, unicode(combobox.currentText()))

        for var, spinbox in self.auto_spinbox.iteritems():
            if spinbox.isEnabled() and isinstance(var, basestring):
                self.set(var, spinbox.value())

        for var, slider in self.auto_slider.iteritems():
            if slider.isEnabled() and isinstance(var, basestring):
                self.set(var, slider.value())

        for var, checkbox in self.auto_checkbox.iteritems():
            if checkbox.isEnabled() and isinstance(var, basestring):
                if checkbox.isChecked():
                    val = u"yes"
                else:
                    val = u"no"
                self.set(var, val)

        for var, qprogedit in self.auto_editor.iteritems():
            if isinstance(var, basestring):
                self.set(var, qprogedit.text())

        return True

    def auto_add_widget(self, widget, var=None):
        """
		Add a widget to the list of auto-widgets

		Arguments:
		widget -- a QWidget

		Keyword arguments:
		var -- the variable to be linked to the widget (default=None)
		"""

        # Use the object id as a fallback name
        if var == None:
            var = id(widget)
        debug.msg(var)
        self.set_focus_widget(widget)
        if isinstance(widget, QtGui.QSpinBox) or isinstance(
                widget, QtGui.QDoubleSpinBox):
            widget.editingFinished.connect(self.apply_edit_changes)
            self.auto_spinbox[var] = widget
        elif isinstance(widget, QtGui.QComboBox):
            widget.activated.connect(self.apply_edit_changes)
            self.auto_combobox[var] = widget
        elif isinstance(widget, QtGui.QSlider):
            widget.editingFinished.connect(self.apply_edit_changes)
            self.auto_slider[var] = widget
        elif isinstance(widget, QtGui.QLineEdit):
            widget.editingFinished.connect(self.apply_edit_changes)
            self.auto_line_edit[var] = widget
        elif isinstance(widget, QtGui.QCheckBox):
            widget.clicked.connect(self.apply_edit_changes)
            self.auto_checkbox[var] = widget
        else:
            raise Exception(u"Cannot auto-add widget of type %s" % widget)

    def clean_cond(self, cond, default=u'always'):
        """
		Cleans a conditional statement. May raise a dialog box if problems are
		encountered.

		Arguments:
		cond	--	A (potentially filthy) conditional statement.

		Keyword arguments:
		default	--	A default value to use for empty

		Returns:
		cond	--	A clean conditional statement conditional statements.
					(default=u'always')
		"""

        cond = self.unistr(cond)
        if not self.sanitize_check(cond):
            cond = self.sanitize(cond)
        if cond.strip() == u'':
            cond = default
        try:
            self.compile_cond(cond)
        except osexception as e:
            self.experiment.notify( \
             u'Failed to compile conditional statement "%s": %s' % (cond, e))
            return default
        return cond

    def children(self):
        """
		returns:
			desc:	A list of children, including grand children, and so on.
			type:	list
		"""

        return []

    def is_child_item(self, item_name):
        """
		desc:
			Checks if an item is somewhere downstream from the current item
			in the experimental hierarchy.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		returns:
			desc:	True if the current item is offspring of the item, False
					otherwise.
			type:	bool
		"""

        return False

    def insert_child_item(self, item_name, index=0):
        """
		desc:
			Inserts a child item, if applicable to the item type.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		keywords:
			index:
				desc:	The index of the child item.
				type:	int
		"""

        pass

    def remove_child_item(self, item_name, index=0):
        """
		desc:
			Removes a child item, if applicable to the item type.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		keywords:
			index:
				desc:	The index of the child item, if applicable. A negative
						value indicates all instances.
				type:	int
		"""

        pass
Exemple #26
0
class inline_script(libopensesame.inline_script.inline_script, qtitem.qtitem):
    """The inline_script GUI controls"""
    def __init__(self, name, experiment, string=None):
        """
		Constructor.

		Arguments:
		name 		--	The item name.
		experiment	--	The experiment object.

		Keywords arguments:
		string		--	A definition string. (default=None)
		"""

        libopensesame.inline_script.inline_script.__init__(self, name, \
         experiment, string)
        qtitem.qtitem.__init__(self)
        self.lock = False
        self._var_info = None

    def apply_edit_changes(self, **args):
        """
		Applies the controls.

		Keywords arguments:
		args	--	A dictionary to accept unused keyword arguments.
		"""

        qtitem.qtitem.apply_edit_changes(self, False)
        sp = self.qprogedit.text(index=0)
        sr = self.qprogedit.text(index=1)
        self.set(u'_prepare', sp)
        self.set(u'_run', sr)
        self.lock = True
        self._var_info = None
        self.experiment.main_window.refresh(self.name)
        self.lock = False

    def init_edit_widget(self):
        """Constructs the GUI controls."""

        from QProgEdit import QTabManager
        qtitem.qtitem.init_edit_widget(self, False)
        self.qprogedit = QTabManager(handler=self.apply_edit_changes, \
         defaultLang=u'Python', cfg=cfg, focusOutHandler= \
         self.apply_edit_changes)
        self.qprogedit.addTab(u'Prepare')
        self.qprogedit.addTab(u'Run')
        # Switch to the run phase, unless there is only content for the prepare
        # phase.
        if self._run == u'' and self._prepare != u'':
            self.qprogedit.setCurrentIndex(0)
        else:
            self.qprogedit.setCurrentIndex(1)
        self.edit_vbox.addWidget(self.qprogedit)

    def edit_widget(self):
        """
		Updates the GUI controls.

		Returns:
		The control QWidget.
		"""

        qtitem.qtitem.edit_widget(self, False)
        if not self.lock:
            self.qprogedit.setText(self._prepare, index=0)
            self.qprogedit.setText(self._run, index=1)
        return self._edit_widget

    def get_ready(self):
        """Applies pending script changes."""

        if self.qprogedit.isModified():
            debug.msg(u'applying pending script changes')
            self.apply_edit_changes(catch=False)
            return True
        return qtitem.qtitem.get_ready(self)
Exemple #27
0
class inline_script(inline_script_runtime, qtplugin):
    """The inline_script GUI controls"""

    description = _(u'Executes Python code')
    help_url = u'manual/python/about'

    def __init__(self, name, experiment, string=None):
        """See item."""

        inline_script_runtime.__init__(self, name, experiment, string)
        qtplugin.__init__(self)

    def apply_edit_changes(self):
        """See qtitem."""

        qtplugin.apply_edit_changes(self)
        sp = self.qprogedit.text(index=0)
        sr = self.qprogedit.text(index=1)
        self.var._prepare = sp
        self.var._run = sr
        self.update_item_icon()

    def set_focus(self):
        """
		desc:
			Allows the item to focus the most important widget.
		"""

        self.qprogedit.setFocus()

    def item_icon(self):
        """
		desc:
			Determines the icon, based on whether the scripts are syntactically
			correct.

		returns:
			desc:	An icon name.
			type:	unicode
		"""

        status = max(
            self.experiment.python_workspace.check_syntax(
                self.var.get(u'_prepare', _eval=False)),
            self.experiment.python_workspace.check_syntax(
                self.var.get(u'_run', _eval=False)))
        if status == 2:
            return u'os-inline_script-syntax-error'
        if status == 1:
            return u'os-inline_script-syntax-warning'
        return u'os-inline_script'

    def build_item_tree(self,
                        toplevel=None,
                        items=[],
                        max_depth=-1,
                        extra_info=None):
        """See qtitem."""

        widget = tree_inline_script_item(self,
                                         extra_info=extra_info,
                                         symbols=(max_depth < 0
                                                  or max_depth > 1))
        items.append(self.name)
        if toplevel is not None:
            toplevel.addChild(widget)
        return widget

    def init_edit_widget(self):
        """See qtitem."""

        from QProgEdit import QTabManager

        qtplugin.init_edit_widget(self, stretch=False)
        self.qprogedit = QTabManager(cfg=cfg, runButton=True)
        self.qprogedit.execute.connect(self.main_window.console.execute)
        self.qprogedit.handlerButtonClicked.connect(self.apply_edit_changes)
        self.qprogedit.focusLost.connect(self.apply_edit_changes)
        self.qprogedit.cursorRowChanged.connect(self.apply_edit_changes)
        self.qprogedit.addTab(_(u'Prepare')).setLang(u'Python')
        self.qprogedit.addTab(_(u'Run')).setLang(u'Python')
        # Switch to the run phase, unless there is only content for the prepare
        # phase.
        if self.var._run == u'' and self.var._prepare != u'':
            self.qprogedit.setCurrentIndex(0)
        else:
            self.qprogedit.setCurrentIndex(1)
        self.edit_vbox.addWidget(self.qprogedit)

    def edit_widget(self):
        """See qtitem."""

        qtplugin.edit_widget(self)
        _prepare = safe_decode(self.var._prepare)
        if _prepare != self.qprogedit.tab(0).text():
            self.qprogedit.tab(0).setText(_prepare)
        _run = safe_decode(self.var._run)
        if _run != self.qprogedit.tab(1).text():
            self.qprogedit.tab(1).setText(_run)

    def get_ready(self):
        """See qtitem."""

        if self.qprogedit.isAnyModified():
            debug.msg(u'applying pending script changes')
            self.apply_edit_changes()
            return True
        return qtplugin.get_ready(self)
Exemple #28
0
class qtitem(QtCore.QObject):
    """Base class for the GUI controls of other items"""
    def __init__(self):
        """Constructor"""

        QtCore.QObject.__init__(self)

        # The auto-widgets are stored in name -> (var, widget) dictionaries
        self.auto_line_edit = {}
        self.auto_combobox = {}
        self.auto_spinbox = {}
        self.auto_slider = {}
        self.auto_editor = {}
        self.auto_checkbox = {}
        self.sanity_criteria = {}

        self.init_edit_widget()
        self.init_script_widget()
        self.script_tab = None
        self.lock = False
        self.edit_mode = u'edit'

        debug.msg(u'created %s' % self.name)

    def open_help_tab(self, page=None):
        """Opens a help tab."""

        self.experiment.main_window.ui.tabwidget.open_help(self.item_type)

    def open_tab(self):
        """Opens the correct tab based on the current edit mode"""

        if self.edit_mode == u'edit':
            self.open_edit_tab()
        else:
            self.open_script_tab()

    def init_edit_widget(self, stretch=True):
        """Build the GUI controls"""

        # Header widget
        self.header = header_widget.header_widget(self)
        self.user_hint_widget = user_hint_widget.user_hint_widget( \
         self.experiment.main_window, self)
        self.header_hbox = QtGui.QHBoxLayout()
        self.header_hbox.addWidget(self.experiment.label_image(self.item_type))
        self.header_hbox.addWidget(self.header)
        self.header_hbox.addStretch()
        self.header_hbox.setContentsMargins(0, 5, 0, 10)

        # Edit script button
        button = QtGui.QPushButton(self.experiment.icon(u"script"), u"")
        button.setToolTip(_(u"Edit script"))
        button.setIconSize(QtCore.QSize(16, 16))
        QtCore.QObject.connect(button, QtCore.SIGNAL(u"clicked()"), \
         self.open_script_tab)
        self.header_hbox.addWidget(button)

        # Help button
        button = QtGui.QPushButton(self.experiment.icon(u"help"), u"")
        button.setToolTip(
            _(u"Tell me more about the %s item") % self.item_type)
        button.setIconSize(QtCore.QSize(16, 16))
        QtCore.QObject.connect(button, QtCore.SIGNAL(u"clicked()"), \
         self.open_help_tab)
        self.header_hbox.addWidget(button)

        self.header_widget = QtGui.QWidget()
        self.header_widget.setLayout(self.header_hbox)

        # The edit_grid is the layout that contains the actual controls for the
        # items.
        self.edit_grid = QtGui.QGridLayout()
        self.edit_grid.setColumnStretch(2, 2)
        self.edit_grid_widget = QtGui.QWidget()
        self.edit_grid.setMargin(0)
        self.edit_grid_widget.setLayout(self.edit_grid)

        # The edit_vbox contains the edit_grid and the header widget
        self.edit_vbox = QtGui.QVBoxLayout()
        self.edit_vbox.setMargin(5)
        self.edit_vbox.addWidget(self.header_widget)
        self.edit_vbox.addWidget(self.user_hint_widget)
        self.edit_vbox.addWidget(self.edit_grid_widget)
        if stretch:
            self.edit_vbox.addStretch()
        self._edit_widget = QtGui.QWidget()
        self._edit_widget.setLayout(self.edit_vbox)
        self._edit_widget.__edit_item__ = self.name

        return self._edit_widget

    def edit_widget(self, stretch=True):
        """
		A dummy edit widget, to be overridden.

		Keywords arguments:
		stretch		--	DEPRECATED (default=True)
		"""

        if not stretch:
            debug.msg(u"passing the stretch argument is deprecated", \
             reason=u"deprecation")
        self.user_hint_widget.clear()
        self.header.restore_name(False)
        self.header.refresh()
        self._edit_widget.__edit_item__ = self.name
        if not self.sanity_check():
            self.open_script_tab()
            return
        self.auto_edit_widget()
        self.user_hint_widget.refresh()
        return self._edit_widget

    def apply_name_change(self, rebuild=True):
        """
		Apply an item name change

		Keywords arguments:
		rebuild -- a deprecated argument (default=True)
		"""

        debug.msg()

        # Sanitize the name, check if it is new and valid, and if so, rename
        new_name = self.experiment.sanitize(self.header.edit_name.text(), \
         strict=True, allow_vars=False)
        if new_name.lower() != self.name.lower():
            valid = self.experiment.check_name(new_name)
            if valid != True:
                self.experiment.notify(valid)
                self.header.edit_name.setText(self.name)
                return
        old_name = self.name
        self.name = new_name
        self._edit_widget.__edit_item__ = new_name
        self.experiment.main_window.dispatch.event_name_change.emit(old_name, \
         new_name)

    def apply_edit_changes(self, rebuild=True):
        """
		Applies the GUI controls.

		Keywords arguments:
		rebuild	--	Specifies whether the overview area (item list) should be
					rebuild. (default=True)
		"""

        debug.msg(self.name)
        if self.experiment.main_window.lock_refresh:
            debug.msg(u"skipping, because refresh in progress")
            return False
        self.auto_apply_edit_changes()
        self.set(u"description", \
         self.experiment.sanitize(unicode( \
          self.header.edit_desc.text()).strip()))
        if self.description == u"":
            self.description = u"No description"
        self.header.label_desc.setText(self.description)
        self.experiment.main_window.dispatch.event_simple_change.emit(
            self.name)
        return True

    def close_edit_tab(self, index=None):
        """
		Closes the edit tab (does nothing by default).

		Keywords arguments:
		index	--	The index of the tab in the tab area. (default=None)
		"""

        pass

    def open_edit_tab(self, index=None, focus=True):
        """
		Opens the GUI control tab, or switches to the tab if it was already
		open.

		Keywords arguments:
		index	--	The index of the tab (if open). (default=None)
		focus	--	Indicates whether the tab should receive focus.
					(default=True)
		"""

        debug.msg(u"%s (#%s)" % (self.name, hash(self)))

        # Switch to edit mode and close the script tab if it was open
        self.edit_mode = u"edit"
        for i in range(self.experiment.ui.tabwidget.count()):
            w = self.experiment.ui.tabwidget.widget(i)
            if hasattr(w, u"__script_item__") and w.__script_item__ == \
             self.name:
                self.experiment.ui.tabwidget.removeTab(i)
                if index == None:
                    index = i
                break

        # Focus the edit tab, instead of reopening, if it was already open
        for i in range(self.experiment.ui.tabwidget.count()):
            w = self.experiment.ui.tabwidget.widget(i)
            if hasattr(w, u"__edit_item__") and w.__edit_item__ == self.name:
                index = i

        # Refresh the controls on the tab. In debug mode don't catch any errors
        if debug.enabled:
            widget = self.edit_widget()
        else:
            try:
                widget = self.edit_widget()
            except Exception as e:
                self.experiment.notify(_(u"%s (Edit the script to fix this)") \
                 % e)
                self.open_script_tab()
                return

        # Open the tab or focus the tab if it was already open
        if index == None:
            self.edit_tab_index = self.experiment.ui.tabwidget.addTab(widget, \
             self.experiment.icon(self.item_type), u"%s" % self.name)
        else:
            self.experiment.ui.tabwidget.insertTab(index, widget, \
             self.experiment.icon(self.item_type), u"%s" % self.name)
            self.edit_tab_index = index
        if focus:
            self.experiment.ui.tabwidget.setCurrentIndex(self.edit_tab_index)

    def apply_script_and_close(self):
        """Applies script changes and opens the edit tab"""

        self.apply_script_changes(mode=u'edit')
        self.experiment.main_window.select_item(self.name)

    def apply_script_changes(self, rebuild=True, catch=True, mode=u'script'):
        """
		Applies changes to the script, by regenerating the item from the script.

		Keywords arguments:
		rebuild	--	Specifies whether the overview area (item list) should be
					rebuild. (default=True)
		catch	--	Indicates whether exceptions should be caught and shown in a
					notification dialog (True) or not be caught (False).
					(default=True)
		mode	--	Indicates whether the item should re-open in edit or script
					mode. (default=u'script')
		"""

        debug.msg(self.name)
        script = self.script_qprogedit.text()
        # Create a new item and make it a clone of the current item
        item = self.experiment.main_window.add_item(self.item_type, False, \
         name=self.name, interactive=False)
        if catch:
            try:
                self.experiment.items[item].from_string(script)
            except Exception as e:
                self.experiment.notify(unicode(e))
                return
        else:
            self.experiment.items[item].from_string(script)
        self.experiment.items[item].name = self.name
        # Replace the current item
        self.experiment.items[self.name] = self.experiment.items[item]
        del self.experiment.items[item]
        self.experiment.items[self.name].init_script_widget()
        self.experiment.items[self.name].edit_mode = mode
        self.experiment.main_window.dispatch.event_script_change.emit(
            self.name)
        # The logic here is pretty complex, and is more-or-less a hack until the
        # event handling code has been improved. Basically, if we want to apply
        # the script and stay in script mode, we have to re-open the script tab,
        # because the entire item is re-generated. This new tab has to be
        # inserted in place of (i.e. with the same index as) the old tab, which
        # has to be removed. We always refocus the tab, but if the tab doesn't
        # actually have focus, we refocus the original tab. This is necessary
        # to avoid repainting artifacts.
        #
        # See also this issue:
        # - <https://github.com/smathot/OpenSesame/issues/219>
        if mode == u'script':
            currentIndex = self.experiment.ui.tabwidget.currentIndex()
            for i in range(self.experiment.ui.tabwidget.count()):
                w = self.experiment.ui.tabwidget.widget(i)
                if hasattr(w, u'__script_item__') and w.__script_item__ == \
                 self.name:
                    if i == currentIndex:
                        focus = True
                    else:
                        focus = False
                    self.experiment.items[self.name].open_script_tab(index=i, \
                     focus=True)
                    self.experiment.ui.tabwidget.removeTab(i + 1)
                    if not focus:
                        self.experiment.ui.tabwidget.setCurrentIndex( \
                         currentIndex)
                    break

    def strip_script_line(self, s):
        """
		Strips unwanted characters from a line of script

		Arguments:
		s -- a line of script

		Returns:
		A stripped line of script
		"""

        if len(s) > 0 and s[0] == u"\t":
            return s[1:] + u"\n"
        return s + u"\n"

    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

    def script_widget(self):
        """
		Update the script tab

		Returns:
		The QWidget containing the script tab
		"""

        self.script_header.setText( \
         _(u"Editing script for <b>%s</b> - %s") % (self.name, \
         self.item_type))
        script = u""
        for s in self.to_string().split(u"\n")[1:]:
            script += self.strip_script_line(s)
        self.script_qprogedit.setText(script)
        self._script_widget.__script_item__ = self.name
        return self._script_widget

    def open_script_tab(self, index=None, focus=True):
        """
		Open/ show the script tab

		Keywords arguments:
		index -- the index of the tab (if it is already open) (default=None)
		focus -- indicates whether the tab should receive focus (default=True)
		"""

        debug.msg(u"%s (#%s)" % (self.name, hash(self)))
        self.edit_mode = u"script"

        # Close the edit tab
        for i in range(self.experiment.ui.tabwidget.count()):
            w = self.experiment.ui.tabwidget.widget(i)
            if hasattr(w, u"__edit_item__") and w.__edit_item__ == self.name:
                self.experiment.ui.tabwidget.removeTab(i)
                if index == None:
                    index = i
                break

        for i in range(self.experiment.ui.tabwidget.count()):
            w = self.experiment.ui.tabwidget.widget(i)
            if hasattr(w,
                       u"__script_item__") and w.__script_item__ == self.name:
                index = i
        if index == None:
            self.script_tab_index = self.experiment.ui.tabwidget.addTab( \
             self.script_widget(), self.experiment.icon(u"script"), u"%s" \
             % self.name)
        else:
            self.script_tab_index = index
            self.experiment.ui.tabwidget.insertTab(index, \
             self.script_widget(), self.experiment.icon(u"script"), u"%s" \
             % self.name)
        if focus:
            self.experiment.ui.tabwidget.setCurrentIndex(self.script_tab_index)

    def close_script_tab(self, index=None):
        """
		Close the script tab (does nothing by defaut)

		Keywords arguments:
		index -- the index of the tab in the tab area (default=None)
		"""

        pass

    def rename(self, from_name, to_name):
        """
		Handle the renaming of an item (not necesarrily the currnet item)

		Arguments:
		from_name -- the old item name
		to_name -- the new item name
		"""

        if self.name == from_name:
            self.name = to_name

    def delete(self, item_name, item_parent=None, index=None):
        """
		Delete an item (not necessarily the current one)

		Arguments:
		item_name -- the name of the item to be deleted

		Keywords arguments:
		item_parent -- the parent item (default=None)
		index -- the index of the item in the parent (default=None)
		"""

        pass

    def rename_var(self, item, from_name, to_name):
        """
		A notification that a variable has been renamed

		Arguments:
		item -- the item doing the renaming
		from_name -- the old variable name
		to_name -- the new variable name
		"""

        pass

    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

    def build_item_tree(self, toplevel=None, items=[]):
        """
		Construct an item tree

		Keyword arguments:
		toplevel -- the toplevel widget (default = None)
		items -- a list of items that have been added, to prevent recursion
				 (default=[])
		"""

        toplevel.addChild(self.item_tree_widget(toplevel))

    def is_offspring(self, item):
        """
		Checks if the item is offspring of the current item, in the sense that
		the current item is contained by the item

		Arguments:
		item -- the potential offspring

		Returns:
		True if the current item is offspring of the item, False otherwise
		"""

        return False

    def parents(self):
        """
		Creates a list of all the items	that the current sequences is connected
		to upstream

		Returns:
		A list of item names
		"""

        l = [self.name]
        for item in self.experiment.items:
            if self.experiment.items[item].is_offspring(self.name):
                l.append(item)
        return l

    def variable_vars(self, exclude=[]):
        """
		Determines if one of the variables of the current item is defined in
		terms of another variable

		Keywords arguments:
		exclude -- a list of variables that should not be checked

		Returns:
		True if there are variably defined variables, False otherwise
		"""

        for var in self.variables:
            if var not in exclude:
                val = self.variables[var]
                if isinstance(val, basestring) and u'[' in val:
                    return True
        return False

    def get_ready(self):
        """
		This function should be overridden to do any last-minute stuff that
		and item should do before an experiment is actually run, such as
		applying pending script changes.

		Returns:
		True if some action has been taken, False if nothing was done
		"""

        if self.script_qprogedit.isModified():
            debug.msg(u'applying pending script changes')
            self.apply_script_changes(catch=False)
            return True
        return False

    def auto_edit_widget(self):
        """Update the GUI controls based on the auto-widgets"""

        debug.msg()
        for var, edit in self.auto_line_edit.iteritems():
            edit.editingFinished.disconnect()
            if self.has(var):
                try:
                    edit.setText(self.unistr(self.get(var, _eval=False)))
                except Exception as e:
                    self.experiment.notify(_(u"Failed to set control '%s': %s") \
                     % (var, e))
            else:
                edit.setText(u"")
            edit.editingFinished.connect(self.apply_edit_changes)

        for var, combobox in self.auto_combobox.iteritems():
            combobox.currentIndexChanged.disconnect()
            if self.has(var):
                try:
                    combobox.setCurrentIndex(combobox.findText( \
                     self.unistr(self.get(var, _eval=False))))
                except Exception as e:
                    self.experiment.notify(_(u"Failed to set control '%s': %s") \
                     % (var, e))
            combobox.currentIndexChanged.connect(self.apply_edit_changes)

        for var, spinbox in self.auto_spinbox.iteritems():
            spinbox.editingFinished.disconnect()
            if self.has(var):
                val = self.get(var, _eval=False)
                if type(val) in (float, int):
                    try:
                        spinbox.setValue(val)
                    except Exception as e:
                        self.experiment.notify(_( \
                         u"Failed to set control '%s': %s") % (var, e))
                else:
                    spinbox.setDisabled(True)
                    self.user_hint_widget.add_user_hint(_( \
                     u'"%s" is defined using variables and can only be edited through the script.' \
                     % var))
            spinbox.editingFinished.connect(self.apply_edit_changes)

        for var, slider in self.auto_slider.iteritems():
            slider.valueChanged.disconnect()
            if self.has(var):
                val = self.get(var, _eval=False)
                if type(val) in (float, int):
                    try:
                        slider.setValue(val)
                    except Exception as e:
                        self.experiment.notify(_( \
                         u"Failed to set control '%s': %s") % (var, e))
                else:
                    slider.setDisabled(True)
                    self.user_hint_widget.add_user_hint(_( \
                     u'"%s" is defined using variables and can only be edited through the script.' \
                     % var))
            slider.valueChanged.connect(self.apply_edit_changes)

        for var, checkbox in self.auto_checkbox.iteritems():
            checkbox.toggled.disconnect()
            if self.has(var):
                try:
                    checkbox.setChecked(self.get(var, _eval=False) == u"yes")
                except Exception as e:
                    self.experiment.notify(_(u"Failed to set control '%s': %s") \
                     % (var, e))
            checkbox.toggled.connect(self.apply_edit_changes)

        for var, qprogedit in self.auto_editor.iteritems():
            if self.has(var):
                try:
                    qprogedit.setText(self.unistr(self.get(var, _eval=False)))
                except Exception as e:
                    self.experiment.notify(_(u"Failed to set control '%s': %s") \
                     % (var, e))

    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.

		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(
                    _(u'All non-alphanumeric characters except underscores have been stripped'
                      ))
            else:
                self.experiment.notify(
                    _(u'The following characters are not allowed and have been stripped: double-quote ("), backslash (\), and newline'
                      ))
        return sane

    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

    def auto_apply_edit_changes(self, rebuild=True):
        """
		Apply the auto-widget controls

		Keyword arguments:
		rebuild -- deprecated (does nothing) (default=True)
		"""

        debug.msg()
        for var, edit in self.auto_line_edit.iteritems():
            if edit.isEnabled() and isinstance(var, basestring):
                val = unicode(edit.text()).strip()
                if val != u"":
                    self.set(var, val)

                # If the variable has no value, we assign a default value if it
                # has been specified, and unset it otherwise.
                elif hasattr(edit, u"default"):
                    self.set(var, edit.default)
                else:
                    self.unset(var)

        for var, combobox in self.auto_combobox.iteritems():
            if combobox.isEnabled() and isinstance(var, basestring):
                self.set(var, unicode(combobox.currentText()))

        for var, spinbox in self.auto_spinbox.iteritems():
            if spinbox.isEnabled() and isinstance(var, basestring):
                self.set(var, spinbox.value())

        for var, slider in self.auto_slider.iteritems():
            if slider.isEnabled() and isinstance(var, basestring):
                self.set(var, slider.value())

        for var, checkbox in self.auto_checkbox.iteritems():
            if checkbox.isEnabled() and isinstance(var, basestring):
                if checkbox.isChecked():
                    val = u"yes"
                else:
                    val = u"no"
                self.set(var, val)

        for var, qprogedit in self.auto_editor.iteritems():
            if isinstance(var, basestring):
                self.set(var, qprogedit.text())

        return True

    def auto_add_widget(self, widget, var=None):
        """
		Add a widget to the list of auto-widgets

		Arguments:
		widget -- a QWidget

		Keyword arguments:
		var -- the variable to be linked to the widget (default=None)
		"""

        # Use the object id as a fallback name
        if var == None:
            var = id(widget)
        debug.msg(var)

        if isinstance(widget, QtGui.QSpinBox) or isinstance(widget, \
         QtGui.QDoubleSpinBox):
            widget.editingFinished.connect(self.apply_edit_changes)
            self.auto_spinbox[var] = widget

        elif isinstance(widget, QtGui.QComboBox):
            widget.currentIndexChanged.connect(self.apply_edit_changes)
            self.auto_combobox[var] = widget

        elif isinstance(widget, QtGui.QSlider):
            widget.editingFinished.connect(self.apply_edit_changes)
            self.auto_slider[var] = widget

        elif isinstance(widget, QtGui.QLineEdit):
            widget.editingFinished.connect(self.apply_edit_changes)
            self.auto_line_edit[var] = widget

        elif isinstance(widget, QtGui.QCheckBox):
            widget.toggled.connect(self.apply_edit_changes)
            self.auto_checkbox[var] = widget

        else:
            raise Exception(u"Cannot auto-add widget of type %s" % widget)

    def clean_cond(self, cond, default=u'always'):
        """
		Cleans a conditional statement. May raise a dialog box if problems are
		encountered.

		Arguments:
		cond	--	A (potentially filthy) conditional statement.

		Keyword arguments:
		default	--	A default value to use for empty

		Returns:
		cond	--	A clean conditional statement conditional statements.
					(default=u'always')
		"""

        cond = self.unistr(cond)
        if not self.sanitize_check(cond):
            cond = self.sanitize(cond)
        if cond.strip() == u'':
            cond = default
        try:
            self.compile_cond(cond)
        except osexception as e:
            self.experiment.notify( \
             u'Failed to compile conditional statement "%s": %s' % (cond, e))
        return cond
Exemple #29
0
    def init_edit_widget(self, stretch=True):

        """
		desc:
			Builds the UI.

		keywords:
			stretch:
				desc:	Indicates whether a vertical stretch should be added to
						the bottom of the controls. This is necessary if the
						controls don't expand.
				type:	bool
		"""

        # Header widget
        self.header = header_widget.header_widget(self)
        self.user_hint_widget = user_hint_widget.user_hint_widget(self.experiment.main_window, self)
        self.header_hbox = QtGui.QHBoxLayout()
        self.header_item_icon = self.theme.qlabel(self.item_icon())
        self.header_hbox.addWidget(self.header_item_icon)
        self.header_hbox.addWidget(self.header)
        self.header_hbox.setContentsMargins(0, 2, 0, 0)

        # Maximize button
        self.button_toggle_maximize = QtGui.QPushButton(self.theme.qicon(u"view-fullscreen"), u"")
        self.button_toggle_maximize.setToolTip(_(u"Toggle pop-out"))
        self.button_toggle_maximize.setIconSize(QtCore.QSize(16, 16))
        self.button_toggle_maximize.clicked.connect(self.toggle_maximize)
        self.header_hbox.addWidget(self.button_toggle_maximize)
        # View button
        self.button_view = item_view_button(self)
        self.header_hbox.addWidget(self.button_view)
        # Help button
        self.button_help = QtGui.QPushButton(self.theme.qicon(u"help"), u"")
        self.button_help.setToolTip(_(u"Tell me more about the %s item") % self.item_type)
        self.button_help.setIconSize(QtCore.QSize(16, 16))
        self.button_help.clicked.connect(self.open_help_tab)
        self.header_hbox.addWidget(self.button_help)

        self.header_widget = QtGui.QWidget()
        self.header_widget.setLayout(self.header_hbox)

        # The edit_grid is the layout that contains the actual controls for the
        # items.
        self.edit_grid = QtGui.QGridLayout()
        self.edit_grid.setColumnStretch(2, 2)
        self.edit_grid_widget = QtGui.QWidget()
        self.edit_grid_widget.setLayout(self.edit_grid)

        # The edit_vbox contains the edit_grid and the header widget
        self.edit_vbox = QtGui.QVBoxLayout()
        self.edit_vbox.addWidget(self.user_hint_widget)
        self.edit_vbox.addWidget(self.edit_grid_widget)
        self.edit_vbox.setContentsMargins(0, 0, 0, 0)
        self.edit_vbox.setSpacing(0)
        if stretch:
            self.edit_vbox.addStretch()
        self._edit_widget = QtGui.QWidget()
        self._edit_widget.setWindowIcon(self.theme.qicon(self.item_type))
        self._edit_widget.setLayout(self.edit_vbox)

        # The _script_widget contains the script editor
        from QProgEdit import QTabManager

        self._script_widget = QTabManager(handlerButtonText=_(u"Apply and close"), cfg=cfg)
        self._script_widget.focusLost.connect(self.apply_script_changes)
        self._script_widget.cursorRowChanged.connect(self.apply_script_changes)
        self._script_widget.handlerButtonClicked.connect(self.set_view_controls)
        self._script_widget.addTab(u"Script").setLang(u"OpenSesame")

        # The container_widget is the top-level widget that is actually inserted
        # into the tab widget.
        self.splitter = qtitem_splitter(self)
        if self.initial_view == u"controls":
            self.set_view_controls()
        elif self.initial_view == u"script":
            self.set_view_script()
        elif self.initial_view == u"split":
            self.set_view_split()
        else:
            debug.msg(u"Invalid initial_view: %s" % self.initial_view, reason=u"warning")
            self.set_view_controls()
        self.splitter.splitterMoved.connect(self.splitter_moved)
        self.container_vbox = QtGui.QVBoxLayout()
        self.container_vbox.setContentsMargins(4, 0, 4, 4)
        self.container_vbox.addWidget(self.header_widget)
        self.container_vbox.addWidget(self.splitter)
        self.container_widget = QtGui.QWidget()
        self.container_widget.setLayout(self.container_vbox)
        self.container_widget.on_activate = self.show_tab
        self.container_widget.__item__ = self.name
Exemple #30
0
class qtitem(QtCore.QObject):

	"""Base class for the GUI controls of other items"""

	initial_view = u'controls'

	def __init__(self):

		"""Constructor"""

		QtCore.QObject.__init__(self)
		# The auto-widgets are stored in name -> (var, widget) dictionaries
		self.auto_line_edit = {}
		self.auto_combobox = {}
		self.auto_spinbox = {}
		self.auto_slider = {}
		self.auto_editor = {}
		self.auto_checkbox = {}
		self.init_edit_widget()
		self.lock = False
		self.maximized = False
		debug.msg(u'created %s' % self.name)

	@property
	def main_window(self):
		return self.experiment.main_window

	@property
	def theme(self):
		return self.experiment.main_window.theme

	@property
	def tabwidget(self):
		return self.experiment.main_window.tabwidget

	def open_tab(self, select_in_tree=True):

		"""
		desc:
			Opens the tab if it wasn't yet open, and switches to it.
		"""

		self.tabwidget.add(self.widget(), self.item_icon(), self.name)
		if select_in_tree:
			self.experiment.main_window.ui.itemtree.select_item(self.name)

	def close_tab(self):

		"""
		desc:
			Closes the tab if it was open.
		"""

		self.tabwidget.remove(self.widget())

	def set_focus(self):

		"""
		desc:
			Gives focus to the most important widget.
		"""

		if hasattr(self, u'focus_widget') and self.focus_widget != None:
			self.focus_widget.setFocus()

	def set_focus_widget(self, widget, override=False):

		"""
		desc:
			Sets the widget that receives focus when the tab is opened.

		arguments:
			widget:
				desc:	The widget to receive focus or `None` to reset.
				type:	[QWidget, NoneType]

		keywords:
			override:
				desc:	Indicates whether the focus widget should be changed if
						there already is a focus widget.
				type:	bool
		"""

		if override or not hasattr(self, u'focus_widget') or self.focus_widget == None:
			self.focus_widget = widget

	def update_item_icon(self):

		"""
		desc:
			Updates the item icon.
		"""

		self.tabwidget.set_icon(self.widget(), self.item_icon())
		self.experiment.items.set_icon(self.name, self.item_icon())
		self.header_item_icon.setPixmap(
			self.theme.qpixmap(self.item_icon(), 32))

	def item_icon(self):

		"""
		returns:
			desc:	The name of the item icon.
			type:	unicode
		"""

		return self.item_type

	def show_tab(self):

		"""
		desc:
			Is called when the tab becomes visible, and updated the contents.
		"""

		self.update_script()
		self.edit_widget()
		self.set_focus()

	def widget(self):

		"""
		returns:
			desc:	The widget that is added to the tabwidget.
			type:	QWidget
		"""

		return self.container_widget

	def init_edit_widget(self, stretch=True):

		"""
		desc:
			Builds the UI.

		keywords:
			stretch:
				desc:	Indicates whether a vertical stretch should be added to
						the bottom of the controls. This is necessary if the
						controls don't expand.
				type:	bool
		"""

		# Header widget
		self.header = header_widget.header_widget(self)
		self.user_hint_widget = user_hint_widget.user_hint_widget(
			self.experiment.main_window, self)
		self.header_hbox = QtGui.QHBoxLayout()
		self.header_item_icon = self.experiment.label_image(self.item_icon())
		self.header_hbox.addWidget(self.header_item_icon)
		self.header_hbox.addWidget(self.header)
		self.header_hbox.setContentsMargins(0, 5, 0, 10)

		# Maximize button
		self.button_toggle_maximize = QtGui.QPushButton(
			self.theme.qicon(u'view-fullscreen'), u'')
		self.button_toggle_maximize.setToolTip(_(u'Toggle pop-out'))
		self.button_toggle_maximize.setIconSize(QtCore.QSize(16, 16))
		self.button_toggle_maximize.clicked.connect(self.toggle_maximize)
		self.header_hbox.addWidget(self.button_toggle_maximize)
		# View button
		self.button_view = item_view_button(self)
		self.header_hbox.addWidget(self.button_view)
		# Help button
		self.button_help = QtGui.QPushButton(self.experiment.icon(u"help"), u"")
		self.button_help.setToolTip(
			_(u"Tell me more about the %s item") % self.item_type)
		self.button_help.setIconSize(QtCore.QSize(16, 16))
		self.button_help.clicked.connect(self.open_help_tab)
		self.header_hbox.addWidget(self.button_help)

		self.header_widget = QtGui.QWidget()
		self.header_widget.setLayout(self.header_hbox)

		# The edit_grid is the layout that contains the actual controls for the
		# items.
		self.edit_grid = QtGui.QGridLayout()
		self.edit_grid.setColumnStretch(2, 2)
		self.edit_grid_widget = QtGui.QWidget()
		self.edit_grid.setMargin(0)
		self.edit_grid_widget.setLayout(self.edit_grid)

		# The edit_vbox contains the edit_grid and the header widget
		self.edit_vbox = QtGui.QVBoxLayout()
		self.edit_vbox.setMargin(5)
		self.edit_vbox.addWidget(self.user_hint_widget)
		self.edit_vbox.addWidget(self.edit_grid_widget)
		if stretch:
			self.edit_vbox.addStretch()
		self._edit_widget = QtGui.QWidget()
		self._edit_widget.setWindowIcon(self.experiment.icon(self.item_type))
		self._edit_widget.setLayout(self.edit_vbox)

		# The _script_widget contains the script editor
		from QProgEdit import QTabManager
		self._script_widget = QTabManager(
			handlerButtonText=_(u'Apply and close'), cfg=cfg)
		self._script_widget.focusLost.connect(self.apply_script_changes)
		self._script_widget.handlerButtonClicked.connect(self.set_view_controls)
		self._script_widget.addTab(u'Script').setLang(u'OpenSesame')

		# The container_widget is the top-level widget that is actually inserted
		# into the tab widget.
		self.splitter = qtitem_splitter(self)
		if self.initial_view == u'controls':
			self.set_view_controls()
		elif self.initial_view == u'script':
			self.set_view_script()
		elif self.initial_view == u'split':
			self.set_view_split()
		else:
			debug.msg(u'Invalid initial_view: %s' % self.initial_view,
				reason=u'warning')
			self.set_view_controls()
		self.splitter.splitterMoved.connect(self.splitter_moved)
		self.container_vbox = QtGui.QVBoxLayout()
		self.container_vbox.addWidget(self.header_widget)
		self.container_vbox.addWidget(self.splitter)
		self.container_widget = QtGui.QWidget()
		self.container_widget.setLayout(self.container_vbox)
		self.container_widget.on_activate = self.show_tab
		self.container_widget.__item__ = self.name

	def splitter_moved(self, pos, index):

		"""
		desc:
			Is called when the splitter handle is manually moved.

		arguments:
			pos:
				desc:	The splitter-handle position.
				type:	int
			index:
				desc:	The index of the splitter handle. Since there is only
						one handle, this is always 0.
				type:	int
		"""

		sizes = self.splitter.sizes()
		self.edit_size = sizes[0]
		self.script_size = sizes[1]
		if self.script_size == 0:
			self.button_view.set_view_icon(u'controls')
		elif self.edit_size == 0:
			self.button_view.set_view_icon(u'script')
		else:
			self.button_view.set_view_icon(u'split')

	def set_view_controls(self):

		"""
		desc:
			Puts the splitter in control view.
		"""

		self.splitter.setSizes([self.splitter.width(), 0])
		self.button_view.set_view_icon(u'controls')

	def set_view_script(self):

		"""
		desc:
			Puts the splitter in script view.
		"""

		self.splitter.setSizes([0, self.splitter.width()])
		self.button_view.set_view_icon(u'script')

	def set_view_split(self):

		"""
		desc:
			Puts the splitter in split view.
		"""

		self.splitter.setSizes([self.splitter.width()/2,
			self.splitter.width()/2])
		self.button_view.set_view_icon(u'split')

	def update(self):

		"""
		desc:
			Updates both the script and the controls.
		"""

		self.update_script()
		self.edit_widget()

	def update_script(self):

		"""
		desc:
			Regenerates the script and updates the script widget.
		"""

		# Normally, the script starts with a 'define' line and is indented by
		# a tab. We want to undo this, and present only unindented content.
		import textwrap
		script = self.to_string()
		script = script[script.find(u'\t'):]
		script = textwrap.dedent(script)
		self._script_widget.setText(script)

	def edit_widget(self, *deprecated, **_deprecated):

		"""
		desc:
			This function updates the controls based on the item state.
		"""

		debug.msg()
		self.auto_edit_widget()
		self.header.refresh()

	def apply_edit_changes(self, *deprecated, **_deprecated):

		"""
		desc:
			Applies changes to the graphical controls.
		"""

		debug.msg()
		self.auto_apply_edit_changes()
		self.update_script()
		self.main_window.set_unsaved(True)
		return True

	def apply_script_changes(self, *deprecated, **_deprecated):

		"""
		desc:
			Applies changes to the script, by re-parsing the item from string.
		"""

		debug.msg()
		old_script = self.to_string()
		new_script = self._script_widget.text()
		try:
			self.from_string(new_script)
		except osexception as e:
			self.experiment.notify(e.html())
			self.main_window.print_debug_window(e)
			self.from_string(old_script)
		self.edit_widget()
		self.main_window.set_unsaved(True)

	def rename(self, from_name, to_name):

		"""
		desc:
			Handles renaming of an item (not necesarrily the current item).

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

		if self.name != from_name:
			return
		self.name = to_name
		self.container_widget.__item__ = self.name
		self.header.set_name(to_name)
		index = self.tabwidget.indexOf(self.widget())
		if index != None:
			self.tabwidget.setTabText(index, to_name)

	def open_help_tab(self):

		"""
		desc:
			Opens a help tab.
		"""

		self.experiment.main_window.ui.tabwidget.open_help(self.item_type)

	def toggle_maximize(self):

		"""
		desc:
			Toggles edit-widget maximization.
		"""

		if not self.maximized:
			# Always ignore close events. This is necessary, because otherwise
			# the pop-out widget can be closed without re-enabling the main
			# window.
			self.container_widget.closeEvent = lambda e: e.ignore()
			self.container_widget.setParent(None)
			self.container_widget.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint|\
				QtCore.Qt.WindowMaximizeButtonHint|\
				QtCore.Qt.CustomizeWindowHint)
			self.container_widget.showMaximized()
			self.container_widget.show()
			self.button_toggle_maximize.setIcon(
				self.theme.qicon(u'view-restore'))
		else:
			self.container_widget.setParent(self.main_window)
			self.open_tab()
			self.button_toggle_maximize.setIcon(
				self.theme.qicon(u'view-fullscreen'))
		self.maximized = not self.maximized
		self.user_hint_widget.disable(self.maximized)
		self.button_help.setDisabled(self.maximized)
		self.main_window.setDisabled(self.maximized)

	def delete(self, item_name, item_parent=None, index=None):

		"""
		Delete an item (not necessarily the current one)

		Arguments:
		item_name -- the name of the item to be deleted

		Keywords arguments:
		item_parent -- the parent item (default=None)
		index -- the index of the item in the parent (default=None)
		"""

		pass

	def build_item_tree(self, toplevel=None, items=[], max_depth=-1,
		extra_info=None):

		"""
		Construct an item tree

		Keyword arguments:
		toplevel -- the toplevel widget (default = None)
		items -- a list of items that have been added, to prevent recursion
				 (default=[])
		"""

		widget = tree_item_item(self, extra_info=extra_info)
		items.append(self.name)
		if toplevel != None:
			toplevel.addChild(widget)
		return widget

	def parents(self):

		"""
		Creates a list of all the items	that the current sequences is connected
		to upstream

		Returns:
		A list of item names
		"""

		l = [self.name]
		for item in self.experiment.items:
			if self.experiment.items[item].is_child_item(self.name):
				l.append(item)
		return l

	def variable_vars(self, exclude=[]):

		"""
		Determines if one of the variables of the current item is defined in
		terms of another variable

		Keywords arguments:
		exclude -- a list of variables that should not be checked

		Returns:
		True if there are variably defined variables, False otherwise
		"""

		for var in self.variables:
			if var not in exclude:
				val = self.variables[var]
				if self.experiment.varref(val):
					return True
		return False

	def get_ready(self):

		"""
		This function should be overridden to do any last-minute stuff that
		and item should do before an experiment is actually run, such as
		applying pending script changes.

		Returns:
		True if some action has been taken, False if nothing was done
		"""

		return False

	def auto_edit_widget(self):

		"""Update the GUI controls based on the auto-widgets"""

		debug.msg()
		for var, edit in self.auto_line_edit.iteritems():
			if self.has(var):
				edit.setText(self.unistr(self.get(var, _eval=False)))
			else:
				edit.setText(u'')

		for var, combobox in self.auto_combobox.iteritems():
			val = self.get_check(var, _eval=False, default=u'')
			i = combobox.findText(self.unistr(val))
			# Set the combobox to the select item
			if i >= 0:
				combobox.setDisabled(False)
				combobox.setCurrentIndex(i)
			# If no value was specified, set the combobox to a blank item
			elif val == u'':
				combobox.setDisabled(False)
				combobox.setCurrentIndex(-1)
			# If an unknown value has been specified, notify the user
			else:
				combobox.setDisabled(True)
				self.user_hint_widget.add(_(u'"%s" is set to a '
					u'variable or unknown value and can only be edited through '
					u'the script.' % var))

		for var, spinbox in self.auto_spinbox.iteritems():
			if self.has(var):
				val = self.get(var, _eval=False)
				if type(val) in (float, int):
					spinbox.setDisabled(False)
					try:
						spinbox.setValue(val)
					except Exception as e:
						self.experiment.notify(_( \
							u"Failed to set control '%s': %s") % (var, e))
				else:
					spinbox.setDisabled(True)
					self.user_hint_widget.add(_( \
						u'"%s" is defined using variables and can only be edited through the script.' \
						% var))

		for var, slider in self.auto_slider.iteritems():
			if self.has(var):
				val = self.get(var, _eval=False)
				if type(val) in (float, int):
					slider.setDisabled(False)
					try:
						slider.setValue(val)
					except Exception as e:
						self.experiment.notify(_( \
							u"Failed to set control '%s': %s") % (var, e))
				else:
					slider.setDisabled(True)
					self.user_hint_widget.add(_( \
						u'"%s" is defined using variables and can only be edited through the script.' \
						% var))

		for var, checkbox in self.auto_checkbox.iteritems():
			if self.has(var):
				try:
					checkbox.setChecked(self.get(var, _eval=False) == u"yes")
				except Exception as e:
					self.experiment.notify(_(u"Failed to set control '%s': %s") \
						% (var, e))

		for var, qprogedit in self.auto_editor.iteritems():
			if self.has(var):
				try:
					qprogedit.setText(self.unistr(self.get(var, _eval=False)))
				except Exception as e:
					self.experiment.notify(_(u"Failed to set control '%s': %s") \
						% (var, e))

	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.

		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(
					_(u'All non-alphanumeric characters except underscores have been stripped'))
			else:
				self.experiment.notify(
					_(u'The following characters are not allowed and have been stripped: double-quote ("), backslash (\), and newline'))
		return sane

	def auto_apply_edit_changes(self, rebuild=True):

		"""
		Apply the auto-widget controls

		Keyword arguments:
		rebuild -- deprecated (does nothing) (default=True)
		"""

		debug.msg()
		for var, edit in self.auto_line_edit.iteritems():
			if edit.isEnabled() and isinstance(var, basestring):
				val = unicode(edit.text()).strip()
				if val != u"":
					self.set(var, val)

				# If the variable has no value, we assign a default value if it
				# has been specified, and unset it otherwise.
				elif hasattr(edit, u"default"):
					self.set(var, edit.default)
				else:
					self.unset(var)

		for var, combobox in self.auto_combobox.iteritems():
			if combobox.isEnabled() and isinstance(var, basestring):
				self.set(var, unicode(combobox.currentText()))

		for var, spinbox in self.auto_spinbox.iteritems():
			if spinbox.isEnabled() and isinstance(var, basestring):
				self.set(var, spinbox.value())

		for var, slider in self.auto_slider.iteritems():
			if slider.isEnabled() and isinstance(var, basestring):
				self.set(var, slider.value())

		for var, checkbox in self.auto_checkbox.iteritems():
			if checkbox.isEnabled() and isinstance(var, basestring):
				if checkbox.isChecked():
					val = u"yes"
				else:
					val = u"no"
				self.set(var, val)

		for var, qprogedit in self.auto_editor.iteritems():
			if isinstance(var, basestring):
				self.set(var, qprogedit.text())

		return True

	def auto_add_widget(self, widget, var=None):

		"""
		Add a widget to the list of auto-widgets

		Arguments:
		widget -- a QWidget

		Keyword arguments:
		var -- the variable to be linked to the widget (default=None)
		"""

		# Use the object id as a fallback name
		if var == None:
			var = id(widget)
		debug.msg(var)
		self.set_focus_widget(widget)
		if isinstance(widget, QtGui.QSpinBox) or isinstance(widget,
			QtGui.QDoubleSpinBox):
			widget.editingFinished.connect(self.apply_edit_changes)
			self.auto_spinbox[var] = widget
		elif isinstance(widget, QtGui.QComboBox):
			widget.activated.connect(self.apply_edit_changes)
			self.auto_combobox[var] = widget
		elif isinstance(widget, QtGui.QSlider):
			widget.editingFinished.connect(self.apply_edit_changes)
			self.auto_slider[var] = widget
		elif isinstance(widget, QtGui.QLineEdit):
			widget.editingFinished.connect(self.apply_edit_changes)
			self.auto_line_edit[var] = widget
		elif isinstance(widget, QtGui.QCheckBox):
			widget.clicked.connect(self.apply_edit_changes)
			self.auto_checkbox[var] = widget
		else:
			raise Exception(u"Cannot auto-add widget of type %s" % widget)

	def clean_cond(self, cond, default=u'always'):

		"""
		Cleans a conditional statement. May raise a dialog box if problems are
		encountered.

		Arguments:
		cond	--	A (potentially filthy) conditional statement.

		Keyword arguments:
		default	--	A default value to use for empty

		Returns:
		cond	--	A clean conditional statement conditional statements.
					(default=u'always')
		"""

		cond = self.unistr(cond)
		if not self.sanitize_check(cond):
			cond = self.sanitize(cond)
		if cond.strip() == u'':
			cond = default
		try:
			self.compile_cond(cond)
		except osexception as e:
			self.experiment.notify( \
				u'Failed to compile conditional statement "%s": %s' % (cond, e))
			return default
		return cond

	def children(self):

		"""
		returns:
			desc:	A list of children, including grand children, and so on.
			type:	list
		"""

		return []

	def is_child_item(self, item_name):

		"""
		desc:
			Checks if an item is somewhere downstream from the current item
			in the experimental hierarchy.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		returns:
			desc:	True if the current item is offspring of the item, False
					otherwise.
			type:	bool
		"""

		return False

	def insert_child_item(self, item_name, index=0):

		"""
		desc:
			Inserts a child item, if applicable to the item type.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		keywords:
			index:
				desc:	The index of the child item.
				type:	int
		"""

		pass

	def remove_child_item(self, item_name, index=0):

		"""
		desc:
			Removes a child item, if applicable to the item type.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		keywords:
			index:
				desc:	The index of the child item, if applicable. A negative
						value indicates all instances.
				type:	int
		"""

		pass
class inline_script(inline_script_runtime, qtplugin):

	"""The inline_script GUI controls"""

	def __init__(self, name, experiment, string=None):

		"""See item."""

		inline_script_runtime.__init__(self, name, experiment, string)
		qtplugin.__init__(self)

	def apply_edit_changes(self):

		"""See qtitem."""

		super(inline_script, self).apply_edit_changes(self)
		sp = self.qprogedit.text(index=0)
		sr = self.qprogedit.text(index=1)
		self.var._prepare = sp
		self.var._run = sr
		self.update_item_icon()

	def set_focus(self):

		"""
		desc:
			Allows the item to focus the most important widget.
		"""

		self.qprogedit.setFocus()

	def item_icon(self):

		"""
		desc:
			Determines the icon, based on whether the scripts are syntactically
			correct.

		returns:
			desc:	An icon name.
			type:	unicode
		"""

		status = max(
			self.experiment.python_workspace.check_syntax(
				self.var.get(u'_prepare', _eval=False)),
			self.experiment.python_workspace.check_syntax(
				self.var.get(u'_run', _eval=False)))
		if status == 2:
			return u'os-inline_script-syntax-error'
		if status == 1:
			return u'os-inline_script-syntax-warning'
		return u'os-inline_script'

	def build_item_tree(self, toplevel=None, items=[], max_depth=-1,
		extra_info=None):

		"""See qtitem."""

		widget = tree_inline_script_item(self, extra_info=extra_info,
			symbols=(max_depth < 0 or max_depth > 1))
		items.append(self.name)
		if toplevel is not None:
			toplevel.addChild(widget)
		return widget

	def init_edit_widget(self):

		"""See qtitem."""

		from QProgEdit import QTabManager
		super(inline_script, self).init_edit_widget(stretch=False)
		self.qprogedit = QTabManager(cfg=cfg, runButton=True)
		self.qprogedit.execute.connect(self.main_window.console.execute)
		self.qprogedit.handlerButtonClicked.connect(self.apply_edit_changes)
		self.qprogedit.focusLost.connect(self.apply_edit_changes)
		self.qprogedit.cursorRowChanged.connect(self.apply_edit_changes)
		self.qprogedit.addTab(u'Prepare').setLang(u'Python')
		self.qprogedit.addTab(u'Run').setLang(u'Python')
		# Switch to the run phase, unless there is only content for the prepare
		# phase.
		if self.var._run == u'' and self.var._prepare != u'':
			self.qprogedit.setCurrentIndex(0)
		else:
			self.qprogedit.setCurrentIndex(1)
		self.edit_vbox.addWidget(self.qprogedit)

	def edit_widget(self):

		"""See qtitem."""

		super(inline_script, self).edit_widget()
		self.qprogedit.tab(0).setText(safe_decode(self.var._prepare))
		self.qprogedit.tab(1).setText(safe_decode(self.var._run))

	def get_ready(self):

		"""See qtitem."""

		if self.qprogedit.isAnyModified():
			debug.msg(u'applying pending script changes')
			self.apply_edit_changes()
			return True
		return super(inline_script, self).get_ready()
Exemple #32
0
    def init_edit_widget(self, stretch=True):
        """
		desc:
			Builds the UI.

		keywords:
			stretch:
				desc:	Indicates whether a vertical stretch should be added to
						the bottom of the controls. This is necessary if the
						controls don't expand.
				type:	bool
		"""

        # Header widget
        self.header = header_widget.header_widget(self)
        self.header_hbox = QtWidgets.QHBoxLayout()
        self.header_item_icon = self.theme.qlabel(self.item_icon())
        self.header_hbox.addWidget(self.header_item_icon)
        self.header_hbox.addWidget(self.header)
        self.header_hbox.setContentsMargins(0, 0, 0, 0)
        self.header_hbox.setSpacing(12)

        # Maximize button
        self.button_toggle_maximize = QtWidgets.QPushButton(
            self.theme.qicon(u'view-fullscreen'), u'')
        self.button_toggle_maximize.setToolTip(_(u'Toggle pop-out'))
        self.button_toggle_maximize.setIconSize(QtCore.QSize(16, 16))
        self.button_toggle_maximize.clicked.connect(self.toggle_maximize)
        self.header_hbox.addWidget(self.button_toggle_maximize)
        # View button
        self.button_view = item_view_button(self)
        self.header_hbox.addWidget(self.button_view)
        # Help button
        self.button_help = QtWidgets.QPushButton(self.theme.qicon(u"help"),
                                                 u"")
        self.button_help.setToolTip(
            _(u"Tell me more about the %s item") % self.item_type)
        self.button_help.setIconSize(QtCore.QSize(16, 16))
        self.button_help.clicked.connect(self.open_help_tab)
        self.header_hbox.addWidget(self.button_help)

        self.header_widget = QtWidgets.QWidget()
        self.header_widget.setLayout(self.header_hbox)

        # The edit_grid is the layout that contains the actual controls for the
        # items.
        self.edit_grid = QtWidgets.QFormLayout()
        if self.label_align == u'right':
            self.edit_grid.setLabelAlignment(QtCore.Qt.AlignRight)
        self.edit_grid.setFieldGrowthPolicy(
            QtWidgets.QFormLayout.FieldsStayAtSizeHint)
        self.edit_grid.setContentsMargins(0, 0, 0, 0)
        self.edit_grid.setVerticalSpacing(6)
        self.edit_grid.setHorizontalSpacing(12)
        self.edit_grid_widget = QtWidgets.QWidget()
        self.edit_grid_widget.setLayout(self.edit_grid)

        # The edit_vbox contains the edit_grid and the header widget
        self.edit_vbox = QtWidgets.QVBoxLayout()
        self.edit_vbox.addWidget(self.edit_grid_widget)
        self.edit_vbox.setContentsMargins(0, 0, 0, 0)
        self.edit_vbox.setSpacing(12)
        if stretch:
            self.edit_vbox.addStretch()
        self._edit_widget = QtWidgets.QWidget()
        self._edit_widget.setWindowIcon(self.theme.qicon(self.item_type))
        self._edit_widget.setLayout(self.edit_vbox)

        # The _script_widget contains the script editor
        from QProgEdit import QTabManager
        self._script_widget = QTabManager(
            handlerButtonText=_(u'Apply and close'), cfg=cfg)
        self._script_widget.focusLost.connect(self.apply_script_changes)
        self._script_widget.cursorRowChanged.connect(self.apply_script_changes)
        self._script_widget.handlerButtonClicked.connect(
            self.apply_script_changes_and_switch_view)
        self._script_widget.addTab(u'Script').setLang(u'OpenSesame')

        # The container_widget is the top-level widget that is actually inserted
        # into the tab widget.
        self.splitter = qtitem_splitter(self)
        if self.initial_view == u'controls':
            self.set_view_controls()
        elif self.initial_view == u'script':
            self.set_view_script()
        elif self.initial_view == u'split':
            self.set_view_split()
        else:
            debug.msg(u'Invalid initial_view: %s' % self.initial_view,
                      reason=u'warning')
            self.set_view_controls()
        self.splitter.splitterMoved.connect(self.splitter_moved)
        self.container_vbox = QtWidgets.QVBoxLayout()
        self.container_vbox.setContentsMargins(12, 12, 12, 12)
        self.container_vbox.setSpacing(18)
        self.container_vbox.addWidget(self.header_widget)
        self.container_vbox.addWidget(self.splitter)
        self.container_widget = QtWidgets.QWidget()
        self.container_widget.setLayout(self.container_vbox)
        self.container_widget.on_activate = self.show_tab
        self.container_widget.__item__ = self.name
Exemple #33
0
class qtitem(base_qtobject):
    """Base class for the GUI controls of other items"""

    initial_view = u'controls'
    label_align = u'right'
    help_url = None
    lazy_init = False

    def __init__(self):
        """Constructor"""

        # The auto-widgets are stored in name -> (var, widget) dictionaries
        self.auto_line_edit = {}
        self.auto_combobox = {}
        self.auto_spinbox = {}
        self.auto_slider = {}
        self.auto_editor = {}
        self.auto_checkbox = {}
        # Lazy initialization means that the control widgets are initialized
        # only when they are shown for the first time. This dramatically speeds
        # up opening of files. However, with some items this is not currently
        # possible, because their widgets are also referred when they are not
        # shown. Currently this means the inline_script.
        if self.lazy_init:
            self.container_widget = None
        else:
            self.init_edit_widget()
        self.lock = False
        self.maximized = False
        self.set_validator()
        debug.msg(u'created %s' % self.name)

    @property
    def main_window(self):
        return self.experiment.main_window

    @property
    def overview_area(self):
        return self.experiment.main_window.ui.itemtree

    @property
    def theme(self):
        return self.experiment.main_window.theme

    @property
    def tabwidget(self):
        return self.experiment.main_window.tabwidget

    @property
    def console(self):
        return self.experiment.main_window.console

    @property
    def extension_manager(self):
        return self.experiment.main_window.extension_manager

    @property
    def default_description(self):
        return _(u'Default description')

    def open_tab(self, select_in_tree=True):
        """
		desc:
			Opens the tab if it wasn't yet open, and switches to it.
		"""

        unsaved = self.main_window.unsaved_changes
        self.tabwidget.add(self.widget(), self.item_icon(), self.name)
        self.main_window.set_unsaved(unsaved)
        if select_in_tree:
            self.experiment.main_window.ui.itemtree.select_item(self.name)

    def close_tab(self):
        """
		desc:
			Closes the tab if it was open.
		"""

        self.tabwidget.remove(self.widget())

    def set_focus(self):
        """
		desc:
			Gives focus to the most important widget.
		"""

        if hasattr(self, u'focus_widget') and self.focus_widget is not None:
            self.focus_widget.setFocus()

    def set_focus_widget(self, widget, override=False):
        """
		desc:
			Sets the widget that receives focus when the tab is opened.

		arguments:
			widget:
				desc:	The widget to receive focus or `None` to reset.
				type:	[QWidget, NoneType]

		keywords:
			override:
				desc:	Indicates whether the focus widget should be changed if
						there already is a focus widget.
				type:	bool
		"""

        if override or not hasattr(
                self, u'focus_widget') or self.focus_widget is None:
            self.focus_widget = widget

    def update_item_icon(self):
        """
		desc:
			Updates the item icon.
		"""

        self.tabwidget.set_icon(self.widget(), self.item_icon())
        self.experiment.items.set_icon(self.name, self.item_icon())
        self.header_item_icon.setPixmap(
            self.theme.qpixmap(self.item_icon(), 32))

    def item_icon(self):
        """
		returns:
			desc:	The name of the item icon.
			type:	unicode
		"""

        return self.item_type

    def show_tab(self):
        """
		desc:
			Is called when the tab becomes visible, and updated the contents.
		"""

        self.update_script()
        self.edit_widget()
        self.main_window.ui.itemtree.select_item(self.name, open_tab=False)

    @requires_init
    def widget(self):
        """
		returns:
			desc:	The widget that is added to the tabwidget.
			type:	QWidget
		"""

        return self.container_widget

    def init_edit_widget(self, stretch=True):
        """
		desc:
			Builds the UI.

		keywords:
			stretch:
				desc:	Indicates whether a vertical stretch should be added to
						the bottom of the controls. This is necessary if the
						controls don't expand.
				type:	bool
		"""

        # Header widget
        self.header = header_widget.header_widget(self)
        self.header_hbox = QtWidgets.QHBoxLayout()
        self.header_item_icon = self.theme.qlabel(self.item_icon())
        self.header_hbox.addWidget(self.header_item_icon)
        self.header_hbox.addWidget(self.header)
        self.header_hbox.setContentsMargins(0, 0, 0, 0)
        self.header_hbox.setSpacing(12)

        # Maximize button
        self.button_toggle_maximize = QtWidgets.QPushButton(
            self.theme.qicon(u'view-fullscreen'), u'')
        self.button_toggle_maximize.setToolTip(_(u'Toggle pop-out'))
        self.button_toggle_maximize.setIconSize(QtCore.QSize(16, 16))
        self.button_toggle_maximize.clicked.connect(self.toggle_maximize)
        self.header_hbox.addWidget(self.button_toggle_maximize)
        # View button
        self.button_view = item_view_button(self)
        self.header_hbox.addWidget(self.button_view)
        # Help button
        self.button_help = QtWidgets.QPushButton(self.theme.qicon(u"help"),
                                                 u"")
        self.button_help.setToolTip(
            _(u"Tell me more about the %s item") % self.item_type)
        self.button_help.setIconSize(QtCore.QSize(16, 16))
        self.button_help.clicked.connect(self.open_help_tab)
        self.header_hbox.addWidget(self.button_help)

        self.header_widget = QtWidgets.QWidget()
        self.header_widget.setLayout(self.header_hbox)

        # The edit_grid is the layout that contains the actual controls for the
        # items.
        self.edit_grid = QtWidgets.QFormLayout()
        if self.label_align == u'right':
            self.edit_grid.setLabelAlignment(QtCore.Qt.AlignRight)
        self.edit_grid.setFieldGrowthPolicy(
            QtWidgets.QFormLayout.FieldsStayAtSizeHint)
        self.edit_grid.setContentsMargins(0, 0, 0, 0)
        self.edit_grid.setVerticalSpacing(6)
        self.edit_grid.setHorizontalSpacing(12)
        self.edit_grid_widget = QtWidgets.QWidget()
        self.edit_grid_widget.setLayout(self.edit_grid)

        # The edit_vbox contains the edit_grid and the header widget
        self.edit_vbox = QtWidgets.QVBoxLayout()
        self.edit_vbox.addWidget(self.edit_grid_widget)
        self.edit_vbox.setContentsMargins(0, 0, 0, 0)
        self.edit_vbox.setSpacing(12)
        if stretch:
            self.edit_vbox.addStretch()
        self._edit_widget = QtWidgets.QWidget()
        self._edit_widget.setWindowIcon(self.theme.qicon(self.item_type))
        self._edit_widget.setLayout(self.edit_vbox)

        # The _script_widget contains the script editor
        from QProgEdit import QTabManager
        self._script_widget = QTabManager(
            handlerButtonText=_(u'Apply and close'), cfg=cfg)
        self._script_widget.focusLost.connect(self.apply_script_changes)
        self._script_widget.cursorRowChanged.connect(self.apply_script_changes)
        self._script_widget.handlerButtonClicked.connect(
            self.apply_script_changes_and_switch_view)
        self._script_widget.addTab(u'Script').setLang(u'OpenSesame')

        # The container_widget is the top-level widget that is actually inserted
        # into the tab widget.
        self.splitter = qtitem_splitter(self)
        if self.initial_view == u'controls':
            self.set_view_controls()
        elif self.initial_view == u'script':
            self.set_view_script()
        elif self.initial_view == u'split':
            self.set_view_split()
        else:
            debug.msg(u'Invalid initial_view: %s' % self.initial_view,
                      reason=u'warning')
            self.set_view_controls()
        self.splitter.splitterMoved.connect(self.splitter_moved)
        self.container_vbox = QtWidgets.QVBoxLayout()
        self.container_vbox.setContentsMargins(12, 12, 12, 12)
        self.container_vbox.setSpacing(18)
        self.container_vbox.addWidget(self.header_widget)
        self.container_vbox.addWidget(self.splitter)
        self.container_widget = QtWidgets.QWidget()
        self.container_widget.setLayout(self.container_vbox)
        self.container_widget.on_activate = self.show_tab
        self.container_widget.__item__ = self.name

    def splitter_moved(self, pos, index):
        """
		desc:
			Is called when the splitter handle is manually moved.

		arguments:
			pos:
				desc:	The splitter-handle position.
				type:	int
			index:
				desc:	The index of the splitter handle. Since there is only
						one handle, this is always 0.
				type:	int
		"""

        sizes = self.splitter.sizes()
        self.edit_size = sizes[0]
        self.script_size = sizes[1]
        if self.script_size == 0:
            self.button_view.set_view_icon(u'controls')
        elif self.edit_size == 0:
            self.button_view.set_view_icon(u'script')
        else:
            self.button_view.set_view_icon(u'split')

    def set_view_controls(self):
        """
		desc:
			Puts the splitter in control view.
		"""

        self.splitter.setSizes([self.splitter.width(), 0])
        self.button_view.set_view_icon(u'controls')

    def set_view_script(self):
        """
		desc:
			Puts the splitter in script view.
		"""

        self.splitter.setSizes([0, self.splitter.width()])
        self.button_view.set_view_icon(u'script')

    def set_view_split(self):
        """
		desc:
			Puts the splitter in split view.
		"""

        self.splitter.setSizes(
            [self.splitter.width() / 2,
             self.splitter.width() / 2])
        self.button_view.set_view_icon(u'split')

    def update(self):
        """
		desc:
			Updates both the script and the controls.
		"""

        # Items are updated when their tab is shown, so we don't need to update
        # them if they aren't shown.
        if self.tabwidget.current_item() != self.name:
            return
        self.update_script()
        self.edit_widget()

    def update_script(self):
        """
		desc:
			Regenerates the script and updates the script widget.
		"""

        # Normally, the script starts with a 'define' line and is indented by
        # a tab. We want to undo this, and present only unindented content.
        import textwrap
        script = self.to_string()
        script = script[script.find(u'\t'):]
        script = textwrap.dedent(script)
        if self._script_widget.text() != script:
            self.main_window.set_unsaved()
            self._script_widget.setText(script)
            self.extension_manager.fire(u'change_item', name=self.name)

    def edit_widget(self, *deprecated, **_deprecated):
        """
		desc:
			This function updates the controls based on the item state.
		"""

        self.auto_edit_widget()
        self.header.refresh()

    def apply_edit_changes(self, *deprecated, **_deprecated):
        """
		desc:
			Applies changes to the graphical controls.
		"""

        self.auto_apply_edit_changes()
        self.update_script()
        return True

    def apply_script_changes(self, *deprecated, **_deprecated):
        """
		desc:
			Applies changes to the script.
		"""

        if not self.validate_script():
            return
        new_script = self._script_widget.text()
        old_script = self.to_string()
        self.from_string(new_script)
        if old_script != new_script:
            self.main_window.set_unsaved()
        self.edit_widget()

    def apply_script_changes_and_switch_view(self, *deprecated, **_deprecated):
        """
		desc:
			Applies changes to the script if possible. If so, switches to the
			controls view.
		"""

        if self.validate_script():
            self.set_view_controls()

    def validate_script(self):
        """
		desc:
			Checks whether the script is syntactically valid. If not, the
			offending line is highlighted if QProgEdit suppors setInvalid().

		returns:
			type:	bool
		"""

        script = self._script_widget.text()
        # First create a dummy item to see if the string can be parsed.
        try:
            self.validator(self.name, self.experiment, script)
            return True
        except Exception as e:
            # If an error occurs, we first parse the first line, then the first
            # and second, and so on, until we find the error.
            l = script.split(u'\n')
            for line_nr, line in enumerate(l):
                test_script = u'\n'.join(l[:line_nr])
                try:
                    self.validator(self.name, self.experiment, test_script)
                except Exception as e_:
                    if not isinstance(e_, osexception):
                        e_ = osexception(e_)
                    if hasattr(self._script_widget, u'setInvalid'):
                        self._script_widget.setInvalid(line_nr, e_.markdown())
                    break
            self.console.write(e)
            return False

    def set_validator(self):
        """
		desc:
			Sets the validator class, that is, the class that is used to parse
			scripts to see if they are syntactically correct. Currently, we use
			the class that defines from_string().
		"""

        import inspect
        meth = self.from_string
        for cls in inspect.getmro(self.__class__):
            if meth.__name__ in cls.__dict__:
                break
        debug.msg(u'validator: %s' % cls)
        self.validator = cls

    def rename(self, from_name, to_name):
        """
		desc:
			Handles renaming of an item (not necesarrily the current item).

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

        if self.name != from_name:
            return
        self.name = to_name
        self.container_widget.__item__ = self.name
        self.header.set_name(to_name)
        index = self.tabwidget.indexOf(self.widget())
        if index is not None:
            self.tabwidget.setTabText(index, to_name)

    def open_help_tab(self):
        """
		desc:
			Opens a help tab.
		"""

        if self.help_url is None:
            self.tabwidget.open_help(self.item_type)
        else:
            self.tabwidget.open_osdoc(self.help_url)

    def toggle_maximize(self):
        """
		desc:
			Toggles edit-widget maximization.
		"""

        if not self.maximized:
            # Always ignore close events. This is necessary, because otherwise
            # the pop-out widget can be closed without re-enabling the main
            # window.
            self.main_window.block_close_event = True
            self.container_widget.closeEvent = lambda e: e.ignore()
            self.container_widget.setParent(None)
            self.container_widget.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint|\
             QtCore.Qt.WindowMaximizeButtonHint|\
             QtCore.Qt.CustomizeWindowHint)
            self.container_widget.showMaximized()
            self.container_widget.show()
            self.button_toggle_maximize.setIcon(
                self.theme.qicon(u'view-restore'))
        else:
            self.main_window.block_close_event = False
            self.container_widget.setParent(self.main_window)
            self.open_tab()
            self.button_toggle_maximize.setIcon(
                self.theme.qicon(u'view-fullscreen'))
        self.maximized = not self.maximized
        self.button_help.setDisabled(self.maximized)
        self.main_window.setDisabled(self.maximized)

    def delete(self, item_name, item_parent=None, index=None):
        """
		desc:
			Is called when an item is deleted (not necessarily the current one).

		arguments:
			item_name:
			 	desc:	The name of the item to be deleted.
				type:	str

		keywords:
			item_parent:
			 	desc:	The name of the parent item.
				type:	str
			index:
			 	desc:	The index of the item in the parent.
				type:	int
		"""

        pass

    def build_item_tree(self,
                        toplevel=None,
                        items=[],
                        max_depth=-1,
                        extra_info=None):
        """
		desc:
			Constructs an item tree.

		keywords:
			toplevel:
			 	desc:	The toplevel widget.
				type:	tree_base_item
			items:
				desc:	A list of item names that have already been added, to
				 		prevent recursion.
				tyep:	list

		returns:
			type:	tree_item_item
		"""

        widget = tree_item_item(self, extra_info=extra_info)
        items.append(self.name)
        if toplevel is not None:
            toplevel.addChild(widget)
        return widget

    def parents(self):
        """
		returns:
			desc:	A list of all parents (names) of the current item.
			type:	list
		"""

        l = [self.name]
        for item in self.experiment.items:
            if self.experiment.items[item].is_child_item(self.name):
                l.append(item)
        return l

    def get_ready(self):
        """
		desc:
			This function should be overridden to do any last-minute stuff that
			and item should do before an experiment is actually run, such as
			applying pending script changes.

		returns:
			desc:	True if some action has been taken, False if nothing was
					done.
			type:	bool
		"""

        return False

    def auto_edit_widget(self):
        """
		desc:
			Updates the GUI controls based on the auto-widgets.
		"""

        for var, edit in self.auto_line_edit.items():
            if isinstance(var, int):
                continue
            if var in self.var:
                val = safe_decode(self.var.get(var, _eval=False))
            else:
                val = u''
            if val != edit.text():
                edit.setText(val)

        for var, combobox in self.auto_combobox.items():
            if isinstance(var, int):
                continue
            val = self.var.get(var, _eval=False, default=u'')
            i = combobox.findText(safe_decode(val))
            # Set the combobox to the select item
            if i >= 0:
                if i != combobox.currentIndex() or not combobox.isEnabled():
                    combobox.setDisabled(False)
                    combobox.setCurrentIndex(i)
            # If no value was specified, set the combobox to a blank item
            elif val == u'':
                if combobox.currentIndex() >= 0 or not combobox.isEnabled():
                    combobox.setDisabled(False)
                    combobox.setCurrentIndex(-1)
            # If an unknown value has been specified, notify the user
            else:
                if combobox.isEnabled():
                    combobox.setDisabled(True)
                    self.extension_manager.fire(
                        u'notify',
                        message=_(
                            u'"%s" is set to a variable or '
                            u'unknown value and can only be edited through '
                            u'the script.') % var,
                        category=u'info')

        for var, spinbox in list(self.auto_spinbox.items()) \
         + list(self.auto_slider.items()):
            if isinstance(var, int):
                continue
            if var in self.var:
                val = self.var.get(var, _eval=False)
                if type(val) in (float, int):
                    if spinbox.value() == val and spinbox.isEnabled():
                        continue
                    spinbox.setDisabled(False)
                    try:
                        spinbox.setValue(val)
                    except Exception as e:
                        self.experiment.notify(
                            _(u"Failed to set control '%s': %s") % (var, e))
                else:
                    if not spinbox.isEnabled():
                        continue
                    spinbox.setDisabled(True)
                    self.extension_manager.fire(
                        u'notify',
                        message=_(
                            u'"%s" is defined using '
                            'variables and can only be edited through the '
                            'script.') % var,
                        category=u'info')

        for var, checkbox in self.auto_checkbox.items():
            if isinstance(var, int):
                continue
            if var in self.var:
                val = self.var.get(var, _eval=False)
                if val in [u'yes', u'no']:
                    checkbox.setDisabled(False)
                    checked = val == u'yes'
                    if checked != checkbox.isChecked():
                        checkbox.setChecked(checked)
                else:
                    checkbox.setDisabled(True)
                    self.extension_manager.fire(
                        u'notify',
                        message=
                        _(u'"%s" is defined using '
                          u'variables or has an invalid value, and can only be '
                          u'edited through the script.') % var,
                        category=u'info')

        for var, qprogedit in self.auto_editor.items():
            if isinstance(var, int):
                continue
            if var in self.var:
                val = safe_decode(self.var.get(var, _eval=False))
                if val != qprogedit.text():
                    qprogedit.setText(val)

    def auto_apply_edit_changes(self, rebuild=True):
        """
		desc:
			Applies the auto-widget controls.

		keywords:
			rebuild:	Deprecated (does nothing).
		"""

        for var, edit in self.auto_line_edit.items():
            if isinstance(var, int):
                continue
            if edit.isEnabled() and isinstance(var, basestring):
                val = str(edit.text()).strip()
                if val != u'':
                    self.var.set(var, val)
                    continue
                # If no text was entered, we use a default if available ...
                if hasattr(edit, u'default'):
                    self.var.set(var, edit.default)
                    continue
                # ... or unset the variable if no default is available.
                self.var.unset(var)

        for var, combobox in self.auto_combobox.items():
            if isinstance(var, int):
                continue
            if combobox.isEnabled() and isinstance(var, basestring):
                self.var.set(var, str(combobox.currentText()))

        for var, spinbox in self.auto_spinbox.items():
            if isinstance(var, int):
                continue
            if spinbox.isEnabled() and isinstance(var, basestring):
                self.var.set(var, spinbox.value())

        for var, slider in self.auto_slider.items():
            if isinstance(var, int):
                continue
            if slider.isEnabled() and isinstance(var, basestring):
                self.var.set(var, slider.value())

        for var, checkbox in self.auto_checkbox.items():
            if isinstance(var, int):
                continue
            if checkbox.isEnabled() and isinstance(var, basestring):
                if checkbox.isChecked():
                    val = u"yes"
                else:
                    val = u"no"
                self.var.set(var, val)

        for var, qprogedit in self.auto_editor.items():
            if isinstance(var, int):
                continue
            if isinstance(var, basestring):
                self.var.set(var, qprogedit.text())

        return True

    def auto_add_widget(self, widget, var=None, apply_func=None):
        """
		desc:
			Adds a widget to the list of auto-widgets.

		arguments:
			widget:
			 	type:	QWidget

		keywords:
			var:	The name of the experimental variable to be linked to the
					widget, or None to use an automatically chosen name.
			type:	[str, NoneType]
		"""

        # Use the object id as a fallback name
        if var is None:
            var = id(widget)
        if apply_func is None:
            apply_func = self.apply_edit_changes
        debug.msg(var)
        self.set_focus_widget(widget)
        if isinstance(widget, QtWidgets.QSpinBox) or isinstance(
                widget, QtWidgets.QDoubleSpinBox):
            widget.editingFinished.connect(apply_func)
            self.auto_spinbox[var] = widget
        elif isinstance(widget, QtWidgets.QComboBox):
            widget.activated.connect(apply_func)
            self.auto_combobox[var] = widget
        elif isinstance(widget, QtWidgets.QSlider) \
         or isinstance(widget, QtWidgets.QDial):
            widget.valueChanged.connect(apply_func)
            self.auto_slider[var] = widget
        elif isinstance(widget, QtWidgets.QLineEdit) or isinstance(
                widget, pool_select):
            widget.editingFinished.connect(apply_func)
            self.auto_line_edit[var] = widget
        elif isinstance(widget, QtWidgets.QCheckBox):
            widget.clicked.connect(apply_func)
            self.auto_checkbox[var] = widget
        else:
            raise Exception(u"Cannot auto-add widget of type %s" % widget)

    def children(self):
        """
		returns:
			desc:	A list of children, including grand children, and so on.
			type:	list
		"""

        return []

    def is_child_item(self, item_name):
        """
		desc:
			Checks if an item is somewhere downstream from the current item
			in the experimental hierarchy.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		returns:
			desc:	True if the current item is offspring of the item, False
					otherwise.
			type:	bool
		"""

        return False

    def insert_child_item(self, item_name, index=0):
        """
		desc:
			Inserts a child item, if applicable to the item type.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		keywords:
			index:
				desc:	The index of the child item.
				type:	int
		"""

        pass

    def remove_child_item(self, item_name, index=0):
        """
		desc:
			Removes a child item, if applicable to the item type.

		arguments:
			item_name:
				desc:	The name of the child item.
				type:	unicode

		keywords:
			index:
				desc:	The index of the child item, if applicable. A negative
						value indicates all instances.
				type:	int
		"""

        pass
Exemple #34
0
    def init_edit_widget(self, stretch=True):
        """
		desc:
			Builds the UI.

		keywords:
			stretch:
				desc:	Indicates whether a vertical stretch should be added to
						the bottom of the controls. This is necessary if the
						controls don't expand.
				type:	bool
		"""

        # Header widget
        self.header = header_widget.header_widget(self)
        self.user_hint_widget = user_hint_widget.user_hint_widget(
            self.experiment.main_window, self)
        self.header_hbox = QtGui.QHBoxLayout()
        self.header_item_icon = self.experiment.label_image(self.item_icon())
        self.header_hbox.addWidget(self.header_item_icon)
        self.header_hbox.addWidget(self.header)
        self.header_hbox.setContentsMargins(0, 5, 0, 10)

        # Maximize button
        self.button_toggle_maximize = QtGui.QPushButton(
            self.theme.qicon(u'view-fullscreen'), u'')
        self.button_toggle_maximize.setToolTip(_(u'Toggle pop-out'))
        self.button_toggle_maximize.setIconSize(QtCore.QSize(16, 16))
        self.button_toggle_maximize.clicked.connect(self.toggle_maximize)
        self.header_hbox.addWidget(self.button_toggle_maximize)
        # View button
        self.button_view = item_view_button(self)
        self.header_hbox.addWidget(self.button_view)
        # Help button
        self.button_help = QtGui.QPushButton(self.experiment.icon(u"help"),
                                             u"")
        self.button_help.setToolTip(
            _(u"Tell me more about the %s item") % self.item_type)
        self.button_help.setIconSize(QtCore.QSize(16, 16))
        self.button_help.clicked.connect(self.open_help_tab)
        self.header_hbox.addWidget(self.button_help)

        self.header_widget = QtGui.QWidget()
        self.header_widget.setLayout(self.header_hbox)

        # The edit_grid is the layout that contains the actual controls for the
        # items.
        self.edit_grid = QtGui.QGridLayout()
        self.edit_grid.setColumnStretch(2, 2)
        self.edit_grid_widget = QtGui.QWidget()
        self.edit_grid.setMargin(0)
        self.edit_grid_widget.setLayout(self.edit_grid)

        # The edit_vbox contains the edit_grid and the header widget
        self.edit_vbox = QtGui.QVBoxLayout()
        self.edit_vbox.setMargin(5)
        self.edit_vbox.addWidget(self.user_hint_widget)
        self.edit_vbox.addWidget(self.edit_grid_widget)
        if stretch:
            self.edit_vbox.addStretch()
        self._edit_widget = QtGui.QWidget()
        self._edit_widget.setWindowIcon(self.experiment.icon(self.item_type))
        self._edit_widget.setLayout(self.edit_vbox)

        # The _script_widget contains the script editor
        from QProgEdit import QTabManager
        self._script_widget = QTabManager(
            handlerButtonText=_(u'Apply and close'), cfg=cfg)
        self._script_widget.focusLost.connect(self.apply_script_changes)
        self._script_widget.handlerButtonClicked.connect(
            self.set_view_controls)
        self._script_widget.addTab(u'Script').setLang(u'OpenSesame')

        # The container_widget is the top-level widget that is actually inserted
        # into the tab widget.
        self.splitter = qtitem_splitter(self)
        if self.initial_view == u'controls':
            self.set_view_controls()
        elif self.initial_view == u'script':
            self.set_view_script()
        elif self.initial_view == u'split':
            self.set_view_split()
        else:
            debug.msg(u'Invalid initial_view: %s' % self.initial_view,
                      reason=u'warning')
            self.set_view_controls()
        self.splitter.splitterMoved.connect(self.splitter_moved)
        self.container_vbox = QtGui.QVBoxLayout()
        self.container_vbox.addWidget(self.header_widget)
        self.container_vbox.addWidget(self.splitter)
        self.container_widget = QtGui.QWidget()
        self.container_widget.setLayout(self.container_vbox)
        self.container_widget.on_activate = self.show_tab