Sizing an embedded widget in QT - c++

So I'm working on a QT5 based GUI and I have a main window that fills in an empty area depending on user input. The gui looks roughly like this
---------------------
|Qlabel 1 |Qlabel 2 |
|-------------------|
| |B1|
| MAIN |B2|
| |B3|
|-------------------|
B1,B2, and B3 are buttons which create popups that ask for user input. Main is the issue. In QT Creator I've set main to be a widget. I've also created a widget which has a layout (again in QT creator) which I'll call filler. On input an instance of filler, I add filler to a layout which I then set on main. The issue I'm having is that sizing seems to be totally dependent on the QT creator sizing of the layout inside of filler.
What I want is for filler to scale to the size of the encapsulating MAIN widget and I'm not sure how to go about this. Any input is helpful!
Edit:
The code which fills in main is roughly
fillerWidget* filler = new fillerWidget(ui->mainWidget, QString::fromStdString(input1), input2);
QGridLayout* fillerLayout = new QGridLayout();
fillerLayout->addWidget(filler, 0, 0);
ui->mainWidget->setLayout(fillerLayout);
ui->mainWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
filler->adjustSize();

Related

How to access QMainWindow from a QWidget which parent is not QMainWindow

I am programming a GUI with Qt Creator (Qt5.10) under OSX. I have a QMainWindow for the main app's window. Inside this I have a QTab widget, in which I have entire widget (let's call it CaptureTabWidget which contains some additional widgets AND a QFrame (the parent of this QFrame is the QTab). In this last widget I will be drawing some stuff. The point is that I would like to write in the statusBar of the QMainWindow some values related to the position of the mouse in that QFrame, but I do not have any idea about how to access the main window from a method inside the CaptureTabWidget which contains the QFrame.
The whole picture goes like this:
X
+-----------+ XXX
|MainWindow | XXXXXXXXX
+-----+-----+ XXX XXXXXX
| X XXX
+-----v------------+ XX
|QTabWidget | XX
|No parent | XX
+-----+------------+ XX
| X
+-----v-------------+ ?? X
|CaptureTabQWidget | X
|Parent:QTabWidget | X
+-----+-------------+ X
| X
+-----v-------------+ XX
|QFrame | XXXXXX
|Parent:CaptureTab | XXX
+-------------------+
I have tried modifying the CaptureTabWidget's constructor in order to receive a QMainWindow* which points to the main window, but it seems Qt Creator doesn't allow custom constructors when promoting widgets in the Designer. I am wondering what is the right way to do this.
If you just aim to write to the statusBar of the parent QMainWindow, you can post a QStatusTipEvent to any child of your QMainWindow, and the event will keep propagating to the parent QWidgets until it reaches the main window where it gets handled by by changing the content in the status bar. In other words, something like this in your QFrame does the job:
QCoreApplication::postEvent(this, new QStatusTipEvent("your tip"));
// ^^^^
// assuming this is a pointer to your QFrame object
As a side note, this is the same way views in Qt (QTableView, QTreeView, QListView, ...) are able to change status tips (when their models provide a custom value for the role Qt::StatusTipRole) while they might be buried deep down in the QObject tree...
For purposes other than just writing to the statusBar of a parent QMainWindow, I would try to generalize the above approach by having a custom QEvent (i.e. one that has a value between QEvent::User and QEvent::MaxUser) and handle that event in my QMainWindow subclass appropriately. I find this way much cleaner and more maintainable than other solutions that depend on global variables/singletons (that keep track of your supposedly one QMainWindow instance).

Qt QStatusBar wider separator

I am using qt to develop an embedded gui application. I am using 2 QStatusBars to make a menu-like buttons one can see on an osciloscope for example:
My problem is I dont know a proper way of separating the buttons from eachother with a certain width. In the picture you can see I have added couple separators to achieve that, but it doesnt look that way when run on the target.
Is there a better way to separate buttons on QStatusBar with certain width?
I'd prefer you use a blank widget to do the seperation as suggested by Martin, like so;
//the 2 widgets in the status bar
button1 = new QPushButton("Button1");
button2 = new QPushButton("Button2");
//the blank widget. You can set your width with 'setFixedWidth(int)'
widget = new QWidget;
widget->setFixedWidth(50);
widget->setHidden(1);
widget->setVisible(1);
//placing them in the status bar
statusBar = new QStatusBar;
statusBar->addWidget(button1);
statusBar->addWidget(widget);
statusBar->addWidget(button2);

