Close button only on active tab of QTabWidget - c++

To save space in a QTabWidget, I would like to show the close icon only for the current tab, like e.g. Firefox is doing:
Is there a simple way using a style sheet, some thing like (not working like this)
QTabBar::tab::!selected::close-button {visible: false;}
or do I have to subclass QTabWidget to get the desired behavior?

You won't need to subclass anything, you can use QTabWidget::tabBar() method to obtain a reference (i.e. QTabBar *) to the tab bar associated with your QTabWidget. (Note that this method is no longer protected, so it can be accessed without subclassing the class)
QTabBar *tabBar = tabWidget->tabBar();
You can now use tabBar reference to hide close buttons on non-current tabs. For example to hide ith button, you can do:
tabBar->tabButton(i, QTabBar::RightSide)->hide();
So a simple workflow could be as follows:
Connect QTabWidget::currentChanged(int index) signal to a slot.
In that slot hide all close buttons other than the button at index.

You can subclass QTabWidget to get access to the QTabBar widget using protected method QTabWidget::tabBar. Then you can connect to the QTabBar::currentChanged signal and hide close button for not selected tabs manually:
QTabBar::ButtonPosition closeSide =
(QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this);
for (int i = 0; i < toolbar->count(); ++i)
{
if (i != toolbar->currentIndex())
{
QWidget *w = toolbar->tabButton(i, closeSide);
w->hide();
}
}

hide() leaves empty space for the invisible close button. This looks funny.
Set the width to 0 instead.

Related

Which UI item allows dynamic expansion on the go in Qt?

Suppose I have a single text box and a radio button placed below the text box .
What I want is that if the user enters some text and ALSO clicks the radio button a similar text box and a radio button should appear just below. And similar thing should happen if the user does that for new UI items.
Any suggestions what classes I should be looking for?
The relevant class you need to handle the inserting of new widgets is one of the QLayout subclasses, most likely QVBoxLayout it sounds like. This will allow you to insert a new textbox and radio button at the bottom of the layout, which will then automatically expand to fit these new widgets.
Inserting the new objects is pretty straightforward. Connect a slot to a signal of the QRadioButton that's at the bottom of the layout, like the QRadioButton::toggled or QRadioButton::clicked signals. This slot will check if the above textbox has any text in it, and, if so, insert a new textbox/radio button pair below them.
To be clear, this slot should be a method of the widget that uses the layout to arrange its sub-widgets. For example, if you're putting all these objects in a QGroupBox, then that's the object to which you'd add the slot I'm describing.
Here is an (untested) example:
class Group : public QGroupBox {
Q_OBJECT
public:
Group(QWidget* parent = nullptr) : QGroupBox(parent)
{
layout = new QVBoxLayout(this);
insertNewRow();
}
private slots:
void insertNewRow(bool checked = true) {
/* You might want to make this remove the row if the button is unchecked */
if (!checked)
return;
/* Only add row if text box is non-empty */
if (textBoxes.isEmpty() || textBoxes.last()->text().isEmpty())
return;
/* Disconnect slot for previous radio button */
QObject::disconnect(radioButtons.last(), 0, 0, 0);
/* Add new text box and button, labeled Button 1, Button 2, etc. */
textBoxes.append(new QLineEdit("", this));
radioButtons.append(new QRadioButton(QString("Button %1").arg(textBoxes.size())), this));
/* Connect signal/slot to add new row when clicked */
QObject::connect(radioButtons.last(), &QRadioButton::toggled, this, &Group::insertNewRow);
}
private:
QVBoxLayout* layout;
QList<QLineEdit*> textBoxes;
QList<QRadioButton*> radioButtons;
};

QAbstractItemView Tab Focus While Editing Item

