|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2017 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a dialog to enter data for an image markup. |
|
8 """ |
|
9 |
|
10 import contextlib |
|
11 |
|
12 from PyQt6.QtCore import pyqtSlot, QSize |
|
13 from PyQt6.QtGui import QImage, QImageReader |
|
14 from PyQt6.QtWidgets import QDialog, QDialogButtonBox |
|
15 |
|
16 from EricWidgets.EricPathPicker import EricPathPickerModes |
|
17 |
|
18 from .Ui_ImageMarkupDialog import Ui_ImageMarkupDialog |
|
19 |
|
20 |
|
21 class ImageMarkupDialog(QDialog, Ui_ImageMarkupDialog): |
|
22 """ |
|
23 Class implementing a dialog to enter data for an image markup. |
|
24 """ |
|
25 HtmlMode = 0 |
|
26 MarkDownMode = 1 |
|
27 RestMode = 2 |
|
28 |
|
29 def __init__(self, mode, parent=None): |
|
30 """ |
|
31 Constructor |
|
32 |
|
33 @param mode mode of the dialog |
|
34 @type int |
|
35 @param parent reference to the parent widget |
|
36 @type QWidget |
|
37 """ |
|
38 super().__init__(parent) |
|
39 self.setupUi(self) |
|
40 |
|
41 if mode == ImageMarkupDialog.MarkDownMode: |
|
42 self.sizeCheckBox.setEnabled(False) |
|
43 self.aspectRatioCheckBox.setEnabled(False) |
|
44 self.widthSpinBox.setEnabled(False) |
|
45 self.heightSpinBox.setEnabled(False) |
|
46 elif mode == ImageMarkupDialog.RestMode: |
|
47 self.titleEdit.setEnabled(False) |
|
48 |
|
49 self.__mode = mode |
|
50 self.__originalImageSize = QSize() |
|
51 |
|
52 filters = { |
|
53 'bmp': self.tr("Windows Bitmap File (*.bmp)"), |
|
54 'cur': self.tr("Windows Cursor File (*.cur)"), |
|
55 'dds': self.tr("DirectDraw-Surface File (*.dds)"), |
|
56 'gif': self.tr("Graphic Interchange Format File (*.gif)"), |
|
57 'icns': self.tr("Apple Icon File (*.icns)"), |
|
58 'ico': self.tr("Windows Icon File (*.ico)"), |
|
59 'jp2': self.tr("JPEG2000 File (*.jp2)"), |
|
60 'jpg': self.tr("JPEG File (*.jpg)"), |
|
61 'jpeg': self.tr("JPEG File (*.jpeg)"), |
|
62 'mng': self.tr("Multiple-Image Network Graphics File (*.mng)"), |
|
63 'pbm': self.tr("Portable Bitmap File (*.pbm)"), |
|
64 'pcx': self.tr("Paintbrush Bitmap File (*.pcx)"), |
|
65 'pgm': self.tr("Portable Graymap File (*.pgm)"), |
|
66 'png': self.tr("Portable Network Graphics File (*.png)"), |
|
67 'ppm': self.tr("Portable Pixmap File (*.ppm)"), |
|
68 'sgi': self.tr("Silicon Graphics Image File (*.sgi)"), |
|
69 'svg': self.tr("Scalable Vector Graphics File (*.svg)"), |
|
70 'svgz': self.tr("Compressed Scalable Vector Graphics File" |
|
71 " (*.svgz)"), |
|
72 'tga': self.tr("Targa Graphic File (*.tga)"), |
|
73 'tif': self.tr("TIFF File (*.tif)"), |
|
74 'tiff': self.tr("TIFF File (*.tiff)"), |
|
75 'wbmp': self.tr("WAP Bitmap File (*.wbmp)"), |
|
76 'webp': self.tr("WebP Image File (*.webp)"), |
|
77 'xbm': self.tr("X11 Bitmap File (*.xbm)"), |
|
78 'xpm': self.tr("X11 Pixmap File (*.xpm)"), |
|
79 } |
|
80 |
|
81 inputFormats = [] |
|
82 readFormats = QImageReader.supportedImageFormats() |
|
83 for readFormat in readFormats: |
|
84 with contextlib.suppress(KeyError): |
|
85 inputFormats.append(filters[bytes(readFormat).decode()]) |
|
86 inputFormats.sort() |
|
87 inputFormats.append(self.tr("All Files (*)")) |
|
88 if filters["png"] in inputFormats: |
|
89 inputFormats.remove(filters["png"]) |
|
90 inputFormats.insert(0, filters["png"]) |
|
91 self.imagePicker.setFilters(';;'.join(inputFormats)) |
|
92 self.imagePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) |
|
93 |
|
94 self.sizeCheckBox.setChecked(True) |
|
95 self.aspectRatioCheckBox.setChecked(True) |
|
96 |
|
97 msh = self.minimumSizeHint() |
|
98 self.resize(max(self.width(), msh.width()), msh.height()) |
|
99 |
|
100 self.__updateOkButton() |
|
101 |
|
102 def __updateOkButton(self): |
|
103 """ |
|
104 Private slot to set the state of the OK button. |
|
105 """ |
|
106 enable = bool(self.imagePicker.text()) |
|
107 if self.__mode == ImageMarkupDialog.MarkDownMode: |
|
108 enable = enable and bool(self.altTextEdit.text()) |
|
109 |
|
110 self.buttonBox.button( |
|
111 QDialogButtonBox.StandardButton.Ok).setEnabled(enable) |
|
112 |
|
113 @pyqtSlot(str) |
|
114 def on_imagePicker_textChanged(self, address): |
|
115 """ |
|
116 Private slot handling changes of the image path. |
|
117 |
|
118 @param address image address (URL or local path) |
|
119 @type str |
|
120 """ |
|
121 if address and "://" not in address: |
|
122 image = QImage(address) |
|
123 # load the file to set the size spin boxes |
|
124 if image.isNull(): |
|
125 self.widthSpinBox.setValue(0) |
|
126 self.heightSpinBox.setValue(0) |
|
127 self.__originalImageSize = QSize() |
|
128 self.__aspectRatio = 1 |
|
129 else: |
|
130 self.widthSpinBox.setValue(image.width()) |
|
131 self.heightSpinBox.setValue(image.height()) |
|
132 self.__originalImageSize = image.size() |
|
133 self.__aspectRatio = ( |
|
134 float(self.__originalImageSize.height()) / |
|
135 self.__originalImageSize.width() |
|
136 ) |
|
137 else: |
|
138 self.widthSpinBox.setValue(0) |
|
139 self.heightSpinBox.setValue(0) |
|
140 self.__originalImageSize = QSize() |
|
141 self.__aspectRatio = 1 |
|
142 |
|
143 self.__updateOkButton() |
|
144 |
|
145 @pyqtSlot(str) |
|
146 def on_altTextEdit_textChanged(self, txt): |
|
147 """ |
|
148 Private slot handling changes of the alternative text. |
|
149 |
|
150 @param txt alternative text |
|
151 @type str |
|
152 """ |
|
153 self.__updateOkButton() |
|
154 |
|
155 @pyqtSlot(bool) |
|
156 def on_sizeCheckBox_toggled(self, checked): |
|
157 """ |
|
158 Private slot to reset the width and height spin boxes. |
|
159 |
|
160 @param checked flag indicating the state of the check box |
|
161 @type bool |
|
162 """ |
|
163 if checked: |
|
164 self.widthSpinBox.setValue(self.__originalImageSize.width()) |
|
165 self.heightSpinBox.setValue(self.__originalImageSize.height()) |
|
166 |
|
167 @pyqtSlot(bool) |
|
168 def on_aspectRatioCheckBox_toggled(self, checked): |
|
169 """ |
|
170 Private slot to adjust the height to match the original aspect ratio. |
|
171 |
|
172 @param checked flag indicating the state of the check box |
|
173 @type bool |
|
174 """ |
|
175 if checked and self.__originalImageSize.isValid(): |
|
176 height = self.widthSpinBox.value() * self.__aspectRatio |
|
177 self.heightSpinBox.setValue(height) |
|
178 |
|
179 @pyqtSlot(int) |
|
180 def on_widthSpinBox_valueChanged(self, width): |
|
181 """ |
|
182 Private slot to adjust the height spin box. |
|
183 |
|
184 @param width width for the image |
|
185 @type int |
|
186 """ |
|
187 if ( |
|
188 self.aspectRatioCheckBox.isChecked() and |
|
189 self.widthSpinBox.hasFocus() |
|
190 ): |
|
191 height = width * self.__aspectRatio |
|
192 self.heightSpinBox.setValue(height) |
|
193 |
|
194 @pyqtSlot(int) |
|
195 def on_heightSpinBox_valueChanged(self, height): |
|
196 """ |
|
197 Private slot to adjust the width spin box. |
|
198 |
|
199 @param height height for the image |
|
200 @type int |
|
201 """ |
|
202 if ( |
|
203 self.aspectRatioCheckBox.isChecked() and |
|
204 self.heightSpinBox.hasFocus() |
|
205 ): |
|
206 width = height / self.__aspectRatio |
|
207 self.widthSpinBox.setValue(width) |
|
208 |
|
209 def getData(self): |
|
210 """ |
|
211 Public method to get the entered data. |
|
212 |
|
213 @return tuple containing the image address, alternative text, |
|
214 title text, flag to keep the original size, width and height |
|
215 @rtype tuple of (str, str, str, bool, int, int) |
|
216 """ |
|
217 return ( |
|
218 self.imagePicker.text(), |
|
219 self.altTextEdit.text(), |
|
220 self.titleEdit.text(), |
|
221 self.sizeCheckBox.isChecked(), |
|
222 self.widthSpinBox.value(), |
|
223 self.heightSpinBox.value(), |
|
224 ) |