1 from builtins
import range
6 from PyQt4.QtSvg
import QSvgRenderer
14 """ TextField for VispaWidget. 16 Text and title shown in VispaWidget are TextField object. 56 """ Sets autosizeFontFlag. 58 If flag is True and text does not fit in its given area the font size will be reduced to fit. 65 """ Sets autoTruncateTextFlag. 67 If flag is True the text will be truncated if it is too long to fit in given space. 74 """ Sets autoscale and autoscalKeepAspectRatio flags. 76 If autoscale flag is True the needed space is increased until text fits. 77 If keepAspectRatio flag is False the aspet ratio may change depending on output flags. 84 """ Sets font and if default height is not yet set default height will be set to font height. 100 """ Calculates font height for given font metrics object. 102 If no font metrics object is given one will be created for the current TextField font. 105 fm = QFontMetrics(self.
_font)
110 """ Sets preferred font size. 115 """ Sets min and max font point size for autosize font capability. 117 See setAutosizeFont(). 125 Width will be equal to getWidth() and height equal to getHeight(). 131 """ Sets preferred width for text output. 136 """ Sets preferred height for text output. 141 """ Calculates the space (width and height) needed to display text. 143 Depending on the flags set the size will be greater than the default size, 144 or the font size will be adjusted, 145 or the text will be truncated. 147 See setAutosizeFont(), setAutotruncate(), setAutoscale(), setDefaultWidth(), setDefaultHeight(). 168 """ Returns width calculated by calculateDimensions(). 173 """ Returns height calculated by calculateDimensions(). 179 """ Set Qt output flags for drawing text. 184 """ Returns set output flags. 189 """ Returns True if text was truncated. 196 """ Returns the font size the text will be drawn in. 208 """ Returns short version of text if it was truncated. 213 """ Evaluates whether the string was truncated or not. 215 If truncated it returns short version, else the whole text. 222 """ Returns True if no text or empty string is set. 229 """ Adjusts values for getWidth() and getHeight() so whole text fits in. 232 fm = QFontMetrics(self.
_font)
236 widthFits = heightFits =
False 241 self.
_width = neededRect.width()
242 self.
_height = neededRect.height()
245 while not widthFits
or not heightFits:
256 if neededRect.width() <= self.
_width:
259 if neededRect.height() <= self.
_height:
264 """ Decreases font so text fits in given widht and height. 266 if self.
_font ==
None:
267 logging.error(
"TextField.autosizeFont() - ERROR: 'font' not set, can't calculate font size")
274 font.setPointSizeF(size + 0.1 * decSize)
275 fm = QFontMetricsF(font)
277 if neededRect.width() > drawRect.width()
or neededRect.height() > drawRect.height():
281 for decSize
in range(0, 10):
282 font.setPointSizeF(size + 0.1 * decSize)
283 fm = QFontMetricsF(font)
285 if neededRect.width() > drawRect.width()
or neededRect.height() > drawRect.height():
293 """ Truncates text if it does not fit in given space. 296 text = QString(self.
_text)
300 fm = QFontMetricsF(font)
302 patterns = text.split(QRegExp(
'\\b'))
304 for pattern
in patterns:
305 short.append(pattern)
306 neededRect = fm.boundingRect(drawRect, self.
_outputFlags, short)
308 if neededRect.width() > drawRect.width()
or neededRect.height() > drawRect.height():
310 counter += len(pattern)
312 if counter < len(text):
317 def paint(self, painter, xPos, yPos, scale=1):
318 """ Draws text on given painter at given position. 320 If scale is given the text will be scaled accordingly. 325 painter.setBrush(Qt.NoBrush)
328 painter.setFont(self.
_font)
339 """ Class supporting random shapes, title and text field. 341 Title and text field content are stored in TextField objects. 342 You can influence the behavior of the widget by severals flags. 343 If you want the widget's size to fit the content size (increases / decreases widget) use enableAutosizing(). 344 You can also chose the decrease the text field's font size, if text does not fit in. See setTextFieldAutosizeFont(). 345 Additionally the text can be truncated instead using setTextFieldAutotruncateText(). 352 ROUNDRECT_RADIUS = 30
353 BACKGROUND_SHAPE =
'RECT' 359 HORIZONTAL_INNER_MARGIN = 5
360 VERTICAL_INNTER_MARGIN = 5
378 PEN_COLOR = QColor(128, 186, 224)
379 FILL_COLOR1 = QColor(188, 215, 241)
380 FILL_COLOR2 = QColor(242, 230, 242)
381 TITLE_COLOR = QColor(Qt.white)
387 SELECT_COLOR = QColor(
'darkblue')
389 SELECTABLE_FLAG =
True 390 FOCUSPOLICY = Qt.StrongFocus
391 SELECTED_FRAME_WIDTH = 2
393 AUTOPOSITIONIZE_WHEN_ZOOMING_FLAG =
True 395 TITLEFIELD_FONTSIZE = 12
396 COLOR_HEADER_BACKGROUND_FLAG =
True 397 USE_BACKGROUND_GRADIENT_FLAG =
True 399 TEXTFIELD_FONTSIZE = 12
400 TEXTFIELD_FONTSIZE_MIN = 2
401 TEXTFIELD_FONTSIZE_MAX = 20
402 TEXTFIELD_FLAGS = Qt.TextWordWrap
403 TEXTFIELD_AUTOSIZE_FONT_FLAG =
False 404 TEXTFIELD_AUTOTRUNCATE_TEXT_FLAG =
False 407 AUTOSIZE_KEEP_ASPECT_RATIO =
True 412 ARROW_SHAPE_BOTTOM = 2
413 ARROW_SHAPE_RIGHT = 3
461 ZoomableWidget.__init__(self, parent)
463 self.
setColors(self.PEN_COLOR, self.FILL_COLOR1, self.FILL_COLOR2)
465 self.
setShape(self.BACKGROUND_SHAPE)
478 """ Returns x coordinate the widget would have if zoom was set to 100%. 484 """ Returns x coordinate the widget would have if zoom was set to 100%. 490 """ Return scale factor of widget. 495 """ Sets widget's zoom. 497 ZoomableWidget.setZoom(self, zoom)
500 ZoomableWidget.resize(self, self.
width(), self.
height())
506 """ Returns pen color for this widget. 511 """ If True the position of this widget will be corrected according to it unzoomed position. 513 Prevents unexpected moving when zoom is enabled due to rounding errors. 518 """ If True the widget can be dragged using a pointing device. 520 If recursive is True also dragablitiy of all children will be set. 524 for child
in self.children():
525 if isinstance(child, VispaWidget):
526 child.setDragable(dragable,
True)
532 """ Sets colors of this widget. 534 The pen color will be used for drawing the widget's frame. 535 Fill color 1 and 2 will be used for background color gradient. 542 """ Sets shape of this widget. 544 Right now supported 'RECT' (default), 'ROUNDRECT', 'CIRCLE'. 549 """ In addition to normal shape this gives whole widget the shape of an arrow. 551 Possible values for direction are 561 """ Sets flag for auto resizing this widget. 563 If auto is True the size of this widget will be adjusted to widget size. 564 If keepAspectRatio is False the aspect ratio may change according to widget content. 574 self._titleField.setAutoscale(auto, keepAspectRatio)
576 self._textField.setAutoscale(auto, keepAspectRatio)
583 """ Returns True if auto resizing is enabled. 585 See enableAutosizing(). 590 """ Makes widget selectable if True. 595 self.setFocusPolicy(self.FOCUSPOLICY)
597 self.setFocusPolicy(Qt.NoFocus)
600 """ Returns True if widget can be selected. 611 """ If set to True the background of the header is painted using pen color. 621 """ If set to True the background color is painted using a QLinearGradient with the two fill colors. 630 def select(self, sel=True, multiSelect=False):
631 """ Marks this widget as selected and informs parent if parent is VispaWidetOwner. 633 If multiSelect is True e.g. due to a pressed Ctrl button while clicking (see mousePressEvent()), 634 the VispaWidgetOwner will also be informed and might not deselect all widgets. 648 if (multiSelect
or self.
isSelected())
and isinstance(self.parent(), VispaWidgetOwner):
649 self.parent().widgetSelected(self, multiSelect)
652 if isinstance(self.parent(), VispaWidgetOwner):
653 self.parent().widgetDoubleClicked(self)
656 """ Returns True if widget is selected. 664 """ Initializes title field. 666 Sets default flags for title field. 670 self._titleField.setDefaultWidth(self.
getDistance(
'titleFieldWidth', 1,
True))
671 self._titleField.setDefaultFontSize(self.TITLEFIELD_FONTSIZE)
672 self._titleField.setAutotruncate(
False)
675 self._titleField.setPenColor(self.TITLE_COLOR)
678 """ Returns True if test field has been set to a non empty string. 680 if self.
_titleField !=
None and not self._titleField.empty():
688 self._titleField.setFont(self.font())
689 self._titleField.setText(title)
698 return self._titleField.text()
704 self._textField.setFontSizeRange(self.TEXTFIELD_FONTSIZE_MIN, self.TEXTFIELD_FONTSIZE_MAX)
705 self._textField.setDefaultWidth(self.
getDistance(
'textFieldWidth', 1,
True))
706 self._textField.setDefaultHeight(self.
getDistance(
'textFieldHeight', 1,
True))
707 self._textField.setDefaultFontSize(self.TEXTFIELD_FONTSIZE)
708 self._textField.setAutosizeFont(self.TEXTFIELD_AUTOSIZE_FONT_FLAG)
709 self._textField.setAutotruncate(self.TEXTFIELD_AUTOTRUNCATE_TEXT_FLAG)
710 self._textField.setOutputFlags(self.TEXTFIELD_FLAGS)
714 """ Returns True if text field text has been set to an non empty string. 716 if self.
_textField !=
None and not self._textField.empty():
721 """ Sets text for text field. 725 self._textField.setFont(self.font())
726 self._textField.setText(text)
730 self.setToolTip(self._textField.text())
734 """ Returns TextField object belonging to text field. 739 """ Returns text of text field. 742 return self._textField.text()
746 """ Sets auto resize flag of text field. 749 self._textField.setAutosizeFont(auto)
753 """ Sets auto truncate flag of text field. 756 self._textField.setAutotruncate(auto)
760 QWidget.setMaximumSize(self, *attr)
764 QWidget.setMinimumSize(self, *attr)
768 """ Calculates needed space for widget content. 783 titleFieldWidth = self.
getDistance(
'titleFieldWidth', 1)
784 titleFieldHeight += self.
getDistance(
'titleFieldHeight', 1)
789 textFieldWidth = self._textField.getWidth()
790 textFieldHeight += self._textField.getHeight()
794 if self._bodyWidget.parent() != self:
797 sh = self._bodyWidget.sizeHint()
798 bodyWidgetWidth = sh.width()
799 bodyWidgetHeight = sh.height()
802 bodyWidth =
max(textFieldWidth, bodyWidgetWidth, imageSizeF.width())
803 bodyHeight =
max(textFieldHeight, bodyWidgetHeight, imageSizeF.height())
805 if titleIsSet
and bodyHeight != 0:
807 neededHeight += self.
getDistance(
'bottomMargin', 1)
809 neededWidth +=
max(bodyWidth, titleFieldWidth)
810 neededHeight += titleFieldHeight + bodyHeight
813 maxWidth = self.maximumSize().
width()
814 maxHeight = self.maximumSize().
height()
816 maxScaleWidth =
min(1.0, 1.0 * maxWidth/neededWidth)
817 maxScaleHeight =
min(1.0, 1.0 * maxHeight/neededHeight)
818 if maxScaleWidth != 1.0
or maxScaleHeight != 1.0:
822 scale =
min(maxScaleWidth, maxScaleHeight)
824 neededHeight *= scale
826 return QSize(
max(self.minimumSize().
width(), neededWidth),
max(self.minimumSize().
height(), neededHeight))
831 ZoomableWidget.resize(self, self.
width(), self.
height())
834 """ Calculates scale factors and resizes widget accordingly. 836 Calculates by which factor width and height of this widget should be scaled 837 depending on the content of the widget. 838 If skipSizeHint is True only the resize part is performed. 843 neededWidth = neededSpace.width()
844 neededHeight = neededSpace.height()
856 ZoomableWidget.resize(self, self.
width(), self.
height())
863 """ Checks which components have to be regarded for size calculation. 865 If you want to make sure this function is called next time a distance is requested use call scheduleRearangeContent(). 866 This function is stronger than autosize, as it also recalculates dimensions of children (e.g. text field). 867 If content did not change autosize is probably the better approach. 877 self._textField.setDefaultWidth(self.
getDistance(
'textFieldWidth', 1,
True))
878 self._textField.setDefaultHeight(self.
getDistance(
'textFieldHeight', 1,
True))
879 self._textField.calculateDimensions()
881 self._titleField.setDefaultWidth(self.
getDistance(
'titleFieldWidth', 1,
True))
882 self._titleField.setDefaultHeight(self.
getDistance(
'titleFieldHeight', 1,
True))
883 self._titleField.calculateDimensions()
890 """ Sets distancesHaveToBeRecalculatedFlag to True. 892 Next time defineDistances() is called distances are recalculated even if zoom has not changed. 897 """ Flag disables any rearanging. 902 """ Makes sure rearangeContent() will be called next time a distance is requested. 904 See rearangeContent(). 909 """ Calls rearangeContent() if needed. 918 ZoomableWidget.showEvent(self, event)
921 """ Returns dictionary containing distances as defined in defineDistances(). 926 """ Defines supported distances. 928 The distances are needed for drawing widget content and are scaled according to current scale / zoom factors. 929 The distances are stored in an dictionary (see distances()). 964 self.
_distances[
'frameTop'] = self.ARROW_SIZE * scale
969 self.
_distances[
'width'] += self.ARROW_SIZE * scale
972 self.
_distances[
'height'] += self.ARROW_SIZE * scale
974 self.
_distances[
'frameLeft'] = self.ARROW_SIZE * scale
978 self.
_distances[
'topMargin'] = self.TOP_MARGIN * scale
979 self.
_distances[
'leftMargin'] = self.LEFT_MARGIN * scale
980 self.
_distances[
'bottomMargin'] = self.BOTTOM_MARGIN * scale
981 self.
_distances[
'rightMargin'] = self.RIGHT_MARGIN * scale
983 self.
_distances[
'horizontalInnerMargin'] = self.HORIZONTAL_INNER_MARGIN * scale
984 self.
_distances[
'verticalInnerMargin'] = self.VERTICAL_INNTER_MARGIN * scale
989 self.
_distances[
'titleFieldWidth'] = self._titleField.getWidth() * scale
990 self.
_distances[
'titleFieldHeight'] = self._titleField.getHeight() * scale
1003 self.
_distances[
'textFieldWidth'] = self._textField.getWidth() * scale
1004 self.
_distances[
'textFieldHeight'] = self._textField.getHeight() * scale
1013 """ Gets the length of the element called 'name'. 1025 logging.warning(self.__class__.__name__ +
": getdistance() - Unknown distance '"+ name +
"'")
1029 """ Returns width of this widget. 1036 """ Returns height of this widget. 1044 """ Returns True if this point is part of the tile field, otherwise False is returned. 1053 """ Draws background for rectangular shape. 1059 myRect = QRectF(l, t, r - l , b - t)
1061 self._backgroundShapePath.addRect(myRect)
1064 """ Draws background for circular shape. 1071 self._backgroundShapePath.addEllipse(0.5 * (w -r), 0.5 * (h -r), r, r)
1074 """ Draws background for rectangular shape with rounded corners. 1076 r = (self.ROUNDRECT_RADIUS) * self.
_scale 1087 r =
min(r, f * h, f * w)
1090 self._backgroundShapePath.moveTo(w, r + t)
1091 self._backgroundShapePath.arcTo(w - r, t, r, r, 0, 90)
1092 self._backgroundShapePath.lineTo(r + l, t)
1093 self._backgroundShapePath.arcTo(l, t, r, r, 90, 90)
1094 self._backgroundShapePath.lineTo(l, h - r)
1095 self._backgroundShapePath.arcTo(l, h - r, r, r, 180, 90)
1096 self._backgroundShapePath.lineTo(w - r, h)
1097 self._backgroundShapePath.arcTo(w - r, h - r, r, r, 270, 90)
1098 self._backgroundShapePath.closeSubpath()
1103 logging.warning(self.__class__.__name__ +
": defineArrowBackgroundShape() - Upgrade your Qt version at least to 4.3 to use this feature. Aborting...")
1110 offset = (self.ROUNDRECT_RADIUS) * self.
_scale * 0.2
1111 p = self._backgroundShapePath.toFillPolygon()
1115 arrowPath = QPainterPath()
1117 arrowPath.lineTo(0.5 * self.
width(), 1)
1119 arrowPath.closeSubpath()
1121 arrowPath = QPainterPath()
1123 arrowPath.lineTo(self.
width(), 0.5 * self.
height())
1125 arrowPath.closeSubpath()
1127 arrowPath = QPainterPath()
1129 arrowPath.lineTo(0.5 * self.
width(), self.
height())
1131 arrowPath.closeSubpath()
1133 arrowPath = QPainterPath()
1135 arrowPath.lineTo(1, 0.5 * self.
height())
1137 arrowPath.closeSubpath()
1143 """ Color background of title in frame pen color. 1151 topRectPath = QPainterPath()
1156 painter.drawPath(headerPath)
1161 backgroundShapePolygon = self._backgroundShapePath.toFillPolygon()
1162 headerPolygonPoints = []
1167 selectedWidth = (self.SELECTED_FRAME_WIDTH +4) * self.
scale()
1170 while i < backgroundShapePolygon.count():
1171 thisP = backgroundShapePolygon.value(i)
1174 if thisP.y() <= self.
getDistance(
'titleFieldBottom') - selectedWidth:
1175 if thisP.y() > headerBottom:
1176 headerBottom = thisP.y()
1177 elif thisP.y() > nearlyBottom:
1178 nearlyBottom = thisP.y()
1179 headerPolygonPoints.append(thisP)
1180 headerPolygon = QPolygonF(headerPolygonPoints)
1182 painter.setPen(Qt.NoPen)
1184 titleBgPath = QPainterPath()
1185 titleBgPath.addPolygon(headerPolygon)
1186 painter.drawPath(titleBgPath)
1188 headerBottom = nearlyBottom
1196 if headerBottom == 0:
1197 headerBottom = self.SELECTED_FRAME_WIDTH - 2
1198 xBorder = self.SELECTED_FRAME_WIDTH - 2
1201 painter.drawRect(QRectF(xBorder, headerBottom, self.
width() - 2 * xBorder - 2, self.
getDistance(
'titleFieldBottom') - headerBottom))
1204 """ Tells TextField object of title to draw title on widget. 1210 painter.setPen(QPen())
1214 """ Tells TextField object of text field to draw title on widget. 1219 painter.setPen(QPen())
1223 """ This function calls drawTextField() and drawImage(). 1225 Inheriting classes should overwrite this function if they wish to draw different things or alter the order of drawing. 1233 frame_width = self.SELECTED_FRAME_WIDTH
1234 return QRect(frame_width, self.
getDistance(
"titleFieldBottom"),
1235 self.
width() - 2* frame_width -1,
1239 """ Reacts on paint event and calls paint() method. 1242 painter = QPainter(self)
1243 if isinstance(self.parent(), VispaWidget):
1244 painter.setClipRegion(event.region().intersected(QRegion(self.parent().
contentRect().translated(- self.pos()))))
1246 painter.setClipRegion(event.region())
1248 if self.
zoom() > 30:
1249 painter.setRenderHint(QPainter.Antialiasing)
1251 painter.setRenderHint(QPainter.Antialiasing,
False)
1254 ZoomableWidget.paintEvent(self, event)
1257 """ Takes care of painting widget content on given painter. 1264 backgroundBrush = QLinearGradient(0, self.
getDistance(
'titleFieldBottom'), 0, self.
height())
1265 backgroundBrush.setColorAt(0, self.
fillColor1)
1266 backgroundBrush.setColorAt(1, self.
fillColor2)
1269 painter.pen().setJoinStyle(Qt.RoundJoin)
1270 painter.setBrush(backgroundBrush)
1289 framePen = QPen(self.SELECT_COLOR)
1290 framePen.setWidth(self.SELECTED_FRAME_WIDTH)
1291 painter.setPen(framePen)
1292 painter.setBrush(Qt.NoBrush)
1307 """ Register mouse offset for dragging and calls select(). 1309 parentIsVispaWidgetOwner = isinstance(self.parent(), VispaWidgetOwner)
1310 if event.modifiers() == Qt.ControlModifier:
1313 elif parentIsVispaWidgetOwner
and not self.
isSelected():
1321 if parentIsVispaWidgetOwner:
1322 self.parent().initWidgetMovement(self)
1325 """ Call dragWidget(). 1328 if bool(event.buttons() & Qt.LeftButton):
1329 self.
dragWidget(self.mapToParent(event.pos()))
1336 self.emit(SIGNAL(
"dragFinished"))
1339 """ Calls delete() method if backspace or delete key is pressed when widget has focus. 1341 if event.key() == Qt.Key_Backspace
or event.key() == Qt.Key_Delete:
1342 if hasattr(self.parent(),
"multiSelectEnabled")
and self.parent().multiSelectEnabled()
and \
1343 hasattr(self.parent(),
"selectedWidgets")
and len(self.parent().selectedWidgets()) > 1:
1345 self.parent().setFocus(Qt.OtherFocusReason)
1346 QCoreApplication.instance().sendEvent(self.parent(), event)
1348 self.emit(SIGNAL(
"deleteButtonPressed"))
1352 self.parent().setFocus(Qt.OtherFocusReason)
1353 QCoreApplication.instance().sendEvent(self.parent(), event)
1356 """ Deletes this widget. 1359 logging.warning(self.__class__.__name__ +
": delete() - Tried to remove undeletable widget. Aborting...")
1362 if isinstance(self.parent(), VispaWidgetOwner):
1363 self.parent().widgetAboutToDelete(self)
1366 self.emit(SIGNAL(
"widgetDeleted"))
1373 """ Returns position from before previous drag operation. 1375 E.g. used for undo function. 1381 """ Perform dragging when user moves cursor while left mouse button is pressed. 1391 if isinstance(self.parent(), VispaWidgetOwner):
1392 self.parent().widgetDragged(self)
1395 """ Move widgt to new position. 1397 You can either give x and y coordinates as parameters or a QPosition object. 1399 if len(target) == 1:
1401 targetX = target[0].x()
1402 targetY = target[0].y()
1412 QWidget.move(self, targetX, targetY)
1418 """ The given image will be shown in the centre of the widget. 1420 Currently supported image types are QPixmap and QSvgRenderer. 1425 """ Draws image onto the widget's centre. See setImage(). 1430 if isinstance(self.
_image, QSvgRenderer):
1432 self._image.render(painter, self.
imageRectF())
1433 elif isinstance(self.
_image, QPixmap):
1435 painter.drawPixmap(self.
imageRectF(), self.
_image, QRectF(self._image.rect()))
1441 """ Returns QSizeF object representing the unzoomed size of the image. See setImage(). 1445 if isinstance(self.
_image, QSvgRenderer):
1446 return QSizeF(self._image.defaultSize())
1447 if isinstance(self.
_image, QPixmap):
1448 return QSizeF(self._image.size())
1449 logging.warning(self.__class__.__name__ +
": imageSizeF() - Unknown image type.")
1453 """ Returns draw area as QRectF for drawImage. 1455 if not width
or not height:
1457 width = size.width()
1458 height = size.height()
1460 if width > self.
width()
or height > self.
height():
1461 widthScale = 1.0 * self.
width() / width
1462 heightScale = 1.0 *self.
height() / height
1463 scale =
min(widthScale, heightScale)
1467 rect = QRectF((self.
width() - width) * 0.5, (self.
height() - height + self.
getDistance(
"titleFieldBottom")) * 0.5, width, height)
1472 return QRect(self.x(), self.y(), self.
width(), self.
height())
1475 """ Accepts any QWidget and displays into the body section. 1477 The body section is below the header. 1480 self._bodyWidget.setParent(self)
1481 if self.isVisible():
1482 self._bodyWidget.setVisible(
True)
1486 """ Returns body widget if there is one or None otherwise.
def setAutotruncate(self, auto)
def setDefaultWidth(self, width)
def setAutosizeFont(self, auto)
def calculateDimensions(self)
def getDrawRect(self, scale=1)
def setPenColor(self, color)
_autoscaleKeepAspectRatioFlag
def setDefaultFontSize(self, fontSize)
def setAutoscale(self, auto, keepAspectRatio)
def paint(self, painter, xPos, yPos, scale=1)
def getFontHeight(self, fm=None)
def setDefaultHeight(self, height)
def setFontSizeRange(self, minFontSize, maxFontSize)
def setOutputFlags(self, flags)