How to create or modify a QT form (after compilation) based on a text file

I would like to create a windows application that displays a window based on a existing text file. For example, I may have a text file with the following information:
window_size 400, 300
push_button 2
radio_button 5
My program should be able to read this text file and create a window of 400 x 300 pixels, with 2 push buttons and 5 radio buttons. Of course, the content of the text file may change and is unpredictable. Please ignore at this moment other nessesary data, like the position and size of the buttons, I just need to know if its possible to do this, and a general idea on how to do it.
I'm using Qt with C++. I may change Qt if there is a better option to do this, but I must stick with C++.
You should parse and set accordingly.
Create a QWidget, parse the first line of the text file, set its size. Then parse next lines to get the number of buttons, create widgets dynamically. Something like:
// main
// ...
QString geoLine = stream.readLine();
int width = geoLine.split(" ")[1].toInt();
int height = geoLine.split(" ")[2].toInt();
QWidget * widget = new QWidget();
widget->setGeometry(0, 0, width, height);
// create a layout for the child widgets, then create and add them dynamically.
QVBoxLayout * layout = new QVBoxLayout();
int nButtons = stream.readLine().split(" ")[1];
// full control of dynamically created objects
QList<QPushButton *> buttons;
while(nButtons > 0) {
QPushButton * button = new QPushButton();
buttons.append(button);
layout.addWidget(button);
nButtons--;
}
// same for radio buttons
// ...
widget->setLayout(layout);
widget->show();
// ... etc, app exec,
qDeleteAll(buttons);
delete widget;
return 0;
If you want to learn the widget type from push_button or radio_button directives; you must switch - case onto those parsed strings.
There is also completely another way. You can create a form (.ui) file using an XML data. You must create a ui class (like the template designer form class created by Qt), and create its .ui file according to your text file - parsed and converted to a proper XML.
As far as I know Qt handles the widget creations using that XML information, and generates the file ui_YourClass.h..
As canberk said you can use native Qt UI file format .ui by usage of QUiLoader class - see reference http://doc-snapshot.qt-project.org/qt5-5.4/quiloader.html

My widget doesn't show on the mainwindow. qt

in my program the senario is like this, user opens a xml file, the program reads it. Show the units and tests and when the user clicks on the desired test, the program needs to show its step. Since the number of steps always change i need to add the widget by code and delete them. But when i try to add a widget by code and set its position it doesn't show up on the main window. ( i am doing this with a seperate class just make things look neater)
Here is my sample code;
void CStepPanel::addstep()
{
QLabel *label1 = new QLabel("step1");
label1->move(450,50);
}
Do you know doesnt it work ? And how can i do that. Since i am new to qt and programming i am having a hard time about this.
Your widget should have a parent to be shown in an other widget or dialog. You should set a parent for the label. If you want it to be displayed on the main window then a pointer to your QMainWindow should be kept in the class CStepPanel (It could be assigned in the constructor of CStepPanel) :
QMainWindow * mainWindow;
And the addstep is like :
QLabel *label1 = new QLabel("step1");
label1->setParent(mainWindow);
label1->move(450,50);

QDockWidget Draggable Tabs

