Exemplo n.º 1
0
class CaseList(QWidget):

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

        addHelpToWidget(self, "init/case_list")

        layout = QVBoxLayout()

        self._list = QListWidget(self)
        self._list.setMinimumHeight(100)
        self._list.setMaximumHeight(250)
        self._default_selection_mode = self._list.selectionMode()
        self.setSelectable(False)

        layout.addWidget(QLabel("Available Cases:"))
        layout.addWidget(self._list)

        self._addRemoveWidget = AddRemoveWidget(self.addItem, self.removeItem, horizontal=True)
        self._addRemoveWidget.enableRemoveButton(False)
        layout.addWidget(self._addRemoveWidget)

        self._title = "New keyword"
        self._description = "Enter name of keyword:"

        self.setLayout(layout)

        ERT.ertChanged.connect(self.updateList)
        self.updateList()

    def setSelectable(self, selectable):
        if selectable:
            self._list.setSelectionMode(self._default_selection_mode)
        else:
            self._list.setSelectionMode(QAbstractItemView.NoSelection)


    def addItem(self):
        dialog = ValidatedDialog("New case", "Enter name of new case:", getAllCases())
        new_case_name = dialog.showAndTell()
        if not new_case_name == "":
            selectOrCreateNewCase(new_case_name)

    def removeItem(self):
        message = "Support for removal of items has not been implemented!"
        QMessageBox.information(self, "Not implemented!", message)


    def updateList(self):
        """Retrieves data from the model and inserts it into the list"""
        case_list = getAllCases()

        self._list.clear()

        for case in case_list:
            self._list.addItem(case)
