Overridding QMessageBox::paintEvent() - c++

I am creating a custom message box for an application. My object is derived from QMessageBox, but I am overriding the paintEvent() method in order to change its appearance. Curiously, although I do not call the base paintEvent method in my derived method, my custom message box is still being painted with an OK button by default. Here is my code:
class MessageWidget : public QMessageBox
{
Q_OBJECT
public:
MessageWidget(QWidget* parent = 0);
~MessageWidget();
void setTitle(const QString& title);
const QString& title() const;
protected:
void paintEvent(QPaintEvent* event);
}
MessageWidget::MessageWidget(QWidget* parent) :
QMessageBox(parent)
{
setWindowFlags(Qt::FramelessWindowHint);
setAutoFillBackground(true);
}
void MessageWidget::paintEvent(QPaintEvent* /*event*/)
{
QPainter painter(this);
QRect boxRect = rect();
QPainterPath path;
path.addRoundedRect(boxRect, 15, 15);
painter.fillPath(path, palette().window());
painter.drawPath(path);
QRect titleRect = boxRect;
int titleHeight = fontMetrics().height();
titleRect.moveBottom(titleHeight);
boxRect.moveTop(titleRect.bottom());
painter.drawLine(titleRect.bottomLeft(), titleRect.bottomRight());
painter.drawText(titleRect, Qt::AlignLeft, "Some Text");
}
How is it that other things are being painted when I'm not calling the base paintEvent method?

The OK button is not being painted. It's a child QWidget added to the message box. Child widget painting is not controlled in the parent's paintEvent.

Related

Qt: Which object/item should I use to create clickable icons

I'm trying to write an editor for an rpg (Role Playing Game) (npc / quests / items etc.). I need to create an icon with a "white background" that represents the npc's image. It should be clickable (when it's clicked, current selected npc's icon ID will be set according to the selection).
I've managed to build a pop-up dialog to show all the icons, but couldn't manage to find a way to create clickable icons. Which class should I implement in order to get it working?
Clickable icons can be achieved using either QPushButton or QToolButton:
QPushButton* button = new QPushButton;
button->setIcon(QIcon("/path/to/my/icon"));
Clickable QLabel : https://wiki.qt.io/Clickable_QLabel
Use with a QPixmap : http://doc.qt.io/qt-4.8/qlabel.html#pixmap-prop
Header
class ClickableLabel : public QLabel
{
Q_OBJECT
public:
explicit ClickableLabel( const QString& text="", QWidget* parent=0 );
~ClickableLabel();
signals:
void clicked();
protected:
void mousePressEvent(QMouseEvent* event);
};
Source
ClickableLabel::ClickableLabel(const QString& text, QWidget* parent)
: QLabel(parent)
{
setText(text);
}
ClickableLabel::~ClickableLabel()
{
}
void ClickableLabel::mousePressEvent(QMouseEvent* event)
{
emit clicked();
}
I've done something similar but didn't want something that looked like a button, nor did I want to get into style overrides or special painting. Instead, I created a ClickableLabel class that derives from QLabel.
The pertinent part of the code is:
class ClickableLabel : public QLabel
{
protected:
virtual void mouseReleaseEvent (QMouseEvent *evt)
{
emit clicked (evt->button ());
}
signals:
void clicked (int button);
...rest of class definition...
}
You can adjust the signal parameters as desired.

How to properly use adjustSize() in Qt

