mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-06-01 18:49:38 +12:00
Adds: ability to import from folder structure. #200
This commit is contained in:
parent
ccf33b3ccf
commit
c6391e976c
|
@ -5,7 +5,10 @@ import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from PyQt5.QtCore import QSettings
|
from PyQt5.QtCore import QSettings
|
||||||
from PyQt5.QtWidgets import QWidget
|
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QCheckBox, QHBoxLayout, \
|
||||||
|
QLabel, QSpinBox, QComboBox, QLineEdit
|
||||||
|
from manuskript.ui.collapsibleGroupBox2 import collapsibleGroupBox2
|
||||||
|
from manuskript.ui import style
|
||||||
|
|
||||||
|
|
||||||
class abstractImporter:
|
class abstractImporter:
|
||||||
|
@ -22,8 +25,10 @@ class abstractImporter:
|
||||||
# For folder, use "<<folder>>"
|
# For folder, use "<<folder>>"
|
||||||
icon = ""
|
icon = ""
|
||||||
|
|
||||||
@classmethod
|
def __init__(self):
|
||||||
def startImport(cls, filePath, parentItem, settingsWidget):
|
self.settings = {}
|
||||||
|
|
||||||
|
def startImport(self, filePath, parentItem, settingsWidget):
|
||||||
"""
|
"""
|
||||||
Takes a str path to the file/folder to import, and the settingsWidget
|
Takes a str path to the file/folder to import, and the settingsWidget
|
||||||
returnend by `self.settingsWidget()` containing the user set settings,
|
returnend by `self.settingsWidget()` containing the user set settings,
|
||||||
|
@ -31,15 +36,161 @@ class abstractImporter:
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def settingsWidget(cls):
|
|
||||||
"""
|
|
||||||
Returns a QWidget if needed for settings.
|
|
||||||
"""
|
|
||||||
return None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def isValid(cls):
|
def isValid(cls):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def settingsWidget(self, widget):
|
||||||
|
"""
|
||||||
|
Takes a QWidget that can be modified and must be returned.
|
||||||
|
"""
|
||||||
|
return widget
|
||||||
|
|
||||||
|
def addPage(self, widget, title):
|
||||||
|
"""
|
||||||
|
Convenience function to add a page to the settingsWidget `widget`, at
|
||||||
|
the end.
|
||||||
|
|
||||||
|
Returns the page widget.
|
||||||
|
"""
|
||||||
|
w = QWidget(widget)
|
||||||
|
w.setLayout(QVBoxLayout())
|
||||||
|
widget.toolBox.insertItem(widget.toolBox.count(), w, title)
|
||||||
|
widget.toolBox.layout().setSpacing(0)
|
||||||
|
return w
|
||||||
|
|
||||||
|
def addGroup(self, parent, title):
|
||||||
|
"""
|
||||||
|
Adds a collapsible group to the given widget.
|
||||||
|
"""
|
||||||
|
g = collapsibleGroupBox2(title=title)
|
||||||
|
parent.layout().addWidget(g)
|
||||||
|
g.setLayout(QVBoxLayout())
|
||||||
|
return g
|
||||||
|
|
||||||
|
def addSetting(self, name, type, label, widget=None, default=None,
|
||||||
|
tooltip=None, min=None, max=None, vals=None, suffix=""):
|
||||||
|
|
||||||
|
self.settings[name] = self.setting(name, type, label, widget, default,
|
||||||
|
tooltip, min, max, vals, suffix)
|
||||||
|
|
||||||
|
def widget(self, name):
|
||||||
|
if name in self.settings:
|
||||||
|
return self.settings[name].widget()
|
||||||
|
|
||||||
|
def getSetting(self, name):
|
||||||
|
if name in self.settings:
|
||||||
|
return self.settings[name]
|
||||||
|
|
||||||
|
|
||||||
|
class setting:
|
||||||
|
"""
|
||||||
|
A class used to store setting, and display a widget for the user to
|
||||||
|
modify it.
|
||||||
|
"""
|
||||||
|
def __init__(self, name, type, label, widget=None, default=None,
|
||||||
|
tooltip=None, min=None, max=None, vals=None, suffix=""):
|
||||||
|
self.name = name
|
||||||
|
self.type = type
|
||||||
|
self.label = label
|
||||||
|
self._widget = widget
|
||||||
|
self.default = default
|
||||||
|
self.min = min
|
||||||
|
self.max = max
|
||||||
|
self.vals = vals.split("|") if vals else []
|
||||||
|
self.suffix = suffix
|
||||||
|
self.tooltip = tooltip
|
||||||
|
|
||||||
|
def widget(self, parent=None):
|
||||||
|
"""
|
||||||
|
Returns the widget used, or creates it if not done yet. If parent
|
||||||
|
is given, widget is inserted in parent's layout.
|
||||||
|
"""
|
||||||
|
if self._widget:
|
||||||
|
return self._widget
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
if "checkbox" in self.type:
|
||||||
|
self._widget = QCheckBox(self.label)
|
||||||
|
if self.default:
|
||||||
|
self._widget.setChecked(True)
|
||||||
|
if parent:
|
||||||
|
parent.layout().addWidget(self._widget)
|
||||||
|
|
||||||
|
elif "number" in self.type:
|
||||||
|
l = QHBoxLayout()
|
||||||
|
label = QLabel(self.label, parent)
|
||||||
|
label.setWordWrap(True)
|
||||||
|
l.addWidget(label, 8)
|
||||||
|
self._widget = QSpinBox()
|
||||||
|
self._widget.setValue(self.default if self.default else 0)
|
||||||
|
if self.min:
|
||||||
|
self._widget.setMinimum(self.min)
|
||||||
|
if self.max:
|
||||||
|
self._widget.setMaximum(self.max)
|
||||||
|
if self.suffix:
|
||||||
|
self._widget.setSuffix(self.suffix)
|
||||||
|
l.addWidget(self._widget, 2)
|
||||||
|
if parent:
|
||||||
|
parent.layout().addLayout(l)
|
||||||
|
|
||||||
|
elif "combo" in self.type:
|
||||||
|
l = QHBoxLayout()
|
||||||
|
label = QLabel(self.label, parent)
|
||||||
|
label.setWordWrap(True)
|
||||||
|
l.addWidget(label, 6)
|
||||||
|
self._widget = QComboBox()
|
||||||
|
self._widget.addItems(self.vals)
|
||||||
|
l.addWidget(self._widget, 2)
|
||||||
|
if parent:
|
||||||
|
parent.layout().addLayout(l)
|
||||||
|
|
||||||
|
elif "text" in self.type:
|
||||||
|
l = QHBoxLayout()
|
||||||
|
label = QLabel(self.label, parent)
|
||||||
|
label.setWordWrap(True)
|
||||||
|
l.addWidget(label, 5)
|
||||||
|
self._widget = QLineEdit()
|
||||||
|
self._widget.setStyleSheet(style.lineEditSS())
|
||||||
|
if self.default:
|
||||||
|
self._widget.setText(self.default)
|
||||||
|
l.addWidget(self._widget, 3)
|
||||||
|
if parent:
|
||||||
|
parent.layout().addLayout(l)
|
||||||
|
|
||||||
|
elif "label" in self.type:
|
||||||
|
self._widget = QLabel(self.label, parent)
|
||||||
|
self._widget.setWordWrap(True)
|
||||||
|
if parent:
|
||||||
|
parent.layout().addWidget(self._widget)
|
||||||
|
|
||||||
|
if self.tooltip:
|
||||||
|
self._widget.setToolTip(self.tooltip)
|
||||||
|
|
||||||
|
return self._widget
|
||||||
|
|
||||||
|
def value(self):
|
||||||
|
"""
|
||||||
|
Return the value contained in the widget.
|
||||||
|
"""
|
||||||
|
if not self._widget:
|
||||||
|
return self.default
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
if "checkbox" in self.type:
|
||||||
|
return self._widget.isChecked()
|
||||||
|
|
||||||
|
elif "number" in self.type:
|
||||||
|
return self._widget.value()
|
||||||
|
|
||||||
|
elif "combo" in self.type:
|
||||||
|
return self._widget.currentText()
|
||||||
|
|
||||||
|
elif "text" in self.type:
|
||||||
|
return self._widget.text()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# --!-- coding: utf8 --!--
|
# --!-- coding: utf8 --!--
|
||||||
|
|
||||||
|
import os
|
||||||
from manuskript.importer.abstractImporter import abstractImporter
|
from manuskript.importer.abstractImporter import abstractImporter
|
||||||
|
from manuskript.models.outlineModel import outlineItem
|
||||||
|
from manuskript.enums import Outline
|
||||||
|
from PyQt5.QtWidgets import qApp
|
||||||
|
|
||||||
|
|
||||||
class folderImporter(abstractImporter):
|
class folderImporter(abstractImporter):
|
||||||
|
@ -10,3 +14,122 @@ class folderImporter(abstractImporter):
|
||||||
description = ""
|
description = ""
|
||||||
fileFormat = "<<folder>>"
|
fileFormat = "<<folder>>"
|
||||||
icon = "folder"
|
icon = "folder"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def isValid(cls):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def startImport(self, filePath, parentItem, settingsWidget, fromString=None):
|
||||||
|
"""
|
||||||
|
Imports from a folder.
|
||||||
|
"""
|
||||||
|
ext = self.getSetting("ext").value()
|
||||||
|
ext = [e.strip().replace("*", "") for e in ext.split(",")]
|
||||||
|
|
||||||
|
sorting = self.getSetting("sortItems").value()
|
||||||
|
|
||||||
|
items = []
|
||||||
|
stack = {}
|
||||||
|
|
||||||
|
if self.getSetting("topLevelFolder").value():
|
||||||
|
parent = outlineItem(title=os.path.basename(filePath),
|
||||||
|
parent=parentItem)
|
||||||
|
items.append(parent)
|
||||||
|
stack[filePath] = parent
|
||||||
|
|
||||||
|
for dirpath, dirnames, filenames in os.walk(filePath):
|
||||||
|
|
||||||
|
if dirpath in stack:
|
||||||
|
item = stack[dirpath]
|
||||||
|
else:
|
||||||
|
# It's the parent folder, and we are not including it
|
||||||
|
# so every item is attached to parentItem
|
||||||
|
item = parentItem
|
||||||
|
|
||||||
|
def addFile(f):
|
||||||
|
fName, fExt = os.path.splitext(f)
|
||||||
|
if fExt in ext:
|
||||||
|
child = outlineItem(title=fName, _type="md", parent=item)
|
||||||
|
with open(os.path.join(dirpath, f), "r") as fr:
|
||||||
|
child._data[Outline.text] = fr.read()
|
||||||
|
items.append(child)
|
||||||
|
|
||||||
|
def addFolder(d):
|
||||||
|
child = outlineItem(title=d, parent=item)
|
||||||
|
items.append(child)
|
||||||
|
stack[os.path.join(dirpath, d)] = child
|
||||||
|
|
||||||
|
if not self.getSetting("separateFolderFiles").value():
|
||||||
|
# Import folder and files together (only makes differences if
|
||||||
|
# they are sorted, really)
|
||||||
|
allFiles = dirnames + filenames
|
||||||
|
if sorting:
|
||||||
|
allFiles = sorted(allFiles)
|
||||||
|
|
||||||
|
for f in allFiles:
|
||||||
|
if f in dirnames:
|
||||||
|
addFolder(f)
|
||||||
|
else:
|
||||||
|
addFile(f)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Import first folders, then files
|
||||||
|
if sorting:
|
||||||
|
dirnames = sorted(dirnames)
|
||||||
|
filenames = sorted(filenames)
|
||||||
|
|
||||||
|
# Import folders
|
||||||
|
for d in dirnames:
|
||||||
|
addFolder(d)
|
||||||
|
|
||||||
|
# Import files
|
||||||
|
for f in filenames:
|
||||||
|
addFile(f)
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
def settingsWidget(self, widget):
|
||||||
|
"""
|
||||||
|
Takes a QWidget that can be modified and must be returned.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Add group
|
||||||
|
group = self.addGroup(widget.toolBox.widget(0),
|
||||||
|
qApp.translate("Import", "Folder import"))
|
||||||
|
#group = cls.addPage(widget, "Folder import")
|
||||||
|
|
||||||
|
self.addSetting("info", "label",
|
||||||
|
qApp.translate("Import", """<b>Info:</b> Imports a whole
|
||||||
|
directory structure. Folders are added as folders, and
|
||||||
|
plaintext documents within (you chose which ones by extension)
|
||||||
|
are added as scene.<br/> """))
|
||||||
|
|
||||||
|
self.addSetting("ext", "text",
|
||||||
|
qApp.translate("Import", "Include only those extensions:"),
|
||||||
|
default="*.txt, *.md",
|
||||||
|
tooltip=qApp.translate("Import", "Coma separated values")),
|
||||||
|
|
||||||
|
self.addSetting("topLevelFolder", "checkbox",
|
||||||
|
qApp.translate("Import", "Include top-level folder"),
|
||||||
|
default=False),
|
||||||
|
|
||||||
|
self.addSetting("sortItems", "checkbox",
|
||||||
|
qApp.translate("Import", "Sort items by name"),
|
||||||
|
default=True),
|
||||||
|
|
||||||
|
self.addSetting("separateFolderFiles", "checkbox",
|
||||||
|
qApp.translate("Import", "Import folder then files"),
|
||||||
|
default=True),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for s in self.settings:
|
||||||
|
self.settings[s].widget(group)
|
||||||
|
|
||||||
|
return widget
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -895,7 +895,7 @@ class outlineItem():
|
||||||
self.IDs = self.listAllIDs()
|
self.IDs = self.listAllIDs()
|
||||||
|
|
||||||
if max([self.IDs.count(i) for i in self.IDs if i]) != 1:
|
if max([self.IDs.count(i) for i in self.IDs if i]) != 1:
|
||||||
print("There are some doublons:", [i for i in self.IDs if i and self.IDs.count(i) != 1])
|
print("WARNING ! There are some items with same IDs:", [i for i in self.IDs if i and self.IDs.count(i) != 1])
|
||||||
|
|
||||||
def checkChildren(item):
|
def checkChildren(item):
|
||||||
for c in item.children():
|
for c in item.children():
|
||||||
|
@ -913,8 +913,9 @@ class outlineItem():
|
||||||
return IDs
|
return IDs
|
||||||
|
|
||||||
def findUniqueID(self):
|
def findUniqueID(self):
|
||||||
|
IDs = [int(i) for i in self.IDs]
|
||||||
k = 0
|
k = 0
|
||||||
while str(k) in self.IDs:
|
while k in self.IDs:
|
||||||
k += 1
|
k += 1
|
||||||
self.IDs.append(str(k))
|
self.IDs.append(str(k))
|
||||||
return str(k)
|
return str(k)
|
||||||
|
|
|
@ -10,6 +10,8 @@ from PyQt5.QtWidgets import QWidget, QTableWidgetItem, QListWidgetItem, QTreeVie
|
||||||
from manuskript.functions import mainWindow, writablePath
|
from manuskript.functions import mainWindow, writablePath
|
||||||
from manuskript.ui.importers.generalSettings_ui import Ui_generalSettings
|
from manuskript.ui.importers.generalSettings_ui import Ui_generalSettings
|
||||||
from manuskript.enums import Outline
|
from manuskript.enums import Outline
|
||||||
|
from manuskript.ui import style
|
||||||
|
|
||||||
|
|
||||||
class generalSettings(QWidget, Ui_generalSettings):
|
class generalSettings(QWidget, Ui_generalSettings):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
|
@ -17,6 +19,7 @@ class generalSettings(QWidget, Ui_generalSettings):
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
|
|
||||||
self.mw = mainWindow()
|
self.mw = mainWindow()
|
||||||
|
self.txtGeneralSplitScenes.setStyleSheet(style.lineEditSS())
|
||||||
|
|
||||||
# TreeView to select parent
|
# TreeView to select parent
|
||||||
# We use a proxy to display only folders
|
# We use a proxy to display only folders
|
||||||
|
|
|
@ -82,7 +82,8 @@ class importerDialog(QWidget, Ui_importer):
|
||||||
def currentFormat(self):
|
def currentFormat(self):
|
||||||
formatName = self.cmbImporters.currentText()
|
formatName = self.cmbImporters.currentText()
|
||||||
F = [F for F in self.importers if F.name == formatName][0]
|
F = [F for F in self.importers if F.name == formatName][0]
|
||||||
return F
|
# We instantiate the class
|
||||||
|
return F()
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
# Import file
|
# Import file
|
||||||
|
@ -94,7 +95,7 @@ class importerDialog(QWidget, Ui_importer):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# We find the current selected format
|
# We find the current selected format
|
||||||
F = self.currentFormat()
|
F = self._format
|
||||||
|
|
||||||
options = QFileDialog.Options()
|
options = QFileDialog.Options()
|
||||||
options |= QFileDialog.DontUseNativeDialog
|
options |= QFileDialog.DontUseNativeDialog
|
||||||
|
@ -159,17 +160,21 @@ class importerDialog(QWidget, Ui_importer):
|
||||||
return
|
return
|
||||||
|
|
||||||
F = self.currentFormat()
|
F = self.currentFormat()
|
||||||
|
self._format = F
|
||||||
|
|
||||||
self.settingsWidget = generalSettings()
|
self.settingsWidget = generalSettings()
|
||||||
self.setGroupWidget(self.grpSettings, self.settingsWidget)
|
|
||||||
self.grpSettings.setMinimumWidth(200)
|
|
||||||
|
|
||||||
#TODO: custom format widget
|
#TODO: custom format widget
|
||||||
|
self.settingsWidget = F.settingsWidget(self.settingsWidget)
|
||||||
|
|
||||||
#toolBox = self.settingsWidget.toolBox
|
#toolBox = self.settingsWidget.toolBox
|
||||||
#w = QWidget()
|
#w = QWidget()
|
||||||
#toolBox.insertItem(toolBox.count(), w, "Pandoc")
|
#toolBox.insertItem(toolBox.count(), w, "Pandoc")
|
||||||
#See pandoc's abstractPlainText
|
#See pandoc's abstractPlainText
|
||||||
|
|
||||||
|
# Set the settings widget in place
|
||||||
|
self.setGroupWidget(self.grpSettings, self.settingsWidget)
|
||||||
|
self.grpSettings.setMinimumWidth(200)
|
||||||
|
|
||||||
# Clear file name
|
# Clear file name
|
||||||
self.setFileName("")
|
self.setFileName("")
|
||||||
|
|
||||||
|
@ -234,10 +239,14 @@ class importerDialog(QWidget, Ui_importer):
|
||||||
Is used by preview and by doImport (actual import).
|
Is used by preview and by doImport (actual import).
|
||||||
|
|
||||||
`outlineModel` is the model where the imported items are added.
|
`outlineModel` is the model where the imported items are added.
|
||||||
|
|
||||||
|
FIXME: Optimisation: when adding many outlineItems, outlineItem.updateWordCount
|
||||||
|
is a bottleneck. It gets called a crazy number of time, and its not
|
||||||
|
necessary.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# We find the current selected format
|
# We find the current selected format
|
||||||
F = self.currentFormat()
|
F = self._format
|
||||||
|
|
||||||
# Parent item
|
# Parent item
|
||||||
ID = self.settingsWidget.importUnderID()
|
ID = self.settingsWidget.importUnderID()
|
||||||
|
@ -263,6 +272,8 @@ class importerDialog(QWidget, Ui_importer):
|
||||||
def trim(item):
|
def trim(item):
|
||||||
if len(item.title()) > 32:
|
if len(item.title()) > 32:
|
||||||
item.setData(Outline.title.value, item.title()[:32])
|
item.setData(Outline.title.value, item.title()[:32])
|
||||||
|
# I think it's overkill to do it recursively, since items
|
||||||
|
# is supposed to contain every imported items.
|
||||||
for c in item.children():
|
for c in item.children():
|
||||||
trim(c)
|
trim(c)
|
||||||
for i in items:
|
for i in items:
|
||||||
|
|
Loading…
Reference in a new issue