Exemplo n.º 2
0
class OWMoleculeVisualizer(OWWidget):
    settingsList = ["colorFragmets", "showFragments"]

    contextHandlers = {
        "":
        DomainContextHandler("", [
            ContextField("selected_title_indices"),
            ContextField("moleculeTitleAttributeList",
                         (DomainContextHandler.List +
                          DomainContextHandler.SelectedRequired +
                          DomainContextHandler.IncludeMetaAttributes),
                         selected="selectedMoleculeTitleAttrs"),
            ContextField(
                "smiles_var", DomainContextHandler.Required +
                DomainContextHandler.IncludeMetaAttributes)
        ],
                             maxAttributesToPickle=1000)
    }

    def __init__(self,
                 parent=None,
                 signalManager=None,
                 title="Molecule visualizer"):
        super(OWMoleculeVisualizer, self).__init__(parent, signalManager,
                                                   title)

        self.colorFragments = 1
        self.showFragments = 0
        self.selectedFragment = ""
        self.moleculeSmiles = []
        self.fragmentSmiles = []
        self.defFragmentSmiles = []
        self.smiles_var = 0
        self.moleculeTitleAttr = 0
        self.moleculeTitleAttributeList = []
        self.selectedMoleculeTitleAttrs = []
        self.fragmentSmilesAttr = 0
        self.imageSize = 200
        self.numColumns = 4
        self.commitOnChange = 0

        ## GUI
        box = OWGUI.widgetBox(self.controlArea, "Info", addSpace=True)
        self.infoLabel = OWGUI.label(box, self, "Chemicals:")
        box = OWGUI.radioButtonsInBox(self.controlArea,
                                      self,
                                      "showFragments",
                                      ["Show molecules", "Show fragments"],
                                      "Show",
                                      callback=self.updateitems)

        self.showFragmentsRadioButton = box.buttons[-1]
        self.markFragmentsCheckBox = OWGUI.checkBox(box,
                                                    self,
                                                    "colorFragments",
                                                    "Mark fragments",
                                                    callback=self._update)
        box.setSizePolicy(QSizePolicy(QSizePolicy.Minimum,
                                      QSizePolicy.Maximum))
        OWGUI.separator(self.controlArea)

        self.moleculeSmilesCombo = OWGUI.comboBox(self.controlArea,
                                                  self,
                                                  "smiles_var",
                                                  "Molecule SMILES Attribute",
                                                  callback=self.updateitems)
        self.moleculeSmilesCombo.box.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum))
        self.smiles_var_model = VariableListModel(parent=self)
        self.moleculeSmilesCombo.setModel(self.smiles_var_model)

        OWGUI.separator(self.controlArea)
        box = OWGUI.widgetBox(self.controlArea,
                              "Molecule Title Attributes",
                              addSpace=True)

        self.title_var_view = QListView(
            selectionMode=QListView.ExtendedSelection)
        self.title_var_model = VariableListModel(parent=self)
        self.title_var_view.setModel(self.title_var_model)
        self.title_var_view.selectionModel().selectionChanged.connect(
            self._title_selection_changed)
        box.layout().addWidget(self.title_var_view)

        OWGUI.separator(self.controlArea)
        self.fragmentSmilesCombo = OWGUI.comboBox(
            self.controlArea,
            self,
            "fragmentSmilesAttr",
            "Fragment SMILES Attribute",
            callback=self.updateFragmentsListBox)

        self.fragmentSmilesCombo.setModel(VariableListModel(parent=self))
        self.fragmentSmilesCombo.box.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum))
        OWGUI.separator(self.controlArea)
        box = OWGUI.spin(self.controlArea,
                         self,
                         "imageSize",
                         50,
                         500,
                         10,
                         box="Image Size",
                         callback=self._image_size_changed)

        box.setSizePolicy(QSizePolicy(QSizePolicy.Minimum,
                                      QSizePolicy.Maximum))

        OWGUI.separator(self.controlArea)
        box = OWGUI.widgetBox(self.controlArea, "Selection", addSpace=True)
        OWGUI.checkBox(box, self, "commitOnChange", "Commit on change")

        self.selectMarkedMoleculesButton = OWGUI.button(
            box, self, "Select &matched molecules", self.select_marked)
        OWGUI.button(box, self, "&Commit", callback=self.commit, default=True)
        OWGUI.separator(self.controlArea)
        OWGUI.rubber(self.controlArea)

        spliter = QSplitter(Qt.Vertical)
        self.scrollArea = ScrollArea(spliter)

        self.grid = GridWidget()
        self.grid.selectionChanged.connect(self._on_selection_changed)

        self.scrollArea.setWidget(self.grid)
        self.scrollArea.setWidgetResizable(True)
        self.mainArea.layout().addWidget(spliter)

        if pybel:
            self.listBox = QListWidget(spliter)
        else:
            self.listBox = QListWidget(None)
            self.listBox.setHidden(True)

        self.listBox.itemClicked.connect(self.fragmentSelection)

        self.fragmentSmilesCombo.box.setDisabled(not pybel)

        self.data = None
        self.data_subset = []
        self.fragment_data = None
        self.resize(800, 600)
        self.listBox.setMaximumHeight(150)
        self.fragmentSmilesCombo.setDisabled(True)
        self.selectMarkedMoleculesButton.setDisabled(True)
        self.markFragmentsCheckBox.setDisabled(True)
        self.showFragmentsRadioButton.setDisabled(True)

        self.loadSettings()

        if not pybel:
            self.showFragments = 0
            self.warning(
                10, "Pybel module not installed. To view molecule fragments\n"
                "please install openbabel python extension.")

        self.__loop = None

    def setMoleculeTable(self, data):
        self.closeContext()
        self.clear()

        self.data = data
        if data is not None:
            all_vars = data.domain.variables + data.domain.get_metas().values()
            text_vars = filter(
                lambda v: isinstance(v, (Orange.feature.Discrete, Orange.
                                         feature.String)), all_vars)
            var_scored = score_smiles_variables(data, text_vars)
            self.smiles_var_model[:] = [var for var, _ in var_scored]
            self.smiles_var = max(range(len(var_scored)),
                                  key=lambda i: var_scored[i][1])
            self.title_var_model[:] = all_vars

            self.setFragmentSmilesCombo()
            self.updateFragmentsListBox()
            if self.data_subset:
                try:
                    self.data_subset = self.data_subset.select(
                        self.data.domain)
                except Exception:
                    self.data_subset = []
            self.openContext("", data)
        else:
            self.defFragmentSmiles = []
            if not self.fragmentSmilesAttr:
                self.listBox.clear()

            self.openContext("", data)
            self.send("Selected Molecules", None)

    def setMoleculeSubset(self, data):
        self.data_subset = data
        try:
            self.data_subset = self.data_subset.select(self.data.domain)
        except Exception:
            self.data_subset = []

    def setFragmentTable(self, data):
        self.fragment_data = data
        if data is not None:
            self.setFragmentSmilesCombo()
            self.updateFragmentsListBox()
            self.selectedFragment = ""
        else:
            self.setFragmentSmilesCombo()
            self.updateFragmentsListBox()

        self.fragmentSmilesCombo.setEnabled(data is not None)

    def handleNewSignals(self):
        self.updateitems()

    def clear(self):
        self.smiles_var_model[:] = []
        self.title_var_model[:] = []

        self.fragmentSmilesCombo.clear()
        self.grid.clear()
        self._widgets = []
        self._items = []

        if self.__loop is not None:
            self.__loop.close()
            self.__loop = None

    def cleargrid(self):
        self.grid.clear()
        self._widgets = []

    def _update_titles(self):
        if self.data is None:
            return

        title_vars = [
            self.title_var_model[ind.row()]
            for ind in self.title_var_view.selectedIndexes()
        ]

        for item, widget in zip(self._items, self._widgets):
            inst = self.data[item.index]
            text = " / ".join(map(str, (inst[var] for var in title_vars)))
            widget.label.setText(text)

    def setFragmentSmilesCombo(self):
        if self.fragment_data:
            candidates = score_smiles_variables(self.fragment_data)
        else:
            candidates = []

        self.fragmentSmilesCombo.model()[:] = [v for v, _ in candidates]

        if self.fragmentSmilesAttr > len(candidates):
            self.fragmentSmilesAttr = 0

    def updateFragmentsListBox(self):
        if pybel is None:
            return

        fragvars = self.fragmentSmilesCombo.model()
        if 0 <= self.fragmentSmilesAttr < len(fragvars):
            fvar = fragvars[self.fragmentSmilesAttr]
        else:
            fvar = None

        if fvar:
            frags = [
                str(e[fvar]) for e in self.fragment_data
                if not e[fvar].is_special()
            ]
            self.fragmentSmiles = [""] + frags
        else:
            self.fragmentSmiles = [""] + self.defFragmentSmiles

        self.listBox.clear()
        self.listBox.addItems(self.fragmentSmiles)

        self.showFragmentsRadioButton.setDisabled(
            len(self.fragmentSmiles) == 1)
        self.markFragmentsCheckBox.setDisabled(len(self.fragmentSmiles) == 1)
        self.selectMarkedMoleculesButton.setDisabled(True)

    def fragmentSelection(self, item):
        if pybel is None:
            return

        index = self.listBox.indexFromItem(item).row()
        if index == -1:
            index = 0
        self.selectedFragment = self.fragmentSmiles[index]
        self.selectMarkedMoleculesButton.setEnabled(bool(
            self.selectedFragment))
        self.markFragmentsCheckBox.setEnabled(bool(self.selectedFragment))
        if not self.showFragments and self.colorFragments:
            self._update()

    def _title_text(self, index):
        title_vars = [
            self.title_var_model[ind.row()]
            for ind in self.title_var_view.selectedIndexes()
        ]
        inst = self.data[index]
        return " / ".join(map(str, (inst[var] for var in title_vars)))

    def _items_from_var(self, var):
        if self.data is None:
            return None

        values = [(i, str(inst[var])) for i, inst in enumerate(self.data)
                  if not inst[var].is_special()]
        return [
            Item(i, smiles, *self._parse_smiles(smiles))
            for i, smiles in values
        ]

    def _parse_smiles(self, smiles):
        try:
            return (OK, molecule_from_smiles(smiles))
        except Exception:
            return (ParseError, None)

    def updateitems(self):
        if self.showFragments and self.fragmentSmiles:
            values = [(None, frag) for frag in self.fragmentSmiles[1:]]
            items = [
                Item(i, smiles, *self._parse_smiles(smiles))
                for i, smiles in values
            ]
        else:
            smilesvar = self.smiles_var_model[self.smiles_var]
            items = self._items_from_var(smilesvar)

        self._items = items
        self.setupgrid()

    def setupgrid(self):
        self.cleargrid()

        layout = self.grid
        widgets = []

        for item in self._items:
            thumb = ThumbnailWidget(self.grid)
            thumb.setImageSize(self.imageSize, self.imageSize)
            if item.index is not None:
                text = self._title_text(item.index)
            else:
                text = ""
            thumb.label.setText(text)

            widgets.append(thumb)
            layout.appendWidget(thumb)

        self._widgets = widgets
        self.infoLabel.setText("Chemicals %i" % len(self._items))
        self._update()

    def __update_items(self, items, widgets, pattern=None):
        for i, item, widget in zip(range(len(items)), items, widgets):
            if item.status != ParseError:
                if pattern is not None:
                    emb = substructure_embedding(item.molecule, pattern)
                    emb = reduce(list.__iadd__, emb, [])
                    svg = molecule_to_svg_with_substructure(item.molecule, emb)
                else:
                    svg = molecule_to_svg(item.molecule)
            else:
                svg = ""

            widget.setData(svg)
            widget.setEnabled(True)
            yield i * 100.0 / len(items)

    def _update(self):
        if self.showFragments and self.fragmentSmiles:
            loop = self.__update_items(self._items, self._widgets)
        elif self.colorFragments and self.selectedFragment:
            pattern = pybel.Smarts(self.selectedFragment)
            loop = self.__update_items(self._items, self._widgets, pattern)
        else:
            loop = self.__update_items(self._items, self._widgets)
        self.__schedule(loop)

    def __schedule(self, coroutine):
        if self.__loop is not None:
            self.progressBarFinished()
            self.__loop.close()
            self.__loop = None

        self.__loop = coroutine

        self.progressBarInit()
        QTimer.singleShot(0, self.__loop_update)

    @Slot()
    def __loop_update(self):
        if self.__loop is None:
            return

        try:
            progress = next(self.__loop)
        except StopIteration:
            self.__loop = None
            self.progressBarFinished()
        else:
            self.progressBarSet(progress)
            QTimer.singleShot(0, self.__loop_update)

    def _title_selection_changed(self):
        self._update_titles()

    def _image_size_changed(self):
        for widget in self._widgets:
            widget.setImageSize(self.imageSize, self.imageSize)

        self.grid.layout().invalidate()

    def select_marked(self):
        if not pybel:
            return

        if not self.showFragments:
            pattern = pybel.Smarts(self.selectedFragment)
            for item, widget in zip(self._items, self._widgets):
                if item.status != ParseError:
                    emb = substructure_embedding(item.molecule, pattern)
                    widget.setSelected(bool(emb))
                else:
                    widget.setSelected(False)

            if self.commitOnChange:
                self.commit()

    def _on_selection_changed(self):
        if self.commitOnChange:
            self.commit()

    def commit(self):
        if self.showFragments:
            svar = self.smiles_var_model[self.smiles_var]
            items = self._items_from_var(svar)
            frags = [
                item for item, w in zip(self._items, self._widgets)
                if w.selected
            ]
            patterns = [pybel.Smarts(item.smiles) for item in frags]

            def test(molecule, patterns):
                return any(
                    bool(substructure_embedding(molecule, patt))
                    for patt in patterns)

            matched = filter(
                lambda item: item.status != ParseError and test(
                    item.molecule, patterns), items)
            instances = [self.data[item.index] for item in matched]

            if instances:
                table = Orange.data.Table(instances)
                self.send("Selected Molecules", table)
            else:
                self.send("Selected Molecules", None)
        else:
            items = [
                item for item, w in zip(self._items, self._widgets)
                if w.selected
            ]
            instances = [self.data[item.index] for item in items]

            if instances:
                table = Orange.data.Table(instances)
                self.send("Selected Molecules", table)
            else:
                self.send("Selected Molecules", None)

    def onDeleteWidget(self):
        OWWidget.onDeleteWidget(self)
        if self.__loop is not None:
            self.__loop.close()
            self.__loop = None