I'm new to Qt, and this issue about auto resizing has driven me crazy.
I create a class called RenderArea that inherits QWidget. In its paintEvent(), I use a QPainter to draw an image. In order for the whole window to scale with the image, I resize before painting. The relevant code is
if (image && !image->isNull())
{
resize(image->size());
painter.drawImage(image->rect(), *image, image->rect());
}
However, RenderArea will stretch too much through other widgets (like buttons and menus). It is contained in a centralWidget with a vertical layout. But when I call centralWidget->adjustSize() it does not scale everything together, but instead shrinks RenderArea t and hides the image.
How do I instruct the central widget as well as the window to scale with the new size of my customized widget? I know I could use a QLabel and set its scaledContents to be true, but I need a lot of other sophisticated rendering so a simple QLabel is not enough.
The sizeHint function should return recommended widget's size. So RenderArea should return image size as its sizeHint. When the image is changed the updateGeometry function should be called to update a cached sizeHint value:
class RenderArea : public QWidget
{
public:
explicit RenderArea(const QImage &image, QWidget *parent = 0)
: QWidget(parent), m_image(image)
{
}
QSize sizeHint() const
{
return m_image.isNull() ? QWidget::sizeHint() : m_image.size();
}
void paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
if (!m_image.isNull())
{
QPainter painter(this);
painter.drawImage(rect(), m_image, m_image.rect());
}
}
void setImage(const QImage &image)
{
m_image = image;
updateGeometry();
}
private:
QImage m_image;
};
When the child widget is resized, the parent widget isn't doing it automatically. Fortunately there is QWidget::adjustSize function which lets us to resize the parent widget to fit its content:
class Window : public QWidget
{
Q_OBJECT
private slots:
void onImageChanged(const QString &fileName)
{
m_area->setImage(QImage(fileName));
adjustSize();
}
private:
RenderArea *m_area;
};

Qt paint figures at one window

I create class Widget, it creates window, this class paints something on the window (i.e. it works as I want).
I create yet one class, Circle, I want to paint on the window of class Widget.
I pass adress of Widget and try to paint on Widget using QPainter paint (address of Widget); (in the instance of Circle) but i don't see anything.
I've tried to make code as shorter as possible during the execution of program I type out address of object Widget. It doesn't change. It means that the address of Widget was passed right.
Everywhere, where I type out address of Widget I receive the same address. Here is the code:
header Widget
class Widget : public QWidget
{
public:
int mi,mcount;
Widget(QWidget *parent = 0);
QPaintEvent *ev;
virtual void paintEvent(QPaintEvent *);
void drawcircle();
};
Widget.cpp
Widget::Widget(QWidget *parent) : QWidget(parent)
{
QWidget::paintEvent(ev);
qDebug()<<this<<"\n"; //
}
void Widget::drawcircle()
{
QPainter paint(this);
paint.drawEllipse(0,0,100,100);
}
void Widget::paintEvent(QPaintEvent *ev)
{ this->drawcircle(); }
header Circle.h
class Circle :public QWidget
{
public:
Circle(Widget *widget); // i do trick here!!!
Widget *mwidg;
QPaintEvent *ev;
virtual void paintEvent(QPaintEvent *);
void drawcircle(Widget *mwidg);
};
Circle.cpp
Circle::Circle(Widget *widget)
{
qDebug()<<"circle widget"<<widget;
QWidget::paintEvent(ev);
mwidg=widget;
qDebug()<<"\n"<<mwidg;
}
void Circle::paintEvent(QPaintEvent *ev)
{ qDebug()<<"circle paintEvent mwidget"<<mwidg<<"\n";
this->drawcircle(mwidg);
}
void Circle::drawcircle(Widget *mwidg)
{
QPainter paint(mwidg);
paint.drawEllipse(20,10,40,40);
paint.drawLine(0,0,500,500);
}
main
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget *w=new Widget;
qDebug()<<"main address of widget"<<w<<"\n";
Circle *f=new Circle(w);
w->show();
return a.exec();
}
program is compiled and linked successful
What exactly are you trying to achieve? You can only paint on any widget in it's own paintEvent() handler, and you should not call paintEvent() yourself, it won't work. Also, get rid of the QPaintEvent member variables.
I would suggest that you make Circle a child of Widget, and then paint the circle from Circle::paintEvent(). Alternatively, use QGraphicsView.
well , thank you for your attempts to help
but all that i was needing:
this -> setParent(widget);
in costructor Circle::Circle, if somebody'll want to see my solve,one can see that figures are moved
source code is here source code

Events in QGraphicsView

