eric6/E5Gui/E5MapWidget.py

changeset 6942
2602857055c5
parent 6645
ad476851d7e0
child 7198
684261ef2165
equal deleted inserted replaced
6941:f99d60d6b59b 6942:2602857055c5
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2014 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a base class for showing a document map.
8 """
9
10 from __future__ import unicode_literals
11
12 from PyQt5.QtCore import Qt, QSize, QRect, QCoreApplication
13 from PyQt5.QtGui import QColor, QBrush, QPainter
14 from PyQt5.QtWidgets import QWidget, QAbstractScrollArea
15
16 from Globals import qVersionTuple
17
18
19 class E5MapWidget(QWidget):
20 """
21 Class implementing a base class for showing a document map.
22 """
23 def __init__(self, parent=None):
24 """
25 Constructor
26
27 @param parent reference to the parent widget (QWidget)
28 """
29 super(E5MapWidget, self).__init__(parent)
30 self.setAttribute(Qt.WA_OpaquePaintEvent)
31
32 self.__width = 14
33 self.__lineBorder = 1
34 self.__lineHeight = 2
35 self.__backgroundColor = QColor("#e7e7e7")
36 self.__setSliderColor()
37
38 self._master = None
39 self.__enabled = False
40 self.__rightSide = True
41
42 if parent is not None and isinstance(parent, QAbstractScrollArea):
43 self.setMaster(parent)
44
45 def __setSliderColor(self):
46 """
47 Private method to set the slider color depending upon the background
48 color.
49 """
50 if self.__backgroundColor.toHsv().value() < 128:
51 # dark background, use white slider
52 self.__sliderColor = Qt.white
53 else:
54 # light background, use black slider
55 self.__sliderColor = Qt.black
56
57 def __updateMasterViewportWidth(self):
58 """
59 Private method to update the master's viewport width.
60 """
61 if self._master:
62 if self.__enabled:
63 width = self.__width
64 else:
65 width = 0
66 if self.__rightSide:
67 self._master.setViewportMargins(0, 0, width, 0)
68 else:
69 self._master.setViewportMargins(width, 0, 0, 0)
70
71 def setMaster(self, master):
72 """
73 Public method to set the map master widget.
74
75 @param master map master widget (QAbstractScrollArea)
76 """
77 self._master = master
78 self._master.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
79 self._master.verticalScrollBar().valueChanged.connect(self.update)
80 self._master.verticalScrollBar().rangeChanged.connect(self.update)
81 self.__updateMasterViewportWidth()
82
83 def setWidth(self, width):
84 """
85 Public method to set the widget width.
86
87 @param width widget width (integer)
88 """
89 if width != self.__width:
90 self.__width = max(6, width) # minimum width 6 pixels
91 self.__updateMasterViewportWidth()
92 self.update()
93
94 def width(self):
95 """
96 Public method to get the widget's width.
97
98 @return widget width (integer)
99 """
100 return self.__width
101
102 def setMapPosition(self, onRight):
103 """
104 Public method to set, whether the map should be shown to the right or
105 left of the master widget.
106
107 @param onRight flag indicating to show the map on the right side of
108 the master widget
109 @type bool
110 """
111 if onRight != self.__rightSide:
112 self.__rightSide = onRight
113 self.__updateMasterViewportWidth()
114 self.update()
115
116 def isOnRightSide(self):
117 """
118 Public method to test, if the map is shown on the right side of the
119 master widget.
120
121 @return flag indicating that the map is to the right of the master
122 widget
123 @rtype bool
124 """
125 return self.__rightSide
126
127 def setLineDimensions(self, border, height):
128 """
129 Public method to set the line (indicator) dimensions.
130
131 @param border border width on each side in x-direction (integer)
132 @param height height of the line in pixels (integer)
133 """
134 if border != self.__lineBorder or height != self.__lineHeight:
135 self.__lineBorder = max(1, border) # min border 1 pixel
136 self.__lineHeight = max(1, height) # min height 1 pixel
137 self.update()
138
139 def lineDimensions(self):
140 """
141 Public method to get the line (indicator) dimensions.
142
143 @return tuple with border width (integer) and line height (integer)
144 """
145 return self.__lineBorder, self.__lineHeight
146
147 def setEnabled(self, enable):
148 """
149 Public method to set the enabled state.
150
151 @param enable flag indicating the enabled state (boolean)
152 """
153 if enable != self.__enabled:
154 self.__enabled = enable
155 self.setVisible(enable)
156 self.__updateMasterViewportWidth()
157
158 def isEnabled(self):
159 """
160 Public method to check the enabled state.
161
162 @return flag indicating the enabled state (boolean)
163 """
164 return self.__enabled
165
166 def setBackgroundColor(self, color):
167 """
168 Public method to set the widget background color.
169
170 @param color color for the background (QColor)
171 """
172 if color != self.__backgroundColor:
173 self.__backgroundColor = color
174 self.__setSliderColor()
175 self.update()
176
177 def backgroundColor(self):
178 """
179 Public method to get the background color.
180
181 @return background color (QColor)
182 """
183 return QColor(self.__backgroundColor)
184
185 def sizeHint(self):
186 """
187 Public method to give an indication about the preferred size.
188
189 @return preferred size (QSize)
190 """
191 return QSize(self.__width, 0)
192
193 def paintEvent(self, event):
194 """
195 Protected method to handle a paint event.
196
197 @param event paint event (QPaintEvent)
198 """
199 # step 1: fill the whole painting area
200 painter = QPainter(self)
201 painter.fillRect(event.rect(), self.__backgroundColor)
202
203 # step 2: paint the indicators
204 self._paintIt(painter)
205
206 # step 3: paint the slider
207 if self._master:
208 penColor = self.__sliderColor
209 painter.setPen(penColor)
210 brushColor = Qt.transparent
211 painter.setBrush(QBrush(brushColor))
212 painter.drawRect(self.__generateSliderRange(
213 self._master.verticalScrollBar()))
214
215 def _paintIt(self, painter):
216 """
217 Protected method for painting the widget's indicators.
218
219 Note: This method should be implemented by subclasses.
220
221 @param painter reference to the painter object (QPainter)
222 """
223 pass
224
225 def mousePressEvent(self, event):
226 """
227 Protected method to handle a mouse button press.
228
229 @param event reference to the mouse event (QMouseEvent)
230 """
231 if event.button() == Qt.LeftButton and self._master:
232 vsb = self._master.verticalScrollBar()
233 value = self.position2Value(event.pos().y() - 1)
234 vsb.setValue(value - 0.5 * vsb.pageStep()) # center on page
235 self.__mousePressPos = None
236
237 def mouseMoveEvent(self, event):
238 """
239 Protected method to handle a mouse moves.
240
241 @param event reference to the mouse event (QMouseEvent)
242 """
243 if event.buttons() & Qt.LeftButton and self._master:
244 vsb = self._master.verticalScrollBar()
245 value = self.position2Value(event.pos().y() - 1)
246 vsb.setValue(value - 0.5 * vsb.pageStep()) # center on page
247
248 def wheelEvent(self, event):
249 """
250 Protected slot handling mouse wheel events.
251
252 @param event reference to the wheel event (QWheelEvent)
253 """
254 if qVersionTuple() >= (5, 0, 0):
255 isVertical = event.angleDelta().x() == 0
256 else:
257 isVertical = event.orientation() == Qt.Vertical
258 if self._master and \
259 event.modifiers() == Qt.NoModifier and \
260 isVertical:
261 QCoreApplication.sendEvent(self._master.verticalScrollBar(), event)
262
263 def calculateGeometry(self):
264 """
265 Public method to recalculate the map widget's geometry.
266 """
267 if self._master:
268 cr = self._master.contentsRect()
269 vsb = self._master.verticalScrollBar()
270 if vsb.isVisible():
271 vsbw = vsb.contentsRect().width()
272 else:
273 vsbw = 0
274 left, top, right, bottom = self._master.getContentsMargins()
275 if right > vsbw:
276 vsbw = 0
277 if self.__rightSide:
278 self.setGeometry(
279 QRect(cr.right() - self.__width - vsbw, cr.top(),
280 self.__width, cr.height()))
281 else:
282 self.setGeometry(
283 QRect(0, cr.top(), self.__width, cr.height()))
284 self.update()
285
286 def scaleFactor(self, slider=False):
287 """
288 Public method to determine the scrollbar's scale factor.
289
290 @param slider flag indicating to calculate the result for the slider
291 (boolean)
292 @return scale factor (float)
293 """
294 if self._master:
295 delta = 0 if slider else 2
296 vsb = self._master.verticalScrollBar()
297 posHeight = vsb.height() - delta - 1
298 valHeight = vsb.maximum() - vsb.minimum() + vsb.pageStep()
299 return float(posHeight) / valHeight
300 else:
301 return 1.0
302
303 def value2Position(self, value, slider=False):
304 """
305 Public method to convert a scrollbar value into a position.
306
307 @param value value to convert (integer)
308 @param slider flag indicating to calculate the result for the slider
309 (boolean)
310 @return position (integer)
311 """
312 if self._master:
313 offset = 0 if slider else 1
314 vsb = self._master.verticalScrollBar()
315 return (value - vsb.minimum()) * self.scaleFactor(slider) + offset
316 else:
317 return value
318
319 def position2Value(self, position, slider=False):
320 """
321 Public method to convert a position into a scrollbar value.
322
323 @param position scrollbar position to convert (integer)
324 @param slider flag indicating to calculate the result for the slider
325 (boolean)
326 @return scrollbar value (integer)
327 """
328 if self._master:
329 offset = 0 if slider else 1
330 vsb = self._master.verticalScrollBar()
331 return vsb.minimum() + max(
332 0, (position - offset) / self.scaleFactor(slider))
333 else:
334 return position
335
336 def generateIndicatorRect(self, position):
337 """
338 Public method to generate an indicator rectangle.
339
340 @param position indicator position (integer)
341 @return indicator rectangle (QRect)
342 """
343 return QRect(self.__lineBorder, position - self.__lineHeight // 2,
344 self.__width - self.__lineBorder, self.__lineHeight)
345
346 def __generateSliderRange(self, scrollbar):
347 """
348 Private method to generate the slider rectangle.
349
350 @param scrollbar reference to the vertical scrollbar (QScrollBar)
351 @return slider rectangle (QRect)
352 """
353 pos1 = self.value2Position(scrollbar.value(), slider=True)
354 pos2 = self.value2Position(scrollbar.value() + scrollbar.pageStep(),
355 slider=True)
356 return QRect(0, pos1, self.__width - 1, pos2 - pos1)

eric ide

mercurial