Exemplo n.º 3
0
class MainWindow(QMainWindow):
    """The main window widget for the program.

    """

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)

        #### 1 CREATE AND INITIALIZE DATA STRUCTURES ####

        self.xLabel = None
        self.xSelection = DEFAULTX
        self.xSelection_old = None
        self.xArray = None

        self.yLabel = None
        self.ySelection = DEFAULTY
        self.ySelection_old = None

        self.yArray = None

        self.filename = None

        self.tdms_file_object = None

        self.channel_registry = {}

        # Y selector on Left
        self.ySelector = QListWidget()
        ySelectorLabel = QLabel("y axis channel")
        self.ySelector.setMaximumWidth(ySelectorLabel.sizeHint().width())

        # File name and plot in the middle
        self.sourceFileName = QLabel("None")
        self.sourceFileName.setSizePolicy(QSizePolicy.Expanding,
                                          QSizePolicy.Fixed)
        sourceFileLabel = QLabel("current file")
        sourceFileLabel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)

        # Matplotlib canvas
        fig = Figure(dpi=100)
        self.canvas = FigureCanvas(fig)
        self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        mpl_toolbar = NavigationToolbar(self.canvas, self.canvas)

        self.axes = fig.add_subplot(111)

        # X selector on bottom
        self.xSelector = QListWidget()
        self.xSelector.addItem("Time")
        self.xSelector.setFlow(0)
        xSelectorLabel = QLabel("x axis channel")
        self.xSelector.setMaximumHeight(self.xSelector.sizeHintForColumn(0))

        # Offset and parameter widgets on the right top
        self.offsetThing = OffsetWidget()
        self.attributesThing = AttributesWidget()

        # Save channel on right bottom
        self.save_chan_chkbx = QCheckBox()
        save_chan_label = QLabel("Save Channel")

        # Status bar at the bottom

        self.fileSizeLabel = QLabel("File Size: {f_size:0>7.3f} MB".format(f_size=0.0))
        self.fileSizeLabel.setFixedWidth(self.fileSizeLabel.sizeHint().width()+10)
        self.fileSizeLabel.setFrameStyle(QFrame.Panel|QFrame.Sunken)

        self.yChanLength = QLabel("Y Channel Length: {y_len:0>7.0f}".format(y_len=0.0))
        self.yChanLength.setFixedWidth(self.yChanLength.sizeHint().width()+10)
        self.yChanLength.setFrameStyle(QFrame.Panel|QFrame.Sunken)
        
        self.xChanLength = QLabel("X Channel Length: {x_len:0>7.0f}".format(x_len=0.0))
        self.xChanLength.setFixedWidth(self.xChanLength.sizeHint().width()+10)
        self.xChanLength.setFrameStyle(QFrame.Panel|QFrame.Sunken)

        status = self.statusBar()
        status.setSizeGripEnabled(False)
        status.addPermanentWidget(self.fileSizeLabel)
        status.addPermanentWidget(self.yChanLength)
        status.addPermanentWidget(self.xChanLength)

        status.showMessage("Ready", 5000)
        
        #2 Create the central widget
        self.centralWidget = QWidget()

        # Left Side
        selectorLayout = QVBoxLayout()
        #selectorLayout.addWidget(xSelectorLabel)
        #selectorLayout.addWidget(self.xSelector)
        selectorLayout.addWidget(ySelectorLabel)
        selectorLayout.addWidget(self.ySelector)
        selectorLayout.addStretch()

        # Center
        centralLayout = QVBoxLayout()
        fileNameLayout = QHBoxLayout()
        xSelectorLayout = QHBoxLayout()
        fileNameLayout.addWidget(sourceFileLabel)
        fileNameLayout.addWidget(self.sourceFileName)
        xSelectorLayout.addWidget(xSelectorLabel)
        xSelectorLayout.addWidget(self.xSelector)
        centralLayout.addLayout(fileNameLayout)
        centralLayout.addWidget(self.canvas)
        centralLayout.addWidget(mpl_toolbar)
        centralLayout.addLayout(xSelectorLayout)

        # Right bottom
        save_chan_layout = QHBoxLayout()
        save_chan_layout.addWidget(self.save_chan_chkbx)
        save_chan_layout.addWidget(save_chan_label)

        # Right Side
        rightLayout = QVBoxLayout()
        rightLayout.addWidget(self.offsetThing)
        rightLayout.addWidget(self.attributesThing)
        rightLayout.addStretch()
        rightLayout.addLayout(save_chan_layout)

        layout = QHBoxLayout()
        layout.addLayout(selectorLayout)
        layout.addLayout(centralLayout)
        layout.addLayout(rightLayout)

        self.centralWidget.setLayout(layout)
        self.setCentralWidget(self.centralWidget)

        self.resize(self.sizeHint())

        #3 Create and set up any dock windows

        #4 Create actions and insert them into menus and toolbars
        fileQuitAction = self.createAction("&Quit", self.close, "Ctrl+Q",
                                           "exit", "Close the application")
        fileOpenAction = self.createAction("&Open TDMS File", self.fileOpen,
                                           QKeySequence.Open, "fileopen",
                                           "Open an existing TDMS file")
        fileExportAction = self.createAction("&Export", self.exprtToHDF5,
                                             "Ctrl+E",
                                             tip="Export the TDMS data to HDF5")

        self.fileMenu = self.menuBar().addMenu("&File")
        self.fileMenuActions = (fileOpenAction, fileExportAction,
                                fileQuitAction)
        #self.addActions(self.fileMenu, self.fileMenuActions)

        self.xSelector.itemSelectionChanged.connect(self.make_x_selection)
        self.ySelector.itemSelectionChanged.connect(self.make_y_selection)
        self.offsetThing.new_offset.connect(self.subtract_offset)
        self.fileMenu.triggered.connect(self.update_file_menu)

        self.save_chan_chkbx.stateChanged.connect(self.toggle_save)

        #5 Read in application's settings
        settings = QSettings()

        # Restore the geometry and state of the main window from last use
        #self.restoreGeometry(settings.value("MainWindow/Geometry"))
        #self.restoreState(settings.value("MainWindow/State"))

        self.setWindowTitle("TDMS to HDF5 Converter")
        self.recentFiles = settings.value("RecentFiles")
        if not self.recentFiles:
            self.recentFiles = []

        self.update_file_menu()

    def update_ui(self):
        pass

    def initVariables(self):
        self.xLabel = None
        self.xSelection = DEFAULTX
        self.xSelection_old = None
        self.xArray = None

        self.yLabel = None
        self.ySelection = DEFAULTY
        self.ySelection_old = None

        self.yArray = None

        self.filename = None

        self.tdms_file_object = None

        self.channel_registry = {}

    def createAction(self, text, slot=None, shortcut=None, icon=None,
                     tip=None, checkable=False, signal="triggered()"):
        # Create the action
        action = QAction(text, self)
        # Give it its icon
        if icon is not None:
            action.setIcon(QIcon(":/{icon}.png".format(icon=icon)))
        # Give it its shortcut
        if shortcut is not None:
            action.setShortcut(shortcut)
        # Set up its help/tip text
        if tip is not None:
            action.setToolTip(tip)
            action.setStatusTip(tip)
        # Connect it to a signal
        if slot is not None:
            self.connect(action, SIGNAL(signal), slot)
        # Make it checkable
        if checkable:
            action.setCheckable(True)
        return action

    def addActions(self, target, actions):
        for action in actions:
            if action is None:
                target.addSeparator()
            else:
                target.addAction(action)

    def update_file_menu(self):
        self.fileMenu.clear()
        self.addActions(self.fileMenu, self.fileMenuActions[:-1])
        current = self.filename if self.filename is not None else None
        recentFiles = []
        for fname in self.recentFiles:
            if fname != current and QFile.exists(fname):
                recentFiles.append(fname)
        if recentFiles:
            self.fileMenu.addSeparator()
            for i, fname in enumerate(recentFiles):
                action = QAction("&{num} {name}".format(num=i+1, name=QFileInfo(fname).fileName()), self)
                action.setData(fname)
                action.triggered.connect(lambda: self.loadFile(fname))
                self.fileMenu.addAction(action)
        self.fileMenu.addSeparator()
        self.fileMenu.addAction(self.fileMenuActions[-1])

    def fileOpen(self): # Process 1
        self.initVariables()
        basedir = os.path.dirname(self.filename) if self.filename is not None \
          else "~/Documents/PhD/root/raw-data/sio2al149/CryoMeasurement"
        formats = "TDMS files (*.tdms)"
        fname = QFileDialog.getOpenFileName(self, "Open a TDMS File",
                                            basedir, formats)

        # Process 1.1 Collect file name
        if fname and QFile.exists(fname):
            self.loadFile(fname)

    def loadFile(self, fname): # Process 1.2 Generate TDMS file object
        self.add_recent_file(fname)
        self.tdms_file_object = TdmsFile(fname)
        self.filename = fname

         # Process 1.3 Read data into local structure
        if self.tdms_file_object:

            # Process 1.3.0 Generate group list
            group_list = self.tdms_file_object.groups()

            # Processes 1.3.1 through 1.3.3 Sort TDMS data
            for group in group_list:
                self.sortTDMSGroupData(group)

            message = "Loaded {f_name}".format(f_name=os.path.basename(fname))
            self.sourceFileName.setText(os.path.basename(fname))

            # Process 2.1 Populate channel selection lists
            self.update_selectors()

        else:
            message = "Failed to load {f_name}".format(f_name=os.path.
                                                       basename(fname))

        self.statusBar().showMessage(message, 5000)

        fsize = os.path.getsize(self.filename)
        self.fileSizeLabel.setText("File Size: {file_size:>7.3f} MB".format(file_size=fsize/1E6))
        #TODO self.updateStatus(message) # see Rapid GUI ch06.pyw

    def add_recent_file(self, fname):
        if fname is None:
            return
        if not fname in self.recentFiles:
            self.recentFiles.insert(0, fname)
            while len(self.recentFiles) > 9:
                self.recentFiles.pop()

    def sortTDMSGroupData(self, group): # Process 1.3 Sort Group data

        # Process 1.3.1 Get <Group> Channels
        group_props = self.tdms_file_object.object(group).properties

        # Process 1.3.2 Get <Group> Properties
        group_chans = self.tdms_file_object.group_channels(group)

        # Process 1.3.3 Create a new channel in the registry for each channel
        # in the group
        for chan in group_chans:
            chan_name = chan.path.split('/')[-1].strip("'")

            # Process 1.3.3.1 Generate new channel object and fill with data
            # Some of the TDMS channels were created, but never populated with
            # data. The following weeds those out.
            try:
                new_chan = Channel(chan_name,
                                device=group,
                                meas_array=chan.data)
            except TypeError:
                self.statusBar().showMessage("Channel {chan} in {dev} has no data"
                                             .format(chan=chan_name, dev=group),
                                             5000)

            try:
                new_chan.set_start_time(chan.property("wf_start_time"))

                new_chan.set_delta_time(chan.property("wf_increment"))

                new_chan.set_location('raw/{c2_name}'.format(c2_name=chan_name))

                if chan_name not in ['TCap', 'xMagnet']:
                    new_chan.set_write()

                # Some of the channel-specific properties were actually
                # saved in the group object's properties list.
                # We retrieve those here.
                # Process 1.3.3.2 Resort the group properties of TDMS ADWin
                if group == "ADWin":
                    for atr_name in ADWIN_DICT[chan_name]:
                        try:
                            new_chan.attributes[atr_name] = \
                              group_props[atr_name]
                        except KeyError:
                            #print('The key {a_name} was not found.'
                            #      .format(a_name=atr_name))
                            #print('The keys available are\n')
                            #print(group_props)
                            pass

                # Process 1.3.3.3 Add new channel to the registry
                self.channel_registry[chan_name] = new_chan

                #print('\tChannel name:\t{ch_name}'.format(ch_name=chan_name))

            except (KeyError, UnboundLocalError):
                pass
                #print('Error: Was unable to load {c3_name}'
                #      .format(c3_name=chan_name))

    def update_selectors(self):

        # Clear the selectors
        self.xSelector.clear()
        self.ySelector.clear()

        # Add the names of the channels in the registry to both selectors
        for key in self.channel_registry.keys():
            self.xSelector.addItem(key)
            self.ySelector.addItem(key)

        # Add the time "channel" to the x selector
        self.xSelector.addItem('Time')

        # Sort the lists (alphabetically) otherwise the order constantly changes
        self.xSelector.sortItems()
        self.ySelector.sortItems()

        # Set the current x selector default
        default_x_item = self.xSelector.findItems(DEFAULTX, Qt.MatchExactly)
        self.xSelector.setCurrentItem(default_x_item[0])

        # Set the current y selector default
        try:
            default_y_item = self.ySelector.findItems(DEFAULTY,
                                                      Qt.MatchExactly)
            self.ySelector.setCurrentItem(default_y_item[0])
        except IndexError:
            self.ySelector.setCurrentRow(0)

        self.xSelector.setMinimumHeight(self.xSelector.sizeHintForRow(0)*3)
        self.ySelector.setMinimumWidth(self.ySelector.sizeHintForColumn(0)+10)

    def exprtToHDF5(self): # Process 5 Save to HDF5
        fname = self.filename.split('.')[0] + '.hdf5'
        basedir = "/home/chris/Documents/PhD/root/data/sio2al149/cryo_measurement"

        if not os.path.exists(basedir):
            os.makedirs(basedir)

        formats = "TDMS files (*.hdf5 *.h5 *.he5 *.hdf)"
    
        dialog = QFileDialog()
        dialog.setFilter(formats)
        dialog.setDefaultSuffix("*.hdf5")
        dialog.selectFile(os.path.join(basedir, fname))
        dialog.setDirectory(basedir)
        if dialog.exec_():
            fname = dialog.selectedFiles()
        else:
            return

        # Process 5.1 Create HDF5 file object
        hdf5_file_object = h5py.File(fname[0])

        # Process 5.2 Create channels at their locations
        for chan in self.channel_registry:

            chan_obj = self.channel_registry[chan]
            chan_name = chan

            #print(chan, self.channel_registry[chan].location,
            #      self.channel_registry[chan].write_to_file)

            # Process 5.2.1 Write channel data
            if self.channel_registry[chan].write_to_file:

                dset = hdf5_file_object.create_dataset(chan_obj.location,
                                                            data=chan_obj.data)

            # Process 5.2.2 Write channel attributes
            for attr_name in self.channel_registry[chan].attributes:
                attr_value = self.channel_registry[chan].attributes[attr_name]

                # Convert the datetime format to a string
                if type(attr_value) is datetime:
                    attr_value = attr_value.isoformat()

                # There's currently a wierd bug when dealing with python3
                # strings.
                # This gets around that
                if type(attr_value) is str:
                    #attr_value = attr_value.encode('utf-8')
                    #attr_value = np.string_(attr_value, dtype="S10")
                    attr_value = np.string_(attr_value)

                dset.attrs.create(attr_name, attr_value)

        # Process 5.3 Write data to file
        hdf5_file_object.flush()
        hdf5_file_object.close()

    def make_x_selection(self):

        self.x_change = True

        # Get the name of the newly selected channel
        self.xSelection = self.xSelector.currentItem().text()

        # Get the axis label
        self.xLabel = self.gen_axis_label(self.xSelection)

        # If the xSelection is time, use the time data instead of measurement
        # data
        if self.xSelection == 'Time':
            try:
                self.xArray = self.channel_registry[self.ySelection].time
            except KeyError:
                self.xArray = np.array([])
        else:
            self.xArray = self.channel_registry[self.xSelection].data

        if self.yLabel:
            self.plotData()

        self.xSelection_old = self.xSelector.currentItem()

        self.x_change = False

        self.xChanLength.setText("X Channel Length: {x_len:>7.0f}".format(x_len=len(self.xArray)))

    def make_y_selection(self, offset=0.0):

        self.y_change = True

        # Get the names of the selected channels from the selectors
        try:
            self.ySelection = self.ySelector.currentItem().text()
        except AttributeError:
            self.ySelection = DEFAULTY

        # Set save channel checkbox state
        self.save_chan_chkbx.setChecked(self.channel_registry[self.ySelection]
                                        .write_to_file)

        # Get the axis label
        self.yLabel = self.gen_axis_label(self.ySelection)

        # Generate the y-channel array to be plotted
        self.yArray = self.channel_registry[self.ySelection].data - offset

        # Update the attributes view
        self.attributesThing.clear_attributes()

        self.attributesThing.select_chan(self.channel_registry[self.ySelection])

        if self.xSelection == 'Time':
            self.make_x_selection()
        else:
            self.plotData()

        self.ySelection_old = self.ySelector.currentItem()

        self.y_change = False

        self.yChanLength.setText("Y Channel Length: {y_len:>7.0f}".format(y_len=len(self.yArray)))

    def gen_axis_label(self, chan_name):

        # Generate the axis labels based on the selected channels
        # Cycle through the labes in the AXESLABELS dictionary
        for axlbl in AXESLABELS.keys():

            # Cycle through the channel names in each label's dictionary entry
            for cn in AXESLABELS[axlbl]:

                # If a channel equals one of the selections, save the label 
                if chan_name == cn:
                    label = axlbl

        return label

    def plotData(self):

        # Clear the plot
        self.axes.cla()

        # Turn on the grid
        self.axes.grid(True)

        # Set the labels
        try: 
            self.axes.set_xlabel(self.xLabel)
        except UnboundLocalError:
            self.statusBar().showMessage("Could not generate an axis label for {chan}"
                                         .format(chan=self.xSelection), 5000)
        try:
            self.axes.set_ylabel(self.yLabel)
        except UnboundLocalError:
            self.statusBar().showMessage("Could not generate an axis label for {chan}"
                                         .format(chan=self.ySelection), 5000)

        # Try plotting the data. There are still no checks in place to make sure
        # the arrays are of the same length.
        try:
            # Plot the data and label it
            self.axes.plot(self.xArray, self.yArray, label=self.ySelection)

            # Show the legend
            self.axes.legend(loc=0)

            # Draw everything
            self.canvas.draw()
        except ValueError:

            QMessageBox.warning(self, "Unequal Arrays", "{y_chan} and {x_chan} "
                                .format(y_chan=self.ySelection,
                                        x_chan=self.xSelection) + \
                                        "are not the same length!")

            if self.x_change:
                self.xSelector.setCurrentItem(self.xSelection_old)
            elif self.y_change:
                self.ySelector.setCurrentItem(self.ySelection_old)

    def subtract_offset(self):
        "Subtract the offset entered from the currently selected y channel."

        offset = self.offsetThing.offset_entry.value()

        self.make_y_selection(offset=offset)

    def toggle_save(self):

        self.channel_registry[self.ySelection].write_to_file = \
          self.save_chan_chkbx.isChecked()

    def create_new_channel(self, ch_name):
        "Create a new channel in the registry."

        #print(ch_name)
        pass

    def closeEvent(self, event):
        """Reimplementation of the close even handler.

        We have to reimplement this because not all close actions, e.g. clicking
        the X button, call the close() method.  We want to catch this so we can
        give the user the opportunity to save unsaved changes before the program
        exits.

        """
        settings = QSettings()
        #settings.setValue("MainWindow/Geometry", QVariant(
        #    self.saveGeometry()))
        #settings.setValue("MainWindow/State", QVariant(
        #    self.saveState()))
        if self.recentFiles:
            recentFiles = self.recentFiles
        else:
            recentFiles = []
        settings.setValue("RecentFiles", recentFiles)
