Ejemplo n.º 1
0
class PathChooser(QWidget):
    """
    PathChooser: shows, enables choosing of and validates paths.
    The data structure expected and sent to the models getValue and setValue is a string.
    """

    PATH_DOES_NOT_EXIST_MSG = "The specified path does not exist."
    FILE_IS_NOT_EXECUTABLE_MSG = "The specified file is not an executable."
    PATH_IS_NOT_A_FILE_MSG = "The specified path must be a file."
    PATH_IS_NOT_ABSOLUTE_MSG = "The specified path must be an absolute path."
    PATH_IS_NOT_A_DIRECTORY_MSG = "The specified path must be a directory."
    REQUIRED_FIELD_MSG = "A path is required."

    #    UNDEFINED = 0
    #    REQUIRED = 1
    #    FILE = 2
    #    DIRECTORY = 4
    #    MUST_EXIST = 8
    #    EXECUTABLE = 16

    def __init__(self, model, help_link=""):
        """
        :type model: ert_gui.ertwidgets.models.path_model.PathModel
        :param help_link: str
        """
        QWidget.__init__(self)
        addHelpToWidget(self, help_link)
        self._validation_support = ValidationSupport(self)

        self._editing = True

        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)

        self._path_line = QLineEdit()
        self._path_line.setMinimumWidth(250)

        layout.addWidget(self._path_line)

        dialog_button = QToolButton(self)
        dialog_button.setIcon(resourceIcon("ide/small/folder"))
        dialog_button.setIconSize(QSize(16, 16))
        dialog_button.clicked.connect(self.selectPath)
        layout.addWidget(dialog_button)

        self.valid_color = self._path_line.palette().color(
            self._path_line.backgroundRole())

        self._path_line.setText(os.getcwd())
        self._editing = False

        self._model = model
        self._model.valueChanged.connect(self.getPathFromModel)

        self._path_line.editingFinished.connect(self.validatePath)
        self._path_line.editingFinished.connect(self.contentsChanged)
        self._path_line.textChanged.connect(self.validatePath)

        self.setLayout(layout)
        self.getPathFromModel()

    def isPathValid(self, path):
        """@rtype: tuple of (bool, str)"""
        path = path.strip()
        path_exists = os.path.exists(path)
        is_file = os.path.isfile(path)
        is_directory = os.path.isdir(path)
        is_executable = os.access(path, os.X_OK)
        is_absolute = os.path.isabs(path)

        valid = True
        message = ""

        if path == "":
            if self._model.pathIsRequired():
                valid = False
                message = PathChooser.REQUIRED_FIELD_MSG
        elif not path_exists:
            if self._model.pathMustExist():
                valid = False
                message = PathChooser.PATH_DOES_NOT_EXIST_MSG
            # todo: check if new (non-existing) file has directory or file format?
        elif path_exists:
            if self._model.pathMustBeExecutable(
            ) and is_file and not is_executable:
                valid = False
                message = PathChooser.FILE_IS_NOT_EXECUTABLE_MSG
            elif self._model.pathMustBeADirectory() and not is_directory:
                valid = False
                message = PathChooser.PATH_IS_NOT_A_DIRECTORY_MSG
            elif self._model.pathMustBeAbsolute() and not is_absolute:
                valid = False
                message = PathChooser.PATH_IS_NOT_ABSOLUTE_MSG
            elif self._model.pathMustBeAFile() and not is_file:
                valid = False
                message = PathChooser.PATH_IS_NOT_A_FILE_MSG

        return valid, message

    def validatePath(self):
        """Called whenever the path is modified"""
        palette = self._path_line.palette()

        valid, message = self.isPathValid(self.getPath())

        validity_type = ValidationSupport.WARNING

        if not valid:
            color = ValidationSupport.ERROR_COLOR
        else:
            color = self.valid_color

        self._validation_support.setValidationMessage(message, validity_type)
        self._path_line.setToolTip(message)
        palette.setColor(self._path_line.backgroundRole(), color)

        self._path_line.setPalette(palette)

    def getPath(self):
        """Returns the path"""
        return os.path.expanduser(str(self._path_line.text()).strip())

    def pathExists(self):
        """Returns True if the entered path exists"""
        return os.path.exists(self.getPath())

    def selectPath(self):
        """Pops up the 'select a file/directory' dialog"""
        # todo: This probably needs some reworking to work properly with different scenarios... (file + dir)
        self._editing = True
        current_directory = self.getPath()

        # if not os.path.exists(currentDirectory):
        #    currentDirectory = "~"

        if self._model.pathMustBeAFile():
            current_directory = QFileDialog.getOpenFileName(
                self, "Select a file path", current_directory)
        else:
            current_directory = QFileDialog.getExistingDirectory(
                self, "Select a directory", current_directory)

        if not current_directory == "":
            if not self._model.pathMustBeAbsolute():
                cwd = os.getcwd()
                match = re.match(cwd + "/(.*)", current_directory)
                if match:
                    current_directory = match.group(1)

            self._path_line.setText(current_directory)
            self._model.setPath(self.getPath())

        self._editing = False

    def contentsChanged(self):
        """Called whenever the path is changed."""
        path_is_valid, message = self.isPathValid(self.getPath())

        if not self._editing and path_is_valid:
            self._model.setPath(self.getPath())

    def getPathFromModel(self):
        """Retrieves data from the model and inserts it into the edit line"""
        self._editing = True

        path = self._model.getPath()
        if path is None:
            path = ""

        self._path_line.setText("%s" % path)
        self._editing = False

    def getValidationSupport(self):
        return self._validation_support

    def isValid(self):
        return self._validation_support.isValid()
