Sun, 29 Dec 2013 19:41:52 +0100
Implemented the wizard dialog.
diff -r 3ad485fab7d4 -r 2fa6997ff09c .hgignore --- a/.hgignore Sun Dec 29 15:40:19 2013 +0100 +++ b/.hgignore Sun Dec 29 19:41:52 2013 +0100 @@ -15,3 +15,4 @@ glob:tmp glob:__pycache__ glob:**.DS_Store +glob:**Ui_*.py
diff -r 3ad485fab7d4 -r 2fa6997ff09c PluginWizardDataUriEncoder.e4p --- a/PluginWizardDataUriEncoder.e4p Sun Dec 29 15:40:19 2013 +0100 +++ b/PluginWizardDataUriEncoder.e4p Sun Dec 29 19:41:52 2013 +0100 @@ -6,7 +6,7 @@ <Hash>2144bd61ef339d39bc1790af5e236aaa2a8828ac</Hash> <ProgLanguage mixed="0">Python3</ProgLanguage> <ProjectType>E4Plugin</ProjectType> - <Description>Plug-in implementing a wizard for the generation of code base64 encoded data URIs.</Description> + <Description>Plug-in implementing a wizard for the generation of code for base64 encoded data URIs.</Description> <Version>0.1</Version> <Author>Detlev Offenbach</Author> <Email>detlevqdie-offenbachs.de</Email> @@ -15,13 +15,18 @@ <Sources> <Source>__init__.py</Source> <Source>PluginWizardDataUriEncoder.py</Source> + <Source>WizardDataUriEncoder/__init__.py</Source> + <Source>WizardDataUriEncoder/DataUriEncoderWizardDialog.py</Source> </Sources> - <Forms/> + <Forms> + <Form>WizardDataUriEncoder/DataUriEncoderWizardDialog.ui</Form> + </Forms> <Translations/> <Resources/> <Interfaces/> <Others> <Other>.hgignore</Other> + <Other>PluginWizardDataUriEncoder.e4p</Other> </Others> <MainScript>PluginWizardDataUriEncoder.py</MainScript> <Vcs> @@ -141,5 +146,6 @@ <FiletypeAssociation pattern="*.ts" type="TRANSLATIONS"/> <FiletypeAssociation pattern="*.ui" type="FORMS"/> <FiletypeAssociation pattern="*.ui.h" type="FORMS"/> + <FiletypeAssociation pattern="Ui_*.py" type="__IGNORE__"/> </FiletypeAssociations> </Project>
diff -r 3ad485fab7d4 -r 2fa6997ff09c PluginWizardDataUriEncoder.py --- a/PluginWizardDataUriEncoder.py Sun Dec 29 15:40:19 2013 +0100 +++ b/PluginWizardDataUriEncoder.py Sun Dec 29 19:41:52 2013 +0100 @@ -0,0 +1,152 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2013 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the base64 data URI encoder wizard plug-in. +""" + +from __future__ import unicode_literals + +import os + +from PyQt4.QtCore import QObject, QTranslator +from PyQt4.QtGui import QDialog + +from E5Gui.E5Application import e5App +from E5Gui.E5Action import E5Action +from E5Gui import E5MessageBox + +# Start-of-Header +name = "Base64 Data URI Encoder Wizard Plug-in" +author = "Detlev Offenbach <detlev@die-offenbachs.de>" +autoactivate = True +deactivateable = True +version = "0.1.0" +className = "WizardDataUriEncoderPlugin" +packageName = "WizardDataUriEncoder" +shortDescription = "Wizard for the creation of code for a base64 data URI." +longDescription = \ + """This plug-in implements a wizard to generate code for""" \ + """ base64 encoded data URIs.""" +needsRestart = False +pyqtApi = 2 +# End-of-Header + +error = "" + + +class WizardDataUriEncoderPlugin(QObject): + """ + Class implementing the base64 data URI encoder wizard plug-in. + """ + def __init__(self, ui): + """ + Constructor + + @param ui reference to the user interface object (UI.UserInterface) + """ + QObject.__init__(self, ui) + self.__ui = ui + self.__action = None + + self.__translator = None + self.__loadTranslator() + + def __initialize(self): + """ + Private slot to (re)initialize the plug-in. + """ + self.__act = None + + def activate(self): + """ + Public method to activate this plug-in. + + @return tuple of None and activation status (boolean) + """ + self.__initAction() + self.__initMenu() + + return None, True + + def deactivate(self): + """ + Public method to deactivate this plug-in. + """ + menu = self.__ui.getMenu("wizards") + if menu: + menu.removeAction(self.__action) + self.__ui.removeE5Actions([self.__action], 'wizards') + + def __loadTranslator(self): + """ + Private method to load the translation file. + """ + if self.__ui is not None: + loc = self.__ui.getLocale() + if loc and loc != "C": + locale_dir = os.path.join( + os.path.dirname(__file__), "WizardDataUriEncoder", "i18n") + translation = "datauriencoder_{0}".format(loc) + translator = QTranslator(None) + loaded = translator.load(translation, locale_dir) + if loaded: + self.__translator = translator + e5App().installTranslator(self.__translator) + else: + print("Warning: translation file '{0}' could not be" + " loaded.".format(translation)) + print("Using default.") + + def __initAction(self): + """ + Private method to initialize the action. + """ + self.__action = E5Action( + self.trUtf8('Base64 Data Uri Encoder Wizard'), + self.trUtf8('Base&64 Data Uri Encoder Wizard...'), + 0, 0, self, + 'wizards_datauriencoder') + self.__action.setStatusTip(self.trUtf8('.desktop Wizard')) + self.__action.setWhatsThis(self.trUtf8( + """<b>Base64 Data Uri Encoder Wizard</b>""" + """<p>This wizard opens a dialog for entering all the parameters""" + """ needed to create code for a base64 encoded data URI.</p>""" + )) + self.__action.triggered[()].connect(self.__handle) + + self.__ui.addE5Actions([self.__action], 'wizards') + + def __initMenu(self): + """ + Private method to add the actions to the right menu. + """ + menu = self.__ui.getMenu("wizards") + if menu: + menu.addAction(self.__action) + + def __handle(self): + """ + Private method to handle the wizards action. + """ + editor = e5App().getObject("ViewManager").activeWindow() + + if editor is None: + E5MessageBox.critical( + self.__ui, + self.trUtf8('No current editor'), + self.trUtf8('Please open or create a file first.')) + else: + from WizardDataUriEncoder.DataUriEncoderWizardDialog import \ + DataUriEncoderWizardDialog + dlg = DataUriEncoderWizardDialog(None) + if dlg.exec_() == QDialog.Accepted: + code = dlg.getCode() + if code: + line, index = editor.getCursorPosition() + # It should be done on this way to allow undo + editor.beginUndoAction() + editor.insertAt(code, line, index) + editor.endUndoAction()
diff -r 3ad485fab7d4 -r 2fa6997ff09c WizardDataUriEncoder/DataUriEncoderWizardDialog.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WizardDataUriEncoder/DataUriEncoderWizardDialog.py Sun Dec 29 19:41:52 2013 +0100 @@ -0,0 +1,252 @@ +# -*- coding: utf-8 -*- + +""" +Module implementing DataUriEncoderWizardDialog. +""" + +import os +import mimetypes +import base64 +import getpass +import datetime + +from PyQt4.QtCore import pyqtSlot +from PyQt4.QtGui import QDialog, QDialogButtonBox, QApplication, QInputDialog + +from E5Gui.E5Completers import E5FileCompleter +from E5Gui import E5FileDialog, E5MessageBox + +from .Ui_DataUriEncoderWizardDialog import Ui_DataUriEncoderWizardDialog + +import Preferences +import Utilities +import UI.PixmapCache + + +Python2Template = """#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from base64 import b64decode +from cStringIO import StringIO + +#metadata +__author__ = '{0}' +__date__ = '{1}' + + +embedded_file = StringIO(b64decode({2})) +print(embedded_file.read()) +""" + + +Python3Template = """#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from base64 import b64decode +from io import BytesIO + +#metadata +__author__ = '{0}' +__date__ = '{1}' + + +embedded_file = BytesIO(b64decode({2})) +print(embedded_file.read()) +""" + + +class DataUriEncoderWizardDialog(QDialog, Ui_DataUriEncoderWizardDialog): + """ + Class documentation goes here. + """ + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget (QWidget) + """ + super().__init__(parent) + self.setupUi(self) + + self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) + + self.__fileCompleter = E5FileCompleter(self.fileEdit) + + self.fileButton.setIcon(UI.PixmapCache.getIcon("open.png")) + + self.embeddingComboBox.addItems([ + self.tr('Do not generate code'), + self.tr('Generate CSS embed code'), + self.tr('Generate Python3 embed code'), + self.tr('Generate Python2 embed code'), + self.tr('Generate HTML embed code'), + self.tr('Generate JS embed code'), + self.tr('Generate QML embed code'), + ]) + + def __getStartDir(self): + """ + Private method to get the start directory for selection dialogs. + + @return start directory (string) + """ + return (Preferences.getMultiProject("Workspace") or + Utilities.getHomeDir()) + + @pyqtSlot() + def on_fileButton_clicked(self): + """ + Private slot to select the file to be encoded via a selection dialog. + """ + start = Utilities.fromNativeSeparators(self.fileEdit.text()) or \ + self.__getStartDir() + inputFile = E5FileDialog.getOpenFileName( + self, + self.trUtf8("Data URI Encoder"), + start, + self.trUtf8( + "Audio Files (*.flac *.mp3 *.ogg *.wav *.weba *.wma);;" + "Image Files (*.gif *.ico *.jpg *.png *.svg *.tif *.webp" + " *.xpm);;" + "Video Files (*.3gp *.avi *.flv *.mp4 *.ogv *.webm *.wmv);;" + "All Files (*)" + ) + ) + if inputFile: + self.fileEdit.setText(Utilities.toNativeSeparators(inputFile)) + + @pyqtSlot(int) + def on_embeddingComboBox_currentIndexChanged(self, index): + """ + Private slot to handle the selection of an embedding method. + + @param index index of the selected entry (integer) + """ + if index in [1, 4]: + self.encryptCheckBox.setChecked(False) + self.dataCheckBox.setChecked(True) + elif index in [2, 3, 5, 6]: + self.encryptCheckBox.setChecked(False) + self.dataCheckBox.setChecked(False) + + @pyqtSlot() + def on_encodeButton_clicked(self): + """ + Private slot to encode the contents of the given file. + """ + filepath = Utilities.toNativeSeparators(self.fileEdit.text().strip()) + mime = mimetypes.guess_type(filepath, strict=False) + mimetype = mime if mime is not None else self.__askMime() + + if os.path.getsize(filepath) // 1024 // 1024 >= 1: + res = E5MessageBox.warning( + self, + self.trUtf8("Data URI Encoder"), + self.trUtf8( + """The file size is > 1 Megabyte. Encoding this will""" + """ take some time depending on your CPU processing""" + """ power!"""), + E5MessageBox.StandardButtons( + E5MessageBox.Cancel | + E5MessageBox.Ok), + E5MessageBox.Cancel) + if res == E5MessageBox.Cancel: + return + + try: + output = '"{0}{1}{2}{3}"'.format( + 'data:' if self.dataCheckBox.isChecked() else '', + mimetype if self.dataCheckBox.isChecked() else '', + ';charset=utf-8;base64,' if self.dataCheckBox.isChecked() + else '', + base64.b64encode(open(filepath, "rb").read()).decode() + ) + except (IOError, OSError) as err: + E5MessageBox.critical( + self, + self.trUtf8("Data URI Encoder"), + self.trUtf8( + """<p>The file <b>{0}</b> could not be read.</p>""" + """<p>Reason: {1}</p>""").format(filepath, str(err))) + return + if self.embeddingComboBox.currentIndex() == 1: + output = ('html, body {{ margin:0; padding:0; background: url({0})' + ' no-repeat center center fixed; background-size:cover' + ' }}'.format(output)) + elif self.embeddingComboBox.currentIndex() == 2: + output = Python3Template.format( + getpass.getuser(), + datetime.datetime.now().isoformat().split('.')[0], output) + elif self.embeddingComboBox.currentIndex() == 3: + output = Python2Template.format( + getpass.getuser(), + datetime.datetime.now().isoformat().split('.')[0], output) + elif self.embeddingComboBox.currentIndex() == 4: + output = '<img src={0} alt="{1}" title="{1}"/>'.format( + output, os.path.basename(filepath)) + elif self.embeddingComboBox.currentIndex() == 5: + output = 'var embedded_file = window.atob({0}); '.format(output) + elif self.embeddingComboBox.currentIndex() == 6: + output = 'Image {{ source: {0} }} '.format(output) + + if self.encryptCheckBox.isChecked(): + output = output.encode('rot_13') + + self.outputTextEdit.setPlainText(output) + + @pyqtSlot() + def on_copyButton_clicked(self): + """ + Private slot to copy the output to the clipboard. + """ + QApplication.clipboard().setText(self.outputTextEdit.toPlainText()) + + @pyqtSlot() + def on_outputTextEdit_textChanged(self): + """ + Private slot to handle the existence of some output text. + """ + enable = bool(self.outputTextEdit.toPlainText()) + self.copyButton.setEnabled(enable) + self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable) + + @pyqtSlot(str) + def on_fileEdit_textChanged(self, txt): + """ + Private slot to handle the editing of the file name. + + @param txt current file name (string) + """ + self.encodeButton.setEnabled(bool(txt) and os.path.isfile(txt)) + + def __askMime(self): + """ + Private method to get the mime type from the user. + + @return entered mime type (string) + """ + mimetypesList = [""] + list(sorted( + set(mimetypes.types_map.values()).union( + set(mimetypes.common_types.values())))) + try: + index = mimetypesList.index("application/octet-stream") + except ValueError: + index = 0 + mimetype, ok = QInputDialog.getItem( + self, + self.trUtf8("Data URI Encoder"), + self.trUtf8("Enter a mime type:"), + mimetypesList, + index, True) + if ok: + return mimetype.lower() + else: + return "" + + def getCode(self): + """ + Public method to get the code. + + @return generated code (string) + """ + return self.outputTextEdit.toPlainText()
diff -r 3ad485fab7d4 -r 2fa6997ff09c WizardDataUriEncoder/DataUriEncoderWizardDialog.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WizardDataUriEncoder/DataUriEncoderWizardDialog.ui Sun Dec 29 19:41:52 2013 +0100 @@ -0,0 +1,238 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>DataUriEncoderWizardDialog</class> + <widget class="QDialog" name="DataUriEncoderWizardDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>700</width> + <height>400</height> + </rect> + </property> + <property name="windowTitle"> + <string>Data URI Encoder</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0" colspan="3"> + <widget class="QLabel" name="label"> + <property name="text"> + <string><b>Encode file as plain text string</b></string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>File to Encode:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="fileEdit"> + <property name="toolTip"> + <string>Enter the file to be encoded</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QToolButton" name="fileButton"> + <property name="toolTip"> + <string>Select the file to be encoded</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="3"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QCheckBox" name="dataCheckBox"> + <property name="text"> + <string>Use "data:type/subtype;base64,..."</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="encryptCheckBox"> + <property name="toolTip"> + <string>Select to encrypt the output with 'rot_13' (use "string".decode(rot_13) to decrypt it)</string> + </property> + <property name="text"> + <string>Use basic Caesar Cipher (rot_13)</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Embedding Template Code:</string> + </property> + </widget> + </item> + <item row="3" column="1" colspan="2"> + <widget class="QComboBox" name="embeddingComboBox"> + <property name="toolTip"> + <string>Select the embedding method</string> + </property> + </widget> + </item> + <item row="4" column="0" colspan="3"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="encodeButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Encode BASE64</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="5" column="0" colspan="3"> + <widget class="Line" name="line"> + <property name="lineWidth"> + <number>2</number> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="6" column="0" colspan="3"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Base64 String Output:</string> + </property> + </widget> + </item> + <item row="7" column="0" colspan="3"> + <widget class="QPlainTextEdit" name="outputTextEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="8" column="0" colspan="3"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QPushButton" name="copyButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="toolTip"> + <string>Press to copy the encoded output to the system clipboard</string> + </property> + <property name="text"> + <string>Copy encoded output to Clipboard</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="9" column="0" colspan="3"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>fileEdit</tabstop> + <tabstop>fileButton</tabstop> + <tabstop>dataCheckBox</tabstop> + <tabstop>encryptCheckBox</tabstop> + <tabstop>embeddingComboBox</tabstop> + <tabstop>encodeButton</tabstop> + <tabstop>outputTextEdit</tabstop> + <tabstop>copyButton</tabstop> + <tabstop>buttonBox</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>DataUriEncoderWizardDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>DataUriEncoderWizardDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui>
diff -r 3ad485fab7d4 -r 2fa6997ff09c WizardDataUriEncoder/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WizardDataUriEncoder/__init__.py Sun Dec 29 19:41:52 2013 +0100 @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2013 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Package implementing the base64 data URI encoder wizard plug-in data +and dialog. +"""
diff -r 3ad485fab7d4 -r 2fa6997ff09c __init__.py --- a/__init__.py Sun Dec 29 15:40:19 2013 +0100 +++ b/__init__.py Sun Dec 29 19:41:52 2013 +0100 @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2013 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Package implementing the base64 data URI encoder wizard plug-in. +"""