Exemplo n.º 4
0
class KeywordList(HelpedWidget):
    """Shows a list of keywords. The data structure expected and sent to the getter and setter is an array of values."""
    def __init__(self, model, list_label="", help_link=""):
        HelpedWidget.__init__(self, list_label, help_link)

        assert isinstance(model, ListModelMixin)
        self.model = model
        self.keyword_list = []

        self.list = QListWidget(self)
        self.list.setMinimumHeight(100)
        self.list.setMaximumHeight(150)
        self.default_selection_mode = self.list.selectionMode()


        self.addWidget(self.list)

        self.addRemoveWidget = AddRemoveWidget(self.addItem, self.removeItem)
        self.addWidget(self.addRemoveWidget)

        self.title = "New keyword"
        self.description = "Enter name of keyword:"

        self.model.observable().attach(ListModelMixin.LIST_CHANGED_EVENT, self.modelChanged)

        self.modelChanged()

    def setSelectable(self, selectable):
        if selectable:
            self.list.setSelectionMode(self.default_selection_mode)
        else:
            self.list.setSelectionMode(QAbstractItemView.NoSelection)

    def setPopupLabels(self, title, description):
        """Change the labels of the default popup."""
        self.title = title
        self.description = description

    def newKeywordPopup(self, keyword_list):
        """
        Pops up a message box asking for a new keyword.
        Override this and return a string to customize the input dialog - Empty string equals canceled.
        The provided list are the already defined keywords
        """
        new_keyword, ok = QInputDialog.getText(self, self.tr(self.title), self.tr(self.description), QLineEdit.Normal)

        if ok:
            return str(new_keyword).strip()
        else:
            return ""

    def addItem(self):
        """Called by the add button to insert a new keyword"""
        new_keyword = self.newKeywordPopup(self.keyword_list)
        if not new_keyword == "":
            self.model.addItem(new_keyword)


    def removeItem(self):
        """Called by the remove button to remove a selected keyword"""
        if not self.list.currentItem() is None:
            row = self.list.currentRow()
            try:
                self.model.removeItem(self.keyword_list[row])
            except NotImplementedError:
                message = "Support for removal of items has not been implemented!"
                QMessageBox.information(self, "Not implemented!", message)


    def modelChanged(self):
        """Retrieves data from the model and inserts it into the list"""
        keywords = self.model.getList()

        self.list.clear()

        self.keyword_list = keywords

        for keyword in keywords:
            self.list.addItem(keyword)