Ejemplo n.º 2
0
class ValidatedDialog(QDialog):
    """
    A dialog for creating a validated new value. Performs validation of name against a provided.
    Can be used to select from the list or for creating a new value that is not on the list.

    """

    INVALID_COLOR = QColor(255, 235, 235)

    def __init__(
        self,
        title="Title",
        description="Description",
        unique_names=None,
        choose_from_list=False,
    ):
        QDialog.__init__(self)
        self.setModal(True)
        self.setWindowTitle(title)
        # self.setMinimumWidth(250)
        # self.setMinimumHeight(150)

        if unique_names is None:
            unique_names = []

        self.unique_names = unique_names
        self.choose_from_list = choose_from_list

        self.layout = QFormLayout()
        self.layout.setSizeConstraint(QLayout.SetFixedSize)

        label = QLabel(description)
        label.setAlignment(Qt.AlignHCenter)

        self.layout.addRow(self.createSpace(5))
        self.layout.addRow(label)
        self.layout.addRow(self.createSpace(10))

        buttons = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
        self.ok_button = buttons.button(QDialogButtonBox.Ok)
        self.ok_button.setEnabled(False)

        if choose_from_list:
            self.param_name_combo = QComboBox()
            self.param_name.currentIndexChanged.connect(self.validateChoice)
            for item in unique_names:
                self.param_name_combo.addItem(item)
            self.layout.addRow("Job:", self.param_name_combo)
        else:
            self.param_name = QLineEdit(self)
            self.param_name.setFocus()
            self.param_name.textChanged.connect(self.validateName)
            self.validColor = self.param_name.palette().color(
                self.param_name.backgroundRole())

            self.layout.addRow("Name:", self.param_name)

        self.layout.addRow(self.createSpace(10))

        self.layout.addRow(buttons)

        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)

        self.setLayout(self.layout)

    def notValid(self, msg):
        """Called when the name is not valid."""
        self.ok_button.setEnabled(False)
        palette = self.param_name.palette()
        palette.setColor(self.param_name.backgroundRole(), self.INVALID_COLOR)
        self.param_name.setToolTip(msg)
        self.param_name.setPalette(palette)

    def valid(self):
        """Called when the name is valid."""
        self.ok_button.setEnabled(True)
        palette = self.param_name.palette()
        palette.setColor(self.param_name.backgroundRole(), self.validColor)
        self.param_name.setToolTip("")
        self.param_name.setPalette(palette)

    def validateName(self, value):
        """Called to perform validation of a name. For specific needs override this function and call valid() and notValid(msg)."""
        value = str(value)

        if value == "":
            self.notValid("Can not be empty!")
        elif not value.find(" ") == -1:
            self.notValid("No spaces allowed!")
        elif value in self.unique_names:
            self.notValid("Name must be unique!")
        else:
            self.valid()

    def validateChoice(self, choice):
        """Only called when using selection mode."""
        self.ok_button.setEnabled(not choice == "")

    def getName(self):
        """Return the new name chosen by the user"""
        if self.choose_from_list:
            return str(self.param_name_combo.currentText())
        else:
            return str(self.param_name.text())

    def showAndTell(self):
        """Shows the dialog and returns the result"""
        if self.exec_():
            return str(self.getName()).strip()

        return ""

    def createSpace(self, size=5):
        """Creates a widget that can be used as spacing on  a panel."""
        qw = QWidget()
        qw.setMinimumSize(QSize(size, size))

        return qw