manuskript/manuskript/ui/importers/importer.py

325 lines
11 KiB
Python
Raw Normal View History

2017-11-06 21:16:44 +13:00
#!/usr/bin/env python
# --!-- coding: utf8 --!--
import json
import os
from PyQt5.QtCore import Qt, QTimer, QUrl
from PyQt5.QtGui import QBrush, QColor, QIcon, QDesktopServices
from PyQt5.QtWidgets import QWidget, QFileDialog, QMessageBox, QStyle
2017-11-06 21:16:44 +13:00
2017-11-15 02:48:28 +13:00
from manuskript.functions import writablePath, appPath
2017-11-06 21:16:44 +13:00
from manuskript.ui.importers.importer_ui import Ui_importer
from manuskript.ui.importers.generalSettings import generalSettings
from manuskript.ui import style
from manuskript import importer
2017-11-16 08:33:27 +13:00
from manuskript.models import outlineModel, outlineItem
2017-11-08 02:25:47 +13:00
from manuskript.enums import Outline
from manuskript.exporter.pandoc import pandocExporter
2017-11-06 21:16:44 +13:00
class importerDialog(QWidget, Ui_importer):
formatsIcon = {
".epub": "application-epub+zip",
".odt": "application-vnd.oasis.opendocument.text",
".docx": "application-vnd.openxmlformats-officedocument.wordprocessingml.document",
".md": "text-x-markdown",
".rst": "text-plain",
".tex": "text-x-tex",
".opml": "text-x-opml+xml",
".xml": "text-x-opml+xml",
".html": "text-html",
}
2017-11-06 21:16:44 +13:00
def __init__(self, parent=None, mw=None):
QWidget.__init__(self, parent)
self.setupUi(self)
# Var
self.mw = mw
self.fileName = ""
self.setStyleSheet(style.mainWindowSS())
2017-11-08 02:25:47 +13:00
self.tree.setStyleSheet("QTreeView{background:transparent;}")
self.editor.setStyleSheet("QWidget{background:transparent;}")
self.editor.toggleSpellcheck(False)
# Register importFormats:
self.importers = importer.importers
# Populate combo box with formats
self.populateImportList()
# Connections
self.btnChoseFile.clicked.connect(self.selectFile)
self.btnClearFileName.clicked.connect(self.setFileName)
self.btnPreview.clicked.connect(self.preview)
2017-11-08 02:40:46 +13:00
self.btnImport.clicked.connect(self.doImport)
self.cmbImporters.currentTextChanged.connect(self.updateSettings)
self.setFileName("")
self.updateSettings()
############################################################################
# Combobox / Formats
############################################################################
def populateImportList(self):
def addFormat(name, icon, identifier):
# Identifier serves to distingues 2 importers that would have the
# same name.
self.cmbImporters.addItem(QIcon.fromTheme(icon), name, identifier)
def addHeader(name):
self.cmbImporters.addItem(name, "header")
2017-11-15 02:48:28 +13:00
self.cmbImporters.setItemData(self.cmbImporters.count() - 1, QBrush(QColor(style.highlightedTextDark)), Qt.ForegroundRole)
self.cmbImporters.setItemData(self.cmbImporters.count() - 1, QBrush(QColor(style.highlightLight)), Qt.BackgroundRole)
item = self.cmbImporters.model().item(self.cmbImporters.count() - 1)
item.setFlags(Qt.ItemIsEnabled)
lastEngine = ""
for f in self.importers:
# Header
if f.engine != lastEngine:
addHeader(f.engine)
lastEngine = f.engine
addFormat(f.name, f.icon, "{}:{}".format(f.engine, f.name))
if not f.isValid():
item = self.cmbImporters.model().item(self.cmbImporters.count() - 1)
item.setFlags(Qt.NoItemFlags)
if not pandocExporter().isValid():
self.cmbImporters.addItem(
self.style().standardIcon(QStyle.SP_MessageBoxWarning),
"Install pandoc to import from much more formats",
"::URL::http://pandoc.org/installing.html")
self.cmbImporters.setCurrentIndex(1)
def currentFormat(self):
formatIdentifier = self.cmbImporters.currentData()
if formatIdentifier == "header":
return None
F = [F for F in self.importers
if formatIdentifier == "{}:{}".format(F.engine, F.name)][0]
# We instantiate the class
return F()
############################################################################
# Import file
############################################################################
def selectFile(self):
"""
Called to select a file in the file system. Uses QFileDialog.
"""
# We find the current selected format
F = self._format
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
if F.fileFormat == "<<folder>>":
options = QFileDialog.DontUseNativeDialog | QFileDialog.ShowDirsOnly
fileName = QFileDialog.getExistingDirectory(self, "Select import folder",
"", options=options)
else:
fileName, _ = QFileDialog.getOpenFileName(self, "Import from file", "",
F.fileFormat, options=options)
self.setFileName(fileName)
self.preview()
def setFileName(self, fileName):
"""
Updates Ui with given filename. Filename can be empty.
"""
if fileName:
self.fileName = fileName
self.lblFileName.setText(os.path.basename(fileName))
self.lblFileName.setToolTip(fileName)
ext = os.path.splitext(fileName)[1]
icon = None
if ext and ext in self.formatsIcon:
icon = QIcon.fromTheme(self.formatsIcon[ext])
elif os.path.isdir(fileName):
icon = QIcon.fromTheme("folder")
if icon:
self.lblIcon.setVisible(True)
h = self.lblFileName.height()
self.lblIcon.setPixmap(icon.pixmap(h, h))
else:
self.lblIcon.hide()
else:
self.fileName = None
self.lblFileName.setText("")
hasFile = True if fileName else False
self.btnClearFileName.setVisible(hasFile)
self.lblIcon.setVisible(hasFile)
self.btnChoseFile.setVisible(not hasFile)
self.btnPreview.setEnabled(hasFile)
self.btnImport.setEnabled(hasFile)
############################################################################
# UI
############################################################################
def updateSettings(self):
"""
When the current format change (through the combobox), we update the
settings widget using the current format provided settings widget.
"""
# We check if we have to open an URL
data = self.cmbImporters.currentData()
if data and data[:7] == "::URL::" and data[7:]:
# FIXME: use functions.openURL after merge with feature/Exporters
QDesktopServices.openUrl(QUrl(data[7:]))
return
F = self.currentFormat()
self._format = F
# Checking if we have a valid importer (otherwise a header)
if not F:
self.grpSettings.setEnabled(False)
self.grpPreview.setEnabled(False)
return
self.grpSettings.setEnabled(True)
self.grpPreview.setEnabled(True)
self.settingsWidget = generalSettings()
#TODO: custom format widget
self.settingsWidget = F.settingsWidget(self.settingsWidget)
# Set the settings widget in place
self.setGroupWidget(self.grpSettings, self.settingsWidget)
self.grpSettings.setMinimumWidth(200)
# Clear file name
self.setFileName("")
def setGroupWidget(self, group, widget):
"""
Sets the given widget as main widget for QGroupBox group.
"""
# Removes every items from given layout.
l = group.layout()
while l.count():
item = l.itemAt(0)
l.removeItem(item)
item.widget().deleteLater()
l.addWidget(widget)
widget.setParent(group)
############################################################################
# Preview / Import
############################################################################
2017-11-06 21:16:44 +13:00
def preview(self):
2017-11-14 10:55:33 +13:00
if not self.fileName:
return
2017-11-08 02:40:46 +13:00
# Creating a temporary outlineModel
previewModel = outlineModel(self)
2017-11-08 02:25:47 +13:00
previewModel.loadFromXML(
self.mw.mdlOutline.saveToXML(),
fromString=True)
2017-11-08 02:40:46 +13:00
# Inserting elements
result = self.startImport(previewModel)
if result:
self.tree.setModel(previewModel)
for i in range(1, previewModel.columnCount()):
self.tree.hideColumn(i)
self.tree.selectionModel().currentChanged.connect(self.editor.setCurrentModelIndex)
self.previewSplitter.setStretchFactor(0, 10)
self.previewSplitter.setStretchFactor(1, 40)
def doImport(self):
"""
Called by the Import button.
"""
self.startImport(self.mw.mdlOutline)
2017-11-08 04:22:59 +13:00
# Signal every views that important model changes have happened.
self.mw.mdlOutline.layoutChanged.emit()
# I'm getting seg fault over this message sometimes...
# Using status bar message instead...
#QMessageBox.information(self, self.tr("Import status"),
#self.tr("Import Complete."))
self.mw.statusBar().showMessage("Import complete!", 5000)
2017-11-08 02:40:46 +13:00
self.close()
def startImport(self, outlineModel):
"""
Where most of the magic happens.
Is used by preview and by doImport (actual import).
`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.
2017-11-08 02:40:46 +13:00
"""
items = []
2017-11-08 02:40:46 +13:00
# We find the current selected format
F = self._format
2017-11-08 02:40:46 +13:00
2017-11-08 02:25:47 +13:00
# Parent item
ID = self.settingsWidget.importUnderID()
2017-11-08 02:40:46 +13:00
parentItem = outlineModel.getItemByID(ID)
# Import in top-level folder?
if self.settingsWidget.importInTopLevelFolder():
parent = outlineItem(title=os.path.basename(self.fileName),
parent=parentItem)
parentItem = parent
items.append(parent)
2017-11-08 02:40:46 +13:00
# Calling the importer
rItems = F.startImport(self.fileName,
2017-11-08 02:25:47 +13:00
parentItem,
self.settingsWidget)
items.extend(rItems)
# Do transformations
2017-11-08 02:40:46 +13:00
items = self.doTransformations(items)
return True
def doTransformations(self, items):
"""
Do general transformations.
"""
2017-11-08 02:25:47 +13:00
# Trim long titles
if self.settingsWidget.trimLongTitles():
for item in items:
2017-11-08 02:25:47 +13:00
if len(item.title()) > 32:
item.setData(Outline.title.value, item.title()[:32])
# Split at
if self.settingsWidget.splitScenes():
for item in items:
item.split(self.settingsWidget.splitScenes(), recursive=False)
2017-11-08 02:40:46 +13:00
return items