Source code for brainvisa.data.qt4gui.databaseCheckGUI
# -*- coding: utf-8 -*-
#  This software and supporting documentation are distributed by
#      Institut Federatif de Recherche 49
#      CEA/NeuroSpin, Batiment 145,
#      91191 Gif-sur-Yvette cedex
#      France
#
# This software is governed by the CeCILL license version 2 under
# French law and abiding by the rules of distribution of free software.
# You can  use, modify and/or redistribute the software under the
# terms of the CeCILL license version 2 as circulated by CEA, CNRS
# and INRIA at the following URL "http://www.cecill.info".
#
# As a counterpart to the access to the source code and  rights to copy,
# modify and redistribute granted by the license, users are provided only
# with a limited warranty  and the software's author,  the holder of the
# economic rights,  and the successive licensors  have only  limited
# liability.
#
# In this respect, the user's attention is drawn to the risks associated
# with loading,  using,  modifying and/or developing or reproducing the
# software by the user in light of its specific status of free software,
# that may mean  that it is complicated to manipulate,  and  that  also
# therefore means  that it is reserved for developers  and  experienced
# professionals having in-depth computer knowledge. Users are therefore
# encouraged to load and test the software's suitability as regards their
# requirements in conditions enabling the security of their systems and/or
# data to be ensured and,  more generally, to use and operate it in the
# same conditions as regards security.
#
# The fact that you are presently reading this means that you have had
# knowledge of the CeCILL license version 2 and that you accept its terms.
# TODO :
# Pourvoir selectionner par selection multiple dans le listview les actions a cocher
# bouton inverser la selection au lieu de checkbox select all
# interface pour check database : plusieurs colonnes : fichier, type, action(s) (conversion, referentiel)
# possibilite de visualiser la donnee
# filtres pour modifier la vue
from __future__ import absolute_import
from brainvisa.configuration import neuroConfig
# WITH NEW DATABASE SYSTEM ####
from brainvisa.data.qt4gui.diskItemBrowser import DiskItemBrowser
import brainvisa.processing.qtgui.backwardCompatibleQt as qt
from soma.qt_gui.qt_backend import uic
import os
from brainvisa.data import neuroDiskItems, neuroHierarchy
from brainvisa.data.actions import Move, Remove, FileProcess, ImportData
import sys
import six
ICON_SIZE = 16
[docs]class ActionsWidget(qt.QDialog):
    """
    A widget to present a list of file with suggested action associated.
    Each action is (de)selectable.
    The user can choose to run all actions now or later (= ok and cancel button of the dialog)
    """
    def __init__(self, processor, parent=None, uiFile='actions.ui'):
        qt.QDialog.__init__(self, parent)
                            # by default the dialog is not modal (doesn't block
                            # waiting user action)
        layout = qt.QVBoxLayout(self)
        self.setLayout(layout)
        p = os.path.join(os.path.dirname(__file__), uiFile)
                         #os.path.join( neuroConfig.mainPath, 'actions.ui' )
        self.ui = qt.QWidget(self)
        uic.loadUi(p, self.ui)
        layout.addWidget(self.ui)
        # change the instruction bar title
        self.titleLabel = self.ui.titleLabel
        self.titleLabel.setText(_t_('Suggested actions on database files :'))
        # actions list
        self.actionsList = self.ui.actionsList
        # self.actionsList.setSorting(-1) # disable sort
        self.actionsList.setHeaderLabels([_t_("File"), _t_("Action")])
        self.actionsList.setIconSize(qt.QSize(ICON_SIZE, ICON_SIZE))
        self.actionsList.setContextMenuPolicy(qt.Qt.CustomContextMenu)
        # item=None
        directory = None
        for name, component in processor.components.items():
            if component.fileProcesses != []:
                directory = DirectoryWidget(
                    name, self.actionsList, directory, "toolbox.png")
                # item=None
                item = self.addActions(
                    directory, None, component.fileProcesses)
        item = self.addActions(
            self.actionsList, directory, processor.fileProcesses)
        # buttons to run actions
        self.runNowButton = self.ui.runNowButton
        self.runNowButton.setText(_t_("Run now"))
        self.runNowButton.clicked.connect(self.runNow)
        self.runNowButton.setToolTip(
            _t_("Executes checked actions immediatly"))
        self.runLaterButton = self.ui.runLaterButton
        self.runLaterButton.setText(_t_("Run later"))
        self.runLaterButton.clicked.connect(self.runLater)
        self.runLaterButton.setToolTip(
            _t_("Executes checked actions at the end of the pipeline. Does nothing outside of the pipeline."))
        # button to invert the state of selected check box item
        self.selectButton = self.ui.selectButton
        self.selectButton.setText(_t_("Check/Uncheck selection"))
        self.selectButton.clicked.connect(self.invertSelection)
        self.selectButton.setToolTip(
            _t_("Inverts the state of selected items"))
        # print "item added"
        self.resize(850, 600)
    def runNow(self):
        self.done(1)
    def runLater(self):
        self.done(2)
    def invertSelection(self):
        it = qt.QTreeWidgetItemIterator(
            self.actionsList, qt.QTreeWidgetItemIterator.Selected)
        while it.value():
            if getattr(it.value(), "model", None):
                if (it.value().checkState(0) == qt.Qt.Checked):
                    it.value().setCheckState(0, qt.Qt.Unchecked)
                    it.value().model.selected = False
                else:
                    it.value().setCheckState(0, qt.Qt.Checked)
                    it.value().model.selected = True
            it += 1
