How to allow interactive resizing of floating Qt5 dock widget - qt

I have the following Qt5.11.0 code to create a qdockwidget. The nature of the widget is such that it makes sense to allow the user to interactively resize the widget, via mouse, as desired when the dockwidget is floating (the example below is contrived, but I believe illustrates the problem).
When I run this and float the dockwidget into its own top-level window, it turns out to be either very hard (Linux) or impossible (OSX) to resize the dockwidget via user interaction. On RHEL Linux 7.6, hovering the mouse over the lower right corner of the floating dockwidget produces a 'resize' cursor, however the hot-spot for such behavior is at best one or two pixels wide, making it very hard and frustrating for users to resize the floating dockwidgets. On OSX 10.13.6, I do not see any option to resize the dockwidget at all via mouse interaction.
Here is the example code:
#include <QApplication>
#include <QMainWindow>
#include <QDockWidget>
#include <QTextEdit>
#include <QTextStream>
#include <QFile>
#include <QSizeGrip>
int
main( int argc, char *argv[] ) {
QApplication app( argc, argv );
QMainWindow* mw = new QMainWindow();
mw->setCentralWidget( new QWidget() );
QDockWidget* dockWidget = new QDockWidget( "Code viewer", mw );
mw->addDockWidget( Qt::LeftDockWidgetArea, dockWidget );
QTextEdit* textEdit = new QTextEdit( dockWidget );
dockWidget->setWidget( textEdit );
QFile file( "/etc/protocols" );
QString filler;
if( ! file.open( QIODevice::ReadOnly ) ) {
exit( -1 );
} else {
QTextStream in( &file );
while( ! in.atEnd() ) {
filler += in.readLine();
}
file.close();
}
textEdit->setText( filler );
mw->show();
return app.exec();
}
I've experimented with QSizeGrip() and searched all over the net, but so far to no avail.
How do I control the hot-zone size for mouse-driven floating dockwidget resizing with Qt on Linux, and how do I enable such in the first place with Qt on OSX?

Unfortunately, the border of a detached QDockWidget is handled by the window decorator, so you can't change it by stylesheets or adjusting the size grip.
Anyway, you can change the dock widget's window flags to force the window decorator to draw regular borders.
Here is an how you can do it in your example:
#include <QApplication>
#include <QMainWindow>
#include <QDockWidget>
#include <QTextEdit>
#include <QTextStream>
#include <QFile>
#include <QSizeGrip>
int main( int argc, char *argv[] ) {
QApplication app( argc, argv );
QMainWindow* mw = new QMainWindow();
mw->setCentralWidget( new QWidget() );
QDockWidget* dockWidget = new QDockWidget( "Code viewer", mw );
mw->addDockWidget( Qt::LeftDockWidgetArea, dockWidget );
// handle floating changes
QObject::connect(dockWidget, &QDockWidget::topLevelChanged, [dockWidget] (bool floating)
{
if (floating)
{
dockWidget->setWindowFlags(Qt::Window);
dockWidget->show();
}
});
QTextEdit* textEdit = new QTextEdit( dockWidget );
dockWidget->setWidget( textEdit );
QFile file( "/etc/protocols" );
QString filler;
if( ! file.open( QIODevice::ReadOnly ) ) {
exit( -1 );
} else {
QTextStream in( &file );
while( ! in.atEnd() ) {
filler += in.readLine();
}
file.close();
}
textEdit->setText( filler );
mw->show();
return app.exec();
}
Here you can find a description of all the available window flags to customize the look of your widget.

Related

How to allow QDockWidget to dock to multiple QMainWindow instances

