I have a QMainWindow and a QDockWidget nested inside this.
I show some graphs, so the QDockWidget expands but the QMainWindow keeps it's initial size so i have to resize it using my mouse.
So, how can i make a QMainWindow resize to QDockWidget size every time?
It was easy at the end.
I take the Qsize of my QDockWidgets and i resize my QMainWIndow to this.
For example i have 2 QDockWidget side by side so what i do is
QSize siz = Dock->size();
QSize siz2 = Dock2->size();
resize(siz.width()+siz2.width(),siz.height);
You might want to rewrite the resizeEvent function of the QDockWidget widget. For that you need to subclass QDockWidget.
class MYDockwidget : public QDockWidget
{
Q_OBJECT
public:
MYDockwidget(QWidget *parent = 0):
QDockWidget(parent)
{}
protected:
void resizeEvent(QResizeEvent *event)
{
QDockWidget::resizeEvent(event);
// Calulate Main window size here.
// the main window is accesible
// through the parent property.
}
};
This approach works, but binds the QDockWidget's resizeEvent to the QMainWindow. The proper solution is to emit a signal when the size of the QDockWidget change.
For that you will need to define a custom signal and of course you want that signal with information about the event in question, hence our signal will be emited with a QSize argument.
class MYDockwidget : public QDockWidget
{
Q_OBJECT
public:
MYDockwidget(QWidget *parent = 0):
QDockWidget(parent)
{}
signals:
void sizeChanged(QSize);
protected:
void resizeEvent(QResizeEvent *event)
{
QDockWidget::resizeEvent(event);
emit sizeChanged(event->size());
}
};
After that you can write code like:
// Inside your main window.
public slots:
void on_dock_size_changed(QSize)
MYDockwidget *dock = new MYDockwidget(this);
connect(dock, SIGNAL(sizeChanged(QSize)), this, SLOT(on_dock_size_changed(QSize)));
void on_dock_size_changed(QSize size)
{
// resize your main window here.
}
Disadvantage:
You will need to set the QDockWidget's properties by hand (programmatically) unless you manage your self to insert your custom widget as a QTDesigner plugin.
Related
Minimal code example:
class Boo : public QPushButton{
public:
Boo(QWidget* w) : QPushButton(w){}
virtual void paintEvent(QPaintEvent* ev){
qDebug()<<__FUNCTION__<<this->objectName();
}
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
virtual void paintEvent(QPaintEvent* ev){
qDebug()<<__FUNCTION__;
}
private:
QTimer t;
Ui::MainWindow *ui;
Boo *b1, *b2;
};
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
b1 = new Boo(this);
b1->setObjectName("O1");
b2 = new Boo(this);
b2->setObjectName("O2");
connect(&t, &QTimer::timeout, this, [this](){
b2->repaint();
});
t.start();
t.setInterval(10);
}
Outputs infinitely: MainWindow::paintEvent Boo::paintEvent "O1" Boo::paintEvent "O2"
but i call repaint only for button b2. Using "QWidget::repaint(int x, int y, int w, int h)" or :QWidget::update()" also invoke repaint for main window.
Problem exists on Qt5.12/5.15 and windows11, but looks like general qt bug.
This issue cause high GPU consumption in our more complex GUI application.
Ok. I found the reason myself, after add:
...
b1->setGeometry(15,15, 12, 12);
...
b2->setGeometry(35,35, 14, 14);
...
virtual void paintEvent(QPaintEvent* ev){
qDebug()<<__FUNCTION__<<ev->rect();
}
ouput says, that actual repaint is done only for button region:
MainWindow::paintEvent QRect(35,35 14x14)
Boo::paintEvent "O2" QRect(0,0 14x14)
Important thing: ivoke setGeometry for button, not just add as child. In other case (usable only for toy example, ofcourse) repaint button with unset geometry will lead to repaint every button with unset geometry.
Qt repaints by default all parents too, to allow widgets being partially transparent. If Qt doesn't repaint the parent on an update, some pixels of the previous paintEvent may still be visible.
Qt provides two methods to optimise this behaviour when no transparent background is needed:
Set autoFillBackground. This option is preferred in case of an opaque background color.
Set Qt::WA_OpaquePaintEvent to indicate that you will paint the whole widget (with opaque colors).
More information
Qt documentation: QWidget: Transparency and Double Buffering
For styling purposes, I'm trying to make all the widgets in a set of widgets adopt a certain background if a pointer enteres any of those widgets;
i.e. if the pointer is insides the containing rectangle of widget A, both A and widget B should change its background. Likewise if the pointer enters B, then A should also adopt the same background. The background should, naturally, fall back to the non-hovering background if the pointer is outside both A and B.
QWidget appears to have most of the plumbing to get this done, but its hidden behind protected.
I really want to avoid inheriting from QWidget if I can avoid it and I really would like to not have to change the stylesheets of the widgets too.
[1] In my case, I have three widgets, not two.
Have an event filter class, and let it hold the affected widgets in a vector of pointers:
#include <QMouseEvent>
class EventFilter : public QObject
{
Q_OBJECT
QVector<QWidget*> widgets;
public:
void append(QWidget * w)
{
w->setAttribute(Qt::WA_Hover);
w->installEventFilter(this);
widgets.append(w);
}
bool eventFilter(QObject *watched, QEvent *event)
{
if(event->type() == QMouseEvent::HoverEnter)
{
for(auto w : widgets)
{
w->setStyleSheet("background-color: red;");
}
}
else if(event->type() == QMouseEvent::HoverLeave)
{
for(auto w : widgets)
{
w->setStyleSheet("background-color: white;");
}
}
return false;
}
};
The append method will set up the widget and push it in the vector.
Have a private filter instance in your form class:
class Form : public QWidget
{
Q_OBJECT
//...
private:
EventFilter filter;
//...
In your form constructor:
ui->setupUi(this);
filter.append(ui->widget1);
filter.append(ui->widget2);
filter.append(ui->widget3);
In the example I used setStyleSheet to set the background color, but you can use whatever strategy you prefer, i.e. resetting the widget palette.
I need to subclass qt class and reimplement the virtual function.
Say that i have the QLCDNumber class and I want to reimplement the function that set the position of the number when resize the LCD number screen, how to achieve that?
I read that by inhert a class from QLCDNumber and reimplement the function,
but where to get that function code so that I can edit what needed in that function? I read the documentation but it explains the function uses and not show its code. Example image:
I mentioned the QLCDNumber class as example, I need to know the prosses of reimplemnt a virtual Qt class function.
For this particular problem you can use this type of approach where you can create an object which inherits QWidget and implement the paintEvent() of the class like this:
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = 0);
~MyWidget();
protected:
void paintEvent(QPaintEvent *pe);
signals:
public slots:
};
and in the paintEvent()
void MyWidget::paintEvent(QPaintEvent *pe)
{
QPainter *painter = new QPainter(this);
painter->begin(this);
// draw what you want here using painter
// and consider your different layouts
painter->end();
}
I was facing one issue with the Widget which is integrated in the QQuickPaintedItem class. When I have Widget integrated in the QQuickPaintedItem, QWidget::isVisible will return false. If I tried to set QWidget::setVisible(true) then it will open another window, which I do not want in my scenario.
Is there any way to get QWidget::isVisible return true so that my child widgets (In my actual scenario, we have 5 layer of parent child hierarchy) will also works fine when I say QWidget::show()?
I have created the scenario similar to it as below.
Header file:
class MyItem: public QQuickPaintedItem{
Q_OBJECT
public:
explicit MyItem(QQuickItem *parent = 0);
void paint(QPainter *painter);
~MyItem();
Q_INVOKABLE void initButton();
protected:
virtual void mousePressEvent( QMouseEvent* event );
private:
QPushButton* bp;
};
source file:
MyItem::MyItem(QQuickItem *parent)
: QQuickPaintedItem(parent)
{
bp = new QPushButton("Hello");
}
MyItem::~MyItem()
{
delete bp;
}
void MyItem::paint(QPainter *painter){
bp->render(painter, QPoint(), QRegion(), QPushButton::DrawWindowBackground | QPushButton::DrawChildren);
}
void MyItem::mousePressEvent( QMouseEvent* event )
{
qDebug() << Q_FUNC_INFO << bp->isVisible();
}
Thanks for help in advance...!!!
I don't know why you want to do this.
Qt do not support to embed a QWidget into a Qt Quick Item in Qt5(Qt Quick 2).
In your code, QWidget is a seperate Window, and you Qt Quick item is in it's own Window.
If you want your Qt Quick item behavior like a Button, you should use Qt Quick's Button control or write one yourself.
If you really want to embed a QWidget into Qt Quick's control tree, you can use Qt Quick 1(Qt4.7/8) instead. Check out QGraphicsProxyWidget's document.
In Qt Creator, I have a couple of widgets declared like so:
Header File:
class MapViewer : public QGraphicsView
{
Q_OBJECT
public:
explicit MapViewer(QGraphicsScene *scene, QWidget *parent = 0);
~MapViewer();
public slots:
void mousePressEvent(QMouseEvent *event);
};
// Declaration for the map editor window.
class MapEditor : public QMainWindow
{
Q_OBJECT
public:
explicit MapEditor(QWidget *parent = 0);
~MapEditor();
public:
QLayout *editorLayout;
QPushButton *btn;
QGraphicsScene *mapScene;
MapViewer *mapView;
private:
Ui::MapEditor *ui;
};
CPP File:
MapEditor::MapEditor(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MapEditor)
{
ui->setupUi(this);
this->setWindowTitle("2DXY :: Map Editor");
this->setGeometry(10,10,1170,750);
editorLayout = new QVBoxLayout; // Create a new layout
this->setLayout(editorLayout); // Set the widget's layout to our newly created layout.
mapScene = new QGraphicsScene(); // Create a new graphics scene to draw upon.
mapView = new MapViewer(mapScene,this); // Create a new graphics view to display our scene - set its parent to 'this' so that it doesn't open in a new window.
mapView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
mapView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
mapView->setGeometry(20,20,1178,546); // Width first, then height.
AND:
void MapViewer::mousePressEvent(QMouseEvent *event)
{
// Show an empty message box, just to check that the event handler works!
QMessageBox *notification = new QMessageBox();
notification->show();
notification->exec();
// Some how access the same QGraphicsScene and View (mapScene, mapView) as above, so
// I can update their contents on the open form / window.
}
And as you can see, I wish to access the Graphics Scene again to update it, then redraw it (or whatever). But I'm not able to access the graphics scene at all, despite a few hours of trial and error with declaring widgets in different ways.
I know that the listener itself works, because if it's set to open a new message box, or output to the debug window, then it works, it's just that I can't access the widgets I've already defined.
I feel that there is a (relatively) easy solution to this problem, and that I'm just missing something obvious.
You passed the QGraphicsScene to your MapRender object's constructor. What do you do with the scene within its constructor? Ideally, you should be storing it as a data member of MapRender. For example:
class MapRender {
public:
MapRender(QGraphicsScene* scene)
: scene_(scene)
{
}
public slots:
void mousePressEvent(QMouseEvent *event);
private:
QGraphicsScene* scene_;
}
Now in your implementation of mousePressEvent, you can access to the scene member:
void MapRender::mousePressEvent(QMouseEvent *event) {
int CursorX = event->globalX();
int CursorY = event->globalY();
QGraphicsRectItem *clickedBox = new QGraphicsRectItem(40,40,32,32);
clickedBox->setBrush(QBrush(Qt::blue));
scene_->addItem(clickedBox);
}
Keep in mind you should ideally be putting the implementation of the constructor in your cpp file, but my example does it in the declaration for brevity.
void MapViewer::mousePressEvent(QMouseEvent *event)
{
// Show an empty message box, just to check that the event handler works!
QMessageBox *notification = new QMessageBox();
notification->show();
notification->exec();
// To add something whenever the user clicks, you don't need the view,
// just the scene.
scene()->addItem( new MyItem() );
}
Remember MapViewer derives from QGraphicsView and the view must know about the scene it belongs to - so it has a scene() method to return it, which you inherited.