[docs]    def addActions(self, parent, after, actions):
        item = after
        if type(actions) is list:
            for action in actions:
                if isinstance(action, FileProcess):
                    item = ActionWidget(action, parent, item)
                else:  # it can be a map
                    for key, value in action:
                        item = DirectoryWidget(key, parent, item)
                        self.addActions(item, None, value)
        else:  # it is a map attribute -> map or list of FileProcess
            for key, value in actions.items():
                item = DirectoryWidget(key, parent, item)
                self.addActions(item, None, value)
        return item
#
[docs]class DirectoryWidget(qt.QTreeWidgetItem):
    defaultIcon = "folder.png"
    def __init__(self, name, parent, after=None, icon=defaultIcon):
        qt.QTreeWidgetItem.__init__(self, parent)
        self.setText(0, name)
        self.setExpanded(True)
        pix = qt.QIcon(os.path.join(neuroConfig.iconPath, icon))
        self.setIcon(0, pix)
#
[docs]class ActionWidget(qt.QTreeWidgetItem):
    """
    Item in an ActionsList.
    Shows a file with associated action.
    """
    def __init__(self, fileProcess, parent, after=None):
        self.model = fileProcess
        qt.QTreeWidgetItem.__init__(self, parent)
        self.setText(0, fileProcess.filePattern())
        self.setToolTip(0, fileProcess.filePattern())
        if fileProcess.action is not None:
            icon = fileProcess.action.icon
            self.setText(1, six.text_type(fileProcess.action))
            self.setToolTip(
                1, fileProcess.tooltip + " " + six.text_type(fileProcess.action))
        else:  # there's nothing to do because the file is correct
            icon = "ok.png"
        pix = qt.QIcon(os.path.join(neuroConfig.iconPath, icon))
        self.setIcon(1, pix)
        self.setExpanded(True)
        self.setCheckState(0, qt.Qt.Checked)
    def stateChange(self, state):
        self.model.selected = state
    def setAction(self, action):
        self.model.action = action
        pix = qt.QIcon(os.path.join(neuroConfig.iconPath, action.icon))
        self.setIcon(1, pix)
        self.setText(1, six.text_type(self.model.action))
        self.setToolTip(
            1, self.model.tooltip + " " + six.text_type(self.model.action))
#
[docs]class UnknownFilesWidget(ActionsWidget):
    """
    Widget that presents a list of unknown files and proposes 2 actions on these files : remove and move.
    It is possible to choose the same action for each file with the buttons remove all and move all.
    It is possible to change the action for a partcular file with context menu.
    For move action, default destination is the database directory.
    """
    def __init__(self, processor, parent=None):
        """
        @type processor: DBCleaner
        @param processor: the database cleaner that find unknown files in the database.
        """
        ActionsWidget.__init__(self, processor, parent)
        self.defaultDest = processor.dbDir
        self.database = processor.db
        # change the instruction bar title
        self.titleLabel.setText(
            _t_('Choose actions for unknown files in the database :'))
        # add buttons to change all actions remove all and move all
        # there is two frames action1 and action2 to enable to add some buttons.
        # Else I cannot add a button to the widget at a right place, added
        # button are always on existing buttons...
        self.removeAllButton = qt.QPushButton(
            _t_("Remove all"), self.ui.action1)
        self.removeAllButton.clicked.connect(self.removeAll)
        self.moveAllButton = qt.QPushButton(_t_("Move all"), self.ui.action2)
        self.moveAllButton.clicked.connect(self.moveAll)
        # add a right click menu to change action for a particular file
        self.popupMenu = qt.QMenu()
        pix = qt.QIcon(os.path.join(neuroConfig.iconPath, Remove.icon))
        self.popupMenu.addAction(pix, "Remove",  self.menuRemoveEvent)
        pix = qt.QIcon(os.path.join(neuroConfig.iconPath, Move.icon))
        self.popupMenu.addAction(pix, "Move",  self.menuMoveEvent)
        pix = qt.QIcon(os.path.join(neuroConfig.iconPath, ImportData.icon))
        self.popupMenu.addAction(pix, "Import",  self.menuImportEvent)
        self.actionsList.customContextMenuRequested.connect(
            self.openContextMenu)