I have a QTreeView populated with items from a model. When a call to edit() is made on an index, a custom editor displays. The editor consists of two QLineEdit widgets.
I want the focus to switch between the two QLineEdit widgets when Tab is pressed. However, pressing Tab cycles through everything else on my program. All my QPushButton and QTabWidget objects are included in the Tab order even though they are completely different widgets than my editor.
I've tried setting the tab order using setTabOrder() to loop it between the two QLineEdit widgets, however this still doesn't isolate the editor widget from the surrounding widgets. Why is this happening?
NOTE: I'm not trying to disable tab ordering anywhere else, just isolate it to my editor for the time being.
Thanks for your time!
This can be easily implemented using QWidget::focusNextPrevChild as follows:
class EditWidget : public QWidget
{
public:
EditWidget(QWidget *pParent) : QWidget(pParent)
{
QHBoxLayout *pLayout = new QHBoxLayout(this);
setLayout(pLayout);
pLayout->addWidget(m_pEdit1 = new QLineEdit ());
pLayout->addWidget(m_pEdit2 = new QLineEdit ());
}
bool focusNextPrevChild(bool next)
{
if (m_pEdit2->hasFocus())
m_pEdit1->setFocus();
else
m_pEdit2->setFocus();
return true; // prevent further actions (i.e. consume the (tab) event)
}
protected:
QLineEdit *m_pEdit1;
QLineEdit *m_pEdit2;
};

QT Editor smart buttons

I have a toolbar with buttons: undo, redo, cut, copy, paste, like below:
editmenu = menuBar()->addMenu(QObject::tr("&Edit"));
undoact = editmenu->addAction(QIcon(":images/undo.png"), QObject::tr("&Undo"));
QObject::connect(editwin, SIGNAL(undoAvailable(bool)), undoact, SLOT(setEnabled(bool)));
QObject::connect(undoact, SIGNAL(triggered()), editwin, SLOT(undo()));
undoact->setShortcuts(QKeySequence::keyBindings(QKeySequence::Undo));
undoact->setEnabled(false);
redoact = editmenu->addAction(QIcon(":images/redo.png"), QObject::tr("&Redo"));
QObject::connect(editwin, SIGNAL(redoAvailable(bool)), redoact, SLOT(setEnabled(bool)));
QObject::connect(redoact, SIGNAL(triggered()), editwin, SLOT(redo()));
redoact->setShortcuts(QKeySequence::keyBindings(QKeySequence::Redo));
redoact->setEnabled(false);
editmenu->addSeparator();
cutact = editmenu->addAction(QIcon(":images/cut.png"), QObject::tr("Cu&t"));
cutact->setShortcuts(QKeySequence::keyBindings(QKeySequence::Cut));
cutact->setEnabled(false);
copyact = editmenu->addAction(QIcon(":images/copy.png"), QObject::tr("&Copy"));
copyact->setShortcuts(QKeySequence::keyBindings(QKeySequence::Copy));
copyact->setEnabled(false);
pasteact = editmenu->addAction(QIcon(":images/paste.png"), QObject::tr("&Paste"));
pasteact->setShortcuts(QKeySequence::keyBindings(QKeySequence::Paste));
I want:
Paste button to be enabled only if clipboard contain text
For some reason, I need to disable all buttons for a limitted period of time. After this I want to enable all buttons. But when I enable, each button to be enabled/disabled according to his condition (eg. undo button to be enabled only if there is something to undo, paste button to be enabled only if there is some text that can be pasted ...)
What do you advise me to do? What is the smartest / simplest method?
http://doc.qt.io/qt-5/qclipboard.html#signals
http://doc.qt.io/qt-5/qclipboard.html#details
Create connections from the QClipboard object to a handler slots in your toolbar/mainwindow class. And then in the handler slot do the logic you want to happen to your toolbar buttons.
For example:
http://doc.qt.io/qt-5/qclipboard.html#dataChanged
// in your constructor after creating your buttons:
QObject::connect(qApp->clipboard(), SIGNAL(dataChanged()),
this, SLOT(onClipboardDataChanged()));
// and then make another connection for the other relevant clipboard signal...
// selectionChanged()
Then later in in your slot:
void MainWindow::onClipboardDataChanged()
{
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
pasteact->setEnabled(mimeData->hasText());
// or you could check the length of the text to see if it is non zero.
}
To disable/renable all the buttons at once, disable the parent object instead.
editToolbar->setEnabled(false);
//... after some period of time or transactions...
editToolbar->setEnabled(true);
But this probably won't disable the built-in accelerators for Copy, Cut and Paste. To kill those keyboard shortcuts, too, you may need to put an event filter on your app.
http://doc.qt.io/qt-5/accelerators.html
http://doc.qt.io/qt-5/qobject.html#eventFilter
http://doc.qt.io/qt-5/qobject.html#installEventFilter
Hope that helps.
PS. I haven't tested this chunk of code. Read the docs. Good luck.