I have a Qt5.11.0 application (on OSX10.13 and CentOS7.4) with multiple QMainWindows. I would like users to be able to dock any given QDockWidget in the application into any of the QMainWindow instances.
The sample code below shows an application with two QMainWindows and one QDockWidget. I can undock and re-dock the QDockWidget from and to the first QMainWindow to which it was attached, however if I hover the detached QDockWidget over the second QMainWindow, the QDockWidget and the second QMainWindow ignore each other, rather than docking together as hoped:
#include <QApplication>
#include <QMainWindow>
#include <QDockWidget>
#include <QLabel>
int
main( int argc, char *argv[] ) {
QApplication app( argc, argv );
QMainWindow* mw1 = new QMainWindow();
QMainWindow* mw2 = new QMainWindow();
mw1->setWindowTitle( "Main Window 1" );
mw2->setWindowTitle( "Main Window 2" );
mw1->setCentralWidget( new QWidget() );
mw2->setCentralWidget( new QWidget() );
QDockWidget* dockWidget = new QDockWidget( "Stepchild", mw1 );
QLabel* label = new QLabel( "Hello World" );
dockWidget->setWidget( label );
mw1->addDockWidget( Qt::LeftDockWidgetArea, dockWidget );
mw1->show();
mw2->show();
QRect first = mw1->geometry();
int pixeloffset = 200;
mw2->setGeometry( first.x() + pixeloffset,
first.y() + pixeloffset,
first.width(),
first.height() );
return app.exec();
}
How can I allow my QDockWidget above to dock in any of the QMainWindows in the application?
Thanks --

How to prevent clipping of cornerWidget in QMenuBar

I'm writing newbie Qt5(.4.0) code on OSX Mavericks. Here's my self-contained test case:
#include <QApplication>
#include <QMainWindow>
#include <QtGui>
#include <QMenuBar>
#include <QGridLayout>
#include <QPushButton>
int
main( int argc, char *argv[] ) {
QApplication app( argc, argv );
QMainWindow* mw = new QMainWindow();
mw->menuBar()->setNativeMenuBar( false );
QMenu* fileMenu = mw->menuBar()->addMenu( "&File" );
QMenu* optionsMenu = mw->menuBar()->addMenu( "&Options" );
QWidget* menuCorner = new QWidget( mw->menuBar() );
QGridLayout* cornerLayout = new QGridLayout();
QPushButton* newWindowButton = new QPushButton( "New Window" );
cornerLayout->addWidget( newWindowButton, 1, 0 );
menuCorner->setLayout( cornerLayout );
mw->menuBar()->setCornerWidget( menuCorner );
mw->show();
return app.exec();
}
The "New Window" pushbutton shows up at the right side of the menubar, as intended, however the bottom half of the "New Window" pushbutton is clipped, and thus hidden, by the bottom separator line for the menubar:
How can I make the new corner-widget pushbutton appear fully in the menubar without getting clipped?
Thanks
OSX menu bars must have a fixed height and your button doesn't fit there. Try to remove layout margins:
cornerLayout->setContentsMargins(0, 0, 0, 0);
And / or make the button smaller:
newWindowButton->setMaximumHeight(30);
Also, adjust margin to what best fits your needs:
menuCorner->setStyleSheet("margin-top: 2");
This is how it looks for me:

Deleting QQuickView on exit cause Qt application to freeze

I founded a deadlock in Qt 5.3. How to fix in correctly? Without ugly fix (not to delete qquickview *)
I have a singleton with a pointer to QQuickView. When I need to close my application I do a call of QGuiApplication::quit() and try to release QQuickView * in destructor of singlenot. Result - application freezes.
Sample:
test.qml
import QtQuick 2.1
Rectangle
{
id: root;
color: "black";
signal quit();
Component.onDestruction: quit();
}
main.cpp
#include <QGuiApplication>
#include <QQuickView>
#include <QQuickItem>
#include <QPointer>
struct Singleton
{
QPointer< QQuickView > w;
static Singleton inst;
int run( int argc, char *argv[] )
{
QGuiApplication a( argc, argv );
w = new QQuickView();
QObject::connect( w, &QQuickView::statusChanged, [=]()
{
QObject::connect( w->rootObject(), SIGNAL( quit() ), qApp, SLOT( quit() ) );
} );
w->setSource( QUrl( "qrc:/test.qml" ) );
w->setResizeMode( QQuickView::SizeRootObjectToView );
w->show();
a.exec();
return 0;
}
~Singleton()
{
delete w; // Comment this to fix bug
}
};
Singleton Singleton::inst;
int main(int argc, char *argv[] )
{
Singleton::inst.run( argc, argv );
return 0;
}
P.S. C++0x is used for simplifying code. Same result on C++03 compilers.
It was a bug in Qt. Fixed since 5.4 version.

Capture QML drawing buffer, without displaying