Exemplo n.º 5
0
class KeywordList(HelpedWidget):
    """Shows a list of keywords. The data structure expected and sent to the getter and setter is an array of values."""
    def __init__(self, model, list_label="", help_link=""):
        HelpedWidget.__init__(self, list_label, help_link)

        assert isinstance(model, ListModelMixin)
        self.model = model
        self.keyword_list = []

        self.list = QListWidget(self)
        self.list.setMinimumHeight(100)
        self.list.setMaximumHeight(150)
        self.default_selection_mode = self.list.selectionMode()

        self.addWidget(self.list)

        self.addRemoveWidget = AddRemoveWidget(self.addItem, self.removeItem)
        self.addWidget(self.addRemoveWidget)

        self.title = "New keyword"
        self.description = "Enter name of keyword:"

        self.model.observable().attach(ListModelMixin.LIST_CHANGED_EVENT,
                                       self.modelChanged)

        self.modelChanged()

    def setSelectable(self, selectable):
        if selectable:
            self.list.setSelectionMode(self.default_selection_mode)
        else:
            self.list.setSelectionMode(QAbstractItemView.NoSelection)

    def setPopupLabels(self, title, description):
        """Change the labels of the default popup."""
        self.title = title
        self.description = description

    def newKeywordPopup(self, keyword_list):
        """
        Pops up a message box asking for a new keyword.
        Override this and return a string to customize the input dialog - Empty string equals canceled.
        The provided list are the already defined keywords
        """
        new_keyword, ok = QInputDialog.getText(self, self.tr(self.title),
                                               self.tr(self.description),
                                               QLineEdit.Normal)

        if ok:
            return str(new_keyword).strip()
        else:
            return ""

    def addItem(self):
        """Called by the add button to insert a new keyword"""
        new_keyword = self.newKeywordPopup(self.keyword_list)
        if not new_keyword == "":
            self.model.addItem(new_keyword)

    def removeItem(self):
        """Called by the remove button to remove a selected keyword"""
        if not self.list.currentItem() is None:
            row = self.list.currentRow()
            try:
                self.model.removeItem(self.keyword_list[row])
            except NotImplementedError:
                message = "Support for removal of items has not been implemented!"
                QMessageBox.information(self, "Not implemented!", message)

    def modelChanged(self):
        """Retrieves data from the model and inserts it into the list"""
        keywords = self.model.getList()

        self.list.clear()

        self.keyword_list = keywords

        for keyword in keywords:
            self.list.addItem(keyword)

    def cleanup(self):
        self.model.observable().detach(ListModelMixin.LIST_CHANGED_EVENT,
                                       self.modelChanged)
