Create tab with UI programmatically - c++

This is my UI:
This is tab created programmatically:
ui->tabWidget->addTab(new QWidget(), "Tab 2");
I want the newly created tab to have the exact same layout as the "Chat room" tab has. Any advice on how to do this would be great.

The easiest way to do this (in Designer) is to create a new UI Form Class called something like ChatTab and base it on QWidget.
Move (i.e., cut and paste) your chat room widgets and layout from your MainWindow UI form to the ChatTab form, but leave the QTabWidget container in your MainWindow form. If you want a chat room to be in your main window when it first opens, promote its first QWidget to a ChatTab from inside Designer.
Any additional tabs should be added programatically like so:
ui->tabWidget->addTab(new ChatTab(), "Tab 2");
Add fancy stuff to the ChatTab constructor if you want to make your life easier.

You should create a custom QTabWidget for this layout, as name ChatWidget, and make a factory API like this:
ChatWidget * ChatWidget::creater(TabWidget * tw, ChatData * cd) {
ChatWidget * cw = ChatWidget.create();
tw.addTab(cast<QWidget>cw, 0);
// some init
...
return cw;
}
Long time not use Qt and C++, may be this give you a hint.

Use your current chatRoom class as a base class and do some layout stuff in it;
Next time you want to create a new tab, just do something like:
ui->tabWidget->addTab(new chatRoom(), "Tab 3");
Hope to help.

Related

Qt Desktop on Mac: QFormLayout sizing with subpanels

TL;DR: I'm having a grow/shrink probably using embedded forms inside a MainWindow. I'm unsure what to try next.
Okay, I have another sizing problem.
This is a sample app of what I'm trying to do:
When I click on the various toolbar options, I intend to change the central widget contents accordingly. Maybe I should just use a tab widget, but I wanted to do it this way.
In the simplest form, with a widget layout like this:
I set the central widget's layout to Horizontal, and the Inner Widget to FormLayout then set the inner widget's expand rules to expand any expandable fields. As I resize the window, the simple line edit expands and contracts as desired.
When I click the bus icon in the toolbar, I swap out the contents of the central widget with a separate panel. That panel has a widget with a form layout, and is also set to expand and collapse. Here are the layout rules for the second panel:
My trigger code does this:
currentCenter = ui->innerWidget; // In the constructor
currentCenter->hide();
if (v1Form == nullptr) {
v1Form = new V1Form(ui->centralWidget);
}
v1Form->show();
currentCenter = v1Form;
I have tried various orders to this, and I tried using setCentralWidget(). In all cases, the new central area remains a fixed size, even though the original one expands and collapses.
What is working: I can readily change the inner contains for different forms. That's working great. (It took a while to figure it out.)
-or- I can make simple popup forms that grow and shrink properly.
What is not working is grow/shrink when I embed my form inside my central widget or if I use setCentralWidget.
I'm not sure what else to try.
Maybe I should just use a tab widget, but I wanted to do it this way.
You should definitely use a QTabWidget as your central widget. It is designed specifically for your use case, and it will greatly simplify your code.
My trigger code does this:
currentCenter = ui->innerWidget; // In the constructor
currentCenter->hide();
if (v1Form == nullptr) {
v1Form = new V1Form(ui->centralWidget);
}
v1Form->show();
currentCenter = v1Form;
With a QTabWidget, your trigger code can be simplified to:
ui->innerTabWidget->setIndex(1).
You don't need to dynamically construct a V1Form. Simply use Qt Designer to create multiple pages in your QTabWidget and implement all your subpanel widgets within your MainWindow.ui.
(Nonetheless, if you want to implement each subpanel in its own separate *.ui file, you can still promote each page in your QTabWidget to your custom widget.)
What is not working is grow/shrink when I embed my form inside my central widget or if I use setCentralWidget.
To address your original symptoms: Your widgets don't grow/shrink because you didn't put them inside a layout that is part of your main window.
I found a solution doing it the way I started. I had to add one line of code:
void MainWindow::switchForm(QWidget *widget) {
if (centralForm != widget) {
if (centralForm != nullptr) {
centralForm->hide();
centralForm = nullptr;
}
if (widget != nullptr) {
centralForm = widget;
centralForm->show();
ui->centralwidget->layout()->addWidget(centralForm);
}
}
}
void MainWindow::on_actionSetup_triggered()
{
if (setupForm == nullptr) {
setupForm = new SetupForm(ui->centralwidget);
}
switchForm(setupForm);
}
The missing line -- adding my new form to the layout:
ui->centralwidget->layout()->addWidget(centralForm);

