Because QTabWidget inherits QWidget we have setWindowModified() available.
But it seems it doesn't work for the tab title:
ui->tab1->setWindowTitle(QString("%1[*]").arg(tr("Tab title")));
ui->tab1->setWindowModified(true);
but it doesn't show the '*' nor changes the tab text.
Is there a way to handle this automatically instead of manually use setTabText()?
I don't think there's any way to get the tab text to follow the widget title by default. Having said that, it should be very easy to fix up by overriding QTabWidget::tabInserted.
class tab_widget: public QTabWidget {
using super = QTabWidget;
using this_class = tab_widget;
public:
using super::super;
protected:
virtual void tabInserted (int index) override
{
super::tabInserted(index);
if (auto *w = widget(index)) {
connect(w, &QWidget::windowTitleChanged, this, &this_class::handle_window_title_change);
}
}
virtual void tabRemoved (int index) override
{
super::tabRemoved(index);
if (auto *w = widget(index)) {
disconnect(w, &QWidget::windowTitleChanged, this, &this_class::handle_window_title_change);
}
}
private:
void handle_window_title_change (const QString &title)
{
if (auto *w = qobject_cast<QWidget *>(sender())) {
setTabText(indexOf(w), title);
}
}
};
Using the above class rather than QTabWidget should result in the tab text mirroring the title of the widget associated with that tab.
Related
I have a class StackedWidget which inherits from QStackedWidget. The standard behaviour of QStackedWidget is that it adopts the size of its biggest element. What I want to do is force it to resize to its current element. I have already tried a couple of solutions found here and none of them work including e.g. setting size policies or calling hide() on all the widgets that go out of sight.
I think problem may reside in two possibilities:
1. Something is terribly wrong with StackedWidget. 2. The problem of resizing does not pertain directly to StackedWidget but to some layout or another widget that StackedWidget is nested in.
Concerning the first possibility, here I provide the StackedWidget class.
Please take a look at what my StackedWidget's declaration looks like:
class StackedWidget : public QStackedWidget
{
Q_OBJECT
private:
QComboBox* widgetChooser = nullptr;
public:
StackedWidget(QWidget* parent) : QStackedWidget(parent) {}
void setWidgetChooser(QComboBox* widgetChooser);
void addWidget(QWidget* widget);
private:
void makeConnection();
signals:
public slots:
void setCurrentIndex(const int& index);
};
Here goes the definition:
void StackedWidget::setWidgetChooser(QComboBox* widgetChooser)
{
if (widgetChooser == nullptr) throw RuntimeError("widgetChooser is nullptr");
this->widgetChooser = widgetChooser;
makeConnection();
}
void StackedWidget::addWidget(QWidget* widget)
{
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QStackedWidget::addWidget(widget);
}
void StackedWidget::makeConnection()
{
connect(widgetChooser, SIGNAL(currentIndexChanged(int)), this, SLOT(setCurrentIndex(int)));
}
void StackedWidget::setCurrentIndex(const int& index)
{
qDebug() << "Index changed to " << index;
QWidget* pWidget = widget(index);
Q_ASSERT(pWidget);
QStackedWidget::setCurrentWidget(pWidget);
pWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
pWidget->adjustSize();
adjustSize();
}
Concering the second possibility, here goes how I use StackedWidget.
void SchemaWidget::TempBuildAlt(const SchemaElement* son, QVBoxLayout* parentLayout)
{
if (auto schemaAlternative = dynamic_cast<const SchemaAlternatives*>(son))
{
QComboBox* widgetBox = BuildWidgetChooserBox(schemaAlternative);
StackedWidget* stackedWidget = BuildStackedWidget(schemaAlternative);
stackedWidget->setWidgetChooser(widgetBox);
QHBoxLayout* boxLayout = new QHBoxLayout;
boxLayout->addWidget(new QLabel(schemaAlternative->Name, this));
boxLayout->addWidget(widgetBox);
QVBoxLayout* layout = new QVBoxLayout;
layout->addLayout(boxLayout);
layout->addWidget(stackedWidget);
parentLayout->addLayout(layout);
} else throw RuntimeError("Cannot cast son to SchemaAlternatives.");
}
QComboBox* SchemaWidget::BuildWidgetChooserBox(const SchemaAlternatives* schema)
{
QComboBox* alternativesBox = new QComboBox(this);
QStringListModel* listModel = new QStringListModel(this);
QStringList list;
for (int i = 1; i <= schema->Sons.High(); ++i)
{
if (const SchemaTree* son = dynamic_cast<const SchemaTree*>(schema->Sons(i).Get()))
{
list << son->Name;
}
else throw RuntimeError("Son cannot be cast to SchemaTree.");
}
listModel->setStringList(list);
alternativesBox->setModel(listModel);
return alternativesBox;
}
StackedWidget* SchemaWidget::BuildStackedWidget(const SchemaAlternatives* schema)
{
StackedWidget* stackedWidget = new StackedWidget(this);
for (int i = 1; i <= schema->Sons.High(); ++i)
{
if (const SchemaTree* alternativeSon = dynamic_cast<const SchemaTree*>(schema->Sons(i).Get()))
{
QWidget* widget = new QWidget(this);
QVBoxLayout* widgetLayout = new QVBoxLayout;
BuildWidget(alternativeSon, widgetLayout);
widget->setLayout(widgetLayout);
stackedWidget->addWidget(widget);
}
else throw RuntimeError("Son could not be cast to SchemaTree.");
}
return stackedWidget;
}
I want to say thanks in advance to anyone who will be willing to spend some time on my problem. I do appreciate it. Thanks. You're great.
I have subclassed QTreeWidget, QTreeWidgetItem and QListWidgetItem. I am trying to add my custom QListWidgetItem to QListWidget but its not getting displayed in the view.
I Have created a dialog. I have inserted a QTreeWidget and QListWidget.
What I want is when I click a Item in QTreeWidget, The QListWidget should get populated with the my custom QListWidgetItems i.e. CustomListWidgetItems contained in the clicked CustomTreeWidgetItem (QTreeWidgetItem derived class).
If I use QListWidgetItem instead of CustomListWidgetItems, every thing works fine.
Header file contents:
class CustomTreeWidgetItem : public QTreeWidgetItem
{
public:
explicit CustomTreeWidgetItem(int type = Type);
public:
QList<CustomListWidgetItem*> projects;//Items to populate ListWidget
};
class CustomListWidgetItem : public QListWidgetItem
{
public:
explicit CustomListWidgetItem(const QIcon & icon, const QString & text, QListWidget * parent = 0, int type = Type);
CustomListWidgetItem(const int& id,QString Name,QString Description,QString icon);
public:
int Id;
QString Name;
QString Description;
QString Icon;
};
Source contents of Custom Listwidgetitem:
CustomListWidgetItem::CustomListWidgetItem(const QIcon &icon, const QString &text, QListWidget *parent, int type) : QListWidgetItem(icon,text,parent,type)
{
}
CustomListWidgetItem::CustomListWidgetItem(const int& id,QString Name,QString Description,QString icon)
{
this->Id = id;
this->Name = Name;
this->Description = Description;
this->Icon = icon;
QString iconPath = QCoreApplication::applicationDirPath() + "/" + icon + ".png";
QIcon qIcon(iconPath);
QListWidgetItem::QListWidgetItem(qIcon,Name);
}
Source contents:
void CustomTreeWidget::treeitemSelected(QTreeWidgetItem * item, int)//Tree item click event
{
CustomTreeWidgetItem *project = static_cast<CustomTreeWidgetItem*>(item);
if(LISTWIDGET)
{
m_listWidget->clear();
m_listWidget->setSelectionMode(QAbstractItemView::SingleSelection);
int count = project->projects.count();
if(count == 0)
{
m_listWidget->addItem("No Projects Here");//This gets displayed
m_listWidget->setSelectionMode(QAbstractItemView::NoSelection);
}
else
{
for (int i = 0; i < count; ++i)
{
m_listWidget->addItem(project->projects[i]);//This does not get displayed
}
}
}
}
During Debugging I can see that the data i.e Name, Description, etc. is present in "project->projects[i]". No error is shown during addItem function call still the items are not visible in view.
Few questions:
- Why do I have the feeling you mix listwidgetitem and treewidgetitem?
- Why do you fill your view everytime an item is selected? is it initialized?
- Can you show where you create the view and start adding items?
Check you have the following:
create a qtreewidget that you add to your view layout
add a root node using something like
QTreeWidgetItem *treeItemRoot = new QTreeWidgetItem(myTreeWidget);
treeItemRoot->setText(0, projectName);
add children nodes
QTreeWidgetItem *treeItemChild = new QTreeWidgetItem();
treeItemRoot->addChild(treeItem);
and post the results.
I created a left QDockWidget, and added 2 QLabel into it.
I want a squared size for my labels (by default 100x100).
So when the user increases the width of the QDockWidget, how should I force my labels to keep a squared size?
I tried a lot of things, without any success :(
I think you shall overwrite the int QWidget::heightForWidth( int aWidth ) const function. So you need to create your own QLabel subclass.
class MyLabel
{
public:
virtual int heightForWidth( int aWidth ) const override
{
return aWidth; // So it will be square.
}
};
I think I have the solution.
I create this subclass :
class SquareLabel : public QLabel
{
public:
SquareLabel(QWidget* parent = 0) : QLabel(parent)
{
setMinimumSize(100, 100);
QSizePolicy pol(QSizePolicy::Preferred, QSizePolicy::Preferred);
pol.setHeightForWidth(true);
setSizePolicy(pol);
}
virtual int heightForWidth(int w) const { return w; }
virtual QSize sizeHint() const { int w = this->width(); return QSize(w, heightForWidth(w)); }
protected:
void resizeEvent(QResizeEvent* event) { }
};
All my labels are added in a QVBoxLayout, and I need this property :
layout->setAlignment(Qt::AlignTop);
Now, I need to resize my pixmap in my label.
I will post it before close this question.
I want to show a text like "Please select one option" on the combobox, and don't show the text in the list, so I set setEditable to true, then set the text to the lineEdit, but after this, only the dropdown button (arrow) is clickable, how can we make the entire combobox clickable? I'm using the QComboBox as below:
QComboBox* combbox= new QComboBox;
combbox->setEditable(true);
combbox->lineEdit()->setReadOnly(true);
combbox->addItem("Option1");
combbox->addItem("Option2");
combbox->lineEdit()->setText("Please select one option");
I resolved this problem as below:
class QTComboBoxButton : public QLineEdit
{
Q_OBJECT
public:
QTComboBoxButton(QWidget *parent = 0);
~QTComboBoxButton();
protected:
void mousePressEvent(QMouseEvent *);
};
QTComboBoxButton::QTComboBoxButton(QWidget *parent /* = 0 */) :
QLineEdit(parent)
{
}
QTComboBoxButton::~QTComboBoxButton()
{
}
void QTComboBoxButton::mousePressEvent(QMouseEvent * e)
{
QComboBox* combo = dynamic_cast<QComboBox*>(parent());
if(combo)
combo->showPopup();
}
QComboBox* combbox= new QComboBox;
combbox->setEditable(true);
combbox->setLineEdit(new QTComboBoxButton(combbox));
combbox->lineEdit()->setReadOnly(true);
combbox->addItem("Option1");
combbox->addItem("Option2");
combbox->lineEdit()->setText("Please select one option");
Making a QComboBox editable is problematic in terms of UI.
I propose a different approach, reimplement a QComboBox and make a default item and remove it if the user clicks the combobox:
#include "mycombo.h"
MyCombo::MyCombo(QWidget *parent) :
QComboBox(parent),
defaultText_("Please select one option")
{
addItem(defaultText_);
}
void MyCombo::mousePressEvent(QMouseEvent* event)
{
if(this->currentText() == defaultText_)
{
this->removeItem(0);
}
QComboBox::mousePressEvent(event);
}
And then just create this combobox and insert the items where you want
MyCombo *combbox = new MyCombo(this);
combbox->addItem("Option1");
combbox->addItem("Option2");
You can use this lib : libqxt
You can find it here : https://bitbucket.org/libqxt/libqxt/wiki/Home
Use the object QxtCheckComboBox, with it you can check multiple items in your ComboBox.
The answer from IdlChina has one drawback: when you click the combobox when it's already shown, it hides and immediately shows again. I have a slightly different approach that doesn't have that problem.
class ButtonComboBox : public QComboBox
{
public:
ButtonComboBox(QWidget *parent = nullptr)
: QComboBox(parent),
_isPopupShown(false),
_timer(new QTimer(this))
{
auto lineEdit = new QLineEdit;
lineEdit->installEventFilter(this);
setLineEdit(lineEdit);
_timer->setSingleShot(true);
_timer->setInterval(100);
}
protected:
bool eventFilter(QObject *object, QEvent *event) override
{
if (object == lineEdit() && event->type() == QEvent::MouseButtonPress) {
if (!_timer->isActive()) {
if (!_isPopupShown)
showPopup();
else if (_isPopupShown)
hidePopup();
return true;
}
}
return false;
}
void showPopup() override
{
QComboBox::showPopup();
_isPopupShown = true;
_timer->start();
}
void hidePopup() override
{
QComboBox::hidePopup();
_isPopupShown = false;
_timer->start();
}
private:
bool _isPopupShown;
QTimer *_timer;
};
I have a problem connecting context menu actions in Qt. I know there are plenty of similar questions around, nevertheless I couldn't find a solution yet.
I have a series of plots built using QCustomplot.
What I want to do is to create a context menu when right-clicking on the background of each plot listing all the signal present in the graph. By clicking an entry of this menu, the corresponding signal should be hidden (if currently visible) or made visible (if hidden).
Now, I've defined a class called PlotHandler of which I paste the relevant parts here below:
plotHandler.cpp
#include "plothandler.h"
PlotHandler::PlotHandler(QStringList groupNames, int startIdx, QWidget *parent) :
QWidget(parent), scrolling(false), refreshing(true)
{
pPlot = new QCustomPlot(this);
pPlot->setContextMenuPolicy(Qt::CustomContextMenu);
connect(pPlot, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequest(QPoint)));
}
void PlotHandler::contextMenuRequest(QPoint pos)
{
int i;
QMenu *menu = new QMenu(this);
for(i=0; i<pGroup->getDataLength(); i++)
{
QAction *menuEntry;
menuEntry = new QAction(pPlot->graph(i)->name(), this);
menuEntry->setProperty("graphIdx", i);
menu->addAction(menuEntry);
connect(menuEntry, SIGNAL(triggered()), this, SLOT(addRemoveGraph()));
}
menu->popup(pPlot->mapToGlobal(pos));
}
void PlotHandler::addRemoveGraph()
{
QAction *selectedSignal = qobject_cast<QAction *>(sender());
int tmp = selectedSignal->property("graphIdx").toInt();
if (pPlot->graph(tmp)->visible())
{
pPlot->graph(tmp)->setVisible(false);
}
else
{
pPlot->graph(tmp)->setVisible(true);
}
}
plotHandler.h
class PlotHandler : public QWidget
{
Q_OBJECT
public:
explicit PlotHandler(QStringList groupNames, int startIdx, QWidget *parent = 0);
QString groupRequested();
private:
QCustomPlot *pPlot;
public slots:
void contextMenuRequest(QPoint pos);
void addRemoveGraph();
}
The menu is correctly showed with the right entries and when I click on an action addRemoveGraph is called. In debug it gives back the following message:
The inferior stopped because it triggered an exception. Stopped in
thread 0 by: Exception at 0x5d6c2f9a, code: 0xc0000005: read access
violation at: 0x0, flags=0x0.
trying to execute
int tmp = selectedSignal->property("graphIdx").toInt();
Could anyone point me towards the right direction?
Thanks in advance
You use QObject::setProperty but QAction doesn't have a property named "graphIdx". When you attempt to read the "graphIdx" property from QAction you will always get an invalid QVariant.
int tmp = selectedSignal->property("graphIdx").toInt();
// tmp always is 0;
You can use QAction::setData if you need to store only one property. Otherwise, use QObject::setProperty to set a custom property on any QObject. QAction is a QObject.
Here below the solution that solved the issue.
plotHandler.cpp
#include "plothandler.h"
PlotHandler::PlotHandler(QStringList groupNames, int startIdx, QWidget *parent) :
QWidget(parent), scrolling(false), refreshing(true)
{
pPlot = new QCustomPlot(this);
pPlot->setContextMenuPolicy(Qt::CustomContextMenu);
connect(pPlot, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequest(QPoint)));
}
void PlotHandler::contextMenuRequest(QPoint pos)
{
int i;
QMenu *menu = new QMenu(this);
for(i=0; i<pGroup->getDataLength(); i++)
{
QAction *menuEntry;
menuEntry = new QAction(pPlot->graph(i)->name(), this);
menuEntry->setData(i);
menu->addAction(menuEntry);
}
connect(menu, SIGNAL(triggered(QAction*)), this, SLOT(addRemoveGraph(QAction*)));
menu->popup(pPlot->mapToGlobal(pos));
}
void PlotHandler::addRemoveGraph(QAction *selectedSignal)
{
int tmp = selectedSignal->property("graphIdx").toInt();
if (pPlot->graph(tmp)->visible())
{
pPlot->graph(tmp)->setVisible(false);
}
else
{
pPlot->graph(tmp)->setVisible(true);
}
pPlot->replot();
}
plotHandler.h
class PlotHandler : public QWidget
{
Q_OBJECT
public:
explicit PlotHandler(QStringList groupNames, int startIdx, QWidget *parent = 0);
QString groupRequested();
private:
QCustomPlot *pPlot;
public slots:
void contextMenuRequest(QPoint pos);
void addRemoveGraph();
}
Thanks everyone for your help.