mirror of
https://github.com/CopterExpress/clever-show.git
synced 2026-05-28 16:09:33 +00:00
370 lines
10 KiB
Python
370 lines
10 KiB
Python
from copy import deepcopy
|
|
|
|
import pickle
|
|
|
|
import config_editor
|
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
|
from PyQt5.QtCore import Qt as Qt
|
|
from PyQt5.QtGui import QCursor, QStandardItemModel
|
|
from PyQt5.QtWidgets import QAbstractItemView, QTreeView, QMenu
|
|
|
|
|
|
class ConfigModelItem:
|
|
def __init__(self, label, value="", is_section=False, state='default', parent=None):
|
|
self.itemData = [label, value]
|
|
self.is_section = is_section
|
|
self.state = state
|
|
|
|
self.childItems = []
|
|
self.parentItem = parent
|
|
|
|
if self.parentItem is not None:
|
|
self.parentItem.appendChild(self)
|
|
|
|
def appendChild(self, item):
|
|
self.childItems.append(item)
|
|
item.parentItem = self
|
|
|
|
def addChildren(self, items, row):
|
|
if row == -1:
|
|
self.childItems.extend(items)
|
|
else:
|
|
#row -= 1
|
|
print('row', row)
|
|
self.childItems[row:row] = items
|
|
|
|
print(self.childItems)
|
|
|
|
for item in items:
|
|
item.parentItem = self
|
|
|
|
def child(self, row):
|
|
return self.childItems[row]
|
|
|
|
def childCount(self):
|
|
return len(self.childItems)
|
|
|
|
def columnCount(self):
|
|
return 2
|
|
|
|
def data(self, column):
|
|
try:
|
|
return self.itemData[column]
|
|
except IndexError:
|
|
return None
|
|
|
|
def set_data(self, data, column):
|
|
try:
|
|
self.itemData[column] = data
|
|
except IndexError:
|
|
return False
|
|
|
|
return True
|
|
|
|
def parent(self):
|
|
return self.parentItem
|
|
|
|
def row(self):
|
|
if self.parentItem is not None:
|
|
return self.parentItem.childItems.index(self)
|
|
|
|
return 0
|
|
|
|
def removeChild(self, position):
|
|
if position < 0 or position > len(self.childItems):
|
|
return False
|
|
print('removing', position)
|
|
child = self.childItems.pop(position)
|
|
child.parentItem = None
|
|
return True
|
|
|
|
def removeChildren(self, row, count):
|
|
print(range(row, row+count))
|
|
for pos in range(row, row+count):
|
|
self.removeChild(pos)
|
|
|
|
return True
|
|
|
|
def __repr__(self):
|
|
return str(self.itemData)
|
|
|
|
|
|
class ConfigModel(QtCore.QAbstractItemModel):
|
|
def __init__(self, data, parent=None):
|
|
super(ConfigModel, self).__init__(parent)
|
|
|
|
self.rootItem = ConfigModelItem("Option", "Value")
|
|
self.setup(data)
|
|
|
|
def headerData(self, section, orientation, role):
|
|
if role == Qt.DisplayRole and orientation == Qt.Horizontal:
|
|
return self.rootItem.data(section)
|
|
|
|
def columnCount(self, parent):
|
|
return 2
|
|
|
|
def rowCount(self, parent):
|
|
if parent.column() > 0:
|
|
return 0
|
|
|
|
if not parent.isValid():
|
|
parentItem = self.rootItem
|
|
else:
|
|
parentItem = parent.internalPointer()
|
|
|
|
return parentItem.childCount()
|
|
|
|
def index(self, row, column, parent):
|
|
if not self.hasIndex(row, column, parent):
|
|
return QtCore.QModelIndex()
|
|
|
|
parentItem = self.nodeFromIndex(parent)
|
|
childItem = parentItem.child(row)
|
|
|
|
if childItem:
|
|
return self.createIndex(row, column, childItem)
|
|
else:
|
|
return QtCore.QModelIndex()
|
|
|
|
def parent(self, index):
|
|
if not index.isValid():
|
|
return QtCore.QModelIndex()
|
|
|
|
childItem = index.internalPointer()
|
|
parentItem = childItem.parent()
|
|
|
|
if parentItem == self.rootItem or parentItem is None:
|
|
return QtCore.QModelIndex()
|
|
|
|
return self.createIndex(parentItem.row(), 0, parentItem)
|
|
|
|
def nodeFromIndex(self, index):
|
|
if index.isValid():
|
|
return index.internalPointer()
|
|
return self.rootItem
|
|
|
|
def data(self, index, role):
|
|
if not index.isValid():
|
|
return None
|
|
|
|
item = index.internalPointer()
|
|
|
|
if role == Qt.DisplayRole or role == Qt.EditRole:
|
|
return item.data(index.column())
|
|
|
|
return None
|
|
|
|
@QtCore.pyqtSlot()
|
|
def setData(self, index, value, role=Qt.EditRole):
|
|
if not index.isValid():
|
|
return False
|
|
|
|
item = index.internalPointer()
|
|
if role == Qt.EditRole:
|
|
item.set_data(value, index.column())
|
|
|
|
self.dataChanged.emit(index, index, (role,))
|
|
|
|
return True
|
|
|
|
def flags(self, index):
|
|
if not index.isValid():
|
|
return QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled # Qt.NoItemFlags
|
|
item = index.internalPointer()
|
|
|
|
flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
|
|
|
|
if index.column() == 0:
|
|
flags |= int(QtCore.Qt.ItemIsDragEnabled)
|
|
if item.is_section:
|
|
flags |= int(QtCore.Qt.ItemIsDropEnabled)
|
|
|
|
if index.column() == 1 and not item.is_section:
|
|
flags |= Qt.ItemIsEditable
|
|
|
|
return flags
|
|
|
|
def supportedDropActions(self):
|
|
return QtCore.Qt.CopyAction | QtCore.Qt.MoveAction
|
|
|
|
def mimeTypes(self):
|
|
return ['bstream', 'text/xml']
|
|
|
|
def mimeData(self, indexes):
|
|
mimedata = QtCore.QMimeData()
|
|
index = indexes[0]
|
|
mimedata.setData('bstream', pickle.dumps(self.nodeFromIndex(index)))
|
|
return mimedata
|
|
|
|
def dropMimeData(self, mimedata, action, row, column, parentIndex):
|
|
print(action)
|
|
print('mim', row)
|
|
if action == Qt.IgnoreAction:
|
|
return True
|
|
|
|
parentNode = self.nodeFromIndex(parentIndex)
|
|
droppedNode = deepcopy(pickle.loads(mimedata.data('bstream')))
|
|
print(droppedNode.itemData, 'node')
|
|
#droppedNode = pickle.loads(mimedata.data('bstream')) #
|
|
#self.removeRow()#self.index(row, column, parentIndex))#parentNode.child(row))
|
|
self.insertItems(row, [droppedNode], parentIndex)
|
|
self.dataChanged.emit(parentIndex, parentIndex)
|
|
|
|
return True
|
|
|
|
def removeRows1(self, row, count, parent):
|
|
print('rem', row, count)
|
|
self.beginRemoveRows(parent, row, row+count-1)
|
|
parentItem = self.nodeFromIndex(parent)
|
|
print(parentItem, parentItem.itemData)
|
|
|
|
#parentItem.removeChild(row)
|
|
parentItem.removeChildren(row, count)
|
|
print(parentItem.childItems)
|
|
|
|
self.endRemoveRows()
|
|
print('removed')
|
|
return True
|
|
|
|
@QtCore.pyqtSlot()
|
|
def removeRow(self, index):
|
|
parent = index.parent()
|
|
self.beginRemoveRows(parent, index.row(), index.row())
|
|
|
|
parentItem = self.nodeFromIndex(parent)
|
|
parentItem.removeChild(index.row())
|
|
|
|
self.endRemoveRows()
|
|
return True
|
|
|
|
def insertItems(self, row, items, parentIndex):
|
|
print('ins', row)
|
|
parent = self.nodeFromIndex(parentIndex)
|
|
self.beginInsertRows(parentIndex, row, row+len(items)-1)
|
|
|
|
parent.addChildren(items, row)
|
|
print(parent.childItems)
|
|
|
|
self.endInsertRows()
|
|
self.dataChanged.emit(parentIndex, parentIndex)
|
|
return True
|
|
|
|
def setup(self, data: dict, parent=None):
|
|
if parent is None:
|
|
parent = self.rootItem
|
|
|
|
for key, value in data.items():
|
|
if isinstance(value, dict):
|
|
item = ConfigModelItem(key, parent=parent, is_section=True)
|
|
self.setup(value, parent=item)
|
|
else:
|
|
parent.appendChild(ConfigModelItem(key, value))
|
|
|
|
def to_dict(self, parent=None) -> dict:
|
|
if parent is None:
|
|
parent = self.rootItem
|
|
|
|
data = {}
|
|
for item in parent.childItems:
|
|
item_name, item_data = item.itemData
|
|
if item.childItems:
|
|
data[item_name] = self.to_dict(item)
|
|
else:
|
|
data[item_name] = item_data
|
|
|
|
return data
|
|
|
|
@property
|
|
def dict(self):
|
|
return self.to_dict()
|
|
|
|
|
|
class ConfigDialog(config_editor.Ui_config_dialog):
|
|
def __init__(self, data):
|
|
super(ConfigDialog, self).__init__()
|
|
self.model = ConfigModel(data)
|
|
|
|
def setupUi(self, config_dialog):
|
|
super(ConfigDialog, self).setupUi(config_dialog)
|
|
|
|
#self.config_view = Tree()
|
|
|
|
self.config_view = Tree()
|
|
self.config_view.setObjectName("config_view")
|
|
self.config_view.setModel(self.model)
|
|
self.gridLayout.addWidget(self.config_view, 0, 0, 1, 1)
|
|
|
|
self.config_view.expandAll()
|
|
#self.config_view.setDragDropMode(True)
|
|
#self.setDragDropMode(QAbstractItemView.InternalMove)
|
|
#self.setDragEnabled(True)
|
|
#self.setAcceptDrops(True)
|
|
#self.setDropIndicatorShown(True)
|
|
|
|
self.delete_button.pressed.connect(self.remove_selected)
|
|
|
|
def remove_selected(self):
|
|
index = self.config_view.selectedIndexes()[0]
|
|
self.model.removeRow(index)\
|
|
|
|
|
|
class Tree(QTreeView):
|
|
def __init__(self):
|
|
QTreeView.__init__(self)
|
|
data = {"section 1": {"opt1": "str", "opt2": 123, "opt3": 1.23, "opt4": False, "...": {'subopt': 'bal'}},
|
|
"section 2": {"opt1": "str", "opt2": [1.1, 2.3, 34], "opt3": 1.23, "opt4": False, "...": ""}}
|
|
#model = ConfigModel(data)
|
|
|
|
#self.setModel(model)
|
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
self.customContextMenuRequested.connect(self.open_menu)
|
|
|
|
self.setSelectionMode(self.SingleSelection)
|
|
self.setDragDropMode(QAbstractItemView.InternalMove)
|
|
self.setDragEnabled(True)
|
|
self.setAcceptDrops(True)
|
|
self.setDropIndicatorShown(True)
|
|
|
|
# def dropEvent(self, e):
|
|
# print(e.dropAction()==QtCore.Qt.MoveAction)
|
|
# if e.keyboardModifiers() & QtCore.Qt.AltModifier:
|
|
# e.setDropAction()
|
|
# print('copy')
|
|
# else:
|
|
# e.setDropAction(QtCore.Qt.MoveAction)
|
|
# print("drop")
|
|
# print(e)
|
|
# e.accept()
|
|
|
|
def open_menu(self):
|
|
menu = QMenu()
|
|
menu.addAction("Create new folder")
|
|
menu.exec_(QCursor.pos())
|
|
|
|
if __name__ == '__main__':
|
|
import sys
|
|
|
|
|
|
def except_hook(cls, exception, traceback):
|
|
sys.__excepthook__(cls, exception, traceback)
|
|
|
|
sys.excepthook = except_hook
|
|
|
|
app = QtWidgets.QApplication(sys.argv)
|
|
Dialog = QtWidgets.QDialog()
|
|
|
|
data = {"section 1": {"opt1": "str", "opt2": 123, "opt3": 1.23, "opt4": False, }}#"...": {'subopt': 'bal'}},}
|
|
#"section 2": {"opt1": "str", "opt2": [1.1, 2.3, 34], "opt3": 1.23, "opt4": False, "...": ""}}
|
|
|
|
ui = ConfigDialog(data)
|
|
ui.setupUi(Dialog)
|
|
|
|
print(Qt.DisplayRole)
|
|
Dialog.show()
|
|
print(app.exec_())
|
|
|
|
print(Dialog.result())
|
|
print(ui.model.to_dict())
|
|
|
|
sys.exit()
|