5 |
5 |
6 """ |
6 """ |
7 Module implementing the base64 data URI encoder wizard dialog. |
7 Module implementing the base64 data URI encoder wizard dialog. |
8 """ |
8 """ |
9 |
9 |
|
10 import base64 |
|
11 import codecs |
|
12 import datetime |
|
13 import getpass |
|
14 import mimetypes |
10 import os |
15 import os |
11 import mimetypes |
16 |
12 import base64 |
17 from PyQt6.QtCore import pyqtSlot |
13 import getpass |
18 from PyQt6.QtWidgets import ( |
14 import datetime |
|
15 |
|
16 from PyQt5.QtCore import pyqtSlot |
|
17 from PyQt5.QtWidgets import ( |
|
18 QDialog, QDialogButtonBox, QApplication, QInputDialog |
19 QDialog, QDialogButtonBox, QApplication, QInputDialog |
19 ) |
20 ) |
20 |
21 |
21 from E5Gui.E5Completers import E5FileCompleter |
22 from EricWidgets import EricMessageBox |
22 from E5Gui import E5FileDialog, E5MessageBox |
23 from EricWidgets.EricPathPicker import EricPathPickerModes |
23 |
24 |
24 from .Ui_DataUriEncoderWizardDialog import Ui_DataUriEncoderWizardDialog |
25 from .Ui_DataUriEncoderWizardDialog import Ui_DataUriEncoderWizardDialog |
25 |
26 |
26 import Preferences |
27 import Preferences |
27 import Utilities |
28 import Utilities |
28 import UI.PixmapCache |
29 |
29 |
30 |
30 |
31 DataUriTemplates = { |
31 Python3Template = """#!/usr/bin/env python3 |
32 "Python3": "\n".join([ |
32 # -*- coding: utf-8 -*- |
33 "#!/usr/bin/env python3", |
33 |
34 "# -*- coding: utf-8 -*-", |
34 from base64 import b64decode |
35 "", |
35 from io import BytesIO |
36 "from base64 import b64decode", |
36 |
37 "from io import BytesIO", |
37 #metadata |
38 "", |
38 __author__ = '{0}' |
39 "#metadata", |
39 __date__ = '{1}' |
40 "__author__ = '{0}'", |
40 |
41 "__date__ = '{1}'", |
41 |
42 "", |
42 embedded_file = BytesIO(b64decode({2})) |
43 "", |
43 print(embedded_file.read()) |
44 "embedded_file = BytesIO(b64decode(", |
44 """ |
45 " {2}", |
45 |
46 "))", |
|
47 "print(embedded_file.read())", |
|
48 ]), |
|
49 |
|
50 "CSS": |
|
51 "html, body {{ margin:0; padding:0; background: url({0})" |
|
52 " no-repeat center center fixed; background-size:cover" |
|
53 " }}", |
|
54 |
|
55 "HTML": '<img src={0} alt="{1}" title="{1}"/>', |
|
56 |
|
57 "JavaScript": "var embedded_file = window.atob({0}); ", |
|
58 |
|
59 "QML": "Image {{ source: {0} }} " |
|
60 } |
46 |
61 |
47 class DataUriEncoderWizardDialog(QDialog, Ui_DataUriEncoderWizardDialog): |
62 class DataUriEncoderWizardDialog(QDialog, Ui_DataUriEncoderWizardDialog): |
48 """ |
63 """ |
49 Class implementing the base64 data URI encoder wizard dialog. |
64 Class implementing the base64 data URI encoder wizard dialog. |
50 """ |
65 """ |
51 def __init__(self, parent=None): |
66 def __init__(self, parent=None): |
52 """ |
67 """ |
53 Constructor |
68 Constructor |
54 |
69 |
55 @param parent reference to the parent widget (QWidget) |
70 @param parent reference to the parent widget |
|
71 @type QWidget |
56 """ |
72 """ |
57 super().__init__(parent) |
73 super().__init__(parent) |
58 self.setupUi(self) |
74 self.setupUi(self) |
59 |
75 |
60 self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) |
76 self.buttonBox.button( |
61 |
77 QDialogButtonBox.StandardButton.Ok).setEnabled(False) |
62 self.__fileCompleter = E5FileCompleter(self.fileEdit) |
78 |
63 |
79 self.filePicker.setWindowTitle(self.tr("Data URI Encoder")) |
64 self.fileButton.setIcon(UI.PixmapCache.getIcon("open.png")) |
80 self.filePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) |
|
81 self.filePicker.setFilters(self.tr( |
|
82 "Audio Files (*.flac *.mp3 *.ogg *.wav *.weba *.wma);;" |
|
83 "Image Files (*.gif *.ico *.jpg *.png *.svg *.tif *.webp" |
|
84 " *.xpm);;" |
|
85 "Video Files (*.3gp *.avi *.flv *.mp4 *.ogv *.webm *.wmv);;" |
|
86 "All Files (*)" |
|
87 )) |
|
88 self.filePicker.setDefaultDirectory( |
|
89 Preferences.getMultiProject("Workspace") or |
|
90 Utilities.getHomeDir() |
|
91 ) |
65 |
92 |
66 self.embeddingComboBox.addItems([ |
93 self.embeddingComboBox.addItems([ |
67 self.tr('Do not generate code'), # 0 |
94 self.tr('Do not generate code'), # 0 |
68 self.tr('Generate Python3 embed code'), # 1 |
95 self.tr('Generate Python3 embedding code'), # 1 |
69 self.tr('Generate CSS embed code'), # 2 |
96 self.tr('Generate CSS embedding code'), # 2 |
70 self.tr('Generate HTML embed code'), # 3 |
97 self.tr('Generate HTML embedding code'), # 3 |
71 self.tr('Generate JS embed code'), # 4 |
98 self.tr('Generate JavaScript embedding code'), # 4 |
72 self.tr('Generate QML embed code'), # 5 |
99 self.tr('Generate QML embedding code'), # 5 |
73 ]) |
100 ]) |
74 |
|
75 def __getStartDir(self): |
|
76 """ |
|
77 Private method to get the start directory for selection dialogs. |
|
78 |
|
79 @return start directory (string) |
|
80 """ |
|
81 return (Preferences.getMultiProject("Workspace") or |
|
82 Utilities.getHomeDir()) |
|
83 |
|
84 @pyqtSlot() |
|
85 def on_fileButton_clicked(self): |
|
86 """ |
|
87 Private slot to select the file to be encoded via a selection dialog. |
|
88 """ |
|
89 start = ( |
|
90 Utilities.fromNativeSeparators(self.fileEdit.text()) or |
|
91 self.__getStartDir() |
|
92 ) |
|
93 inputFile = E5FileDialog.getOpenFileName( |
|
94 self, |
|
95 self.tr("Data URI Encoder"), |
|
96 start, |
|
97 self.tr( |
|
98 "Audio Files (*.flac *.mp3 *.ogg *.wav *.weba *.wma);;" |
|
99 "Image Files (*.gif *.ico *.jpg *.png *.svg *.tif *.webp" |
|
100 " *.xpm);;" |
|
101 "Video Files (*.3gp *.avi *.flv *.mp4 *.ogv *.webm *.wmv);;" |
|
102 "All Files (*)" |
|
103 ) |
|
104 ) |
|
105 if inputFile: |
|
106 self.fileEdit.setText(Utilities.toNativeSeparators(inputFile)) |
|
107 |
101 |
108 @pyqtSlot(int) |
102 @pyqtSlot(int) |
109 def on_embeddingComboBox_currentIndexChanged(self, index): |
103 def on_embeddingComboBox_currentIndexChanged(self, index): |
110 """ |
104 """ |
111 Private slot to handle the selection of an embedding method. |
105 Private slot to handle the selection of an embedding method. |
112 |
106 |
113 @param index index of the selected entry (integer) |
107 @param index index of the selected entry |
|
108 @type int |
114 """ |
109 """ |
115 if index in [2, 3]: |
110 if index in [2, 3]: |
116 self.encryptCheckBox.setChecked(False) |
111 self.encryptCheckBox.setChecked(False) |
117 self.dataCheckBox.setChecked(True) |
112 self.dataCheckBox.setChecked(True) |
118 else: |
113 else: |
122 @pyqtSlot() |
117 @pyqtSlot() |
123 def on_encodeButton_clicked(self): |
118 def on_encodeButton_clicked(self): |
124 """ |
119 """ |
125 Private slot to encode the contents of the given file. |
120 Private slot to encode the contents of the given file. |
126 """ |
121 """ |
127 filepath = Utilities.toNativeSeparators(self.fileEdit.text().strip()) |
122 filepath = self.filePicker.text().strip() |
128 mime = mimetypes.guess_type(filepath, strict=False) |
123 mime = mimetypes.guess_type(filepath, strict=False) |
129 mimetype = mime if mime is not None else self.__askMime() |
124 mimetype = mime if mime is not None else self.__askMime() |
130 |
125 |
131 if os.path.getsize(filepath) // 1024 // 1024 >= 1: |
126 if os.path.getsize(filepath) // 1024 // 1024 >= 1: |
132 res = E5MessageBox.warning( |
127 res = EricMessageBox.warning( |
133 self, |
128 self, |
134 self.tr("Data URI Encoder"), |
129 self.tr("Data URI Encoder"), |
135 self.tr( |
130 self.tr( |
136 """The file size is > 1 Megabyte. Encoding this will""" |
131 """The file size is > 1 Megabyte. Encoding this will""" |
137 """ take some time depending on your CPU processing""" |
132 """ take some time depending on your CPU processing""" |
138 """ power!"""), |
133 """ power!"""), |
139 E5MessageBox.StandardButtons( |
134 EricMessageBox.Cancel | |
140 E5MessageBox.Cancel | |
135 EricMessageBox.Ok, |
141 E5MessageBox.Ok), |
136 EricMessageBox.Cancel) |
142 E5MessageBox.Cancel) |
137 if res == EricMessageBox.Cancel: |
143 if res == E5MessageBox.Cancel: |
|
144 return |
138 return |
145 |
139 |
146 try: |
140 try: |
147 with open(filepath, "rb") as f: |
141 with open(filepath, "rb") as f: |
148 output = '"{0}{1}{2}{3}"'.format( |
142 output = '"{0}{1}{2}{3}"'.format( |
151 ';charset=utf-8;base64,' if self.dataCheckBox.isChecked() |
145 ';charset=utf-8;base64,' if self.dataCheckBox.isChecked() |
152 else '', |
146 else '', |
153 base64.b64encode(f.read()).decode() |
147 base64.b64encode(f.read()).decode() |
154 ) |
148 ) |
155 except OSError as err: |
149 except OSError as err: |
156 E5MessageBox.critical( |
150 EricMessageBox.critical( |
157 self, |
151 self, |
158 self.tr("Data URI Encoder"), |
152 self.tr("Data URI Encoder"), |
159 self.tr( |
153 self.tr( |
160 """<p>The file <b>{0}</b> could not be read.</p>""" |
154 """<p>The file <b>{0}</b> could not be read.</p>""" |
161 """<p>Reason: {1}</p>""").format(filepath, str(err))) |
155 """<p>Reason: {1}</p>""").format(filepath, str(err))) |
162 return |
156 return |
163 if self.embeddingComboBox.currentIndex() == 1: # Python 3 |
157 if self.embeddingComboBox.currentIndex() == 1: # Python 3 |
164 output = Python3Template.format( |
158 output = DataUriTemplates["Python3"].format( |
165 getpass.getuser(), |
159 getpass.getuser(), |
166 datetime.datetime.now().isoformat().split('.')[0], output) |
160 datetime.datetime.now().isoformat().split('.')[0], output) |
167 elif self.embeddingComboBox.currentIndex() == 2: # CSS |
161 elif self.embeddingComboBox.currentIndex() == 2: # CSS |
168 output = ('html, body {{ margin:0; padding:0; background: url({0})' |
162 output = DataUriTemplates["CSS"].format(output) |
169 ' no-repeat center center fixed; background-size:cover' |
|
170 ' }}'.format(output)) |
|
171 elif self.embeddingComboBox.currentIndex() == 3: # HTML |
163 elif self.embeddingComboBox.currentIndex() == 3: # HTML |
172 output = '<img src={0} alt="{1}" title="{1}"/>'.format( |
164 output = DataUriTemplates["HTML"].format( |
173 output, os.path.basename(filepath)) |
165 output, os.path.basename(filepath)) |
174 elif self.embeddingComboBox.currentIndex() == 4: # JS |
166 elif self.embeddingComboBox.currentIndex() == 4: # JS |
175 output = 'var embedded_file = window.atob({0}); '.format(output) |
167 output = DataUriTemplates["JavaScript"].format(output) |
176 elif self.embeddingComboBox.currentIndex() == 5: # QML |
168 elif self.embeddingComboBox.currentIndex() == 5: # QML |
177 output = 'Image {{ source: {0} }} '.format(output) |
169 output = DataUriTemplates["QML"].format(output) |
178 |
170 |
179 if self.encryptCheckBox.isChecked(): |
171 if self.encryptCheckBox.isChecked(): |
180 output = output.encode('rot_13') |
172 output = codecs.encode(output, 'rot_13') |
181 |
173 |
182 self.outputTextEdit.setPlainText(output) |
174 self.outputTextEdit.setPlainText(output) |
183 |
175 |
184 @pyqtSlot() |
176 @pyqtSlot() |
185 def on_copyButton_clicked(self): |
177 def on_copyButton_clicked(self): |
193 """ |
185 """ |
194 Private slot to handle the existence of some output text. |
186 Private slot to handle the existence of some output text. |
195 """ |
187 """ |
196 enable = bool(self.outputTextEdit.toPlainText()) |
188 enable = bool(self.outputTextEdit.toPlainText()) |
197 self.copyButton.setEnabled(enable) |
189 self.copyButton.setEnabled(enable) |
198 self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(enable) |
190 self.buttonBox.button( |
|
191 QDialogButtonBox.StandardButton.Ok).setEnabled(enable) |
199 |
192 |
200 @pyqtSlot(str) |
193 @pyqtSlot(str) |
201 def on_fileEdit_textChanged(self, txt): |
194 def on_filePicker_textChanged(self, txt): |
202 """ |
195 """ |
203 Private slot to handle the editing of the file name. |
196 Private slot to handle the editing of the file name. |
204 |
197 |
205 @param txt current file name (string) |
198 @param txt current file name |
|
199 @type str |
206 """ |
200 """ |
207 self.encodeButton.setEnabled(bool(txt) and os.path.isfile(txt)) |
201 self.encodeButton.setEnabled(bool(txt) and os.path.isfile(txt)) |
208 |
202 |
209 def __askMime(self): |
203 def __askMime(self): |
210 """ |
204 """ |
211 Private method to get the mime type from the user. |
205 Private method to get the mime type from the user. |
212 |
206 |
213 @return entered mime type (string) |
207 @return entered mime type |
|
208 @rtype str |
214 """ |
209 """ |
215 mimetypesList = [""] + list(sorted( |
210 mimetypesList = [""] + list(sorted( |
216 set(mimetypes.types_map.values()).union( |
211 set(mimetypes.types_map.values()).union( |
217 set(mimetypes.common_types.values())))) |
212 set(mimetypes.common_types.values())))) |
218 try: |
213 try: |