I'm having trouble getting events in QGraphicsView working. I've subclassed QGraphicsView and tried to overload the mousePressEvent and wheelEvent. But neither mousePressEvent nor wheelEvent get called.
Here's my code (Edited a few things now):
Declaration:
#include <QGraphicsView>
#include <QGraphicsScene>
class rasImg: public QGraphicsView
{
public:
rasImg(QString file);
~rasImg(void);
initialize();
QGraphicsView *view;
QGraphicsScene *scene;
private:
virtual void mousePressEvent (QGraphicsSceneMouseEvent *event);
virtual void wheelEvent ( QGraphicsSceneMouseEvent * event );
}
Definition:
#include "rasImg.h"
void rasImg::initialize()
{
view = new QGraphicsView();
scene = new QGraphicsScene(QRect(0, 0, MaxRow, MaxCol));
scene->addText("HELLO");
scene->setBackgroundBrush(QColor(100,100,100));
view->setDragMode(QGraphicsView::ScrollHandDrag);
view->setScene(scene);
}
void rasImg::mousePressEvent (QGraphicsSceneMouseEvent *event)
{
qDebug()<<"Mouse released";
scene->setBackgroundBrush(QColor(100,0,0));
}
void rasImg::wheelEvent ( QGraphicsSceneMouseEvent * event )
{
qDebug()<<"Mouse released";
scene->setBackgroundBrush(QColor(100,0,0));
}
So, what am I doing wrong?.Why don't I see a message or background color change when I click the view or use the mouse wheel?
You're not getting the events because they're being handled by the scene object you're creating, not your class.
Remove the QGraphicsScene *scene; from your rasImg and try something like this for the constructor:
rasImg::rasImg(QString file)
: QGraphicsScene(QRect(0, 0, MaxRow, MaxCol))
{
addText("HELLO");
setBackgroundBrush(QColor(100,100,100));
setDragMode(QGraphicsView::ScrollHandDrag);
view = new QGraphicsView();
view->setScene(this);
}
If you want that in two steps, you could do:
rasImg::rasImg(QString file)
: QGraphicsScene()
{
// any constructor work you need to do
}
rasImg::initialize()
{
addText("HELLO");
setSceneRect(QRect(0, 0, MaxRow, MaxCol));
setBackgroundBrush(QColor(100,100,100));
setDragMode(QGraphicsView::ScrollHandDrag);
view = new QGraphicsView();
view->setScene(this);
}
The point is that the scene that is displayed must be an actual instance of your rasImg, not an instance of QGraphicsScene.
If it's the view you're subclassing, do the same thing. The view you're displaying must be an instance of your class, not a plain QGraphicsView.
rasImg::rasImg(QString file)
: QGraphicsView()
{
// constructor work
}
void rasImg::initialize()
{
scene = new QGraphicsScene(QRect(0, 0, MaxRow, MaxCol));
scene->addText("HELLO");
scene->setBackgroundBrush(QColor(100,100,100));
setDragMode(QGraphicsView::ScrollHandDrag);
setScene(scene);
}
QGraphicsView is derived from QWidget. Therefore it receives mouse events like regular widgets. If your code really is
void rasImg::mousePressEvent (QGraphicsSceneMouseEvent *event)
It can not receive events since it should be
void rasImg::mousePressEvent ( QMouseEvent *event )
QGraphicsSceneMouseEvent is for items in QGraphicsScene that receive mouse input.
If you would like to handle clicks on a specific GUI element rather than handle click on the whole scene, you should derive your own class either from QGraphicsItem (see example of SimpleClass here) or derive from one of existing elements, e.g. QGraphicsPixmapItem.
In both cases in your derived class you can override void mousePressEvent(QGraphicsSceneMouseEvent *event);

Qt - drag and drop with graphics view framework

