Exemple #1
2
class Widget(QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        self.uiTeachingWidget = teachingwidget.Widget()
        self.uiExperimentWidget = experimentwidget.Widget()

        self.uiStackedWidget = QStackedWidget()
        self.uiStackedWidget.addWidget(self.uiTeachingWidget)
        self.uiStackedWidget.addWidget(self.uiExperimentWidget)
        self.uiButtonNext = QPushButton('Next')
        self.uiButtonBack = QPushButton('Back')
        self.uiButtonBack.setEnabled(False)
        hboxLayout = QHBoxLayout()
        hboxLayout.addStretch(1)
        hboxLayout.addWidget(self.uiButtonBack)
        hboxLayout.addWidget(self.uiButtonNext)

        vboxLayout = QVBoxLayout()
        vboxLayout.addWidget(self.uiStackedWidget)
        vboxLayout.addLayout(hboxLayout)
        self.setLayout(vboxLayout)
        self.setWindowTitle('Teaching one linear neuron (example 3)')
        self.connect(self.uiButtonNext, SIGNAL('clicked()'), self.nextWidget)
        self.connect(self.uiButtonBack, SIGNAL('clicked()'), self.backWidget)

    def nextWidget(self):
        self.uiButtonNext.setEnabled(False)
        self.uiButtonBack.setEnabled(True)
        self.uiStackedWidget.setCurrentIndex(1)

    def backWidget(self):
        self.uiButtonNext.setEnabled(True)
        self.uiButtonBack.setEnabled(False)
        self.uiStackedWidget.setCurrentIndex(0)
Exemple #2
1
class RangeSelection(QWidget):
    """
    Allow to select a date, a build id, a release number or an arbitrary
    changeset.
    """
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        layout = QHBoxLayout(self)
        self._create_widgets()
        layout.addWidget(self.stacked)
        layout.addWidget(self.select_combo)
        self.setLayout(layout)

    def _create_widgets(self):
        self.stacked = QStackedWidget()
        self.datew = QDateEdit()
        self.datew.setDisplayFormat("yyyy-MM-dd")
        self.stacked.addWidget(self.datew)
        self.buildidw = QLineEdit()
        self.stacked.addWidget(self.buildidw)
        self.releasew = QComboBox()
        self.releasew.addItems([str(k) for k in sorted(releases())])
        self.stacked.addWidget(self.releasew)
        self.revw = QLineEdit()
        self.stacked.addWidget(self.revw)

        self.select_combo = QComboBox()
        self.select_combo.addItems(['date', 'buildid', 'release', 'changeset'])
        self.select_combo.activated.connect(self.stacked.setCurrentIndex)

    def get_value(self):
        currentw = self.stacked.currentWidget()
        if currentw == self.datew:
            return self.datew.date().toPyDate()
        elif currentw == self.buildidw:
            buildid = unicode(self.buildidw.text())
            try:
                return parse_date(buildid)
            except DateFormatError:
                raise DateFormatError(buildid, "Not a valid build id: `%s`")
        elif currentw == self.releasew:
            return parse_date(
                date_of_release(str(self.releasew.currentText())))
        elif currentw == self.revw:
            return unicode(self.revw.text())
Exemple #3
0
class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        # codeCompletionBlock start
        from PyQt4.QtGui import QComboBox, QToolButton, QStackedWidget

        self.accountSelection = QComboBox()
        self.accountButton = QToolButton()
        self.accountStack = QStackedWidget()
        # codeCompletionBlock end

        uic.loadUi(getUiFile('MainWindow'), self)

        self.model = Model()
        self.model.loadLastSession()

        self.actionAdd_Account.triggered.connect(self.addSubAccount)
        self.accountSelection.currentIndexChanged.connect(self.accountStack.setCurrentIndex)

    def addSubAccount(self):
        data = inquireAccountData()
        account = self.model.createNewAccount(data['name'],
                                              data['description'],
                                              data['numbers'])
        self.model.addNewAccount(account)

        self.accountSelection.addItem(account.description)
        self.accountStack.addWidget(AccountWidget())
Exemple #4
0
class ConfigWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        layout = QFormLayout()
        self.setLayout(layout)

        self.label = QLabel("Audio Input")
        self.inputLayout = QHBoxLayout()
        self.combobox = QComboBox()
        self.combobox.setMinimumWidth(150)
        self.inputSettingsToolButton = QToolButton()
        self.inputSettingsToolButton.setText("Settings")
        configIcon = QIcon.fromTheme("preferences-other")
        self.inputSettingsToolButton.setIcon(configIcon)
        self.inputSettingsToolButton.setSizePolicy(QSizePolicy.Maximum,
                                                   QSizePolicy.Maximum)
        self.inputSettingsToolButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.inputSettingsStack = QStackedWidget()
        blankWidget = QWidget()
        self.inputSettingsStack.addWidget(blankWidget)
        self.inputSettingsStack.addWidget(self.inputSettingsToolButton)
        self.inputLayout.addWidget(self.combobox)
        self.inputLayout.addWidget(self.inputSettingsStack)
        layout.addRow(self.label, self.inputLayout)
Exemple #5
0
class FormComboWidget(QWidget):

    def __init__(self, datalist, comment="", parent=None):
        QWidget.__init__(self, parent)
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.combobox = QComboBox()
        layout.addWidget(self.combobox)

        self.stackwidget = QStackedWidget(self)
        layout.addWidget(self.stackwidget)
        self.connect(self.combobox, SIGNAL("currentIndexChanged(int)"),
                     self.stackwidget, SLOT("setCurrentIndex(int)"))

        self.widgetlist = []
        for data, title, comment in datalist:
            self.combobox.addItem(title)
            widget = FormWidget(data, comment=comment, parent=self)
            self.stackwidget.addWidget(widget)
            self.widgetlist.append(widget)

    def setup(self):
        for widget in self.widgetlist:
            widget.setup()

    def get(self):
        return [widget.get() for widget in self.widgetlist]
Exemple #6
0
class FormComboWidget(QWidget):
    def __init__(self, datalist, comment="", parent=None):
        QWidget.__init__(self, parent)
        layout = QVBoxLayout()
        self.setLayout(layout)
        self.combobox = QComboBox()
        layout.addWidget(self.combobox)

        self.stackwidget = QStackedWidget(self)
        layout.addWidget(self.stackwidget)
        self.connect(self.combobox, SIGNAL("currentIndexChanged(int)"),
                     self.stackwidget, SLOT("setCurrentIndex(int)"))

        self.widgetlist = []
        for data, title, comment in datalist:
            self.combobox.addItem(title)
            widget = FormWidget(data, comment=comment, parent=self)
            self.stackwidget.addWidget(widget)
            self.widgetlist.append(widget)

    def setup(self):
        for widget in self.widgetlist:
            widget.setup()

    def get(self):
        return [widget.get() for widget in self.widgetlist]
Exemple #7
0
class ConfigWidget(QWidget):

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        layout = QFormLayout()
        self.setLayout(layout)

        self.label = QLabel("Audio Input")
        self.inputLayout = QHBoxLayout()
        self.combobox = QComboBox()
        self.combobox.setMinimumWidth(150)
        self.inputSettingsToolButton = QToolButton()
        self.inputSettingsToolButton.setText("Settings")
        configIcon = QIcon.fromTheme("preferences-other")
        self.inputSettingsToolButton.setIcon(configIcon)
        self.inputSettingsToolButton.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.inputSettingsToolButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.inputSettingsStack = QStackedWidget()
        blankWidget = QWidget()
        self.inputSettingsStack.addWidget(blankWidget)
        self.inputSettingsStack.addWidget(self.inputSettingsToolButton)
        self.inputLayout.addWidget(self.combobox)
        self.inputLayout.addWidget(self.inputSettingsStack)
        layout.addRow(self.label, self.inputLayout)
class ComboTabWidget(QWidget):
    def __init__(self, parent):
        super(ComboTabWidget, self).__init__(parent)
        layout = QVBoxLayout(self)
        layout.setSpacing(0)
        
        self.switchCombo = QComboBox(self)
        layout.addWidget(self.switchCombo, 0, Qt.AlignCenter)
        
        groupBox = QGroupBox(self)
        groupBoxLayout = QVBoxLayout(groupBox)
        groupBoxLayout.setSpacing(0)
        
        self.pageArea = QStackedWidget(groupBox)
        groupBoxLayout.addWidget(self.pageArea)
        
        layout.addWidget(groupBox, 1)
        
        self.switchCombo.currentIndexChanged.connect(self.pageArea.setCurrentIndex)
        
    def setTabPosition(self, tabPos):
        pass
        
    def addTab(self, w, tabText):
        self.pageArea.addWidget(w)
        self.switchCombo.addItem(tabText)
    
    def insertTab(self, pos, w, tabText):
        self.pageArea.insertWidget(pos, w)
        self.switchCombo.insertItem(pos, tabText)
        
    def removeTab(self, index=-1):
        if index < 0:
            index = self.currentIndex()
        
        w = self.pageArea.widget(index)
        
        self.pageArea.removeWidget(w)
        self.switchCombo.removeItem(index)
        
    def updateTab(self, w, tabText, index=-1):
        if index < 0:
            index = self.switchCombo.currentIndex()
        
        self.removeTab(index)
        self.insertTab(index, w, tabText)
        self.setCurrentIndex(index)
        
    def setCurrentIndex(self, index):
        self.switchCombo.setCurrentIndex(index)
        
    def widget(self, index):
        return self.pageArea.widget(index)
        
    def currentIndex(self):
        return self.switchCombo.currentIndex()
    
    def count(self):
        return self.switchCombo.count()
Exemple #9
0
class RunConfigDialog(BaseRunConfigDialog):
    """Run configuration dialog box: multiple file version"""
    def __init__(self, parent=None):
        BaseRunConfigDialog.__init__(self, parent)
        self.file_to_run = None
        self.combo = None
        self.stack = None

    def run_btn_clicked(self):
        """Run button was just clicked"""
        self.file_to_run = unicode(self.combo.currentText())

    def setup(self, fname):
        """Setup Run Configuration dialog with filename *fname*"""
        combo_label = QLabel(_("Select a run configuration:"))
        self.combo = QComboBox()
        self.combo.setMaxVisibleItems(20)
        self.combo.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
        self.combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        self.stack = QStackedWidget()

        configurations = _get_run_configurations()
        for index, (filename, options) in enumerate(configurations):
            if fname == filename:
                break
        else:
            # There is no run configuration for script *fname*:
            # creating a temporary configuration that will be kept only if
            # dialog changes are accepted by the user
            configurations.insert(0, (fname, RunConfiguration(fname).get()))
            index = 0
        for filename, options in configurations:
            widget = RunConfigOptions(self)
            widget.set(options)
            self.combo.addItem(filename)
            self.stack.addWidget(widget)
        self.connect(self.combo, SIGNAL("currentIndexChanged(int)"),
                     self.stack.setCurrentIndex)
        self.combo.setCurrentIndex(index)

        self.add_widgets(combo_label, self.combo, 10, self.stack)
        self.add_button_box(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        self.setWindowTitle(_("Run Settings"))

    def accept(self):
        """Reimplement Qt method"""
        configurations = []
        for index in range(self.stack.count()):
            filename = unicode(self.combo.itemText(index))
            runconfigoptions = self.stack.widget(index)
            if index == self.stack.currentIndex() and\
               not runconfigoptions.is_valid():
                return
            options = runconfigoptions.get()
            configurations.append((filename, options))
        _set_run_configurations(configurations)
        QDialog.accept(self)
Exemple #10
0
class RunConfigDialog(BaseRunConfigDialog):
    """Run configuration dialog box: multiple file version"""
    def __init__(self, parent=None):
        BaseRunConfigDialog.__init__(self, parent)
        self.file_to_run = None
        self.combo = None
        self.stack = None
        
    def run_btn_clicked(self):
        """Run button was just clicked"""
        self.file_to_run = unicode(self.combo.currentText())
        
    def setup(self, fname):
        """Setup Run Configuration dialog with filename *fname*"""
        combo_label = QLabel(_("Select a run configuration:"))
        self.combo = QComboBox()
        self.combo.setMaxVisibleItems(20)
        self.combo.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLength)
        self.combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        
        self.stack = QStackedWidget()

        configurations = _get_run_configurations()
        for index, (filename, options) in enumerate(configurations):
            if fname == filename:
                break
        else:
            # There is no run configuration for script *fname*:
            # creating a temporary configuration that will be kept only if
            # dialog changes are accepted by the user
            configurations.insert(0, (fname, RunConfiguration(fname).get()))
            index = 0
        for filename, options in configurations:
            widget = RunConfigOptions(self)
            widget.set(options)
            self.combo.addItem(filename)
            self.stack.addWidget(widget)
        self.connect(self.combo, SIGNAL("currentIndexChanged(int)"),
                     self.stack.setCurrentIndex)
        self.combo.setCurrentIndex(index)

        self.add_widgets(combo_label, self.combo, 10, self.stack)
        self.add_button_box(QDialogButtonBox.Ok|QDialogButtonBox.Cancel)

        self.setWindowTitle(_("Run Settings"))
        
    def accept(self):
        """Reimplement Qt method"""
        configurations = []
        for index in range(self.stack.count()):
            filename = unicode(self.combo.itemText(index))
            runconfigoptions = self.stack.widget(index)
            if index == self.stack.currentIndex() and\
               not runconfigoptions.is_valid():
                return
            options = runconfigoptions.get()
            configurations.append( (filename, options) )
        _set_run_configurations(configurations)
        QDialog.accept(self)
Exemple #11
0
class AccountWidget(QWidget):
    def __init__(self, parent=None):
        super(AccountWidget, self).__init__(parent)

        # codeCompletionBlock start
        from PyQt4.QtGui import QTreeWidget, QStackedWidget
        self.dateTree = QTreeWidget()
        self.monthStack = QStackedWidget()
        # codeCompletionBlock end

        uic.loadUi(getUiFile('AccountWidget'), self)

        self.dateTree.currentItemChanged.connect(self.syncronizeStack)

        self.addTopLevelPeriod('CurrentTotal')

    def addNewSubaccount(self):
        data = self.inquireSubaccountData()
        print self

    def inquireSubaccountData(self):
        pass

    def addNextMonth(self):
        # treewidget get toplevel items
        # find last item
        # add new toplevvelitem if  toplevel item has 12 subitems
        # add new sublevel item

        last = self.getLastItem()

        month = last.monthwidget
        self.dateTree.addMonth(month)


    def addSecondLevelPeriod(self, text):
        # TODO: find previous item
        item = self.dateTree.currentItem()
        if item:
            item.addChildren([QTreeWidgetItem([text])])
            self.monthStack.addWidget(MonthWidget())
            print self.getLastItem()

    def getLastItem(self):
        return MonthItem(1, MonthWidget())

    def addTopLevelPeriod(self, text):
        self.monthStack.addWidget(MonthWidget())
        item = QTreeWidgetItem([text])
        self.dateTree.addTopLevelItem(item)
        self.dateTree.setCurrentItem(item)

    def syncronizeStack(self, item):
        print item
Exemple #12
0
class ConfigWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        layout = QFormLayout()
        self.setLayout(layout)

        self.inputLabel = QLabel("Video Input")
        self.inputLayout = QHBoxLayout()
        self.inputCombobox = QComboBox()
        self.inputSettingsToolButton = QToolButton()
        self.inputSettingsToolButton.setText("Settings")
        configIcon = QIcon.fromTheme("preferences-other")
        self.inputSettingsToolButton.setIcon(configIcon)
        self.inputSettingsToolButton.setSizePolicy(QSizePolicy.Maximum,
                                                   QSizePolicy.Maximum)
        self.inputSettingsToolButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.inputSettingsStack = QStackedWidget()
        blankWidget = QWidget()
        self.inputSettingsStack.addWidget(blankWidget)
        self.inputSettingsStack.addWidget(self.inputSettingsToolButton)
        self.inputLayout.addWidget(self.inputCombobox)
        self.inputLayout.addWidget(self.inputSettingsStack)
        layout.addRow(self.inputLabel, self.inputLayout)

        self.videocolourLabel = QLabel(self.tr("Colour Format"))
        self.videocolourComboBox = QComboBox()
        self.videocolourComboBox.addItem("video/x-raw-rgb")
        self.videocolourComboBox.addItem("video/x-raw-yuv")
        self.videocolourComboBox.setSizePolicy(QSizePolicy.Minimum,
                                               QSizePolicy.Maximum)
        layout.addRow(self.videocolourLabel, self.videocolourComboBox)

        self.framerateLabel = QLabel("Framerate")
        self.framerateLayout = QHBoxLayout()
        self.framerateSlider = QSlider()
        self.framerateSlider.setOrientation(Qt.Horizontal)
        self.framerateSlider.setMinimum(1)
        self.framerateSlider.setMaximum(60)
        self.framerateSpinBox = QSpinBox()
        self.framerateSpinBox.setMinimum(1)
        self.framerateSpinBox.setMaximum(60)
        self.framerateLayout.addWidget(self.framerateSlider)
        self.framerateLayout.addWidget(self.framerateSpinBox)
        layout.addRow(self.framerateLabel, self.framerateLayout)

        self.videoscaleLabel = QLabel("Video Scale")
        self.videoscaleComboBox = QComboBox()
        for scale in resmap:
            self.videoscaleComboBox.addItem(scale)
        self.videoscaleComboBox.setSizePolicy(QSizePolicy.Minimum,
                                              QSizePolicy.Maximum)
        layout.addRow(self.videoscaleLabel, self.videoscaleComboBox)
Exemple #13
0
class ConfigWidget(QWidget):

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        layout = QFormLayout()
        self.setLayout(layout)

        self.inputLabel = QLabel("Video Input")
        self.inputLayout = QHBoxLayout()
        self.inputCombobox = QComboBox()
        self.inputSettingsToolButton = QToolButton()
        self.inputSettingsToolButton.setText("Settings")
        configIcon = QIcon.fromTheme("preferences-other")
        self.inputSettingsToolButton.setIcon(configIcon)
        self.inputSettingsToolButton.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.inputSettingsToolButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.inputSettingsStack = QStackedWidget()
        blankWidget = QWidget()
        self.inputSettingsStack.addWidget(blankWidget)
        self.inputSettingsStack.addWidget(self.inputSettingsToolButton)
        self.inputLayout.addWidget(self.inputCombobox)
        self.inputLayout.addWidget(self.inputSettingsStack)
        layout.addRow(self.inputLabel, self.inputLayout)

        self.videocolourLabel = QLabel(self.tr("Colour Format"))
        self.videocolourComboBox = QComboBox()
        self.videocolourComboBox.addItem("video/x-raw-rgb")
        self.videocolourComboBox.addItem("video/x-raw-yuv")
        self.videocolourComboBox.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum)
        layout.addRow(self.videocolourLabel, self.videocolourComboBox)

        self.framerateLabel = QLabel("Framerate")
        self.framerateLayout = QHBoxLayout()
        self.framerateSlider = QSlider()
        self.framerateSlider.setOrientation(Qt.Horizontal)
        self.framerateSlider.setMinimum(1)
        self.framerateSlider.setMaximum(60)
        self.framerateSpinBox = QSpinBox()
        self.framerateSpinBox.setMinimum(1)
        self.framerateSpinBox.setMaximum(60)
        self.framerateLayout.addWidget(self.framerateSlider)
        self.framerateLayout.addWidget(self.framerateSpinBox)
        layout.addRow(self.framerateLabel, self.framerateLayout)

        self.videoscaleLabel = QLabel("Video Scale")
        self.videoscaleComboBox = QComboBox()
        for scale in resmap:
            self.videoscaleComboBox.addItem(scale)
        self.videoscaleComboBox.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum)
        layout.addRow(self.videoscaleLabel, self.videoscaleComboBox)
Exemple #14
0
class FormTabWidget(QWidget):
    def __init__(self, datalist, comment="", parent=None):
        QWidget.__init__(self, parent)
        layout = QHBoxLayout()
        self.contentsWidget = QListWidget()
        self.contentsWidget.setViewMode(QListView.ListMode)
        self.contentsWidget.setMovement(QListView.Static)
        self.contentsWidget.setMaximumWidth(128)

        self.pagesWidget = QStackedWidget()
        layout.addWidget(self.contentsWidget)
        layout.addWidget(self.pagesWidget)
        self.setLayout(layout)
        self.widgetlist = []

        for elem in datalist:
            if len(elem) == 4:
                data, title, comment, icon = elem
            else:
                data, title, comment = elem
                icon = None

            if len(data[0]) == 3:
                widget = FormComboWidget(data, comment=comment, parent=self)
            else:
                widget = FormWidget(data, comment=comment, parent=self)
            #index = self.tabwidget.addTab(widget, title)
            #self.tabwidget.setTabToolTip(index, comment)
            self.pagesWidget.addWidget(widget)
            contentItem = QListWidgetItem(self.contentsWidget)
            if icon:
                contentItem.setIcon(icon)
            contentItem.setText(title)
            contentItem.setToolTip(comment)
            contentItem.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
            self.contentsWidget.addItem(contentItem)
            self.widgetlist.append(widget)

        self.contentsWidget.currentRowChanged.connect(self.changePage)

    def changePage(self, current):
        if current != -1:
            self.pagesWidget.setCurrentIndex(current)

    def setup(self):
        for widget in self.widgetlist:
            widget.setup()

    def get(self):
        return [ widget.get() for widget in self.widgetlist]
Exemple #15
0
 def addWidgets(self):
     """Create main layout."""
     logger = self.logger
     logger.debug('adding widgets')
     layout = QVBoxLayout()
     self.setLayout(layout)
     layout.setSpacing(0)
     layout.setContentsMargins(0, 0, 0, 0)
     tabbar = QTabBar(self)
     tabbar.setFocusPolicy(Qt.NoFocus)
     tabbar.setVisible(False)
     tabbar.currentChanged.connect(self.changeVault)
     layout.addWidget(tabbar)
     self.tabbar = tabbar
     stack = QStackedWidget(self)
     layout.addWidget(stack)
     novault = NoVaultWidget(stack)
     stack.addWidget(novault)
     self.stack = stack
Exemple #16
0
 def addWidgets(self):
     """Create main layout."""
     logger = self.logger
     logger.debug('adding widgets')
     layout = QVBoxLayout()
     self.setLayout(layout)
     layout.setSpacing(0)
     layout.setContentsMargins(0, 0, 0, 0)
     tabbar = QTabBar(self)
     tabbar.setFocusPolicy(Qt.NoFocus)
     tabbar.setVisible(False)
     tabbar.currentChanged.connect(self.changeVault)
     layout.addWidget(tabbar)
     self.tabbar = tabbar
     stack = QStackedWidget(self)
     layout.addWidget(stack)
     novault = NoVaultWidget(stack)
     stack.addWidget(novault)
     self.stack = stack
Exemple #17
0
class QChatTab(QWidget):
    def __init__(self, chatWindow, nick):
        QWidget.__init__(self)

        self.chatWindow = chatWindow
        self.nick = nick
        self.unreadCount = 0

        self.widgetStack = QStackedWidget(self)
        self.widgetStack.addWidget(
            QNickInputWidget('new_chat.png',
                             150,
                             self.connectClicked,
                             parent=self))
        self.widgetStack.addWidget(QConnectingWidget(self))
        self.widgetStack.addWidget(
            QChatWidget(self.chatWindow.connectionManager, self))

        # Skip the chat layout if the nick was given denoting an incoming connection
        if self.nick is None or self.nick == '':
            self.widgetStack.setCurrentIndex(0)
        else:
            self.widgetStack.setCurrentIndex(2)

        layout = QHBoxLayout()
        layout.addWidget(self.widgetStack)
        self.setLayout(layout)

    def connectClicked(self, nick):
        # Check that the nick isn't already connected
        if self.chatWindow.isNickInTabs(nick):
            QMessageBox.warning(self, errors.TITLE_ALREADY_CONNECTED,
                                errors.ALREADY_CONNECTED % (nick))
            return

        self.nick = nick
        self.widgetStack.widget(1).setConnectingToNick(self.nick)
        self.widgetStack.setCurrentIndex(1)
        self.chatWindow.connectionManager.openChat(self.nick)

    def showNowChattingMessage(self):
        self.widgetStack.setCurrentIndex(2)
        self.widgetStack.widget(2).showNowChattingMessage(self.nick)

    def appendMessage(self, message, source):
        self.widgetStack.widget(2).appendMessage(message, source)

    def resetOrDisable(self):
        # If the connecting widget is showing, reset to the nick input widget
        # If the chat widget is showing, disable it to prevent sending of more messages
        curWidgetIndex = self.widgetStack.currentIndex()
        if curWidgetIndex == 1:
            self.widgetStack.setCurrentIndex(0)
        elif curWidgetIndex == 2:
            self.widgetStack.widget(2).disable()

    def enable(self):
        self.widgetStack.setCurrentIndex(2)
        self.widgetStack.widget(2).enable()
Exemple #18
0
class QChatTab(QWidget):
    def __init__(self, chatWindow, nick):
        QWidget.__init__(self)

        self.chatWindow = chatWindow
        self.nick = nick
        self.unreadCount = 0

        self.widgetStack = QStackedWidget(self)
        self.widgetStack.addWidget(QNickInputWidget('new_chat.png', 150, self.connectClicked, parent=self))
        self.widgetStack.addWidget(QConnectingWidget(self))
        self.widgetStack.addWidget(QChatWidget(self.chatWindow.connectionManager, self))

        # Skip the chat layout if the nick was given denoting an incoming connection
        if self.nick is None or self.nick == '':
            self.widgetStack.setCurrentIndex(0)
        else:
            self.widgetStack.setCurrentIndex(2)

        layout = QHBoxLayout()
        layout.addWidget(self.widgetStack)
        self.setLayout(layout)


    def connectClicked(self, nick):
        # Check that the nick isn't already connected
        if self.chatWindow.isNickInTabs(nick):
            QMessageBox.warning(self, errors.TITLE_ALREADY_CONNECTED, errors.ALREADY_CONNECTED % (nick))
            return

        self.nick = nick
        self.widgetStack.widget(1).setConnectingToNick(self.nick)
        self.widgetStack.setCurrentIndex(1)
        self.chatWindow.connectionManager.openChat(self.nick)


    def showNowChattingMessage(self):
        self.widgetStack.setCurrentIndex(2)
        self.widgetStack.widget(2).showNowChattingMessage(self.nick)


    def appendMessage(self, message, source):
        self.widgetStack.widget(2).appendMessage(message, source)


    def resetOrDisable(self):
        # If the connecting widget is showing, reset to the nick input widget
        # If the chat widget is showing, disable it to prevent sending of more messages
        curWidgetIndex = self.widgetStack.currentIndex()
        if curWidgetIndex == 1:
            self.widgetStack.setCurrentIndex(0)
        elif curWidgetIndex == 2:
            self.widgetStack.widget(2).disable()


    def enable(self):
        self.widgetStack.setCurrentIndex(2)
        self.widgetStack.widget(2).enable()
Exemple #19
0
class ConfigWidget(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        layout = QGridLayout()
        self.setLayout(layout)

        self.source1_label = QLabel('Source 1')
        self.source1_combobox = QComboBox()
        self.source1_combobox.setSizePolicy(QSizePolicy.Minimum,
                                            QSizePolicy.Maximum)
        self.source1_button = QToolButton()
        self.source1_button.setText("Settings")
        configIcon = QIcon.fromTheme("preferences-other")
        self.source1_button.setIcon(configIcon)
        self.source1_button.setSizePolicy(QSizePolicy.Maximum,
                                          QSizePolicy.Maximum)
        self.source1_button.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.source1_stack = QStackedWidget()
        blankWidget = QWidget()
        self.source1_stack.addWidget(blankWidget)
        self.source1_stack.addWidget(self.source1_button)
        layout.addWidget(self.source1_label, 0, 0)
        layout.addWidget(self.source1_combobox, 0, 1)
        layout.addWidget(self.source1_stack, 0, 2)

        self.source2_label = QLabel('Source 2')
        self.source2_combobox = QComboBox()
        self.source2_combobox.setSizePolicy(QSizePolicy.Minimum,
                                            QSizePolicy.Maximum)
        self.source2_button = QToolButton()
        self.source2_button.setText("Settings")
        self.source2_button.setIcon(configIcon)  # reuse icon from source1
        self.source2_button.setSizePolicy(QSizePolicy.Maximum,
                                          QSizePolicy.Maximum)
        self.source2_button.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.source2_stack = QStackedWidget()
        blankWidget = QWidget()
        self.source2_stack.addWidget(blankWidget)
        self.source2_stack.addWidget(self.source2_button)
        layout.addWidget(self.source2_label, 1, 0)
        layout.addWidget(self.source2_combobox, 1, 1)
        layout.addWidget(self.source2_stack, 1, 2)
Exemple #20
0
class ConfigWidget(QWidget):

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        layout = QGridLayout()
        self.setLayout(layout)

        self.mainInputLabel = QLabel("Main Source")
        self.mainInputComboBox = QComboBox()
        self.mainInputSetupButton = QToolButton()
        self.mainInputSetupButton.setText("Settings")
        configIcon = QIcon.fromTheme("preferences-other")
        self.mainInputSetupButton.setIcon(configIcon)
        self.mainInputSetupButton.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.mainInputSetupButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.mainInputSetupStack = QStackedWidget()
        blankWidget = QWidget()
        self.mainInputSetupStack.addWidget(blankWidget)
        self.mainInputSetupStack.addWidget(self.mainInputSetupButton)
        layout.addWidget(self.mainInputLabel, 0, 0)
        layout.addWidget(self.mainInputComboBox, 0, 1)
        layout.addWidget(self.mainInputSetupStack, 0, 2)

        self.pipInputLabel = QLabel("PIP Source")
        self.pipInputComboBox = QComboBox()
        self.pipInputSetupButton = QToolButton()
        self.pipInputSetupButton.setText("Settings")
        self.pipInputSetupButton.setIcon(configIcon)  # reuse the one from main input
        self.pipInputSetupButton.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.pipInputSetupButton.setToolButtonStyle(Qt.ToolButtonIconOnly)
        self.pipInputSetupStack = QStackedWidget()
        blankWidget = QWidget()
        self.pipInputSetupStack.addWidget(blankWidget)
        self.pipInputSetupStack.addWidget(self.pipInputSetupButton)
        layout.addWidget(self.pipInputLabel, 1, 0)
        layout.addWidget(self.pipInputComboBox, 1, 1)
        layout.addWidget(self.pipInputSetupStack, 1, 2)
Exemple #21
0
class LunchMenuWidget(QWidget):
    textViewIndex = 0
    textViewAdditivesMap = {}

    def __init__(self, parent):
        super(LunchMenuWidget, self).__init__(parent)

        box = QVBoxLayout(self)
        box.addWidget(QLabel(u"Initializing...", self))

    def initializeLayout(self):
        layout = self.layout()

        child = layout.takeAt(0)
        while child != None:
            child.widget().deleteLater()
            child = layout.takeAt(0)

        self.messages = LunchMenu.messages()
        self.toggleMessages = LunchMenu.toggleMessages()

        self.additives = LunchMenu.additives()
        self.toggleAdditives = LunchMenu.toggleAdditives()

        buttonBar = self.createButtonBar(self)

        layout.addLayout(buttonBar)

        self.menuNotebook = QStackedWidget(self)
        self.createNotebook()
        layout.addWidget(self.menuNotebook)

        self.setSizePolicy(QSizePolicy.MinimumExpanding,
                           QSizePolicy.MinimumExpanding)

    def create_arrow_button(self, parent, arrow_type):
        button = QToolButton(parent)
        button.setArrowType(arrow_type)
        return button

    def goLeft(self):
        curIndex = self.combobox.currentIndex()
        if curIndex > 0:
            self.combobox.setCurrentIndex(curIndex - 1)

    def goRight(self):
        curIndex = self.combobox.currentIndex()
        if curIndex < 4:
            self.combobox.setCurrentIndex(curIndex + 1)

    def goToday(self):
        now = LunchMenu.today()

        minDelta = sys.maxint
        minDeltaI = 0
        i = 0
        for aLunchMenu in LunchMenu.allLunchMenus():
            if aLunchMenu == None or isinstance(aLunchMenu, Exception):
                # parse error, use current day of week
                if now.weekday() < 5:
                    minDeltaI = now.weekday()
                else:
                    minDeltaI = 4
                break
            td = now - aLunchMenu.lunchDate
            delta = abs((td.microseconds +
                         (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6)
            if delta < minDelta:
                minDelta = delta
                minDeltaI = i
            i = i + 1

        self.combobox.setCurrentIndex(minDeltaI)

    def goTodayClicked(self):
        self.goToday()

    def isToggled(self):
        index = self.menuNotebook.currentIndex()
        return (index >= 5)

    def changed_combo(self, index):
        if self.isToggled():
            self.menuNotebook.setCurrentIndex(index + 5)
        else:
            self.menuNotebook.setCurrentIndex(index)
        self.leftButton.setEnabled(index != 0)
        self.rightButton.setEnabled(index != 4)

    def toggleLanguage(self):
        index = self.menuNotebook.currentIndex()
        isToggle = (index >= 5)
        if isToggle:
            self.switchLanguageButton.setText(self.messages["toggleLanguage"])
            index = index - 5
        else:
            self.switchLanguageButton.setText(self.messages["toggleLanguage2"])
            index = index + 5
        self.menuNotebook.setCurrentIndex(index)

    def createButtonBar(self, parent):
        self.combobox = QComboBox(parent)
        self.combobox.addItem(self.messages['monday'])
        self.combobox.addItem(self.messages['tuesday'])
        self.combobox.addItem(self.messages['wednesday'])
        self.combobox.addItem(self.messages['thursday'])
        self.combobox.addItem(self.messages['friday'])
        self.combobox.currentIndexChanged.connect(self.changed_combo)
        comboBoxHeight = self.combobox.sizeHint().height()

        self.leftButton = self.create_arrow_button(parent, Qt.LeftArrow)
        self.leftButton.clicked.connect(self.goLeft)
        self.leftButton.setMinimumSize(comboBoxHeight, comboBoxHeight)

        self.rightButton = self.create_arrow_button(parent, Qt.RightArrow)
        self.rightButton.clicked.connect(self.goRight)
        self.rightButton.setMinimumSize(comboBoxHeight, comboBoxHeight)

        navButtons = QHBoxLayout()
        navButtons.addWidget(self.leftButton, 0, Qt.AlignRight)
        navButtons.addWidget(self.combobox, 0, Qt.AlignCenter)
        navButtons.addWidget(self.rightButton, 0, Qt.AlignLeft)

        buttonBar = QHBoxLayout()
        todayButton = QPushButton(self.messages['today'], parent)
        todayButton.clicked.connect(self.goTodayClicked)
        todayButton.setMinimumHeight(comboBoxHeight)
        buttonBar.addWidget(todayButton)

        buttonBar.addWidget(QWidget(parent), 1)
        buttonBar.addLayout(navButtons, 1)
        buttonBar.addWidget(QWidget(parent), 1)

        self.switchLanguageButton = QPushButton(
            self.messages["toggleLanguage"], parent)
        self.switchLanguageButton.clicked.connect(self.toggleLanguage)
        self.switchLanguageButton.setMinimumHeight(comboBoxHeight)
        buttonBar.addWidget(self.switchLanguageButton, 0, Qt.AlignRight)

        return buttonBar

    def addMenuLine(self, parent, text, box, header=False):
        aLabel = QLabel(text, parent)
        if header:
            aLabel.setAlignment(Qt.AlignCenter)
            oldFont = aLabel.font()
            aLabel.setFont(QFont(oldFont.family(), 13, QFont.Bold))
        box.addWidget(aLabel, 0, Qt.AlignBottom)

    def addLocaleErrorPage(self, parent, box, toggle):
        aLabel = QLabel(self.messages['parseLocaleError'], parent)
        aLabel.setWordWrap(True)
        box.addWidget(aLabel)

        aButton = QPushButton(self.messages['installLocaleButton'], parent)
        if toggle:
            aButton.clicked.connect(self.installLanguageSupportToggle)
        else:
            aButton.clicked.connect(self.installLanguageSupport)
        box.addWidget(aButton)

    def addExceptionPage(self, parent, box, error, _toggle):
        aLabel = QLabel(
            self.messages['otherException'] + u" " + unicode(error), parent)
        aLabel.setWordWrap(True)
        box.addWidget(aLabel)

    def installLanguageSupportForLocale(self, locale):
        locale = locale.partition("_")[0]
        if subprocess.call(
            ['gksu', "apt-get -q -y install language-pack-%s" % locale]) != 0:
            QMessageBox().critical(self.menuNotebook,
                                   "Installation Error",
                                   self.messages['installLocaleError'],
                                   buttons=QMessageBox.Ok,
                                   defaultButton=QMessageBox.Ok)
        else:
            QMessageBox().information(self.menuNotebook,
                                      "Success",
                                      self.messages['installLocaleSuccess'],
                                      buttons=QMessageBox.Ok,
                                      defaultButton=QMessageBox.Ok)

    def installLanguageSupport(self):
        self.installLanguageSupportForLocale(self.defaultLocaleString)

    def installLanguageSupportToggle(self):
        self.installLanguageSupportForLocale(self.messages['toggleLocale'])

    def formatTitleAndDescription(self, title, description, keyInfo):
        if title and description:
            result = "%s, %s" % (title, description)
        elif title:
            result = title
        else:
            result = description

        if keyInfo:
            return "%s: %s" % (keyInfo.title(), result)
        return result

    def addMenuContent(self, parent, desc, menuContents, box, messages,
                       additivesDict):
        self.addMenuLine(parent, desc, box)
        if desc in menuContents:
            contentList = menuContents[desc]
        else:
            contentList = [(messages[u'noContents'], None, [], None)]
            log_debug("lunch menu does not contain key '%s'" % desc)

        textview = GrowingTextEdit(parent, messages, additivesDict)
        textview.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        textview.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        textview.setLineWrapMode(QTextEdit.WidgetWidth)
        textview.setReadOnly(True)
        textview.document().setIndentWidth(10)

        if len(contentList) == 1:
            title, description, additives, keyInfo = contentList[0]
            textview.append(
                self.formatTitleAndDescription(title, description, keyInfo),
                additives)
        elif len(contentList) > 1:
            cursor = textview.textCursor()
            listFormat = QTextListFormat()
            listFormat.setStyle(QTextListFormat.ListDisc)
            listFormat.setIndent(1)
            cursor.createList(listFormat)
            for title, description, additives, keyInfo in contentList:
                textview.append(
                    self.formatTitleAndDescription(title, description,
                                                   keyInfo), additives)

        box.addWidget(textview, 0)

    def createNotebook(self):
        self.combobox.setCurrentIndex(0)
        for _ in range(self.menuNotebook.count()):
            self.menuNotebook.removeWidget(self.menuNotebook.widget(0))
        curMessages = self.messages
        curAdditives = self.additives
        for index in range(10):
            if index == 5:
                try:
                    if getPlatform() != PLATFORM_WINDOWS:
                        locale.setlocale(
                            locale.LC_TIME,
                            (self.messages["toggleLocale"], "UTF-8"))
                except:
                    log_exception("error setting locale")
                curMessages = self.toggleMessages
                curAdditives = self.toggleAdditives
            pageWidget = QWidget(self.menuNotebook)
            page = QVBoxLayout(pageWidget)
            thisLunchMenu = LunchMenu.allLunchMenus()[index]
            if thisLunchMenu != None and type(thisLunchMenu) == LunchMenu:
                title = curMessages[
                    'lunchMenuFor'] + u" " + thisLunchMenu.lunchDate.strftime(
                        curMessages['dateFormatDisplayed']).decode("utf-8")
                self.addMenuLine(pageWidget, title, page, True)
                if thisLunchMenu.isValid():
                    self.addMenuContent(pageWidget,
                                        curMessages['soupDisplayed'],
                                        thisLunchMenu.contents, page,
                                        curMessages, curAdditives)
                    self.addMenuContent(pageWidget,
                                        curMessages['mainDishesDisplayed'],
                                        thisLunchMenu.contents, page,
                                        curMessages, curAdditives)
                    self.addMenuContent(pageWidget,
                                        curMessages['supplementsDisplayed'],
                                        thisLunchMenu.contents, page,
                                        curMessages, curAdditives)
                    self.addMenuContent(pageWidget,
                                        curMessages['dessertsDisplayed'],
                                        thisLunchMenu.contents, page,
                                        curMessages, curAdditives)
                else:
                    self.addMenuLine(pageWidget, curMessages['noLunchToday'],
                                     page)
            elif type(thisLunchMenu) == locale.Error:
                self.addLocaleErrorPage(pageWidget, page, index >= 5)
                pass
            elif isinstance(thisLunchMenu, Exception):
                self.addExceptionPage(pageWidget, page, thisLunchMenu,
                                      index >= 5)

            self.menuNotebook.addWidget(pageWidget)
        try:
            if getPlatform() != PLATFORM_WINDOWS:
                locale.setlocale(locale.LC_TIME,
                                 (LunchMenu.defaultLocaleString, "UTF-8"))
        except:
            log_exception("error setting locale")

        self.goToday()
class DataSelectionGui(QWidget):
    """
    Manages all GUI elements in the data selection applet.
    This class itself is the central widget and also owns/manages the applet drawer widgets.
    """

    ###########################################
    ### AppletGuiInterface Concrete Methods ###
    ###########################################

    def centralWidget( self ):
        return self

    def appletDrawer( self ):
        return self._drawer

    def menus( self ):
        return []

    def viewerControlWidget(self):
        return self._viewerControlWidgetStack

    def setImageIndex(self, imageIndex):
        if imageIndex is not None:
            self.laneSummaryTableView.selectRow(imageIndex)
            for detailWidget in self._detailViewerWidgets:
                detailWidget.datasetDetailTableView.selectRow(imageIndex)

    def stopAndCleanUp(self):
        for editor in self.volumeEditors.values():
            self.viewerStack.removeWidget( editor )
            self._viewerControlWidgetStack.removeWidget( editor.viewerControlWidget() )
            editor.stopAndCleanUp()
        self.volumeEditors.clear()

    def imageLaneAdded(self, laneIndex):
        if len(self.laneSummaryTableView.selectedIndexes()) == 0:
            self.laneSummaryTableView.selectRow(laneIndex)
        
        # We don't have any real work to do because this gui initiated the lane addition in the first place
        if self.guiMode != GuiMode.Batch:
            if(len(self.topLevelOperator.DatasetGroup) != laneIndex+1):
                import warnings
                warnings.warn("DataSelectionGui.imageLaneAdded(): length of dataset multislot out of sync with laneindex [%s != %s + 1]" % (len(self.topLevelOperator.Dataset), laneIndex))

    def imageLaneRemoved(self, laneIndex, finalLength):
        # We assume that there's nothing to do here because THIS GUI initiated the lane removal
        if self.guiMode != GuiMode.Batch:
            assert len(self.topLevelOperator.DatasetGroup) == finalLength

    ###########################################
    ###########################################

    def __init__(self, dataSelectionOperator, serializer, guiControlSignal, guiMode=GuiMode.Normal, title="Input Selection"):
        with Tracer(traceLogger):
            super(DataSelectionGui, self).__init__()

            self.title = title

            self._viewerControls = QWidget()
            self.topLevelOperator = dataSelectionOperator
            self.guiMode = guiMode
            self.serializer = serializer
            self.guiControlSignal = guiControlSignal
            self.threadRouter = ThreadRouter(self)

            self._initCentralUic()
            self._initAppletDrawerUic()
            
            self._viewerControlWidgetStack = QStackedWidget(self)

            def handleImageRemoved(multislot, index, finalLength):
                # Remove the viewer for this dataset
                imageSlot = self.topLevelOperator.Image[index]
                if imageSlot in self.volumeEditors.keys():
                    editor = self.volumeEditors[imageSlot]
                    self.viewerStack.removeWidget( editor )
                    self._viewerControlWidgetStack.removeWidget( editor.viewerControlWidget() )
                    editor.stopAndCleanUp()

            self.topLevelOperator.Image.notifyRemove( bind( handleImageRemoved ) )

    def _initCentralUic(self):
        """
        Load the GUI from the ui file into this class and connect it with event handlers.
        """
        # Load the ui file into this class (find it in our own directory)
        localDir = os.path.split(__file__)[0]+'/'
        uic.loadUi(localDir+"/dataSelection.ui", self)

        self._initTableViews()
        self._initViewerStack()
        self.splitter.setSizes( [150, 850] )

    def _initAppletDrawerUic(self):
        """
        Load the ui file for the applet drawer, which we own.
        """
        localDir = os.path.split(__file__)[0]+'/'
        self._drawer = uic.loadUi(localDir+"/dataSelectionDrawer.ui")

    def _initTableViews(self):
        self.fileInfoTabWidget.setTabText( 0, "Summary" )
        self.laneSummaryTableView.setModel( DataLaneSummaryTableModel(self, self.topLevelOperator) )
        self.laneSummaryTableView.dataLaneSelected.connect( self.showDataset )
        self.laneSummaryTableView.addFilesRequested.connect( self.handleAddFiles )
        self.laneSummaryTableView.addStackRequested.connect( self.handleAddStack )
        self.laneSummaryTableView.addByPatternRequested.connect( self.handleAddByPattern )
        self.removeLaneButton.clicked.connect( self.handleRemoveLaneButtonClicked )
        self.laneSummaryTableView.removeLanesRequested.connect( self.handleRemoveLaneButtonClicked )

        self._retained = [] # Retain menus so they don't get deleted
        self._detailViewerWidgets = []
        for roleIndex, role in enumerate(self.topLevelOperator.DatasetRoles.value):
            detailViewer = DataDetailViewerWidget( self, self.topLevelOperator, roleIndex )
            self._detailViewerWidgets.append( detailViewer )

            # Button
            menu = QMenu(parent=self)
            menu.setObjectName("addFileButton_role_{}".format( roleIndex ))
            menu.addAction( "Add File(s)..." ).triggered.connect( partial(self.handleAddFiles, roleIndex) )
            menu.addAction( "Add Volume from Stack..." ).triggered.connect( partial(self.handleAddStack, roleIndex) )
            menu.addAction( "Add Many by Pattern..." ).triggered.connect( partial(self.handleAddByPattern, roleIndex) )
            detailViewer.appendButton.setMenu( menu )
            self._retained.append(menu)
            
            # Context menu            
            detailViewer.datasetDetailTableView.replaceWithFileRequested.connect( partial(self.handleReplaceFile, roleIndex) )
            detailViewer.datasetDetailTableView.replaceWithStackRequested.connect( partial(self.replaceWithStack, roleIndex) )
            detailViewer.datasetDetailTableView.editRequested.connect( partial(self.editDatasetInfo, roleIndex) )
            detailViewer.datasetDetailTableView.resetRequested.connect( partial(self.handleClearDatasets, roleIndex) )

            # Drag-and-drop
            detailViewer.datasetDetailTableView.addFilesRequested.connect( partial( self.addFileNames, roleIndex=roleIndex ) )

            # Selection handling
            def showFirstSelectedDataset( _roleIndex, lanes ):
                if lanes:
                    self.showDataset( lanes[0], _roleIndex )
            detailViewer.datasetDetailTableView.dataLaneSelected.connect( partial(showFirstSelectedDataset, roleIndex) )

            self.fileInfoTabWidget.insertTab(roleIndex, detailViewer, role)
                
        self.fileInfoTabWidget.currentChanged.connect( self.handleSwitchTabs )
        self.fileInfoTabWidget.setCurrentIndex(0)

    def handleSwitchTabs(self, tabIndex ):
        if tabIndex < len(self._detailViewerWidgets):
            roleIndex = tabIndex # If summary tab is moved to the front, change this line.
            detailViewer = self._detailViewerWidgets[roleIndex]
            selectedLanes = detailViewer.datasetDetailTableView.selectedLanes
            if selectedLanes:
                self.showDataset( selectedLanes[0], roleIndex )

    def _initViewerStack(self):
        self.volumeEditors = {}
        self.viewerStack.addWidget( QWidget() )

    def handleRemoveLaneButtonClicked(self):
        """
        The user clicked the "Remove" button.
        Remove the currently selected row(s) from both the GUI and the top-level operator.
        """
        # Figure out which lanes to remove
        selectedIndexes = self.laneSummaryTableView.selectedIndexes()
        rows = set()
        for modelIndex in selectedIndexes:
            rows.add( modelIndex.row() )
        rows.discard( self.laneSummaryTableView.model().rowCount() )

        # Remove in reverse order so row numbers remain consistent
        for row in reversed(sorted(rows)):
            # Remove from the GUI
            self.laneSummaryTableView.model().removeRow(row)
            # Remove from the operator
            finalSize = len(self.topLevelOperator.DatasetGroup) - 1
            self.topLevelOperator.DatasetGroup.removeSlot(row, finalSize)
    
            # The gui and the operator should be in sync
            assert self.laneSummaryTableView.model().rowCount() == len(self.topLevelOperator.DatasetGroup)+1

    def showDataset(self, laneIndex, roleIndex=None):
        if laneIndex == -1:
            self.viewerStack.setCurrentIndex(0)
            return
        
        assert threading.current_thread().name == "MainThread"
        imageSlot = self.topLevelOperator.Image[laneIndex]

        # Create if necessary
        if imageSlot not in self.volumeEditors.keys():
            
            class DatasetViewer(LayerViewerGui):
                def moveToTop(self, roleIndex):
                    opLaneView = self.topLevelOperatorView
                    if not opLaneView.DatasetRoles.ready():
                        return
                    datasetRoles = opLaneView.DatasetRoles.value
                    roleName = datasetRoles[roleIndex]
                    try:
                        layerIndex = [l.name for l in self.layerstack].index(roleName)
                    except ValueError:
                        return
                    else:
                        self.layerstack.selectRow(layerIndex)
                        self.layerstack.moveSelectedToTop()

                def setupLayers(self):
                    opLaneView = self.topLevelOperatorView
                    if not opLaneView.DatasetRoles.ready():
                        return []
                    layers = []
                    datasetRoles = opLaneView.DatasetRoles.value
                    for roleIndex, slot in enumerate(opLaneView.ImageGroup):
                        if slot.ready():
                            roleName = datasetRoles[roleIndex]
                            layer = self.createStandardLayerFromSlot(slot)
                            layer.name = roleName
                            layers.append(layer)
                    return layers

            opLaneView = self.topLevelOperator.getLane(laneIndex)
            layerViewer = DatasetViewer(opLaneView, crosshair=False)
            
            # Maximize the x-y view by default.
            layerViewer.volumeEditorWidget.quadview.ensureMaximized(2)

            self.volumeEditors[imageSlot] = layerViewer
            self.viewerStack.addWidget( layerViewer )
            self._viewerControlWidgetStack.addWidget( layerViewer.viewerControlWidget() )

        # Show the right one
        viewer = self.volumeEditors[imageSlot]
        displayedRole = self.fileInfoTabWidget.currentIndex()
        viewer.moveToTop(displayedRole)
        self.viewerStack.setCurrentWidget( viewer )
        self._viewerControlWidgetStack.setCurrentWidget( viewer.viewerControlWidget() )


    def handleAddFiles(self, roleIndex):
        self.addFiles(roleIndex)

    def handleReplaceFile(self, roleIndex, startingLane):
        self.addFiles(roleIndex, startingLane)

    def addFiles(self, roleIndex, startingLane=None):
        """
        The user clicked the "Add File" button.
        Ask him to choose a file (or several) and add them to both
          the GUI table and the top-level operator inputs.
        """
        # Find the directory of the most recently opened image file
        mostRecentImageFile = PreferencesManager().get( 'DataSelection', 'recent image' )
        if mostRecentImageFile is not None:
            defaultDirectory = os.path.split(mostRecentImageFile)[0]
        else:
            defaultDirectory = os.path.expanduser('~')

        # Launch the "Open File" dialog
        fileNames = self.getImageFileNamesToOpen(defaultDirectory)

        # If the user didn't cancel
        if len(fileNames) > 0:
            PreferencesManager().set('DataSelection', 'recent image', fileNames[0])
            try:
                self.addFileNames(fileNames, roleIndex, startingLane)
            except RuntimeError as e:
                QMessageBox.critical(self, "Error loading file", str(e))

    def handleAddByPattern(self, roleIndex):
        # Find the most recent directory

        # TODO: remove code duplication
        mostRecentDirectory = PreferencesManager().get( 'DataSelection', 'recent mass directory' )
        if mostRecentDirectory is not None:
            defaultDirectory = os.path.split(mostRecentDirectory)[0]
        else:
            defaultDirectory = os.path.expanduser('~')

        fileNames = self.getMass(defaultDirectory)

        # If the user didn't cancel
        if len(fileNames) > 0:
            PreferencesManager().set('DataSelection', 'recent mass directory', os.path.split(fileNames[0])[0])
            try:
                self.addFileNames(fileNames, roleIndex)
            except RuntimeError as e:
                QMessageBox.critical(self, "Error loading file", str(e))


    def getImageFileNamesToOpen(self, defaultDirectory):
        """
        Launch an "Open File" dialog to ask the user for one or more image files.
        """
        extensions = OpDataSelection.SupportedExtensions
        filt = "Image files (" + ' '.join('*.' + x for x in extensions) + ')'
        options = QFileDialog.Options()
        if ilastik_config.getboolean("ilastik", "debug"):
            options |=  QFileDialog.DontUseNativeDialog
        fileNames = QFileDialog.getOpenFileNames( self, "Select Images", 
                                 defaultDirectory, filt, options=options )
        # Convert from QtString to python str
        fileNames = [str(s) for s in fileNames]
        return fileNames

    def _findFirstEmptyLane(self, roleIndex):
        opTop = self.topLevelOperator
        
        # Determine the number of files this role already has
        # Search for the last valid value.
        firstNewLane = 0
        for laneIndex, slot in reversed(zip(range(len(opTop.DatasetGroup)), opTop.DatasetGroup)):
            if slot[roleIndex].ready():
                firstNewLane = laneIndex+1
                break
        return firstNewLane

    def addFileNames(self, fileNames, roleIndex, startingLane=None):
        """
        Add the given filenames to both the GUI table and the top-level operator inputs.
        If startingLane is None, the filenames will be *appended* to the role's list of files.
        """
        infos = []

        if startingLane is None:        
            startingLane = self._findFirstEmptyLane(roleIndex)
            endingLane = startingLane+len(fileNames)-1
        else:
            assert startingLane < len(self.topLevelOperator.DatasetGroup)
            endingLane = startingLane+len(fileNames)-1

        # Assign values to the new inputs we just allocated.
        # The GUI will be updated by callbacks that are listening to slot changes
        for i, filePath in enumerate(fileNames):
            datasetInfo = DatasetInfo()
            cwd = self.topLevelOperator.WorkingDirectory.value
            
            if not areOnSameDrive(filePath,cwd):
                QMessageBox.critical(self, "Drive Error","Data must be on same drive as working directory.")
                return
                
            absPath, relPath = getPathVariants(filePath, cwd)
            
            # Relative by default, unless the file is in a totally different tree from the working directory.
            if len(os.path.commonprefix([cwd, absPath])) > 1:
                datasetInfo.filePath = relPath
            else:
                datasetInfo.filePath = absPath
                
            datasetInfo.nickname = PathComponents(absPath).filenameBase

            h5Exts = ['.ilp', '.h5', '.hdf5']
            if os.path.splitext(datasetInfo.filePath)[1] in h5Exts:
                datasetNames = self.getPossibleInternalPaths( absPath )
                if len(datasetNames) > 0:
                    datasetInfo.filePath += str(datasetNames[0])
                else:
                    raise RuntimeError("HDF5 file %s has no image datasets" % datasetInfo.filePath)

            # Allow labels by default if this gui isn't being used for batch data.
            datasetInfo.allowLabels = ( self.guiMode == GuiMode.Normal )
            infos.append(datasetInfo)

        # if no exception was thrown, set up the operator now
        opTop = self.topLevelOperator
        originalSize = len(opTop.DatasetGroup)
            
        if len( opTop.DatasetGroup ) < endingLane+1:
            opTop.DatasetGroup.resize( endingLane+1 )
        for laneIndex, info in zip(range(startingLane, endingLane+1), infos):
            try:
                self.topLevelOperator.DatasetGroup[laneIndex][roleIndex].setValue( info )
            except DatasetConstraintError as ex:
                # Give the user a chance to fix the problem
                if not self.handleDatasetConstraintError(info, info.filePath, ex, roleIndex, laneIndex):
                    opTop.DatasetGroup.resize( originalSize )
                    break
            except:
                QMessageBox.critical( self, "Dataset Load Error", "Wasn't able to load your dataset into the workflow.  See console for details." )
                opTop.DatasetGroup.resize( originalSize )
                raise
        self.updateInternalPathVisiblity()

    @threadRouted
    def handleDatasetConstraintError(self, info, filename, ex, roleIndex, laneIndex):
        msg = "Can't use default properties for dataset:\n\n" + \
              filename + "\n\n" + \
              "because it violates a constraint of the {} applet.\n\n".format( ex.appletName ) + \
              ex.message + "\n\n" + \
              "Please enter valid dataset properties to continue."
        QMessageBox.warning( self, "Dataset Needs Correction", msg )
        
        return self.repairDatasetInfo( info, roleIndex, laneIndex )

    def repairDatasetInfo(self, info, roleIndex, laneIndex):
        defaultInfos = {}
        defaultInfos[laneIndex] = info
        editorDlg = DatasetInfoEditorWidget(self, self.topLevelOperator, roleIndex, [laneIndex], defaultInfos)
        return ( editorDlg.exec_() == QDialog.Accepted )

    def getPossibleInternalPaths(self, absPath):
        datasetNames = []
        # Open the file as a read-only so we can get a list of the internal paths
        with h5py.File(absPath, 'r') as f:
            # Define a closure to collect all of the dataset names in the file.
            def accumulateDatasetPaths(name, val):
                if type(val) == h5py._hl.dataset.Dataset and 3 <= len(val.shape) <= 5:
                    datasetNames.append( '/' + name )
            # Visit every group/dataset in the file
            f.visititems(accumulateDatasetPaths)
        return datasetNames

    def handleAddStack(self, roleIndex):
        self.replaceWithStack(roleIndex, laneIndex=None)

    def replaceWithStack(self, roleIndex, laneIndex):
        """
        The user clicked the "Import Stack Files" button.
        """
        stackDlg = StackFileSelectionWidget(self)
        stackDlg.exec_()
        if stackDlg.result() != QDialog.Accepted :
            return
        files = stackDlg.selectedFiles
        if len(files) == 0:
            return

        info = DatasetInfo()
        info.filePath = "//".join( files )
        prefix = os.path.commonprefix(files)
        info.nickname = PathComponents(prefix).filenameBase + "..."

        # Allow labels by default if this gui isn't being used for batch data.
        info.allowLabels = ( self.guiMode == GuiMode.Normal )
        info.fromstack = True

        originalNumLanes = len(self.topLevelOperator.DatasetGroup)

        if laneIndex is None:
            laneIndex = self._findFirstEmptyLane(roleIndex)
        if len(self.topLevelOperator.DatasetGroup) < laneIndex+1:
            self.topLevelOperator.DatasetGroup.resize(laneIndex+1)

        def importStack():
            self.guiControlSignal.emit( ControlCommand.DisableAll )
            # Serializer will update the operator for us, which will propagate to the GUI.
            try:
                self.serializer.importStackAsLocalDataset( info )
                try:
                    self.topLevelOperator.DatasetGroup[laneIndex][roleIndex].setValue(info)
                except DatasetConstraintError as ex:
                    # Give the user a chance to repair the problem.
                    filename = files[0] + "\n...\n" + files[-1]
                    if not self.handleDatasetConstraintError( info, filename, ex, roleIndex, laneIndex ):
                        self.topLevelOperator.DatasetGroup.resize(originalNumLanes)
            finally:
                self.guiControlSignal.emit( ControlCommand.Pop )

        req = Request( importStack )
        req.notify_failed( partial(self.handleFailedStackLoad, files, originalNumLanes ) )
        req.submit()

    @threadRouted
    def handleFailedStackLoad(self, files, originalNumLanes, exc, exc_info):
        import traceback
        traceback.print_tb(exc_info[2])
        msg = "Failed to load stack due to the following error:\n{}".format( exc )
        msg += "Attempted stack files were:"
        for f in files:
            msg += f + "\n"
        QMessageBox.critical(self, "Failed to load image stack", msg)
        self.topLevelOperator.DatasetGroup.resize(originalNumLanes)

    def getMass(self, defaultDirectory):
        # TODO: launch dialog and get files

        # Convert from QtString to python str
        loader = MassFileLoader(defaultDirectory=defaultDirectory)
        loader.exec_()
        if loader.result() == QDialog.Accepted:
            fileNames = [str(s) for s in loader.filenames]
        else:
            fileNames = []
        return fileNames

    def handleClearDatasets(self, roleIndex, selectedRows):
        for row in selectedRows:
            self.topLevelOperator.DatasetGroup[row][roleIndex].disconnect()

        # Remove all operators that no longer have any connected slots        
        last_valid = -1
        laneIndexes = range( len(self.topLevelOperator.DatasetGroup) )
        for laneIndex, multislot in reversed(zip(laneIndexes, self.topLevelOperator.DatasetGroup)):
            any_ready = False
            for slot in multislot:
                any_ready |= slot.ready()
            if not any_ready:
                self.topLevelOperator.DatasetGroup.removeSlot( laneIndex, len(self.topLevelOperator.DatasetGroup)-1 )

    def editDatasetInfo(self, roleIndex, laneIndexes):
        editorDlg = DatasetInfoEditorWidget(self, self.topLevelOperator, roleIndex, laneIndexes)
        editorDlg.exec_()

    def updateInternalPathVisiblity(self):
        for widget in self._detailViewerWidgets:
            view = widget.datasetDetailTableView
            model = view.model()
            view.setColumnHidden(DatasetDetailedInfoColumn.InternalID,
                                 not model.hasInternalPaths())
Exemple #23
0
class NewSimulationWindow(QDialog):

    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.core = NewSimulationCore(self)
        self.create_widgets(parent)
        
        self.environments = []
        self.env_wid = {}  # Environment Name to Environment Widget
        self.env_obj = {}  # Environment Name to Environment Objects
        
        self.exec()
        
    def create_widgets(self, parent):
        
        # Layout
        GBuilder().set_props(self, min_sz_x=900, min_sz_y=700)
        self.setWindowTitle("Simulation Configuration")
        self.setFixedHeight(700)
        self.setFixedWidth(1350)
        main_lo = QVBoxLayout()
        self.setLayout(main_lo)

        # Label
        self.desc_label = GBuilder().label(self, "Create a new simulation. Create one or more environments and"\
                                           " fill them with components. Then connect the components.", None, None)
        self.desc_label.setFixedHeight(20)
        main_lo.addWidget(self.desc_label)
        
        # Horizontal Layout
        hl_1 = QHBoxLayout()
        main_lo.addLayout(hl_1)
        
        # Groupboxes
        self.components_gb = GBuilder().hand_groupbox(self, "Components", None, None)
        self.components_gb.setFixedWidth(300)
        self.environment_gb = GBuilder().groupbox(self, "Environments", None, None)
        hl_1.addWidget(self.components_gb)
        hl_1.addWidget(self.environment_gb)
        
        # Components Boxes
        self.comps_layout = QVBoxLayout()
        self.components_gb.setLayout(self.comps_layout)
        self._add_component_boxes()
               
        # Buttons
        main_lo.addWidget(GBuilder().hor_line(self))
        hl = QHBoxLayout()
        hl.addWidget(GBuilder().label(self, ""))
        ok_but = GBuilder().pushbutton(self, "OK", self._ok_hit)
        ok_but.setFixedWidth(150)
        ok_but.setFixedHeight(25)
        cancel_but = GBuilder().pushbutton(self, "Cancel", self._ok_hit)
        cancel_but.setFixedWidth(150)
        cancel_but.setFixedHeight(25)
        hl.addWidget(ok_but)
        hl.addWidget(cancel_but)
        main_lo.addLayout(hl)
        
        # Fill Components Boxes
        self._fill_ecu_boxes()
        self._fill_bus_boxes()
        self._fill_others_boxes()
        
        # Fill environment
        self._fill_environment_box()
        
        # Style 
        self._set_stle()
 
    def _add_component_boxes(self):
        
        # ECU Box
        self.ecu_box_wid = QScrollArea()
        wid = QWidget()
        self.ecu_box_wid.setWidget(wid)        
        self.ecu_box_wid.setWidgetResizable(True)               
        self.ecu_box = QGridLayout()
        wid.setLayout(self.ecu_box)   
        self.ecu_box_wid_wid = wid     
        self.comps_layout.addWidget(self.ecu_box_wid)

        # Bus Box
        self.bus_box_wid = QScrollArea()
        wid = QWidget()
        self.bus_box_wid.setWidget(wid)        
        self.bus_box_wid.setWidgetResizable(True)             
        self.bus_box = QGridLayout()
        wid.setLayout(self.bus_box)    
        self.bus_box_wid_wid = wid      
        self.comps_layout.addWidget(self.bus_box_wid)

        # Others Box
        self.others_box_wid = QScrollArea()
        wid = QWidget()
        self.others_box_wid.setWidget(wid)        
        self.others_box_wid.setWidgetResizable(True)       
        self.others_box = QGridLayout()
        wid.setLayout(self.others_box)
        self.others_box_wid_wid = wid  
        self.comps_layout.addWidget(self.others_box_wid)

    def _fill_ecu_boxes(self):
        
        # Creatable Objects
        kys = ECUFactory().createable_objects()   
        row = 0
        col = 0
        
        for ky in list(kys):                
            
            # Information gathering
            name = self._load_gui_name(ky, ECUFactory())  
            ico = self._load_gui_icon(ky, ECUFactory())                     
            
            # New Element per Object
            gb = GBuilder().groupbox(self, name, None, None)
            gb.setFont(QtGui.QFont('SansSerif', 7))
            gb.setFixedHeight(70)
            gb.setFixedWidth(70)            
            db = GBuilder().dragbutton(gb, '', self._add_component_boxes, ico, icon_x=45, icon_y=45, size_x=60, size_y=50, pos_x=5, pos_y=15)
            db.set_drop_func(self.core.add_ecu_box_to_env)
            db.set_move_icon_context_acts(self.core.load_context_menu_actions(ky))
            db.ecu_key = ky
            db.setCheckable(True)                
            db.setStyleSheet('QPushButton {background-color: #F2F2F2; color: red;border: 0px solid gray;border-radius: 12px;}')
            
            # Add to Layout
            self.ecu_box.addWidget(gb, row, col, Qt.AlignTop)            
            col += 1
            if col == 3:
                row += 1
                col = 0
                
        # Add Widget        
        self.ecu_box.addWidget(QWidget())
        
    def _fill_bus_boxes(self):
        
        kys = BusFactory().createable_objects()          
        
        row = 0
        col = 0
        
        for ky in list(kys):
            
            # Information gathering
            name = self._load_gui_name(ky, BusFactory())  
            ico = self._load_gui_icon(ky, BusFactory())                        
            
            # New Element per Object
            gb = GBuilder().groupbox(self, name, None, None)
            gb.setFont(QtGui.QFont('SansSerif', 7))
            gb.setFixedHeight(70)
            gb.setFixedWidth(70)            
            db = GBuilder().dragbutton(gb, '', self._add_component_boxes, ico, icon_x=45, icon_y=45, size_x=60, size_y=50, pos_x=5, pos_y=15)
            db.set_drop_func(self.core.add_bus_box_to_env)
            db.set_move_icon_context_acts(self.core.load_context_menu_actions(ky))
            db.ecu_key = ky
            db.setCheckable(True)
            db.setStyleSheet('QPushButton {background-color: #F2F2F2; color: red;border: 0px solid gray;border-radius: 12px;}')
            
            # Add to Layout
            self.bus_box.addWidget(gb, row, col, Qt.AlignTop)            
            col += 1
            if col == 3:
                row += 1
                col = 0
                
        # Add Widget        
        self.bus_box.addWidget(QWidget())
        
    def _fill_others_boxes(self):
        kys = ECUFactory().createable_objects()          
        
        row = 0
        col = 0

    def _fill_environment_box(self):
        
        # Main Layout
        main_lo = QVBoxLayout()        
        self.environment_gb.setLayout(main_lo)
                
        # Combobox
        lo, self.env_select_cb, lab = GBuilder().label_combobox(self, "Current Environment:", [], self._env_select_changed)
        hl = QHBoxLayout()
        hl.addLayout(lo)
        but = GBuilder().pushbutton(self, "New", self._new_env_hit)
        but.setFixedWidth(40)
        hl.addWidget(but)        
        lab.setFixedWidth(140)
        self.env_select_cb.setFixedHeight(22)  
        self.env_select_cb.setFixedWidth(350)      
        main_lo.addLayout(hl)
        
        # Groupbox (to make it look nicer)
        self.content_gb = EnvironmentView(self.environment_gb)    
        main_lo.addWidget(self.content_gb)
        self.content_gb.setFixedHeight(550)  
        self.content_gb.setFixedWidth(1000)  
        self.content_gb.setLayout(lo)
        
        # QStackedWidget with environments
        self.stacked_env = QStackedWidget()
        lo.addWidget(self.stacked_env)
       

    def _set_stle(self):
        
        self.content_gb.setStyleSheet('QGroupBox {background-color: #F2F2F2; color: red; border: 2px solid gray;border-radius: 12px;} EnvironmentView {background-color: #F2F2F2; color: red; border: 2px solid gray;border-radius: 12px;}')

        self.ecu_box_wid_wid.setStyleSheet('QWidget { border: 0.5px solid gray;border-radius: 3px;}')
        self.ecu_box_wid.setStyleSheet('QWidget { border: 0.5px solid gray;border-radius: 3px;}')
         
        self.bus_box_wid_wid.setStyleSheet('QWidget { border: 0.5px solid gray;border-radius: 3px;}')
        self.bus_box_wid.setStyleSheet('QWidget { border: 0.5px solid gray;border-radius: 3px;}')
         
        self.others_box_wid_wid.setStyleSheet('QWidget { border: 0.5px solid gray;border-radius: 3px;}')
        self.others_box_wid.setStyleSheet('QWidget { border: 0.5px solid gray;border-radius: 3px;}')
        
    def _ok_hit(self):
        
        # 1. Create environments using defined processing methods
        environments_list = NewSimulationCore().run_processes()        
        
        # 2. Save environments via API / Load them via API as well

        self.close()
        
    def _cancel_hit(self):
        print("Cancel")
        self.close()
        
    def _new_env_hit(self):
        
        # Add to list
        nr = len(self.environments)
        self.environments.append("Environment %s" % nr)        
        self.env_select_cb.addItem(self.environments[-1])
        self.env_select_cb.setCurrentIndex(nr)
        
        # Create new Stacked widget entry
        wid = QWidget()
        self.stacked_env.addWidget(wid)
        self.env_wid["Environment %s" % nr] = wid
        
        lo = QVBoxLayout()

        wid.setLayout(lo)
        self.stacked_env.setCurrentIndex(nr)

    def _env_select_changed(self):
        idx = self.env_select_cb.currentIndex()        
        self.stacked_env.setCurrentIndex(idx)        
        NewSimulationCore().selected_env = self.env_select_cb.currentText()
        self.content_gb.selected_env = self.env_select_cb.currentText()
        
        self._show_env(NewSimulationCore().selected_env, NewSimulationCore().env_map)
        
    def _get_env_elem_by_icon(self, env_elems, move_icon):        
        for elem in env_elems:            
            if str(elem.move_icon) == str(move_icon):
                return elem
        return None 
        
    def _load_gui_name(self, ky, factory):
        try:                                 
            cls = factory.get_class(ky)
            name = cls.GUI_NAME
        except:
            name = ky
        return name
    
    def _load_gui_icon(self, ky, factory):
        try:                                 
            cls = factory.get_class(ky)
            name = cls.GUI_ICON
        except:
            name = os.getcwd() + r'/icons/standard_ecu.png'
        return name

    def _show_env(self, selected_env, env_map):
        
        GBuilder().update_connected(self.content_gb, self.environment_gb.pos(), self.content_gb.pos(), self.content_gb.selected_env)
        
        # Mainwindow
        for chil in self.children():
            if isinstance(chil, DragLabel):
                try:
                    a = self._get_env_elem_by_icon(env_map[selected_env], chil)   
                    
                    if a == None:
                        chil.hide()
                    else:
                        chil.show()
                except:
                    chil.hide()
class EditorWidget(QWidget):

    # Señales
    allFilesClosed = pyqtSignal()

    def __init__(self):
        super(EditorWidget, self).__init__()
        self._recents_files = []

        box = QVBoxLayout(self)
        box.setContentsMargins(0, 0, 0, 0)
        box.setSpacing(0)

        # Combo container
        self.combo = ComboContainer(self)
        box.addWidget(self.combo)

        # Stacked
        self.stack = QStackedWidget()
        box.addWidget(self.stack)

        self.connect(self.combo.combo_file,
                     SIGNAL("currentIndexChanged(int)"), self.change_item)

    def add_widget(self, widget):
        index = self.stack.addWidget(widget)
        if not self.combo.isVisible():
            self.combo.setVisible(True)
        self.stack.setCurrentIndex(index)

    def add_item_combo(self, text):
        self.combo.combo_file.addItem(text)
        self.combo.combo_file.setCurrentIndex(
            self.combo.combo_file.count() - 1)

    def remove_item_combo(self, index):
        self.combo.combo_file.removeItem(index)

    def change_item(self, index):
        self.stack.setCurrentIndex(index)
        self.emit(SIGNAL("currentWidgetChanged(int)"), index)

    def current_widget(self):
        return self.stack.currentWidget()

    def current_index(self):
        return self.stack.currentIndex()

    def widget(self, index):
        return self.stack.widget(index)

    def count(self):
        return self.stack.count()

    def close_file(self):
        self.remove_widget(self.current_widget(), self.current_index())

    def close_file_project(self, widget, index):
        #FIXME: unir con close file
        self.remove_widget(widget, index)

    def close_all(self):
        for index in range(self.count()):
            self.remove_widget(self.current_widget(), 0)

    def editor_modified(self, value):
        weditor = self.current_widget()
        index = self.current_index()
        self.combo.set_modified(weditor, index, value)

    def _add_to_recent(self, filename):
        if filename == 'Untitled':
            return
        if filename not in self._recents_files:
            self._recents_files.append(filename)
            self.emit(SIGNAL("recentFile(QStringList)"), self._recents_files)

    def check_files_not_saved(self):
        value = False
        for index in range(self.count()):
            weditor = self.widget(index)
            value = value or weditor.is_modified
        return value

    def files_not_saved(self):
        files = []
        for index in range(self.count()):
            weditor = self.widget(index)
            if weditor.is_modified:
                files.append(weditor.filename)
        return files

    def opened_files(self):
        files = []
        for index in range(self.count()):
            weditor = self.widget(index)
            path = weditor.filename
            if path == 'Untitled':
                continue
            files.append(path)
        return files

    def remove_widget(self, widget, index):
        if not isinstance(widget, editor.Editor):
            return
        if index != -1:
            self.stack.setCurrentIndex(index)

            flags = QMessageBox.Yes
            flags |= QMessageBox.No
            flags |= QMessageBox.Cancel

            result = QMessageBox.No
            if widget.is_modified:
                result = QMessageBox.question(self, self.tr(
                    "Archivo no guardado!"),
                    self.tr("El archivo <b>{0}</b> "
                            "tiene cambios sin guardar. "
                            "Quieres guardarlos?").format(widget.filename),
                    QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
                if result == QMessageBox.Cancel:
                    return
                elif result == QMessageBox.Yes:
                    self.emit(SIGNAL("saveCurrentFile()"))
            self._add_to_recent(widget.filename)
            self.stack.removeWidget(widget)
            self.emit(SIGNAL("fileClosed(int)"), index)
            self.remove_item_combo(index)
            widget.obj_file.stop_system_watcher()
            if self.current_widget() is not None:
                self.current_widget().setFocus()
            else:
                self.allFilesClosed.emit()

    def add_symbols(self, symbols):
        self.combo.add_symbols_combo(symbols)
Exemple #25
0
class PlotScalesWidget(QWidget):
    plotScaleChanged = pyqtSignal()

    def __init__(self, type_key, title, select_min_time_value=False):
        QWidget.__init__(self)

        self.__type_key = type_key
        self.__type = None

        self.__double_spinner = self.createDoubleSpinner(minimum=-999999999.0,
                                                         maximum=999999999.0)
        self.__integer_spinner = self.createIntegerSpinner(minimum=0,
                                                           maximum=999999999)

        self.__time_map = ReportStepsModel().getList()
        self.__time_index_map = {}
        for index in range(len(self.__time_map)):
            time = self.__time_map[index]
            self.__time_index_map[time] = index

        self.__time_spinner = self.createTimeSpinner(
            select_minimum_value=select_min_time_value)

        layout = QVBoxLayout()
        self.setLayout(layout)

        self.__label = QLabel(title)
        self.__label.setAlignment(Qt.AlignHCenter)

        self.__stack = QStackedWidget()
        self.__stack.setSizePolicy(QSizePolicy(QSizePolicy.Preferred))
        self.__stack.addWidget(self.__integer_spinner)
        self.__stack.addWidget(self.__double_spinner)
        self.__stack.addWidget(self.__time_spinner)

        layout.addWidget(self.__stack)
        layout.addWidget(self.__label)

        self.setLayout(layout)

    def createDoubleSpinner(self, minimum, maximum):
        spinner = QDoubleSpinBox()
        spinner.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        spinner.setMinimumWidth(105)
        spinner.setRange(minimum, maximum)
        spinner.setKeyboardTracking(False)
        spinner.setDecimals(8)

        spinner.editingFinished.connect(self.plotScaleChanged)
        spinner.valueChanged.connect(self.plotScaleChanged)

        return spinner

    def createIntegerSpinner(self, minimum, maximum):
        spinner = QSpinBox()
        spinner.setMinimumWidth(75)
        spinner.setRange(minimum, maximum)
        spinner.setKeyboardTracking(False)

        spinner.editingFinished.connect(self.plotScaleChanged)
        spinner.valueChanged.connect(self.plotScaleChanged)

        return spinner

    def createTimeSpinner(self, select_minimum_value):
        def converter(item):
            return "%s" % (str(item.date()))

        spinner = ListSpinBox(self.__time_map)
        spinner.setMinimumWidth(75)

        if select_minimum_value:
            spinner.setValue(0)

        spinner.valueChanged[int].connect(self.plotScaleChanged)
        spinner.editingFinished.connect(self.plotScaleChanged)
        spinner.setStringConverter(converter)

        return spinner

    def getValue(self):
        if self.__type is int:
            return self.__integer_spinner.value()
        elif self.__type is float:
            return self.__double_spinner.value()
        elif self.__type is CTime:
            index = self.__time_spinner.value()
            return self.__time_map[index]
        else:
            raise TypeError("Unsupported spinner type: %s" % self.__type)

    def setValue(self, value):
        if value is not None:
            if self.__type is int:
                self.__integer_spinner.setValue(int(value))
            elif self.__type is float:
                self.__double_spinner.setValue(value)
            elif self.__type is CTime:
                index = self.__time_index_map[value]
                self.__time_spinner.setValue(index)
            else:
                raise TypeError("Unsupported spinner type: %s" % self.__type)

    def setFontSize(self, size):
        font = self.__double_spinner.font()
        font.setPointSize(size)
        self.__double_spinner.setFont(font)

        font = self.__integer_spinner.font()
        font.setPointSize(size)
        self.__integer_spinner.setFont(font)

        font = self.__time_spinner.font()
        font.setPointSize(size)
        self.__time_spinner.setFont(font)

        font = self.__label.font()
        font.setPointSize(size)
        self.__label.setFont(font)

    def setType(self, spinner_type):
        self.__type = spinner_type
        if spinner_type is int:
            self.__stack.setCurrentWidget(self.__integer_spinner)
        elif spinner_type is float:
            self.__stack.setCurrentWidget(self.__double_spinner)
        elif spinner_type is CTime:
            self.__stack.setCurrentWidget(self.__time_spinner)
        else:
            raise TypeError("Unsupported spinner type: %s" % spinner_type)

    def getType(self):
        return self.__type
Exemple #26
0
class DataSelectionGui(QWidget):
    """
    Manages all GUI elements in the data selection applet.
    This class itself is the central widget and also owns/manages the applet drawer widgets.
    """

    ###########################################
    ### AppletGuiInterface Concrete Methods ###
    ###########################################

    def centralWidget( self ):
        return self

    def appletDrawer( self ):
        return self._drawer

    def menus( self ):
        return []

    def viewerControlWidget(self):
        return self._viewerControlWidgetStack

    def setImageIndex(self, imageIndex):
        if imageIndex is not None:
            self.laneSummaryTableView.selectRow(imageIndex)
            for detailWidget in self._detailViewerWidgets:
                detailWidget.selectRow(imageIndex)

    def stopAndCleanUp(self):
        self._cleaning_up = True
        for editor in self.volumeEditors.values():
            self.viewerStack.removeWidget( editor )
            self._viewerControlWidgetStack.removeWidget( editor.viewerControlWidget() )
            editor.stopAndCleanUp()
        self.volumeEditors.clear()

    def imageLaneAdded(self, laneIndex):
        if len(self.laneSummaryTableView.selectedIndexes()) == 0:
            self.laneSummaryTableView.selectRow(laneIndex)
        
        # We don't have any real work to do because this gui initiated the lane addition in the first place
        if self.guiMode != GuiMode.Batch:
            if(len(self.topLevelOperator.DatasetGroup) != laneIndex+1):
                import warnings
                warnings.warn("DataSelectionGui.imageLaneAdded(): length of dataset multislot out of sync with laneindex [%s != %s + 1]" % (len(self.topLevelOperator.DatasetGroup), laneIndex))

    def imageLaneRemoved(self, laneIndex, finalLength):
        # There's nothing to do here because the GUI already 
        #  handles operator resizes via slot callbacks.
        pass

    def allowLaneSelectionChange(self):
        return False

    ###########################################
    ###########################################

    class UserCancelledError(Exception):
        # This exception type is raised when the user cancels the 
        #  addition of dataset files in the middle of the process somewhere.
        # It isn't an error -- it's used for control flow.
        pass

    def __init__(self, parentApplet, dataSelectionOperator, serializer, instructionText, guiMode=GuiMode.Normal, max_lanes=None, show_axis_details=False):
        """
        Constructor.
        
        :param dataSelectionOperator: The top-level operator.  Must be of type :py:class:`OpMultiLaneDataSelectionGroup`.
        :param serializer: The applet's serializer.  Must be of type :py:class:`DataSelectionSerializer`
        :param instructionText: A string to display in the applet drawer.
        :param guiMode: Either ``GuiMode.Normal`` or ``GuiMode.Batch``.  Currently, there is no difference between normal and batch mode.
        :param max_lanes: The maximum number of lanes that the user is permitted to add to this workflow.  If ``None``, there is no maximum.
        """
        super(DataSelectionGui, self).__init__()
        self._cleaning_up = False
        self.parentApplet = parentApplet
        self._max_lanes = max_lanes
        self._default_h5_volumes = {}
        self.show_axis_details = show_axis_details

        self._viewerControls = QWidget()
        self.topLevelOperator = dataSelectionOperator
        self.guiMode = guiMode
        self.serializer = serializer
        self.threadRouter = ThreadRouter(self)

        self._initCentralUic()
        self._initAppletDrawerUic(instructionText)
        
        self._viewerControlWidgetStack = QStackedWidget(self)

        def handleImageRemove(multislot, index, finalLength):
            # Remove the viewer for this dataset
            datasetSlot = self.topLevelOperator.DatasetGroup[index]
            if datasetSlot in self.volumeEditors.keys():
                editor = self.volumeEditors[datasetSlot]
                self.viewerStack.removeWidget( editor )
                self._viewerControlWidgetStack.removeWidget( editor.viewerControlWidget() )
                editor.stopAndCleanUp()

        self.topLevelOperator.DatasetGroup.notifyRemove( bind( handleImageRemove ) )
        
        opWorkflow = self.topLevelOperator.parent
        assert hasattr(opWorkflow.shell, 'onSaveProjectActionTriggered'), \
            "This class uses the IlastikShell.onSaveProjectActionTriggered function.  Did you rename it?"


    def _initCentralUic(self):
        """
        Load the GUI from the ui file into this class and connect it with event handlers.
        """
        # Load the ui file into this class (find it in our own directory)
        localDir = os.path.split(__file__)[0]+'/'
        uic.loadUi(localDir+"/dataSelection.ui", self)

        self._initTableViews()
        self._initViewerStack()
        self.splitter.setSizes( [150, 850] )

    def _initAppletDrawerUic(self, instructionText):
        """
        Load the ui file for the applet drawer, which we own.
        """
        localDir = os.path.split(__file__)[0]+'/'
        self._drawer = uic.loadUi(localDir+"/dataSelectionDrawer.ui")
        self._drawer.instructionLabel.setText( instructionText )

    def _initTableViews(self):
        self.fileInfoTabWidget.setTabText( 0, "Summary" )
        self.laneSummaryTableView.setModel( DataLaneSummaryTableModel(self, self.topLevelOperator) )
        self.laneSummaryTableView.dataLaneSelected.connect( self.showDataset )
        self.laneSummaryTableView.addFilesRequested.connect( self.addFiles )
        self.laneSummaryTableView.addStackRequested.connect( self.addStack )
        self.laneSummaryTableView.removeLanesRequested.connect( self.handleRemoveLaneButtonClicked )

        # These two helper functions enable/disable an 'add files' button for a given role  
        #  based on the the max lane index for that role and the overall permitted max_lanes
        def _update_button_status(viewer, role_index):
            if self._max_lanes:
                viewer.setEnabled( self._findFirstEmptyLane(role_index) < self._max_lanes )

        def _handle_lane_added( button, role_index, lane_slot, lane_index ):
            def _handle_role_slot_added( role_slot, added_slot_index, *args ):
                if added_slot_index == role_index:
                    role_slot.notifyReady( bind(_update_button_status, button, role_index) )
                    role_slot.notifyUnready( bind(_update_button_status, button, role_index) )
            lane_slot[lane_index].notifyInserted( _handle_role_slot_added )

        self._retained = [] # Retain menus so they don't get deleted
        self._detailViewerWidgets = []
        for roleIndex, role in enumerate(self.topLevelOperator.DatasetRoles.value):
            detailViewer = DatasetDetailedInfoTableView(self)
            detailViewer.setModel(DatasetDetailedInfoTableModel(self,
                self.topLevelOperator, roleIndex))
            self._detailViewerWidgets.append( detailViewer )

            # Button
            detailViewer.addFilesRequested.connect(
                    partial(self.addFiles, roleIndex))
            detailViewer.addStackRequested.connect(
                    partial(self.addStack, roleIndex))
            detailViewer.addRemoteVolumeRequested.connect(
                    partial(self.addDvidVolume, roleIndex))

            # Monitor changes to each lane so we can enable/disable the 'add lanes' button for each tab
            self.topLevelOperator.DatasetGroup.notifyInserted( bind( _handle_lane_added, detailViewer, roleIndex ) )
            self.topLevelOperator.DatasetGroup.notifyRemoved( bind( _update_button_status, detailViewer, roleIndex ) )
            
            # While we're at it, do the same for the buttons in the summary table, too
            self.topLevelOperator.DatasetGroup.notifyInserted( bind( _handle_lane_added, self.laneSummaryTableView.addFilesButtons[roleIndex], roleIndex ) )
            self.topLevelOperator.DatasetGroup.notifyRemoved( bind( _update_button_status, self.laneSummaryTableView.addFilesButtons[roleIndex], roleIndex ) )
            
            # Context menu
            detailViewer.replaceWithFileRequested.connect(
                    partial(self.handleReplaceFile, roleIndex) )
            detailViewer.replaceWithStackRequested.connect(
                    partial(self.addStack, roleIndex) )
            detailViewer.editRequested.connect(
                    partial(self.editDatasetInfo, roleIndex) )
            detailViewer.resetRequested.connect(
                    partial(self.handleClearDatasets, roleIndex) )

            # Drag-and-drop
            detailViewer.addFilesRequestedDrop.connect( partial( self.addFileNames, roleIndex=roleIndex ) )

            # Selection handling
            def showFirstSelectedDataset( _roleIndex, lanes ):
                if lanes:
                    self.showDataset( lanes[0], _roleIndex )
            detailViewer.dataLaneSelected.connect( partial(showFirstSelectedDataset, roleIndex) )

            self.fileInfoTabWidget.insertTab(roleIndex, detailViewer, role)
                
        self.fileInfoTabWidget.currentChanged.connect( self.handleSwitchTabs )
        self.fileInfoTabWidget.setCurrentIndex(0)

    def handleSwitchTabs(self, tabIndex ):
        if tabIndex < len(self._detailViewerWidgets):
            roleIndex = tabIndex # If summary tab is moved to the front, change this line.
            detailViewer = self._detailViewerWidgets[roleIndex]
            selectedLanes = detailViewer.selectedLanes
            if selectedLanes:
                self.showDataset( selectedLanes[0], roleIndex )

    def _initViewerStack(self):
        self.volumeEditors = {}
        self.viewerStack.addWidget( QWidget() )

    def handleRemoveLaneButtonClicked(self):
        """
        The user clicked the "Remove" button.
        Remove the currently selected row(s) from both the GUI and the top-level operator.
        """
        # Figure out which lanes to remove
        selectedIndexes = self.laneSummaryTableView.selectedIndexes()
        rows = set()
        for modelIndex in selectedIndexes:
            rows.add( modelIndex.row() )

        # Don't remove the last row, which is just buttons.
        rows.discard( self.laneSummaryTableView.model().rowCount()-1 )

        # Remove in reverse order so row numbers remain consistent
        for row in reversed(sorted(rows)):
            # Remove lanes from the operator.
            # The table model will notice the changes and update the rows accordingly.
            finalSize = len(self.topLevelOperator.DatasetGroup) - 1
            self.topLevelOperator.DatasetGroup.removeSlot(row, finalSize)
    
    @threadRouted
    def showDataset(self, laneIndex, roleIndex=None):
        if self._cleaning_up:
            return
        if laneIndex == -1:
            self.viewerStack.setCurrentIndex(0)
            return
        
        assert threading.current_thread().name == "MainThread"
        
        if laneIndex >= len(self.topLevelOperator.DatasetGroup):
            return
        datasetSlot = self.topLevelOperator.DatasetGroup[laneIndex]

        # Create if necessary
        if datasetSlot not in self.volumeEditors.keys():
            class DatasetViewer(LayerViewerGui):
                def moveToTop(self, roleIndex):
                    opLaneView = self.topLevelOperatorView
                    if not opLaneView.DatasetRoles.ready():
                        return
                    datasetRoles = opLaneView.DatasetRoles.value
                    if roleIndex >= len(datasetRoles):
                        return
                    roleName = datasetRoles[roleIndex]
                    try:
                        layerIndex = [l.name for l in self.layerstack].index(roleName)
                    except ValueError:
                        return
                    else:
                        self.layerstack.selectRow(layerIndex)
                        self.layerstack.moveSelectedToTop()

                def setupLayers(self):
                    opLaneView = self.topLevelOperatorView
                    if not opLaneView.DatasetRoles.ready():
                        return []
                    layers = []
                    datasetRoles = opLaneView.DatasetRoles.value
                    for roleIndex, slot in enumerate(opLaneView.ImageGroup):
                        if slot.ready():
                            roleName = datasetRoles[roleIndex]
                            layer = self.createStandardLayerFromSlot(slot)
                            layer.name = roleName
                            layers.append(layer)
                    return layers

            opLaneView = self.topLevelOperator.getLane(laneIndex)
            layerViewer = DatasetViewer(self.parentApplet, opLaneView, crosshair=False)
            
            # Maximize the x-y view by default.
            layerViewer.volumeEditorWidget.quadview.ensureMaximized(2)

            self.volumeEditors[datasetSlot] = layerViewer
            self.viewerStack.addWidget( layerViewer )
            self._viewerControlWidgetStack.addWidget( layerViewer.viewerControlWidget() )

        # Show the right one
        viewer = self.volumeEditors[datasetSlot]
        displayedRole = self.fileInfoTabWidget.currentIndex()
        viewer.moveToTop(displayedRole)
        self.viewerStack.setCurrentWidget( viewer )
        self._viewerControlWidgetStack.setCurrentWidget( viewer.viewerControlWidget() )


    def handleReplaceFile(self, roleIndex, startingLane):
        self.addFiles(roleIndex, startingLane)

    def addFiles(self, roleIndex, startingLane=None):
        """
        The user clicked the "Add File" button.
        Ask him to choose a file (or several) and add them to both
          the GUI table and the top-level operator inputs.
        """
        # Find the directory of the most recently opened image file
        mostRecentImageFile = PreferencesManager().get( 'DataSelection', 'recent image' )
        if mostRecentImageFile is not None:
            defaultDirectory = os.path.split(mostRecentImageFile)[0]
        else:
            defaultDirectory = os.path.expanduser('~')

        # Launch the "Open File" dialog
        fileNames = self.getImageFileNamesToOpen(self, defaultDirectory)

        # If the user didn't cancel
        if len(fileNames) > 0:
            PreferencesManager().set('DataSelection', 'recent image', fileNames[0])
            try:
                self.addFileNames(fileNames, roleIndex, startingLane)
            except Exception as ex:
                log_exception( logger )
                QMessageBox.critical(self, "Error loading file", str(ex))

    @classmethod
    def getImageFileNamesToOpen(cls, parent_window, defaultDirectory):
        """
        Launch an "Open File" dialog to ask the user for one or more image files.
        """
        extensions = OpDataSelection.SupportedExtensions
        filter_strs = ["*." + x for x in extensions]
        filters = ["{filt} ({filt})".format(filt=x) for x in filter_strs]
        filt_all_str = "Image files (" + ' '.join(filter_strs) + ')'

        fileNames = []
        
        if ilastik_config.getboolean("ilastik", "debug"):
            # use Qt dialog in debug mode (more portable?)
            file_dialog = QFileDialog(parent_window, "Select Images")
            file_dialog.setOption(QFileDialog.DontUseNativeDialog, True)
            # do not display file types associated with a filter
            # the line for "Image files" is too long otherwise
            file_dialog.setFilters([filt_all_str] + filters)
            file_dialog.setNameFilterDetailsVisible(False)
            # select multiple files
            file_dialog.setFileMode(QFileDialog.ExistingFiles)
            file_dialog.setDirectory( defaultDirectory )

            if file_dialog.exec_():
                fileNames = file_dialog.selectedFiles()
        else:
            # otherwise, use native dialog of the present platform
            fileNames = QFileDialog.getOpenFileNames(parent_window, "Select Images", defaultDirectory, filt_all_str)
        # Convert from QtString to python str
        fileNames = map(encode_from_qstring, fileNames)
        return fileNames

    def _findFirstEmptyLane(self, roleIndex):
        opTop = self.topLevelOperator
        
        # Determine the number of files this role already has
        # Search for the last valid value.
        firstNewLane = 0
        for laneIndex, slot in reversed(zip(range(len(opTop.DatasetGroup)), opTop.DatasetGroup)):
            if slot[roleIndex].ready():
                firstNewLane = laneIndex+1
                break
        return firstNewLane

    def addFileNames(self, fileNames, roleIndex, startingLane=None, rois=None):
        """
        Add the given filenames to both the GUI table and the top-level operator inputs.
        If startingLane is None, the filenames will be *appended* to the role's list of files.
        
        If rois is provided, it must be a list of (start,stop) tuples (one for each fileName)
        """
        # What lanes will we touch?
        startingLane, endingLane = self._determineLaneRange(fileNames, roleIndex, startingLane)
        if startingLane is None:
            # Something went wrong.
            return

        # If we're only adding new lanes, NOT modifying existing lanes...
        adding_only = startingLane == len(self.topLevelOperator)

        # Create a list of DatasetInfos
        try:
            infos = self._createDatasetInfos(roleIndex, fileNames, rois)
        except DataSelectionGui.UserCancelledError:
            return
        
        # If no exception was thrown so far, set up the operator now
        loaded_all = self._configureOpWithInfos(roleIndex, startingLane, endingLane, infos)
        
        if loaded_all:
            # Now check the resulting slots.
            # If they should be copied to the project file, say so.
            self._reconfigureDatasetLocations(roleIndex, startingLane, endingLane)
    
            self._checkDataFormatWarnings(roleIndex, startingLane, endingLane)
    
            # If we succeeded in adding all images, show the first one.
            self.showDataset(startingLane, roleIndex)

        # Notify the workflow that we just added some new lanes.
        if adding_only:
            workflow = self.parentApplet.topLevelOperator.parent
            workflow.handleNewLanesAdded()

        # Notify the workflow that something that could affect applet readyness has occurred.
        self.parentApplet.appletStateUpdateRequested.emit()

        self.updateInternalPathVisiblity()

    def _determineLaneRange(self, fileNames, roleIndex, startingLane=None):
        """
        Determine which lanes should be configured if the user wants to add the 
            given fileNames to the specified role, starting at startingLane.
        If startingLane is None, assume the user wants to APPEND the 
            files to the role's slots.
        """
        if startingLane is None or startingLane == -1:
            startingLane = len(self.topLevelOperator.DatasetGroup)
            endingLane = startingLane+len(fileNames)-1
        else:
            assert startingLane < len(self.topLevelOperator.DatasetGroup)
            max_files = len(self.topLevelOperator.DatasetGroup) - \
                    startingLane
            if len(fileNames) > max_files:
                msg = "You selected {num_selected} files for {num_slots} "\
                      "slots. To add new files use the 'Add new...' option "\
                      "in the context menu or the button in the last row."\
                              .format(num_selected=len(fileNames),
                                      num_slots=max_files)
                QMessageBox.critical( self, "Too many files", msg )
                return (None, None)
            endingLane = min(startingLane+len(fileNames)-1,
                    len(self.topLevelOperator.DatasetGroup))
            
        if self._max_lanes and endingLane >= self._max_lanes:
            msg = "You may not add more than {} file(s) to this workflow.  Please try again.".format( self._max_lanes )
            QMessageBox.critical( self, "Too many files", msg )
            return (None, None)

        return (startingLane, endingLane)

    def _createDatasetInfos(self, roleIndex, filePaths, rois):
        """
        Create a list of DatasetInfos for the given filePaths and rois
        rois may be None, in which case it is ignored.
        """
        if rois is None:
            rois = [None]*len(filePaths)
        assert len(rois) == len(filePaths)

        infos = []
        for filePath, roi in zip(filePaths, rois):
            info = self._createDatasetInfo(roleIndex, filePath, roi)
            infos.append(info)
        return infos

    def _createDatasetInfo(self, roleIndex, filePath, roi):
        """
        Create a DatasetInfo object for the given filePath and roi.
        roi may be None, in which case it is ignored.
        """
        cwd = self.topLevelOperator.WorkingDirectory.value
        datasetInfo = DatasetInfo(filePath, cwd=cwd)
        datasetInfo.subvolume_roi = roi # (might be None)
                
        absPath, relPath = getPathVariants(filePath, cwd)
        
        # If the file is in a totally different tree from the cwd,
        # then leave the path as absolute.  Otherwise, override with the relative path.
        if relPath is not None and len(os.path.commonprefix([cwd, absPath])) > 1:
            datasetInfo.filePath = relPath
            
        h5Exts = ['.ilp', '.h5', '.hdf5']
        if os.path.splitext(datasetInfo.filePath)[1] in h5Exts:
            datasetNames = self.getPossibleInternalPaths( absPath )
            if len(datasetNames) == 0:
                raise RuntimeError("HDF5 file %s has no image datasets" % datasetInfo.filePath)
            elif len(datasetNames) == 1:
                datasetInfo.filePath += str(datasetNames[0])
            else:
                # If exactly one of the file's datasets matches a user's previous choice, use it.
                if roleIndex not in self._default_h5_volumes:
                    self._default_h5_volumes[roleIndex] = set()
                previous_selections = self._default_h5_volumes[roleIndex]
                possible_auto_selections = previous_selections.intersection(datasetNames)
                if len(possible_auto_selections) == 1:
                    datasetInfo.filePath += str(list(possible_auto_selections)[0])
                else:
                    # Ask the user which dataset to choose
                    dlg = H5VolumeSelectionDlg(datasetNames, self)
                    if dlg.exec_() == QDialog.Accepted:
                        selected_index = dlg.combo.currentIndex()
                        selected_dataset = str(datasetNames[selected_index])
                        datasetInfo.filePath += selected_dataset
                        self._default_h5_volumes[roleIndex].add( selected_dataset )
                    else:
                        raise DataSelectionGui.UserCancelledError()

        # Allow labels by default if this gui isn't being used for batch data.
        datasetInfo.allowLabels = ( self.guiMode == GuiMode.Normal )
        return datasetInfo

    def _configureOpWithInfos(self, roleIndex, startingLane, endingLane, infos):
        """
        Attempt to configure the specified role and lanes of the 
        top-level operator with the given DatasetInfos.
        
        Returns True if all lanes were configured successfully, or False if something went wrong.
        """
        opTop = self.topLevelOperator
        originalSize = len(opTop.DatasetGroup)

        # Resize the slot if necessary            
        if len( opTop.DatasetGroup ) < endingLane+1:
            opTop.DatasetGroup.resize( endingLane+1 )
        
        # Configure each subslot
        for laneIndex, info in zip(range(startingLane, endingLane+1), infos):
            try:
                self.topLevelOperator.DatasetGroup[laneIndex][roleIndex].setValue( info )
            except DatasetConstraintError as ex:
                return_val = [False]
                # Give the user a chance to fix the problem
                self.handleDatasetConstraintError(info, info.filePath, ex, roleIndex, laneIndex, return_val)
                if not return_val[0]:
                    # Not successfully repaired.  Roll back the changes and give up.
                    opTop.DatasetGroup.resize( originalSize )
                    return False
            except OpDataSelection.InvalidDimensionalityError as ex:
                    opTop.DatasetGroup.resize( originalSize )
                    QMessageBox.critical( self, "Dataset has different dimensionality", ex.message )
                    return False
            except Exception as ex:
                msg = "Wasn't able to load your dataset into the workflow.  See error log for details."
                log_exception( logger, msg )
                QMessageBox.critical( self, "Dataset Load Error", msg )
                opTop.DatasetGroup.resize( originalSize )
                return False
        
        return True

    def _reconfigureDatasetLocations(self, roleIndex, startingLane, endingLane):
        """
        Check the metadata for the given slots.  
        If the data is stored a format that is poorly optimized for 3D access, 
        then configure it to be copied to the project file.
        Finally, save the project if we changed something. 
        """
        save_needed = False
        opTop = self.topLevelOperator
        for lane_index in range(startingLane, endingLane+1):
            output_slot = opTop.ImageGroup[lane_index][roleIndex]
            if output_slot.meta.prefer_2d and 'z' in output_slot.meta.axistags:
                shape = numpy.array(output_slot.meta.shape)
                total_volume = numpy.prod(shape)
                
                # Only copy to the project file if the total volume is reasonably small
                if total_volume < 0.5e9:
                    info_slot = opTop.DatasetGroup[lane_index][roleIndex]
                    info = info_slot.value
                    info.location = DatasetInfo.Location.ProjectInternal
                    info_slot.setValue( info, check_changed=False )
                    save_needed = True

        if save_needed:
            logger.info("Some of your data cannot be accessed efficiently in 3D in its current format."
                        "  It will now be copied to the project file.")
            opWorkflow = self.topLevelOperator.parent
            opWorkflow.shell.onSaveProjectActionTriggered()

    def _checkDataFormatWarnings(self, roleIndex, startingLane, endingLane):
        warn_needed = False
        opTop = self.topLevelOperator
        for lane_index in range(startingLane, endingLane+1):
            output_slot = opTop.ImageGroup[lane_index][roleIndex]
            if output_slot.meta.inefficient_format:
                warn_needed = True

        if warn_needed:        
            QMessageBox.warning( self, "Inefficient Data Format", 
                              "Your data cannot be accessed efficiently in its current format.  "
                              "Check the console output for details.\n"
                              "(For HDF5 files, be sure to enable chunking on your dataset.)" )

    @threadRouted
    def handleDatasetConstraintError(self, info, filename, ex, roleIndex, laneIndex, return_val=[False]):
        msg = "Can't use default properties for dataset:\n\n" + \
              filename + "\n\n" + \
              "because it violates a constraint of the {} applet.\n\n".format( ex.appletName ) + \
              ex.message + "\n\n" + \
              "Please enter valid dataset properties to continue."
        QMessageBox.warning( self, "Dataset Needs Correction", msg )
        
        # The success of this is 'returned' via our special out-param
        # (We can't return a value from this func because it is @threadRouted.
        successfully_repaired = self.repairDatasetInfo( info, roleIndex, laneIndex )
        return_val[0] = successfully_repaired

    def repairDatasetInfo(self, info, roleIndex, laneIndex):
        """Open the dataset properties editor and return True if the new properties are acceptable."""
        defaultInfos = {}
        defaultInfos[laneIndex] = info
        editorDlg = DatasetInfoEditorWidget(self, self.topLevelOperator, roleIndex, [laneIndex], defaultInfos, show_axis_details=self.show_axis_details)
        dlg_state = editorDlg.exec_()
        return ( dlg_state == QDialog.Accepted )

    @classmethod
    def getPossibleInternalPaths(cls, absPath, min_ndim=3, max_ndim=5):
        datasetNames = []
        # Open the file as a read-only so we can get a list of the internal paths
        with h5py.File(absPath, 'r') as f:
            # Define a closure to collect all of the dataset names in the file.
            def accumulateDatasetPaths(name, val):
                if type(val) == h5py._hl.dataset.Dataset and min_ndim <= len(val.shape) <= max_ndim:
                    datasetNames.append( '/' + name )
            # Visit every group/dataset in the file
            f.visititems(accumulateDatasetPaths)
        return datasetNames

    def addStack(self, roleIndex, laneIndex):
        """
        The user clicked the "Import Stack Files" button.
        """
        stackDlg = StackFileSelectionWidget(self)
        stackDlg.exec_()
        if stackDlg.result() != QDialog.Accepted :
            return
        files = stackDlg.selectedFiles
        sequence_axis = stackDlg.sequence_axis
        if len(files) == 0:
            return

        cwd = self.topLevelOperator.WorkingDirectory.value
        info = DatasetInfo(os.path.pathsep.join(files), cwd=cwd)

        originalNumLanes = len(self.topLevelOperator.DatasetGroup)

        if laneIndex is None or laneIndex == -1:
            laneIndex = len(self.topLevelOperator.DatasetGroup)
        if len(self.topLevelOperator.DatasetGroup) < laneIndex+1:
            self.topLevelOperator.DatasetGroup.resize(laneIndex+1)

        def importStack():
            self.parentApplet.busy = True
            self.parentApplet.appletStateUpdateRequested.emit()

            # Serializer will update the operator for us, which will propagate to the GUI.
            try:
                self.serializer.importStackAsLocalDataset( info, sequence_axis )
                try:
                    self.topLevelOperator.DatasetGroup[laneIndex][roleIndex].setValue(info)
                except DatasetConstraintError as ex:
                    # Give the user a chance to repair the problem.
                    filename = files[0] + "\n...\n" + files[-1]
                    return_val = [False]
                    self.handleDatasetConstraintError( info, filename, ex, roleIndex, laneIndex, return_val )
                    if not return_val[0]:
                        # Not successfully repaired.  Roll back the changes and give up.
                        self.topLevelOperator.DatasetGroup.resize(originalNumLanes)
            finally:
                self.parentApplet.busy = False
                self.parentApplet.appletStateUpdateRequested.emit()

        req = Request( importStack )
        req.notify_finished( lambda result: self.showDataset(laneIndex, roleIndex) )
        req.notify_failed( partial(self.handleFailedStackLoad, files, originalNumLanes ) )
        req.submit()

    @threadRouted
    def handleFailedStackLoad(self, files, originalNumLanes, exc, exc_info):
        msg = "Failed to load stack due to the following error:\n{}".format( exc )
        msg += "\nAttempted stack files were:\n"
        msg += "\n".join(files)
        log_exception( logger, msg, exc_info )
        QMessageBox.critical(self, "Failed to load image stack", msg)
        self.topLevelOperator.DatasetGroup.resize(originalNumLanes)

    def handleClearDatasets(self, roleIndex, selectedRows):
        for row in selectedRows:
            self.topLevelOperator.DatasetGroup[row][roleIndex].disconnect()

        # Remove all operators that no longer have any connected slots        
        laneIndexes = range( len(self.topLevelOperator.DatasetGroup) )
        for laneIndex, multislot in reversed(zip(laneIndexes, self.topLevelOperator.DatasetGroup)):
            any_ready = False
            for slot in multislot:
                any_ready |= slot.ready()
            if not any_ready:
                self.topLevelOperator.DatasetGroup.removeSlot( laneIndex, len(self.topLevelOperator.DatasetGroup)-1 )

        # Notify the workflow that something that could affect applet readyness has occurred.
        self.parentApplet.appletStateUpdateRequested.emit()

    def editDatasetInfo(self, roleIndex, laneIndexes):
        editorDlg = DatasetInfoEditorWidget(self, self.topLevelOperator, roleIndex, laneIndexes, show_axis_details=self.show_axis_details)
        editorDlg.exec_()
        self.parentApplet.appletStateUpdateRequested.emit()

    def updateInternalPathVisiblity(self):
        for view in self._detailViewerWidgets:
            model = view.model()
            view.setColumnHidden(DatasetDetailedInfoColumn.InternalID,
                                 not model.hasInternalPaths())
    
    def addDvidVolume(self, roleIndex, laneIndex):
        # TODO: Provide list of recently used dvid hosts, loaded from user preferences
        recent_hosts_pref = PreferencesManager.Setting("DataSelection", "Recent DVID Hosts")
        recent_hosts = recent_hosts_pref.get()
        if not recent_hosts:
            recent_hosts = ["localhost:8000"]
        recent_hosts = filter(lambda h: h, recent_hosts)
            
        from dvidDataSelectionBrowser import DvidDataSelectionBrowser
        browser = DvidDataSelectionBrowser(recent_hosts, parent=self)
        if browser.exec_() == DvidDataSelectionBrowser.Rejected:
            return

        if None in browser.get_selection():
            QMessageBox.critical("Couldn't use your selection.")
            return

        rois = None
        hostname, dset_uuid, volume_name, uuid = browser.get_selection()
        dvid_url = 'http://{hostname}/api/node/{uuid}/{volume_name}'.format( **locals() )
        subvolume_roi = browser.get_subvolume_roi()

        # Relocate host to top of 'recent' list, and limit list to 10 items.
        try:
            i = recent_hosts.index(recent_hosts)
            del recent_hosts[i]
        except ValueError:
            pass
        finally:
            recent_hosts.insert(0, hostname)        
            recent_hosts = recent_hosts[:10]

        # Save pref
        recent_hosts_pref.set(recent_hosts)

        if subvolume_roi is None:
            self.addFileNames([dvid_url], roleIndex, laneIndex)
        else:
            # In ilastik, we display the dvid volume axes in C-order, despite the dvid convention of F-order
            # Transpose the subvolume roi to match
            # (see implementation of OpDvidVolume)
            start, stop = subvolume_roi
            start = tuple(reversed(start))
            stop = tuple(reversed(stop))
            self.addFileNames([dvid_url], roleIndex, laneIndex, [(start, stop)])
Exemple #27
0
class SimulationPanel(QWidget):

    def __init__(self):
        QWidget.__init__(self)

        layout = QVBoxLayout()

        self._simulation_mode_combo = QComboBox()
        addHelpToWidget(self._simulation_mode_combo, "run/simulation_mode")

        self._simulation_mode_combo.currentIndexChanged.connect(self.toggleSimulationMode)

        simulation_mode_layout = QHBoxLayout()
        simulation_mode_layout.addSpacing(10)
        simulation_mode_layout.addWidget(QLabel("Simulation mode:"), 0, Qt.AlignVCenter)
        simulation_mode_layout.addWidget(self._simulation_mode_combo, 0, Qt.AlignVCenter)

        simulation_mode_layout.addSpacing(20)

        self.run_button = QToolButton()
        self.run_button.setIconSize(QSize(32, 32))
        self.run_button.setText("Start Simulation")
        self.run_button.setIcon(resourceIcon("ide/gear_in_play"))
        self.run_button.clicked.connect(self.runSimulation)
        self.run_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        addHelpToWidget(self.run_button, "run/start_simulation")

        simulation_mode_layout.addWidget(self.run_button)
        simulation_mode_layout.addStretch(1)

        layout.addSpacing(5)
        layout.addLayout(simulation_mode_layout)
        layout.addSpacing(10)

        self._simulation_stack = QStackedWidget()
        self._simulation_stack.setLineWidth(1)
        self._simulation_stack.setFrameStyle(QFrame.StyledPanel)

        layout.addWidget(self._simulation_stack)

        self._simulation_widgets = OrderedDict()
        """ :type: OrderedDict[BaseRunModel,SimulationConfigPanel]"""

        self.addSimulationConfigPanel(EnsembleExperimentPanel())
        self.addSimulationConfigPanel(EnsembleSmootherPanel())
        self.addSimulationConfigPanel(IteratedEnsembleSmootherPanel(advanced_option=True))
        self.addSimulationConfigPanel(MultipleDataAssimilationPanel())

        self.setLayout(layout)


    def addSimulationConfigPanel(self, panel):
        assert isinstance(panel, SimulationConfigPanel)

        panel.toggleAdvancedOptions(False)
        self._simulation_stack.addWidget(panel)

        simulation_model = panel.getSimulationModel()

        self._simulation_widgets[simulation_model] = panel

        if not panel.is_advanced_option:
            self._simulation_mode_combo.addItem(str(simulation_model), simulation_model)

        panel.simulationConfigurationChanged.connect(self.validationStatusChanged)


    def getActions(self):
        return []

    def toggleAdvancedOptions(self, show_advanced):
        current_model = self.getCurrentSimulationModel()

        self._simulation_mode_combo.clear()

        for model, panel in self._simulation_widgets.iteritems():
            if show_advanced or not panel.is_advanced_option:
                self._simulation_mode_combo.addItem(str(model), model)

        old_index = self._simulation_mode_combo.findText(str(current_model))
        self._simulation_mode_combo.setCurrentIndex(old_index if old_index > -1 else 0)

    def toggleAdvancedMode(self, show_advanced):
        for panel in self._simulation_widgets.values():
            panel.toggleAdvancedOptions(show_advanced)

        self.toggleAdvancedOptions(show_advanced)

    def getCurrentSimulationModel(self):
        data = self._simulation_mode_combo.itemData(self._simulation_mode_combo.currentIndex(), Qt.UserRole)
        return data.toPyObject()

    def getSimulationArguments(self):
        """ @rtype: dict[str,object]"""
        simulation_widget = self._simulation_widgets[self.getCurrentSimulationModel()]
        return simulation_widget.getSimulationArguments()


    def runSimulation(self):
        case_name = getCurrentCaseName()
        message = "Are you sure you want to use case '%s' for initialization of the initial ensemble when running the simulations?" % case_name
        start_simulations = QMessageBox.question(self, "Start simulations?", message, QMessageBox.Yes | QMessageBox.No )

        if start_simulations == QMessageBox.Yes:
            run_model = self.getCurrentSimulationModel()
            arguments = self.getSimulationArguments()
            dialog = RunDialog(run_model, arguments, self)
            dialog.startSimulation()
            dialog.exec_()

            ERT.emitErtChange() # simulations may have added new cases.


    def toggleSimulationMode(self):
        current_model = self.getCurrentSimulationModel()
        if current_model is not None:
            widget = self._simulation_widgets[self.getCurrentSimulationModel()]
            self._simulation_stack.setCurrentWidget(widget)
            self.validationStatusChanged()


    def validationStatusChanged(self):
        widget = self._simulation_widgets[self.getCurrentSimulationModel()]
        self.run_button.setEnabled(widget.isConfigurationValid())
Exemple #28
0
class DigitalerRoboter(QWidget):

    'Signale definieren'
    signal_animation_aktiv = Signal(bool)
    signal_koordinaten = Signal(int, int, int)

    'Methode __init__ '

    def __init__(self, parent=None):

        'Vererbung aller Attribute und Methoden von QWidget'
        super(DigitalerRoboter, self).__init__(parent)

        'Zeichenflächen mit Koordinatensystem instanziieren'

        'Hintergrundfarbe der Zeichenflächen festlegen'
        farbe_zeichenflaeche = '#b5afb5'

        'Zeichenfläche mit Koordinatensystem instanziieren - Draufsicht'
        self.zeichenflaeche_draufsicht = QPlotWidget()

        #Hintergrundfarbe festlegen
        self.zeichenflaeche_draufsicht.figure.set_facecolor( \
        farbe_zeichenflaeche)

        #Koordinatensystem an die Zeichenfläche anpassen
        self.zeichenflaeche_draufsicht.figure.tight_layout()

        #gleiche Skalierung der Achsen des Koordinatensystems
        self.zeichenflaeche_draufsicht.axes.set_aspect('equal')

        #Grenzen des Koordinatensystems festlegen
        self.zeichenflaeche_draufsicht.axes.set_xlim([-650, 650])
        self.zeichenflaeche_draufsicht.axes.set_ylim([-225, 625])

        #Achsen des Koordinatensystems und Beschriftung ausblenden
        self.zeichenflaeche_draufsicht.axes.axis('off')

        #Zeichenfläche deaktivieren
        self.zeichenflaeche_draufsicht.setEnabled(False)

        'Zeichenfläche mit Koordinatensystem instanziieren - Seitenansicht'
        self.zeichenflaeche_seitenansicht = QPlotWidget()

        #Hintergrundfarbe festlegen
        self.zeichenflaeche_seitenansicht.figure.set_facecolor( \
        farbe_zeichenflaeche)

        #Koordinatensystem an die Zeichenfläche anpassen
        self.zeichenflaeche_seitenansicht.figure.tight_layout()

        #gleiche Skalierung der Achsen des Koordinatensystems
        self.zeichenflaeche_seitenansicht.axes.set_aspect('equal')

        #Grenzen des Koordinatensystems festlegen
        self.zeichenflaeche_seitenansicht.axes.set_xlim(-250, 650)
        self.zeichenflaeche_seitenansicht.axes.set_ylim([-225, 625])

        #Achsen des Koordinatensystems und Beschriftung ausblenden
        self.zeichenflaeche_seitenansicht.axes.axis('off')

        #Zeichenfläche deaktivieren
        self.zeichenflaeche_seitenansicht.setEnabled(False)

        'Zeichenfläche mit Koordinatensystem instanziieren - Greifer'
        self.zeichenflaeche_ansicht_greifer = QPlotWidget()

        #Hintergrundfarbe festlegen
        self.zeichenflaeche_ansicht_greifer.figure.set_facecolor( \
        farbe_zeichenflaeche)

        #Koordinatensystem an die Zeichenfläche anpassen
        self.zeichenflaeche_ansicht_greifer.figure.tight_layout()

        #gleiche Skalierung der Achsen des Koordinatensystems
        self.zeichenflaeche_ansicht_greifer.axes.set_aspect('equal')

        #Grenzen des Koordinatensystems festlegen
        self.zeichenflaeche_ansicht_greifer.axes.set_xlim([-350, 350])
        self.zeichenflaeche_ansicht_greifer.axes.set_ylim([-350, 350])

        #Achsen des Koordinatensystems und Beschriftung ausblenden
        self.zeichenflaeche_ansicht_greifer.axes.axis('off')

        #Zeichenfläche deaktivieren
        self.zeichenflaeche_ansicht_greifer.setEnabled(False)
        '''Die Zeichenflächen werden gestapelt, da immer nur eine
        Ansicht angezeigt werden kann.'''

        'QStackedWidget-Objekt instanziieren'
        self.ansicht = QStackedWidget()
        #Draufischt: CurrentIndex(0)
        self.ansicht.addWidget(self.zeichenflaeche_draufsicht)
        #Seitenansicht: CurrentIndex(1)
        self.ansicht.addWidget(self.zeichenflaeche_seitenansicht)
        #Greifer: CurrentIndex(2)
        self.ansicht.addWidget(self.zeichenflaeche_ansicht_greifer)

        'Layout festlegen'
        layout = QHBoxLayout()
        layout.addWidget(self.ansicht)
        self.setLayout(layout)
        '''Lage und Orientierung des Werkzeugkoordinatensystem sowie die
        Öffnungsradien des Greifers in Ausgangsposition laden'''

        'Denavit-Hartenberg-Parameter laden'
        (d1, d2, d3, d4, d5, a1, a2, a3, a4, a5, \
        alpha1, alpha2, alpha3, alpha4, alpha5) = denavit_hartenberg()
        self.a3 = a3

        'Orientierung des Werkzeugkoordinatensystems laden'
        self.x5in0, self.y5in0, self.z5in0 = \
        self.orientierung_laden('programmEin')

        'Lagevektor des Werkzeugkoordinatensystems laden'
        self.P05in0 = self.lage_laden('programmEin')

        'Öffnungsradien des Greifers laden'
        self.r6, self.r7 = self.greifer_laden('programmEin')

        'Mittelpunktkoordinaten der Punkte berechnen'
        self.modellrechnung()

        'Methode animation_anzeigen aufrufen'
        self.animation_anzeigen()

    '''Methode animation_anzeigen - Die Methode dient dem Instanziieren
    der drei Ansichten. Jeder Animation wird die entsprechenden Zeichen-
    fläche als Parent und die Mittelpunktkoordinaten der Circle-Objekte
    übergeben. Weiter werden die Signale mit den Slots verbunden.'''

    def animation_anzeigen(self):

        'DraufsichtRoboter-Objekt instanziieren'
        self.animation_draufsicht = DraufsichtRoboter( \
        self.zeichenflaeche_draufsicht, \
        self.xP1in0, self.yP1in0, self.xP2in0, self.yP2in0, \
        self.xP4in0, self.yP4in0, self.xP5in0, self.yP5in0)

        'Signal und Slot verbinden'
        self.animation_draufsicht.xy_neu.connect( \
        self.koordinaten_draufsicht)

        'SeitenansichtRoboter-Objekt instanziieren'
        self.animation_seitenansicht = SeitenansichtRoboter( \
        self.zeichenflaeche_seitenansicht, \
        self.xP1in1, self.yP1in1, self.xP2in1, self.yP2in1, self.xP3in1, \
        self.yP3in1, self.xP4in1, self.yP4in1, self.xP5in1, self.yP5in1)

        'Signal und Slot verbinden'
        self.animation_seitenansicht.xy_neu.connect( \
        self.koordinaten_seitenansicht)

        'RueckansichtGreifer-Objekt instanziieren'
        self.animation_greifer = RueckansichtGreifer( \
        self.zeichenflaeche_ansicht_greifer, \
        self.xP6in4z, self.yP6in4z, self.xP7in4z, self.yP7in4z)

        'Signal und Slot verbinden'
        self.animation_greifer.xy_neu.connect( \
        self.koordinaten_greifer)

    '''Methode animation_aktivieren - Die Methode aktiviert die Zeichen-
    flächen. In Folge kann der digitale Roboter durch Verschieben der 
    Punkte bewegt werden. Die bewegbaren Punkte erscheinen in Farbe.'''

    def animation_aktivieren(self, b):

        'Zeichenflächen aktivieren'
        self.zeichenflaeche_draufsicht.setEnabled(b)
        self.zeichenflaeche_seitenansicht.setEnabled(b)
        self.zeichenflaeche_ansicht_greifer.setEnabled(b)

        'Einfärben der bewegbaren Punkte'
        self.animation_draufsicht.punkte_faerben(b)
        self.animation_seitenansicht.punkte_faerben(b)
        self.animation_greifer.punkte_faerben(b)

    '''Methode animation_aktualisieren - Die Methode ermöglicht das
    Aktualisieren des digitalen Roboters. Dabei sind die Lage (Zeilenvektor
    vom Format 1x3) und Orientierung (Zeilenvektor Format 1x9) des Werkzeug-
    koordinatensystems in 0-Koordinaten sowie der Öffnungsradius des Greifers 
    (Zeilenvektor vom Format 1x1) zu übergeben.'''

    def animation_aktualisieren(self, orientierung, lage, greifer):

        'Orientierung des Werkzeugkoordinatensystems'

        'Einheitsvektoren zuordnen'
        x5in0, y5in0, z5in0 = hsplit(orientierung, 3)  #Format 1x3

        'Format anpassen'
        x5in0 = x5in0.transpose()  #Format 3x1
        self.x5in0 = vstack((x5in0, array([[0]])))  #Format 4x1
        y5in0 = y5in0.transpose()  #Format 3x1
        self.y5in0 = vstack((y5in0, array([[0]])))  #Format 4x1
        z5in0 = z5in0.transpose()  #Format 3x1
        self.z5in0 = vstack((z5in0, array([[0]])))  #Format 4x1

        'Lage des Werkzeugkoordinatensystems'

        'Lagevektor zuordnen und Format anpassen'
        P05in0 = lage  #Format 1x3
        P05in0 = P05in0.transpose()  #Format 3x1
        self.P05in0 = vstack((P05in0, array([[1]])))  #Format 4x1

        'Öffnungsradien des Greifers zuordnen und Datentyp ändern'
        self.r6 = float(greifer)
        self.r7 = self.r6

        'Mittelpunktkoordinaten der Circle-Objekte berechnen'
        self.modellrechnung()

        'Draufsicht aktualisieren'
        self.animation_draufsicht.ansicht_aktualisieren( \
        self.xP2in0, self.yP2in0, self.xP4in0, \
        self.yP4in0, self.xP5in0, self.yP5in0)

        'Seitenansicht aktualisieren'
        self.animation_seitenansicht.ansicht_aktualisieren(
        self.xP2in1, self.yP2in1, self.xP3in1, self.yP3in1, \
        self.xP4in1, self.yP4in1, self.xP5in1, self.yP5in1)

        'Greifer aktualisieren'
        self.animation_greifer.ansicht_aktualisieren( \
        self.xP6in4z, self.yP6in4z, self.xP7in4z, self.yP7in4z)

        'Lagekoordinaten des Werkzeugkoordinatensystems'
        x5in0 = int(self.xP5in0)
        y5in0 = int(self.yP5in0)
        z5in0 = int(self.zP5in0)

        'Signal zum Aktualisieren der Lagekoordinaten senden'
        self.signal_koordinaten.emit(x5in0, y5in0, z5in0)

    '''Methode animation_zuruecksetzen - Die Methode setzt den digitalen 
    Roboter auf die Ausgangsposition zurück. Weiter werden die Bewegungs-
    möglichkeit deaktiviert und die Punkte entfärbt.'''

    def animation_zuruecksetzen(self, b):

        if b == True:

            'Orientierung des Werkzeugkoordinatensystems laden'
            self.x5in0, self.y5in0, self.z5in0 = self.orientierung_laden( \
            'programmEin')

            'Lagevektor des Werkzeugkoordinatensystems laden'
            self.P05in0 = self.lage_laden('programmEin')

            'Öffnungsradien des Greifers laden'
            self.r6, self.r7 = self.greifer_laden('programmEin')

            'Mittelpunktkoordinaten der Punkte berechnen'
            self.modellrechnung()

            'Draufsicht aktualisieren'
            self.animation_draufsicht.ansicht_aktualisieren( \
            self.xP2in0, self.yP2in0, self.xP4in0, \
            self.yP4in0, self.xP5in0, self.yP5in0)

            'Seitenansicht aktualisieren'
            self.animation_seitenansicht.ansicht_aktualisieren(
            self.xP2in1, self.yP2in1, self.xP3in1, self.yP3in1, \
            self.xP4in1, self.yP4in1, self.xP5in1, self.yP5in1)

            'Greifer aktualisieren'
            self.animation_greifer.ansicht_aktualisieren( \
            self.xP6in4z, self.yP6in4z, self.xP7in4z, self.yP7in4z)

            'Geist ausblenden und Koordinaten aktualisieren'
            self.geisterstunde(False)

            'Entfärben der bewegbaren Punkte'
            self.animation_draufsicht.punkte_faerben(False)
            self.animation_seitenansicht.punkte_faerben(False)
            self.animation_greifer.punkte_faerben(False)

            'Lagekoordinaten des Werkzeugkoordinatensystems'
            x5in0 = int(self.xP5in0)
            y5in0 = int(self.yP5in0)
            z5in0 = int(self.zP5in0)

            'Signal zum Aktualisieren der Lagekoordinaten senden'
            self.signal_koordinaten.emit(x5in0, y5in0, z5in0)

    '''Methode ansicht_wechseln - Die Methode ermöglicht den Wechsel 
    zwischen den drei Ansichten.'''

    def ansicht_wechseln(self, index):

        self.ansicht.setCurrentIndex(index)

    '''Methode bilder_aufnehmen - Die Methode ermöglicht die Aufnahme und
    das Abspeichern von Bildern der Drauf- und Seitenansicht.'''

    def bilder_aufnehmen(self):

        'Ansichten zentrieren'
        self.zeichenflaeche_draufsicht.axes.set_xlim([-625, 625])
        self.zeichenflaeche_draufsicht.axes.set_ylim([-325, 525])
        self.zeichenflaeche_seitenansicht.axes.set_xlim(-525, 725)
        self.zeichenflaeche_seitenansicht.axes.set_ylim([-325, 525])

        'Arbeitsverzeichnis'
        workdir = getcwd()

        'Dateiname und Pfad'
        dateiname = 'bild_draufsicht.svg'
        dir = path.join(workdir, 'speicher', 'bildspeicher', dateiname)

        'Draufsicht als Bild speichern'
        self.zeichenflaeche_draufsicht.figure.savefig(dir, \
        dpi = 1200, facecolor = self.farbe_zeichenflaeche)

        'Dateiname und Pfad'
        dateiname = 'bild_seitenansicht.svg'
        dir = path.join(workdir, 'speicher', 'bildspeicher', dateiname)

        'Seitenansicht als Bild speichern'
        self.zeichenflaeche_seitenansicht.figure.savefig(dir, \
        dpi = 1200, facecolor = self.farbe_zeichenflaeche)

    '''Methode geisterstunde - Bei der Bewegung des digitalen Roboters wird
    die zuletzt gespeicherte Position als Schatten eingeblendet. Die Methode
    ermöglicht das Ein- oder Ausblenden des Geistes.'''

    def geisterstunde(self, b):

        self.animation_draufsicht.geisterstunde(b)
        self.animation_seitenansicht.geisterstunde(b)
        self.animation_greifer.geisterstunde(b)

    '''Methode greifer_laden - Die Methode lädt den Öffnungsradius des
    Greifers (Zeilenvektor vom Format 1x1) und gibt diesen als Gleit-
    kommazahl zurück. Da sich der Greifer synchron öffnet und schließt 
    sind die Radien von Punkt6 und Punkt 7 gleich.'''

    def greifer_laden(self, name):

        'Arbeitsverzeichnis'
        workdir = getcwd()

        'Dateiname und Pfad'
        dateiname = name + '_greifer.npy'
        dir = path.join(workdir, 'speicher', 'programmspeicher', dateiname)

        'Öffnungsradius laden'
        greifer = load(dir)

        'Datentyp ändern'
        r6 = float(greifer)
        r7 = r6

        return r6, r7

    '''Methode koordinaten - Die Methode zerlegt einen Spaltenvektor 
    in die Koordinaten und gibt diese als Gleitkommazahl zurück.'''

    def koordinaten(self, r):

        'Format des Vektors'
        zeilenzahl, spaltenzahl = r.shape

        'Vektor in Zeilen zerlegen'
        zeilen = vsplit(r, zeilenzahl)

        'Koordinaten zuordnen'
        x = float(zeilen[0])
        y = float(zeilen[1])
        z = float(zeilen[2])

        return x, y, z

    '''Methode koordinaten_draufsicht - Die Bewegung des digitalen 
    Roboters ändert die x0,y0-Koordinaten der Punkte 2, 4 und 5. Diese 
    werden der Methode übergeben. Die z0-Koordinaten der Punkte 2, 4 und 5, 
    die 1-Koordinaten der Punkte 2, 3, 4, und 5 sowie die die 4z-Koordinaten 
    der Punkte 6 und 7 bleiben unverändert. In Folge sind die x0,y0-Koord-
    inaten der drei Punkte und der Winkel theta1 zu aktualisieren sowie die 
    von theta1 abhängigen Transformationsvorschriften und die 0-Koordinaten 
    von Punkt3 neu zu berechnen.'''

    def koordinaten_draufsicht(self, xP2, yP2, xP4, yP4, xP5, yP5):

        'Mittelpunktkoordinaten aktualisieren'
        self.xP2in0 = xP2
        self.yP2in0 = yP2
        self.xP4in0 = xP4
        self.yP4in0 = yP4
        self.xP5in0 = xP5
        self.yP5in0 = yP5

        'Theta1 (Denavit-Hartenberg-Parameter) aktualisieren'
        self.theta1 = self.animation_draufsicht.theta1

        'Transformation = f(theta1)'

        'Neuberechnung der Transformtionsmatrizen Aij'
        self.A01 = A01(self.theta1)
        self.A02 = A02(self.theta1, self.theta2)
        self.A03 = A03(self.theta1, self.theta2, self.theta3)
        self.A04 = A04(self.theta1, self.theta2, self.theta3, self.theta4)
        self.A05 = A05(self.theta1, self.theta2, self.theta3, self.theta4, \
        self.theta5)

        'Neuberechnung der Mittelpunktkoordinaten'

        'Ortsvektor von Punkt3'
        #in 1-Koordinaten
        rP3in1 = array([[self.xP3in1], [self.yP3in1], [self.zP3in1], [1]])
        #in 0-Koordinaten
        rP3in0 = around(dot(self.A01, rP3in1), 3)

        'Koordinaten von Punkt3'
        #in 0-Koordinaten
        self.xP3in0, self.yP3in0, self.zP3in0 = self.koordinaten(rP3in0)

        'Lagekoordinaten des Werkzeugkoordinatensystems'
        x5in0 = int(self.xP5in0)
        y5in0 = int(self.yP5in0)
        z5in0 = int(self.zP5in0)

        'Signal zum Aktualisieren der Lagekoordinaten senden'
        self.signal_koordinaten.emit(x5in0, y5in0, z5in0)

        'Signal, das die Bewegung des digitalen Roboters signalisiert, senden'
        self.signal_animation_aktiv.emit(True)

    '''Methode koordinaten_greifer - Die Bewegung des Greifers ändert 
    die x4z,y4z-Koordinaten der Punkte 6 und 7. Diese werden der Methode 
    übergeben. Die 4z-Koordinaten der Punkte 6 und 7 bleiben unverändert. 
    In Folge sind die x4z,y4z-Koordinaten der beiden Punkte und der Winkel 
    theta5 zu aktualisieren sowie die von theta5 abhängigen Transformations-
    vorschriften neu zu berechnen.'''

    def koordinaten_greifer(self, xP6in4z, yP6in4z, xP7in4z, yP7in4z):

        'Mittelpunktkoordinaten aktualisieren'
        self.xP6in4z = xP6in4z
        self.yP6in4z = yP6in4z
        self.xP7in4z = xP7in4z
        self.yP7in4z = yP7in4z

        'Theta5 (Denavit-Hartenberg-Parameter) aktualisieren'
        self.theta5 = self.animation_greifer.theta5

        'Radien aktualisieren'
        self.r6 = self.animation_greifer.r6
        self.r7 = self.r6

        'Transformation = f(theta5)'

        'Neuberechnung der Transformtionsmatrizen Aij'
        self.A05 = A05(self.theta1, self.theta2, self.theta3, self.theta4, \
        self.theta5)

        self.A15 = A15(self.theta2, self.theta3, self.theta4, self.theta5)

        self.A45 = A45(self.theta5)

        'Signal, das die Bewegung des digitalen Roboters signalisiert, senden'
        self.signal_animation_aktiv.emit(True)

    '''Methode koordinaten_seitenansicht - Die Bewegung des digitalen 
    Roboters ändert die x1,y1-Koordinaten der Punkte 2, 3, 4, und 5. Diese 
    werden der Methode übergeben. Die z1-Koordinaten der Punkte 2, 3, 4 und
    5 und die 4z-Koordinaten der Punkte 6 und 7 bleiben erhalten. In Folge 
    sind die x1,y1-Koordinaten der vier Punkte und die Winkel theta2, theta3 
    und theta4 zu aktualisieren sowie die von den Winkeln abhängigen 
    Transformationsvorschriften und die 0-Koordinaten der vier Punkte neu
    zu berechnen.'''
    def koordinaten_seitenansicht(self, xP2in1, yP2in1, xP3in1, yP3in1, \
    xP4in1, yP4in1, xP5in1, yP5in1):

        'Mittelpunktkoordinaten aktualisieren'
        self.xP2in1 = xP2in1
        self.yP2in1 = yP2in1
        self.xP3in1 = xP3in1
        self.yP3in1 = yP3in1
        self.xP4in1 = xP4in1
        self.yP4in1 = yP4in1
        self.xP5in1 = xP5in1
        self.yP5in1 = yP5in1

        'Theta2, Theta3 und Theta4 (Denavit-Hartenberg-P.) aktualisieren'
        self.theta2 = self.animation_seitenansicht.theta2
        self.theta3 = self.animation_seitenansicht.theta3
        self.theta4 = self.animation_seitenansicht.theta4

        'Transformation = f(theta2, theta3, theta4)'

        'Neuberechnung der Transformationsmatrizen Aij'
        self.A02 = A02(self.theta1, self.theta2)
        self.A03 = A03(self.theta1, self.theta2, self.theta3)
        self.A04 = A04(self.theta1, self.theta2, self.theta3, self.theta4)
        self.A05 = A05(self.theta1, self.theta2, self.theta3, self.theta4, \
        self.theta5)

        self.A12 = A12(self.theta2)
        self.A13 = A13(self.theta2, self.theta3)
        self.A14 = A14(self.theta2, self.theta3, self.theta4)
        self.A15 = A15(self.theta2, self.theta3, self.theta4, self.theta5)

        'Neuberechnung der Mittelpunktkoordinaten'

        'Ortsvektor von Punkt2'
        #in 1-Koordinaten
        rP2in1 = array([[self.xP2in1], [self.yP2in1], [self.zP2in1], [1]])
        #in 0-Koordinaten
        rP2in0 = around(dot(self.A01, rP2in1), 3)

        'Ortsvektor von Punkt3'
        #in 1-Koordinaten
        rP3in1 = array([[self.xP3in1], [self.yP3in1], [self.zP3in1], [1]])
        #in 0-Koordinaten
        rP3in0 = around(dot(self.A01, rP3in1), 3)

        'Ortsvektor von Punkt4'
        #in 1-Koordinaten
        rP4in1 = array([[self.xP4in1], [self.yP4in1], [self.zP4in1], [1]])
        #in 0-Koordinaten
        rP4in0 = around(dot(self.A01, rP4in1), 3)

        'Ortsvektor von Punkt5'
        #in 1-Koordinaten
        rP5in1 = array([[self.xP5in1], [self.yP5in1], [self.zP5in1], [1]])
        #in 0-Koordinaten
        rP5in0 = around(dot(self.A01, rP5in1), 3)

        'Koordinaten von Punkt2'
        #in 0-Koordinaten
        self.xP2in0, self.yP2in0, self.zP2in0 = self.koordinaten(rP2in0)

        'Koordinaten von Punkt3'
        #in 0-Koordinaten
        self.xP3in0, self.yP3in0, self.zP3in0 = self.koordinaten(rP3in0)

        'Koordinaten von Punkt4'
        #in 0-Koordinaten
        self.xP4in0, self.yP4in0, self.zP4in0 = self.koordinaten(rP4in0)

        'Koordinaten von Punkt5'
        #in 0-Koordinaten
        self.xP5in0, self.yP5in0, self.zP5in0 = self.koordinaten(rP5in0)

        'Bewegungen des digitalen Roboters synchronisieren'
        self.animation_draufsicht.pointG2.set_visible(True)
        self.animation_draufsicht.pointG4.set_visible(True)
        self.animation_draufsicht.pointG5.set_visible(True)
        self.animation_draufsicht.lineG1.set_visible(True)
        self.animation_draufsicht.lineG2.set_visible(True)
        self.animation_draufsicht.lineG3.set_visible(True)

        'Draufsicht aktualisieren'
        self.animation_draufsicht.ansicht_aktualisieren( \
        self.xP2in0, self.yP2in0, self.xP4in0, \
        self.yP4in0, self.xP5in0, self.yP5in0)

        'Lagekoordinaten des Werkzeugkoordinatensystems'
        x5in0 = int(self.xP5in0)
        y5in0 = int(self.yP5in0)
        z5in0 = int(self.zP5in0)

        'Signal zum Aktualisieren der Lagekoordinaten senden'
        self.signal_koordinaten.emit(x5in0, y5in0, z5in0)

        'Signal, das die Bewegung des digitalen Roboters signalisiert, senden'
        self.signal_animation_aktiv.emit(True)

    '''Methode koordinaten_abfragen - Die Methode gibt die aktuellen 
    Lagekoordinaten des Werkzeugkoordinatensystems zurück.'''

    def koordinaten_abfragen(self):

        xP5in0 = self.xP5in0
        yP5in0 = self.yP5in0
        zP5in0 = self.zP5in0

        return xP5in0, yP5in0, zP5in0

    '''Methode lage_berechnen - Die Methode gibt die Lage (Zeilenvektor 
    vom Format 1x3) und Orientierung (Zeilenvektor vom Format 1x9) des
    Werkzeugkoordinatensystems in 0-Koordinaten, die Winkel (Denavit-
    Hartenberg-Parameter) (Zeilenvektor vom Format 1x5) und den 
    Öffnungsradius des Greifers (Zeilenvektor vom Format 1x1) zurück.'''

    def lage_berechnen(self):

        'Geist ausblenden'
        self.geisterstunde(False)
        '''Kinematische Vorwärtstransformation zur Berechnung von Lage
        und Orientierung des Werkzeugkoordinatensystems in 0-Koordinaten.
        Die Vektoren werden im Format 3x1 zurückgegeben.'''
        x5in0, y5in0, z5in0, P05in0 = kinematik_vor(self.theta1, \
        self.theta2, self.theta3, self.theta4, self.theta5)

        'Orientierung des Werkzeugkoordinatensystems'

        'Format der Einheitsvektoren anpassen'
        x5in0 = x5in0.transpose()  #Format 1x3
        y5in0 = y5in0.transpose()  #Format 1x3
        z5in0 = z5in0.transpose()  #Format 1x3

        'Orientierungsvektor erstellen'
        orientierung = hstack((x5in0, y5in0, z5in0))  #Format 1x9
        orientierung = around(orientierung, 3)

        'Lage des Werkzeugkoordinatensystems'

        'Format des Lagevektors anpassen und Vektor zuweisen'
        lage = P05in0.transpose()  #Format 1x3
        lage = around(lage, 3)

        'Winkelvektor erstellen'
        winkel = array([[self.theta1, self.theta2, \
        self.theta3, self.theta4, self.theta5]]) #Format 1x5
        winkel = around(winkel, 3)

        'Öffnungsradius des Greifers'
        greifer = array([[self.r6]])  #Format 1x1

        return orientierung, lage, winkel, greifer

    '''Methode modellrechnung - Die Methode berechnet aus der Lage und 
    Orientierung des Werkzeugkoordinatensystems in 0-Koordinaten sowie 
    dem Öffnungsradius des Greifers die Mittelpunktkoordinaten der Circle-
    Objekte. Insgesamt gibt es sieben Circle-Objekte und drei Ansichten. Die 
    Draufsicht ist die Projektion des digitalen Roboters auf die x0,y0-Ebene. 
    Dargestellt werden Punkt1, Punkt2, Punkt4 und Punkt5. Die Seitenansicht 
    ist die Projektion des digitalen Roboters auf die x1,y1-Ebene. Dargestellt 
    werden Punkt1, Punkt2, Punkt3, Punkt4 und Punkt5. Die Ansicht des Greifers
    ist die Projektion auf eine Zeichenebene parallel zur x4,y4-Ebene.
    Dargestellt werden Punkt6 und Punkt7. Während die Mittelpunktkoordinaten
    von Punkt6 und Punkt 7 nur in Zeichenkoordinaten berechnet werden, werden
    die Koordinaten der Punkte 1 bis 5 auch in 0-Koordinaten berechnet.'''

    def modellrechnung(self):
        '''Kinematische Rückwärtstransformation zur Berechnung der 
        Winkel (Denavit-Hartenberg-Parameter) bei gegebener Lage und
        Orientierung des Werkzeugkoordinatensystems'''
        theta = kinematik_inv(self.x5in0, self.y5in0, self.z5in0, self.P05in0)

        'Drehwinkel zuordnen'
        self.theta1 = theta[0]
        self.theta2 = theta[1]
        self.theta3 = theta[2]
        self.theta4 = theta[3]
        self.theta5 = theta[4]

        'Transformationsmatrizen Aij zur Überführung von Ki in Kj'
        self.A01 = A01(self.theta1)
        self.A02 = A02(self.theta1, self.theta2)
        self.A03 = A03(self.theta1, self.theta2, self.theta3)
        self.A04 = A04(self.theta1, self.theta2, self.theta3, self.theta4)
        self.A05 = A05(self.theta1, self.theta2, self.theta3, self.theta4, \
        self.theta5)

        self.A12 = A12(self.theta2)
        self.A13 = A13(self.theta2, self.theta3)
        self.A14 = A14(self.theta2, self.theta3, self.theta4)
        self.A15 = A15(self.theta2, self.theta3, self.theta4, self.theta5)

        self.A45 = A45(self.theta5)

        'Ortsvektor von Punkt2'
        #in 2-Koordinaten
        rP2in2 = array([[0], [0], [0], [1]])
        #in 1-Koordinaten
        rP2in1 = around(dot(self.A12, rP2in2), 3)
        #in 0-Koordinaten
        rP2in0 = around(dot(self.A02, rP2in2), 3)

        'Ortsvektor von Punkt3'
        l = self.a3 * (cos(8 * pi / 180) - sin(8 * pi / 180))
        #in 3-Koordinaten
        rP3in3 = array([[-l * cos(8 * pi / 180)], [l * sin(8 * pi / 180)], [0],
                        [1]])
        #in 1-Koordinaten
        rP3in1 = around(dot(self.A13, rP3in3), 3)
        #in 0-Koordinaten
        rP3in0 = around(dot(self.A03, rP3in3), 3)

        'Ortsvektor von Punk4'
        #in 4-Koordinaten
        rP4in4 = array([[0], [0], [0], [1]])
        #in 1-Koordinaten
        rP4in1 = around(dot(self.A14, rP4in4), 3)
        #in 0-Koordinaten
        rP4in0 = around(dot(self.A04, rP4in4), 3)

        'Ortsvektor von Punk5'
        #in 5-Koordinaten
        rP5in5 = array([[0], [0], [0], [1]])
        #in 1-Koordinaten
        rP5in1 = around(dot(self.A15, rP5in5), 3)
        #in 0-Koordinaten
        rP5in0 = self.P05in0

        phi6 = pi - self.theta5
        phi7 = phi6 + pi

        'Ortsvektor von Punkt6'
        #in 4z-Koordinaten
        rP6in4z = array([[self.r6 * cos(phi6)], [self.r6 * sin(phi6)], [0],
                         [1]])

        'Ortsvektor von Punkt7'
        #in 4z-Koordinaten
        rP7in4z = array([[self.r7 * cos(phi7)], [self.r7 * sin(phi7)], [0],
                         [1]])

        'Koordinaten von Punkt1'
        #in 1-Koordinaten
        self.xP1in1, self.yP1in1, self.zP1in1 = (0, 0, 0)
        #in 0-Koordinaten
        self.xP1in0, self.yP1in0, self.zP1in0 = (0, 0, 0)

        'Koordinaten von Punkt2'
        #in 1-Koordinaten
        self.xP2in1, self.yP2in1, self.zP2in1 = self.koordinaten(rP2in1)
        #in 0-Koordinaten
        self.xP2in0, self.yP2in0, self.zP2in0 = self.koordinaten(rP2in0)

        'Koordinaten von Punkt3'
        #in 1-Koordinaten
        self.xP3in1, self.yP3in1, self.zP3in1 = self.koordinaten(rP3in1)
        #in 0-Koordinaten
        self.xP3in0, self.yP3in0, self.zP3in0 = self.koordinaten(rP3in0)

        'Koordinaten von Punkt4'
        #in 1-Koordinaten
        self.xP4in1, self.yP4in1, self.zP4in1 = self.koordinaten(rP4in1)
        #in 0-Koordinaten
        self.xP4in0, self.yP4in0, self.zP4in0 = self.koordinaten(rP4in0)

        'Koordinaten von Punkt5'
        #in 1-Koordinaten
        self.xP5in1, self.yP5in1, self.zP5in1 = self.koordinaten(rP5in1)
        #in 0-Koordinaten
        self.xP5in0, self.yP5in0, self.zP5in0 = self.koordinaten(rP5in0)

        'Zeichenkoordinaten von Punkt6'
        #in 4z-Koordinaten
        self.xP6in4z, self.yP6in4z, self.zP6in4z = self.koordinaten(rP6in4z)

        'Zeichenkoordinaten von Punkt7'
        #in 4z-Koordinaten
        self.xP7in4z, self.yP7in4z, self.zP7in4z = self.koordinaten(rP7in4z)

    '''Methode orientierung_laden - Die Methode lädt die Orientierung des
    Werkzeugkoordinatensystems in 0-Koordinaten (Zeilenvektor vom Format 
    1x9) und gibt die Einheitsvektoren des Werkzeugkoordinatensystems als 
    Spaltenvektoren vom Format 4x1 zurück.'''

    def orientierung_laden(self, name):

        'Arbeitsverzeichnis'
        workdir = getcwd()

        'Dateiname und Pfad'
        dateiname = name + '_orientierung.npy'
        dir = path.join(workdir, 'speicher', 'programmspeicher', dateiname)

        'Orientierung laden'
        orientierung = load(dir)

        'Einheitsvektoren zuordnen'
        x5in0, y5in0, z5in0 = hsplit(orientierung, 3)  #Format 1x3

        'Format anpassen'
        x5in0 = x5in0.transpose()  #Format 3x1
        x5in0 = vstack((x5in0, array([[0]])))  #Format 4x1
        y5in0 = y5in0.transpose()  #Format 3x1
        y5in0 = vstack((y5in0, array([[0]])))  #Format 4x1
        z5in0 = z5in0.transpose()  #Format 3x1
        z5in0 = vstack((z5in0, array([[0]])))  #Format 4x1

        return x5in0, y5in0, z5in0

    '''Methode lage_laden - Die Methode lädt den Lagevektor des
    Werkzeugkoordinatensystems in 0-Koordinaten (Zeilenvektor vom Format 
    1x3) und gibt diese als Spaltenvektor vom Format 4x1 zurück.'''

    def lage_laden(self, name):

        'Arbeitsverzeichnis'
        workdir = getcwd()

        'Dateiname und Pfad'
        dateiname = name + '_lage.npy'
        dir = path.join(workdir, 'speicher', 'programmspeicher', dateiname)

        'Lagevektor laden'
        lage = load(dir)

        'Format anpassen'
        P05in0 = lage  #Format 1x3
        P05in0 = P05in0.transpose()  #Format 3x1
        P05in0 = vstack((P05in0, array([[1]])))  #Format 4x1

        return P05in0
Exemple #29
0
class ConfigDialog(QDialog):
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        self.contents_widget = QListWidget()
        self.contents_widget.setMovement(QListView.Static)
        self.contents_widget.setMinimumWidth(120)
        self.contents_widget.setMaximumWidth(120)
        self.contents_widget.setSpacing(1)

        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Apply
                                | QDialogButtonBox.Cancel)
        self.apply_btn = bbox.button(QDialogButtonBox.Apply)
        self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()"))
        self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()"))
        self.connect(bbox, SIGNAL("clicked(QAbstractButton*)"),
                     self.button_clicked)

        self.pages_widget = QStackedWidget()
        self.connect(self.pages_widget, SIGNAL("currentChanged(int)"),
                     self.current_page_changed)

        self.connect(self.contents_widget, SIGNAL("currentRowChanged(int)"),
                     self.pages_widget.setCurrentIndex)
        self.contents_widget.setCurrentRow(0)

        hlayout = QHBoxLayout()
        hlayout.addWidget(self.contents_widget)
        hlayout.addWidget(self.pages_widget)
        hlayout.setStretch(1, 1)

        btnlayout = QHBoxLayout()
        btnlayout.addStretch(1)
        btnlayout.addWidget(bbox)

        vlayout = QVBoxLayout()
        vlayout.addLayout(hlayout)
        vlayout.addLayout(btnlayout)

        self.setLayout(vlayout)

        self.setWindowTitle("Preferences")
        self.setWindowIcon(get_icon("configure.png"))

    def get_current_index(self):
        """Return current page index"""
        return self.contents_widget.currentRow()

    def set_current_index(self, index):
        """Set current page index"""
        self.contents_widget.setCurrentRow(index)

    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.pages_widget.count()):
            configpage = self.pages_widget.widget(index)
            if not configpage.is_valid():
                return
            configpage.apply_changes()
        QDialog.accept(self)

    def button_clicked(self, button):
        if button is self.apply_btn:
            # Apply button was clicked
            configpage = self.pages_widget.currentWidget()
            if not configpage.is_valid():
                return
            configpage.apply_changes()

    def current_page_changed(self, index):
        widget = self.pages_widget.widget(index)
        self.apply_btn.setVisible(widget.apply_callback is not None)
        self.apply_btn.setEnabled(widget.is_modified)

    def add_page(self, widget):
        self.connect(self, SIGNAL('check_settings()'), widget.check_settings)
        self.connect(widget,
                     SIGNAL('show_this_page()'),
                     lambda row=self.contents_widget.count(): self.
                     contents_widget.setCurrentRow(row))
        self.connect(widget, SIGNAL("apply_button_enabled(bool)"),
                     self.apply_btn.setEnabled)
        self.pages_widget.addWidget(widget)
        item = QListWidgetItem(self.contents_widget)
        item.setIcon(widget.get_icon())
        item.setText(widget.get_name())
        item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
        item.setSizeHint(QSize(0, 25))

    def check_all_settings(self):
        """This method is called to check all configuration page settings
        after configuration dialog has been shown"""
        self.emit(SIGNAL('check_settings()'))
Exemple #30
0
class LeapFlow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)

        self.controller = Controller()
        self.listener = LeapListener(self)
        self.controller.add_listener(self.listener)

        self.mode = "gallery"
        self.scroll = False
        self.direction = ""
        self.direction_x = 0
        self.scroll_velocity = 0
        self.current_index = 0

        # List containing images for the gallery
        self.list_view = QListWidget()
        self.list_view.setFlow(0)
        self.list_view.setHorizontalScrollMode(1)
        self.list_view.setMouseTracking(True)
        self.list_view.itemClicked.connect(self.show_image)

        # Setting the style of the ListView, background, item selected, etc
        self.list_view.setStyleSheet(
            """
				QListWidget::item:hover {background: transparent;}
				QListWidget::item:disabled:hover {background: transparent;}
				QListWidget::item:hover:!active {background: transparent;}
				QListWidget::item:selected:active {background: transparent;}
	            QListWidget::item:selected:!active {background: transparent;}
	            QListWidget::item:selected:disabled {background: transparent;}
	            QListWidget::item:selected:!disabled {background: transparent;}
	            QListWidget {background: #2C3539}
			"""
        )

        # Image viewer
        self.scene = QGraphicsScene()
        self.viewer = QGraphicsView(self.scene)

        self.stackedWidget = QStackedWidget()
        self.stackedWidget.addWidget(self.list_view)
        self.stackedWidget.addWidget(self.viewer)

        self.setCentralWidget(self.stackedWidget)
        self.resize(500, 400)
        self.showMaximized()

        scan = ScanLibrary("/home/chris/Example")
        threads.append(scan)
        self.connect(scan, SIGNAL(scan.signal), self.add_images_to_list)
        scan.start()

        self.connect(self, SIGNAL("scrollChanged(bool)"), self.scroll_view)

    def setScroll(self, scroll):
        """Emit signal to scroll the view"""
        if self.scroll != scroll:
            self.scroll = scroll
            self.emit(SIGNAL("scrollChanged(bool)"), scroll)

    def scroll_view(self):
        """Scroll the view based on scroll velocity and direction"""

        x = self.direction_x * self.scroll_velocity / 100
        bar_x = self.list_view.horizontalScrollBar().value()
        self.list_view.horizontalScrollBar().setValue(bar_x + x)

    def add_images_to_list(self):
        """To add a widget to the listview you must add a QListWidgetItem
		and replace with your widget"""

        for image in library:
            item = QListWidgetItem()
            pixmap = QPixmap.fromImage(QImage(library[image]))
            label = QLabel()
            label.setPixmap(pixmap.scaled(600, 400))
            item.setSizeHint(label.sizeHint())
            item.setData(0, library[image])
            self.list_view.addItem(item)
            self.list_view.setItemWidget(item, label)

    def show_image(self, item):
        """"Display the selected image"""

        self.current_index = self.list_view.indexFromItem(item).row()
        self.scene.addPixmap((QPixmap.fromImage(QImage(item.text()))).scaled(self.viewer.size()))

        self.stackedWidget.setCurrentIndex(1)
        self.mode = "viewer"

    def previous_image(self):
        """Load previous image"""

        if self.current_index - 1 >= 0:
            self.current_index -= 1
            self.load_image()

    def next_image(self):
        """Load next image"""

        if self.current_index + 1 <= len(library.keys()) - 1:
            self.current_index += 1
            self.load_image()

    def load_image(self):
        """Load the image in self.current_index"""

        self.scene.addPixmap(QPixmap.fromImage(QImage(library[library.keys()[self.current_index]])))
Exemple #31
0
class ViewSpace(QWidget):
    """A ViewSpace manages a stack of views, one of them is visible.
    
    The ViewSpace also has a statusbar, accessible in the status attribute.
    The viewChanged(View) signal is emitted when the current view for this ViewSpace changes.
    
    Also, when a ViewSpace is created (e.g. when a window is created or split), the
    app.viewSpaceCreated(space) signal is emitted.
    
    You can use the app.viewSpaceCreated() and the ViewSpace.viewChanged() signals to implement
    things on a per ViewSpace basis, e.g. in the statusbar of a ViewSpace.
    
    """
    viewChanged = pyqtSignal(view_.View)
    
    def __init__(self, manager, parent=None):
        super(ViewSpace, self).__init__(parent)
        self.manager = weakref.ref(manager)
        self.views = []
        
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.setLayout(layout)
        self.stack = QStackedWidget(self)
        layout.addWidget(self.stack)
        self.status = ViewStatusBar(self)
        self.status.setEnabled(False)
        layout.addWidget(self.status)
        app.languageChanged.connect(self.updateStatusBar)
        app.viewSpaceCreated(self)
        
    def activeView(self):
        if self.views:
            return self.views[-1]

    def document(self):
        """Returns the currently active document in this space.
        
        If there are no views, returns None.
        
        """
        if self.views:
            return self.views[-1].document()
    
    def showDocument(self, doc):
        """Shows the document, creating a View if necessary."""
        if doc is self.document():
            return
        cur = self.activeView()
        for view in self.views[:-1]:
            if doc is view.document():
                self.views.remove(view)
                break
        else:
            view = view_.View(doc)
            self.stack.addWidget(view)
        self.views.append(view)
        if cur:
            self.disconnectView(cur)
        self.connectView(view)
        self.stack.setCurrentWidget(view)
        self.updateStatusBar()
        
    def removeDocument(self, doc):
        active = doc is self.document()
        if active:
            self.disconnectView(self.activeView())
        for view in self.views:
            if doc is view.document():
                self.views.remove(view)
                view.deleteLater()
                break
        else:
            return
        if active and self.views:
            self.connectView(self.views[-1])
            self.stack.setCurrentWidget(self.views[-1])
            self.updateStatusBar()
    
    def connectView(self, view):
        view.installEventFilter(self)
        view.cursorPositionChanged.connect(self.updateCursorPosition)
        view.modificationChanged.connect(self.updateModificationState)
        view.document().urlChanged.connect(self.updateDocumentName)
        self.viewChanged.emit(view)

    def disconnectView(self, view):
        view.removeEventFilter(self)
        view.cursorPositionChanged.disconnect(self.updateCursorPosition)
        view.modificationChanged.disconnect(self.updateModificationState)
        view.document().urlChanged.disconnect(self.updateDocumentName)
    
    def eventFilter(self, view, ev):
        if ev.type() == QEvent.FocusIn:
            self.setActiveViewSpace()
        return False

    def setActiveViewSpace(self):
        self.manager().setActiveViewSpace(self)
        
    def updateStatusBar(self):
        """Update all info in the statusbar, e.g. on document change."""
        if self.views:
            self.updateCursorPosition()
            self.updateModificationState()
            self.updateDocumentName()
        
    def updateCursorPosition(self):
        cur = self.activeView().textCursor()
        line = cur.blockNumber() + 1
        try:
            column = cur.positionInBlock()
        except AttributeError: # only in very recent PyQt4
            column = cur.position() - cur.block().position()
        self.status.pos.setText(_("Line: {line}, Col: {column}").format(
            line = line, column = column))
    
    def updateModificationState(self):
        modified = self.document().isModified()
        pixmap = icons.get('document-save').pixmap(16) if modified else QPixmap()
        self.status.state.setPixmap(pixmap)
    
    def updateDocumentName(self):
        self.status.info.setText(self.document().documentName())
Exemple #32
0
class LeapFlow (QMainWindow):

	def __init__ (self):
		QMainWindow.__init__ (self)

		self.controller = Controller ()
		self.listener = LeapListener (self)
		self.controller.add_listener (self.listener)

		self.mode = "gallery"
		self.scroll = False
		self.direction = ""
		self.direction_x = 0
		self.scroll_velocity = 0
		self.current_index = 0

		# List containing images for the gallery
		self.list_view = QListWidget ()
		self.list_view.setFlow (0)
		self.list_view.setHorizontalScrollMode (1)
		self.list_view.setMouseTracking (True)
		self.list_view.itemClicked.connect (self.show_image)

		# Setting the style of the ListView, background, item selected, etc
		self.list_view.setStyleSheet ("""
				QListWidget::item:hover {background: transparent;}
				QListWidget::item:disabled:hover {background: transparent;}
				QListWidget::item:hover:!active {background: transparent;}
				QListWidget::item:selected:active {background: transparent;}
	            QListWidget::item:selected:!active {background: transparent;}
	            QListWidget::item:selected:disabled {background: transparent;}
	            QListWidget::item:selected:!disabled {background: transparent;}
	            QListWidget {background: #2C3539}
			""")

		# Image viewer
		self.scene = QGraphicsScene ()
		self.viewer = QGraphicsView (self.scene)

		self.stackedWidget = QStackedWidget ()
		self.stackedWidget.addWidget (self.list_view)
		self.stackedWidget.addWidget (self.viewer)

		self.setCentralWidget (self.stackedWidget)
		self.resize (500, 400)
		self.showMaximized ()

		scan = ScanLibrary ("/home/chris/Example")
		threads.append (scan)
		self.connect (scan, SIGNAL (scan.signal), self.add_images_to_list)
		scan.start ()

		self.connect (self, SIGNAL ("scrollChanged(bool)"), self.scroll_view)

	def setScroll (self, scroll):
		"""Emit signal to scroll the view"""
		if (self.scroll != scroll):
			self.scroll = scroll
			self.emit (SIGNAL("scrollChanged(bool)"), scroll)

	def scroll_view (self):
		"""Scroll the view based on scroll velocity and direction"""

		x = self.direction_x * self.scroll_velocity / 100
		bar_x = self.list_view.horizontalScrollBar ().value ()
		self.list_view.horizontalScrollBar ().setValue (bar_x + x)

	def add_images_to_list (self):
		"""To add a widget to the listview you must add a QListWidgetItem
		and replace with your widget"""

		for image in library:
			item = QListWidgetItem ()
			pixmap = QPixmap.fromImage (QImage (library[image]))
			label = QLabel ()
			label.setPixmap (pixmap.scaled (600, 400))
			item.setSizeHint (label.sizeHint ())
			item.setData (0, library[image])
			self.list_view.addItem (item)
			self.list_view.setItemWidget (item, label)

	def show_image (self, item):
		""""Display the selected image"""

		self.current_index = self.list_view.indexFromItem (item).row ()
		self.scene.addPixmap ((QPixmap.fromImage (QImage (item.text()))).scaled (self.viewer.size()))

		self.stackedWidget.setCurrentIndex (1)
		self.mode = "viewer"

	def previous_image (self):
		"""Load previous image"""

		if self.current_index - 1 >= 0:
			self.current_index -= 1
			self.load_image ()

	def next_image (self):
		"""Load next image"""

		if self.current_index + 1 <= len(library.keys ()) - 1:
			self.current_index += 1
			self.load_image ()

	def load_image (self):
		"""Load the image in self.current_index"""

		self.scene.addPixmap (QPixmap.fromImage (QImage (library[library.keys()[self.current_index]])))
Exemple #33
0
class Preferences(QDialog):
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        self.setWindowTitle(self.tr("Configuraciones - Edis"))
        self.__sections = []
        # Opacity effect
        self.effect = QGraphicsOpacityEffect()
        self.setGraphicsEffect(self.effect)
        self.animation = QPropertyAnimation(self.effect, "opacity")

        Edis.load_component("preferences", self)
        # Install sections
        #lint:disable
        from src.ui.dialogs.preferences import (environment_configuration,
                                                editor_configuration,
                                                compiler_configuration)
        #lint:enable
        self.load_ui()
        key_escape = QShortcut(QKeySequence(Qt.Key_Escape), self)
        self.connect(key_escape, SIGNAL("activated()"), self.close)
        self.connect(self.btn_cancel, SIGNAL("clicked()"), self.close)
        self.connect(self.btn_guardar, SIGNAL("clicked()"), self._save)

    def install_section(self, obj):
        self.__sections.append(obj)

    def load_ui(self):
        container = QVBoxLayout(self)

        box = QHBoxLayout()
        box.setContentsMargins(0, 0, 0, 0)
        box.setSpacing(0)
        toolbar = QToolBar()
        toolbar.setToolButtonStyle(3)
        toolbar.setOrientation(Qt.Vertical)
        toolbar.setIconSize(QSize(30, 30))
        toolbar.setObjectName("preferencias")

        environment_section = toolbar.addAction(QIcon(":image/general-pref"),
                                                "Entorno")
        editor_section = toolbar.addAction(QIcon(":image/editor-pref"),
                                           "Editor")
        compiler_section = toolbar.addAction(QIcon(":image/compiler-pref"),
                                             "Compilador")
        self.connect(environment_section, SIGNAL("triggered()"),
                     lambda: self.change_widget(0))
        self.connect(editor_section, SIGNAL("triggered()"),
                     lambda: self.change_widget(1))
        self.connect(compiler_section, SIGNAL("triggered()"),
                     lambda: self.change_widget(2))

        # Set size
        for action in toolbar.actions():
            widget = toolbar.widgetForAction(action)
            widget.setFixedSize(80, 50)

        box.addWidget(toolbar)

        self.stack = QStackedWidget()
        box.addWidget(self.stack)

        # Load sections and subsections
        for section in self.__sections:
            for name, obj in list(section.get_tabs().items()):
                section.install_tab(obj, name)
            self.stack.addWidget(section)

        box_buttons = QHBoxLayout()
        box_buttons.setMargin(10)
        box_buttons.setSpacing(10)
        box_buttons.addStretch(1)
        self.btn_cancel = QPushButton(self.tr("Cancelar"))
        self.btn_guardar = QPushButton(self.tr("Guardar"))
        box_buttons.addWidget(self.btn_cancel)
        box_buttons.addWidget(self.btn_guardar)
        container.addLayout(box)
        container.addLayout(box_buttons)

    def change_widget(self, index):
        if not self.isVisible():
            self.show()
        self.stack.setCurrentIndex(index)

    def _save(self):
        for index in range(self.stack.count()):
            self.stack.widget(index).save()
        self.close()

    def close(self):
        super(Preferences, self).close()
        self.emit(SIGNAL("configurationsClose(PyQt_PyObject)"), self)

    def showEvent(self, event):
        super(Preferences, self).showEvent(event)
        self.animation.setDuration(400)
        self.animation.setStartValue(0)
        self.animation.setEndValue(1)
        self.animation.start()
class MixerPanel(QtGui.QFrame):
    '''A color mixer to hook up to an image.
    You pass the image you the panel to operate on
    and it operates on that image in place. You also
    pass a callback to be called to trigger a refresh.
    This callback is called every time the mixer modifies
    your image.'''
    def __init__(self, img):
        QtGui.QFrame.__init__(self)
        #self.setFrameStyle(QtGui.QFrame.Box|QtGui.QFrame.Sunken)

        self.img = img
        self.mixer = ColorMixer(self.img)
        self.callback = None

        #---------------------------------------------------------------
        # ComboBox
        #---------------------------------------------------------------

        self.combo_box_entries = ['RGB Color', 'HSV Color',
                                  'Brightness/Contrast',
                                  'Gamma',
                                  'Gamma (Sigmoidal)']
        self.combo_box = QtGui.QComboBox()
        for entry in self.combo_box_entries:
            self.combo_box.addItem(entry)
        self.combo_box.currentIndexChanged.connect(self.combo_box_changed)

        #---------------------------------------------------------------
        # RGB color sliders
        #---------------------------------------------------------------

        # radio buttons
        self.rgb_add = QtGui.QRadioButton('Additive')
        self.rgb_mul = QtGui.QRadioButton('Multiplicative')
        self.rgb_mul.toggled.connect(self.rgb_radio_changed)
        self.rgb_add.toggled.connect(self.rgb_radio_changed)

        # sliders
        rs = IntelligentSlider('R', 0.51, -255, self.rgb_changed)
        gs = IntelligentSlider('G', 0.51, -255, self.rgb_changed)
        bs = IntelligentSlider('B', 0.51, -255, self.rgb_changed)
        self.rs = rs
        self.gs = gs
        self.bs = bs

        self.rgb_widget = QWidget()
        self.rgb_widget.layout = QGridLayout(self.rgb_widget)
        self.rgb_widget.layout.addWidget(self.rgb_add, 0, 0, 1, 3)
        self.rgb_widget.layout.addWidget(self.rgb_mul, 1, 0, 1, 3)
        self.rgb_widget.layout.addWidget(self.rs, 2, 0)
        self.rgb_widget.layout.addWidget(self.gs, 2, 1)
        self.rgb_widget.layout.addWidget(self.bs, 2, 2)


        #---------------------------------------------------------------
        # HSV sliders
        #---------------------------------------------------------------

        # radio buttons
        self.hsv_add = QtGui.QRadioButton('Additive')
        self.hsv_mul = QtGui.QRadioButton('Multiplicative')
        self.hsv_mul.toggled.connect(self.hsv_radio_changed)
        self.hsv_mul.toggled.connect(self.hsv_radio_changed)

        # sliders
        hs = IntelligentSlider('H', 0.36, -180, self.hsv_changed)
        ss = IntelligentSlider('S', 0.002, 0, self.hsv_changed)
        vs = IntelligentSlider('V', 0.002, 0, self.hsv_changed)
        self.hs = hs
        self.ss = ss
        self.vs = vs

        self.hsv_widget = QWidget()
        self.hsv_widget.layout = QGridLayout(self.hsv_widget)
        self.hsv_widget.layout.addWidget(self.hsv_add, 0, 0, 1, 3)
        self.hsv_widget.layout.addWidget(self.hsv_mul, 1, 0, 1, 3)
        self.hsv_widget.layout.addWidget(self.hs, 2, 0)
        self.hsv_widget.layout.addWidget(self.ss, 2, 1)
        self.hsv_widget.layout.addWidget(self.vs, 2, 2)


        #---------------------------------------------------------------
        # Brightness/Contrast sliders
        #---------------------------------------------------------------

        # sliders
        cont = IntelligentSlider('x', 0.002, 0, self.bright_changed)
        bright = IntelligentSlider('+', 0.51, -255, self.bright_changed)
        self.cont = cont
        self.bright = bright

        # layout
        self.bright_widget = QWidget()
        self.bright_widget.layout = QtGui.QGridLayout(self.bright_widget)
        self.bright_widget.layout.addWidget(self.cont, 0, 0)
        self.bright_widget.layout.addWidget(self.bright, 0, 1)


        #-----------------------------------------------------------------------
        # Gamma Slider
        #-----------------------------------------------------------------------
        gamma = IntelligentSlider('gamma', 0.005, 0, self.gamma_changed)
        self.gamma = gamma

        # layout
        self.gamma_widget = QWidget()
        self.gamma_widget.layout = QtGui.QGridLayout(self.gamma_widget)
        self.gamma_widget.layout.addWidget(self.gamma, 0, 0)


        #---------------------------------------------------------------
        # Sigmoid Gamma sliders
        #---------------------------------------------------------------

        # sliders
        alpha = IntelligentSlider('alpha', 0.011, 1, self.sig_gamma_changed)
        beta = IntelligentSlider('beta', 0.012, 0, self.sig_gamma_changed)
        self.a_gamma = alpha
        self.b_gamma = beta

        # layout
        self.sig_gamma_widget = QWidget()
        self.sig_gamma_widget.layout = QtGui.QGridLayout(self.sig_gamma_widget)
        self.sig_gamma_widget.layout.addWidget(self.a_gamma, 0, 0)
        self.sig_gamma_widget.layout.addWidget(self.b_gamma, 0, 1)

        #---------------------------------------------------------------
        # Buttons
        #---------------------------------------------------------------
        self.commit_button = QtGui.QPushButton('Commit')
        self.commit_button.clicked.connect(self.commit_changes)
        self.revert_button = QtGui.QPushButton('Revert')
        self.revert_button.clicked.connect(self.revert_changes)

        #---------------------------------------------------------------
        # Mixer Layout
        #---------------------------------------------------------------
        self.sliders = QStackedWidget()
        self.sliders.addWidget(self.rgb_widget)
        self.sliders.addWidget(self.hsv_widget)
        self.sliders.addWidget(self.bright_widget)
        self.sliders.addWidget(self.gamma_widget)
        self.sliders.addWidget(self.sig_gamma_widget)

        self.layout = QtGui.QGridLayout(self)
        self.layout.addWidget(self.combo_box, 0, 0)
        self.layout.addWidget(self.sliders, 1, 0)
        self.layout.addWidget(self.commit_button, 2, 0)
        self.layout.addWidget(self.revert_button, 3, 0)

        #---------------------------------------------------------------
        # State Initialization
        #---------------------------------------------------------------

        self.combo_box.setCurrentIndex(0)
        self.rgb_mul.setChecked(True)
        self.hsv_mul.setChecked(True)


    def set_callback(self, callback):
        self.callback = callback

    def combo_box_changed(self, index):
        self.sliders.setCurrentIndex(index)
        self.reset()

    def rgb_radio_changed(self):
        self.reset()

    def hsv_radio_changed(self):
        self.reset()

    def reset(self):
        self.reset_sliders()
        self.mixer.set_to_stateimg()
        if self.callback:
            self.callback()

    def reset_sliders(self):
        # handle changing the conversion factors necessary
        if self.rgb_add.isChecked():
            self.rs.set_conv_fac(0.51, -255)
            self.rs.set_value(0)
            self.gs.set_conv_fac(0.51, -255)
            self.gs.set_value(0)
            self.bs.set_conv_fac(0.51, -255)
            self.bs.set_value(0)
        else:
            self.rs.set_conv_fac(0.002, 0)
            self.rs.set_value(1.)
            self.gs.set_conv_fac(0.002, 0)
            self.gs.set_value(1.)
            self.bs.set_conv_fac(0.002, 0)
            self.bs.set_value(1.)

        self.hs.set_value(0)
        if self.hsv_add.isChecked():
            self.ss.set_conv_fac(0.002, -1)
            self.ss.set_value(0)
            self.vs.set_conv_fac(0.002, -1)
            self.vs.set_value(0)
        else:
            self.ss.set_conv_fac(0.002, 0)
            self.ss.set_value(1.)
            self.vs.set_conv_fac(0.002, 0)
            self.vs.set_value(1.)

        self.bright.set_value(0)
        self.cont.set_value(1.)

        self.gamma.set_value(1)
        self.a_gamma.set_value(1)
        self.b_gamma.set_value(0.5)


    def rgb_changed(self, name, val):
        if name == 'R':
            channel = self.mixer.RED
        elif name == 'G':
            channel = self.mixer.GREEN
        else:
            channel = self.mixer.BLUE

        if self.rgb_mul.isChecked():
            self.mixer.multiply(channel, val)
        elif self.rgb_add.isChecked():
            self.mixer.add(channel, val)
        else:
            pass

        if self.callback:
            self.callback()

    def hsv_changed(self, name, val):
        h = self.hs.val()
        s = self.ss.val()
        v = self.vs.val()

        if self.hsv_mul.isChecked():
            self.mixer.hsv_multiply(h, s, v)
        elif self.hsv_add.isChecked():
            self.mixer.hsv_add(h, s, v)
        else:
            pass

        if self.callback:
            self.callback()

    def bright_changed(self, name, val):
        b = self.bright.val()
        c = self.cont.val()
        self.mixer.brightness(c, b)

        if self.callback:
            self.callback()

    def gamma_changed(self, name, val):
        self.mixer.gamma(val)

        if self.callback:
            self.callback()

    def sig_gamma_changed(self, name, val):
        ag = self.a_gamma.val()
        bg = self.b_gamma.val()
        self.mixer.sigmoid_gamma(ag, bg)

        if self.callback:
            self.callback()

    def commit_changes(self):
        self.mixer.commit_changes()
        self.reset_sliders()

    def revert_changes(self):
        self.mixer.revert()
        self.reset_sliders()

        if self.callback:
            self.callback()
Exemple #35
0
class DataExportGui(QWidget):
    """
    Manages all GUI elements in the data selection applet.
    This class itself is the central widget and also owns/manages the applet drawer widgets.
    """

    ###########################################
    ### AppletGuiInterface Concrete Methods ###
    ###########################################

    def centralWidget(self):
        return self

    def appletDrawer(self):
        return self.drawer

    def menus(self):
        return []

    def viewerControlWidget(self):
        return self._viewerControlWidgetStack

    def setImageIndex(self, index):
        pass

    def stopAndCleanUp(self):
        for editor in self.layerViewerGuis.values():
            self.viewerStack.removeWidget(editor)
            editor.stopAndCleanUp()
        self.layerViewerGuis.clear()

    def imageLaneAdded(self, laneIndex):
        pass

    def imageLaneRemoved(self, laneIndex, finalLength):
        pass

    def allowLaneSelectionChange(self):
        return False

    ###########################################
    ###########################################

    def __init__(self, parentApplet, topLevelOperator):
        super(DataExportGui, self).__init__()

        self.drawer = None
        self.topLevelOperator = topLevelOperator

        self.threadRouter = ThreadRouter(self)
        self._thunkEventHandler = ThunkEventHandler(self)

        self._initAppletDrawerUic()
        self.initCentralUic()
        self.initViewerControls()

        self.parentApplet = parentApplet
        self.progressSignal = parentApplet.progressSignal

        self.overwrite = False

        @threadRoutedWithRouter(self.threadRouter)
        def handleNewDataset(multislot, index):
            # Make room in the GUI table
            self.batchOutputTableWidget.insertRow(index)

            # Update the table row data when this slot has new data
            # We can't bind in the row here because the row may change in the meantime.
            multislot[index].notifyReady(bind(self.updateTableForSlot))
            if multislot[index].ready():
                self.updateTableForSlot(multislot[index])

            multislot[index].notifyUnready(self._updateExportButtons)
            multislot[index].notifyReady(self._updateExportButtons)

        self.topLevelOperator.ExportPath.notifyInserted(bind(handleNewDataset))

        # For each dataset that already exists, update the GUI
        for i, subslot in enumerate(self.topLevelOperator.ExportPath):
            handleNewDataset(self.topLevelOperator.ExportPath, i)
            if subslot.ready():
                self.updateTableForSlot(subslot)

        @threadRoutedWithRouter(self.threadRouter)
        def handleLaneRemoved(multislot, index, finalLength):
            if self.batchOutputTableWidget.rowCount() <= finalLength:
                return

            # Remove the row we don't need any more
            self.batchOutputTableWidget.removeRow(index)

            # Remove the viewer for this dataset
            imageMultiSlot = self.topLevelOperator.Inputs[index]
            if imageMultiSlot in self.layerViewerGuis.keys():
                layerViewerGui = self.layerViewerGuis[imageMultiSlot]
                self.viewerStack.removeWidget(layerViewerGui)
                self._viewerControlWidgetStack.removeWidget(
                    layerViewerGui.viewerControlWidget())
                layerViewerGui.stopAndCleanUp()

        self.topLevelOperator.Inputs.notifyRemove(bind(handleLaneRemoved))

    def _initAppletDrawerUic(self, drawerPath=None):
        """
        Load the ui file for the applet drawer, which we own.
        """
        if drawerPath is None:
            localDir = os.path.split(__file__)[0]
            drawerPath = os.path.join(localDir, "dataExportDrawer.ui")
        self.drawer = uic.loadUi(drawerPath)

        self.drawer.settingsButton.clicked.connect(self._chooseSettings)
        self.drawer.exportAllButton.clicked.connect(self.exportAllResults)
        self.drawer.exportAllButton.setIcon(QIcon(ilastikIcons.Save))
        self.drawer.deleteAllButton.clicked.connect(self.deleteAllResults)
        self.drawer.deleteAllButton.setIcon(QIcon(ilastikIcons.Clear))

        @threadRoutedWithRouter(self.threadRouter)
        def _handleNewSelectionNames(*args):
            input_names = self.topLevelOperator.SelectionNames.value
            self.drawer.inputSelectionCombo.addItems(input_names)

        self.topLevelOperator.SelectionNames.notifyDirty(
            _handleNewSelectionNames)
        _handleNewSelectionNames()

        self.drawer.inputSelectionCombo.currentIndexChanged.connect(
            self._handleInputComboSelectionChanged)

    def _handleInputComboSelectionChanged(self, index):
        assert index < len(self.topLevelOperator.SelectionNames.value)
        if self.drawer.inputSelectionCombo.currentText(
        ) == self.topLevelOperator.TableOnlyName.value:
            self.topLevelOperator.TableOnly.setValue(True)
        else:
            self.topLevelOperator.TableOnly.setValue(False)
            self.topLevelOperator.InputSelection.setValue(index)

    def initCentralUic(self):
        """
        Load the GUI from the ui file into this class and connect it with event handlers.
        """
        # Load the ui file into this class (find it in our own directory)
        localDir = os.path.split(__file__)[0]
        uic.loadUi(localDir + "/dataExport.ui", self)

        self.batchOutputTableWidget.resizeRowsToContents()
        self.batchOutputTableWidget.resizeColumnsToContents()
        self.batchOutputTableWidget.setAlternatingRowColors(True)
        self.batchOutputTableWidget.setShowGrid(False)
        self.batchOutputTableWidget.horizontalHeader().setResizeMode(
            0, QHeaderView.Interactive)

        self.batchOutputTableWidget.horizontalHeader().resizeSection(
            Column.Dataset, 200)
        self.batchOutputTableWidget.horizontalHeader().resizeSection(
            Column.ExportLocation, 250)
        self.batchOutputTableWidget.horizontalHeader().resizeSection(
            Column.Action, 100)

        self.batchOutputTableWidget.verticalHeader().hide()

        # Set up handlers
        self.batchOutputTableWidget.itemSelectionChanged.connect(
            self.handleTableSelectionChange)

        # Set up the viewer area
        self.initViewerStack()
        self.splitter.setSizes([150, 850])

    def initViewerStack(self):
        self.layerViewerGuis = {}
        self.viewerStack.addWidget(QWidget())

    def initViewerControls(self):
        self._viewerControlWidgetStack = QStackedWidget(parent=self)

    def showEvent(self, event):
        super(DataExportGui, self).showEvent(event)
        self.showSelectedDataset()

    def hideEvent(self, event):
        super(DataExportGui, self).hideEvent(event)

        # Make sure all 'on disk' layers are discarded so we aren't using those files any more.
        for opLaneView in self.topLevelOperator:
            opLaneView.cleanupOnDiskView()

    def _chooseSettings(self):
        opExportModelOp, opSubRegion = get_model_op(self.topLevelOperator)
        if opExportModelOp is None:
            QMessageBox.information(
                self, "Image not ready for export",
                "Export isn't possible yet: No images are ready for export.  "
                "Please configure upstream pipeline with valid settings, "
                "check that images were specified in the (batch) input applet and try again."
            )
            return

        settingsDlg = DataExportOptionsDlg(self, opExportModelOp)
        if settingsDlg.exec_() == DataExportOptionsDlg.Accepted:
            # Copy the settings from our 'model op' into the real op
            setting_slots = [
                opExportModelOp.RegionStart, opExportModelOp.RegionStop,
                opExportModelOp.InputMin, opExportModelOp.InputMax,
                opExportModelOp.ExportMin, opExportModelOp.ExportMax,
                opExportModelOp.ExportDtype, opExportModelOp.OutputAxisOrder,
                opExportModelOp.OutputFilenameFormat,
                opExportModelOp.OutputInternalPath,
                opExportModelOp.OutputFormat
            ]

            # Disconnect the special 'transaction' slot to prevent these
            #  settings from triggering many calls to setupOutputs.
            self.topLevelOperator.TransactionSlot.disconnect()

            for model_slot in setting_slots:
                real_inslot = getattr(self.topLevelOperator, model_slot.name)
                if model_slot.ready():
                    real_inslot.setValue(model_slot.value)
                else:
                    real_inslot.disconnect()

            # Re-connect the 'transaction' slot to apply all settings at once.
            self.topLevelOperator.TransactionSlot.setValue(True)

            # Discard the temporary model op
            opExportModelOp.cleanUp()
            opSubRegion.cleanUp()

            # Update the gui with the new export paths
            for index, slot in enumerate(self.topLevelOperator.ExportPath):
                self.updateTableForSlot(slot)

    def getSlotIndex(self, multislot, subslot):
        # Which index is this slot?
        for index, slot in enumerate(multislot):
            if slot == subslot:
                return index
        return -1

    @threadRouted
    def updateTableForSlot(self, slot):
        """
        Update the table row that corresponds to the given slot of the top-level operator (could be either input slot)
        """
        row = self.getSlotIndex(self.topLevelOperator.ExportPath, slot)
        assert row != -1, "Unknown input slot!"

        if not self.topLevelOperator.ExportPath[row].ready() or\
           not self.topLevelOperator.RawDatasetInfo[row].ready():
            return

        try:
            nickname = self.topLevelOperator.RawDatasetInfo[row].value.nickname
            exportPath = self.topLevelOperator.ExportPath[row].value
        except Slot.SlotNotReadyError:
            # Sadly, it is possible to get here even though we checked for .ready() immediately beforehand.
            # That's because the graph has a diamond-shaped DAG of connections, but the graph has no transaction mechanism
            # (It's therefore possible for RawDatasetInfo[row] to be ready() even though it's upstream partner is NOT ready.
            return

        self.batchOutputTableWidget.setItem(
            row, Column.Dataset,
            QTableWidgetItem(decode_to_qstring(nickname, 'utf-8')))
        self.batchOutputTableWidget.setItem(
            row, Column.ExportLocation,
            QTableWidgetItem(decode_to_qstring(exportPath)))

        exportNowButton = QPushButton("Export")
        exportNowButton.setToolTip("Generate individual batch output dataset.")
        exportNowButton.clicked.connect(
            bind(self.exportResultsForSlot, self.topLevelOperator[row]))
        self.batchOutputTableWidget.setCellWidget(row, Column.Action,
                                                  exportNowButton)

        # Select a row if there isn't one already selected.
        selectedRanges = self.batchOutputTableWidget.selectedRanges()
        if len(selectedRanges) == 0:
            self.batchOutputTableWidget.selectRow(0)

    def setEnabledIfAlive(self, widget, enable):
        if not sip.isdeleted(widget):
            widget.setEnabled(enable)

    def _updateExportButtons(self, *args):
        """Called when at least one dataset became 'unready', so we have to disable the export button."""
        all_ready = True
        # Enable/disable the appropriate export buttons in the table.
        # Use ThunkEvents to ensure that this happens in the Gui thread.
        for row, slot in enumerate(self.topLevelOperator.ImageToExport):
            all_ready &= slot.ready()
            export_button = self.batchOutputTableWidget.cellWidget(
                row, Column.Action)
            if export_button is not None:
                executable_event = ThunkEvent(
                    partial(self.setEnabledIfAlive, export_button,
                            slot.ready()))
                QApplication.instance().postEvent(self, executable_event)

        # Disable the "Export all" button unless all slots are ready.
        executable_event = ThunkEvent(
            partial(self.setEnabledIfAlive, self.drawer.exportAllButton,
                    all_ready))
        QApplication.instance().postEvent(self, executable_event)

    def handleTableSelectionChange(self):
        """
        Any time the user selects a new item, select the whole row.
        """
        self.selectEntireRow()
        self.showSelectedDataset()

    def selectEntireRow(self):
        # FIXME: There is a better way to do this...
        # Figure out which row is selected
        selectedItemRows = set()
        selectedRanges = self.batchOutputTableWidget.selectedRanges()
        for rng in selectedRanges:
            for row in range(rng.topRow(), rng.bottomRow() + 1):
                selectedItemRows.add(row)

        # Disconnect from selection change notifications while we do this
        self.batchOutputTableWidget.itemSelectionChanged.disconnect(
            self.handleTableSelectionChange)
        for row in selectedItemRows:
            self.batchOutputTableWidget.selectRow(row)

        # Reconnect now that we're finished
        self.batchOutputTableWidget.itemSelectionChanged.connect(
            self.handleTableSelectionChange)

    def exportSlots(self, laneViewList):
        try:
            # Set the busy flag so the workflow knows not to allow
            #  upstream changes or shell changes while we're exporting
            self.parentApplet.busy = True
            self.parentApplet.appletStateUpdateRequested.emit()

            # Disable our own gui
            QApplication.instance().postEvent(
                self,
                ThunkEvent(partial(self.setEnabledIfAlive, self.drawer,
                                   False)))
            QApplication.instance().postEvent(
                self, ThunkEvent(partial(self.setEnabledIfAlive, self, False)))

            # Start with 1% so the progress bar shows up
            self.progressSignal.emit(0)
            self.progressSignal.emit(1)

            def signalFileProgress(slotIndex, percent):
                self.progressSignal.emit(
                    (100 * slotIndex + percent) / len(laneViewList))

            # Client hook
            self.parentApplet.prepare_for_entire_export()

            for i, opLaneView in enumerate(laneViewList):
                lane_index = self.topLevelOperator.innerOperators.index(
                    opLaneView)
                logger.debug("Exporting result {}".format(i))

                # If the operator provides a progress signal, use it.
                slotProgressSignal = opLaneView.progressSignal
                slotProgressSignal.subscribe(partial(signalFileProgress, i))

                try:
                    # Client hook
                    self.parentApplet.prepare_lane_for_export(lane_index)

                    # Export the image
                    opLaneView.run_export()

                    # Client hook
                    if self.parentApplet.postprocessCanCheckForExistingFiles():
                        exportSuccessful = self.parentApplet.post_process_lane_export(
                            lane_index, checkOverwriteFiles=True)
                        if not exportSuccessful:
                            userSelection = [None]
                            self.showOverwriteQuestion(userSelection)
                            if userSelection[0]:
                                self.parentApplet.post_process_lane_export(
                                    lane_index, checkOverwriteFiles=False)
                    else:
                        self.parentApplet.post_process_lane_export(lane_index)

                except Exception as ex:
                    if opLaneView.ExportPath.ready():
                        msg = "Failed to generate export file: \n"
                        msg += opLaneView.ExportPath.value
                        msg += "\n{}".format(ex)
                    else:
                        msg = "Failed to generate export file."
                        msg += "\n{}".format(ex)
                    log_exception(logger, msg)
                    self.showExportError(msg)

                # We're finished with this file.
                self.progressSignal.emit(100 * (i + 1) /
                                         float(len(laneViewList)))

            # Client hook
            self.parentApplet.post_process_entire_export()

            # Ensure the shell knows we're really done.
            self.progressSignal.emit(100)
        except:
            # Cancel our progress.
            self.progressSignal.emit(0, True)
            raise
        finally:
            # We're not busy any more.  Tell the workflow.
            self.parentApplet.busy = False
            self.parentApplet.appletStateUpdateRequested.emit()

            # Re-enable our own gui
            QApplication.instance().postEvent(
                self,
                ThunkEvent(partial(self.setEnabledIfAlive, self.drawer, True)))
            QApplication.instance().postEvent(
                self, ThunkEvent(partial(self.setEnabledIfAlive, self, True)))

    def postProcessLane(self, lane_index):
        """
        Called immediately after the result for each lane is exported.
        Can be overridden by subclasses for post-processing purposes.
        """
        pass

    @threadRouted
    def showExportError(self, msg):
        QMessageBox.critical(self, "Failed to export", msg)

    @threadRouted
    def showOverwriteQuestion(self, userSelection):
        assert isinstance(userSelection, list)
        reply = QMessageBox.question(
            self, 'Warning!',
            'This filename already exists. Are you sure you want to overwrite?',
            QMessageBox.Yes, QMessageBox.No)
        if reply == QMessageBox.Yes:
            userSelection[0] = True
        else:
            userSelection[0] = False

    def exportResultsForSlot(self, opLane):
        # Make sure all 'on disk' layers are discarded so we aren't using those files any more.
        for opLaneView in self.topLevelOperator:
            opLaneView.cleanupOnDiskView()

        # Do this in a separate thread so the UI remains responsive
        exportThread = threading.Thread(target=bind(self.exportSlots,
                                                    [opLane]),
                                        name="DataExportThread")
        exportThread.start()

    def exportAllResults(self):
        # Make sure all 'on disk' layers are discarded so we aren't using those files any more.
        for opLaneView in self.topLevelOperator:
            opLaneView.cleanupOnDiskView()

        # Do this in a separate thread so the UI remains responsive
        exportThread = threading.Thread(target=bind(self.exportSlots,
                                                    self.topLevelOperator),
                                        name="DataExportThread")
        exportThread.start()

    def deleteAllResults(self):
        for innerOp in self.topLevelOperator:
            operatorView = innerOp
            operatorView.cleanupOnDiskView()
            pathComp = PathComponents(operatorView.ExportPath.value,
                                      operatorView.WorkingDirectory.value)
            if os.path.exists(pathComp.externalPath):
                os.remove(pathComp.externalPath)
            operatorView.setupOnDiskView()
            # we need to toggle the dirts state in order to enforce a frech dirty signal
            operatorView.Dirty.setValue(False)
            operatorView.Dirty.setValue(True)

    def showSelectedDataset(self):
        """
        Show the exported file in the viewer
        """
        # Get the selected row and corresponding slot value
        selectedRanges = self.batchOutputTableWidget.selectedRanges()
        if len(selectedRanges) == 0:
            return
        row = selectedRanges[0].topRow()

        # Hide all layers that come from the disk.
        for opLaneView in self.topLevelOperator:
            opLaneView.cleanupOnDiskView()

        # Activate the 'on disk' layers for this lane (if possible)
        opLane = self.topLevelOperator.getLane(row)
        opLane.setupOnDiskView()

        # Create if necessary
        imageMultiSlot = self.topLevelOperator.Inputs[row]
        if imageMultiSlot not in self.layerViewerGuis.keys():
            layerViewer = self.createLayerViewer(opLane)

            # Maximize the x-y view by default.
            layerViewer.volumeEditorWidget.quadview.ensureMaximized(2)

            self.layerViewerGuis[imageMultiSlot] = layerViewer
            self.viewerStack.addWidget(layerViewer)
            self._viewerControlWidgetStack.addWidget(
                layerViewer.viewerControlWidget())

        # Show the right one
        layerViewer = self.layerViewerGuis[imageMultiSlot]
        self.viewerStack.setCurrentWidget(layerViewer)
        self._viewerControlWidgetStack.setCurrentWidget(
            layerViewer.viewerControlWidget())

    def createLayerViewer(self, opLane):
        """
        This method provides an instance of LayerViewerGui for the given data lane.
        If this GUI class is subclassed, this method can be reimplemented to provide 
        custom layer types for the exported layers.
        """
        return DataExportLayerViewerGui(self.parentApplet, opLane)
Exemple #36
0
class ConfigDialog(QDialog):
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        self.contents_widget = QListWidget()
        self.contents_widget.setMovement(QListView.Static)
        self.contents_widget.setMinimumWidth(120)
        self.contents_widget.setMaximumWidth(120)
        self.contents_widget.setSpacing(1)

        bbox = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Apply
                                     |QDialogButtonBox.Cancel)
        self.apply_btn = bbox.button(QDialogButtonBox.Apply)
        self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()"))
        self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()"))
        self.connect(bbox, SIGNAL("clicked(QAbstractButton*)"),
                     self.button_clicked)

        self.pages_widget = QStackedWidget()
        self.connect(self.pages_widget, SIGNAL("currentChanged(int)"),
                     self.current_page_changed)

        self.connect(self.contents_widget, SIGNAL("currentRowChanged(int)"),
                     self.pages_widget.setCurrentIndex)
        self.contents_widget.setCurrentRow(0)

        hlayout = QHBoxLayout()
        hlayout.addWidget(self.contents_widget)
        hlayout.addWidget(self.pages_widget)
        hlayout.setStretch(1,1)

        btnlayout = QHBoxLayout()
        btnlayout.addStretch(1)
        btnlayout.addWidget(bbox)

        vlayout = QVBoxLayout()
        vlayout.addLayout(hlayout)
        vlayout.addLayout(btnlayout)

        self.setLayout(vlayout)

        self.setWindowTitle("Preferences")
        self.setWindowIcon(get_icon("configure.png"))
        
    def get_current_index(self):
        """Return current page index"""
        return self.contents_widget.currentRow()
        
    def set_current_index(self, index):
        """Set current page index"""
        self.contents_widget.setCurrentRow(index)
        
    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.pages_widget.count()):
            configpage = self.pages_widget.widget(index)
            if not configpage.is_valid():
                return
            configpage.apply_changes()
        QDialog.accept(self)
        
    def button_clicked(self, button):
        if button is self.apply_btn:
            # Apply button was clicked
            configpage = self.pages_widget.currentWidget()
            if not configpage.is_valid():
                return
            configpage.apply_changes()
            
    def current_page_changed(self, index):
        widget = self.pages_widget.widget(index)
        self.apply_btn.setVisible(widget.apply_callback is not None)
        self.apply_btn.setEnabled(widget.is_modified)
        
    def add_page(self, widget):
        self.connect(self, SIGNAL('check_settings()'), widget.check_settings)
        self.connect(widget, SIGNAL('show_this_page()'),
                     lambda row=self.contents_widget.count():
                     self.contents_widget.setCurrentRow(row))
        self.connect(widget, SIGNAL("apply_button_enabled(bool)"),
                     self.apply_btn.setEnabled)
        self.pages_widget.addWidget(widget)
        item = QListWidgetItem(self.contents_widget)
        item.setIcon(widget.get_icon())
        item.setText(widget.get_name())
        item.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
        item.setSizeHint(QSize(0, 25))
        
    def check_all_settings(self):
        """This method is called to check all configuration page settings
        after configuration dialog has been shown"""
        self.emit(SIGNAL('check_settings()'))
Exemple #37
0
class Declaration(QDialog, ui_declaration.Ui_Declaration):
    def __init__(self, parent, noi):
        super(Declaration, self).__init__(parent)
        self.setupUi(self)
        self.noidec = noi
        self.parent = parent
        self.scenario = parent.scenario


        self.pages_widget = QStackedWidget()
        self.connect(self.pages_widget, SIGNAL("currentChanged(int)"), self.current_page_changed)
        self.connect(self.contents_widget, SIGNAL("currentRowChanged(int)"), self.pages_widget.setCurrentIndex)
                
        self.scrollArea.setWidget(self.pages_widget)

        self.connect(self.next_btn, SIGNAL('clicked()'), self.next_page)
        self.connect(self.prev_btn, SIGNAL('clicked()'), self.prev_page)

        self.pages = [Page01(self),  Page02(self), Page03(self), Page04(self), 
                      Page05(self), Page06(self), Page07(self), PageIsf(self)]

        for widget in self.pages:
            self.add_page(widget)


        self.set_current_index(0)
        self.current_page_changed(0)

    def current_page_changed(self, index):
        nb = self.pages_widget.count() - 1
        self.prev_btn.setEnabled(True)
        self.next_btn.setEnabled(True)
        if index == nb:
            self.next_btn.setEnabled(False)
        if index == 0:
            self.prev_btn.setEnabled(False)            

    def next_page(self):
        idx = self.pages_widget.currentIndex()
        self.set_current_index(idx + 1)

    def prev_page(self):
        idx = self.pages_widget.currentIndex()
        self.set_current_index(idx - 1)

    def get_current_index(self):
        """Return current page index"""
        return self.contents_widget.currentRow()
        
    def set_current_index(self, index):
        """Set current page index"""
        self.contents_widget.setCurrentRow(index)
        self.pages_widget.setCurrentIndex(index)

    def accept(self):
        for page in self.pages:
            for key in page.__dict__:
                widget = getattr(page,key)
                if  isinstance(widget, QSpinBox):
                    var = str(widget.objectName())
                    val = widget.value()
                    page.updateFoyer(var, val)
                elif isinstance(widget, QCheckBox):
                    var = str(widget.objectName())
                    val = 1*(widget.checkState()>=1)
                    page.updateFoyer(var, val)
        QDialog.accept(self)

    def add_page(self, widget):
        self.pages_widget.addWidget(widget)
        item = QListWidgetItem(self.contents_widget)
        item.setText(widget.get_name())
        item.setFlags(Qt.ItemIsSelectable|Qt.ItemIsEnabled)
Exemple #38
0
class FltsSearchDockWidget(QDockWidget):
    """
    Dock widget for showing search widgets with each widget corresponding to
    a search configuration.
    """
    def __init__(self, *args, **kwargs):
        super(FltsSearchDockWidget, self).__init__(*args, **kwargs)
        self.setAllowedAreas(Qt.BottomDockWidgetArea)
        self._search_reg = SearchConfigurationRegistry.instance()
        self._stack_widget = QStackedWidget(self)
        self.setWidget(self._stack_widget)

        # Index data source to their location in the stack widget
        self._search_widget_idx = dict()

    def show_search_widget(self, data_source):
        """
        Shows the search widget associated with the given data source. If
        not found then it will create a new one by using the factory
        method in the search configuration.
        :param data_source: Data source name.
        :type data_source: str
        :return: Returns True if the operation was successful else False. It
        will be false if the data source is not found in the registry.
        :rtype: bool
        """
        config = self._search_reg.search_config(data_source)
        if not config:
            return False

        # Set current search widget
        self._set_current_widget(config)

        return True

    def _set_current_widget(self, config):
        # Updates dock widget based on the specified config.
        data_source = config.data_source

        # Create widget if it does not exist in the stack
        if not data_source in self._search_widget_idx:
            search_widget = config.create_widget()
            idx = self._stack_widget.addWidget(search_widget)
            self._search_widget_idx[data_source] = idx
        else:
            idx = self._search_widget_idx[data_source]

        self._stack_widget.setCurrentIndex(idx)

        # Set title
        self.setWindowTitle(u'Search {0}'.format(config.display_name))

    def current_widget(self):
        """
        :return: Returns the current search widget or None if there are no
        widgets in the stack.
        :rtype: QWidget
        """
        return self._stack_widget.currentWidget()

    def clear(self):
        """
        Removes all the search widgets and resets the indices.
        """
        while self._stack_widget.count() > 0:
            sw = self._stack_widget.widget(0)
            self._stack_widget.removeWidget(sw)
            del sw

        self._search_widget_idx = dict()
Exemple #39
0
class ArrayEditor(QDialog):
    """Array Editor Dialog"""    
    def __init__(self, parent=None):
        super(ArrayEditor, self).__init__(parent)
    
    def setup_and_check(self, data, title='', xy=False, readonly=False):
        """
        Setup ArrayEditor:
        return False if data is not supported, True otherwise
        """
        self.arraywidget = None
        self.is_record_array = data.dtype.names is not None
        if data.ndim > 2:
            self.error(self.tr("Arrays with more than 2 dimensions "
                               "are not supported"))
            return False
        if not self.is_record_array:
            dtn = data.dtype.name
            if dtn not in SUPPORTED_FORMATS and not dtn.startswith('string') \
               and not dtn.startswith('unicode'):
                arr = self.tr("%1 arrays").arg(data.dtype.name)
                self.error(self.tr("%1 are currently not supported").arg(arr))
                return False
        
        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.setWindowIcon(get_icon('arredit.png'))
        title = self.tr("Array editor") + \
                "%s" % (" - "+str(title) if str(title) else "")
        if readonly:
            title += ' (' + self.tr('read only') + ')'
        self.setWindowTitle(title)
        self.resize(600, 500)
        
        # Stack widget
        self.stack = QStackedWidget(self)
        if self.is_record_array:
            for name in data.dtype.names:
                self.stack.addWidget(ArrayEditorWidget(self, data[name],
                                                       xy, readonly))
        else:
            self.stack.addWidget(ArrayEditorWidget(self, data, xy, readonly))
        self.arraywidget = self.stack.currentWidget()
        self.connect(self.stack, SIGNAL('currentChanged(int)'),
                     self.current_widget_changed)
        self.layout.addWidget(self.stack, 1, 0)

        # Buttons configuration
        btn_layout = QHBoxLayout()
        if self.is_record_array:
            btn_layout.addWidget(QLabel(self.tr("Record array fields:")))
            ra_combo = QComboBox(self)
            self.connect(ra_combo, SIGNAL('currentIndexChanged(int)'),
                         self.stack.setCurrentIndex)
            names = []
            for name in data.dtype.names:
                field = data.dtype.fields[name]
                text = name
                if len(field) >= 3:
                    title = field[2]
                    if not isinstance(title, basestring):
                        title = repr(title)
                    text += ' - '+title
                names.append(text)
            ra_combo.addItems(names)
            btn_layout.addWidget(ra_combo)
            btn_layout.addStretch()
        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()"))
        self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()"))
        btn_layout.addWidget(bbox)
        self.layout.addLayout(btn_layout, 2, 0)
        
        self.setMinimumSize(400, 300)
        
        # Make the dialog act as a window
        self.setWindowFlags(Qt.Window)
        
        return True
        
    def current_widget_changed(self, index):
        self.arraywidget = self.stack.widget(index)
        
    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.stack.count()):
            self.stack.widget(index).accept_changes()
        QDialog.accept(self)

    def error(self, message):
        """An error occured, closing the dialog box"""
        QMessageBox.critical(self, self.tr("Array editor"), message)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.reject()

    def reject(self):
        """Reimplement Qt method"""
        if self.arraywidget is not None:
            for index in range(self.stack.count()):
                self.stack.widget(index).reject_changes()
        QDialog.reject(self)
Exemple #40
0
    def initGUI(self):
        self.setWindowTitle(QApplication.applicationName())
        self.fmt = QFontMetrics(QFont())
        layout = QVBoxLayout()
        
        toplayout = QHBoxLayout()
        currentLayout = QGridLayout()
        currentLayout.setContentsMargins(0,20,0,20)
        currentLayout.setHorizontalSpacing(100)
        currentLayout.setVerticalSpacing(20)
        # current song information
        currentLayout.addWidget(QLabel("<b>Artist</b>"),1,0)
        self.artist = QLabel()
        currentLayout.addWidget(self.artist,1,1)
        currentLayout.addWidget(QLabel("<b>Title</b>"),2,0)
        self.title  = QLabel()
        currentLayout.addWidget(self.title,2,1)
        currentLayout.addWidget(QLabel("<b>Albumartist</b>"),3,0)
        self.albumartist = QLabel()
        currentLayout.addWidget(self.albumartist,3,1)
        currentLayout.addWidget(QLabel("<b>Album</b>"),4,0)
        self.album  = QLabel()
        currentLayout.addWidget(self.album,4,1)
        # playlist and song position
        self.playlistposition = QLabel()
        currentLayout.addWidget(self.playlistposition,5,0)
        poslayout = QHBoxLayout()
        poslayout.setSpacing(10)
        self.time = QLabel("00:00")
        poslayout.addWidget(self.time)
        self.position = QSlider(Qt.Horizontal)
        self.position.setTracking(False)
        self.position.setSingleStep(10)
        self.position.sliderReleased.connect( self.seek)
        self.position.sliderMoved.connect(
            lambda x: self.time.setText(self.mpd.timeString(x)))
        poslayout.addWidget(self.position)
        self.length = QLabel("00:00")
        poslayout.addWidget(self.length)
        currentLayout.addLayout(poslayout,5,1)
        toplayout.addLayout(currentLayout)
        layout.addLayout(toplayout)
        layout.addStretch(1)
        
        self.settingsWidget = QMenu()
        self.consumeBtn = self.settingsWidget.addAction("Consume")
        self.consumeBtn.setCheckable(True)
        self.consumeBtn.triggered.connect( lambda x: self.mpd.consume(int(x)))
        self.singleBtn  = self.settingsWidget.addAction("Single")
        self.singleBtn.setCheckable(True)
        self.singleBtn.triggered.connect( lambda x: self.mpd.single(int(x)))
        
        toolLayout = QHBoxLayout()
        self.settingsBtn = QToolButton()
        self.settingsBtn.setFixedSize(64,64)
        self.settingsBtn.setIcon( self.ih.settingsButton)
        self.settingsBtn.clicked.connect(self.showAdditionalControls)
        toolLayout.addWidget(self.settingsBtn)

        
        toolWidget = QStackedWidget()
        transpWidget = QWidget()
        transpLayout = QHBoxLayout()
        self.prevBtn = self.createButton(
            self.ih.prevButton, self.ih.prevButtonPressed)
        self.prevBtn.clicked.connect( lambda x: self.mpd.previous())
        transpLayout.addWidget(self.prevBtn)
        self.playBtn = self.createCheckButton(
            self.ih.playButton, self.ih.pauseButton)
        self.playBtn.clicked.connect( self.playPressed)
        transpLayout.addWidget(self.playBtn)
        self.stopBtn = self.createButton(
            self.ih.stopButton, self.ih.stopButtonPressed)
        self.stopBtn.clicked.connect( lambda x: self.mpd.stop())
        transpLayout.addWidget(self.stopBtn)
        self.nextBtn = self.createButton(
            self.ih.nextButton, self.ih.nextButtonPressed)
        self.nextBtn.clicked.connect( lambda x: self.mpd.next())
        transpLayout.addWidget(self.nextBtn)
        self.shuffleBtn = self.createCheckButton(
            self.ih.shuffleButton, self.ih.shuffleButtonPressed)
        self.shuffleBtn.toggled.connect(
            lambda x: self.mpd.random(1) if x else self.mpd.random(0))
        transpLayout.addWidget(self.shuffleBtn)
        self.repeatBtn = self.createCheckButton(
            self.ih.repeatButton, self.ih.repeatButtonPressed)
        self.repeatBtn.toggled.connect(
            lambda x: self.mpd.repeat(1) if x else self.mpd.repeat(0))
        transpLayout.addWidget(self.repeatBtn)
        transpLayout.addSpacing(64)
        transpWidget.setLayout(transpLayout)
        toolWidget.addWidget( transpWidget)
        
        self.volume = QSlider(Qt.Horizontal)
        self.volume.valueChanged.connect(self.mpd.setvol)
        toolWidget.addWidget(self.volume)
        
        toolLayout.addWidget(toolWidget)
        self.volumeBtn = QToolButton()
        self.volumeBtn.setFixedSize(64,64)
        self.volumeBtn.setCheckable(True)
        self.volumeBtn.setIcon( self.ih.volumeButton)
        self.volumeBtn.toggled.connect(
            lambda x: toolWidget.setCurrentIndex(x))
        toolLayout.addWidget(self.volumeBtn)
        layout.addLayout(toolLayout)
        self.setLayout(layout)
Exemple #41
0
class GUI(object):

    def __init__(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        
        # Set size of window
        MainWindow.resize(800, 589)
        MainWindow.setFocusPolicy(QtCore.Qt.NoFocus)
        MainWindow.setWindowTitle("Text to Kill")
        
        self.centralwidget = QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")

        self.gridLayout = QGridLayout(self.centralwidget)
        self.gridLayout.setMargin(0)
        self.gridLayout.setObjectName("gridLayout")
        self.stackedWidget = QStackedWidget(self.centralwidget)
        self.stackedWidget.setEnabled(True)
        self.stackedWidget.setObjectName("stackedWidget")
        
        font = QFont()
        font.setFamily("Times New Roman")
        
        # Main menu page
        self.menuPage = QWidget()
        self.menuPage.setObjectName("menuPage")
        self.titleLabel = QLabel(self.menuPage)
        self.titleLabel.setGeometry(QtCore.QRect(250, 60, 300, 50))
        font.setPointSize(45)
        self.titleLabel.setFont(font)
        self.titleLabel.setAlignment(QtCore.Qt.AlignCenter)
        self.titleLabel.setObjectName("titleLabel")
        self.titleLabel.setText("Text to Kill")
        self.subtitleLabel = QLabel(self.menuPage)
        self.subtitleLabel.setGeometry(QtCore.QRect(100, 140, 600, 40))
        font.setPointSize(25)
        self.subtitleLabel.setFont(font)
        self.subtitleLabel.setAlignment(QtCore.Qt.AlignCenter)
        self.subtitleLabel.setObjectName("subtitleLabel")
        self.subtitleLabel.setText("The Murder Mystery Automation System")
        self.createButton = QPushButton(self.menuPage)
        self.createButton.setGeometry(QtCore.QRect(310, 260, 180, 60))
        self.createButton.setObjectName("createButton")
        self.createButton.setText("Create Game")
        self.runButton = QPushButton(self.menuPage)
        self.runButton.setGeometry(QtCore.QRect(310, 350, 180, 60))
        self.runButton.setObjectName("runButton")
        self.runButton.setText("Run Game")
        self.stackedWidget.addWidget(self.menuPage)
        
        # Create page
        self.createPage = QWidget()
        self.createPage.setObjectName("createPage")
        self.createTabWidget = QTabWidget(self.createPage)
        self.createTabWidget.setGeometry(QtCore.QRect(0, 0, 800, 600))

        self.createTabWidget.setFocusPolicy(QtCore.Qt.NoFocus)
        self.createTabWidget.setObjectName("createTabWidget")
        
        # Create game tab
        self.createTab = QWidget()
        self.createTab.setObjectName("createTab")
        self.createDoneButton = QPushButton(self.createTab)
        self.createDoneButton.setGeometry(QtCore.QRect(580, 470, 180, 60))
        self.createDoneButton.setObjectName("createDoneButton")
        self.createDoneButton.setText("Done")
        self.gameNameEdit = QLineEdit(self.createTab)
        self.gameNameEdit.setGeometry(QtCore.QRect(140, 20, 160, 30))
        self.gameNameEdit.setObjectName("gameNameEdit")
        self.gameNameLabel = QLabel(self.createTab)
        self.gameNameLabel.setGeometry(QtCore.QRect(20, 25, 110, 20))

        font.setPointSize(15)
        self.gameNameLabel.setFont(font)
        self.gameNameLabel.setAlignment(QtCore.Qt.AlignCenter)
        self.gameNameLabel.setObjectName("gameNameLabel")
        self.gameNameLabel.setText("Game name")
        self.line = QFrame(self.createTab)
        self.line.setGeometry(QtCore.QRect(20, 150, 311, 20))
        self.line.setFrameShape(QFrame.HLine)
        self.line.setFrameShadow(QFrame.Sunken)
        self.line.setObjectName("line")
        self.addCharLabel = QLabel(self.createTab)
        self.addCharLabel.setGeometry(QtCore.QRect(20, 180, 160, 20))
        
        font.setPointSize(20)
        self.addCharLabel.setFont(font)
        self.addCharLabel.setAlignment(QtCore.Qt.AlignCenter)
        self.addCharLabel.setObjectName("addCharLabel")
        self.addCharLabel.setText("Add Character")
        self.charNameLabel = QLabel(self.createTab)
        self.charNameLabel.setGeometry(QtCore.QRect(20, 230, 66, 20))

        font.setPointSize(15)
        self.charNameLabel.setFont(font)
        self.charNameLabel.setAlignment(QtCore.Qt.AlignCenter)
        self.charNameLabel.setObjectName("charNameLabel")
        self.charNameLabel.setText("Name")
        self.charNameEdit = QLineEdit(self.createTab)
        self.charNameEdit.setGeometry(QtCore.QRect(140, 220, 160, 30))
        self.charNameEdit.setObjectName("charNameEdit")
        self.charAbilScroll = QListWidget(self.createTab)
        self.charAbilScroll.setGeometry(QtCore.QRect(140, 260, 161, 51))
        self.charAbilScroll.setObjectName("charAbilScroll")
        self.characterTable = QTableWidget(self.createTab)
        self.characterTable.setGeometry(QtCore.QRect(405, 20, 381, 401))
        self.characterTable.setFocusPolicy(QtCore.Qt.NoFocus)
        self.characterTable.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.characterTable.setRowCount(1)
        self.characterTable.setColumnCount(2)
        self.characterTable.setObjectName("characterTable")
        self.characterTable.horizontalHeader().setVisible(False)
        self.characterTable.horizontalHeader().setCascadingSectionResizes(False)
        self.characterTable.horizontalHeader().setMinimumSectionSize(50)
        self.characterTable.horizontalHeader().setStretchLastSection(True)
        self.characterTable.verticalHeader().setVisible(False)
        self.characterTable.verticalHeader().setDefaultSectionSize(30)
        self.scrollArea = QListWidget(self.createTab)
        self.scrollArea.setGeometry(QtCore.QRect(140, 60, 161, 71))
        self.scrollArea.setObjectName("scrollArea")
        self.scrollArea.setSelectionMode(3)
        self.createSaveButton = QPushButton(self.createTab)
        self.createSaveButton.setGeometry(QtCore.QRect(380, 470, 180, 60))
        self.createSaveButton.setObjectName("createSaveButton")
        self.createSaveButton.setText("Save")
        self.charAbilitiesLabel = QLabel(self.createTab)
        self.charAbilitiesLabel.setGeometry(QtCore.QRect(30, 280, 71, 20))

        font.setPointSize(15)
        self.charAbilitiesLabel.setFont(font)
        self.charAbilitiesLabel.setObjectName("charAbilitiesLabel")
        self.charAbilitiesLabel.setText("Abilities")
        self.abilitiesDropdown = QComboBox(self.createTab)
        self.abilitiesDropdown.setGeometry(QtCore.QRect(140, 330, 151, 25))
        self.abilitiesDropdown.setObjectName("abilitiesDropdown")
        
        self.addCharButton = QPushButton(self.createTab)
        self.addCharButton.setGeometry(QtCore.QRect(30, 370, 98, 27))
        self.addCharButton.setObjectName("addCharButton")
        self.addCharButton.setText("Add")
        self.gameAbilitiesLabel = QLabel(self.createTab)
        self.gameAbilitiesLabel.setGeometry(QtCore.QRect(30, 80, 71, 20))
        
        self.setGameAbilButton = QPushButton(self.createTab)
        self.setGameAbilButton.setGeometry(QtCore.QRect(30, 110, 71, 27))
        self.setGameAbilButton.setObjectName("setGameAbilButton")
        self.setGameAbilButton.setText("Set")
        
        self.saveCharButton = QPushButton(self.createTab)
        self.saveCharButton.setGeometry(QtCore.QRect(70, 430, 180, 60))
        self.saveCharButton.setObjectName("saveCharButton")
        self.saveCharButton.setText("Save Character")

        font.setPointSize(15)
        self.gameAbilitiesLabel.setFont(font)
        self.gameAbilitiesLabel.setObjectName("gameAbilitiesLabel")
        self.gameAbilitiesLabel.setText("Abilities")
        self.createTabWidget.addTab(self.createTab, "")
        
        # Setup tab widget
        self.setupTab = QWidget()
        self.setupTab.setObjectName("setupTab")
        self.setupDoneButton = QPushButton(self.setupTab)
        self.setupDoneButton.setGeometry(QtCore.QRect(580, 470, 180, 60))
        self.setupDoneButton.setObjectName("setupDoneButton")
        self.setupDoneButton.setText("Done")
        self.setupTable = QTableWidget(self.setupTab)
        self.setupTable.setGeometry(QtCore.QRect(20, 20, 750, 400))
        self.setupTable.setFocusPolicy(QtCore.Qt.TabFocus)
        self.setupTable.setRowCount(1)
        self.setupTable.setColumnCount(3)
        self.setupTable.setObjectName("setupTable")
        self.setupTable.horizontalHeader().setVisible(False)
        self.setupTable.horizontalHeader().setCascadingSectionResizes(False)
        self.setupTable.horizontalHeader().setDefaultSectionSize(187)
        self.setupTable.horizontalHeader().setHighlightSections(False)
        self.setupTable.horizontalHeader().setStretchLastSection(True)
        self.setupTable.verticalHeader().setVisible(False)
        self.setupTable.verticalHeader().setHighlightSections(False)
        self.setupSaveButton = QPushButton(self.setupTab)
        self.setupSaveButton.setGeometry(QtCore.QRect(380, 470, 180, 60))
        self.setupSaveButton.setObjectName("setupSaveButton")
        self.setupSaveButton.setText("Save")
        self.createTabWidget.addTab(self.setupTab, "")
        self.createTabWidget.setTabText(self.createTabWidget.indexOf(self.createTab), "Create New Game")
        self.createTabWidget.setTabText(self.createTabWidget.indexOf(self.setupTab), "Set Up Game")
        self.stackedWidget.addWidget(self.createPage)
        
        # Game page
        self.gamePage = QWidget()
        self.gamePage.setObjectName("gamePage")
        self.gameTabWidget = QTabWidget(self.gamePage)
        self.gameTabWidget.setGeometry(QtCore.QRect(0, 0, 800, 600))
        self.gameTabWidget.setFocusPolicy(QtCore.Qt.NoFocus)
        self.gameTabWidget.setObjectName("gameTabWidget")
        self.statusTab = QWidget()
        self.statusTab.setObjectName("statusTab")
        self.startGameButton = QPushButton(self.statusTab)
        self.startGameButton.setGeometry(QtCore.QRect(60, 180, 180, 60))
        self.startGameButton.setObjectName("startGameButton")
        self.startGameButton.setText("Start Game")
        self.endGameButton = QPushButton(self.statusTab)
        self.endGameButton.setGeometry(QtCore.QRect(60, 260, 180, 60))
        self.endGameButton.setObjectName("endGameButton")
        self.endGameButton.setText("End Game")
        self.loadGameLabel = QLabel(self.statusTab)
        self.loadGameLabel.setGeometry(QtCore.QRect(20, 65, 101, 21))

        font.setPointSize(15)
        self.loadGameLabel.setFont(font)
        self.loadGameLabel.setObjectName("loadGameLabel")
        self.loadGameLabel.setText("Load Game")
        self.gameTabWidget.addTab(self.statusTab, "")
        self.logTab = QWidget()
        self.logTab.setObjectName("logTab")
        self.logList = QListWidget(self.logTab)
        self.logList.setGeometry(QtCore.QRect(30, 30, 730, 500))
        self.logList.setObjectName("logList")
        self.gameTabWidget.addTab(self.logTab, "")
        self.inputTab = QWidget()
        self.inputTab.setObjectName("inputTab")
        self.gameTabWidget.addTab(self.inputTab, "")
        self.gameTabWidget.setTabText(self.gameTabWidget.indexOf(self.statusTab), "Game Status")
        self.gameTabWidget.setTabText(self.gameTabWidget.indexOf(self.logTab), "Game Log")
        self.gameTabWidget.setTabText(self.gameTabWidget.indexOf(self.inputTab), "Input")
        self.stackedWidget.addWidget(self.gamePage)
        self.gridLayout.addWidget(self.stackedWidget, 0, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        self.stackedWidget.setCurrentIndex(0)
        self.createTabWidget.setCurrentIndex(0)
        self.gameTabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        pass
Exemple #42
0
class ArrayEditor(QDialog):
    """Array Editor Dialog"""
    def __init__(self, parent=None):
        super(ArrayEditor, self).__init__(parent)

    def setup_and_check(self, data, title='', xy=False, readonly=False):
        """
        Setup ArrayEditor:
        return False if data is not supported, True otherwise
        """
        self.arraywidget = None
        self.is_record_array = data.dtype.names is not None
        if len(data.shape) > 2:
            self.error(
                self.tr("Arrays with more than 2 dimensions "
                        "are not supported"))
            return False
        if not self.is_record_array:
            dtn = data.dtype.name
            if dtn not in SUPPORTED_FORMATS and not dtn.startswith('string') \
               and not dtn.startswith('unicode'):
                arr = self.tr("%1 arrays").arg(data.dtype.name)
                self.error(self.tr("%1 are currently not supported").arg(arr))
                return False

        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.setWindowIcon(get_icon('arredit.png'))
        title = self.tr("Array editor") + \
                "%s" % (" - "+str(title) if str(title) else "")
        if readonly:
            title += ' (' + self.tr('read only') + ')'
        self.setWindowTitle(title)
        self.resize(600, 500)

        # Stack widget
        self.stack = QStackedWidget(self)
        if self.is_record_array:
            for name in data.dtype.names:
                self.stack.addWidget(
                    ArrayEditorWidget(self, data[name], xy, readonly))
        else:
            self.stack.addWidget(ArrayEditorWidget(self, data, xy, readonly))
        self.arraywidget = self.stack.currentWidget()
        self.connect(self.stack, SIGNAL('currentChanged(int)'),
                     self.current_widget_changed)
        self.layout.addWidget(self.stack, 1, 0)

        # Buttons configuration
        btn_layout = QHBoxLayout()
        if self.is_record_array:
            btn_layout.addWidget(QLabel(self.tr("Record array fields:")))
            ra_combo = QComboBox(self)
            self.connect(ra_combo, SIGNAL('currentIndexChanged(int)'),
                         self.stack.setCurrentIndex)
            names = []
            for name in data.dtype.names:
                field = data.dtype.fields[name]
                text = name
                if len(field) >= 3:
                    title = field[2]
                    if not isinstance(title, basestring):
                        title = repr(title)
                    text += ' - ' + title
                names.append(text)
            ra_combo.addItems(names)
            btn_layout.addWidget(ra_combo)
            btn_layout.addStretch()
        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()"))
        self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()"))
        btn_layout.addWidget(bbox)
        self.layout.addLayout(btn_layout, 2, 0)

        self.setMinimumSize(400, 300)

        # Make the dialog act as a window
        self.setWindowFlags(Qt.Window)

        return True

    def current_widget_changed(self, index):
        self.arraywidget = self.stack.widget(index)

    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.stack.count()):
            self.stack.widget(index).accept_changes()
        QDialog.accept(self)

    def error(self, message):
        """An error occured, closing the dialog box"""
        QMessageBox.critical(self, self.tr("Array editor"), message)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.reject()

    def reject(self):
        """Reimplement Qt method"""
        if self.arraywidget is not None:
            for index in range(self.stack.count()):
                self.stack.widget(index).reject_changes()
        QDialog.reject(self)
class QgsTextAnnotationDialog(QDialog):
    def __init__(self, item):
        QDialog.__init__(self)
        self.gridLayout = QGridLayout(self)
        self.gridLayout.setObjectName(("gridLayout"))
        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setObjectName(("horizontalLayout"))
        self.mFontComboBox = QFontComboBox(self)
        self.mFontComboBox.setObjectName(("mFontComboBox"))
        self.horizontalLayout.addWidget(self.mFontComboBox)
        spacerItem = QSpacerItem(38, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.mFontSizeSpinBox = QSpinBox(self)
        self.mFontSizeSpinBox.setObjectName(("mFontSizeSpinBox"))
        self.horizontalLayout.addWidget(self.mFontSizeSpinBox)
        self.mBoldPushButton = QPushButton(self)
        self.mBoldPushButton.setMinimumSize(QSize(50, 0))
        self.mBoldPushButton.setCheckable(True)
        self.mBoldPushButton.setObjectName(("mBoldPushButton"))
        self.horizontalLayout.addWidget(self.mBoldPushButton)
        self.mItalicsPushButton = QPushButton(self)
        self.mItalicsPushButton.setMinimumSize(QSize(50, 0))
        self.mItalicsPushButton.setCheckable(True)
        self.mItalicsPushButton.setObjectName(("mItalicsPushButton"))
        self.horizontalLayout.addWidget(self.mItalicsPushButton)
        self.mFontColorButton = QgsColorButton(self)
        self.mFontColorButton.setText((""))
        self.mFontColorButton.setAutoDefault(False)
        self.mFontColorButton.setObjectName(("mFontColorButton"))
        self.horizontalLayout.addWidget(self.mFontColorButton)
        self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1)
        self.mButtonBox = QDialogButtonBox(self)
        self.mButtonBox.setOrientation(Qt.Horizontal)
        self.mButtonBox.setStandardButtons(QDialogButtonBox.Cancel|QDialogButtonBox.Ok)
        self.mButtonBox.setObjectName(("mButtonBox"))
        self.gridLayout.addWidget(self.mButtonBox, 3, 0, 1, 1)
        self.mTextEdit = QTextEdit(self)
        self.mTextEdit.setObjectName(("mTextEdit"))
        self.gridLayout.addWidget(self.mTextEdit, 1, 0, 1, 1)
        self.mStackedWidget = QStackedWidget(self)
        self.mStackedWidget.setObjectName(("mStackedWidget"))
        self.page = QWidget()
        self.page.setObjectName(("page"))
        self.mStackedWidget.addWidget(self.page)
        self.page_2 = QWidget()
        self.page_2.setObjectName(("page_2"))
        self.mStackedWidget.addWidget(self.page_2)
        self.gridLayout.addWidget(self.mStackedWidget, 2, 0, 1, 1)
        self.setLayout(self.gridLayout)
        
        self.mStackedWidget.setCurrentIndex(0)
        QObject.connect(self.mButtonBox, SIGNAL(("accepted()")), self.accept)
        QObject.connect(self.mButtonBox, SIGNAL(("rejected()")), self.reject)
        
        self.setTabOrder(self.mFontComboBox, self.mFontSizeSpinBox)
        self.setTabOrder(self.mFontSizeSpinBox, self.mBoldPushButton)
        self.setTabOrder(self.mBoldPushButton, self.mItalicsPushButton)
        self.setTabOrder(self.mItalicsPushButton, self.mFontColorButton)
        self.setTabOrder(self.mFontColorButton, self.mTextEdit)
        self.setTabOrder(self.mTextEdit, self.mButtonBox)
        
        self.setWindowTitle("Annotation text")
        self.mBoldPushButton.setText("B")
        self.mItalicsPushButton.setText("I")
        
        self.mTextDocument = None
        self.mItem = item
        self.mEmbeddedWidget = QgsAnnotationWidget(self, self.mItem )
        self.mEmbeddedWidget.show()
        self.mStackedWidget.addWidget( self.mEmbeddedWidget )
        self.mStackedWidget.setCurrentWidget( self.mEmbeddedWidget )
        if ( self.mItem != None ):
            self.mTextDocument = self.mItem.document()
            self.mTextEdit.setDocument( self.mTextDocument )
        self.mFontColorButton.setColorDialogTitle(  "Select font color"  )
        self.mFontColorButton.setColorDialogOptions( QColorDialog.ShowAlphaChannel )
        self.setCurrentFontPropertiesToGui()
        QObject.connect( self.mButtonBox, SIGNAL("accepted()"), self.applyTextToItem)
#         QObject.connect( self.mFontComboBox, SIGNAL( "currentFontChanged(QFont())"), self.changeCurrentFormat)
        self.mFontComboBox.currentFontChanged.connect(self.changeCurrentFormat)
        QObject.connect( self.mFontSizeSpinBox, SIGNAL( "valueChanged( int )" ), self.changeCurrentFormat ) 
        QObject.connect( self.mBoldPushButton, SIGNAL( "toggled( bool )" ), self.changeCurrentFormat)
        QObject.connect( self.mItalicsPushButton, SIGNAL( "toggled( bool )" ), self.changeCurrentFormat)
        QObject.connect( self.mTextEdit, SIGNAL( "cursorPositionChanged()" ), self.setCurrentFontPropertiesToGui )
        
#         QObject.connect( self.mButtonBox, SIGNAL( "accepted()" ), self.applySettingsToItem)
        deleteButton = QPushButton( "Delete" )
        QObject.connect( deleteButton, SIGNAL( "clicked()" ), self.deleteItem )
        self.mButtonBox.addButton( deleteButton, QDialogButtonBox.RejectRole )
    def applyTextToItem(self):
        if ( self.mItem  != None and self.mTextDocument !=None ):
            if ( self.mEmbeddedWidget != None):
                self.mEmbeddedWidget.apply()
            self.mItem.setDocument( self.mTextDocument )
            self.mItem.update()
    def changeCurrentFormat(self):
        newFont = QFont()
        newFont.setFamily( self.mFontComboBox.currentFont().family() )
        #bold
        if ( self.mBoldPushButton.isChecked() ):
            newFont.setBold( True )
        else:
            newFont.setBold( False )
        #italic
        if ( self.mItalicsPushButton.isChecked() ):
            newFont.setItalic( True )
        else:
            newFont.setItalic( False )
        #size
        newFont.setPointSize( self.mFontSizeSpinBox.value() )
        self.mTextEdit.setCurrentFont( newFont )
        #color
        self.mTextEdit.setTextColor( self.mFontColorButton.color() )
        
    def on_mFontColorButton_colorChanged(self, color ):
        self.changeCurrentFormat()
    def setCurrentFontPropertiesToGui(self):
        self.blockAllSignals( True )
        currentFont = self.mTextEdit.currentFont()
        self.mFontComboBox.setCurrentFont( currentFont )
        self.mFontSizeSpinBox.setValue( currentFont.pointSize() )
        self.mBoldPushButton.setChecked( currentFont.bold() )
        self.mItalicsPushButton.setChecked( currentFont.italic() )
        self.mFontColorButton.setColor( self.mTextEdit.textColor() )
        self.blockAllSignals( False )
        
    def blockAllSignals(self, block ):
        self.mFontComboBox.blockSignals( block )
        self.mFontSizeSpinBox.blockSignals( block )
        self.mBoldPushButton.blockSignals( block )
        self.mItalicsPushButton.blockSignals( block )
        self.mFontColorButton.blockSignals( block )
    
    def deleteItem(self):
        scene = self.mItem.scene()
        if ( scene != None ):
            scene.removeItem( self.mItem )
        self.mItem = None
class OWImportImages(widget.OWWidget):
    name = "Import Images"
    description = "Import images from a directory(s)"
    icon = "icons/ImportImages.svg"
    priority = 110

    outputs = [("Data", Orange.data.Table)]

    #: list of recent paths
    recent_paths = settings.Setting([])  # type: List[RecentPath]
    currentPath = settings.Setting(None)

    want_main_area = False
    resizing_enabled = False

    Modality = Qt.ApplicationModal
    # Modality = Qt.WindowModal

    MaxRecentItems = 20

    def __init__(self):
        super().__init__()
        #: widget's runtime state
        self.__state = State.NoState
        self._imageMeta = []
        self._imageCategories = {}

        self.__invalidated = False
        self.__pendingTask = None

        vbox = gui.vBox(self.controlArea)
        hbox = gui.hBox(vbox)
        self.recent_cb = QComboBox(
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=16,
        )
        self.recent_cb.activated[int].connect(self.__onRecentActivated)
        icons = standard_icons(self)

        browseaction = QAction(
            "Open/Load Images", self,
            iconText="\N{HORIZONTAL ELLIPSIS}",
            icon=icons.dir_open_icon,
            toolTip="Select a directory from which to load the images"
        )
        browseaction.triggered.connect(self.__runOpenDialog)
        reloadaction = QAction(
            "Reload", self,
            icon=icons.reload_icon,
            toolTip="Reload current image set"
        )
        reloadaction.triggered.connect(self.reload)
        self.__actions = namespace(
            browse=browseaction,
            reload=reloadaction,
        )

        browsebutton = QPushButton(
            browseaction.iconText(),
            icon=browseaction.icon(),
            toolTip=browseaction.toolTip(),
            clicked=browseaction.trigger
        )
        reloadbutton = QPushButton(
            reloadaction.iconText(),
            icon=reloadaction.icon(),
            clicked=reloadaction.trigger,
            default=True,
        )

        hbox.layout().addWidget(self.recent_cb)
        hbox.layout().addWidget(browsebutton)
        hbox.layout().addWidget(reloadbutton)

        self.addActions([browseaction, reloadaction])

        reloadaction.changed.connect(
            lambda: reloadbutton.setEnabled(reloadaction.isEnabled())
        )
        box = gui.vBox(vbox, "Info")
        self.infostack = QStackedWidget()

        self.info_area = QLabel(
            text="No image set selected",
            wordWrap=True
        )
        self.progress_widget = QProgressBar(
            minimum=0, maximum=0
        )
        self.cancel_button = QPushButton(
            "Cancel", icon=icons.cancel_icon,
        )
        self.cancel_button.clicked.connect(self.cancel)

        w = QWidget()
        vlayout = QVBoxLayout()
        vlayout.setContentsMargins(0, 0, 0, 0)
        hlayout = QHBoxLayout()
        hlayout.setContentsMargins(0, 0, 0, 0)

        hlayout.addWidget(self.progress_widget)
        hlayout.addWidget(self.cancel_button)
        vlayout.addLayout(hlayout)

        self.pathlabel = TextLabel()
        self.pathlabel.setTextElideMode(Qt.ElideMiddle)
        self.pathlabel.setAttribute(Qt.WA_MacSmallSize)

        vlayout.addWidget(self.pathlabel)
        w.setLayout(vlayout)

        self.infostack.addWidget(self.info_area)
        self.infostack.addWidget(w)

        box.layout().addWidget(self.infostack)

        self.__initRecentItemsModel()
        self.__invalidated = True
        self.__executor = ThreadExecutor(self)

        QApplication.postEvent(self, QEvent(RuntimeEvent.Init))

    def __initRecentItemsModel(self):
        if self.currentPath is not None and \
                not os.path.isdir(self.currentPath):
            self.currentPath = None

        recent_paths = []
        for item in self.recent_paths:
            if os.path.isdir(item.abspath):
                recent_paths.append(item)
        recent_paths = recent_paths[:OWImportImages.MaxRecentItems]
        recent_model = self.recent_cb.model()
        for pathitem in recent_paths:
            item = RecentPath_asqstandarditem(pathitem)
            recent_model.appendRow(item)

        self.recent_paths = recent_paths

        if self.currentPath is not None and \
                os.path.isdir(self.currentPath) and self.recent_paths and \
                os.path.samefile(self.currentPath, self.recent_paths[0].abspath):
            self.recent_cb.setCurrentIndex(0)
        else:
            self.currentPath = None
            self.recent_cb.setCurrentIndex(-1)
        self.__actions.reload.setEnabled(self.currentPath is not None)

    def customEvent(self, event):
        """Reimplemented."""
        if event.type() == RuntimeEvent.Init:
            if self.__invalidated:
                try:
                    self.start()
                finally:
                    self.__invalidated = False

        super().customEvent(event)

    def __runOpenDialog(self):
        startdir = os.path.expanduser("~/")
        if self.recent_paths:
            startdir = self.recent_paths[0].abspath

        if OWImportImages.Modality == Qt.WindowModal:
            dlg = QFileDialog(
                self, "Select Top Level Directory", startdir,
                acceptMode=QFileDialog.AcceptOpen,
                modal=True,
            )
            dlg.setFileMode(QFileDialog.Directory)
            dlg.setOption(QFileDialog.ShowDirsOnly)
            dlg.setDirectory(startdir)
            dlg.setAttribute(Qt.WA_DeleteOnClose)

            @dlg.accepted.connect
            def on_accepted():
                dirpath = dlg.selectedFiles()
                if dirpath:
                    self.setCurrentPath(dirpath[0])
                    self.start()
            dlg.open()
        else:
            dirpath = QFileDialog.getExistingDirectory(
                self, "Select Top Level Directory", startdir
            )
            if dirpath:
                self.setCurrentPath(dirpath)
                self.start()

    def __onRecentActivated(self, index):
        item = self.recent_cb.itemData(index)
        if item is None:
            return
        assert isinstance(item, RecentPath)
        self.setCurrentPath(item.abspath)
        self.start()

    def __updateInfo(self):
        if self.__state == State.NoState:
            text = "No image set selected"
        elif self.__state == State.Processing:
            text = "Processing"
        elif self.__state == State.Done:
            nvalid = sum(imeta.isvalid for imeta in self._imageMeta)
            ncategories = len(self._imageCategories)
            if ncategories < 2:
                text = "{} images".format(nvalid)
            else:
                text = "{} images / {} categories".format(nvalid, ncategories)
        elif self.__state == State.Cancelled:
            text = "Cancelled"
        elif self.__state == State.Error:
            text = "Error state"
        else:
            assert False

        self.info_area.setText(text)

        if self.__state == State.Processing:
            self.infostack.setCurrentIndex(1)
        else:
            self.infostack.setCurrentIndex(0)

    def setCurrentPath(self, path):
        """
        Set the current root image path to path

        If the path does not exists or is not a directory the current path
        is left unchanged

        Parameters
        ----------
        path : str
            New root import path.

        Returns
        -------
        status : bool
            True if the current root import path was successfully
            changed to path.
        """
        if self.currentPath is not None and path is not None and \
                os.path.isdir(self.currentPath) and os.path.isdir(path) and \
                os.path.samefile(self.currentPath, path):
            return True

        if not os.path.exists(path):
            warnings.warn("'{}' does not exist".format(path), UserWarning)
            return False
        elif not os.path.isdir(path):
            warnings.warn("'{}' is not a directory".format(path), UserWarning)
            return False

        newindex = self.addRecentPath(path)
        self.recent_cb.setCurrentIndex(newindex)
        if newindex >= 0:
            self.currentPath = path
        else:
            self.currentPath = None
        self.__actions.reload.setEnabled(self.currentPath is not None)

        if self.__state == State.Processing:
            self.cancel()

        return True

    def addRecentPath(self, path):
        """
        Prepend a path entry to the list of recent paths

        If an entry with the same path already exists in the recent path
        list it is moved to the first place

        Parameters
        ----------
        path : str
        """
        existing = None
        for pathitem in self.recent_paths:
            if os.path.samefile(pathitem.abspath, path):
                existing = pathitem
                break

        model = self.recent_cb.model()

        if existing is not None:
            selected_index = self.recent_paths.index(existing)
            assert model.item(selected_index).data(Qt.UserRole) is existing
            self.recent_paths.remove(existing)
            row = model.takeRow(selected_index)
            self.recent_paths.insert(0, existing)
            model.insertRow(0, row)
        else:
            item = RecentPath(path, None, None)
            self.recent_paths.insert(0, item)
            model.insertRow(0, RecentPath_asqstandarditem(item))
        return 0

    def __setRuntimeState(self, state):
        assert state in State
        self.setBlocking(state == State.Processing)
        message = ""
        if state == State.Processing:
            assert self.__state in [State.Done,
                                    State.NoState,
                                    State.Error,
                                    State.Cancelled]
            message = "Processing"
        elif state == State.Done:
            assert self.__state == State.Processing
        elif state == State.Cancelled:
            assert self.__state == State.Processing
            message = "Cancelled"
        elif state == State.Error:
            message = "Error during processing"
        elif state == State.NoState:
            message = ""
        else:
            assert False

        self.__state = state

        if self.__state == State.Processing:
            self.infostack.setCurrentIndex(1)
        else:
            self.infostack.setCurrentIndex(0)

        self.setStatusMessage(message)
        self.__updateInfo()

    def reload(self):
        """
        Restart the image scan task
        """
        if self.__state == State.Processing:
            self.cancel()

        self._imageMeta = []
        self._imageCategories = {}
        self.start()

    def start(self):
        """
        Start/execute the image indexing operation
        """
        self.error()

        self.__invalidated = False
        if self.currentPath is None:
            return

        if self.__state == State.Processing:
            assert self.__pendingTask is not None
            log.info("Starting a new task while one is in progress. "
                     "Cancel the existing task (dir:'{}')"
                     .format(self.__pendingTask.startdir))
            self.cancel()

        startdir = self.currentPath

        self.__setRuntimeState(State.Processing)

        report_progress = methodinvoke(
            self, "__onReportProgress", (object,))

        task = ImageScan(startdir, report_progress=report_progress)

        # collect the task state in one convenient place
        self.__pendingTask = taskstate = namespace(
            task=task,
            startdir=startdir,
            future=None,
            watcher=None,
            cancelled=False,
            cancel=None,
        )

        def cancel():
            # Cancel the task and disconnect
            if taskstate.future.cancel():
                pass
            else:
                taskstate.task.cancelled = True
                taskstate.cancelled = True
                try:
                    taskstate.future.result(timeout=3)
                except UserInterruptError:
                    pass
                except TimeoutError:
                    log.info("The task did not stop in in a timely manner")
            taskstate.watcher.finished.disconnect(self.__onRunFinished)

        taskstate.cancel = cancel

        def run_image_scan_task_interupt():
            try:
                return task.run()
            except UserInterruptError:
                # Suppress interrupt errors, so they are not logged
                return

        taskstate.future = self.__executor.submit(run_image_scan_task_interupt)
        taskstate.watcher = FutureWatcher(taskstate.future)
        taskstate.watcher.finished.connect(self.__onRunFinished)

    @Slot()
    def __onRunFinished(self):
        assert QThread.currentThread() is self.thread()
        assert self.__state == State.Processing
        assert self.__pendingTask is not None
        assert self.sender() is self.__pendingTask.watcher
        assert self.__pendingTask.future.done()
        task = self.__pendingTask
        self.__pendingTask = None

        try:
            image_meta = task.future.result()
        except Exception as err:
            sys.excepthook(*sys.exc_info())
            state = State.Error
            image_meta = []
            self.error(traceback.format_exc())
        else:
            state = State.Done
            self.error()

        categories = {}

        for imeta in image_meta:
            # derive categories from the path relative to the starting dir
            dirname = os.path.dirname(imeta.path)
            relpath = os.path.relpath(dirname, task.startdir)
            categories[dirname] = relpath

        self._imageMeta = image_meta
        self._imageCategories = categories

        self.__setRuntimeState(state)
        self.commit()

    def cancel(self):
        """
        Cancel current pending task (if any).
        """
        if self.__state == State.Processing:
            assert self.__pendingTask is not None
            self.__pendingTask.cancel()
            self.__pendingTask = None
            self.__setRuntimeState(State.Cancelled)

    @Slot(object)
    def __onReportProgress(self, arg):
        # report on scan progress from a worker thread
        # arg must be a namespace(count: int, lastpath: str)
        assert QThread.currentThread() is self.thread()
        if self.__state == State.Processing:
            self.pathlabel.setText(prettyfypath(arg.lastpath))

    def commit(self):
        """
        Create and commit a Table from the collected image meta data.
        """
        if self._imageMeta:
            categories = self._imageCategories
            if len(categories) > 1:
                cat_var = Orange.data.DiscreteVariable(
                    "category", values=list(sorted(categories.values()))
                )
            else:
                cat_var = None
            # Image name (file basename without the extension)
            imagename_var = Orange.data.StringVariable("image name")
            # Full fs path
            image_var = Orange.data.StringVariable("image")
            image_var.attributes["type"] = "image"
            # file size/width/height
            size_var = Orange.data.ContinuousVariable(
                "size", number_of_decimals=0)
            width_var = Orange.data.ContinuousVariable(
                "width", number_of_decimals=0)
            height_var = Orange.data.ContinuousVariable(
                "height", number_of_decimals=0)
            domain = Orange.data.Domain(
                [], [cat_var] if cat_var is not None else [],
                [imagename_var, image_var, size_var, width_var, height_var]
            )
            cat_data = []
            meta_data = []

            for imgmeta in self._imageMeta:
                if imgmeta.isvalid:
                    if cat_var is not None:
                        category = categories.get(os.path.dirname(imgmeta.path))
                        cat_data.append([cat_var.to_val(category)])
                    else:
                        cat_data.append([])
                    basename = os.path.basename(imgmeta.path)
                    imgname, _ = os.path.splitext(basename)

                    meta_data.append(
                        [imgname, imgmeta.path, imgmeta.size,
                         imgmeta.width, imgmeta.height]
                    )

            cat_data = numpy.array(cat_data, dtype=float)
            meta_data = numpy.array(meta_data, dtype=object)
            table = Orange.data.Table.from_numpy(
                domain, numpy.empty((len(cat_data), 0), dtype=float),
                cat_data, meta_data
            )
        else:
            table = None

        self.send("Data", table)

    def onDeleteWidget(self):
        self.cancel()
        self.__executor.shutdown(wait=True)
Exemple #45
0
class NodeWindow(QMainWindow):
    def __init__(self, data, scheme, parent=None):
        super(NodeWindow, self).__init__(parent)
        self.pathWidget = PathWidget(self.openWidgetByPath, data.path())
        self.setStatusBar(self.pathWidget)
#        layout_set_sm_and_mrg(self.layout)
        self.cachedWidgets = {}
        self.currentStructuredWidget = None
        self.stacked = QStackedWidget(self)
        self.setCentralWidget(self.stacked)

        self.data, self.scheme = data, scheme
        self.data.add_set_notify(self.change_caption)
        self.toolbar = QToolBar()
        self.toolbar.addActions((self.parent().actionSave,self.parent().actionSaveAs,))
        self.addToolBar(self.toolbar)
        self.setUnifiedTitleAndToolBarOnMac(True)
        self.messageBoxChanged = None
        self.reallyQuit = False
        self.change_caption()

        if "ExcelScheme" in self.scheme.get_meta():
            actionExcelExport = QAction("Export to excel", self)
            self.toolbar.addAction(actionExcelExport)
            actionExcelExport.triggered.connect(self.excel_export)
            actionExcelMerge = QAction("Merge from excel", self)
            actionExcelMerge.triggered.connect(self.excel_import)
            self.toolbar.addAction(actionExcelMerge)

        self.tree_widget = DataTreeWidget(self.data, self)
        dock = QDockWidget(self)
        dock.setWidget(self.tree_widget)
        self.addDockWidget(Qt.LeftDockWidgetArea, dock)
        self.tree_widget.pathChanged.connect(self._open_widget_by_path)

        self.openWidgetByPath(Path())


    def change_caption(self):
        changed = ""
        if self.data.changed:
            changed = "* "
        self.setWindowTitle("{} {}".format(changed, self.get_window_caption()))

    def get_window_caption(self):
        return os.path.basename(self.parent().save_filename or "New Data")

    def openWidgetByPath(self, path):
        self._open_widget_by_path(path)
        self.tree_widget.pathChange(path)

    def _open_widget_by_path(self, path):
#        #fixme
#        try:
            if path in self.cachedWidgets:
    #            if self.currentStructuredWidget:
    #                self.currentStructuredWidget.hide()
                self.currentStructuredWidget = self.cachedWidgets[path]
                self.stacked.setCurrentWidget(self.currentStructuredWidget)
                self.pathWidget.setPath(path)
            else:
                if "Type" not in path.get(self.scheme): #fimxe soon
                    self.cachedWidgets[path] = StructuredWidget(unicode(path), path.get(self.data, reduce_sub_elements=True), path.get(self.scheme), self.openWidgetByPath, self)
                    self.stacked.addWidget(self.cachedWidgets[path])
                    self._open_widget_by_path(path)
                else:
                    print ""
                    pass
#        except KeyError:
#            pass

    def closeEvent(self, event):
        if self.reallyQuit or not self.data.changed:
            event.accept()
        else:
            self.dialogChanged()
            event.ignore()

    def dialogChanged(self):
        if not self.messageBoxChanged:
            self.messageBoxChanged = QMessageBox("SDI",
                "The document has been modified.\n"+
                    "Do you want to save your changes?",
                QMessageBox.Warning,
                QMessageBox.Yes | QMessageBox.Default,
                QMessageBox.No,
                QMessageBox.Cancel | QMessageBox.Escape,
                self
            )
            self.messageBoxChanged.setWindowModality (Qt.WindowModal )
            self.messageBoxChanged.finished.connect(self.finishClose)
        self.messageBoxChanged.show()

    def finishClose(self, value):
        if value==QMessageBox.Yes:
            self.reallyQuit = self.parent().save_data()
            if not self.reallyQuit:
                return
        elif value==QMessageBox.No:
            self.reallyQuit = True
        elif value==QMessageBox.Cancel:
            return
        self.close()

    def excel_export(self):
        excel_filename = unicode(QFileDialog.getSaveFileName(self, "Save File",
                            "New excel file.xls", "Excel files (*.xls)"))
        if excel_filename:
            export_to_excel(self.data, self.scheme, excel_filename)

    def excel_import(self):
        excel_filename = unicode(QFileDialog.getOpenFileName(self, "Open File", get_home_dir(),  "Excel files (*.xls)"))
        if excel_filename:
            data_to_merge = import_from_excel(self.scheme, excel_filename)
            self.parent()._merge_data(data_to_merge)
class OWImportImages(widget.OWWidget):
    name = "Import Images"
    description = "Import images from a directory(s)"
    icon = "icons/ImportImages.svg"
    priority = 110

    outputs = [("Data", Orange.data.Table)]

    #: list of recent paths
    recent_paths = settings.Setting([])  # type: List[RecentPath]
    currentPath = settings.Setting(None)

    want_main_area = False
    resizing_enabled = False

    Modality = Qt.ApplicationModal
    # Modality = Qt.WindowModal

    MaxRecentItems = 20

    def __init__(self):
        super().__init__()
        #: widget's runtime state
        self.__state = State.NoState
        self._imageMeta = []
        self._imageCategories = {}

        self.__invalidated = False
        self.__pendingTask = None

        vbox = gui.vBox(self.controlArea)
        hbox = gui.hBox(vbox)
        self.recent_cb = QComboBox(
            sizeAdjustPolicy=QComboBox.AdjustToMinimumContentsLengthWithIcon,
            minimumContentsLength=16,
        )
        self.recent_cb.activated[int].connect(self.__onRecentActivated)
        icons = standard_icons(self)

        browseaction = QAction(
            "Open/Load Images",
            self,
            iconText="\N{HORIZONTAL ELLIPSIS}",
            icon=icons.dir_open_icon,
            toolTip="Select a directory from which to load the images")
        browseaction.triggered.connect(self.__runOpenDialog)
        reloadaction = QAction("Reload",
                               self,
                               icon=icons.reload_icon,
                               toolTip="Reload current image set")
        reloadaction.triggered.connect(self.reload)
        self.__actions = namespace(
            browse=browseaction,
            reload=reloadaction,
        )

        browsebutton = QPushButton(browseaction.iconText(),
                                   icon=browseaction.icon(),
                                   toolTip=browseaction.toolTip(),
                                   clicked=browseaction.trigger)
        reloadbutton = QPushButton(
            reloadaction.iconText(),
            icon=reloadaction.icon(),
            clicked=reloadaction.trigger,
            default=True,
        )

        hbox.layout().addWidget(self.recent_cb)
        hbox.layout().addWidget(browsebutton)
        hbox.layout().addWidget(reloadbutton)

        self.addActions([browseaction, reloadaction])

        reloadaction.changed.connect(
            lambda: reloadbutton.setEnabled(reloadaction.isEnabled()))
        box = gui.vBox(vbox, "Info")
        self.infostack = QStackedWidget()

        self.info_area = QLabel(text="No image set selected", wordWrap=True)
        self.progress_widget = QProgressBar(minimum=0, maximum=0)
        self.cancel_button = QPushButton(
            "Cancel",
            icon=icons.cancel_icon,
        )
        self.cancel_button.clicked.connect(self.cancel)

        w = QWidget()
        vlayout = QVBoxLayout()
        vlayout.setContentsMargins(0, 0, 0, 0)
        hlayout = QHBoxLayout()
        hlayout.setContentsMargins(0, 0, 0, 0)

        hlayout.addWidget(self.progress_widget)
        hlayout.addWidget(self.cancel_button)
        vlayout.addLayout(hlayout)

        self.pathlabel = TextLabel()
        self.pathlabel.setTextElideMode(Qt.ElideMiddle)
        self.pathlabel.setAttribute(Qt.WA_MacSmallSize)

        vlayout.addWidget(self.pathlabel)
        w.setLayout(vlayout)

        self.infostack.addWidget(self.info_area)
        self.infostack.addWidget(w)

        box.layout().addWidget(self.infostack)

        self.__initRecentItemsModel()
        self.__invalidated = True
        self.__executor = ThreadExecutor(self)

        QApplication.postEvent(self, QEvent(RuntimeEvent.Init))

    def __initRecentItemsModel(self):
        if self.currentPath is not None and \
                not os.path.isdir(self.currentPath):
            self.currentPath = None

        recent_paths = []
        for item in self.recent_paths:
            if os.path.isdir(item.abspath):
                recent_paths.append(item)
        recent_paths = recent_paths[:OWImportImages.MaxRecentItems]
        recent_model = self.recent_cb.model()
        for pathitem in recent_paths:
            item = RecentPath_asqstandarditem(pathitem)
            recent_model.appendRow(item)

        self.recent_paths = recent_paths

        if self.currentPath is not None and \
                os.path.isdir(self.currentPath) and self.recent_paths and \
                os.path.samefile(self.currentPath, self.recent_paths[0].abspath):
            self.recent_cb.setCurrentIndex(0)
        else:
            self.currentPath = None
            self.recent_cb.setCurrentIndex(-1)
        self.__actions.reload.setEnabled(self.currentPath is not None)

    def customEvent(self, event):
        """Reimplemented."""
        if event.type() == RuntimeEvent.Init:
            if self.__invalidated:
                try:
                    self.start()
                finally:
                    self.__invalidated = False

        super().customEvent(event)

    def __runOpenDialog(self):
        startdir = os.path.expanduser("~/")
        if self.recent_paths:
            startdir = self.recent_paths[0].abspath

        if OWImportImages.Modality == Qt.WindowModal:
            dlg = QFileDialog(
                self,
                "Select Top Level Directory",
                startdir,
                acceptMode=QFileDialog.AcceptOpen,
                modal=True,
            )
            dlg.setFileMode(QFileDialog.Directory)
            dlg.setOption(QFileDialog.ShowDirsOnly)
            dlg.setDirectory(startdir)
            dlg.setAttribute(Qt.WA_DeleteOnClose)

            @dlg.accepted.connect
            def on_accepted():
                dirpath = dlg.selectedFiles()
                if dirpath:
                    self.setCurrentPath(dirpath[0])
                    self.start()

            dlg.open()
        else:
            dirpath = QFileDialog.getExistingDirectory(
                self, "Select Top Level Directory", startdir)
            if dirpath:
                self.setCurrentPath(dirpath)
                self.start()

    def __onRecentActivated(self, index):
        item = self.recent_cb.itemData(index)
        if item is None:
            return
        assert isinstance(item, RecentPath)
        self.setCurrentPath(item.abspath)
        self.start()

    def __updateInfo(self):
        if self.__state == State.NoState:
            text = "No image set selected"
        elif self.__state == State.Processing:
            text = "Processing"
        elif self.__state == State.Done:
            nvalid = sum(imeta.isvalid for imeta in self._imageMeta)
            ncategories = len(self._imageCategories)
            if ncategories < 2:
                text = "{} images".format(nvalid)
            else:
                text = "{} images / {} categories".format(nvalid, ncategories)
        elif self.__state == State.Cancelled:
            text = "Cancelled"
        elif self.__state == State.Error:
            text = "Error state"
        else:
            assert False

        self.info_area.setText(text)

        if self.__state == State.Processing:
            self.infostack.setCurrentIndex(1)
        else:
            self.infostack.setCurrentIndex(0)

    def setCurrentPath(self, path):
        """
        Set the current root image path to path

        If the path does not exists or is not a directory the current path
        is left unchanged

        Parameters
        ----------
        path : str
            New root import path.

        Returns
        -------
        status : bool
            True if the current root import path was successfully
            changed to path.
        """
        if self.currentPath is not None and path is not None and \
                os.path.isdir(self.currentPath) and os.path.isdir(path) and \
                os.path.samefile(self.currentPath, path):
            return True

        if not os.path.exists(path):
            warnings.warn("'{}' does not exist".format(path), UserWarning)
            return False
        elif not os.path.isdir(path):
            warnings.warn("'{}' is not a directory".format(path), UserWarning)
            return False

        newindex = self.addRecentPath(path)
        self.recent_cb.setCurrentIndex(newindex)
        if newindex >= 0:
            self.currentPath = path
        else:
            self.currentPath = None
        self.__actions.reload.setEnabled(self.currentPath is not None)

        if self.__state == State.Processing:
            self.cancel()

        return True

    def addRecentPath(self, path):
        """
        Prepend a path entry to the list of recent paths

        If an entry with the same path already exists in the recent path
        list it is moved to the first place

        Parameters
        ----------
        path : str
        """
        existing = None
        for pathitem in self.recent_paths:
            if os.path.samefile(pathitem.abspath, path):
                existing = pathitem
                break

        model = self.recent_cb.model()

        if existing is not None:
            selected_index = self.recent_paths.index(existing)
            assert model.item(selected_index).data(Qt.UserRole) is existing
            self.recent_paths.remove(existing)
            row = model.takeRow(selected_index)
            self.recent_paths.insert(0, existing)
            model.insertRow(0, row)
        else:
            item = RecentPath(path, None, None)
            self.recent_paths.insert(0, item)
            model.insertRow(0, RecentPath_asqstandarditem(item))
        return 0

    def __setRuntimeState(self, state):
        assert state in State
        self.setBlocking(state == State.Processing)
        message = ""
        if state == State.Processing:
            assert self.__state in [
                State.Done, State.NoState, State.Error, State.Cancelled
            ]
            message = "Processing"
        elif state == State.Done:
            assert self.__state == State.Processing
        elif state == State.Cancelled:
            assert self.__state == State.Processing
            message = "Cancelled"
        elif state == State.Error:
            message = "Error during processing"
        elif state == State.NoState:
            message = ""
        else:
            assert False

        self.__state = state

        if self.__state == State.Processing:
            self.infostack.setCurrentIndex(1)
        else:
            self.infostack.setCurrentIndex(0)

        self.setStatusMessage(message)
        self.__updateInfo()

    def reload(self):
        """
        Restart the image scan task
        """
        if self.__state == State.Processing:
            self.cancel()

        self._imageMeta = []
        self._imageCategories = {}
        self.start()

    def start(self):
        """
        Start/execute the image indexing operation
        """
        self.error()

        self.__invalidated = False
        if self.currentPath is None:
            return

        if self.__state == State.Processing:
            assert self.__pendingTask is not None
            log.info("Starting a new task while one is in progress. "
                     "Cancel the existing task (dir:'{}')".format(
                         self.__pendingTask.startdir))
            self.cancel()

        startdir = self.currentPath

        self.__setRuntimeState(State.Processing)

        report_progress = methodinvoke(self, "__onReportProgress", (object, ))

        task = ImageScan(startdir, report_progress=report_progress)

        # collect the task state in one convenient place
        self.__pendingTask = taskstate = namespace(
            task=task,
            startdir=startdir,
            future=None,
            watcher=None,
            cancelled=False,
            cancel=None,
        )

        def cancel():
            # Cancel the task and disconnect
            if taskstate.future.cancel():
                pass
            else:
                taskstate.task.cancelled = True
                taskstate.cancelled = True
                try:
                    taskstate.future.result(timeout=3)
                except UserInterruptError:
                    pass
                except TimeoutError:
                    log.info("The task did not stop in in a timely manner")
            taskstate.watcher.finished.disconnect(self.__onRunFinished)

        taskstate.cancel = cancel

        def run_image_scan_task_interupt():
            try:
                return task.run()
            except UserInterruptError:
                # Suppress interrupt errors, so they are not logged
                return

        taskstate.future = self.__executor.submit(run_image_scan_task_interupt)
        taskstate.watcher = FutureWatcher(taskstate.future)
        taskstate.watcher.finished.connect(self.__onRunFinished)

    @Slot()
    def __onRunFinished(self):
        assert QThread.currentThread() is self.thread()
        assert self.__state == State.Processing
        assert self.__pendingTask is not None
        assert self.sender() is self.__pendingTask.watcher
        assert self.__pendingTask.future.done()
        task = self.__pendingTask
        self.__pendingTask = None

        try:
            image_meta = task.future.result()
        except Exception as err:
            sys.excepthook(*sys.exc_info())
            state = State.Error
            image_meta = []
            self.error(traceback.format_exc())
        else:
            state = State.Done
            self.error()

        categories = {}

        for imeta in image_meta:
            # derive categories from the path relative to the starting dir
            dirname = os.path.dirname(imeta.path)
            relpath = os.path.relpath(dirname, task.startdir)
            categories[dirname] = relpath

        self._imageMeta = image_meta
        self._imageCategories = categories

        self.__setRuntimeState(state)
        self.commit()

    def cancel(self):
        """
        Cancel current pending task (if any).
        """
        if self.__state == State.Processing:
            assert self.__pendingTask is not None
            self.__pendingTask.cancel()
            self.__pendingTask = None
            self.__setRuntimeState(State.Cancelled)

    @Slot(object)
    def __onReportProgress(self, arg):
        # report on scan progress from a worker thread
        # arg must be a namespace(count: int, lastpath: str)
        assert QThread.currentThread() is self.thread()
        if self.__state == State.Processing:
            self.pathlabel.setText(prettyfypath(arg.lastpath))

    def commit(self):
        """
        Create and commit a Table from the collected image meta data.
        """
        if self._imageMeta:
            categories = self._imageCategories
            if len(categories) > 1:
                cat_var = Orange.data.DiscreteVariable(
                    "category", values=list(sorted(categories.values())))
            else:
                cat_var = None
            # Image name (file basename without the extension)
            imagename_var = Orange.data.StringVariable("image name")
            # Full fs path
            image_var = Orange.data.StringVariable("image")
            image_var.attributes["type"] = "image"
            # file size/width/height
            size_var = Orange.data.ContinuousVariable("size",
                                                      number_of_decimals=0)
            width_var = Orange.data.ContinuousVariable("width",
                                                       number_of_decimals=0)
            height_var = Orange.data.ContinuousVariable("height",
                                                        number_of_decimals=0)
            domain = Orange.data.Domain(
                [], [cat_var] if cat_var is not None else [],
                [imagename_var, image_var, size_var, width_var, height_var])
            cat_data = []
            meta_data = []

            for imgmeta in self._imageMeta:
                if imgmeta.isvalid:
                    if cat_var is not None:
                        category = categories.get(os.path.dirname(
                            imgmeta.path))
                        cat_data.append([cat_var.to_val(category)])
                    else:
                        cat_data.append([])
                    basename = os.path.basename(imgmeta.path)
                    imgname, _ = os.path.splitext(basename)

                    meta_data.append([
                        imgname, imgmeta.path, imgmeta.size, imgmeta.width,
                        imgmeta.height
                    ])

            cat_data = numpy.array(cat_data, dtype=float)
            meta_data = numpy.array(meta_data, dtype=object)
            table = Orange.data.Table.from_numpy(
                domain, numpy.empty((len(cat_data), 0), dtype=float), cat_data,
                meta_data)
        else:
            table = None

        self.send("Data", table)

    def onDeleteWidget(self):
        self.cancel()
        self.__executor.shutdown(wait=True)
Exemple #47
0
class DataSelectionGui(QWidget):
    """
    Manages all GUI elements in the data selection applet.
    This class itself is the central widget and also owns/manages the applet drawer widgets.
    """

    ###########################################
    ### AppletGuiInterface Concrete Methods ###
    ###########################################

    def centralWidget(self):
        return self

    def appletDrawer(self):
        return self._drawer

    def menus(self):
        return []

    def viewerControlWidget(self):
        return self._viewerControlWidgetStack

    def setImageIndex(self, imageIndex):
        if imageIndex is not None:
            self.laneSummaryTableView.selectRow(imageIndex)
            for detailWidget in self._detailViewerWidgets:
                detailWidget.selectRow(imageIndex)

    def stopAndCleanUp(self):
        self._cleaning_up = True
        for editor in self.volumeEditors.values():
            self.viewerStack.removeWidget(editor)
            self._viewerControlWidgetStack.removeWidget(
                editor.viewerControlWidget())
            editor.stopAndCleanUp()
        self.volumeEditors.clear()

    def imageLaneAdded(self, laneIndex):
        if len(self.laneSummaryTableView.selectedIndexes()) == 0:
            self.laneSummaryTableView.selectRow(laneIndex)

        # We don't have any real work to do because this gui initiated the lane addition in the first place
        if self.guiMode != GuiMode.Batch:
            if (len(self.topLevelOperator.DatasetGroup) != laneIndex + 1):
                import warnings
                warnings.warn(
                    "DataSelectionGui.imageLaneAdded(): length of dataset multislot out of sync with laneindex [%s != %s + 1]"
                    % (len(self.topLevelOperator.DatasetGroup), laneIndex))

    def imageLaneRemoved(self, laneIndex, finalLength):
        # There's nothing to do here because the GUI already
        #  handles operator resizes via slot callbacks.
        pass

    def allowLaneSelectionChange(self):
        return False

    ###########################################
    ###########################################

    class UserCancelledError(Exception):
        # This exception type is raised when the user cancels the
        #  addition of dataset files in the middle of the process somewhere.
        # It isn't an error -- it's used for control flow.
        pass

    def __init__(self,
                 parentApplet,
                 dataSelectionOperator,
                 serializer,
                 instructionText,
                 guiMode=GuiMode.Normal,
                 max_lanes=None,
                 show_axis_details=False):
        """
        Constructor.
        
        :param dataSelectionOperator: The top-level operator.  Must be of type :py:class:`OpMultiLaneDataSelectionGroup`.
        :param serializer: The applet's serializer.  Must be of type :py:class:`DataSelectionSerializer`
        :param instructionText: A string to display in the applet drawer.
        :param guiMode: Either ``GuiMode.Normal`` or ``GuiMode.Batch``.  Currently, there is no difference between normal and batch mode.
        :param max_lanes: The maximum number of lanes that the user is permitted to add to this workflow.  If ``None``, there is no maximum.
        """
        super(DataSelectionGui, self).__init__()
        self._cleaning_up = False
        self.parentApplet = parentApplet
        self._max_lanes = max_lanes
        self._default_h5_volumes = {}
        self.show_axis_details = show_axis_details

        self._viewerControls = QWidget()
        self.topLevelOperator = dataSelectionOperator
        self.guiMode = guiMode
        self.serializer = serializer
        self.threadRouter = ThreadRouter(self)

        self._initCentralUic()
        self._initAppletDrawerUic(instructionText)

        self._viewerControlWidgetStack = QStackedWidget(self)

        def handleImageRemove(multislot, index, finalLength):
            # Remove the viewer for this dataset
            datasetSlot = self.topLevelOperator.DatasetGroup[index]
            if datasetSlot in self.volumeEditors.keys():
                editor = self.volumeEditors[datasetSlot]
                self.viewerStack.removeWidget(editor)
                self._viewerControlWidgetStack.removeWidget(
                    editor.viewerControlWidget())
                editor.stopAndCleanUp()

        self.topLevelOperator.DatasetGroup.notifyRemove(
            bind(handleImageRemove))

        opWorkflow = self.topLevelOperator.parent
        assert hasattr(opWorkflow.shell, 'onSaveProjectActionTriggered'), \
            "This class uses the IlastikShell.onSaveProjectActionTriggered function.  Did you rename it?"

    def _initCentralUic(self):
        """
        Load the GUI from the ui file into this class and connect it with event handlers.
        """
        # Load the ui file into this class (find it in our own directory)
        localDir = os.path.split(__file__)[0] + '/'
        uic.loadUi(localDir + "/dataSelection.ui", self)

        self._initTableViews()
        self._initViewerStack()
        self.splitter.setSizes([150, 850])

    def _initAppletDrawerUic(self, instructionText):
        """
        Load the ui file for the applet drawer, which we own.
        """
        localDir = os.path.split(__file__)[0] + '/'
        self._drawer = uic.loadUi(localDir + "/dataSelectionDrawer.ui")
        self._drawer.instructionLabel.setText(instructionText)

    def _initTableViews(self):
        self.fileInfoTabWidget.setTabText(0, "Summary")
        self.laneSummaryTableView.setModel(
            DataLaneSummaryTableModel(self, self.topLevelOperator))
        self.laneSummaryTableView.dataLaneSelected.connect(self.showDataset)
        self.laneSummaryTableView.addFilesRequested.connect(self.addFiles)
        self.laneSummaryTableView.addStackRequested.connect(self.addStack)
        self.laneSummaryTableView.removeLanesRequested.connect(
            self.handleRemoveLaneButtonClicked)

        # These two helper functions enable/disable an 'add files' button for a given role
        #  based on the the max lane index for that role and the overall permitted max_lanes
        def _update_button_status(viewer, role_index):
            if self._max_lanes:
                viewer.setEnabled(
                    self._findFirstEmptyLane(role_index) < self._max_lanes)

        def _handle_lane_added(button, role_index, lane_slot, lane_index):
            def _handle_role_slot_added(role_slot, added_slot_index, *args):
                if added_slot_index == role_index:
                    role_slot.notifyReady(
                        bind(_update_button_status, button, role_index))
                    role_slot.notifyUnready(
                        bind(_update_button_status, button, role_index))

            lane_slot[lane_index].notifyInserted(_handle_role_slot_added)

        self._retained = []  # Retain menus so they don't get deleted
        self._detailViewerWidgets = []
        for roleIndex, role in enumerate(
                self.topLevelOperator.DatasetRoles.value):
            detailViewer = DatasetDetailedInfoTableView(self)
            detailViewer.setModel(
                DatasetDetailedInfoTableModel(self, self.topLevelOperator,
                                              roleIndex))
            self._detailViewerWidgets.append(detailViewer)

            # Button
            detailViewer.addFilesRequested.connect(
                partial(self.addFiles, roleIndex))
            detailViewer.addStackRequested.connect(
                partial(self.addStack, roleIndex))
            detailViewer.addRemoteVolumeRequested.connect(
                partial(self.addDvidVolume, roleIndex))

            # Monitor changes to each lane so we can enable/disable the 'add lanes' button for each tab
            self.topLevelOperator.DatasetGroup.notifyInserted(
                bind(_handle_lane_added, detailViewer, roleIndex))
            self.topLevelOperator.DatasetGroup.notifyRemoved(
                bind(_update_button_status, detailViewer, roleIndex))

            # While we're at it, do the same for the buttons in the summary table, too
            self.topLevelOperator.DatasetGroup.notifyInserted(
                bind(_handle_lane_added,
                     self.laneSummaryTableView.addFilesButtons[roleIndex],
                     roleIndex))
            self.topLevelOperator.DatasetGroup.notifyRemoved(
                bind(_update_button_status,
                     self.laneSummaryTableView.addFilesButtons[roleIndex],
                     roleIndex))

            # Context menu
            detailViewer.replaceWithFileRequested.connect(
                partial(self.handleReplaceFile, roleIndex))
            detailViewer.replaceWithStackRequested.connect(
                partial(self.addStack, roleIndex))
            detailViewer.editRequested.connect(
                partial(self.editDatasetInfo, roleIndex))
            detailViewer.resetRequested.connect(
                partial(self.handleClearDatasets, roleIndex))

            # Drag-and-drop
            detailViewer.addFilesRequestedDrop.connect(
                partial(self.addFileNames, roleIndex=roleIndex))

            # Selection handling
            def showFirstSelectedDataset(_roleIndex, lanes):
                if lanes:
                    self.showDataset(lanes[0], _roleIndex)

            detailViewer.dataLaneSelected.connect(
                partial(showFirstSelectedDataset, roleIndex))

            self.fileInfoTabWidget.insertTab(roleIndex, detailViewer, role)

        self.fileInfoTabWidget.currentChanged.connect(self.handleSwitchTabs)
        self.fileInfoTabWidget.setCurrentIndex(0)

    def handleSwitchTabs(self, tabIndex):
        if tabIndex < len(self._detailViewerWidgets):
            roleIndex = tabIndex  # If summary tab is moved to the front, change this line.
            detailViewer = self._detailViewerWidgets[roleIndex]
            selectedLanes = detailViewer.selectedLanes
            if selectedLanes:
                self.showDataset(selectedLanes[0], roleIndex)

    def _initViewerStack(self):
        self.volumeEditors = {}
        self.viewerStack.addWidget(QWidget())

    def handleRemoveLaneButtonClicked(self):
        """
        The user clicked the "Remove" button.
        Remove the currently selected row(s) from both the GUI and the top-level operator.
        """
        # Figure out which lanes to remove
        selectedIndexes = self.laneSummaryTableView.selectedIndexes()
        rows = set()
        for modelIndex in selectedIndexes:
            rows.add(modelIndex.row())

        # Don't remove the last row, which is just buttons.
        rows.discard(self.laneSummaryTableView.model().rowCount() - 1)

        # Remove in reverse order so row numbers remain consistent
        for row in reversed(sorted(rows)):
            # Remove lanes from the operator.
            # The table model will notice the changes and update the rows accordingly.
            finalSize = len(self.topLevelOperator.DatasetGroup) - 1
            self.topLevelOperator.DatasetGroup.removeSlot(row, finalSize)

    @threadRouted
    def showDataset(self, laneIndex, roleIndex=None):
        if self._cleaning_up:
            return
        if laneIndex == -1:
            self.viewerStack.setCurrentIndex(0)
            return

        assert threading.current_thread().name == "MainThread"

        if laneIndex >= len(self.topLevelOperator.DatasetGroup):
            return
        datasetSlot = self.topLevelOperator.DatasetGroup[laneIndex]

        # Create if necessary
        if datasetSlot not in self.volumeEditors.keys():

            class DatasetViewer(LayerViewerGui):
                def moveToTop(self, roleIndex):
                    opLaneView = self.topLevelOperatorView
                    if not opLaneView.DatasetRoles.ready():
                        return
                    datasetRoles = opLaneView.DatasetRoles.value
                    if roleIndex >= len(datasetRoles):
                        return
                    roleName = datasetRoles[roleIndex]
                    try:
                        layerIndex = [l.name
                                      for l in self.layerstack].index(roleName)
                    except ValueError:
                        return
                    else:
                        self.layerstack.selectRow(layerIndex)
                        self.layerstack.moveSelectedToTop()

                def setupLayers(self):
                    opLaneView = self.topLevelOperatorView
                    if not opLaneView.DatasetRoles.ready():
                        return []
                    layers = []
                    datasetRoles = opLaneView.DatasetRoles.value
                    for roleIndex, slot in enumerate(opLaneView.ImageGroup):
                        if slot.ready():
                            roleName = datasetRoles[roleIndex]
                            layer = self.createStandardLayerFromSlot(slot)
                            layer.name = roleName
                            layers.append(layer)
                    return layers

            opLaneView = self.topLevelOperator.getLane(laneIndex)
            layerViewer = DatasetViewer(self.parentApplet,
                                        opLaneView,
                                        crosshair=False)

            # Maximize the x-y view by default.
            layerViewer.volumeEditorWidget.quadview.ensureMaximized(2)

            self.volumeEditors[datasetSlot] = layerViewer
            self.viewerStack.addWidget(layerViewer)
            self._viewerControlWidgetStack.addWidget(
                layerViewer.viewerControlWidget())

        # Show the right one
        viewer = self.volumeEditors[datasetSlot]
        displayedRole = self.fileInfoTabWidget.currentIndex()
        viewer.moveToTop(displayedRole)
        self.viewerStack.setCurrentWidget(viewer)
        self._viewerControlWidgetStack.setCurrentWidget(
            viewer.viewerControlWidget())

    def handleReplaceFile(self, roleIndex, startingLane):
        self.addFiles(roleIndex, startingLane)

    def addFiles(self, roleIndex, startingLane=None):
        """
        The user clicked the "Add File" button.
        Ask him to choose a file (or several) and add them to both
          the GUI table and the top-level operator inputs.
        """
        # Find the directory of the most recently opened image file
        mostRecentImageFile = PreferencesManager().get('DataSelection',
                                                       'recent image')
        if mostRecentImageFile is not None:
            defaultDirectory = os.path.split(mostRecentImageFile)[0]
        else:
            defaultDirectory = os.path.expanduser('~')

        # Launch the "Open File" dialog
        fileNames = self.getImageFileNamesToOpen(self, defaultDirectory)

        # If the user didn't cancel
        if len(fileNames) > 0:
            PreferencesManager().set('DataSelection', 'recent image',
                                     fileNames[0])
            try:
                self.addFileNames(fileNames, roleIndex, startingLane)
            except Exception as ex:
                log_exception(logger)
                QMessageBox.critical(self, "Error loading file", str(ex))

    @classmethod
    def getImageFileNamesToOpen(cls, parent_window, defaultDirectory):
        """
        Launch an "Open File" dialog to ask the user for one or more image files.
        """
        extensions = OpDataSelection.SupportedExtensions
        filter_strs = ["*." + x for x in extensions]
        filters = ["{filt} ({filt})".format(filt=x) for x in filter_strs]
        filt_all_str = "Image files (" + ' '.join(filter_strs) + ')'

        fileNames = []

        if ilastik_config.getboolean("ilastik", "debug"):
            # use Qt dialog in debug mode (more portable?)
            file_dialog = QFileDialog(parent_window, "Select Images")
            file_dialog.setOption(QFileDialog.DontUseNativeDialog, True)
            # do not display file types associated with a filter
            # the line for "Image files" is too long otherwise
            file_dialog.setFilters([filt_all_str] + filters)
            file_dialog.setNameFilterDetailsVisible(False)
            # select multiple files
            file_dialog.setFileMode(QFileDialog.ExistingFiles)
            file_dialog.setDirectory(defaultDirectory)

            if file_dialog.exec_():
                fileNames = file_dialog.selectedFiles()
        else:
            # otherwise, use native dialog of the present platform
            fileNames = QFileDialog.getOpenFileNames(parent_window,
                                                     "Select Images",
                                                     defaultDirectory,
                                                     filt_all_str)
        # Convert from QtString to python str
        fileNames = map(encode_from_qstring, fileNames)
        return fileNames

    def _findFirstEmptyLane(self, roleIndex):
        opTop = self.topLevelOperator

        # Determine the number of files this role already has
        # Search for the last valid value.
        firstNewLane = 0
        for laneIndex, slot in reversed(
                zip(range(len(opTop.DatasetGroup)), opTop.DatasetGroup)):
            if slot[roleIndex].ready():
                firstNewLane = laneIndex + 1
                break
        return firstNewLane

    def addFileNames(self, fileNames, roleIndex, startingLane=None, rois=None):
        """
        Add the given filenames to both the GUI table and the top-level operator inputs.
        If startingLane is None, the filenames will be *appended* to the role's list of files.
        
        If rois is provided, it must be a list of (start,stop) tuples (one for each fileName)
        """
        # What lanes will we touch?
        startingLane, endingLane = self._determineLaneRange(
            fileNames, roleIndex, startingLane)
        if startingLane is None:
            # Something went wrong.
            return

        # If we're only adding new lanes, NOT modifying existing lanes...
        adding_only = startingLane == len(self.topLevelOperator)

        # Create a list of DatasetInfos
        try:
            infos = self._createDatasetInfos(roleIndex, fileNames, rois)
        except DataSelectionGui.UserCancelledError:
            return

        # If no exception was thrown so far, set up the operator now
        loaded_all = self._configureOpWithInfos(roleIndex, startingLane,
                                                endingLane, infos)

        if loaded_all:
            # Now check the resulting slots.
            # If they should be copied to the project file, say so.
            self._reconfigureDatasetLocations(roleIndex, startingLane,
                                              endingLane)

            self._checkDataFormatWarnings(roleIndex, startingLane, endingLane)

            # If we succeeded in adding all images, show the first one.
            self.showDataset(startingLane, roleIndex)

        # Notify the workflow that we just added some new lanes.
        if adding_only:
            workflow = self.parentApplet.topLevelOperator.parent
            workflow.handleNewLanesAdded()

        # Notify the workflow that something that could affect applet readyness has occurred.
        self.parentApplet.appletStateUpdateRequested.emit()

        self.updateInternalPathVisiblity()

    def _determineLaneRange(self, fileNames, roleIndex, startingLane=None):
        """
        Determine which lanes should be configured if the user wants to add the 
            given fileNames to the specified role, starting at startingLane.
        If startingLane is None, assume the user wants to APPEND the 
            files to the role's slots.
        """
        if startingLane is None or startingLane == -1:
            startingLane = len(self.topLevelOperator.DatasetGroup)
            endingLane = startingLane + len(fileNames) - 1
        else:
            assert startingLane < len(self.topLevelOperator.DatasetGroup)
            max_files = len(self.topLevelOperator.DatasetGroup) - \
                    startingLane
            if len(fileNames) > max_files:
                msg = "You selected {num_selected} files for {num_slots} "\
                      "slots. To add new files use the 'Add new...' option "\
                      "in the context menu or the button in the last row."\
                              .format(num_selected=len(fileNames),
                                      num_slots=max_files)
                QMessageBox.critical(self, "Too many files", msg)
                return (None, None)
            endingLane = min(startingLane + len(fileNames) - 1,
                             len(self.topLevelOperator.DatasetGroup))

        if self._max_lanes and endingLane >= self._max_lanes:
            msg = "You may not add more than {} file(s) to this workflow.  Please try again.".format(
                self._max_lanes)
            QMessageBox.critical(self, "Too many files", msg)
            return (None, None)

        return (startingLane, endingLane)

    def _createDatasetInfos(self, roleIndex, filePaths, rois):
        """
        Create a list of DatasetInfos for the given filePaths and rois
        rois may be None, in which case it is ignored.
        """
        if rois is None:
            rois = [None] * len(filePaths)
        assert len(rois) == len(filePaths)

        infos = []
        for filePath, roi in zip(filePaths, rois):
            info = self._createDatasetInfo(roleIndex, filePath, roi)
            infos.append(info)
        return infos

    def _createDatasetInfo(self, roleIndex, filePath, roi):
        """
        Create a DatasetInfo object for the given filePath and roi.
        roi may be None, in which case it is ignored.
        """
        cwd = self.topLevelOperator.WorkingDirectory.value
        datasetInfo = DatasetInfo(filePath, cwd=cwd)
        datasetInfo.subvolume_roi = roi  # (might be None)

        absPath, relPath = getPathVariants(filePath, cwd)

        # If the file is in a totally different tree from the cwd,
        # then leave the path as absolute.  Otherwise, override with the relative path.
        if relPath is not None and len(os.path.commonprefix([cwd, absPath
                                                             ])) > 1:
            datasetInfo.filePath = relPath

        h5Exts = ['.ilp', '.h5', '.hdf5']
        if os.path.splitext(datasetInfo.filePath)[1] in h5Exts:
            datasetNames = self.getPossibleInternalPaths(absPath)
            if len(datasetNames) == 0:
                raise RuntimeError("HDF5 file %s has no image datasets" %
                                   datasetInfo.filePath)
            elif len(datasetNames) == 1:
                datasetInfo.filePath += str(datasetNames[0])
            else:
                # If exactly one of the file's datasets matches a user's previous choice, use it.
                if roleIndex not in self._default_h5_volumes:
                    self._default_h5_volumes[roleIndex] = set()
                previous_selections = self._default_h5_volumes[roleIndex]
                possible_auto_selections = previous_selections.intersection(
                    datasetNames)
                if len(possible_auto_selections) == 1:
                    datasetInfo.filePath += str(
                        list(possible_auto_selections)[0])
                else:
                    # Ask the user which dataset to choose
                    dlg = H5VolumeSelectionDlg(datasetNames, self)
                    if dlg.exec_() == QDialog.Accepted:
                        selected_index = dlg.combo.currentIndex()
                        selected_dataset = str(datasetNames[selected_index])
                        datasetInfo.filePath += selected_dataset
                        self._default_h5_volumes[roleIndex].add(
                            selected_dataset)
                    else:
                        raise DataSelectionGui.UserCancelledError()

        # Allow labels by default if this gui isn't being used for batch data.
        datasetInfo.allowLabels = (self.guiMode == GuiMode.Normal)
        return datasetInfo

    def _configureOpWithInfos(self, roleIndex, startingLane, endingLane,
                              infos):
        """
        Attempt to configure the specified role and lanes of the 
        top-level operator with the given DatasetInfos.
        
        Returns True if all lanes were configured successfully, or False if something went wrong.
        """
        opTop = self.topLevelOperator
        originalSize = len(opTop.DatasetGroup)

        # Resize the slot if necessary
        if len(opTop.DatasetGroup) < endingLane + 1:
            opTop.DatasetGroup.resize(endingLane + 1)

        # Configure each subslot
        for laneIndex, info in zip(range(startingLane, endingLane + 1), infos):
            try:
                self.topLevelOperator.DatasetGroup[laneIndex][
                    roleIndex].setValue(info)
            except DatasetConstraintError as ex:
                return_val = [False]
                # Give the user a chance to fix the problem
                self.handleDatasetConstraintError(info, info.filePath, ex,
                                                  roleIndex, laneIndex,
                                                  return_val)
                if not return_val[0]:
                    # Not successfully repaired.  Roll back the changes and give up.
                    opTop.DatasetGroup.resize(originalSize)
                    return False
            except OpDataSelection.InvalidDimensionalityError as ex:
                opTop.DatasetGroup.resize(originalSize)
                QMessageBox.critical(self,
                                     "Dataset has different dimensionality",
                                     ex.message)
                return False
            except Exception as ex:
                msg = "Wasn't able to load your dataset into the workflow.  See error log for details."
                log_exception(logger, msg)
                QMessageBox.critical(self, "Dataset Load Error", msg)
                opTop.DatasetGroup.resize(originalSize)
                return False

        return True

    def _reconfigureDatasetLocations(self, roleIndex, startingLane,
                                     endingLane):
        """
        Check the metadata for the given slots.  
        If the data is stored a format that is poorly optimized for 3D access, 
        then configure it to be copied to the project file.
        Finally, save the project if we changed something. 
        """
        save_needed = False
        opTop = self.topLevelOperator
        for lane_index in range(startingLane, endingLane + 1):
            output_slot = opTop.ImageGroup[lane_index][roleIndex]
            if output_slot.meta.prefer_2d and 'z' in output_slot.meta.axistags:
                shape = numpy.array(output_slot.meta.shape)
                total_volume = numpy.prod(shape)

                # Only copy to the project file if the total volume is reasonably small
                if total_volume < 0.5e9:
                    info_slot = opTop.DatasetGroup[lane_index][roleIndex]
                    info = info_slot.value
                    info.location = DatasetInfo.Location.ProjectInternal
                    info_slot.setValue(info, check_changed=False)
                    save_needed = True

        if save_needed:
            logger.info(
                "Some of your data cannot be accessed efficiently in 3D in its current format."
                "  It will now be copied to the project file.")
            opWorkflow = self.topLevelOperator.parent
            opWorkflow.shell.onSaveProjectActionTriggered()

    def _checkDataFormatWarnings(self, roleIndex, startingLane, endingLane):
        warn_needed = False
        opTop = self.topLevelOperator
        for lane_index in range(startingLane, endingLane + 1):
            output_slot = opTop.ImageGroup[lane_index][roleIndex]
            if output_slot.meta.inefficient_format:
                warn_needed = True

        if warn_needed:
            QMessageBox.warning(
                self, "Inefficient Data Format",
                "Your data cannot be accessed efficiently in its current format.  "
                "Check the console output for details.\n"
                "(For HDF5 files, be sure to enable chunking on your dataset.)"
            )

    @threadRouted
    def handleDatasetConstraintError(self,
                                     info,
                                     filename,
                                     ex,
                                     roleIndex,
                                     laneIndex,
                                     return_val=[False]):
        msg = "Can't use default properties for dataset:\n\n" + \
              filename + "\n\n" + \
              "because it violates a constraint of the {} applet.\n\n".format( ex.appletName ) + \
              ex.message + "\n\n" + \
              "Please enter valid dataset properties to continue."
        QMessageBox.warning(self, "Dataset Needs Correction", msg)

        # The success of this is 'returned' via our special out-param
        # (We can't return a value from this func because it is @threadRouted.
        successfully_repaired = self.repairDatasetInfo(info, roleIndex,
                                                       laneIndex)
        return_val[0] = successfully_repaired

    def repairDatasetInfo(self, info, roleIndex, laneIndex):
        """Open the dataset properties editor and return True if the new properties are acceptable."""
        defaultInfos = {}
        defaultInfos[laneIndex] = info
        editorDlg = DatasetInfoEditorWidget(
            self,
            self.topLevelOperator,
            roleIndex, [laneIndex],
            defaultInfos,
            show_axis_details=self.show_axis_details)
        dlg_state = editorDlg.exec_()
        return (dlg_state == QDialog.Accepted)

    @classmethod
    def getPossibleInternalPaths(cls, absPath, min_ndim=3, max_ndim=5):
        datasetNames = []
        # Open the file as a read-only so we can get a list of the internal paths
        with h5py.File(absPath, 'r') as f:
            # Define a closure to collect all of the dataset names in the file.
            def accumulateDatasetPaths(name, val):
                if type(val) == h5py._hl.dataset.Dataset and min_ndim <= len(
                        val.shape) <= max_ndim:
                    datasetNames.append('/' + name)

            # Visit every group/dataset in the file
            f.visititems(accumulateDatasetPaths)
        return datasetNames

    def addStack(self, roleIndex, laneIndex):
        """
        The user clicked the "Import Stack Files" button.
        """
        stackDlg = StackFileSelectionWidget(self)
        stackDlg.exec_()
        if stackDlg.result() != QDialog.Accepted:
            return
        files = stackDlg.selectedFiles
        sequence_axis = stackDlg.sequence_axis
        if len(files) == 0:
            return

        cwd = self.topLevelOperator.WorkingDirectory.value
        info = DatasetInfo(os.path.pathsep.join(files), cwd=cwd)

        originalNumLanes = len(self.topLevelOperator.DatasetGroup)

        if laneIndex is None or laneIndex == -1:
            laneIndex = len(self.topLevelOperator.DatasetGroup)
        if len(self.topLevelOperator.DatasetGroup) < laneIndex + 1:
            self.topLevelOperator.DatasetGroup.resize(laneIndex + 1)

        def importStack():
            self.parentApplet.busy = True
            self.parentApplet.appletStateUpdateRequested.emit()

            # Serializer will update the operator for us, which will propagate to the GUI.
            try:
                self.serializer.importStackAsLocalDataset(info, sequence_axis)
                try:
                    self.topLevelOperator.DatasetGroup[laneIndex][
                        roleIndex].setValue(info)
                except DatasetConstraintError as ex:
                    # Give the user a chance to repair the problem.
                    filename = files[0] + "\n...\n" + files[-1]
                    return_val = [False]
                    self.handleDatasetConstraintError(info, filename, ex,
                                                      roleIndex, laneIndex,
                                                      return_val)
                    if not return_val[0]:
                        # Not successfully repaired.  Roll back the changes and give up.
                        self.topLevelOperator.DatasetGroup.resize(
                            originalNumLanes)
            finally:
                self.parentApplet.busy = False
                self.parentApplet.appletStateUpdateRequested.emit()

        req = Request(importStack)
        req.notify_finished(
            lambda result: self.showDataset(laneIndex, roleIndex))
        req.notify_failed(
            partial(self.handleFailedStackLoad, files, originalNumLanes))
        req.submit()

    @threadRouted
    def handleFailedStackLoad(self, files, originalNumLanes, exc, exc_info):
        msg = "Failed to load stack due to the following error:\n{}".format(
            exc)
        msg += "\nAttempted stack files were:\n"
        msg += "\n".join(files)
        log_exception(logger, msg, exc_info)
        QMessageBox.critical(self, "Failed to load image stack", msg)
        self.topLevelOperator.DatasetGroup.resize(originalNumLanes)

    def handleClearDatasets(self, roleIndex, selectedRows):
        for row in selectedRows:
            self.topLevelOperator.DatasetGroup[row][roleIndex].disconnect()

        # Remove all operators that no longer have any connected slots
        laneIndexes = range(len(self.topLevelOperator.DatasetGroup))
        for laneIndex, multislot in reversed(
                zip(laneIndexes, self.topLevelOperator.DatasetGroup)):
            any_ready = False
            for slot in multislot:
                any_ready |= slot.ready()
            if not any_ready:
                self.topLevelOperator.DatasetGroup.removeSlot(
                    laneIndex,
                    len(self.topLevelOperator.DatasetGroup) - 1)

        # Notify the workflow that something that could affect applet readyness has occurred.
        self.parentApplet.appletStateUpdateRequested.emit()

    def editDatasetInfo(self, roleIndex, laneIndexes):
        editorDlg = DatasetInfoEditorWidget(
            self,
            self.topLevelOperator,
            roleIndex,
            laneIndexes,
            show_axis_details=self.show_axis_details)
        editorDlg.exec_()
        self.parentApplet.appletStateUpdateRequested.emit()

    def updateInternalPathVisiblity(self):
        for view in self._detailViewerWidgets:
            model = view.model()
            view.setColumnHidden(DatasetDetailedInfoColumn.InternalID,
                                 not model.hasInternalPaths())

    def addDvidVolume(self, roleIndex, laneIndex):
        # TODO: Provide list of recently used dvid hosts, loaded from user preferences
        recent_hosts_pref = PreferencesManager.Setting("DataSelection",
                                                       "Recent DVID Hosts")
        recent_hosts = recent_hosts_pref.get()
        if not recent_hosts:
            recent_hosts = ["localhost:8000"]
        recent_hosts = filter(lambda h: h, recent_hosts)

        from dvidDataSelectionBrowser import DvidDataSelectionBrowser
        browser = DvidDataSelectionBrowser(recent_hosts, parent=self)
        if browser.exec_() == DvidDataSelectionBrowser.Rejected:
            return

        if None in browser.get_selection():
            QMessageBox.critical("Couldn't use your selection.")
            return

        rois = None
        hostname, dset_uuid, volume_name, uuid = browser.get_selection()
        dvid_url = 'http://{hostname}/api/node/{uuid}/{volume_name}'.format(
            **locals())
        subvolume_roi = browser.get_subvolume_roi()

        # Relocate host to top of 'recent' list, and limit list to 10 items.
        try:
            i = recent_hosts.index(hostname)
            del recent_hosts[i]
        except ValueError:
            pass
        finally:
            recent_hosts.insert(0, hostname)
            recent_hosts = recent_hosts[:10]

        # Save pref
        recent_hosts_pref.set(recent_hosts)

        if subvolume_roi is None:
            self.addFileNames([dvid_url], roleIndex, laneIndex)
        else:
            # In ilastik, we display the dvid volume axes in C-order, despite the dvid convention of F-order
            # Transpose the subvolume roi to match
            # (see implementation of OpDvidVolume)
            start, stop = subvolume_roi
            start = tuple(reversed(start))
            stop = tuple(reversed(stop))
            self.addFileNames([dvid_url], roleIndex, laneIndex,
                              [(start, stop)])
Exemple #48
0
class SimulationPanel(QWidget):
    def __init__(self):
        QWidget.__init__(self)

        layout = QVBoxLayout()

        self._simulation_mode_combo = QComboBox()
        addHelpToWidget(self._simulation_mode_combo, "run/simulation_mode")

        self._simulation_mode_combo.currentIndexChanged.connect(
            self.toggleSimulationMode)

        simulation_mode_layout = QHBoxLayout()
        simulation_mode_layout.addSpacing(10)
        simulation_mode_layout.addWidget(QLabel("Simulation mode:"), 0,
                                         Qt.AlignVCenter)
        simulation_mode_layout.addWidget(self._simulation_mode_combo, 0,
                                         Qt.AlignVCenter)

        simulation_mode_layout.addSpacing(20)

        self.run_button = QToolButton()
        self.run_button.setIconSize(QSize(32, 32))
        self.run_button.setText("Start Simulation")
        self.run_button.setIcon(resourceIcon("ide/gear_in_play"))
        self.run_button.clicked.connect(self.runSimulation)
        self.run_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        addHelpToWidget(self.run_button, "run/start_simulation")

        simulation_mode_layout.addWidget(self.run_button)
        simulation_mode_layout.addStretch(1)

        layout.addSpacing(5)
        layout.addLayout(simulation_mode_layout)
        layout.addSpacing(10)

        self._simulation_stack = QStackedWidget()
        self._simulation_stack.setLineWidth(1)
        self._simulation_stack.setFrameStyle(QFrame.StyledPanel)

        layout.addWidget(self._simulation_stack)

        self._simulation_widgets = {}
        """ :type: dict[BaseRunModel,SimulationConfigPanel]"""

        self.addSimulationConfigPanel(EnsembleExperimentPanel())
        self.addSimulationConfigPanel(EnsembleSmootherPanel())
        self.addSimulationConfigPanel(IteratedEnsembleSmootherPanel())
        self.addSimulationConfigPanel(MultipleDataAssimilationPanel())

        self.setLayout(layout)

    def addSimulationConfigPanel(self, panel):
        assert isinstance(panel, SimulationConfigPanel)

        panel.toggleAdvancedOptions(False)
        self._simulation_stack.addWidget(panel)

        simulation_model = panel.getSimulationModel()

        self._simulation_widgets[simulation_model] = panel
        self._simulation_mode_combo.addItem(str(simulation_model),
                                            simulation_model)
        panel.simulationConfigurationChanged.connect(
            self.validationStatusChanged)

    def getActions(self):
        return []

    def toggleAdvancedMode(self, show_advanced):
        for panel in self._simulation_widgets.values():
            panel.toggleAdvancedOptions(show_advanced)

    def getCurrentSimulationModel(self):
        data = self._simulation_mode_combo.itemData(
            self._simulation_mode_combo.currentIndex(), Qt.UserRole)
        return data.toPyObject()

    def getSimulationArguments(self):
        """ @rtype: dict[str,object]"""
        simulation_widget = self._simulation_widgets[
            self.getCurrentSimulationModel()]
        return simulation_widget.getSimulationArguments()

    def runSimulation(self):
        case_name = getCurrentCaseName()
        message = "Are you sure you want to use case '%s' for initialization of the initial ensemble when running the simulations?" % case_name
        start_simulations = QMessageBox.question(
            self, "Start simulations?", message,
            QMessageBox.Yes | QMessageBox.No)

        if start_simulations == QMessageBox.Yes:
            run_model = self.getCurrentSimulationModel()
            arguments = self.getSimulationArguments()
            dialog = RunDialog(run_model, arguments, self)
            dialog.startSimulation()
            dialog.exec_()

            ERT.emitErtChange()  # simulations may have added new cases.

    def toggleSimulationMode(self):
        widget = self._simulation_widgets[self.getCurrentSimulationModel()]
        self._simulation_stack.setCurrentWidget(widget)
        self.validationStatusChanged()

    def validationStatusChanged(self):
        widget = self._simulation_widgets[self.getCurrentSimulationModel()]
        self.run_button.setEnabled(widget.isConfigurationValid())
    def __init__(self, parent, layer):
        '''
        Constructor
        '''
        QDialog.__init__(self, parent)
        self.resize(200, 200)

        self.rasterLayer = layer

        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth())
        self.setSizePolicy(sizePolicy)

        verticalLayout = QVBoxLayout(self)
        verticalLayout.setObjectName("verticalLayout")
        stackedWidget = QStackedWidget(self)
        stackedWidget.setObjectName("stackedWidget")
        pageRender = QWidget(stackedWidget)
        pageRender.setObjectName("pageRender")
        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(pageRender.sizePolicy().hasHeightForWidth())
        pageRender.setSizePolicy(sizePolicy)

        horizontalLayout = QHBoxLayout(pageRender)
        horizontalLayout.setObjectName("horizontalLayout")
        frameRender = QFrame(pageRender)
        frameRender.setObjectName("frameRender")
        frameRender.setFrameShape(QFrame.StyledPanel)
        frameRender.setFrameShadow(QFrame.Raised)

        sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(frameRender.sizePolicy().hasHeightForWidth())
        frameRender.setSizePolicy(sizePolicy)
        self.vLayoutFrameRender = QVBoxLayout(frameRender)
        self.vLayoutFrameRender.setObjectName("vLayoutFrameRender")

        horizontalLayout.addWidget(frameRender)
        self.cmbRendererType = ComboBoxPanel(frameRender)
        self.cmbRendererType.Caption = "Render Type"
        self.cmbRendererType.LabelWidth = 70
        self.cmbRendererType.Items = ["Mutiband color", "Paletted", "Singleband gray", "Singleband pseudocolor"]
        self.connect(self.cmbRendererType, SIGNAL("Event_0"), self.cmbRendererType_currentIndexChanged)

        self.vLayoutFrameRender.addWidget(self.cmbRendererType)
        self.gbRenderer = GroupBox(frameRender)
        self.gbRenderer.Caption = self.cmbRendererType.SelectedItem
        self.vLayoutFrameRender.addWidget(self.gbRenderer)

        self.qgsMultiBandColorRendererWidget = QgsMultiBandColorRendererWidget(self.rasterLayer)
        self.qgsPalettedRendererWidget = QgsPalettedRendererWidget(self.rasterLayer)
        self.qgsSingleBandGrayRendererWidget = QgsSingleBandGrayRendererWidget(self.rasterLayer)
        self.qgsSingleBandPseudoColorRendererWidget = QgsSingleBandPseudoColorRendererWidget(self.rasterLayer)


        self.gbRenderer.Add = self.qgsMultiBandColorRendererWidget
        self.gbRenderer.Add = self.qgsPalettedRendererWidget
        self.gbRenderer.Add = self.qgsSingleBandGrayRendererWidget
        self.gbRenderer.Add = self.qgsSingleBandPseudoColorRendererWidget

        self.qgsPalettedRendererWidget.setVisible(False)
        self.qgsSingleBandGrayRendererWidget.setVisible(False)
        self.qgsSingleBandPseudoColorRendererWidget.setVisible(False)

        stackedWidget.addWidget(pageRender)
        # page_2 = QWidget()
        # page_2.setObjectName("page_2")
        # stackedWidget.addWidget(page_2)

        verticalLayout.addWidget(stackedWidget)

        buttonBox = QDialogButtonBox(self)
        buttonBox.setObjectName("buttonBox")
        buttonBox.setOrientation(Qt.Horizontal)
        buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok | QDialogButtonBox.Apply)
        btnApply = buttonBox.button(QDialogButtonBox.Apply)
        btnApply.clicked.connect(self.btnApply_clicked)
        verticalLayout.addWidget(buttonBox)


        # retranslateUi(Dialog)
        buttonBox.accepted.connect(self.OK)
        buttonBox.rejected.connect(self.reject)

        if self.rasterLayer.renderer().bandCount() == 1:
            self.cmbRendererType.SelectedIndex = 2
        elif self.rasterLayer.renderer().bandCount() > 1:
            self.cmbRendererType.SelectedIndex = 0
Exemple #50
0
class ArrayEditor(QDialog):
    """Array Editor Dialog"""    
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)
        
        # Destroying the C++ object right after closing the dialog box,
        # otherwise it may be garbage-collected in another QThread
        # (e.g. the editor's analysis thread in Spyder), thus leading to
        # a segmentation fault on UNIX or an application crash on Windows
        self.setAttribute(Qt.WA_DeleteOnClose)
        
        self.data = None
        self.arraywidget = None
        self.stack = None
        self.layout = None
    
    def setup_and_check(self, data, title='', readonly=False,
                        xlabels=None, ylabels=None):
        """
        Setup ArrayEditor:
        return False if data is not supported, True otherwise
        """
        self.data = data
        is_record_array = data.dtype.names is not None
        is_masked_array = isinstance(data, np.ma.MaskedArray)
        if data.size == 0:
            self.error(_("Array is empty"))
            return False
        if data.ndim > 2:
            self.error(_("Arrays with more than 2 dimensions "
                               "are not supported"))
            return False
        if xlabels is not None and len(xlabels) != self.data.shape[1]:
            self.error(_("The 'xlabels' argument length "
						 	   "do no match array column number"))
            return False
        if ylabels is not None and len(ylabels) != self.data.shape[0]:
            self.error(_("The 'ylabels' argument length "
							   "do no match array row number"))
            return False
        if not is_record_array:
            dtn = data.dtype.name
            if dtn not in SUPPORTED_FORMATS and not dtn.startswith('string') \
               and not dtn.startswith('unicode'):
                arr = _("%s arrays") % data.dtype.name
                self.error(_("%s are currently not supported") % arr)
                return False
        
        self.layout = QGridLayout()
        self.setLayout(self.layout)
        self.setWindowIcon(get_icon('arredit.png'))
        if title:
            title = unicode(title) # in case title is not a string
        else:
            title = _("Array editor")
        if readonly:
            title += ' (' + _('read only') + ')'
        self.setWindowTitle(title)
        self.resize(600, 500)
        
        # Stack widget
        self.stack = QStackedWidget(self)
        if is_record_array:
            for name in data.dtype.names:
                self.stack.addWidget(ArrayEditorWidget(self, data[name],
                                                   readonly, xlabels, ylabels))
        elif is_masked_array:
            self.stack.addWidget(ArrayEditorWidget(self, data, readonly,
                                                   xlabels, ylabels))
            self.stack.addWidget(ArrayEditorWidget(self, data.data, readonly,
                                                   xlabels, ylabels))
            self.stack.addWidget(ArrayEditorWidget(self, data.mask, readonly,
                                                   xlabels, ylabels))
        else:
            self.stack.addWidget(ArrayEditorWidget(self, data, readonly,
                                                   xlabels, ylabels))
        self.arraywidget = self.stack.currentWidget()
        self.connect(self.stack, SIGNAL('currentChanged(int)'),
                     self.current_widget_changed)
        self.layout.addWidget(self.stack, 1, 0)

        # Buttons configuration
        btn_layout = QHBoxLayout()
        if is_record_array or is_masked_array:
            if is_record_array:
                btn_layout.addWidget(QLabel(_("Record array fields:")))
                names = []
                for name in data.dtype.names:
                    field = data.dtype.fields[name]
                    text = name
                    if len(field) >= 3:
                        title = field[2]
                        if not isinstance(title, basestring):
                            title = repr(title)
                        text += ' - '+title
                    names.append(text)
            else:
                names = [_('Masked data'), _('Data'), _('Mask')]
            ra_combo = QComboBox(self)
            self.connect(ra_combo, SIGNAL('currentIndexChanged(int)'),
                         self.stack.setCurrentIndex)
            ra_combo.addItems(names)
            btn_layout.addWidget(ra_combo)
            if is_masked_array:
                label = QLabel(_("<u>Warning</u>: changes are applied separately"))
                label.setToolTip(_("For performance reasons, changes applied "\
                                   "to masked array won't be reflected in "\
                                   "array's data (and vice-versa)."))
                btn_layout.addWidget(label)
            btn_layout.addStretch()
        bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.connect(bbox, SIGNAL("accepted()"), SLOT("accept()"))
        self.connect(bbox, SIGNAL("rejected()"), SLOT("reject()"))
        btn_layout.addWidget(bbox)
        self.layout.addLayout(btn_layout, 2, 0)
        
        self.setMinimumSize(400, 300)
        
        # Make the dialog act as a window
        self.setWindowFlags(Qt.Window)
        
        return True
        
    def current_widget_changed(self, index):
        self.arraywidget = self.stack.widget(index)
        
    def accept(self):
        """Reimplement Qt method"""
        for index in range(self.stack.count()):
            self.stack.widget(index).accept_changes()
        QDialog.accept(self)
        
    def get_value(self):
        """Return modified array -- this is *not* a copy"""
        # It is import to avoid accessing Qt C++ object as it has probably
        # already been destroyed, due to the Qt.WA_DeleteOnClose attribute
        return self.data

    def error(self, message):
        """An error occured, closing the dialog box"""
        QMessageBox.critical(self, _("Array editor"), message)
        self.setAttribute(Qt.WA_DeleteOnClose)
        self.reject()

    def reject(self):
        """Reimplement Qt method"""
        if self.arraywidget is not None:
            for index in range(self.stack.count()):
                self.stack.widget(index).reject_changes()
        QDialog.reject(self)
Exemple #51
0
class SimulationPanel(QWidget):

    def __init__(self):
        QWidget.__init__(self)

        layout = QVBoxLayout()

        simulation_mode_layout = QHBoxLayout()
        simulation_mode_layout.addSpacing(10)
        simulation_mode_model = SimulationModeModel()
        simulation_mode_model.observable().attach(SimulationModeModel.CURRENT_CHOICE_CHANGED_EVENT, self.toggleSimulationMode)
        simulation_mode_combo = ComboChoice(simulation_mode_model, "Simulation mode", "run/simulation_mode")
        simulation_mode_layout.addWidget(QLabel(simulation_mode_combo.getLabel()), 0, Qt.AlignVCenter)
        simulation_mode_layout.addWidget(simulation_mode_combo, 0, Qt.AlignVCenter)

        # simulation_mode_layout.addStretch()
        simulation_mode_layout.addSpacing(20)

        self.run_button = QToolButton()
        self.run_button.setIconSize(QSize(32, 32))
        self.run_button.setText("Start Simulation")
        self.run_button.setIcon(util.resourceIcon("ide/gear_in_play"))
        self.run_button.clicked.connect(self.runSimulation)
        self.run_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        HelpedWidget.addHelpToWidget(self.run_button, "run/start_simulation")

        simulation_mode_layout.addWidget(self.run_button)
        simulation_mode_layout.addStretch(1)

        layout.addSpacing(5)
        layout.addLayout(simulation_mode_layout)
        layout.addSpacing(10)

        self.simulation_stack = QStackedWidget()
        self.simulation_stack.setLineWidth(1)
        self.simulation_stack.setFrameStyle(QFrame.StyledPanel)

        layout.addWidget(self.simulation_stack)

        self.simulation_widgets = {}

        self.addSimulationConfigPanel(EnsembleExperimentPanel())
        self.addSimulationConfigPanel(EnsembleSmootherPanel())
        self.addSimulationConfigPanel(MultipleDataAssimilationPanel())
        self.addSimulationConfigPanel(IteratedEnsembleSmootherPanel())

        self.setLayout(layout)


    def addSimulationConfigPanel(self, panel):
        assert isinstance(panel, SimulationConfigPanel)

        panel.toggleAdvancedOptions(False)
        self.simulation_stack.addWidget(panel)
        self.simulation_widgets[panel.getSimulationModel()] = panel

        panel.simulationConfigurationChanged.connect(self.validationStatusChanged)


    def getActions(self):
        return []


    def toggleAdvancedMode(self, show_advanced):
        for panel in self.simulation_widgets.values():
            panel.toggleAdvancedOptions(show_advanced)


    def getCurrentSimulationMode(self):
        return SimulationModeModel().getCurrentChoice()


    def runSimulation(self):
        case_name = CaseSelectorModel().getCurrentChoice()
        message = "Are you sure you want to use case '%s' for initialization of the initial ensemble when running the simulations?" % case_name
        start_simulations = QMessageBox.question(self, "Start simulations?", message, QMessageBox.Yes | QMessageBox.No )

        if start_simulations == QMessageBox.Yes:
            run_model = self.getCurrentSimulationMode()

            dialog = RunDialog(run_model, self)
            dialog.startSimulation()
            dialog.exec_()

            CaseList().externalModificationNotification() # simulations may have added new cases.


    def toggleSimulationMode(self):
        widget = self.simulation_widgets[self.getCurrentSimulationMode()]
        self.simulation_stack.setCurrentWidget(widget)
        self.validationStatusChanged()


    def validationStatusChanged(self):
        widget = self.simulation_widgets[self.getCurrentSimulationMode()]
        self.run_button.setEnabled(widget.isConfigurationValid())
Exemple #52
0
class SimulationPanel(QWidget):

    def __init__(self):
        QWidget.__init__(self)

        layout = QVBoxLayout()

        simulation_mode_layout = QHBoxLayout()
        simulation_mode_layout.addSpacing(10)
        simulation_mode_model = SimulationModeModel()
        simulation_mode_model.observable().attach(SimulationModeModel.CURRENT_CHOICE_CHANGED_EVENT, self.toggleSimulationMode)
        simulation_mode_combo = ComboChoice(simulation_mode_model, "Simulation mode", "run/simulation_mode")
        simulation_mode_layout.addWidget(QLabel(simulation_mode_combo.getLabel()), 0, Qt.AlignVCenter)
        simulation_mode_layout.addWidget(simulation_mode_combo, 0, Qt.AlignVCenter)

        # simulation_mode_layout.addStretch()
        simulation_mode_layout.addSpacing(20)

        self.run_button = QToolButton()
        self.run_button.setIconSize(QSize(32, 32))
        self.run_button.setText("Start Simulation")
        self.run_button.setIcon(util.resourceIcon("ide/gear_in_play"))
        self.run_button.clicked.connect(self.runSimulation)
        self.run_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        HelpedWidget.addHelpToWidget(self.run_button, "run/start_simulation")

        simulation_mode_layout.addWidget(self.run_button)
        simulation_mode_layout.addStretch(1)

        layout.addSpacing(5)
        layout.addLayout(simulation_mode_layout)
        layout.addSpacing(10)

        self.simulation_stack = QStackedWidget()
        self.simulation_stack.setLineWidth(1)
        self.simulation_stack.setFrameStyle(QFrame.StyledPanel)

        layout.addWidget(self.simulation_stack)

        self.simulation_widgets = {}

        self.addSimulationConfigPanel(EnsembleExperimentPanel())
        self.addSimulationConfigPanel(EnsembleSmootherPanel())
        self.addSimulationConfigPanel(MultipleDataAssimilationPanel())
        self.addSimulationConfigPanel(IteratedEnsembleSmootherPanel())

        self.setLayout(layout)


    def addSimulationConfigPanel(self, panel):
        assert isinstance(panel, SimulationConfigPanel)

        panel.toggleAdvancedOptions(False)
        self.simulation_stack.addWidget(panel)
        self.simulation_widgets[panel.getSimulationModel()] = panel

        panel.simulationConfigurationChanged.connect(self.validationStatusChanged)


    def getActions(self):
        return []


    def toggleAdvancedMode(self, show_advanced):
        for panel in self.simulation_widgets.values():
            panel.toggleAdvancedOptions(show_advanced)


    def getCurrentSimulationMode(self):
        return SimulationModeModel().getCurrentChoice()


    def runSimulation(self):
        case_name = CaseSelectorModel().getCurrentChoice()
        message = "Are you sure you want to use case '%s' for initialization of the initial ensemble when running the simulations?" % case_name
        start_simulations = QMessageBox.question(self, "Start simulations?", message, QMessageBox.Yes | QMessageBox.No )

        if start_simulations == QMessageBox.Yes:
            run_model = self.getCurrentSimulationMode()

            dialog = RunDialog(run_model)
            dialog.startSimulation()
            dialog.exec_()

            CaseList().externalModificationNotification() # simulations may have added new cases.


    def toggleSimulationMode(self):
        widget = self.simulation_widgets[self.getCurrentSimulationMode()]
        self.simulation_stack.setCurrentWidget(widget)
        self.validationStatusChanged()


    def validationStatusChanged(self):
        widget = self.simulation_widgets[self.getCurrentSimulationMode()]
        self.run_button.setEnabled(widget.isConfigurationValid())
class DataExportGui(QWidget):
    """
    Manages all GUI elements in the data selection applet.
    This class itself is the central widget and also owns/manages the applet drawer widgets.
    """
    ###########################################
    ### AppletGuiInterface Concrete Methods ###
    ###########################################
    
    def centralWidget( self ):
        return self

    def appletDrawer(self):
        return self.drawer

    def menus( self ):
        return []

    def viewerControlWidget(self):
        return self._viewerControlWidgetStack

    def setImageIndex(self, index):
        pass

    def stopAndCleanUp(self):
        for editor in self.layerViewerGuis.values():
            self.viewerStack.removeWidget( editor )
            editor.stopAndCleanUp()
        self.layerViewerGuis.clear()

    def imageLaneAdded(self, laneIndex):
        pass

    def imageLaneRemoved(self, laneIndex, finalLength):
        pass

    def allowLaneSelectionChange(self):
        return False

    ###########################################
    ###########################################
    
    def __init__(self, parentApplet, topLevelOperator):
        super(DataExportGui, self).__init__()

        self.drawer = None
        self.topLevelOperator = topLevelOperator

        self.threadRouter = ThreadRouter(self)
        self._thunkEventHandler = ThunkEventHandler(self)
        
        self._initAppletDrawerUic()
        self.initCentralUic()
        self.initViewerControls()
        
        self.parentApplet = parentApplet
        self.progressSignal = parentApplet.progressSignal
        
        @threadRoutedWithRouter(self.threadRouter)
        def handleNewDataset( multislot, index ):
            # Make room in the GUI table
            self.batchOutputTableWidget.insertRow( index )
            
            # Update the table row data when this slot has new data
            # We can't bind in the row here because the row may change in the meantime.
            multislot[index].notifyReady( bind( self.updateTableForSlot ) )
            if multislot[index].ready():
                self.updateTableForSlot( multislot[index] )

            multislot[index].notifyUnready( self._updateExportButtons )
            multislot[index].notifyReady( self._updateExportButtons )

        self.topLevelOperator.ExportPath.notifyInserted( bind( handleNewDataset ) )
        
        # For each dataset that already exists, update the GUI
        for i, subslot in enumerate(self.topLevelOperator.ExportPath):
            handleNewDataset( self.topLevelOperator.ExportPath, i )
            if subslot.ready():
                self.updateTableForSlot(subslot)

        @threadRoutedWithRouter(self.threadRouter)
        def handleLaneRemoved( multislot, index, finalLength ):
            if self.batchOutputTableWidget.rowCount() <= finalLength:
                return

            # Remove the row we don't need any more
            self.batchOutputTableWidget.removeRow( index )

            # Remove the viewer for this dataset
            imageMultiSlot = self.topLevelOperator.Inputs[index]
            if imageMultiSlot in self.layerViewerGuis.keys():
                layerViewerGui = self.layerViewerGuis[imageMultiSlot]
                self.viewerStack.removeWidget( layerViewerGui )
                self._viewerControlWidgetStack.removeWidget( layerViewerGui.viewerControlWidget() )
                layerViewerGui.stopAndCleanUp()

        self.topLevelOperator.Inputs.notifyRemove( bind( handleLaneRemoved ) )
    
    def _initAppletDrawerUic(self, drawerPath=None):
        """
        Load the ui file for the applet drawer, which we own.
        """
        if drawerPath is None:
            localDir = os.path.split(__file__)[0]
            drawerPath = os.path.join( localDir, "dataExportDrawer.ui")
        self.drawer = uic.loadUi(drawerPath)

        self.drawer.settingsButton.clicked.connect( self._chooseSettings )
        self.drawer.exportAllButton.clicked.connect( self.exportAllResults )
        self.drawer.exportAllButton.setIcon( QIcon(ilastikIcons.Save) )
        self.drawer.deleteAllButton.clicked.connect( self.deleteAllResults )
        self.drawer.deleteAllButton.setIcon( QIcon(ilastikIcons.Clear) )
        
        @threadRoutedWithRouter(self.threadRouter)
        def _handleNewSelectionNames( *args ):
            input_names = self.topLevelOperator.SelectionNames.value
            self.drawer.inputSelectionCombo.addItems( input_names )
        self.topLevelOperator.SelectionNames.notifyDirty( _handleNewSelectionNames )
        _handleNewSelectionNames()

        def _handleInputComboSelectionChanged( index ):
            assert index < len(self.topLevelOperator.SelectionNames.value)
            if self.drawer.inputSelectionCombo.currentText() == self.topLevelOperator.TableOnlyName.value:
                self.topLevelOperator.TableOnly.setValue(True)
            else:
                self.topLevelOperator.TableOnly.setValue(False)
                self.topLevelOperator.InputSelection.setValue( index )
        self.drawer.inputSelectionCombo.currentIndexChanged.connect( _handleInputComboSelectionChanged )

    def initCentralUic(self):
        """
        Load the GUI from the ui file into this class and connect it with event handlers.
        """
        # Load the ui file into this class (find it in our own directory)
        localDir = os.path.split(__file__)[0]
        uic.loadUi(localDir+"/dataExport.ui", self)

        self.batchOutputTableWidget.resizeRowsToContents()
        self.batchOutputTableWidget.resizeColumnsToContents()
        self.batchOutputTableWidget.setAlternatingRowColors(True)
        self.batchOutputTableWidget.setShowGrid(False)
        self.batchOutputTableWidget.horizontalHeader().setResizeMode(0, QHeaderView.Interactive)
        
        self.batchOutputTableWidget.horizontalHeader().resizeSection(Column.Dataset, 200)
        self.batchOutputTableWidget.horizontalHeader().resizeSection(Column.ExportLocation, 250)
        self.batchOutputTableWidget.horizontalHeader().resizeSection(Column.Action, 100)

        self.batchOutputTableWidget.verticalHeader().hide()

        # Set up handlers
        self.batchOutputTableWidget.itemSelectionChanged.connect(self.handleTableSelectionChange)

        # Set up the viewer area
        self.initViewerStack()
        self.splitter.setSizes([150, 850])
    
    def initViewerStack(self):
        self.layerViewerGuis = {}
        self.viewerStack.addWidget( QWidget() )
        
    def initViewerControls(self):
        self._viewerControlWidgetStack = QStackedWidget(parent=self)

    def showEvent(self, event):
        super( DataExportGui, self ).showEvent(event)
        self.showSelectedDataset()
    
    def hideEvent(self, event):
        super( DataExportGui, self ).hideEvent(event)
        
        # Make sure all 'on disk' layers are discarded so we aren't using those files any more.
        for opLaneView in self.topLevelOperator:
            opLaneView.cleanupOnDiskView()    

    def _chooseSettings(self):
        opExportModelOp, opSubRegion = get_model_op( self.topLevelOperator )
        if opExportModelOp is None:
            QMessageBox.information( self, 
                                     "Image not ready for export", 
                                     "Export isn't possible yet: No images are ready for export.  "
                                     "Please configure upstream pipeline with valid settings, "
                                     "check that images were specified in the (batch) input applet and try again." )
            return
        
        settingsDlg = DataExportOptionsDlg(self, opExportModelOp)
        if settingsDlg.exec_() == DataExportOptionsDlg.Accepted:
            # Copy the settings from our 'model op' into the real op
            setting_slots = [ opExportModelOp.RegionStart,
                              opExportModelOp.RegionStop,
                              opExportModelOp.InputMin,
                              opExportModelOp.InputMax,
                              opExportModelOp.ExportMin,
                              opExportModelOp.ExportMax,
                              opExportModelOp.ExportDtype,
                              opExportModelOp.OutputAxisOrder,
                              opExportModelOp.OutputFilenameFormat,
                              opExportModelOp.OutputInternalPath,
                              opExportModelOp.OutputFormat ]

            # Disconnect the special 'transaction' slot to prevent these 
            #  settings from triggering many calls to setupOutputs.
            self.topLevelOperator.TransactionSlot.disconnect()

            for model_slot in setting_slots:
                real_inslot = getattr(self.topLevelOperator, model_slot.name)
                if model_slot.ready():
                    real_inslot.setValue( model_slot.value )
                else:
                    real_inslot.disconnect()

            # Re-connect the 'transaction' slot to apply all settings at once.
            self.topLevelOperator.TransactionSlot.setValue(True)

            # Discard the temporary model op
            opExportModelOp.cleanUp()
            opSubRegion.cleanUp()

            # Update the gui with the new export paths            
            for index, slot in enumerate(self.topLevelOperator.ExportPath):
                self.updateTableForSlot(slot)

    def getSlotIndex(self, multislot, subslot ):
        # Which index is this slot?
        for index, slot in enumerate(multislot):
            if slot == subslot:
                return index
        return -1

    @threadRouted
    def updateTableForSlot(self, slot):
        """
        Update the table row that corresponds to the given slot of the top-level operator (could be either input slot)
        """
        row = self.getSlotIndex( self.topLevelOperator.ExportPath, slot )
        assert row != -1, "Unknown input slot!"

        if not self.topLevelOperator.ExportPath[row].ready() or\
           not self.topLevelOperator.RawDatasetInfo[row].ready():
            return
        
        try:
            nickname = self.topLevelOperator.RawDatasetInfo[row].value.nickname
            exportPath = self.topLevelOperator.ExportPath[row].value
        except Slot.SlotNotReadyError:
            # Sadly, it is possible to get here even though we checked for .ready() immediately beforehand.
            # That's because the graph has a diamond-shaped DAG of connections, but the graph has no transaction mechanism
            # (It's therefore possible for RawDatasetInfo[row] to be ready() even though it's upstream partner is NOT ready.
            return
                
        self.batchOutputTableWidget.setItem( row, Column.Dataset, QTableWidgetItem( decode_to_qstring(nickname, 'utf-8') ) )
        self.batchOutputTableWidget.setItem( row, Column.ExportLocation, QTableWidgetItem( decode_to_qstring(exportPath) ) )

        exportNowButton = QPushButton("Export")
        exportNowButton.setToolTip("Generate individual batch output dataset.")
        exportNowButton.clicked.connect( bind(self.exportResultsForSlot, self.topLevelOperator[row] ) )
        self.batchOutputTableWidget.setCellWidget( row, Column.Action, exportNowButton )

        # Select a row if there isn't one already selected.
        selectedRanges = self.batchOutputTableWidget.selectedRanges()
        if len(selectedRanges) == 0:
            self.batchOutputTableWidget.selectRow(0)

    def setEnabledIfAlive(self, widget, enable):
        if not sip.isdeleted(widget):
            widget.setEnabled(enable)
    def _updateExportButtons(self, *args):
        """Called when at least one dataset became 'unready', so we have to disable the export button."""
        all_ready = True
        # Enable/disable the appropriate export buttons in the table.
        # Use ThunkEvents to ensure that this happens in the Gui thread.        
        for row, slot in enumerate( self.topLevelOperator.ImageToExport ):
            all_ready &= slot.ready()
            export_button = self.batchOutputTableWidget.cellWidget( row, Column.Action )
            if export_button is not None:
                executable_event = ThunkEvent( partial(self.setEnabledIfAlive, export_button, slot.ready()) )
                QApplication.instance().postEvent( self, executable_event )

        # Disable the "Export all" button unless all slots are ready.
        executable_event = ThunkEvent( partial(self.setEnabledIfAlive, self.drawer.exportAllButton, all_ready) )
        QApplication.instance().postEvent( self, executable_event )

    def handleTableSelectionChange(self):
        """
        Any time the user selects a new item, select the whole row.
        """
        self.selectEntireRow()
        self.showSelectedDataset()
    
    def selectEntireRow(self):
        # FIXME: There is a better way to do this...
        # Figure out which row is selected
        selectedItemRows = set()
        selectedRanges = self.batchOutputTableWidget.selectedRanges()
        for rng in selectedRanges:
            for row in range(rng.topRow(), rng.bottomRow()+1):
                selectedItemRows.add(row)
        
        # Disconnect from selection change notifications while we do this
        self.batchOutputTableWidget.itemSelectionChanged.disconnect(self.handleTableSelectionChange)
        for row in selectedItemRows:
            self.batchOutputTableWidget.selectRow(row)

        # Reconnect now that we're finished
        self.batchOutputTableWidget.itemSelectionChanged.connect(self.handleTableSelectionChange)

    def exportSlots(self, laneViewList ):
        try:
            # Set the busy flag so the workflow knows not to allow 
            #  upstream changes or shell changes while we're exporting
            self.parentApplet.busy = True
            self.parentApplet.appletStateUpdateRequested.emit()
            
            # Disable our own gui
            QApplication.instance().postEvent( self, ThunkEvent( partial(self.setEnabledIfAlive, self.drawer, False) ) )
            QApplication.instance().postEvent( self, ThunkEvent( partial(self.setEnabledIfAlive, self, False) ) )
            
            # Start with 1% so the progress bar shows up
            self.progressSignal.emit(0)
            self.progressSignal.emit(1)

            def signalFileProgress(slotIndex, percent):
                self.progressSignal.emit( (100*slotIndex + percent) / len(laneViewList) ) 

            # Client hook
            self.parentApplet.prepare_for_entire_export()

            for i, opLaneView in enumerate(laneViewList):
                lane_index = self.topLevelOperator.innerOperators.index(opLaneView)
                logger.debug("Exporting result {}".format(i))

                # If the operator provides a progress signal, use it.
                slotProgressSignal = opLaneView.progressSignal
                slotProgressSignal.subscribe( partial(signalFileProgress, i) )

                try:
                    # Client hook
                    self.parentApplet.prepare_lane_for_export(lane_index)

                    # Export the image
                    opLaneView.run_export()
                    
                    # Client hook
                    self.parentApplet.post_process_lane_export(lane_index)
                except Exception as ex:
                    if opLaneView.ExportPath.ready():
                        msg = "Failed to generate export file: \n"
                        msg += opLaneView.ExportPath.value
                        msg += "\n{}".format( ex )
                    else:
                        msg = "Failed to generate export file."
                        msg += "\n{}".format( ex )
                    log_exception( logger, msg )
                    self.showExportError(msg)

                # We're finished with this file. 
                self.progressSignal.emit( 100*(i+1)/float(len(laneViewList)) )

            # Client hook
            self.parentApplet.post_process_entire_export()
                
            # Ensure the shell knows we're really done.
            self.progressSignal.emit(100)
        except:
            # Cancel our progress.
            self.progressSignal.emit(0, True)
            raise
        finally:
            # We're not busy any more.  Tell the workflow.
            self.parentApplet.busy = False
            self.parentApplet.appletStateUpdateRequested.emit()
            
            # Re-enable our own gui
            QApplication.instance().postEvent( self, ThunkEvent( partial(self.setEnabledIfAlive, self.drawer, True) ) )
            QApplication.instance().postEvent( self, ThunkEvent( partial(self.setEnabledIfAlive, self, True) ) )


    def postProcessLane(self, lane_index):
        """
        Called immediately after the result for each lane is exported.
        Can be overridden by subclasses for post-processing purposes.
        """
        pass
        
    @threadRouted
    def showExportError(self, msg):
        QMessageBox.critical(self, "Failed to export", msg )

    def exportResultsForSlot(self, opLane):
        # Make sure all 'on disk' layers are discarded so we aren't using those files any more.
        for opLaneView in self.topLevelOperator:
            opLaneView.cleanupOnDiskView()
        
        # Do this in a separate thread so the UI remains responsive
        exportThread = threading.Thread(target=bind(self.exportSlots, [opLane]), name="DataExportThread")
        exportThread.start()
    
    def exportAllResults(self):
        # Make sure all 'on disk' layers are discarded so we aren't using those files any more.
        for opLaneView in self.topLevelOperator:
            opLaneView.cleanupOnDiskView()

        # Do this in a separate thread so the UI remains responsive
        exportThread = threading.Thread(target=bind(self.exportSlots, self.topLevelOperator), name="DataExportThread")
        exportThread.start()

    def deleteAllResults(self):
        for innerOp in self.topLevelOperator:
            operatorView = innerOp
            operatorView.cleanupOnDiskView()
            pathComp = PathComponents(operatorView.ExportPath.value, operatorView.WorkingDirectory.value)
            if os.path.exists(pathComp.externalPath):
                os.remove(pathComp.externalPath)
            operatorView.setupOnDiskView()
            # we need to toggle the dirts state in order to enforce a frech dirty signal
            operatorView.Dirty.setValue( False )
            operatorView.Dirty.setValue( True )

    def showSelectedDataset(self):
        """
        Show the exported file in the viewer
        """
        # Get the selected row and corresponding slot value
        selectedRanges = self.batchOutputTableWidget.selectedRanges()
        if len(selectedRanges) == 0:
            return
        row = selectedRanges[0].topRow()
        
        # Hide all layers that come from the disk.
        for opLaneView in self.topLevelOperator:
            opLaneView.cleanupOnDiskView()

        # Activate the 'on disk' layers for this lane (if possible)
        opLane = self.topLevelOperator.getLane(row)
        opLane.setupOnDiskView()
        
        # Create if necessary
        imageMultiSlot = self.topLevelOperator.Inputs[row]
        if imageMultiSlot not in self.layerViewerGuis.keys():
            layerViewer = self.createLayerViewer(opLane)

            # Maximize the x-y view by default.
            layerViewer.volumeEditorWidget.quadview.ensureMaximized(2)
            
            self.layerViewerGuis[imageMultiSlot] = layerViewer
            self.viewerStack.addWidget( layerViewer )
            self._viewerControlWidgetStack.addWidget( layerViewer.viewerControlWidget() )

        # Show the right one
        layerViewer = self.layerViewerGuis[imageMultiSlot]
        self.viewerStack.setCurrentWidget( layerViewer )
        self._viewerControlWidgetStack.setCurrentWidget( layerViewer.viewerControlWidget() )


    def createLayerViewer(self, opLane):
        """
        This method provides an instance of LayerViewerGui for the given data lane.
        If this GUI class is subclassed, this method can be reimplemented to provide 
        custom layer types for the exported layers.
        """
        return DataExportLayerViewerGui(self.parentApplet, opLane)
Exemple #54
0
class Preferencias(QDialog):

    def __init__(self, parent=None):
        QDialog.__init__(self, parent, Qt.Dialog)
        self.setWindowTitle(self.tr("Preferencias - EDIS"))
        self.setMinimumSize(700, 500)
        self.general = preferencias_general.ConfiguracionGeneral(self)
        self.editor = preferencias_editor.TabEditor()
        self.gui = preferencias_gui.ConfiguracionGUI(self)
        self._ejecucion = preferencias_ejecucion.ConfiguracionEjecucion(self)

        # valor: texto en combo, clave: instancia de widgets
        self.widgets = OrderedDict([
            ('General', self.general),
            ('Editor', self.editor),
            ('GUI', self.gui),
            ('Ejecucion', self._ejecucion)])
            #])

        self.load_ui()

        # Conexiones
        self.connect(self.button_general, SIGNAL("clicked()"),
                     lambda: self.cambiar_widget(0))
        self.connect(self.button_editor, SIGNAL("clicked()"),
                     lambda: self.cambiar_widget(1))
        self.connect(self.button_gui, SIGNAL("clicked()"),
                     lambda: self.cambiar_widget(2))
        self.connect(self.button_compi, SIGNAL("clicked()"),
                     lambda: self.cambiar_widget(3))
        self.connect(self.btn_cancel, SIGNAL("clicked()"), self.close)
        self.connect(self.btn_guardar, SIGNAL("clicked()"), self._guardar)

        EDIS.cargar_componente("preferencias", self)

    def load_ui(self):
        box = QVBoxLayout(self)
        box.setContentsMargins(0, 0, 0, 0)
        box.setSpacing(0)

        toolbar = QToolBar()
        toolbar.setIconSize(QSize(40, 40))
        toolbar.setObjectName("preferencias")
        toolbar.setToolButtonStyle(Qt.ToolButtonIconOnly)

        self.button_general = ToolButton("General",
                                         paths.ICONOS['general'])
        self.button_editor = ToolButton("Editor",
                                        paths.ICONOS['edit'])
        self.button_gui = ToolButton("Interfáz", paths.ICONOS['gui'])
        self.button_compi = ToolButton("Ejecución", paths.ICONOS['build'])

        toolbar.addWidget(self.button_general)
        toolbar.addWidget(self.button_editor)
        toolbar.addWidget(self.button_gui)
        toolbar.addWidget(self.button_compi)

        box.addWidget(toolbar)

        self.stack = QStackedWidget()
        box.addWidget(self.stack)

        [self.stack.addWidget(widget)
            for widget in list(self.widgets.values())]

        box_buttons = QHBoxLayout()
        box_buttons.setMargin(10)
        box_buttons.setSpacing(10)
        box_buttons.addStretch(1)
        self.btn_cancel = QPushButton(self.tr("Cancelar"))
        self.btn_guardar = QPushButton(self.tr("Guardar"))
        box_buttons.addWidget(self.btn_cancel)
        box_buttons.addWidget(self.btn_guardar)

        box.addLayout(box_buttons)

    def mostrar(self):
        self.stack.setCurrentIndex(0)
        self.show()

    def cambiar_widget(self, index):
        if not self.isVisible():
            self.show()
        self.stack.setCurrentIndex(index)

    def _guardar(self):
        [self.stack.widget(i).guardar()
            for i in range(self.stack.count())]
        self.close()
class DataSelectionGui(QWidget):
    """
    Manages all GUI elements in the data selection applet.
    This class itself is the central widget and also owns/manages the applet drawer widgets.
    """

    ###########################################
    ### AppletGuiInterface Concrete Methods ###
    ###########################################

    def centralWidget( self ):
        return self

    def appletDrawer( self ):
        return self._drawer

    def menus( self ):
        return []

    def viewerControlWidget(self):
        return self._viewerControlWidgetStack

    def setImageIndex(self, imageIndex):
        if imageIndex is not None:
            self.laneSummaryTableView.selectRow(imageIndex)
            for detailWidget in self._detailViewerWidgets:
                detailWidget.selectRow(imageIndex)

    def stopAndCleanUp(self):
        for editor in self.volumeEditors.values():
            self.viewerStack.removeWidget( editor )
            self._viewerControlWidgetStack.removeWidget( editor.viewerControlWidget() )
            editor.stopAndCleanUp()
        self.volumeEditors.clear()

    def imageLaneAdded(self, laneIndex):
        if len(self.laneSummaryTableView.selectedIndexes()) == 0:
            self.laneSummaryTableView.selectRow(laneIndex)
        
        # We don't have any real work to do because this gui initiated the lane addition in the first place
        if self.guiMode != GuiMode.Batch:
            if(len(self.topLevelOperator.DatasetGroup) != laneIndex+1):
                import warnings
                warnings.warn("DataSelectionGui.imageLaneAdded(): length of dataset multislot out of sync with laneindex [%s != %s + 1]" % (len(self.topLevelOperator.DatasetGroup), laneIndex))

    def imageLaneRemoved(self, laneIndex, finalLength):
        # We assume that there's nothing to do here because THIS GUI initiated the lane removal
        if self.guiMode != GuiMode.Batch:
            assert len(self.topLevelOperator.DatasetGroup) == finalLength

    ###########################################
    ###########################################

    def __init__(self, parentApplet, dataSelectionOperator, serializer, instructionText, guiMode=GuiMode.Normal, max_lanes=None):
        """
        Constructor.
        
        :param dataSelectionOperator: The top-level operator.  Must be of type :py:class:`OpMultiLaneDataSelectionGroup`.
        :param serializer: The applet's serializer.  Must be of type :py:class:`DataSelectionSerializer`
        :param instructionText: A string to display in the applet drawer.
        :param guiMode: Either ``GuiMode.Normal`` or ``GuiMode.Batch``.  Currently, there is no difference between normal and batch mode.
        :param max_lanes: The maximum number of lanes that the user is permitted to add to this workflow.  If ``None``, there is no maximum.
        """
        super(DataSelectionGui, self).__init__()

        self.parentApplet = parentApplet
        self._max_lanes = max_lanes

        self._viewerControls = QWidget()
        self.topLevelOperator = dataSelectionOperator
        self.guiMode = guiMode
        self.serializer = serializer
        self.threadRouter = ThreadRouter(self)

        self._initCentralUic()
        self._initAppletDrawerUic(instructionText)
        
        self._viewerControlWidgetStack = QStackedWidget(self)

        def handleImageRemoved(multislot, index, finalLength):
            # Remove the viewer for this dataset
            imageSlot = self.topLevelOperator.Image[index]
            if imageSlot in self.volumeEditors.keys():
                editor = self.volumeEditors[imageSlot]
                self.viewerStack.removeWidget( editor )
                self._viewerControlWidgetStack.removeWidget( editor.viewerControlWidget() )
                editor.stopAndCleanUp()

        self.topLevelOperator.Image.notifyRemove( bind( handleImageRemoved ) )

    def _initCentralUic(self):
        """
        Load the GUI from the ui file into this class and connect it with event handlers.
        """
        # Load the ui file into this class (find it in our own directory)
        localDir = os.path.split(__file__)[0]+'/'
        uic.loadUi(localDir+"/dataSelection.ui", self)

        self._initTableViews()
        self._initViewerStack()
        self.splitter.setSizes( [150, 850] )

    def _initAppletDrawerUic(self, instructionText):
        """
        Load the ui file for the applet drawer, which we own.
        """
        localDir = os.path.split(__file__)[0]+'/'
        self._drawer = uic.loadUi(localDir+"/dataSelectionDrawer.ui")
        self._drawer.instructionLabel.setText( instructionText )

    def _initTableViews(self):
        self.fileInfoTabWidget.setTabText( 0, "Summary" )
        self.laneSummaryTableView.setModel( DataLaneSummaryTableModel(self, self.topLevelOperator) )
        self.laneSummaryTableView.dataLaneSelected.connect( self.showDataset )
        self.laneSummaryTableView.addFilesRequested.connect( self.addFiles )
        self.laneSummaryTableView.addStackRequested.connect( self.addStack )
        self.laneSummaryTableView.removeLanesRequested.connect( self.handleRemoveLaneButtonClicked )

        # These two helper functions enable/disable an 'add files' button for a given role  
        #  based on the the max lane index for that role and the overall permitted max_lanes
        def _update_button_status(viewer, role_index):
            if self._max_lanes:
                viewer.setEnabled( self._findFirstEmptyLane(role_index) < self._max_lanes )

        def _handle_lane_added( button, role_index, slot, lane_index ):
            slot[lane_index][role_index].notifyReady( bind(_update_button_status, button, role_index) )
            slot[lane_index][role_index].notifyUnready( bind(_update_button_status, button, role_index) )

        self._retained = [] # Retain menus so they don't get deleted
        self._detailViewerWidgets = []
        for roleIndex, role in enumerate(self.topLevelOperator.DatasetRoles.value):
            detailViewer = DatasetDetailedInfoTableView(self)
            detailViewer.setModel(DatasetDetailedInfoTableModel(self,
                self.topLevelOperator, roleIndex))
            self._detailViewerWidgets.append( detailViewer )

            # Button
            detailViewer.addFilesRequested.connect(
                    partial(self.addFiles, roleIndex))
            detailViewer.addStackRequested.connect(
                    partial(self.addStack, roleIndex))
            detailViewer.addRemoteVolumeRequested.connect(
                    partial(self.addDvidVolume, roleIndex))

            # Monitor changes to each lane so we can enable/disable the 'add lanes' button for each tab
            self.topLevelOperator.DatasetGroup.notifyInserted( bind( _handle_lane_added, detailViewer, roleIndex ) )
            self.topLevelOperator.DatasetGroup.notifyRemoved( bind( _update_button_status, detailViewer, roleIndex ) )
            
            # While we're at it, do the same for the buttons in the summary table, too
            self.topLevelOperator.DatasetGroup.notifyInserted( bind( _handle_lane_added, self.laneSummaryTableView.addFilesButtons[roleIndex], roleIndex ) )
            self.topLevelOperator.DatasetGroup.notifyRemoved( bind( _update_button_status, self.laneSummaryTableView.addFilesButtons[roleIndex], roleIndex ) )
            
            # Context menu
            detailViewer.replaceWithFileRequested.connect(
                    partial(self.handleReplaceFile, roleIndex) )
            detailViewer.replaceWithStackRequested.connect(
                    partial(self.addStack, roleIndex) )
            detailViewer.editRequested.connect(
                    partial(self.editDatasetInfo, roleIndex) )
            detailViewer.resetRequested.connect(
                    partial(self.handleClearDatasets, roleIndex) )

            # Drag-and-drop
            detailViewer.addFilesRequestedDrop.connect( partial( self.addFileNames, roleIndex=roleIndex ) )

            # Selection handling
            def showFirstSelectedDataset( _roleIndex, lanes ):
                if lanes:
                    self.showDataset( lanes[0], _roleIndex )
            detailViewer.dataLaneSelected.connect( partial(showFirstSelectedDataset, roleIndex) )

            self.fileInfoTabWidget.insertTab(roleIndex, detailViewer, role)
                
        self.fileInfoTabWidget.currentChanged.connect( self.handleSwitchTabs )
        self.fileInfoTabWidget.setCurrentIndex(0)

    def handleSwitchTabs(self, tabIndex ):
        if tabIndex < len(self._detailViewerWidgets):
            roleIndex = tabIndex # If summary tab is moved to the front, change this line.
            detailViewer = self._detailViewerWidgets[roleIndex]
            selectedLanes = detailViewer.selectedLanes
            if selectedLanes:
                self.showDataset( selectedLanes[0], roleIndex )

    def _initViewerStack(self):
        self.volumeEditors = {}
        self.viewerStack.addWidget( QWidget() )

    def handleRemoveLaneButtonClicked(self):
        """
        The user clicked the "Remove" button.
        Remove the currently selected row(s) from both the GUI and the top-level operator.
        """
        # Figure out which lanes to remove
        selectedIndexes = self.laneSummaryTableView.selectedIndexes()
        rows = set()
        for modelIndex in selectedIndexes:
            rows.add( modelIndex.row() )

        # Don't remove the last row, which is just buttons.
        rows.discard( self.laneSummaryTableView.model().rowCount()-1 )

        # Remove in reverse order so row numbers remain consistent
        for row in reversed(sorted(rows)):
            # Remove from the GUI
            self.laneSummaryTableView.model().removeRow(row)
            # Remove from the operator
            finalSize = len(self.topLevelOperator.DatasetGroup) - 1
            self.topLevelOperator.DatasetGroup.removeSlot(row, finalSize)
    
            # The gui and the operator should be in sync (model has one extra row for the button row)
            assert self.laneSummaryTableView.model().rowCount() == len(self.topLevelOperator.DatasetGroup)+1

    @threadRouted
    def showDataset(self, laneIndex, roleIndex=None):
        if laneIndex == -1:
            self.viewerStack.setCurrentIndex(0)
            return
        
        assert threading.current_thread().name == "MainThread"
        imageSlot = self.topLevelOperator.Image[laneIndex]

        # Create if necessary
        if imageSlot not in self.volumeEditors.keys():
            
            class DatasetViewer(LayerViewerGui):
                def moveToTop(self, roleIndex):
                    opLaneView = self.topLevelOperatorView
                    if not opLaneView.DatasetRoles.ready():
                        return
                    datasetRoles = opLaneView.DatasetRoles.value
                    if roleIndex >= len(datasetRoles):
                        return
                    roleName = datasetRoles[roleIndex]
                    try:
                        layerIndex = [l.name for l in self.layerstack].index(roleName)
                    except ValueError:
                        return
                    else:
                        self.layerstack.selectRow(layerIndex)
                        self.layerstack.moveSelectedToTop()

                def setupLayers(self):
                    opLaneView = self.topLevelOperatorView
                    if not opLaneView.DatasetRoles.ready():
                        return []
                    layers = []
                    datasetRoles = opLaneView.DatasetRoles.value
                    for roleIndex, slot in enumerate(opLaneView.ImageGroup):
                        if slot.ready():
                            roleName = datasetRoles[roleIndex]
                            layer = self.createStandardLayerFromSlot(slot)
                            layer.name = roleName
                            layers.append(layer)
                    return layers

            opLaneView = self.topLevelOperator.getLane(laneIndex)
            layerViewer = DatasetViewer(self.parentApplet, opLaneView, crosshair=False)
            
            # Maximize the x-y view by default.
            layerViewer.volumeEditorWidget.quadview.ensureMaximized(2)

            self.volumeEditors[imageSlot] = layerViewer
            self.viewerStack.addWidget( layerViewer )
            self._viewerControlWidgetStack.addWidget( layerViewer.viewerControlWidget() )

        # Show the right one
        viewer = self.volumeEditors[imageSlot]
        displayedRole = self.fileInfoTabWidget.currentIndex()
        viewer.moveToTop(displayedRole)
        self.viewerStack.setCurrentWidget( viewer )
        self._viewerControlWidgetStack.setCurrentWidget( viewer.viewerControlWidget() )


    def handleReplaceFile(self, roleIndex, startingLane):
        self.addFiles(roleIndex, startingLane)

    def addFiles(self, roleIndex, startingLane=None):
        """
        The user clicked the "Add File" button.
        Ask him to choose a file (or several) and add them to both
          the GUI table and the top-level operator inputs.
        """
        # Find the directory of the most recently opened image file
        mostRecentImageFile = PreferencesManager().get( 'DataSelection', 'recent image' )
        if mostRecentImageFile is not None:
            defaultDirectory = os.path.split(mostRecentImageFile)[0]
        else:
            defaultDirectory = os.path.expanduser('~')

        # Launch the "Open File" dialog
        fileNames = self.getImageFileNamesToOpen(defaultDirectory)

        # If the user didn't cancel
        if len(fileNames) > 0:
            PreferencesManager().set('DataSelection', 'recent image', fileNames[0])
            try:
                self.addFileNames(fileNames, roleIndex, startingLane)
            except RuntimeError as e:
                QMessageBox.critical(self, "Error loading file", str(e))

    def getImageFileNamesToOpen(self, defaultDirectory):
        """
        Launch an "Open File" dialog to ask the user for one or more image files.
        """
        file_dialog = QFileDialog(self, "Select Images")

        extensions = OpDataSelection.SupportedExtensions
        filter_strs = ["*." + x for x in extensions]
        filters = ["{filt} ({filt})".format(filt=x) for x in filter_strs]
        filt_all_str = "Image files (" + ' '.join(filter_strs) + ')'
        file_dialog.setFilters([filt_all_str] + filters)

        # do not display file types associated with a filter
        # the line for "Image files" is too long otherwise
        file_dialog.setNameFilterDetailsVisible(False)
        # select multiple files
        file_dialog.setFileMode(QFileDialog.ExistingFiles)
        if ilastik_config.getboolean("ilastik", "debug"):
            file_dialog.setOption(QFileDialog.DontUseNativeDialog, True)

        if file_dialog.exec_():
            fileNames = file_dialog.selectedFiles()
            # Convert from QtString to python str
            fileNames = map(encode_from_qstring, fileNames)
            return fileNames

        return []

    def _findFirstEmptyLane(self, roleIndex):
        opTop = self.topLevelOperator
        
        # Determine the number of files this role already has
        # Search for the last valid value.
        firstNewLane = 0
        for laneIndex, slot in reversed(zip(range(len(opTop.DatasetGroup)), opTop.DatasetGroup)):
            if slot[roleIndex].ready():
                firstNewLane = laneIndex+1
                break
        return firstNewLane

    def addFileNames(self, fileNames, roleIndex, startingLane=None):
        """
        Add the given filenames to both the GUI table and the top-level operator inputs.
        If startingLane is None, the filenames will be *appended* to the role's list of files.
        """
        infos = []

        if startingLane is None or startingLane == -1:
            startingLane = len(self.topLevelOperator.DatasetGroup)
            endingLane = startingLane+len(fileNames)-1
        else:
            assert startingLane < len(self.topLevelOperator.DatasetGroup)
            max_files = len(self.topLevelOperator.DatasetGroup) - \
                    startingLane
            if len(fileNames) > max_files:
                msg = "You selected {num_selected} files for {num_slots} "\
                      "slots. To add new files use the 'Add new...' option "\
                      "in the context menu or the button in the last row."\
                              .format(num_selected=len(fileNames),
                                      num_slots=max_files)
                QMessageBox.critical( self, "Too many files", msg )
                return
            endingLane = min(startingLane+len(fileNames)-1,
                    len(self.topLevelOperator.DatasetGroup))
            
        if self._max_lanes and endingLane >= self._max_lanes:
            msg = "You may not add more than {} file(s) to this workflow.  Please try again.".format( self._max_lanes )
            QMessageBox.critical( self, "Too many files", msg )
            return

        # Assign values to the new inputs we just allocated.
        # The GUI will be updated by callbacks that are listening to slot changes
        for i, filePath in enumerate(fileNames):
            datasetInfo = DatasetInfo()
            cwd = self.topLevelOperator.WorkingDirectory.value
            
            absPath, relPath = getPathVariants(filePath, cwd)
            
            # Relative by default, unless the file is in a totally different tree from the working directory.
            if relPath is not None and len(os.path.commonprefix([cwd, absPath])) > 1:
                datasetInfo.filePath = relPath
            else:
                datasetInfo.filePath = absPath
                
            datasetInfo.nickname = PathComponents(absPath).filenameBase

            h5Exts = ['.ilp', '.h5', '.hdf5']
            if os.path.splitext(datasetInfo.filePath)[1] in h5Exts:
                datasetNames = self.getPossibleInternalPaths( absPath )
                if len(datasetNames) > 0:
                    datasetInfo.filePath += str(datasetNames[0])
                else:
                    raise RuntimeError("HDF5 file %s has no image datasets" % datasetInfo.filePath)

            # Allow labels by default if this gui isn't being used for batch data.
            datasetInfo.allowLabels = ( self.guiMode == GuiMode.Normal )
            infos.append(datasetInfo)

        # if no exception was thrown, set up the operator now
        opTop = self.topLevelOperator
        originalSize = len(opTop.DatasetGroup)
            
        if len( opTop.DatasetGroup ) < endingLane+1:
            opTop.DatasetGroup.resize( endingLane+1 )
        for laneIndex, info in zip(range(startingLane, endingLane+1), infos):
            try:
                self.topLevelOperator.DatasetGroup[laneIndex][roleIndex].setValue( info )
            except DatasetConstraintError as ex:
                return_val = [False]
                # Give the user a chance to fix the problem
                self.handleDatasetConstraintError(info, info.filePath, ex, roleIndex, laneIndex, return_val)
                if not return_val[0]:
                    # Not successfully repaired.  Roll back the changes and give up.
                    opTop.DatasetGroup.resize( originalSize )
                    break
            except OpDataSelection.InvalidDimensionalityError as ex:
                    opTop.DatasetGroup.resize( originalSize )
                    QMessageBox.critical( self, "Dataset has different dimensionality", ex.message )
                    break
            except:
                QMessageBox.critical( self, "Dataset Load Error", "Wasn't able to load your dataset into the workflow.  See console for details." )
                opTop.DatasetGroup.resize( originalSize )
                raise

        # If we succeeded in adding all images, show the first one.
        if laneIndex == endingLane:
            self.showDataset(startingLane, roleIndex)

        # Notify the workflow that something that could affect applet readyness has occurred.
        self.parentApplet.appletStateUpdateRequested.emit()

        self.updateInternalPathVisiblity()

    @threadRouted
    def handleDatasetConstraintError(self, info, filename, ex, roleIndex, laneIndex, return_val=[False]):
        msg = "Can't use default properties for dataset:\n\n" + \
              filename + "\n\n" + \
              "because it violates a constraint of the {} applet.\n\n".format( ex.appletName ) + \
              ex.message + "\n\n" + \
              "Please enter valid dataset properties to continue."
        QMessageBox.warning( self, "Dataset Needs Correction", msg )
        
        # The success of this is 'returned' via our special out-param
        # (We can't return a value from this func because it is @threadRouted.
        successfully_repaired = self.repairDatasetInfo( info, roleIndex, laneIndex )
        return_val[0] = successfully_repaired

    def repairDatasetInfo(self, info, roleIndex, laneIndex):
        """Open the dataset properties editor and return True if the new properties are acceptable."""
        defaultInfos = {}
        defaultInfos[laneIndex] = info
        editorDlg = DatasetInfoEditorWidget(self, self.topLevelOperator, roleIndex, [laneIndex], defaultInfos)
        dlg_state = editorDlg.exec_()
        return ( dlg_state == QDialog.Accepted )

    def getPossibleInternalPaths(self, absPath):
        datasetNames = []
        # Open the file as a read-only so we can get a list of the internal paths
        with h5py.File(absPath, 'r') as f:
            # Define a closure to collect all of the dataset names in the file.
            def accumulateDatasetPaths(name, val):
                if type(val) == h5py._hl.dataset.Dataset and 3 <= len(val.shape) <= 5:
                    datasetNames.append( '/' + name )
            # Visit every group/dataset in the file
            f.visititems(accumulateDatasetPaths)
        return datasetNames

    def addStack(self, roleIndex, laneIndex):
        """
        The user clicked the "Import Stack Files" button.
        """
        stackDlg = StackFileSelectionWidget(self)
        stackDlg.exec_()
        if stackDlg.result() != QDialog.Accepted :
            return
        files = stackDlg.selectedFiles
        if len(files) == 0:
            return

        info = DatasetInfo()
        info.filePath = "//".join( files )
        prefix = os.path.commonprefix(files)
        info.nickname = PathComponents(prefix).filenameBase
        # Add an underscore for each wildcard digit
        num_wildcards = len(files[-1]) - len(prefix) - len( os.path.splitext(files[-1])[1] )
        info.nickname += "_"*num_wildcards

        # Allow labels by default if this gui isn't being used for batch data.
        info.allowLabels = ( self.guiMode == GuiMode.Normal )
        info.fromstack = True

        originalNumLanes = len(self.topLevelOperator.DatasetGroup)

        if laneIndex is None or laneIndex == -1:
            laneIndex = len(self.topLevelOperator.DatasetGroup)
        if len(self.topLevelOperator.DatasetGroup) < laneIndex+1:
            self.topLevelOperator.DatasetGroup.resize(laneIndex+1)

        def importStack():
            self.parentApplet.busy = True
            self.parentApplet.appletStateUpdateRequested.emit()

            # Serializer will update the operator for us, which will propagate to the GUI.
            try:
                self.serializer.importStackAsLocalDataset( info )
                try:
                    self.topLevelOperator.DatasetGroup[laneIndex][roleIndex].setValue(info)
                except DatasetConstraintError as ex:
                    # Give the user a chance to repair the problem.
                    filename = files[0] + "\n...\n" + files[-1]
                    return_val = [False]
                    self.handleDatasetConstraintError( info, filename, ex, roleIndex, laneIndex, return_val )
                    if not return_val[0]:
                        # Not successfully repaired.  Roll back the changes and give up.
                        self.topLevelOperator.DatasetGroup.resize(originalNumLanes)
            finally:
                self.parentApplet.busy = False
                self.parentApplet.appletStateUpdateRequested.emit()

        req = Request( importStack )
        req.notify_finished( lambda result: self.showDataset(laneIndex, roleIndex) )
        req.notify_failed( partial(self.handleFailedStackLoad, files, originalNumLanes ) )
        req.submit()

    @threadRouted
    def handleFailedStackLoad(self, files, originalNumLanes, exc, exc_info):
        import traceback
        traceback.print_tb(exc_info[2])
        msg = "Failed to load stack due to the following error:\n{}".format( exc )
        msg += "Attempted stack files were:"
        for f in files:
            msg += f + "\n"
        QMessageBox.critical(self, "Failed to load image stack", msg)
        self.topLevelOperator.DatasetGroup.resize(originalNumLanes)

    def handleClearDatasets(self, roleIndex, selectedRows):
        for row in selectedRows:
            self.topLevelOperator.DatasetGroup[row][roleIndex].disconnect()

        # Remove all operators that no longer have any connected slots        
        last_valid = -1
        laneIndexes = range( len(self.topLevelOperator.DatasetGroup) )
        for laneIndex, multislot in reversed(zip(laneIndexes, self.topLevelOperator.DatasetGroup)):
            any_ready = False
            for slot in multislot:
                any_ready |= slot.ready()
            if not any_ready:
                self.topLevelOperator.DatasetGroup.removeSlot( laneIndex, len(self.topLevelOperator.DatasetGroup)-1 )

        # Notify the workflow that something that could affect applet readyness has occurred.
        self.parentApplet.appletStateUpdateRequested.emit()

    def editDatasetInfo(self, roleIndex, laneIndexes):
        editorDlg = DatasetInfoEditorWidget(self, self.topLevelOperator, roleIndex, laneIndexes)
        editorDlg.exec_()

    def updateInternalPathVisiblity(self):
        for view in self._detailViewerWidgets:
            model = view.model()
            view.setColumnHidden(DatasetDetailedInfoColumn.InternalID,
                                 not model.hasInternalPaths())
    
    def addDvidVolume(self, roleIndex, laneIndex):
        # TODO: Provide list of recently used dvid hosts, loaded from user preferences
        from dvidclient.gui.contents_browser import ContentsBrowser
        browser = ContentsBrowser(["localhost:8000"], parent=self)
        if browser.exec_() == ContentsBrowser.Rejected:
            return

        hostname, dset_index, volume_name, uuid = browser.get_selection()
        dvid_url = 'http://{hostname}/api/node/{uuid}/{volume_name}'.format( **locals() )        
        self.addFileNames([dvid_url], roleIndex, laneIndex)
Exemple #56
0
class Container(QSplitter):

    def __init__(self, orientation=Qt.Vertical):
        super(Container, self).__init__(orientation)
        self.__last_open_folder = None
        self.__filename = ""
        self.__created = False
        self.__modified = False
        vbox = QVBoxLayout(self)
        vbox.setContentsMargins(0, 0, 0, 0)
        # Stacked
        self.stacked = QStackedWidget()
        vbox.addWidget(self.stacked)

        # Table
        self.table_widget = table_widget.TableWidget()

        Pireal.load_service("container", self)

    def create_data_base(self, filename=''):
        """ This function opens or creates a database

        :param filename: Database filename
        """

        if self.__created:
            QMessageBox.critical(self, self.tr("Error"),
                                 self.tr("Solo puede tener una base de datos "
                                         "abierta a la vez."))
            return
        if not filename:
            db_name, ok = QInputDialog.getText(self, self.tr("Nueva DB"),
                                               self.tr("Nombre:"))
            if not ok:
                return
        else:
            # From file
            try:
                db_name, data = file_manager.open_database(filename)
            except Exception as reason:
                QMessageBox.critical(self, self.tr("Error!"),
                                     reason.__str__())
                return

            self.table_widget.add_data_base(data)

        # Remove Start Page widget
        if isinstance(self.stacked.widget(0), start_page.StartPage):
            self.stacked.removeWidget(self.stacked.widget(0))
        self.stacked.addWidget(self.table_widget)
        # Title
        pireal = Pireal.get_service("pireal")
        pireal.change_title(db_name)
        # Enable QAction's
        pireal.enable_disable_db_actions()
        self.__created = True

    def create_new_relation(self):
        dialog = new_relation_dialog.NewRelationDialog(self)
        dialog.show()

    def remove_relation(self):
        lateral = Pireal.get_service("lateral")
        rname = lateral.get_relation_name()
        if not rname:
            QMessageBox.critical(self, self.tr("Error"),
                                 self.tr("No se ha seleccionado ninguna "
                                         "relación."))
            return
        r = QMessageBox.question(self, self.tr("Confirmación"),
                                 self.tr("Seguro que quieres eliminar la "
                                         "relación <b>{}</b>").format(rname),
                                             QMessageBox.Yes | QMessageBox.No)
        if r == QMessageBox.No:
            return
        index = lateral.current_index()
        # Remove table
        self.table_widget.remove_table(index)
        # Remove item from list widget
        lateral.remove_item(index)

    def new_query(self, filename=''):
        query_widget = Pireal.get_service("query_widget")
        self.addWidget(query_widget)
        if not query_widget.isVisible():
            query_widget.show()
        pireal = Pireal.get_service("pireal")
        pireal.enable_disable_query_actions()
        query_widget.new_query(filename)

        self.connect(query_widget,
                     SIGNAL("currentEditorSaved(QPlainTextEdit)"),
                     self.save_query)

    @property
    def modified(self):
        return self.__modified

    def show_start_page(self):
        sp = start_page.StartPage()
        self.stacked.addWidget(sp)

    def close_db(self):
        """ Close data base """

        widget = self.stacked.currentWidget()
        if isinstance(widget, table_widget.TableWidget):
            # Clear list of relations
            lateral = Pireal.get_service("lateral")
            lateral.clear_items()
            lateral.hide()
            # Close table widget
            self.stacked.removeWidget(widget)
            # Add start page
            self.show_start_page()

            self.__created = False

    def save_query(self, weditor=None):
        if weditor is None:
            query_widget = Pireal.get_service("query_widget")
            # Editor instance
            weditor = query_widget.get_active_editor()
        if weditor.rfile.is_new:
            return self.save_query_as(weditor)
        content = weditor.toPlainText()
        weditor.rfile.write(content)
        weditor.document().setModified(False)

        self.emit(SIGNAL("currentFileSaved(QString)"),
                  self.tr("Archivo guardado: {}").format(weditor.filename))

    def open_file(self):

        if self.__last_open_folder is None:
            directory = os.path.expanduser("~")
        else:
            directory = self.__last_open_folder
        filename = QFileDialog.getOpenFileName(self, self.tr("Abrir Archivo"),
                                               directory, settings.DBFILE,
                                               QFileDialog.DontUseNativeDialog)
        if not filename:
            return
        # Save folder
        self.__last_open_folder = file_manager.get_path(filename)

        ext = file_manager.get_extension(filename)
        if ext == '.pqf':
            # Query file
            self.new_query(filename)
        elif ext == '.rdb':
            self.load_rdb_database(file_manager.read_rdb_file(filename))
        else:
            self.create_data_base(filename)

    def load_rdb_database(self, content):
        csv_content = ""
        for line in content.splitlines():
            if line.startswith('@'):
                csv_content += '@'
                portion = line.split('(')
                name = portion[0][1:]
                csv_content += name + ':'
                for i in portion[1].split(','):
                    if not i.startswith(' '):
                        field = i.split('/')[0].strip()
                        csv_content += field + ','
            else:
                if not line:
                    continue
                csv_content += line
            csv_content += '\n'

        self.table_widget.add_table_from_rdb_content(csv_content)

    def save_query_as(self, editor=None):
        if editor is None:
            query_widget = Pireal.get_service("query_widget")
            editor = query_widget.get_active_editor()
        directory = os.path.expanduser("~")
        filename = QFileDialog.getSaveFileName(self,
                                               self.tr("Guardar Archivo"),
                                               directory)
        if not filename:
            return
        content = editor.toPlainText()
        editor.rfile.write(content, filename)
        editor.document().setModified(False)

    def load_relation(self, filenames=[]):
        """ Load relation from file """

        if not filenames:
            native_dialog = QFileDialog.DontUseNativeDialog
            if self.__last_open_folder is None:
                directory = os.path.expanduser("~")
            else:
                directory = self.__last_open_folder
            ffilter = settings.RFILES.split(';;')[-1]
            filenames = QFileDialog.getOpenFileNames(self,
                                                     self.tr("Abrir Archivo"),
                                                     directory, ffilter,
                                                     native_dialog)
            if not filenames:
                return
            # Save folder
            self.__last_open_folder = file_manager.get_path(filenames[0])
            self.__modified = True

        # Load tables
        self.table_widget.load_relation(filenames)

    def execute_queries(self):
        query_widget = Pireal.get_service("query_widget")
        query_widget.execute_queries()

    def undo_action(self):
        query_widget = Pireal.get_service("query_widget")
        query_widget.undo()

    def redo_action(self):
        query_widget = Pireal.get_service("query_widget")
        query_widget.redo()

    def cut_action(self):
        query_widget = Pireal.get_service("query_widget")
        query_widget.cut()

    def copy_action(self):
        query_widget = Pireal.get_service("query_widget")
        query_widget.copy()

    def paste_action(self):
        query_widget = Pireal.get_service("query_widget")
        query_widget.paste()

    def check_opened_query_files(self):
        query_widget = Pireal.get_service("query_widget")
        return query_widget.opened_files()