started reorderable content list
This commit is contained in:
+219
-4
@@ -1,16 +1,231 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
from PyQt5.QtGui import QFont, QSyntaxHighlighter
|
from PyQt5.QtGui import QFont, QSyntaxHighlighter
|
||||||
from PyQt5.QtWidgets import QWidget, QLabel, QTabWidget, QHBoxLayout, QSplitter, QPlainTextEdit
|
from PyQt5.QtWidgets import QWidget, QLabel, QHBoxLayout, QVBoxLayout, QSplitter, QListView, QAbstractItemView
|
||||||
|
|
||||||
|
import markdown
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
class Summary(QLabel):
|
class Summary(QWidget):
|
||||||
def __init__(self):
|
def __init__(self, core):
|
||||||
super(Summary, self).__init__()
|
super(Summary, self).__init__()
|
||||||
|
self.core = core
|
||||||
|
|
||||||
|
sum_json = open(os.path.join(self.core.cwd,'.config/summary.json')).read()
|
||||||
|
self.sum = json.loads(sum_json)
|
||||||
|
|
||||||
|
vbox = QVBoxLayout()
|
||||||
|
vbox.setContentsMargins(0,0,0,0)
|
||||||
|
|
||||||
|
self.list = SummaryList(self)
|
||||||
|
vbox.addWidget(self.list)
|
||||||
|
|
||||||
|
# self.actions = SummaryActions(self)
|
||||||
|
# self.addWidget(self.actions)
|
||||||
|
|
||||||
|
self.setLayout(vbox)
|
||||||
|
|
||||||
|
class SumListModel(QtCore.QAbstractListModel):
|
||||||
|
dragDropFinished = QtCore.pyqtSignal()
|
||||||
|
def __init__(self,sum):
|
||||||
|
QtCore.QAbstractItemModel.__init__(self)
|
||||||
|
self.nodes = ['node0', 'node1', 'node2', 'node3', 'node4', 'node5']
|
||||||
|
self.lastDroppedItems = []
|
||||||
|
self.pendingRemoveRowsAfterDrop = False
|
||||||
|
|
||||||
|
def rowForItem(self, text):
|
||||||
|
'''
|
||||||
|
rowForItem method returns the row corresponding to the passed in item
|
||||||
|
or None if no such item exists in the model
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
row = self.nodes.index(text)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
return row
|
||||||
|
|
||||||
|
def index(self, row, column, parent):
|
||||||
|
if row < 0 or row >= len(self.nodes):
|
||||||
|
return QtCore.QModelIndex()
|
||||||
|
return self.createIndex(row, column)
|
||||||
|
|
||||||
|
def parent(self, index):
|
||||||
|
return QtCore.QModelIndex()
|
||||||
|
|
||||||
|
def rowCount(self, index):
|
||||||
|
if index.isValid():
|
||||||
|
return 0
|
||||||
|
return len(self.nodes)
|
||||||
|
|
||||||
|
def data(self, index, role):
|
||||||
|
if not index.isValid():
|
||||||
|
return None
|
||||||
|
if role == QtCore.Qt.DisplayRole:
|
||||||
|
row = index.row()
|
||||||
|
if row < 0 or row >= len(self.nodes):
|
||||||
|
return None
|
||||||
|
return self.nodes[row]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def supportedDropActions(self):
|
||||||
|
return QtCore.Qt.MoveAction
|
||||||
|
|
||||||
|
def flags(self, index):
|
||||||
|
if not index.isValid():
|
||||||
|
return QtCore.Qt.ItemIsEnabled
|
||||||
|
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | \
|
||||||
|
QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled
|
||||||
|
|
||||||
|
def insertRows(self, row, count, index):
|
||||||
|
if index.isValid():
|
||||||
|
return False
|
||||||
|
if count <= 0:
|
||||||
|
return False
|
||||||
|
# inserting 'count' empty rows starting at 'row'
|
||||||
|
self.beginInsertRows(QtCore.QModelIndex(), row, row + count - 1)
|
||||||
|
for i in range(0, count):
|
||||||
|
self.nodes.insert(row + i, '')
|
||||||
|
self.endInsertRows()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def removeRows(self, row, count, index):
|
||||||
|
if index.isValid():
|
||||||
|
return False
|
||||||
|
if count <= 0:
|
||||||
|
return False
|
||||||
|
num_rows = self.rowCount(QtCore.QModelIndex())
|
||||||
|
self.beginRemoveRows(QtCore.QModelIndex(), row, row + count - 1)
|
||||||
|
for i in range(count, 0, -1):
|
||||||
|
self.nodes.pop(row - i + 1)
|
||||||
|
self.endRemoveRows()
|
||||||
|
|
||||||
|
if self.pendingRemoveRowsAfterDrop:
|
||||||
|
'''
|
||||||
|
If we got here, it means this call to removeRows is the automatic
|
||||||
|
'cleanup' action after drag-n-drop performed by Qt
|
||||||
|
'''
|
||||||
|
self.pendingRemoveRowsAfterDrop = False
|
||||||
|
self.dragDropFinished.emit()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def setData(self, index, value, role):
|
||||||
|
if not index.isValid():
|
||||||
|
return False
|
||||||
|
if index.row() < 0 or index.row() > len(self.nodes):
|
||||||
|
return False
|
||||||
|
self.nodes[index.row()] = str(value)
|
||||||
|
self.dataChanged.emit(index, index)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def mimeTypes(self):
|
||||||
|
return ['application/vnd.treeviewdragdrop.list']
|
||||||
|
|
||||||
|
def mimeData(self, indexes):
|
||||||
|
mimedata = QtCore.QMimeData()
|
||||||
|
encoded_data = QtCore.QByteArray()
|
||||||
|
stream = QtCore.QDataStream(encoded_data, QtCore.QIODevice.WriteOnly)
|
||||||
|
for index in indexes:
|
||||||
|
if index.isValid():
|
||||||
|
text = self.data(index, 0)
|
||||||
|
stream << QtCore.QByteArray(text.encode('utf-8'))
|
||||||
|
mimedata.setData('application/vnd.treeviewdragdrop.list', encoded_data)
|
||||||
|
return mimedata
|
||||||
|
|
||||||
|
def dropMimeData(self, data, action, row, column, parent):
|
||||||
|
if action == QtCore.Qt.IgnoreAction:
|
||||||
|
return True
|
||||||
|
if not data.hasFormat('application/vnd.treeviewdragdrop.list'):
|
||||||
|
return False
|
||||||
|
if column > 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
num_rows = self.rowCount(QtCore.QModelIndex())
|
||||||
|
if num_rows <= 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if row < 0:
|
||||||
|
if parent.isValid():
|
||||||
|
row = parent.row()
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
encoded_data = data.data('application/vnd.treeviewdragdrop.list')
|
||||||
|
stream = QtCore.QDataStream(encoded_data, QtCore.QIODevice.ReadOnly)
|
||||||
|
|
||||||
|
new_items = []
|
||||||
|
rows = 0
|
||||||
|
while not stream.atEnd():
|
||||||
|
text = QtCore.QByteArray()
|
||||||
|
stream >> text
|
||||||
|
text = bytes(text).decode('utf-8')
|
||||||
|
index = self.nodes.index(text)
|
||||||
|
new_items.append((text, index))
|
||||||
|
rows += 1
|
||||||
|
|
||||||
|
self.lastDroppedItems = []
|
||||||
|
for (text, index) in new_items:
|
||||||
|
target_row = row
|
||||||
|
if index < row:
|
||||||
|
target_row += 1
|
||||||
|
self.beginInsertRows(QtCore.QModelIndex(), target_row, target_row)
|
||||||
|
self.nodes.insert(target_row, text)
|
||||||
|
self.endInsertRows()
|
||||||
|
self.lastDroppedItems.append(text)
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
self.pendingRemoveRowsAfterDrop = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
class SumListSelectionModel(QtCore.QItemSelectionModel):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QtCore.QItemSelectionModel.__init__(self, parent)
|
||||||
|
|
||||||
|
def onModelItemsReordered(self):
|
||||||
|
new_selection = QtCore.QItemSelection()
|
||||||
|
new_index = QtCore.QModelIndex()
|
||||||
|
for item in self.model().lastDroppedItems:
|
||||||
|
row = self.model().rowForItem(item)
|
||||||
|
if row is None:
|
||||||
|
continue
|
||||||
|
new_index = self.model().index(row, 0, QtCore.QModelIndex())
|
||||||
|
new_selection.select(new_index, new_index)
|
||||||
|
|
||||||
|
self.clearSelection()
|
||||||
|
flags = QtCore.QItemSelectionModel.ClearAndSelect | \
|
||||||
|
QtCore.QItemSelectionModel.Rows | \
|
||||||
|
QtCore.QItemSelectionModel.Current
|
||||||
|
self.select(new_selection, flags)
|
||||||
|
self.setCurrentIndex(new_index, flags)
|
||||||
|
|
||||||
|
class SummaryList(QListView):
|
||||||
|
def __init__(self,prt):
|
||||||
|
super(SummaryList, self).__init__()
|
||||||
|
print("list")
|
||||||
|
self.prt = prt
|
||||||
|
model = SumListModel(sum)
|
||||||
|
selectionModel = SumListSelectionModel(model)
|
||||||
|
model.dragDropFinished.connect(selectionModel.onModelItemsReordered)
|
||||||
|
self.setModel(model)
|
||||||
|
self.setSelectionModel(selectionModel)
|
||||||
|
self.setDragDropMode(QAbstractItemView.InternalMove)
|
||||||
|
self.setDragDropOverwriteMode(False)
|
||||||
|
|
||||||
|
|
||||||
|
class SummaryActions(QWidget):
|
||||||
|
def __init__(self,sum):
|
||||||
|
print("actions")
|
||||||
|
# for p in self.sum:
|
||||||
|
|
||||||
|
# class SummaryActions():
|
||||||
|
# def
|
||||||
|
|
||||||
class ContentStack(QWidget):
|
class ContentStack(QWidget):
|
||||||
def __init__(self, core):
|
def __init__(self, core):
|
||||||
@@ -23,7 +238,7 @@ class ContentStack(QWidget):
|
|||||||
|
|
||||||
hsplitter = QSplitter(QtCore.Qt.Horizontal)
|
hsplitter = QSplitter(QtCore.Qt.Horizontal)
|
||||||
|
|
||||||
self.summary = QLabel("Summary (markdown files list).")
|
self.summary = Summary(core)
|
||||||
hsplitter.addWidget(self.summary)
|
hsplitter.addWidget(self.summary)
|
||||||
|
|
||||||
self.mdsource = QLabel("Content (markdown src).")
|
self.mdsource = QLabel("Content (markdown src).")
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys, os, re
|
||||||
|
|
||||||
|
from PyQt5 import QtCore
|
||||||
|
from PyQt5.QtCore import QUrl
|
||||||
|
from PyQt5.QtGui import QKeySequence, QFont, QSyntaxHighlighter
|
||||||
|
from PyQt5.QtWidgets import QWidget, QLabel, QTabWidget, QVBoxLayout, QHBoxLayout, QSplitter, QPlainTextEdit, QShortcut
|
||||||
|
from PyQt5.QtWebKit import QWebSettings
|
||||||
|
from PyQt5.QtWebKitWidgets import QWebView, QWebInspector
|
||||||
|
|
||||||
|
|
||||||
|
class WebkitView(QWebView):
|
||||||
|
def __init__(self, port):
|
||||||
|
self.port = port
|
||||||
|
self.view = QWebView.__init__(self)
|
||||||
|
self.load(QUrl('http://localhost:'+str(self.port)))
|
||||||
|
self.settings().setAttribute(QWebSettings.DeveloperExtrasEnabled, True)
|
||||||
|
# self.settings().setAttribute(QWebSettings.PluginsEnabled, True)
|
||||||
|
|
||||||
|
|
||||||
|
class WebkitInspector(QWebInspector):
|
||||||
|
def __init__(self, webkitview):
|
||||||
|
super(WebkitInspector, self).__init__()
|
||||||
|
self.webkitview = webkitview
|
||||||
|
self.setPage(self.webkitview.page())
|
||||||
|
# TODO: webkitinspector is disappearing when chaging tabs
|
||||||
|
|
||||||
|
class CodeEditor(QPlainTextEdit):
|
||||||
|
def __init__(self, core, tabs, file=None):
|
||||||
|
super(CodeEditor, self).__init__()
|
||||||
|
self.core = core
|
||||||
|
self.tabs = tabs
|
||||||
|
# self.file = file
|
||||||
|
self.file = os.path.join(self.core.cwd,file)
|
||||||
|
|
||||||
|
self.insertPlainText(open(self.file, 'r').read())
|
||||||
|
self.changed = False
|
||||||
|
self.textChanged.connect(self.onTextChanged)
|
||||||
|
|
||||||
|
self.shortcut = QShortcut(QKeySequence("Ctrl+s"), self)
|
||||||
|
self.shortcut.activated.connect(self.save)
|
||||||
|
|
||||||
|
def onTextChanged(self):
|
||||||
|
# print('textChanged')
|
||||||
|
# print(self.toPlainText())
|
||||||
|
# open(self.file, 'w').write(self.toPlainText())
|
||||||
|
if not self.changed:
|
||||||
|
self.changed = True
|
||||||
|
i = self.tabs.currentIndex()
|
||||||
|
self.tabs.setTabText(i, "* "+self.tabs.tabText(i))
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
if self.changed:
|
||||||
|
open(self.file, 'w').write(self.toPlainText())
|
||||||
|
i = self.tabs.currentIndex()
|
||||||
|
self.tabs.setTabText(i, re.sub(r'^\*\s', '', self.tabs.tabText(i)))
|
||||||
|
self.changed = False
|
||||||
|
# TODO: how to combine file save and project save
|
||||||
|
|
||||||
|
|
||||||
|
class Editor(QWidget):
|
||||||
|
def __init__(self, core):
|
||||||
|
super(Editor, self).__init__()
|
||||||
|
self.core = core
|
||||||
|
|
||||||
|
self.layout = QVBoxLayout(self)
|
||||||
|
self.layout.setContentsMargins(0,0,0,0)
|
||||||
|
|
||||||
|
# Initialize tab screen
|
||||||
|
self.tabs = QTabWidget()
|
||||||
|
|
||||||
|
self.scsstab = CodeEditor(core, self.tabs, 'assets/scss/styles.scss')
|
||||||
|
self.jstab = CodeEditor(core, self.tabs, 'assets/js/script.js')
|
||||||
|
|
||||||
|
# Add tabs
|
||||||
|
self.tabs.addTab(self.scsstab,"scss")
|
||||||
|
self.tabs.addTab(self.jstab,"js")
|
||||||
|
|
||||||
|
# Add tabs to widget
|
||||||
|
self.layout.addWidget(self.tabs)
|
||||||
|
self.setLayout(self.layout)
|
||||||
|
|
||||||
|
# font = QFont()
|
||||||
|
# font.setFamily('Courier')
|
||||||
|
# font.setFixedPitch(True)
|
||||||
|
# font.setPointSize(10)
|
||||||
|
# self.setFont(font)
|
||||||
|
# self.highlighter = Highlighter(self.document())
|
||||||
|
# https://pypi.python.org/pypi/QScintilla/2.9.2
|
||||||
|
|
||||||
|
|
||||||
|
class DesignStack(QWidget):
|
||||||
|
def __init__(self, core):
|
||||||
|
super(DesignStack, self).__init__()
|
||||||
|
|
||||||
|
# self.grid = QGridLayout()
|
||||||
|
hbox = QHBoxLayout()
|
||||||
|
hbox.setContentsMargins(0,0,0,0)
|
||||||
|
self.setLayout(hbox)
|
||||||
|
|
||||||
|
|
||||||
|
# webviewbox = QVBoxLayout()
|
||||||
|
vsplitter = QSplitter(QtCore.Qt.Vertical)
|
||||||
|
|
||||||
|
self.webkitview = WebkitView(core.server.port)
|
||||||
|
vsplitter.addWidget(self.webkitview)
|
||||||
|
|
||||||
|
self.webkitinspector = WebkitInspector(self.webkitview)
|
||||||
|
vsplitter.addWidget(self.webkitinspector)
|
||||||
|
|
||||||
|
hsplitter = QSplitter(QtCore.Qt.Horizontal)
|
||||||
|
hsplitter.addWidget(vsplitter)
|
||||||
|
|
||||||
|
self.editor = Editor(core)
|
||||||
|
hsplitter.addWidget(self.editor)
|
||||||
|
|
||||||
|
hbox.addWidget(hsplitter)
|
||||||
|
|
||||||
|
|
||||||
|
# def onChanged(self, text):
|
||||||
|
# print("ViewTba Layout Changed")
|
||||||
|
# self.lbl.setText(text)
|
||||||
|
# self.lbl.adjustSize()
|
||||||
Reference in New Issue
Block a user