I'm trying to make a simple draggable item using the graphics framework. Here's the code for what I did so far:
Widget class:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
};
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
DragScene *scene = new DragScene();
DragView *view = new DragView();
QHBoxLayout *layout = new QHBoxLayout();
DragItem *item = new DragItem();
view->setAcceptDrops(true);
scene->addItem(item);
view->setScene(scene);
layout->addWidget(view);
this->setLayout(layout);
}
Widget::~Widget()
{
}
DragView class:
class DragView : public QGraphicsView
{
public:
DragView(QWidget *parent = 0);
};
DragView::DragView(QWidget *parent) : QGraphicsView(parent)
{
setRenderHints(QPainter::Antialiasing);
}
DragScene class:
class DragScene : public QGraphicsScene
{
public:
DragScene(QObject* parent = 0);
protected:
void dragEnterEvent(QGraphicsSceneDragDropEvent *event);
void dragMoveEvent(QGraphicsSceneDragDropEvent *event);
void dragLeaveEvent(QGraphicsSceneDragDropEvent *event);
void dropEvent(QGraphicsSceneDragDropEvent *event);
};
DragScene::DragScene(QObject* parent) : QGraphicsScene(parent)
{
}
void DragScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event){
}
void DragScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event){
}
void DragScene::dragLeaveEvent(QGraphicsSceneDragDropEvent *event){
}
void DragScene::dropEvent(QGraphicsSceneDragDropEvent *event){
qDebug() << event->pos();
event->acceptProposedAction();
DragItem *item = new DragItem();
this->addItem(item);
// item->setPos(event->pos()); before badgerr's tip
item->setPos(event->scenePos());
}
DragItem class:
class DragItem : public QGraphicsItem
{
public:
DragItem(QGraphicsItem *parent = 0);
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
protected:
void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
};
DragItem::DragItem(QGraphicsItem *parent) : QGraphicsItem(parent)
{
setFlag(QGraphicsItem::ItemIsMovable);
}
QRectF DragItem::boundingRect() const{
const QPointF *p0 = new QPointF(-10,-10);
const QPointF *p1 = new QPointF(10,10);
return QRectF(*p0,*p1);
}
void DragItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
if(painter == 0)
painter = new QPainter();
painter->drawEllipse(QPoint(0,0),10,10);
}
void DragItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event){
}
void DragItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event){
}
void DragItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
QMimeData* mime = new QMimeData();
QDrag* drag = new QDrag(event->widget());
drag->setMimeData(mime);
drag->exec();
}
void DragItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event){
}
main.cpp instantiates a Widget and shows it. When I try to drag the circle, the app just creates another circle over the original one, regardless of where I release the drag. qDebug() in DragScene's dropEvent() shows QPointF(0,0) everytime the drag ends. I'm having a hard time trying to understand exactly what I have to do, which classes I should subclass, which methods needs to be overriden, to make this work. The documentation on this isn't very detailed. I'd like to know how to make this work, and if there's some other, more comprehensive resource to learn about the graphics view framework, besides the official documentation (which is excellent btw, but it would be great if there was a more detailed treatise on the subject).
EDIT:
Following badgerr's advice, I replaced item->pos() in DragScene::dropEvent() with item->scenePos(), now the drop event creates a new circle in the drop site, which is more or less what I wanted. But the original circle is still in place, and while the drag is in progress, the item doesn't follow the mouse cursor.
The QGraphicsSceneDragDropEvent documentation says that pos() should return the cursor position in relation to the view that sent the event, which, unless I got it wrong, shouldn't be (0,0) all the time. Weird.
I've read in a forum post that you can use QDrag::setPixMap() to show something during the drag, and in examples I've seen pictures being set as pixmaps, but how do I make the pixmap just like the graphics item I'm supposed to be dragging?
There is an example with Qt Assistant called the "Drag and Drop Robot Example" which appears to use the QDrag method, I don't know if you've had a look at that already.
edit: Just a quick observation, you appear to be creating a new DragItem in your DropEvent, instead of using the mimeData() of the event itself, and since your item draws itself at 0,0, that might explain why you have a new one appearing at that position regardless of where you drop your DragItem.
When I wrote a graphics dragging thing similar to this, I went about it a slightly different way. Maybe it will help you:
Instead of using the QDrag stuff, I used only the mousePressEvent, mouseReleaseEvent, and mouseMoveEvent functions of QGraphicsItem. The mouse press/release set a flag indicating the drag state, and move followed a process of
call prepareGeometryChange();
update the bounds of the QGraphicsItem (my boundingRect() function returns these bounds. All my graphics item bounds are in QGraphicsScene space)
call update();
Then in my paint() function I draw a shape using the boundingRect().
Sorry I don't have a code sample to share, I'll knock one up if I get time.