Source code for spinetoolbox.spine_db_editor.widgets.custom_qwidgets
######################################################################################################################
# Copyright (C) 2017-2020 Spine project consortium
# This file is part of Spine Toolbox.
# Spine Toolbox is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
# Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option)
# any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
# Public License for more details. You should have received a copy of the GNU Lesser General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
######################################################################################################################
"""
Custom QWidgets.
:author: M. Marin (KTH)
:date: 13.5.2018
"""
import os
from PySide2.QtWidgets import (
QMenu,
QToolButton,
QLabel,
QGraphicsOpacityEffect,
QDialog,
QVBoxLayout,
QDialogButtonBox,
QListWidget,
)
from PySide2.QtCore import Slot, QVariantAnimation, QPointF, Qt
from sqlalchemy.engine.url import URL
from ...helpers import open_url
from ...mvcmodels.filter_checkbox_list_model import LazyFilterCheckboxListModel, DataToValueFilterCheckboxListModel
from ...widgets.custom_qwidgets import FilterWidgetBase
[docs]class DataToValueFilterWidget(FilterWidgetBase):
def __init__(self, parent, data_to_value, show_empty=True):
"""Init class.
Args:
parent (QWidget)
data_to_value (method): a method to translate item data to a value for display role
"""
super().__init__(parent)
self._filter_model = DataToValueFilterCheckboxListModel(self, data_to_value, show_empty=show_empty)
self._filter_model.set_list(self._filter_state)
self._ui_list.setModel(self._filter_model)
self.connect_signals()
[docs]class LazyFilterWidget(FilterWidgetBase):
def __init__(self, parent, source_model, show_empty=True):
"""Init class.
Args:
parent (SpineDBEditor)
source_model (CompoundParameterModel, optional): a model to lazily get data from
"""
super().__init__(parent)
self._filter_model = LazyFilterCheckboxListModel(self, source_model, show_empty=show_empty)
self._filter_model.set_list(self._filter_state)
self.connect_signals()
[docs]class OpenFileButton(QToolButton):
"""A button to open files or show them in the folder."""
def __init__(self, file_path, ds_form):
super().__init__()
self.ds_form = ds_form
self.file_path = file_path
self.dir_name, self.file_name = os.path.split(file_path)
self.setText(self.file_name)
self.setPopupMode(QToolButton.MenuButtonPopup)
self.setStyleSheet(
"""
QToolButton {
padding-left: 12px; padding-right: 32px; padding-top: 6px; padding-bottom: 6px;
background-color: #ffffff;
border: 1px solid #cccccc;
border-style: outset;
border-radius: 6px;
}
QToolButton:hover {
background-color: #eeeeee;
}
QToolButton:pressed {
background-color: #dddddd;
}
QToolButton::menu-button {
border: 1px solid #cccccc;
border-style: outset;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
width: 20px;
}
"""
)
menu = QMenu(ds_form)
self.setMenu(menu)
open_file_action = menu.addAction("Open")
open_containing_folder_action = menu.addAction("Open containing folder")
open_file_action.triggered.connect(self.open_file)
open_containing_folder_action.triggered.connect(self.open_containing_folder)
self.clicked.connect(open_file_action.triggered)
@Slot(bool)
@Slot(bool)
[docs]class OpenSQLiteFileButton(OpenFileButton):
"""A button to open sqlite files, show them in the folder, or add them to the project."""
def __init__(self, file_path, ds_form):
super().__init__(file_path, ds_form)
self.url = URL("sqlite", database=self.file_path)
self.menu().addSeparator()
add_to_project_action = self.menu().addAction("Add to project")
add_to_project_action.triggered.connect(self.add_to_project)
@Slot(bool)
[docs] def open_file(self, checked=False):
codename = os.path.splitext(self.file_name)[0]
self.ds_form._open_sqlite_url(self.url, codename)
@Slot(bool)
[docs]class ShootingLabel(QLabel):
def __init__(self, origin, destination, parent=None, duration=1200):
super().__init__("foo", parent=parent)
self.origin = QPointF(origin)
self.direction = QPointF(destination - origin)
self.effect = QGraphicsOpacityEffect()
self.setGraphicsEffect(self.effect)
self.anim = QVariantAnimation()
self.anim.setDuration(duration)
self.anim.setStartValue(0.0)
self.anim.setEndValue(1.0)
self.anim.valueChanged.connect(self._handle_value_changed)
self.anim.finished.connect(self.close)
self.move(origin)
self.setAttribute(Qt.WA_TransparentForMouseEvents)
[docs] def _handle_value_changed(self, value):
opacity = 1.0 - abs(2 * value - 1.0)
self.effect.setOpacity(opacity)
pos = self.origin + value * self.direction
self.move(pos.toPoint())
[docs]class CustomInputDialog(QDialog):
def __init__(self, parent, title):
super().__init__(parent)
self.setWindowTitle(title)
self._new_item = None
self._editable_text = ""
self._accepted_item = None
self._list_wg = QListWidget()
self._list_wg.itemDoubleClicked.connect(self._handle_item_double_clicked)
self._list_wg.itemChanged.connect(self._handle_item_changed)
[docs] def accept(self, item=None):
if item is None:
item = self._list_wg.currentItem()
if item is self._new_item and item.text() == self._editable_text:
self._list_wg.editItem(item)
return
self._accepted_item = item
self.done(QDialog.Accepted)
@Slot("QListWidgetItem")
@Slot("QListWidgetItem")
[docs] def _handle_item_changed(self, item):
if item is self._new_item and item.text() != self._editable_text:
item.setForeground(qApp.palette().text()) # pylint: disable=undefined-variable
self._new_item = None
@classmethod
[docs] def get_item(cls, parent, title, label, items, icons=None, editable_text=""):
if icons is None:
icons = {}
dialog = cls(parent, title)
layout = QVBoxLayout(dialog)
label = QLabel(label)
label.setWordWrap(True)
if editable_text:
dialog._editable_text = editable_text
items.append(dialog._editable_text)
dialog._list_wg.addItems(items)
for item in dialog._list_wg.findItems("*", Qt.MatchWildcard):
icon = icons.get(item.text())
if icon is not None:
item.setData(Qt.DecorationRole, icon)
if editable_text:
dialog._new_item = dialog._list_wg.item(dialog._list_wg.count() - 1)
dialog._new_item.setFlags(dialog._new_item.flags() | Qt.ItemIsEditable)
foreground = qApp.palette().text() # pylint: disable=undefined-variable
color = foreground.color()
color.setAlpha(128)
foreground.setColor(color)
dialog._new_item.setForeground(foreground)
button_box = QDialogButtonBox()
button_box.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok)
layout.addWidget(label)
layout.addWidget(dialog._list_wg)
layout.addWidget(button_box)
button_box.accepted.connect(dialog.accept)
button_box.rejected.connect(dialog.close)
if dialog.exec_() == QDialog.Rejected:
return None
return dialog._accepted_item.text()