E5Gui/E5MapWidget.py

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

eric ide

mercurial