I need to grab each QML (QtQuick 2) drawing frame and sent it over the network.
At the moment I have used method listed below, but this method has two big disadvantage
1) Due to Qt5 documentation grabWindow() function has performance issues
2) It can't work with hidden QML window
Is it possible to get OpenGL render buffer right after QQuickWindow::afterRendering ?
Using FBOs ? Shared opengl context ?
class Grab: public QObject
{
public:
Grab( QQuickWindow * wnd ) : wnd_(wnd) {}
public slots:
void Grabme()
{
QImage image = wnd_->grabWindow();
}
private:
QQuickWindow *wnd_;
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QtQuick2ApplicationViewer viewer;
viewer.setMainQmlFile(QStringLiteral("qml/grab1/main.qml"));
viewer.showExpanded();
Grab grab( &viewer );
QObject::connect( &viewer, &QtQuick2ApplicationViewer::frameSwapped,
&grab, &Grab::Grabme, Qt::DirectConnection );
return app.exec();
}
Example bellow can grab any qml content to FBO and then sent it as Image via signal.
Only one problem of this approach is visibility, grab window must be visible for successful grabbing. If anybody knows how to prevent this you can help me and provide more advanced approach.
// main.cpp
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
GrabWindow grab;
grab.setResizeMode( QQuickView::SizeViewToRootObject );
grab.setSource( QUrl::fromLocalFile("qml/main.qml") );
grab.setFlags( Qt::Popup );
grab.show();
return app.exec();
}
// grabwindow.hpp
#pragma once
#include <QOpenGLFramebufferObject>
#include <QScopedPointer>
#include <QQuickView>
#include <QImage>
class GrabWindow: public QQuickView
{
Q_OBJECT
signals:
void changeImage( const QImage &image );
public:
GrabWindow( QWindow * parent = 0 );
private slots:
void afterRendering();
void beforeRendering();
private:
QScopedPointer<QOpenGLFramebufferObject> fbo_;
};
// grabwindow.cpp
#include "grabwindow.hpp"
#include <limits>
GrabWindow::GrabWindow( QWindow * parent ) :
QQuickView( parent )
{
setClearBeforeRendering( false );
setPosition( std::numeric_limits<unsigned short>::max(), std::numeric_limits<unsigned short>::max() );
connect( this, SIGNAL( afterRendering() ), SLOT( afterRendering() ), Qt::DirectConnection );
connect( this, SIGNAL( beforeRendering() ), SLOT( beforeRendering() ), Qt::DirectConnection );
}
void GrabWindow::afterRendering()
{
if( !fbo_.isNull() )
{
emit changeImage( fbo_->toImage() );
}
}
void GrabWindow::beforeRendering()
{
if (!fbo_)
{
fbo_.reset(new QOpenGLFramebufferObject( size(), QOpenGLFramebufferObject::NoAttachment) );
setRenderTarget(fbo_.data());
}
}
I managed to find a trick to make grabWindow() work when the Window is "not visible". The trick is to set the window's visibility: Window.Minimized and the flags: Qt.Tool. The window is not displayed to the user, but to the Qt's internals it appears to be visible and the grabWindow() method call works as expected. Remember to call that method only once the scene has been initialised.
The only problem with this solution (that I have come across) is that if the window's color property is set to transparent, the captured content has black background.
With later versions of Qt 5.X you can also use the software render backend.
The following renders any scene in the background without any visible window or OpenGL tricks:
// main.cpp
#include <QGuiApplication>
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickItem>
#include <QQuickWindow>
#include <QQuickRenderControl>
int main(int argc, char *argv[])
{
const char *source = "qrc:/main.qml";
if (argc > 1) source = argv[1];
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
QGuiApplication app{argc, argv};
QQuickRenderControl renderControl;
QQuickWindow window{&renderControl};
QQmlEngine engine;
QQmlComponent component{
&engine,
QUrl{QString::fromUtf8(source)}
};
QQuickItem *rootItem = qobject_cast<QQuickItem *>(component.create());
window.contentItem()->setSize(rootItem->size());
rootItem->setParentItem(window.contentItem());
window.resize(rootItem->size().width(), rootItem->size().height());
QImage image = renderControl.grab();
image.save("output.png");
return 0;
}

(Qt) Unusable window after one button click

