mirror of
https://github.com/olivierkes/manuskript.git
synced 2024-05-02 20:12:24 +12:00
Improves the Mind Map importer #208
This commit is contained in:
parent
c4f8d0da60
commit
fd0cd2cd4f
6
icons/NumixMsk/16x16/actions/document-import.svg
Normal file
6
icons/NumixMsk/16x16/actions/document-import.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<g transform="matrix(-1,0,0,1,16,0)" style="fill-rule:evenodd">
|
||||
<path d="m 1.714 0 8.571 0 c 1.714 0 1.714 0 1.714 1.778 l 0 12.444 c 0 1.778 0 1.778 -1.714 1.778 l -8.571 0 c -1.714 0 -1.714 0 -1.714 -1.778 l 0 -12.444 c 0 -1.778 0 -1.778 1.714 -1.778" style="fill:#fff"/>
|
||||
<path d="m 9.376 2.664 c 0.141 0 0.318 0.154 0.594 0.375 1.667 1.333 5 4 5 4 1.108 0.887 1.104 1.117 0 2 l -5 4 c -0.828 0.663 -0.953 0.375 -1 -1.125 l -0.031 -1.875 c -4.969 0 -6.969 -3 -6.938 -7.03 1.969 2.031 3.969 3.03 6.969 2.969 l 0 -1.813 c 0 -1 0.125 -1.5 0.406 -1.5 z" style="fill:#268bd2"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 654 B |
6
icons/NumixMsk/22x22/actions/document-import.svg
Normal file
6
icons/NumixMsk/22x22/actions/document-import.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
|
||||
<g transform="matrix(-1,0,0,1,22.000425,0)" style="fill-rule:evenodd">
|
||||
<path d="m 3,2 10,0 c 2,0 2,0 2,2 l 0,14 c 0,2 0,2 -2,2 L 3,20 C 1,20 1,20 1,18 L 1,4 C 1,2 1,2 3,2 Z" style="fill:#fff"/>
|
||||
<path d="m 13.929 4.163 6.857 5.38 c 1.667 1.308 1.57 1.458 0 2.69 l -6.857 5.38 c -0.955 0.751 -0.929 0.472 -0.929 -1.384 l 0 -2.228 c -6.814 0 -10 -4.457 -10 -9.879 c 2.7 2.732 5.885 3.963 10 3.879 l 0 -2 c 0 -0.978 -0.025 -2.499 0.929 -1.834 z" style="fill:#268bd2"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 544 B |
6
icons/NumixMsk/24x24/actions/document-import.svg
Normal file
6
icons/NumixMsk/24x24/actions/document-import.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g transform="matrix(-1,0,0,1,23.000425,1)" style="fill-rule:evenodd">
|
||||
<path d="m 3,2 10,0 c 2,0 2,0 2,2 l 0,14 c 0,2 0,2 -2,2 L 3,20 C 1,20 1,20 1,18 L 1,4 C 1,2 1,2 3,2 Z" style="fill:#fff"/>
|
||||
<path d="m 13.929 4.163 6.857 5.38 c 1.667 1.308 1.57 1.458 0 2.69 l -6.857 5.38 c -0.955 0.751 -0.929 0.472 -0.929 -1.384 l 0 -2.228 c -6.814 0 -10 -4.457 -10 -9.879 c 2.7 2.732 5.885 3.963 10 3.879 l 0 -2 c 0 -0.978 -0.025 -2.499 0.929 -1.834 z" style="fill:#268bd2"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 544 B |
6
icons/NumixMsk/32x32/actions/document-import.svg
Normal file
6
icons/NumixMsk/32x32/actions/document-import.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<g transform="matrix(-1,0,0,1,31.99928,0)" style="fill-rule:evenodd">
|
||||
<path d="M 3.429688,0 20.570312,0 C 24,0 24,0 24,3.554688 l 0,24.890624 C 24,32 24,32 20.570312,32 L 3.429688,32 C 0,32 0,32 0,28.445312 L 0,3.554688 C 0,0 0,0 3.429688,0 Z" style="fill:#fff"/>
|
||||
<path d="m 18.751 5.27 c 0.281 0 0.635 0.308 1.188 0.75 3.334 2.667 10 8 10 8 2.217 1.774 2.209 2.233 0 4 l -10 8 c -1.657 1.325 -1.938 1 -2 -2.25 -0.019 -1 0 -3.75 0 -3.75 l -0.063 0 c -9.938 0 -13.939 -6 -13.876 -14.06 3.938 4.062 7.938 6.06 13.939 5.937 0 -1.434 -0.031 -2.66 0 -3.625 0.063 -2.25 0.25 -3 0.813 -3 z" style="fill:#268bd2"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 686 B |
6
icons/NumixMsk/48x48/actions/document-import.svg
Normal file
6
icons/NumixMsk/48x48/actions/document-import.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||
<g transform="matrix(-1,0,0,1,47.999149,0)" style="fill-rule:evenodd">
|
||||
<path d="m 8,6 20,0 c 4,0 4,0 4,4 l 0,28 c 0,4 0,4 -4,4 L 8,42 C 4,42 4,42 4,38 L 4,10 C 4,6 4,6 8,6 Z" style="fill:#fff"/>
|
||||
<path d="m 29.945 10.325 13.77 10.781 c 3.05 2.388 3.041 3.01 0 5.391 l -13.77 10.781 c -1.919 1.502 -1.945 0.697 -1.945 -2.772 -0.039 -1.348 0 -4.506 0 -4.506 c -0.031 0 0.031 0 0 0 c -13.684 0 -20.09 -8.893 -20 -19.759 5.422 5.475 11.738 7.849 20 7.68 l 0 -3.92 c 0 -1.96 0.029 -5 1.945 -3.675 z" style="fill:#268bd2"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 596 B |
6
icons/NumixMsk/64x64/actions/document-import.svg
Normal file
6
icons/NumixMsk/64x64/actions/document-import.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<g transform="matrix(-1,0,0,1,63.998608,0)" style="fill-rule:evenodd">
|
||||
<path d="M 6.855469,0 41.144531,0 C 48,0 48,0 48,7.109375 l 0,49.78125 C 48,64 48,64 41.144531,64 L 6.855469,64 C 0,64 0,64 0,56.890625 L 0,7.109375 C 0,0 0,0 6.855469,0 Z" style="fill:#fff"/>
|
||||
<path d="m 36.861 11.5 c 0.55 0 1.243 0.603 2.324 1.466 l 19.567 15.641 c 4.338 3.468 4.322 4.366 0 7.821 l -19.567 15.641 c -3.241 2.59 -3.184 1.967 -3.184 -4.399 0 0 0.183 -7.67 0.061 -7.67 l -0.122 0 c -19.444 0 -28.06 -11.392 -27.939 -27.16 7.704 7.943 16.26 12.4 28 12.156 l 0 -7.635 c 0.122 -3.91 -0.24 -5.865 0.861 -5.865" style="fill:#268bd2"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 696 B |
36
manuskript/converters/__init__.py
Normal file
36
manuskript/converters/__init__.py
Normal file
|
@ -0,0 +1,36 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
"""
|
||||
The converters package provide functions to quickly convert on the fly from
|
||||
one format to another. It is responsible to check what external library are
|
||||
present, and do the job as best as possible with what we have in hand.
|
||||
"""
|
||||
|
||||
from manuskript.converters.abstractConverter import abstractConverter
|
||||
from manuskript.converters.pandocConverter import pandocConverter
|
||||
#from manuskript.converters.markdownConverter import markdownConverter
|
||||
|
||||
|
||||
def HTML2MD(html):
|
||||
|
||||
# Convert using pandoc
|
||||
if pandocConverter.isValid():
|
||||
return pandocConverter.convert(html, _from="html", to="markdown")
|
||||
|
||||
# Convert to plain text using QTextEdit
|
||||
return HTML2PlainText(html)
|
||||
|
||||
|
||||
def HTML2PlainText(html):
|
||||
"""
|
||||
Convert from HTML to plain text.
|
||||
"""
|
||||
|
||||
if pandocConverter.isValid():
|
||||
return pandocConverter.convert(html, _from="html", to="plain")
|
||||
|
||||
# Last resort: probably resource ineficient
|
||||
e = QTextEdit()
|
||||
e.setHtml(html)
|
||||
return e.toPlainText()
|
18
manuskript/converters/abstractConverter.py
Normal file
18
manuskript/converters/abstractConverter.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
|
||||
|
||||
class abstractConverter:
|
||||
"""
|
||||
A convertor is used to convert (duh) between stuff. They provide access
|
||||
to external libraries that may or may not be present.
|
||||
|
||||
Now, things are a bit messy, since classes in `exporter` (and `importer` to a lesser extent) do
|
||||
the same. In a better world, classes from `exporter` and `importer` would
|
||||
use convertors to do their stuff. (TODO)
|
||||
"""
|
||||
name = ""
|
||||
|
||||
@classmethod
|
||||
def isValid(cls):
|
||||
return False
|
38
manuskript/converters/markdownConverter.py
Normal file
38
manuskript/converters/markdownConverter.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import qApp, QMessageBox
|
||||
from PyQt5.QtGui import QCursor
|
||||
|
||||
from manuskript.converters import abstractConverter
|
||||
from manuskript.functions import mainWindow
|
||||
|
||||
try:
|
||||
import markdown as MD
|
||||
except ImportError:
|
||||
MD = None
|
||||
|
||||
|
||||
class markdownConverter(abstractConverter):
|
||||
"""
|
||||
Converter using python module markdown.
|
||||
"""
|
||||
|
||||
name = "python module markdown"
|
||||
|
||||
@classmethod
|
||||
def isValid(self):
|
||||
return MD is not None
|
||||
|
||||
@classmethod
|
||||
def convert(self, markdown):
|
||||
if not self.isValid:
|
||||
print("ERROR: markdownConverter is called but not valid.")
|
||||
return ""
|
||||
|
||||
html = MD.markdown(markdown)
|
||||
return html
|
85
manuskript/converters/pandocConverter.py
Normal file
85
manuskript/converters/pandocConverter.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env python
|
||||
# --!-- coding: utf8 --!--
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import qApp, QMessageBox
|
||||
from PyQt5.QtGui import QCursor
|
||||
|
||||
from manuskript.converters import abstractConverter
|
||||
from manuskript.functions import mainWindow
|
||||
|
||||
|
||||
class pandocConverter(abstractConverter):
|
||||
|
||||
name = "pandoc"
|
||||
cmd = "pandoc"
|
||||
|
||||
@classmethod
|
||||
def isValid(self):
|
||||
if self.path() != None:
|
||||
return 2
|
||||
elif self.customPath() and os.path.exists(self.customPath):
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
|
||||
@classmethod
|
||||
def customPath(self):
|
||||
settings = QSettings()
|
||||
return settings.value("Exporters/{}_customPath".format(self.name), "")
|
||||
|
||||
@classmethod
|
||||
def path(self):
|
||||
return shutil.which(self.cmd)
|
||||
|
||||
@classmethod
|
||||
def convert(self, src, _from="markdown", to="html", args=None, outputfile=None):
|
||||
if not self.isValid:
|
||||
print("ERROR: pandocConverter is called but not valid.")
|
||||
return ""
|
||||
|
||||
cmd = [self.runCmd()]
|
||||
|
||||
cmd += ["--from={}".format(_from)]
|
||||
cmd += ["--to={}".format(to)]
|
||||
|
||||
if args:
|
||||
cmd += args
|
||||
|
||||
if outputfile:
|
||||
cmd.append("--output={}".format(outputfile))
|
||||
|
||||
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
|
||||
p = subprocess.Popen(
|
||||
cmd,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
if not type(src) == bytes:
|
||||
src = src.encode("utf-8") # assumes utf-8
|
||||
|
||||
stdout, stderr = p.communicate(src)
|
||||
|
||||
qApp.restoreOverrideCursor()
|
||||
|
||||
if stderr:
|
||||
err = stderr.decode("utf-8")
|
||||
print(err)
|
||||
QMessageBox.critical(mainWindow().dialog,
|
||||
qApp.translate("Export", "Error"), err)
|
||||
return None
|
||||
|
||||
return stdout.decode("utf-8")
|
||||
|
||||
@classmethod
|
||||
def runCmd(self):
|
||||
if self.isValid() == 2:
|
||||
return self.cmd
|
||||
elif self.isValid() == 1:
|
||||
return self.customPath
|
|
@ -276,17 +276,6 @@ def findFirstFile(regex, path="resources"):
|
|||
if re.match(regex, l):
|
||||
return os.path.join(p, l)
|
||||
|
||||
|
||||
def HTML2PlainText(html):
|
||||
"""
|
||||
Ressource-inefficient way to convert HTML to plain text.
|
||||
@param html:
|
||||
@return:
|
||||
"""
|
||||
e = QTextEdit()
|
||||
e.setHtml(html)
|
||||
return e.toPlainText()
|
||||
|
||||
def customIcons():
|
||||
"""
|
||||
Returns a list of possible customIcons. String from theme.
|
||||
|
|
|
@ -7,6 +7,7 @@ from manuskript.enums import Outline
|
|||
from lxml import etree as ET
|
||||
from manuskript.functions import mainWindow
|
||||
from manuskript.importer.abstractImporter import abstractImporter
|
||||
from manuskript.converters import HTML2MD, HTML2PlainText
|
||||
|
||||
class mindMapImporter(abstractImporter):
|
||||
|
||||
|
@ -71,7 +72,7 @@ class mindMapImporter(abstractImporter):
|
|||
|
||||
self.addSetting("importTipAs", "combo",
|
||||
qApp.translate("Import", "Import tip as:"),
|
||||
vals="Folder|Text",
|
||||
vals="Text|Folder",
|
||||
)
|
||||
|
||||
for s in self.settings:
|
||||
|
@ -81,17 +82,65 @@ class mindMapImporter(abstractImporter):
|
|||
|
||||
def parseItems(self, underElement, parentItem=None):
|
||||
items = []
|
||||
title = underElement.get('TEXT')
|
||||
if title is not None:
|
||||
|
||||
item = outlineItem(parent=parentItem, title=title)
|
||||
items.append(item)
|
||||
# Title
|
||||
title = underElement.get('TEXT', "").replace("\n", " ")
|
||||
if not title:
|
||||
title = qApp.translate("Import", "Untitled")
|
||||
|
||||
children = underElement.findall('node')
|
||||
if children is not None and len(children) > 0:
|
||||
for c in children:
|
||||
items.extend(self.parseItems(c, item))
|
||||
elif self.getSetting("importTipAs").value() == "Text":
|
||||
item.setData(Outline.type.value, 'md')
|
||||
item = outlineItem(parent=parentItem, title=title)
|
||||
items.append(item)
|
||||
|
||||
# URL
|
||||
url = underElement.get('LINK', None)
|
||||
|
||||
# Rich text content
|
||||
content = ""
|
||||
content = underElement.find("richcontent")
|
||||
if content is not None:
|
||||
# In Freemind, can be note or node
|
||||
# Note: it's a note
|
||||
# Node: it's the title of the node, in rich text
|
||||
content_type = content.get("TYPE", "NOTE")
|
||||
content = ET.tostring(content.find("html"))
|
||||
|
||||
if content and content_type == "NODE":
|
||||
# Content is title
|
||||
# convert rich text title (in html) to plain text
|
||||
title = HTML2PlainText(content) #.replace("\n", " ").strip()
|
||||
# Count the number of lines
|
||||
lines = [l.strip() for l in title.split("\n") if l.strip()]
|
||||
|
||||
# If there is one line, we use it as title.
|
||||
# Otherwise we leave it to be inserted as a note.
|
||||
if len(lines) == 1:
|
||||
item.setData(Outline.title.value, "".join(lines))
|
||||
content = ""
|
||||
|
||||
if content:
|
||||
# Set the note content as text value
|
||||
content = HTML2MD(content)
|
||||
item.setData(Outline.notes.value, content)
|
||||
|
||||
if url:
|
||||
# Set the url in notes
|
||||
item.setData(Outline.notes.value,
|
||||
item.data(Outline.notes.value) + "\n\n" + url)
|
||||
|
||||
children = underElement.findall('node')
|
||||
|
||||
# Process children
|
||||
if children is not None and len(children) > 0:
|
||||
for c in children:
|
||||
items.extend(self.parseItems(c, item))
|
||||
|
||||
# Process if no children
|
||||
elif self.getSetting("importTipAs").value() == "Text":
|
||||
# Transform item to text
|
||||
item.setData(Outline.type.value, 'md')
|
||||
# Move notes to text
|
||||
if item.data(Outline.notes.value):
|
||||
item.setData(Outline.text.value, item.data(Outline.notes.value))
|
||||
item.setData(Outline.notes.value, "")
|
||||
|
||||
return items
|
||||
|
|
|
@ -18,7 +18,8 @@ from PyQt5.QtGui import QColor, QStandardItem
|
|||
|
||||
from manuskript import settings
|
||||
from manuskript.enums import Character, World, Plot, PlotStep, Outline
|
||||
from manuskript.functions import mainWindow, iconColor, iconFromColorString, HTML2PlainText
|
||||
from manuskript.functions import mainWindow, iconColor, iconFromColorString
|
||||
from manuskript.converters import HTML2PlainText
|
||||
from lxml import etree as ET
|
||||
|
||||
from manuskript.load_save.version_0 import loadFilesFromZip
|
||||
|
|
|
@ -15,7 +15,8 @@ from manuskript import settings
|
|||
from lxml import etree as ET
|
||||
|
||||
from manuskript.enums import Outline
|
||||
from manuskript.functions import mainWindow, toInt, wordCount, HTML2PlainText
|
||||
from manuskript.functions import mainWindow, toInt, wordCount
|
||||
from manuskript.converters import HTML2PlainText
|
||||
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
|
|
@ -226,6 +226,9 @@ class importerDialog(QWidget, Ui_importer):
|
|||
|
||||
def preview(self):
|
||||
|
||||
if not self.fileName:
|
||||
return
|
||||
|
||||
# Creating a temporary outlineModel
|
||||
previewModel = outlineModel(self)
|
||||
previewModel.loadFromXML(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Form implementation generated from reading ui file 'manuskript/ui/importers/importer_ui.ui'
|
||||
#
|
||||
# Created by: PyQt5 UI code generator 5.9
|
||||
# Created by: PyQt5 UI code generator 5.5.1
|
||||
#
|
||||
# WARNING! All changes made in this file will be lost!
|
||||
|
||||
|
|
|
@ -45,8 +45,7 @@
|
|||
<string>Chose file</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="document-import">
|
||||
<normaloff>../../../../../../../../../../.designer/backup</normaloff>../../../../../../../../../../.designer/backup</iconset>
|
||||
<iconset theme="document-import"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
Loading…
Reference in a new issue