Do I need to check one by one to know which radiobutton is checked in a group in Qt

I know that I can use this kind of code to know which radio button is checked in Qt:
int checkButton;
if( ui->radioButton_0->isChecked() ){
checkButton = 0;
}else if(ui->radioButton_1->isChecked()){
checkButton = 1;
}else if
...
Are there any easier way to know which radio button is checked in a group in Qt. I think it is really helpful if there is such kind of easier way when the group of radio button is large. Code may look like that:
int checkbutton = groupName.getCheckButtonIngroup();
Also we can put a few radiobuttons in groupbox in Qt Designer and after this find children of groupbox, add children to buttonGroup and use the checkedId or checkedButton methods.
void MainWindow::on_pushButton_15_clicked()
{
QButtonGroup group;
QList<QRadioButton *> allButtons = ui->groupBox->findChildren<QRadioButton *>();
qDebug() <<allButtons.size();
for(int i = 0; i < allButtons.size(); ++i)
{
group.addButton(allButtons[i],i);
}
qDebug() << group.checkedId();
qDebug() << group.checkedButton();
}
First of all you need to add all radio buttons to a button group. There are two ways to do that:
In Qt Designer select all radio buttons you want to add and select Assign to button group from the context menu.
From the code. Create a new QButtonGroup and add the buttons there with QButtonGroup::addButton.
Then anytime you can get to know what button is checked:
If you need a pointer of a checked button use QButtonGroup::checkedButton.
If you need a number of the button you need to add buttons to the group manually with addButton(QAbstractButton* button, int id). After this use QButtonGroup::checkedId to get an identifier of the checked button.

Modify a tab in a QTabWidget where each tab represents a QTableView

I have a tab widget where every tab is a QTableView. I would like to be able to pass an updated model (QModelIndex) into each tab whenever the contents of that tab need to change.
The alternative (and nastier way) is for me to delete all the tabs, and then recreate them.
I know I can get the widget in the tab by doing something like:
tabWidget->widget(i);
This will return a widget, which is really a QTableView, but I want to update the model that is in that widget without having to delete and recreate the tab.
Thank you!
P.S. This is my current attempt...
for (int i = 0; i < tableView.size(); i++)
{
tabWidget->setCurrentWidget(tableView.at(i));
QTableView* updatedTable = (QTableView*)tabWidget->currentWidget();
updatedTable->setModel(dataModel);
tableView.replace(i, updatedTable);
}
It's not clear why you can't keep the QTableView widget and just change the model, as in your code. Doesn't the view refresh without this tableView.replace thing?
There doesn't appear to be a direct API for replacing the widget you put in with addTab() without going through a tab removal step. But instead of inserting the QTableView directly, you could instead call addTab() on a dummy widget that has a layout in it with a single item. A QStackedLayout, for instance:
QWidget* dummy = new QWidget;
QStackedLayout stackedLayout = new QStackedLayout;
stackedLayout->addWidget(tableView);
dummy->setLayout(stackedLayout);
tabWidget->addTab(dummy);
Then later, when you want to replace the tableView with a new one:
QWidget* dummy = tabWidget->currentWidget();
QStackedLayout newStackedLayout = new QStackedLayout;
newStackedLayout->addWidget(newTableView);
delete dummy->layout();
dummy->setLayout(newStackedLayout);
I still wonder what this is buying you that reusing the old table view couldn't do.