How to resize a QLabel displayed by a QWidgetAction after changing it's text

I use a QWidgetAction to add a header to a context menu (that will also show up on Windows, no matter what style is used, in contrast to addSection(), which does not always actually display the title).
The action's widget is a QLabel. It's text is changed by each invocation of the context menu. The menu is setup in the constructor of my class, and the QWidgetAction is added like so (all m_ variables are member variables declared in the header):
m_contextMenu = new QMenu(this);
m_menuTitle = new QLabel;
m_menuTitle->setAlignment(Qt::AlignCenter);
m_menuTitle->setMargin(4);
QWidgetAction *titleAction = new QWidgetAction(m_contextMenu);
titleAction->setDefaultWidget(m_menuTitle);
m_contextMenu->addAction(titleAction);
m_contextMenu->addSeparator();
When the menu is requested, the text of the label is changed and the menu is displayed like so:
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
...
m_contextMenu->exec(place_to_display);
When the label's text is set for the first time (with a short text the label's text is set to), everything is fine:
but when it's set to some longer text, the size remains the same and the text is cropped:
I tried to fix this, but the only working solution I found was to define the QActions displayed in the menu in the constructor, owned by this, setting the label's text, clearing the menu and adding the actions again, like so:
m_contextMenu->clear();
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
m_contextMenu->addAction(m_menuTitleAction);
m_contextMenu->addSeparator();
m_contextMenu->addAction(m_editAction);
m_contextMenu->addAction(m_deleteAction);
m_contextMenu->exec(place_to_display);
Is there a way to resize the title without rebuilding the menu each time?
The solution is to send a resize event instead:
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
...
QResizeEvent re(new_size, m_contextMenu->size());
qApp->sendEvent(m_contextMenu, &re);
This will set the QMenu's internal itemsDirty flag and will force geometry recalculation when the menu is shown. Note that the new size in the event does not matter, as the menu will aways resize based on its sizeHint()!
The QResizeEvent solution didn't really work for me (with a more complex widget), I found the generic solution by reading the QMenu and QAction source code.
m_widget->installEventFilter(this);
// and in my case m_widget->layout()->setSizeConstraint(QLayout::SetFixedSize);
bool SomeClass::eventFilter(QObject* watched, QEvent* event)
{
if (watched == m_widget && event->type() == QEvent::Resize) {
// Force QMenu to recalculate the geometry of this item
QActionEvent e(QEvent::ActionChanged, this);
qApp->sendEvent(m_contextMenu, &e);
}
...
}
QActionEvent triggers everything we need in QMenu: recalculating geometries, resizing the menu to its new size (if it's visible), etc.
This answer extends user362515's answer.
There is little more effort required if you change the size of a hidden widget action (e.g., because of its menu is currently collapsed).
Create a new class ActionWidget which derives publicly from QWidget.
Then override the showEvent method and implement it like this:
void ActionWidget::showEvent(QShowEvent* event)
{
QResizeEvent resize_event(QSize(), parentWidget()->size());
parentWidget()->adjustSize();
qApp->sendEvent(parentWidget(), &resize_event);
QWidget::showEvent(event);
}
Notice that adjustSize must be called on the parent widget of the action widget and the event must be sent to the parent widget.
Of course, you must also reimplement QWidgetAction::createWidget such that it returns an instance of the ActionWidget-class and make sure that ActionWidget reports a proper (updated) size hint.

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);

Is it possible to inherit QVBoxLayout and set it as layout in QWidget?