I am using QDockWidgets and placing two of them on the left side of my application so that tabs can be used to select between them. However, Qt's default behavior for this looks horrible and is unintuitive. Instead of being able to drag the tabs to move the widgets, it places another bar below the selected tab (with the same name) that must be dragged instead. As a user, it would be hard to figure this out.
(My QDockWidgets are "Attributes" and "Library")
Is there a way to get rid of this second bar and make it so I can move my QDockWidgets by dragging the tabs themselves?
If you are adding QTabWidgets to a main window derived from QMainWindow, you can try tabifyDockWidget.
It tabifies two QDockWidgets just like you wanted and of course you are able to drag them.
dockWidget1 = new QDockWidget("Tab1") ;
dockWidget2 = new QDockWidget("Tab2") ;
this->addDockWidget(Qt::LeftDockWidgetArea , dockWidget1 );
this->addDockWidget(Qt::LeftDockWidgetArea , dockWidget2 );
this->tabifyDockWidget(dockWidget1,dockWidget2);
I think, Tom was not too far away from a solution:
You can set your own Widget as title bar:
myDockingWidget->setTitleBarWidget(myTitleBar)
If you design this widget to not show the dock window title, you have it. Via the signal QDockWidget::topLevelChanged your docking widget can even become informed, when it gets floating, so you could then enable the title in myTitleBar again.
As far as I can see from QDockWidget::mousePressEvent implementation in src/gui/widgets/qdockwidget.cpp dragging the dockwidgets using tabs is NOT possible:
QDockWidgetLayout *dwLayout
= qobject_cast<QDockWidgetLayout*>(layout);
if (!dwLayout->nativeWindowDeco()) {
QRect titleArea = dwLayout->titleArea();
if (event->button() != Qt::LeftButton ||
!titleArea.contains(event->pos()) ||
// check if the tool window is movable... do nothing if it
// is not (but allow moving if the window is floating)
(!hasFeature(this, QDockWidget::DockWidgetMovable) && !q->isFloating()) ||
qobject_cast<QMainWindow*>(parent) == 0 ||
isAnimating() || state != 0) {
return false;
}
initDrag(event->pos(), false);
....
As you can see from the implementation one of the things that the QDockWidget checks before allowing undocking is whether the mouse press event has come from title bar or not.
have you tried:
myDockingWidget->setTitleBarWidget(0)
edit:
QWidget* titleWidget = new QWidget(this);
mUi.dockWidget->setTitleBarWidget(titleWidget);
where 'this' is a QMainWindow
this will remove the title bar, though im not sure how to make the QDockWidget draggable from the tabs
Edited:
Please do not use this method. It introduces problems rather than soloves them.
Maybe you can try this wierd way, that is move the QWidget in the dock widget area to the title bar.
I modify the demo in folder
C:\Qt\Qt5.12.9\Examples\Qt-5.12.9\widgets\mainwindows\dockwidgets
to show how it works:
In "void MainWindow::createDockWindows()"
QDockWidget *dock = new QDockWidget(tr("Customers"), this);
dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
//make a panel to hold your widgets
QWidget *p = new QWidget(dock);
QVBoxLayout *l = new QVBoxLayout(p);
p->setLayout(l);
customerList = new QListWidget(p);
l->addWidget(customerList);
customerList->addItems(QStringList()
<< "John Doe, Harmony Enterprises, 12 Lakeside, Ambleton"
<< "Jane Doe, Memorabilia, 23 Watersedge, Beaton"
<< "Tammy Shea, Tiblanka, 38 Sea Views, Carlton"
<< "Tim Sheen, Caraba Gifts, 48 Ocean Way, Deal"
<< "Sol Harvey, Chicos Coffee, 53 New Springs, Eccleston"
<< "Sally Hobart, Tiroli Tea, 67 Long River, Fedula");
dock->setWidget(new QWidget());//hide the real dock area
dock->setTitleBarWidget(p); //use the titlebar erea to show the content
The demo:
Drag the edge of the panel to move, actually you can drag the empty area (no child widget area). The widget on this panel still functional properly.
I also think that setTitleBarWidget() really does the trick. I remember seeing it being used for a similar purpose in the source code of the Amarok music player. Amarok has a QMainWindow which only contains dock widgets. You might want to have a look at the source code there.
It looks like you've set your dock tab position to be on the top. The default is for it to be on the bottom. Then it's not as visually jarring to have the tab text right next to the title bar text.
I don't think there's any way to do what you're proposing in Qt (eliminate the QDockWidget title bar and drag from the tab), at least not with the standard widgets. You could probably write a lot of custom code to make it happen, but that's probably not worth it.
Instead, I'd suggest moving the tabs to the bottom (see QMainWindow::setTabPosition) or possibly one of the sides.