I have a window with many buttons. Each one triggers a sub-program (written using the Opencv API). Each sub-program displays images and stuff on windows.
The problem is, when I close these windows (via the little red cross), all the buttons become unclickable. So if I want to launch another program, I'll have to exit the main window and run it again.
In other words, I want to be able to run all the sub-programs without having to start over every time.
Here's the GUI's code :
.cpp
#include "fenprincipale.h"
#include "ui_fenprincipale.h"
#include<highgui.h>
#include<cv.h>
#include <moyenetmedian.h>
#include<morpho.h>
#include<tracking.h>
#include<contour.h>
#include<QApplication>
FenPrincipale::FenPrincipale(QWidget *parent) :
QWidget(parent),
ui(new Ui::FenPrincipale)
{
ui->setupUi(this);
MoyenEtMedian *moyenEtMedian = new MoyenEtMedian;
morpho * mor = new morpho;
tracking * tra= new tracking;
contour * cont= new contour;
QObject::connect(ui->bMoyMed, SIGNAL( clicked() ), moyenEtMedian, SLOT( exec() ), Qt::AutoConnection );
QObject::connect(ui->bMorph, SIGNAL( clicked() ), mor, SLOT( exec() ), Qt::AutoConnection );
QObject::connect(ui->bTrack, SIGNAL( clicked() ), tra, SLOT( exec() ), Qt::AutoConnection );
QObject::connect(ui->bCont, SIGNAL( clicked() ), cont, SLOT( exec() ), Qt::AutoConnection );
}
FenPrincipale::~FenPrincipale()
{
delete ui;
}
.h :
#ifndef FENPRINCIPALE_H
#define FENPRINCIPALE_H
#include <QWidget>
#include <QApplication>
namespace Ui {
class FenPrincipale;
}
class FenPrincipale : public QWidget
{
Q_OBJECT
public:
explicit FenPrincipale(QWidget *parent = 0);
void switch_callback(int);
void execMoyMed (void);
~FenPrincipale();
private:
Ui::FenPrincipale *ui;
};
#endif // FENPRINCIPALE_H
the main class :
#include <QCoreApplication>
#include <QApplication>
#include <QtGui>
#include <QWidget>
#include "fenprincipale.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
FenPrincipale fenetre;
fenetre.show();
return a.exec();
}
Slot implementation for "moyenetmedian" :
void MoyenEtMedian::exec(void)
{
const char* name = "Filtres";
IplImage* img = cvLoadImage( "C:/Users/XELTINFO/ProjetVision/image.png" );
IplImage* out = cvCreateImage( cvGetSize(img), IPL_DEPTH_8U, 3 );
cvNamedWindow( name, 1 );
cvShowImage(name, out);
// Create trackbar
cvCreateTrackbar2( "Filtre", name, &g_switch_value, 1, &MoyenEtMedian::switch_callback, this );
while( 1 ) {
switch( filterInt ){
case 0:
cvSmooth( img, out, CV_BLUR, 7, 7 );
break;
case 1:
cvSmooth( img, out, CV_MEDIAN, 7, 7 );
break;
}
if(filterInt != lastfilterInt){
cvShowImage(name, out);
lastfilterInt = filterInt;
}
if( cvWaitKey( 15 ) == 27 )
break;
}
cvReleaseImage( &img );
cvReleaseImage( &out );
cvDestroyWindow( name );
}
The class declaration :
#ifndef MOYENETMEDIAN_H
#define MOYENETMEDIAN_H
#include "ui_fenprincipale.h"
#include<QObject>
class MoyenEtMedian : public QObject
{
Q_OBJECT
public:
MoyenEtMedian();
static void switch_callback(int position, void*);
public slots :
void exec(void);
};
#endif // MOYENETMEDIAN_H
The class delcarations and slots implementations are very similar for all classes. I'll add the rest if this isn't enough.
You are blocking the event loop in your exec() slot, since it doesn't return immediately. You should instead subclass QWidget and override keyPressEvent() to get keyboard input from Qt's event loop instead of doing the busy-loop you currently have.
So when using Qt with OpenCV, I would setup the polling using Qt's timers instead of a while loop.
There is a pretty good tutorial of using QTimers to interact with OpenCV objects here:
http://www.youtube.com/watch?v=0ONxIy8itRA
Jump to 35 or 38 minutes into it to see how he writes his classes.
Basically, you let Qt do the waiting and timing, instead of having a while loop with a wait call doing the timing.
And if possible, let Qt create the windows, and nest the OpenCV windows into Qt's windows so that Qt can manage the events on the windows.
Hope that helps.

Resources