What I'm trying to accomplish:
Create 2 classes that inherit QVBoxLayout simply to set up each class with a series of different objects.
e.g.:
Class 1 (inherits QVBoxLayout), has QLabels to show an appointment and those labels set up with this->addWidget(labels);
Class 2 (inherits QVBoxLayout), has QLineEdits (and so on) to edit an appointment and those objects are also set up with this->addWidget(lineedits);
Is it possible to have a QWidget class then switch between these 2 layouts by calling this->setLayout(class1_object); and this->setLayout(class2_object);?
Or how would you suggest the swapping of the active objects on the widget (when clicking the edit button on the view-part or the save button on the edit-part)?
Simply use object->setShown(false);?
IMO, it's easier to use QTabWidget here.
Make a QTabWidget with 2 tabs. On Tab1, put your labels. On Tab2, put your edits. Call Tab2 something like "Edit the appointment". Now, use the currentChanged() slot for catching the tab switching.
If saving edits should be simple, all you will need to do is just to copy the edited data from edits to labels, and vice-versa.
If saving requires more than that, e.g. you want a confirmation dialog, you can permit the user to change back to Tab1 until some condition is met:
void MainWindow::on_tabWidget_currentChanged(int index)
{
//if the user is trying to go back to Tab1 (where the labels are)...
if(index == 0)
{
//...and if user didn't accept something, we just return him to the current tab
//It's probably a good idea to tell him what went wrong, too :P
if(!userAcceptedSaveDialog())
ui.tabWidget.setCurrentIndex(1);
}
}

QtT How To Save Data/State When Changing Central Widgets In QMainWindow

I have inherited the QmainWindow class to use as the mainwindow for the application that I am building.
I have set the central widget as a pointer to another class, that I have created.
//main window constructor
postEntryWidget = 0; // null pointer to another class that extends QWidget
dataEntryWidget = new Data_Entry_Widget; //extends QWidget
setCentralWidget(dataEntryWidget); //set the widget in the main window
When the user clicks on an action, this sets the central widget to another pointer to another widget class.
/*
*this is the implementation of the slot that would be connected to the QAction
*connected to the postEntryWidget slot
*/
if(!postEntryWidget)
postEntryWidget = new Post_Entry_Widget;
setCentralWidget(postEntryWidget);
/*
*this is the implementation of the slot that would be connected to the QAction
*connected to the dataEntryWidget slot
*/
if(!dataEntryWidget)
dataEntryWidget = new Post_Entry_Widget;
setCentralWidget(dataEntryWidget);
This breaks when switching back and forth between views. And If I add a null point to the preceding view I lose the data when I go back to that view.
/*
*this is the implementation of the slot that would be connected to the QAction
*connected to the postEntryWidget slot
*/
dataEntryWidget = 0; //set the previous widget to a null pointer but loses data
if(!postEntryWidget)
postEntryWidget = new Post_Entry_Widget;
setCentralWidget(postEntryWidget);
How would I keep the state between the two views without creating a custom data structure or is this bad practice. I am most familiar with php and web dev so I am not sure if this is even the best way to go about this.
Thanks in advance
Not totally sure what your goal is. But if you are trying to permit someone the ability to go back to something they were working on, then perhaps you'd be better off using a tab widget instead of hiding the existence of that work?
QTabWidget documentation
Qt Tabbed Dialog example
So you'd make that your central widget, and plug the Post_Entry_Widget and Data_Entry_Widget instances under that. An advantage of that is that Qt manages the tab switching for you.
If you don't want tabs there is also a QStackedWidget, which just lets you programmatically switch between a set of widgets.
It is more complicated than it seems. The problem is, that when setCentralWidget() is called, current centralWidget() gets deleted. In order to preserve its contents, you need to remove it from the window by reparenting it to NULL or 0. Try to change your code to:
if(!postEntryWidget)
postEntryWidget = new Post_Entry_Widget;
if (centralWidget()) centralWidget()->setParent(0); //reparent if exists
setCentralWidget(postEntryWidget);
/*
...
*/
if(!dataEntryWidget)
dataEntryWidget = new Post_Entry_Widget;
if (centralWidget()) centralWidget()->setParent(0); //reparent if exists
setCentralWidget(dataEntryWidget);