Results 1 to 3 of 3

Thread: Calculating best bounding box for text

  1. #1
    Join Date
    Aug 2010
    Posts
    99
    Thanks
    3
    Qt products
    Qt4
    Platforms
    Windows

    Default Calculating best bounding box for text

    I wanted to make a tooltip "bubble" or "balloon" for my app which can pop up in my app's status bar.
    Since it appears that the only way to get such a widget is in the Window's taskbar (using QSystemTrayIcon), i decided to make my own.

    I have it mostly working, but one thing i do not know how to do is make it resize so that it fits all the text but keeps a decent aspect ratio (i.e. does not get extremely long or tall).

    This is what i have so far:


    I thought i could get the bounding box of the text within an initial rect, with Qt::TextWordWrap set, then check it's aspect ratio. If it is not right try again with a bigger box until i get one the right size.
    I came up with this code:
    Qt Code:
    1. QRect findBestSize(const QFontMetrics& fontMetrics, const String& message) {
    2. QRect rect = fontMetrics.boundingRect(0, 0, 100, 50,
    3. Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap, message);
    4. float ratio = float(rect.width())/float(rect.height());
    5. while(ratio < 1.5 || ratio > 2.0) {
    6. rect.adjust(0, 0, 10, 5);
    7. rect = fontMetrics.boundingRect(0, 0, rect.width(), rect.height(),
    8. Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap, message);
    9. ratio = float(rect.width())/float(rect.height());
    10. }
    11. return rect;
    12. }
    To copy to clipboard, switch view to plain text mode 

    But it doesn't work. And i am not sure what boundingRect() actually does. What is the rect that you give it and what rect does it return?
    I am not even sure how my code should work, but i am thinking that there are many different sized rects which can contain the text (i.e. such that the text wraps to two lines, three lines, etc) and i want the rect which is about 3:1 ratio.


    Here is the full code below:
    Qt Code:
    1. // File: BalloonTip.h
    2.  
    3. #ifndef BALLOON_TIP_H
    4. #define BALLOON_TIP_H
    5.  
    6. class BalloonTip : public QWidget {
    7. private:
    8. static const QBrush backgroundBrush;
    9. static const QPen outlinePen;
    10. static const int textPadding = 6;
    11. static const int arrowHeight = 20; // That triangle bit at the bottom
    12. QTimer expireTimer;
    13. QWidget* owner; // TODO: think of a better name.
    14. public:
    15. BalloonTip();
    16. ~BalloonTip() {}
    17.  
    18. void setOwner(QWidget* widget) { owner = widget; }
    19. void showMessage(const String& message, int timeout/*ms*/);
    20. void showMessage(QWidget* widget, const String& message, int timeout/*ms*/) {
    21. setOwner(widget); showMessage(message, timeout);
    22. }
    23. protected:
    24. void paintEvent(QPaintEvent*);
    25. };
    26.  
    27. #endif // BALLOON_TIP_H
    28.  
    29.  
    30.  
    31. /**************************************************************/
    32.  
    33. // File: BalloonTip.cpp
    34.  
    35. #include "BalloonTip.h"
    36.  
    37. const QBrush BalloonTip::backgroundBrush(QColor(255, 255, 225));
    38. const QPen BalloonTip::outlinePen(Qt::black, 1);
    39.  
    40. BalloonTip::BalloonTip() :
    41. QWidget(NULL, Qt::ToolTip | Qt::FramelessWindowHint),
    42. expireTimer(), owner(NULL) {
    43. setAttribute(Qt::WA_TranslucentBackground);
    44. expireTimer.setSingleShot(true);
    45. connect(&expireTimer, SIGNAL(timeout()), this, SLOT(hide()));
    46. }
    47.  
    48. /* TODO: Get this working. What it should do is start at a minimum size,
    49.   100,50 and increase the bounding box until all the text fits in AND
    50.   the aspect ration is between 1.5 and 2.0
    51. QRect findBestSize(const QFontMetrics& fontMetrics, const String& message) {
    52.   QRect rect = fontMetrics.boundingRect(0, 0, 100, 50,
    53.   Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap, message);
    54.   float ratio = float(rect.width())/float(rect.height());
    55.   while(ratio < 1.5 || ratio > 2.0) {
    56.   rect.adjust(0, 0, 10, 5);
    57.   rect = fontMetrics.boundingRect(0, 0, rect.width(), rect.height(),
    58.   Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap, message);
    59.   ratio = float(rect.width())/float(rect.height());
    60.   }
    61.   return rect;
    62. }*/
    63.  
    64. void BalloonTip::showMessage(const String& message, int timeout/*ms*/) {
    65. if (!owner) return;
    66.  
    67. QPoint ownerPos = owner->mapToGlobal(QPoint(0,0));
    68. int width = (textPadding*2)+200; // Hard-code size for now
    69. int height = (textPadding*2)+60+arrowHeight;
    70. int x = ownerPos.x() - (width-15-(owner->width()/2));
    71. int y = ownerPos.y() - height;
    72.  
    73. setWindowTitle(message);
    74. move(x, y); resize(width, height);
    75. expireTimer.start(timeout);
    76. show();
    77. update(); // Force redraw if it is already showing
    78. }
    79.  
    80. /*------------------------------------------------------------------+
    81. | Do custom painting. The entire rect is filled with the background |
    82. | colour and the clip region is used to make an outline path. |
    83. +------------------------------------------------------------------*/
    84. void BalloonTip::paintEvent(QPaintEvent*) {
    85. QPainter painter(this);
    86. const int roundness = 12;
    87. const int rectBase = height()-arrowHeight;
    88.  
    89. painter.setRenderHint(QPainter::Antialiasing);
    90.  
    91. QPoint p1(width()-35, rectBase-2);
    92. QPoint p2(width()-15, height());
    93. QPoint p3(width()-15, rectBase-2);
    94. QPolygon triangle;
    95. triangle << p1 << p2 << p3;
    96.  
    97. // TODO: This doesn't look quite right when drawing the bottom
    98. // triangle bit. I need to erase the border of the rectangle
    99. // in the triangle area, but anti-aliasing means that the triangle's
    100. // border then slighly overlaps.
    101. painter.setBrush(backgroundBrush);
    102. painter.setPen(outlinePen);
    103. painter.drawRoundedRect(1, 1, width()-2, rectBase-2, roundness, roundness);
    104. painter.setPen(Qt::transparent);
    105. painter.drawPolygon(triangle);
    106. painter.setPen(outlinePen);
    107. painter.drawLine(p1, p2);
    108. painter.drawLine(p2, p3);
    109.  
    110. painter.drawText(textPadding, textPadding,
    111. width()-textPadding, rectBase-textPadding,
    112. Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap,
    113. windowTitle());
    114. }
    To copy to clipboard, switch view to plain text mode 

    Can anyone help me?
    thanks
    [Window Detective] - Windows UI spy utility
    [War Thunder]

  2. #2
    Join Date
    Jan 2006
    Location
    Germany
    Posts
    4,380
    Thanks
    19
    Thanked 1,005 Times in 913 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows Symbian S60
    Wiki edits
    5

    Default Re: Calculating best bounding box for text

    The problem seems to be that you alter the rect which is given back from boundingRect. I would do it like that: Start with a fix width (= minimum widht), use boundingRect to get the hight for the with. If it is not in the ratio you wont only adjust the with and let the height recalculated. Not tested, but:
    Qt Code:
    1. int width = 100;
    2. QRect rect = fontMetrics.boundingRect(0, 0, width, 0,
    3. Qt::AlignLeft | Qt::TextWordWrap, message);
    4. float ratio = float(rect.width())/float(rect.height());
    5. while(ratio < 1.5 || ratio > 2.0) {
    6. if (ratio < 1.5)
    7. width += 15;
    8. else
    9. width -= 15;
    10. rect = fontMetrics.boundingRect(0, 0, width, 0,
    11. Qt::AlignLeft | Qt::TextWordWrap, message);
    12. ratio = float(rect.width())/float(rect.height());
    13. }
    To copy to clipboard, switch view to plain text mode 

  3. #3
    Join Date
    Aug 2010
    Posts
    99
    Thanks
    3
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: Calculating best bounding box for text

    Quote Originally Posted by Lykurg View Post
    The problem seems to be that you alter the rect which is given back from boundingRect.
    Yeah, i was trying to reuse that object by increasing it's dimensions and passing it back into boundingRect.

    I would do it like that: Start with a fix width (= minimum widht), use boundingRect to get the hight for the with. If it is not in the ratio you wont only adjust the with and let the height recalculated. Not tested, but:
    ...
    That looks more like what i want. And it makes sense to only adjust the width and have the height calculated from that. I will try this code later.

    Thanks!
    [Window Detective] - Windows UI spy utility
    [War Thunder]

Similar Threads

  1. Bounding QWidget within QGraphicsView
    By crazymonkey in forum Newbie
    Replies: 1
    Last Post: 22nd September 2010, 19:44
  2. Getting the bounding rectangle
    By ioannis in forum Qt Programming
    Replies: 1
    Last Post: 22nd May 2009, 00:41
  3. Html text bounding rectangle
    By bunjee in forum Qt Programming
    Replies: 8
    Last Post: 15th May 2008, 11:25
  4. hide bounding rect
    By dreamer in forum Qt Programming
    Replies: 1
    Last Post: 8th May 2008, 09:40
  5. calculating checksum in serialcommunication
    By jjbabu in forum Qt Programming
    Replies: 5
    Last Post: 12th October 2007, 12:43

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.