#!/usr/bin/python3
# -*- coding: latin-1 -*-
"""
Copyright (C) 2014 Technische Universität Berlin,
Fakultät IV - Elektrotechnik und Informatik,
Fachgebiet Regelungssysteme,
Einsteinufer 17, D-10587 Berlin, Germany
This file is part of PaPI.
PaPI is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PaPI 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with PaPI. If not, see <http://www.gnu.org/licenses/>.
Contributors
Sven Knuth
"""
from PyQt5.QtWidgets import QMdiSubWindow, QAction, QToolBar, QTreeView, QMenu
from PyQt5.QtCore import Qt, QRegExp, pyqtSignal, QSortFilterProxyModel, QModelIndex
from PyQt5.QtGui import QStandardItemModel, QStandardItem, QPixmap, QBrush, QColor, QIcon, QDragEnterEvent, QDropEvent
from papi.data.DPlugin import *
from papi.data.DSignal import DSignal
from papi.gui.default import get16Pixmap, get16Icon
from papi.yapsy.PluginInfo import PluginInfo
# ------------------------------------
# Item Object
# ------------------------------------
[docs]class PaPITreeItem(QStandardItem):
"""
Default Tree item as it is used in PaPI.
"""
def __init__(self, object, name):
super(PaPITreeItem, self).__init__(name)
self.object = object
self.name = name
self.tool_tip = "Plugin: " + self.name
self.visible = True
[docs] def data(self, role=None, **kwargs):
"""
For Qt.Role see 'http://qt-project.org/doc/qt-4.8/qt.html#ItemDataRole-enum'
:param role:
:return:
"""
if role == Qt.ToolTipRole:
return self.tool_tip
if role == Qt.DisplayRole:
return self.name
if role == Qt.DecorationRole:
return self.get_decoration()
if role == Qt.UserRole:
return self.object
return None
[docs] def get_decoration(self):
return None
[docs] def clean(self):
"""
This function is called to remove all rows which contain an item marked as 'deleted'.
This only works with items having an state-attribute e.g. DPlugin.
:return: True if items were removed and has no more children
"""
contains_deleted_items = False
for row in range(self.rowCount()):
treeItem = self.child(row)
if treeItem is not None:
item = treeItem.data(Qt.UserRole)
canBeRemoved = treeItem.clean()
if hasattr(item, 'state'):
if item.state == 'deleted':
contains_deleted_items = True
self.removeRow(row)
if hasattr(item, 'deleted'):
if item.deleted:
contains_deleted_items = True
self.removeRow(row)
if canBeRemoved:
contains_deleted_items = True
self.removeRow(row)
return not self.hasChildren() and contains_deleted_items
[docs]class PaPIRootItem(PaPITreeItem):
def __init__(self, name):
super(PaPIRootItem, self).__init__(name, name)
self.setEditable(False)
self.setSelectable(False)
self.name = name
[docs] def data(self, role=None, **kwargs):
"""
For Qt.Role see 'http://qt-project.org/doc/qt-4.8/qt.html#ItemDataRole-enum'
:param role:
:return:
"""
# if role == Qt.ToolTipRole:
# return self.tool_tip
if role == Qt.DisplayRole:
return self.name + " (" + str(self.rowCount()) + ")"
# if role == Qt.BackgroundRole:
# return QBrush(QColor(0,0,0,0))
#
# if role in [ Qt.ForegroundRole, Qt.TextColorRole]:
# return QBrush(QColor(0,0,0,100))
# if role == Qt.DecorationRole:
# return self.get_decoration()
# if role == Qt.UserRole:
# return self.object
return None
# return super(PaPIRootItem, self).data(role)
[docs] def hasItem(self, searched_item):
"""
Used to check if an item is already part of this Tree
:param searched_item: Searched for this item
:return:
"""
for row in range(self.rowCount()):
tree_item = self.child(row)
if tree_item is not None:
item = tree_item.data(Qt.UserRole)
if item == searched_item:
return True
[docs] def mark_visibility_by_name(self, value):
all_items_are_hidden = True
for row in range(self.rowCount()):
tree_item = self.child(row)
regex = QRegExp(value, Qt.CaseInsensitive, QRegExp.Wildcard)
if regex.exactMatch(tree_item.name):
tree_item.visible = True
all_items_are_hidden = False
else:
tree_item.visible = False
self.visible = not all_items_are_hidden
# ------------------------------------
# Model Objects
# ------------------------------------
[docs]class PaPITreeModel(QStandardItemModel):
def __init__(self, parent=None):
super(PaPITreeModel, self).__init__(parent)
[docs] def flags(self, index):
row = index.row()
col = index.column()
parent = index.parent()
if not parent.isValid():
return Qt.NoItemFlags
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled
[docs] def remove_item(self, remove_item):
for r in range(self.rowCount()):
for c in range(self.columnCount()):
item = self.item(r, c)
if item == remove_item:
self.removeRow(r)
return
[docs] def mark_visibility_by_name(self, name):
for r in range(self.rowCount()):
item = self.item(r, 0)
item.mark_visibility_by_name(name)
# ------------------------------------
# Item Custom
# ------------------------------------
[docs]class PluginTreeItem(PaPITreeItem):
def __init__(self, plugin_info):
super(PluginTreeItem, self).__init__(plugin_info, plugin_info.name)
self.plugin_info = plugin_info
self.setEditable(False)
[docs] def get_decoration(self):
l = len(self.object.name)
path = self.object.path[:-l]
path += 'box.png'
px = QPixmap(path)
return px
[docs] def data(self, role=None, **kwargs):
if role == Qt.BackgroundRole:
if not self.object.loadable:
return QBrush(QColor(255, 0, 0, 50))
return super(PluginTreeItem, self).data(role)
[docs]class DPluginTreeItem(PaPITreeItem):
def __init__(self, dplugin: DPlugin):
super(DPluginTreeItem, self).__init__(dplugin, dplugin.uname)
self.dplugin = dplugin
self.name = dplugin.uname
self.setEditable(False)
[docs] def get_decoration(self):
l = len(self.dplugin.plugin_identifier)
path = self.dplugin.path[:-l]
path += 'box.png'
px = QPixmap(path)
return px
[docs]class DParameterTreeItem(PaPITreeItem):
def __init__(self, dparameter: DParameter):
super(DParameterTreeItem, self).__init__(dparameter, str(dparameter.value))
self.dparameter = dparameter
self.setEditable(False)
self.tool_tip = dparameter.name
[docs] def get_decoration(self):
return None
[docs]class DBlockTreeItem(PaPITreeItem):
def __init__(self, dblock: DBlock):
super(DBlockTreeItem, self).__init__(dblock, dblock.name)
self.dblock = dblock
self.setSelectable(False)
self.setEditable(False)
self.tool_tip = "DBlock: " + dblock.name
if isinstance(dblock, DEvent):
brush = QBrush()
brush.setColor(Qt.magenta)
self.tool_tip = "DEvent: " + dblock.name
self.setForeground(brush)
self.setSelectable(True)
[docs] def get_decoration(self):
if isinstance(self.object, DEvent):
return get16Pixmap("events.png")
if isinstance(self.object, DBlock):
return get16Pixmap("transmit.png")
return None
[docs]class DSignalTreeItem(PaPITreeItem):
def __init__(self, dsignal: DSignal, check_box):
super(DSignalTreeItem, self).__init__(dsignal, dsignal.dname)
self.dsignal = dsignal
self.setSelectable(False)
self.setEditable(False)
self.tool_tip = "InternalName: " + dsignal.uname
self.check_box = check_box
[docs] def get_decoration(self):
return None
[docs] def data(self, role=None, **kwargs):
"""
For Qt.Role see 'http://qt-project.org/doc/qt-4.8/qt.html#ItemDataRole-enum'
:param role:
:return:
"""
if role == Qt.ToolTipRole:
return self.tool_tip
if role == Qt.DisplayRole:
if self.check_box.isChecked():
return self.dsignal.uname
else:
return self.dsignal.dname
if role == Qt.DecorationRole:
return self.get_decoration()
if role == Qt.UserRole:
return self.object
if role == Qt.EditRole:
return self.dsignal.dname
return None
# ------------------------------------
# Model Custom
# ------------------------------------
[docs]class PluginTreeModel(PaPITreeModel):
"""
This model is used to handle Plugin objects in TreeView created by the yapsy plugin manager.
"""
def __init__(self, parent=None):
super(PluginTreeModel, self).__init__(parent)
[docs]class DPluginTreeModel(PaPITreeModel):
"""
This model is used to handle DPlugin objects in TreeView.
"""
def __init__(self, parent=None):
super(DPluginTreeModel, self).__init__(parent)
[docs]class DParameterTreeModel(PaPITreeModel):
"""
This model is used to handle DParameter objects in TreeView.
"""
def __init__(self, parent=None):
super(DParameterTreeModel, self).__init__(parent)
[docs] def flags(self, index):
"""
This function returns the flags for a specific index.
For Qt.ItemFlags see 'http://qt-project.org/doc/qt-4.8/qt.html#ItemFlag-enum'
:param index:
:return:
"""
row = index.row()
col = index.column()
parent = index.parent()
# if not parent.isValid():
# return ~Qt.ItemIsSelectable & ~Qt.ItemIsEditable
if col == 0:
return Qt.ItemIsSelectable | Qt.ItemIsEnabled
if col == 1:
return Qt.ItemIsEditable | Qt.ItemIsEnabled
[docs] def data(self, index, role=None):
"""
For Qt.Role see 'http://qt-project.org/doc/qt-4.8/qt.html#ItemDataRole-enum'
:param index:
:param role:
:return:
"""
if not index.isValid():
return None
row = index.row()
col = index.column()
if role == Qt.ToolTipRole:
return super(DParameterTreeModel, self).data(index, Qt.ToolTipRole)
if role == Qt.DisplayRole:
if col == 0:
dparameter = super(DParameterTreeModel, self).data(index, Qt.UserRole)
return dparameter.name
if col == 1:
index_sibling = index.sibling(row, col - 1)
dparameter = super(DParameterTreeModel, self).data(index_sibling, Qt.UserRole)
return dparameter.value
if role == Qt.DecorationRole:
if col == 0:
return super(DParameterTreeModel, self).data(index, Qt.DecorationRole)
if col == 1:
return get16Icon('bullet_gear')
if role == Qt.UserRole:
return super(DParameterTreeModel, self).data(index, Qt.UserRole)
if role == Qt.EditRole:
if col == 1:
index_sibling = index.sibling(row, col - 1)
dparameter = super(DParameterTreeModel, self).data(index_sibling, Qt.UserRole)
return str(dparameter.value)
return None
[docs] def setData(self, index, value, role=None):
"""
This function is called when a content in a row is edited by the user.
:param index: Current selected index.
:param value: New value from user
:param role:
:return:
"""
if not index.isValid():
return None
row = index.row()
col = index.column()
if role == Qt.EditRole:
if col == 1:
index_sibling = index.sibling(row, col - 1)
dparameter = super(DParameterTreeModel, self).data(index_sibling, Qt.UserRole)
if dparameter.regex is not None:
rx = QRegExp(dparameter.regex)
if rx.exactMatch(value):
dparameter.value = value
self.dataChanged.emit(index_sibling, index_sibling)
else:
dparameter.value = value
self.dataChanged.emit(index_sibling, index_sibling)
return True
return False
[docs]class DBlockTreeModel(PaPITreeModel):
def __init__(self, check_box, parent=None):
super(DBlockTreeModel, self).__init__(parent)
self.check_box = check_box
[docs] def flags(self, index):
"""
This function returns the flags for a specific index.
For Qt.ItemFlags see 'http://qt-project.org/doc/qt-4.8/qt.html#ItemFlag-enum'
:param index:
:return:
"""
parent = index.parent()
flags = parent.flags()
if not parent.isValid():
flags ^= Qt.ItemIsSelectable
flags ^= Qt.ItemIsEditable
return flags
if parent.isValid():
if self.check_box.isChecked():
flags ^= Qt.ItemIsEditable
return flags
else:
flags |= Qt.ItemIsSelectable
flags |= Qt.ItemIsEnabled
flags |= Qt.ItemIsEditable
return flags
[docs] def setData(self, index, value, role=None):
"""
This function is called when a content in a row is edited by the user.
:param index: Current selected index.
:param value: New value from user
:param role:
:return:
"""
if not index.isValid():
return None
row = index.row()
col = index.column()
if role == Qt.EditRole:
dsignal = super(DBlockTreeModel, self).data(index, Qt.UserRole)
if value != dsignal.dname:
dsignal.dname = value
self.dataChanged.emit(index, index)
return True
return False
[docs]class CustomFieldModel(QStandardItemModel):
def __init__(self, parent=None):
super(CustomFieldModel, self).__init__(parent)
[docs] def data(self, index, role=None):
"""
For Qt.Role see 'http://qt-project.org/doc/qt-4.8/qt.html#ItemDataRole-enum'
:param index:
:param role:
:return:
"""
if not index.isValid():
return None
row = index.row()
col = index.column()
if role == Qt.ToolTipRole:
return super(CustomFieldModel, self).data(index, Qt.ToolTipRole)
if role == Qt.DisplayRole:
if col == 0:
field = super(CustomFieldModel, self).data(index, Qt.UserRole)
return field.desc
if col == 1:
index_sibling = index.sibling(row, col - 1)
field = super(CustomFieldModel, self).data(index_sibling, Qt.UserRole)
return field.size
if col == 2:
return None
if role == Qt.DecorationRole:
pass
if role == Qt.UserRole:
pass
if role == Qt.EditRole:
if col == 0:
field = super(CustomFieldModel, self).data(index, Qt.UserRole)
return field.desc
if col == 1:
index_sibling = index.sibling(row, col - 1)
field = super(CustomFieldModel, self).data(index_sibling, Qt.UserRole)
return field.size
if col == 2:
return None
return None
[docs] def setData(self, index, value, role=None):
"""
This function is called when a content in a row is edited by the user.
:param index: Current selected index.
:param value: New value from user
:param role:
:return:
"""
if not index.isValid():
return None
row = index.row()
col = index.column()
if role == Qt.EditRole:
if col == 0:
field = super(CustomFieldModel, self).data(index, Qt.UserRole)
field.desc = value
self.dataChanged.emit(index, None)
return True
if col == 1:
index_sibling = index.sibling(row, col - 1)
field = super(CustomFieldModel, self).data(index_sibling, Qt.UserRole)
field.size = value
self.dataChanged.emit(index, None)
return False
# ------------------------------------
# Custom Proxy object
# ------------------------------------
[docs]class PaPITreeProxyModel(QSortFilterProxyModel):
[docs] def filterAcceptsRow(self, p_int:int, sourceParent: QModelIndex):
index0 = self.sourceModel().index(p_int, 0, sourceParent)
# if sourceParent.data() is None:
# return True
# text = self.sourceModel().data(index0)
item = self.sourceModel().itemFromIndex(index0)
return item.visible
#
# if self.sourceModel().data(index0) is not None:
# text = self.sourceModel().data(index0)
# reg = self.filterRegExp()
# return reg.exactMatch(text)
# return False
# ------------------------------------
# Custom GUI elements
# ------------------------------------
[docs]class PaPIMDISubWindow(QMdiSubWindow):
def __init__(self):
super(PaPIMDISubWindow, self).__init__()
self.movable = True
[docs] def mouseMoveEvent(self, event):
if self.movable:
super(PaPIMDISubWindow, self).mouseMoveEvent(event)
[docs] def set_movable(self, flag):
self.movable = flag
[docs]class PaPIFavAction(QAction):
def __init__(self, *args, **kwargs):
super(PaPIFavAction, self).__init__(*args, **kwargs)