示例#1
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
示例#2
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()
示例#3
0
class ReactionBox(QGraphicsItem):
    """Handle to the line edits on the map"""
    def __init__(self, parent: MapView, r_id: str, name):
        QGraphicsItem.__init__(self)

        self.map = parent
        self.id = r_id
        self.name = name

        self.item = QLineEdit()
        self.item.setMaximumWidth(80)
        r = self.map.appdata.project.cobra_py_model.reactions.get_by_id(r_id)
        text = "Id: " + r.id + "\nName: " + r.name \
            + "\nEquation: " + r.build_reaction_string()\
            + "\nLowerbound: " + str(r.lower_bound) \
            + "\nUpper bound: " + str(r.upper_bound) \
            + "\nObjective coefficient: " + str(r.objective_coefficient)

        self.item.setToolTip(text)
        self.proxy = self.map.scene.addWidget(self.item)
        self.proxy.show()

        palette = self.item.palette()
        palette.setColor(QPalette.Base, self.map.appdata.default_color)
        role = self.item.foregroundRole()
        palette.setColor(role, Qt.black)
        self.item.setPalette(palette)

        self.setCursor(Qt.OpenHandCursor)
        self.setAcceptedMouseButtons(Qt.LeftButton)
        self.item.textEdited.connect(self.value_changed)
        self.item.returnPressed.connect(self.returnPressed)

        self.item.setContextMenuPolicy(Qt.CustomContextMenu)
        self.item.customContextMenuRequested.connect(self.on_context_menu)

        # create context menu
        self.pop_menu = QMenu(parent)
        maximize_action = QAction('maximize flux for this reaction', parent)
        self.pop_menu.addAction(maximize_action)
        maximize_action.triggered.connect(self.emit_maximize_action)
        minimize_action = QAction('minimize flux for this reaction', parent)
        self.pop_menu.addAction(minimize_action)
        minimize_action.triggered.connect(self.emit_minimize_action)
        switch_action = QAction('switch to reaction mask', parent)
        self.pop_menu.addAction(switch_action)
        switch_action.triggered.connect(self.switch_to_reaction_mask)
        remove_action = QAction('remove from map', parent)
        self.pop_menu.addAction(remove_action)
        remove_action.triggered.connect(self.remove)

        self.pop_menu.addSeparator()

    def returnPressed(self):
        if validate_value(self.item.text()):
            self.map.value_changed(self.id, self.item.text())

        # TODO: actually I want to repaint
        # self.map.update()

    def value_changed(self):
        test = self.item.text().replace(" ", "")
        if test == "":
            self.map.value_changed(self.id, test)
            self.set_color(self.map.appdata.default_color)
        elif validate_value(self.item.text()):
            self.map.value_changed(self.id, self.item.text())
            if self.id in self.map.appdata.project.scen_values.keys():
                self.set_color(self.map.appdata.scen_color)
            else:
                self.set_color(self.map.appdata.comp_color)
        else:
            self.set_color(Qt.magenta)

        # TODO: actually I want to repaint
        # self.map.update()

    def set_val_and_color(self, value: Tuple[float, float]):
        self.set_value(value)
        self.recolor()

    def set_value(self, value: Tuple[float, float]):
        (vl, vu) = value
        if isclose(vl, vu, abs_tol=self.map.appdata.abs_tol):
            self.item.setText(str(round(vl, self.map.appdata.rounding)))
        else:
            self.item.setText(
                str((round(vl, self.map.appdata.rounding),
                     round(vu, self.map.appdata.rounding))))
        self.item.setCursorPosition(0)

    def recolor(self):
        value = self.item.text()
        test = value.replace(" ", "")
        if test == "":
            self.set_color(self.map.appdata.default_color)
        elif validate_value(value):
            if self.id in self.map.appdata.project.scen_values.keys():
                value = self.map.appdata.project.scen_values[self.id]
                self.set_color(self.map.appdata.scen_color)
            else:
                value = self.map.appdata.project.comp_values[self.id]
                (vl, vu) = value
                if math.isclose(vl, vu, abs_tol=self.map.appdata.abs_tol):
                    if self.map.appdata.modes_coloring:
                        if vl == 0:
                            self.set_color(Qt.red)
                        else:
                            self.set_color(Qt.green)
                    else:
                        self.set_color(self.map.appdata.comp_color)
                else:
                    if math.isclose(vl, 0.0, abs_tol=self.map.appdata.abs_tol):
                        self.set_color(self.map.appdata.special_color_1)
                    elif math.isclose(vu,
                                      0.0,
                                      abs_tol=self.map.appdata.abs_tol):
                        self.set_color(self.map.appdata.special_color_1)
                    elif vl <= 0 and vu >= 0:
                        self.set_color(self.map.appdata.special_color_1)
                    else:
                        self.set_color(self.map.appdata.special_color_2)
        else:
            self.set_color(Qt.magenta)

    def set_color(self, color: QColor):
        palette = self.item.palette()
        palette.setColor(QPalette.Base, color)
        role = self.item.foregroundRole()
        palette.setColor(role, Qt.black)
        self.item.setPalette(palette)

    def boundingRect(self):
        return QRectF(-15, -15, 20, 20)

    def paint(self, painter: QPainter, _option, _widget: QWidget):
        # set color depending on wether the value belongs to the scenario
        if self.id in self.map.appdata.project.scen_values.keys():
            painter.setPen(Qt.magenta)
            painter.setBrush(Qt.magenta)
        else:
            painter.setPen(Qt.darkGray)

        painter.drawRect(-15, -15, 20, 20)
        painter.setPen(Qt.darkGray)
        painter.drawLine(-5, 0, -5, -10)
        painter.drawLine(0, -5, -10, -5)

    def mousePressEvent(self, _event: QGraphicsSceneMouseEvent):
        pass

    def mouseReleaseEvent(self, _event: QGraphicsSceneMouseEvent):
        pass

    def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent):
        drag = QDrag(event.widget())
        mime = QMimeData()
        mime.setText(str(self.id))
        drag.setMimeData(mime)
        # self.setCursor(Qt.ClosedHandCursor)
        drag.exec_()
        # self.setCursor(Qt.OpenHandCursor)

    def setPos(self, x, y):
        self.proxy.setPos(x, y)
        super().setPos(x, y)

    def on_context_menu(self, point):
        # show context menu
        self.pop_menu.exec_(self.item.mapToGlobal(point))

    def remove(self):
        self.map.remove_box(self.id)
        self.map.drag = False

    def switch_to_reaction_mask(self):
        self.map.switchToReactionMask.emit(self.id)
        self.map.drag = False

    def emit_maximize_action(self):
        self.map.maximizeReaction.emit(self.id)
        self.map.drag = False

    def emit_minimize_action(self):
        self.map.minimizeReaction.emit(self.id)
        self.map.drag = False