[docs]    def openContextMenu(self, pos):
        """
        Called on contextMenuRequested signal. It opens the popup menu at cursor position.
        """
        self.popupMenu.exec(qt.QCursor.pos())
    def menuRemoveEvent(self):
        item = self.actionsList.currentItem()
        if item:
            action = Remove(os.path.dirname(item.model.file))
            item.setAction(action)
    def menuMoveEvent(self):
        item = self.actionsList.currentItem()
        if item:
            # open a dialog to choose where to move
            # getExistingDirectory ( QWidget * parent = 0, const QString &
            # caption = QString(), const QString & dir = QString(), Options
            # options = ShowDirsOnly )
            dest = six.text_type(
                qt.QFileDialog.getExistingDirectory(
                    self, _t_("Choose a directory for destination : "),
                           self.defaultDest, qt.QFileDialog.ShowDirsOnly | qt.QFileDialog.DontUseNativeDialog))
            action = Move(dest)
            item.setAction(action)
    def importDialogAccepted(self, item, action):
        values = self.importDialog.getValues()
        if len(values) > 0:
            action.dest = values[0]
            item.setAction(action)
[docs]    def removeAll(self):
        """
        Called when the user click on remove all button. Set action Remove on all unknown file.
        """
        it = qt.QTreeWidgetItemIterator(self.actionsList)
        while it.value():
            action = Remove(os.path.dirname(it.value().model.file))
            it.value().setAction(action)
            it += 1
[docs]    def moveAll(self):
        """
        Called when the user click on move all button. Set action Move on all unknown file.
        """
        # open a dialog to choose where to move
        dest = six.text_type(
            qt.QFileDialog.getExistingDirectory(
                self, _t_("Choose a directory for destination : "),
                       self.defaultDest, qt.QFileDialog.ShowDirsOnly | qt.QFileDialog.DontUseNativeDialog))
        it = qt.QTreeWidgetItemIterator(self.actionsList)
        while it.value():
            action = Move(
                os.path.join(dest, os.path.basename(it.value().model.file)))
            it.value().setAction(action)
            it += 1
#
[docs]class CheckFilesWidget(ActionsWidget):
    """
    Widget to present checked database files.
    There are several columns to provide information about database items : filename, format, type, suggested action.
    If a file is correct, there is no action associated : an icon "ok" is displayed.
    This widget is based on check.ui qt designer file.
    The checked files are grouped by filters attributes : these attributes are displayed as directories in the listview.
    """
    def __init__(self, processor, parent=None):
        """
        @type processor: DBChecker
        @param processor: the database checker that checks database files and can suggest actions if some files are incorrect.
        """
        super(CheckFilesWidget, self).__init__(processor, parent, "check.ui")
        # actions list
        self.actionsList.setHeaderLabels(
            [_t_("File"), _t_("Type"), _t_("Format"), _t_("Action")])
[docs]    def addActions(self, parent, after, actions):
        """
        This method is redefined because, item are different from ActionsWidget items (more columns to fill)
        """
        item = after
        if type(actions) is list:
            for action in actions:
                if isinstance(action, FileProcess):
                    item = CheckFileWidget(action, parent, item)
                else:  # it can be a map
                    for key, value in action:
                        if key:
                            item = DirectoryWidget(key, parent, item)
                            self.addActions(item, None, value)
                        else:
                            item = self.addActions(parent, item, value)
        else:  # it is a map attribute -> map or list of FileProcess
            for key, value in actions.items():
                if key:
                    item = DirectoryWidget(key, parent, item)
                    self.addActions(item, None, value)
                else:
                    item = self.addActions(parent, item, value)
        return item
#
[docs]class CheckFileWidget(qt.QTreeWidgetItem):
    """
    Item in an CheckFilesWidget.
    For each checked file, show filename, type, format and associated action (or "ok" icon if there is no action).
    """
    def __init__(self, fileProcess, parent, after=None):
        self.model = fileProcess
        qt.QTreeWidgetItem.__init__(self, parent)
        self.setText(0, os.path.basename(fileProcess.diskItem.name))
        self.setToolTip(0, self.text(0))
        self.setText(1, six.text_type(fileProcess.diskItem.type))
        self.setText(2, six.text_type(fileProcess.diskItem.format))
        if fileProcess.action is not None:
            icon = fileProcess.action.icon
            self.setText(3, six.text_type(fileProcess.action))
        else:  # there's nothing to do because the file is correct
            icon = "ok.png"
        pix = qt.QIcon(os.path.join(neuroConfig.iconPath, icon))
        self.setIcon(3, pix)
        if fileProcess.selected:
            self.setCheckState(0, qt.Qt.Checked)  # the item is selected
    def stateChange(self, state):
        self.model.selected = state
    # TODO
    def setAction(self, action):
        self.model.action = action
        pix = qt.QIcon(os.path.join(neuroConfig.iconPath, action.icon))
        self.setIcon(3, pix)
        self.setText(3, six.text_type(self.model.action))