class OWMoleculeVisualizer(OWWidget):
    settingsList = ["colorFragmets", "showFragments"]

    contextHandlers = {
        "": DomainContextHandler(
            "",
            [ContextField("selected_title_indices"),
             ContextField("moleculeTitleAttributeList",
                          (DomainContextHandler.List +
                           DomainContextHandler.SelectedRequired +
                           DomainContextHandler.IncludeMetaAttributes),
                          selected="selectedMoleculeTitleAttrs"),
             ContextField("smiles_var",
                          DomainContextHandler.Required +
                          DomainContextHandler.IncludeMetaAttributes)],
             maxAttributesToPickle=1000)
    }

    def __init__(self, parent=None, signalManager=None,
                 title="Molecule visualizer"):
        super(OWMoleculeVisualizer, self).__init__(parent, signalManager, title)

        self.colorFragments = 1
        self.showFragments = 0
        self.selectedFragment = ""
        self.moleculeSmiles = []
        self.fragmentSmiles = []
        self.defFragmentSmiles = []
        self.smiles_var = 0
        self.moleculeTitleAttr = 0
        self.moleculeTitleAttributeList = []
        self.selectedMoleculeTitleAttrs = []
        self.fragmentSmilesAttr = 0
        self.imageSize = 200
        self.numColumns = 4
        self.commitOnChange = 0

        ## GUI
        box = OWGUI.widgetBox(self.controlArea, "Info", addSpace=True)
        self.infoLabel = OWGUI.label(box, self, "Chemicals:")
        box = OWGUI.radioButtonsInBox(
            self.controlArea, self, "showFragments",
            ["Show molecules", "Show fragments"], "Show",
            callback=self.updateitems
        )

        self.showFragmentsRadioButton = box.buttons[-1]
        self.markFragmentsCheckBox = OWGUI.checkBox(
            box, self, "colorFragments", "Mark fragments",
            callback=self._update
        )
        box.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum))
        OWGUI.separator(self.controlArea)

        self.moleculeSmilesCombo = OWGUI.comboBox(
            self.controlArea, self, "smiles_var",
            "Molecule SMILES Attribute",
            callback=self.updateitems
        )
        self.moleculeSmilesCombo.box.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum)
        )
        self.smiles_var_model = VariableListModel(parent=self)
        self.moleculeSmilesCombo.setModel(self.smiles_var_model)

        OWGUI.separator(self.controlArea)
        box = OWGUI.widgetBox(self.controlArea, "Molecule Title Attributes",
                              addSpace=True)

        self.title_var_view = QListView(
            selectionMode=QListView.ExtendedSelection
        )
        self.title_var_model = VariableListModel(parent=self)
        self.title_var_view.setModel(self.title_var_model)
        self.title_var_view.selectionModel().selectionChanged.connect(
            self._title_selection_changed
        )
        box.layout().addWidget(self.title_var_view)

        OWGUI.separator(self.controlArea)
        self.fragmentSmilesCombo = OWGUI.comboBox(
            self.controlArea, self, "fragmentSmilesAttr",
            "Fragment SMILES Attribute",
            callback=self.updateFragmentsListBox
        )

        self.fragmentSmilesCombo.setModel(VariableListModel(parent=self))
        self.fragmentSmilesCombo.box.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum)
        )
        OWGUI.separator(self.controlArea)
        box = OWGUI.spin(self.controlArea, self, "imageSize", 50, 500, 10,
                         box="Image Size", callback=self._image_size_changed)

        box.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum))

        OWGUI.separator(self.controlArea)
        box = OWGUI.widgetBox(self.controlArea, "Selection", addSpace=True)
        OWGUI.checkBox(box, self, "commitOnChange", "Commit on change")

        self.selectMarkedMoleculesButton = OWGUI.button(
            box, self, "Select &matched molecules", self.select_marked
        )
        OWGUI.button(box, self, "&Commit", callback=self.commit, default=True)
        OWGUI.separator(self.controlArea)
        OWGUI.rubber(self.controlArea)

        spliter = QSplitter(Qt.Vertical)
        self.scrollArea = ScrollArea(spliter)

        self.grid = GridWidget()
        self.grid.selectionChanged.connect(self._on_selection_changed)

        self.scrollArea.setWidget(self.grid)
        self.scrollArea.setWidgetResizable(True)
        self.mainArea.layout().addWidget(spliter)

        if pybel:
            self.listBox = QListWidget(spliter)
        else:
            self.listBox = QListWidget(None)
            self.listBox.setHidden(True)

        self.listBox.itemClicked.connect(self.fragmentSelection)

        self.fragmentSmilesCombo.box.setDisabled(not pybel)

        self.data = None
        self.data_subset = []
        self.fragment_data = None
        self.resize(800, 600)
        self.listBox.setMaximumHeight(150)
        self.fragmentSmilesCombo.setDisabled(True)
        self.selectMarkedMoleculesButton.setDisabled(True)
        self.markFragmentsCheckBox.setDisabled(True)
        self.showFragmentsRadioButton.setDisabled(True)

        self.loadSettings()

        if not pybel:
            self.showFragments = 0
            self.warning(10,
                         "Pybel module not installed. To view molecule fragments\n"
                         "please install openbabel python extension.")

        self.__loop = None

    def setMoleculeTable(self, data):
        self.closeContext()
        self.clear()

        self.data = data
        if data is not None:
            all_vars = data.domain.variables + data.domain.get_metas().values()
            text_vars = filter(
                lambda v: isinstance(v, (Orange.feature.Discrete,
                                         Orange.feature.String)),
                all_vars)
            var_scored = score_smiles_variables(data, text_vars)
            self.smiles_var_model[:] = [var for var, _ in var_scored]
            self.smiles_var = max(range(len(var_scored)),
                                  key=lambda i: var_scored[i][1])
            self.title_var_model[:] = all_vars

            self.setFragmentSmilesCombo()
            self.updateFragmentsListBox()
            if self.data_subset:
                try:
                    self.data_subset = self.data_subset.select(self.data.domain)
                except Exception:
                    self.data_subset = []
            self.openContext("", data)
        else:
            self.defFragmentSmiles = []
            if not self.fragmentSmilesAttr:
                self.listBox.clear()

            self.openContext("", data)
            self.send("Selected Molecules", None)

    def setMoleculeSubset(self, data):
        self.data_subset = data
        try:
            self.data_subset = self.data_subset.select(self.data.domain)
        except Exception:
            self.data_subset = []

    def setFragmentTable(self, data):
        self.fragment_data = data
        if data is not None:
            self.setFragmentSmilesCombo()
            self.updateFragmentsListBox()
            self.selectedFragment = ""
        else:
            self.setFragmentSmilesCombo()
            self.updateFragmentsListBox()

        self.fragmentSmilesCombo.setEnabled(data is not None)

    def handleNewSignals(self):
        self.updateitems()

    def clear(self):
        self.smiles_var_model[:] = []
        self.title_var_model[:] = []

        self.fragmentSmilesCombo.clear()
        self.grid.clear()
        self._widgets = []
        self._items = []

        if self.__loop is not None:
            self.__loop.close()
            self.__loop = None

    def cleargrid(self):
        self.grid.clear()
        self._widgets = []

    def _update_titles(self):
        if self.data is None:
            return

        title_vars = [self.title_var_model[ind.row()]
                      for ind in self.title_var_view.selectedIndexes()]

        for item, widget in zip(self._items, self._widgets):
            inst = self.data[item.index]
            text = " / ".join(map(str, (inst[var] for var in title_vars)))
            widget.label.setText(text)

    def setFragmentSmilesCombo(self):
        if self.fragment_data:
            candidates = score_smiles_variables(self.fragment_data)
        else:
            candidates = []

        self.fragmentSmilesCombo.model()[:] = [v for v, _ in candidates]

        if self.fragmentSmilesAttr > len(candidates):
            self.fragmentSmilesAttr = 0

    def updateFragmentsListBox(self):
        if pybel is None:
            return

        fragvars = self.fragmentSmilesCombo.model()
        if 0 <= self.fragmentSmilesAttr < len(fragvars):
            fvar = fragvars[self.fragmentSmilesAttr]
        else:
            fvar = None

        if fvar:
            frags = [str(e[fvar]) for e in self.fragment_data
                     if not e[fvar].is_special()]
            self.fragmentSmiles = [""] + frags
        else:
            self.fragmentSmiles = [""] + self.defFragmentSmiles

        self.listBox.clear()
        self.listBox.addItems(self.fragmentSmiles)

        self.showFragmentsRadioButton.setDisabled(len(self.fragmentSmiles) == 1)
        self.markFragmentsCheckBox.setDisabled(len(self.fragmentSmiles) == 1)
        self.selectMarkedMoleculesButton.setDisabled(True)

    def fragmentSelection(self, item):
        if pybel is None:
            return

        index = self.listBox.indexFromItem(item).row()
        if index == -1:
            index = 0
        self.selectedFragment = self.fragmentSmiles[index]
        self.selectMarkedMoleculesButton.setEnabled(bool(self.selectedFragment))
        self.markFragmentsCheckBox.setEnabled(bool(self.selectedFragment))
        if not self.showFragments and self.colorFragments:
            self._update()

    def _title_text(self, index):
        title_vars = [self.title_var_model[ind.row()]
                      for ind in self.title_var_view.selectedIndexes()]
        inst = self.data[index]
        return " / ".join(map(str, (inst[var] for var in title_vars)))

    def _items_from_var(self, var):
        if self.data is None:
            return None

        values = [(i, str(inst[var])) for i, inst in enumerate(self.data)
                  if not inst[var].is_special()]
        return [Item(i, smiles, *self._parse_smiles(smiles))
                for i, smiles in values]

    def _parse_smiles(self, smiles):
        try:
            return (OK, molecule_from_smiles(smiles))
        except Exception:
            return (ParseError, None)

    def updateitems(self):
        if self.showFragments and self.fragmentSmiles:
            values = [(None, frag) for frag in self.fragmentSmiles[1:]]
            items = [Item(i, smiles, *self._parse_smiles(smiles))
                     for i, smiles in values]
        else:
            smilesvar = self.smiles_var_model[self.smiles_var]
            items = self._items_from_var(smilesvar)

        self._items = items
        self.setupgrid()

    def setupgrid(self):
        self.cleargrid()

        layout = self.grid
        widgets = []

        for item in self._items:
            thumb = ThumbnailWidget(self.grid)
            thumb.setImageSize(self.imageSize, self.imageSize)
            if item.index is not None:
                text = self._title_text(item.index)
            else:
                text = ""
            thumb.label.setText(text)

            widgets.append(thumb)
            layout.appendWidget(thumb)

        self._widgets = widgets
        self.infoLabel.setText("Chemicals %i" % len(self._items))
        self._update()

    def __update_items(self, items, widgets, pattern=None):
        for i, item, widget in zip(range(len(items)), items, widgets):
            if item.status != ParseError:
                if pattern is not None:
                    emb = substructure_embedding(item.molecule, pattern)
                    emb = reduce(list.__iadd__, emb, [])
                    svg = molecule_to_svg_with_substructure(item.molecule, emb)
                else:
                    svg = molecule_to_svg(item.molecule)
            else:
                svg = ""

            widget.setData(svg)
            widget.setEnabled(True)
            yield i * 100.0 / len(items)

    def _update(self):
        if self.showFragments and self.fragmentSmiles:
            loop = self.__update_items(self._items, self._widgets)
        elif self.colorFragments and self.selectedFragment:
            pattern = pybel.Smarts(self.selectedFragment)
            loop = self.__update_items(self._items, self._widgets, pattern)
        else:
            loop = self.__update_items(self._items, self._widgets)
        self.__schedule(loop)

    def __schedule(self, coroutine):
        if self.__loop is not None:
            self.progressBarFinished()
            self.__loop.close()
            self.__loop = None

        self.__loop = coroutine

        self.progressBarInit()
        QTimer.singleShot(0, self.__loop_update)

    @Slot()
    def __loop_update(self):
        if self.__loop is None:
            return

        try:
            progress = next(self.__loop)
        except StopIteration:
            self.__loop = None
            self.progressBarFinished()
        else:
            self.progressBarSet(progress)
            QTimer.singleShot(0, self.__loop_update)

    def _title_selection_changed(self):
        self._update_titles()

    def _image_size_changed(self):
        for widget in self._widgets:
            widget.setImageSize(self.imageSize, self.imageSize)

        self.grid.layout().invalidate()

    def select_marked(self):
        if not pybel:
            return

        if not self.showFragments:
            pattern = pybel.Smarts(self.selectedFragment)
            for item, widget in zip(self._items, self._widgets):
                if item.status != ParseError:
                    emb = substructure_embedding(item.molecule, pattern)
                    widget.setSelected(bool(emb))
                else:
                    widget.setSelected(False)

            if self.commitOnChange:
                self.commit()

    def _on_selection_changed(self):
        if self.commitOnChange:
            self.commit()

    def commit(self):
        if self.showFragments:
            svar = self.smiles_var_model[self.smiles_var]
            items = self._items_from_var(svar)
            frags = [item for item, w in zip(self._items, self._widgets)
                     if w.selected]
            patterns = [pybel.Smarts(item.smiles) for item in frags]

            def test(molecule, patterns):
                return any(bool(substructure_embedding(molecule, patt))
                           for patt in patterns)

            matched = filter(
                lambda item: item.status != ParseError and
                             test(item.molecule, patterns),
                items
            )
            instances = [self.data[item.index] for item in matched]

            if instances:
                table = Orange.data.Table(instances)
                self.send("Selected Molecules", table)
            else:
                self.send("Selected Molecules", None)
        else:
            items = [item for item, w in zip(self._items, self._widgets)
                     if w.selected]
            instances = [self.data[item.index] for item in items]

            if instances:
                table = Orange.data.Table(instances)
                self.send("Selected Molecules", table)
            else:
                self.send("Selected Molecules", None)

    def onDeleteWidget(self):
        OWWidget.onDeleteWidget(self)
        if self.__loop is not None:
            self.__loop.close()
            self.__loop = None