Docking with QWinWidget: Adding DockWidgetAreas to QWidgets - qt

I have an application utilizing QWinWidget in a Win32 window. I'd like to add DockWidgets and the associated behaviour to it. There don't seem to be any exposed APIs for adding custom DockAreas, and the latest docs are sparse beyond adding DockWidgets to a QMainWindow. Older docs imply there once was a public QDockArea class.
So far, my best option appears to be adding a neutered QMainWindow (no top-level status, no frame, etc.) to the QWinWidget and going from there (second source).
I was hoping there was a way to add DockAreas to any container, but it doesn't appear that way. As a side note, QWinWidget is used to have window manager control with our custom frame requirement, but if there's a pure QMainWindow/QWidget way to have the same result (with Qt::FramelessWindowHint), I'd be happy to switch over.

As I said in the comments, I've added within my QWinWidget in my Win32 window a QMainWindow field. That is:
class QWinWidget : public QWidget
{
...
QMainWidget* mainWidget;
}
QWinWidget::QWinWidget()
{
mainWidget = new QMainWindow(this);
mainWidget->setWindowFlags(Qt::Widget); //1
}
Note that while the docs and some forum posts (from this post) indicate you need to set window flags explicitly due to the QMainWindow constructor setting Qt::Window, I tested it without the setWindowFlags() line (marked with //1 above) without noticing anything wrong.
So, this gives me the nice QWinWidget window I spent so much time making, inside a frameless Win32 window, with a QMainWindow child and all the features that brings along with it. Docking, menu bar, status bar, etc.

Related

QHeaderView mouse tracking fails

I need to connect some simple filter functionality, to a mouse click on a QTreeView header item. Simple enough, I implemented a slot function that connects to:
QTreeView::header()->sectionClicked(int)
After setting
QTreeView::header()->setSectionsClickable(true)
,sectionClicked is emitted any time I click on a header that is highlighted by the default hover effect that any clickable header will produce.
The issue is, hovering over some areas in clickable headers will not be recognized. Hence, in these parts there is no highlight and I will not get any sectionClicked triggers. I traced it back further and derived my own class from QHeaderView and put some output into mouseMoveEvent.
class MyHeaderView : public QHeaderView
{
Q_OBJECT
public:
MyHeaderView(QWidget* parent = 0)
: QHeaderView(Qt::Horizontal, parent)
{
setMouseTracking(true);
}
protected:
virtual void mouseMoveEvent(QMouseEvent* event)
{
qDebug() << event->pos();
}
};
While leaving all QTreeView settings as they were, I set an instance of this class to be the header via
QTreeView::setHeader(QHeaderView*)
I could see, that in all areas were the hover effect failed, I did not get the debug output you can see in the overridden mouseMoveEvent.
As a result I am assuming, that the mouse tracking is not working correctly.
The overall application I am working on is quiet big, so I set up a stand alone example, for all of this. To my surprise, everything works as expected. I am clueless, how I should proceed with this. Can anyone think of reasons for the mouse tracking to fail in some parts of a widget? Could it be a frame rate issue tied to lack of performance? Are there settings on widgets that affect the overall mouse tracked area? Can parent widgets affect mouse tracking?
In my application I have a controller class that handles a lot of application logic connected to various signals the tree view emits. This class is not suppost to render any visualization. Hence, it would be the most reasonable choice to derive from QObject. After a while I noticed, that it was in fact derived from QWidget. Being a QWidget I am guessing it tries to render some sort of visual representation, which the overlays the tree view. That's why I don't get any mouse events in some parts of the header.
After changing the base class of my controller to QObject, everything works fine again.

How to start second QDialog window on side of MainWindow?

I have a application in Qt, and a MainWindow. Now, I also added a helping QDialog which can be hooked up. This QDialog does not influence the programmflow, it just displays information.
But, it always starts on top of the MainWindow :/
SO I would like to start it on the side of the main window, so that it does not hinder the view to the main window?? How?
In my opinion , You should try this,
Use the overload of the QWidget::setParent() function to change the ownership of a QDialog widget, using set as NULL(but It will not share the parent's taskbar entry).
QDialog::show() returns control to the caller immediately, So it will not interfere in mainwindow flow.
Let's say, your application is in the full screen mode. Where then the QDialog should appear? It will be shown on the top and you won't be satisfied again.
If it doesn't influence the flow of the app then you are using it to communicate some sort of message. Can you use different ways? For instance, QStatusBar?
If not, why not to split the mainWindow with QSplitter or similar classes and provide an area where you can post all the info messages?
It sounds you want modaless dialog. In main window, use a slot to create the dialog.
void MainWindow::show_dialog()
{
if ( pDialog== NULL )
pDialog= new XYZ_Dialog(this);
QPoint p = pos();
QSize s = frameSize();
pDialog->setGeometry(p.x()+s.width(), p.y(), s.width()*1/2, s.height());
pDialog->show();
}
What I try to say is if the position of the new dialog bothers you, you set the position and size of it, before showing it.

Qt Parent child relationship for independent qmainwindows

Using Pyside, but a general Qt question:
I am building a Qt app with a controlling QMainWindow. From this window the user can open other QMainWindows (or QDialogs) and from some of those she can open more. The user is intended to think of the first QMainWindow as "the app" and the others as lots of different views on more or less the same data.
So I'd like all the windows to be independently stackable so the user can set up the screen to their own requirements. In particular I want the user to be able to bring the first QMainWindow on top if wanted. But I don't really want each window to have its own task bar entry (though I can live with that). Also I would like them to minimise and restore together, and I would like them all to close when the first main window closes.
If I parent them all on the first mainwindow it works nicely except they stay on top of it which is not what I want.
Instead I have it kind of working by making them all independent with parent = None. Then I register them all with the main window and close them all when it closes. But this makes them a bit too independent - they minimise separately and have their own task bar entry.
Am I missing some obvious fix to this? Is there any easy way (a flag?) to stop the children staying on top of the parent?
Or is there some UI guideline that I am breaking by desiring this?
Or is there a cleaner design somehow? I thought of adding a dummy parent that they could all descend from but maybe that's messy. Would that parent need a visual presence? I wouldn't want that.
Suggestions?
You can have as many QMainWindows as you want, or parentless QWidgets. I think the best way to handle your situation is to create your own pseudo parent-child relationship like this:
In your QMainWindow subclass, store a QList of all the QWidgets you want it to manage. Then, again in your QMainWindow subclass, reimplement methods such as QWidget::closeEvent(), QWidget::hideEvent() (for when the window is minimized), and QWidget::showEvent() (for when it is restored) so that it also closes, hides, or shows all of the widgets in its QList. Make sure to also delete them in the QMainWindow subclass's destructor. Now, whenever you create a sub-window, pass the main window a pointer to it not as a normal QWidget child, but just so that it can be added to the main window's QList of QWidgets to manage. E.g.:
MainWindowSubclass::addPseudoChild(QWidget *pseudoChild)
{
myListOfPseudoChildren.append(pseudoChild);
}
Another alternative that hasn't been mentioned yet is populating a QMdiArea with QMdiSubWindows. It doesn't do exactly what you asked for, but it's a pretty clean design nonetheless.
So I thought I would add what I eventually settled upon. This was particularly inspired by the comments of #leemes (Thanks - good stuff) and a little experimentation of my own.
I used the code attached here DetachTabExample
to develop a "Detachable Tab" widget and tab bar. This allows tabs to be dragged outside the main window when they become independent windows. Then if closed they return to the tab bar.
Then I placed all my content in the QMainWindow but in separate tabs. The users can drag the ones they want out on to the other monitor. Seems to be working fine. There are still some extra windows that I have floating but it has cut down the clutter and clarified the structure.

Attach Qt windows?

Is there any way to attach two Qt windows together? For example, if window A is the main window and window B is another widget I want to be able to show window B to the side of A and have both windows move together if the windows are dragged.
Not that I am aware of, but you can try following the trail of QMoveEvent. When a given widget is moved void QWidget::moveEvent ( QMoveEvent * event ) is called, and the QMoveEvent contains both old and new pos. Using this information, you can inject a move event in the other widget as well, and make it follow.
Of course, I am speaking of two independent widgets, each one in its own window. If they are contained, you don't need anything but a Layout management (see QLayout and related classes).
I don't work with Qt since a long time, so there could be a better method, but if I had to do it right now, this is the strategy I would use.
Also, I have the feeling that the QMoveEvent will be invoked only at start and end, unless you enable mouse tracking. If the former is the case, you will obtain that the other widget will "teleport" at the end of the move, instead of following smoothly.
You might be after something like this:
http://doc.qt.io/archives/4.6/qt4-mainwindow.html
Window A would be a QMainWindow and window B would be a QDockWidget.

how can I have more than a UI for a QMainWindow?

I would like to have a QMainWindow that can change it's look at runtime, i.e when a user clicks a button. Besides keeping references to different UI classes generated by the QtDesigner, is there another way of doing that? Maybe by storing each UI in a layout ? What do you think ?
How much of the main window do you want to change? You could use a QStackWidget any place you want to change things, and change the shown page of the widget when the button is pressed. This will be quick to change, but for large or complicated UIs it may be slightly slower at startup, since it will be creating the widgets for both UIs at the same time. (There are fairly easy ways to change this, also, but they do add complications to something that could be straightforward for most people.) Also, if both layouts should have the same data, just in different places, you have the additional overhead of keeping both sets of UIs up to date while the program is running.
I think I got it now.
You have a QMainWindow and when a certain event is triggered you want to change the appearance of that particular window, like remove some buttons, add a treeview widget or what not.
Well the straight forward approach would be to do it manually, remove some widgets and add new ones using regular C++ code. This can be abit hard if you're used to Qt Designer.
The other way I can think of is using Qt Designer to generate the code for the other appearances and copy it to a special function. The code generated by Qt Designer is usually in a header file called "ui_classname.h" and isn't hard to understand. You will however need to remove some of it as not all would be necessary.
Also, instead of copying the generated code from Qt Designer you could just call it. Usually your widget has a pointer to the generated class and in your widget's constructor you see something like this:
MyWindow::MyWindow(QWidget *parent) : QMainWindow(parent), m_ui(new Ui::MyWindow)
{
m_ui->setupUi(this);
}
and in the corresponding header file:
class MyWindow : public QMainWindow {
...
private:
Ui::MyWindow *m_ui;
};
You could add the additional generated classes for the other appearances and use them when your event triggers.
It might look like this:
class MyWindow : public QMainWindow {
...
private:
void changeAppearance(int id);
Ui::MyWindow *m_ui;
Ui::MyWindowFirstAppearance *m_uiFirst;
Ui::MyWindowSecondAppearance *m_uiSecond;
...
};
void MyWindow::changeAppearance(int id)
{
// some code to remove the current appearance, basically the opposite of what setupUi is doing
if (id == 0)
m_ui->setupUi(this);
else...
m_uiFirst->setupUi(this);
...
}
This has the benefit of using the generated classes directly, so every change you do in Qt Designer doesn't require a change to your main window. The problem is I'm not sure if it's legal to call setupUi more than once and in a place other than your widget's constructor, so you'll have to check that (by looking at what's happening in the setupUi function).
You can dynamically load UI layouts at runtime. Check Qt documentation for QUiLoader class.
This also enables you to upgrade UI layouts without modifying a single line of code.
What I do is to design two UIs under two different QFrames, put the two QFrames in a layout together in the QMainWindow, and then hide() and show() the correct QFrame that you want...
You can use a QTabWidget and hide its buttons. You can then design each GUI in a separate window of the tabWidget. It has the advantage over hiding frames that you won't clutter up your Qt Creator window.
If I'm getting this right, I think you might want to take a loot at style sheets. It allows you to have "skins" for your widgets, much like CSS.
If I didn't get this right, and what you're trying to do is generate .ui files on the fly and create widgets with those using QUiLoader, then you're probably going at this the wrong way since I can't think of a good reason a regular application would need that.

Resources