How to change the label property of a QTabWidget on double click? - qt

How do I recognize a double click on a tab in order to change its label?
Preferrably I could edit the label in place but alternatively I could also get a string from another input box. Any suggestions?
The tab gets added and the label specified currently like:
QString tab_label = QString("Shell (") + QString::number(session->id(), 16) + ")";
addTab(session->widget(), tab_label);
and I'd want to be able to edit the label after creation.
Oh and I should mention here that I'm a Qt newbie, too!
EDIT1
full method:
int SessionStack::addSession(Session::SessionType type)
{
Session* session = new Session(type, this);
connect(session, SIGNAL(titleChanged(int,QString)), this, SIGNAL(titleChanged(int,QString)));
connect(session, SIGNAL(terminalManuallyActivated(Terminal*)), this, SLOT(handleManualTerminalActivation(Terminal*)));
connect(session, SIGNAL(activityDetected(Terminal*)), m_window, SLOT(handleTerminalActivity(Terminal*)));
connect(session, SIGNAL(silenceDetected(Terminal*)), m_window, SLOT(handleTerminalSilence(Terminal*)));
connect(session, SIGNAL(destroyed(int)), this, SLOT(cleanup(int)));
m_sessions.insert(session->id(), session);
QString tab_label = QString("Shell (") + QString::number(session->id(), 16) + ")";
addTab(session->widget(), tab_label);
emit sessionAdded(session->id());
raiseSession(session->id());
return session->id();
}

There's a QTabBar::tabBarDoubleClicked signal, you just need to connect it to a slot to detect a double click. Also you'll need some widget to actually edit the tab's text. If you want it "out of place" (say, you open a dialog) then it should be enough to do something like:
connect(tabWidget->tabBar(), &QTabBar::tabBarDoubleClicked,
this, MyWidget::editTabBarLabel);
void MyWidget::editTabBarLabel(int tabIndex)
{
if (tabIndex < 0)
return;
// open dialog asking for the new label,
// f.i. via QInputDialog::getText
// set the new label bakc
}
If instead you want some in-place modification you'll need to more or less heavily modify QTabBar to do so.
The simplest option would be opening a QLineEdit on the right tab. To get the geometry of a tab via QTabBar:.tabrect, so that you can place the line edit in the same geometry. You'll very likely fall short on that path (see below) and you'll need to subclass QTabBar and use initStyleOption for the given tab, then set the lineedit's geometry to the right subrect (for instance, do not cover the "side widgets" of a tab).
Random pseudo braindumped code:
void MyTabBar::editTabBarLabel(int tabIndex)
{
if (tabIndex < 0)
return;
if (!m_lineEdit) {
m_lineEdit = new QLineEdit(this);
// once done, commit the change, or abort it, anyhow
// destroy/hide (?) the line edit
connect(m_lineEdit, &QLineEdit::editingFinished,
this, &MyTabBar::renameLabel);
} else {
// we're actually editing something else, what to do?
// commit the other change and start editing here?
}
m_editedTabIndex = tabIndex; // remember which one we're editing
m_lineEdit->setText(tabText(tabIndex));
// not "entirely" accurate, you need better subrect control here,
// cf. QStyle and https://doc.qt.io/qt-5/style-reference.html#widget-walkthrough
// that's why this should really be a QTabBar subclass, because
// you'll need to invoke initStyleOption and then fetch the subrects from the style
m_lineEdit->setGeometry(tabRect(tabIndex));
m_lineEdit->show();
}
// also track resize of the tabbar, relayout, tab reorder, tab insertion and removal, etc.
// move the QLineEdit accordingly

Related

Adding tab to QTabWidget. is empty?

Well, the thing is I have a tabwidget created in qtcreator, with many tabs and in the tabs there are many lineedit and other objects.
The closeable property of the tabWidget is set to true.
I execute the program and close the tabs, but when I want to reopen the tab, it's empty, I'm using this code:
tabs->addTab(new QWidget(),"TAB 1");
I want to use the same tab create on the design of qtcreator.
Your problem is that you are adding empty widget in your code:
tabs->addTab(new QWidget(),"TAB 1");
Instead you need to keep you widgets and add them like that:
QWidget* widget; // it is stored
int index = ui->tabWidget->addTab(widget, "TAB 1");
Where to take these widgets?
It is not enough to set closable to true, you also to use signal/slot:
connect(ui->tabWidget, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));
And finally:
void YourWindow::closeTab(int index)
{
// do something else
QWidget* widget = ui->tabWidget->widget(index);
ui->tabWidget->removeTab(index);
// here you can remember it and use later when adding tab
}

Bring the dockable widgets to its default position in Qt

I have several dockable widgets and the user can move it, to any place in the screen.
Also sometimes if the user drags out of the viewable area, there is no way to bring it back.
Is there any way to restore the widget position in Qt, to default position?
Store the Widget positions in Registry. Have a default setting while installation and save the screen geometry. Eg:
QSettings settings;
settings.beginGroup("MainWindow");
settings.setValue("MRU", m_RecentFiles);
settings.setValue("maximized", isMaximized());
settings.setValue("minimized", isMinimized());
if(!isMaximized())
{
settings.setValue("size", size());
}
QByteArray array = saveState();
settings.setValue("state", array);
QDesktopWidget desktopWidget;
int nb = desktopWidget.numScreens();
settings.setValue("screenNumber", nb);
for(int i = 0; i < nb; ++i)
{
YString screenName = "screen" + YString::number(i);
QRect rect = desktopWidget.screenGeometry(i);
settings.setValue(screenName.data(), rect);
}
You can use the QMainWindow::saveState() and QMainWindow::restoreState() methods.
saveState returns a QByteArray containing the internal state of the main window, including the state and positions of the dock area's dock widgets. You can, for example, save this array in a file and restore the contents later by calling restoreState().
If you want to have a default position, then position the dock widgets as you like to have it by default, retrieve the byte array (e.g. through some action which is only available in debug mode of your application), put the array hard coded into your source code or into some default configuration file, and then implement some kind of "reset" or "set defaults" action by simply passing this array to the restoreState() method.
Generally you just add a check into the mouseMoveEvent that tests the QWidgets location and then decide if we need to move the widget back on screen.

How can add action with QWidget.addAction()?

In Qt, I want to add some actions in a widget using QWidget.addAction().
I can easily do it with QToolBar.addAction(), but when I use QWidget.addAction(), it doesn't work.
How can I use QWidget.addAction()?
Here is my function:
void Reb::addActionToBar(QString *tabName, QAction *action)
{
//if tab exist, just add the action, else:
tab_widget->addTab(new QWidget(), *tabName);
for(int i = 0 ; i <= tab_widget->count() ; i++) {
if(tab_widget->tabText(i) == tabName) {
action.setParent(tab_widget->widget(i));
tab_widget->widget(i)->addAction(action);
}
}
}
And as you know tab_widget is a QTabWidget...
I have no error but i can't see my action in tab.
QWidget::addAction() does not do add the action to the UI - the only place where the widget's actions are shown is in the widget's context menu, given the right context menu policy.
QTabWidget has no means to display actions in its UI. Actions are usually displayed in toolbars or menubars, so you would need to add the action there.
As a side note, there is no need to pass QStrings by pointer, simply pass the QString by const reference:
void Reb::addActionToBar(const QString &tabName, QAction *action)
Also, your code has an off-by-one error, use i < tab_widget->count() instead of i <= tab_widget->count() to fix that.

How to detect QTableWidget scroll source (code itself/user (wheel)/user (scrollbar))?

I'm writing a program using Qt 4.8 that displays a table (QTableWidget) filled with filenames and file's params. First an user adds files to the list and then clicks process. The code itself updates the contents of the table with simple progress description. I want the table by default to be scrolled automatically to show the last processed file and that code is ready.
If I want to scroll it by hand the widget is being scrolled automatically as soon as something changes moving the viewport to the last element. I want to be able to override the automated scroll if I detect that it was the user who wanted to change view.
This behavior can be seen in many terminal emulator programs. When there's a new line added the view is scrolled but when user forces the terminal to see some previous lines the terminal does not try to scroll down.
How could I do that?
Solution:
I created an object which filters event processed by my QTableWidget and QScrollBar embedded inside. If I spot the event that should turn off automatic scrolling I just set a flag and stop scrolling view if that flag is set.
Everything is implemented inside tableController class. Here are parts of three crucial methods.
bool tableController::eventFilter(QObject* object, QEvent* event)
{
switch (event->type())
{
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::Wheel:
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
_autoScrollEnabled = false;
default:
break;
}
return QObject::eventFilter(object, event);
}
void tableController::changeFile(int idx)
{
[...]
if (_autoScrollEnabled)
{
QTableWidgetItem* s = _table.item(_engine.getLastProcessed(), 1);
_table.scrollToItem(s);
}
[...]
}
void tableController::tableController()
{
[...]
_autoScrollEnabled = true;
_table.installEventFilter(this);
_table.verticalTableScrollbar()->installEventFilter(this);
[...]
}
Thanks for all the help. I hope somebody will find it useful :)
Subclass QTableWidget and overload its wheelEvent. You can use the parameters of the supplied QWheelEvent object in order to determine if the user scrolled up or down.
Then use a simple boolean flag which is set (or reset) in your wheelEvent override. The method which is responsible for calling scrollToBottom() should then consider this boolean flag.
You will have to find a way to figure out when to set or reset that flag, e.g. always set it when the user scrolls up and reset it when the user scrolls down and the currently displayed area is at the bottom.
connect(_table->view()->verticalScrollBar(), &QAbstractSlider::actionTriggered, this, [this](int) {
_autoScrollEnabled = false;
});

How to make QComboBox popup upwards?

my QComboBox-derived class lives in a QGraphicsScene at the bottom end of the (visible) screen - but it pops up downwards, thus out of view.
(How) is it possible to force the popup to open above the widget?
I've tried re-implementing showPopup like this:
void MyComboBox::showPopup()
{
QAbstractItemView *popupView = view();
popupView->move(0,-100);
//popupView->window->move(0,-100);
QComboBox::showPopup();
}
The result is, that the content seems to be shifted, but not the underlying popup object.
I think it might be possible to find a solution with styles as indicated in
this article, but I can't find any Styles control that might be helpful here. I am rather new to C++ as well as Qt, so I might be missing something obvious.
I'd appreciate any help on this matter!
Best regards,
Sebastian
With the information found here, I was able to get it done this way:
void SteuerQComboBox::showPopup() {
QComboBox::showPopup();
QWidget *popup = this->findChild<QFrame*>();
popup->move(popup->x(),popup->y()-this->height()-popup->height());
}
Note that it's crucially important to call the base classes "showPopup" first.
Thanks to everybody who was reading my question and thinking about it!
user1319422's solution isn't bad, but it has two problems.
If your platform has GUI animation, the listbox will animate opening downwards, then is moved above the text box.
If you disable combobox animation (or you don't have it), the call to QComboBox::showPopup() still makes the GUI element start to appear on the screen already. So, moving it there would cause it to flicker as it appears in the first place and moves to the next.
So, to address the first problem, I just switched off animation:
void MyComboBox::showPopup()
{
bool oldAnimationEffects = qApp->isEffectEnabled(Qt::UI_AnimateCombo);
qApp->setEffectEnabled(Qt::UI_AnimateCombo, false);
QComboBox::showPopup();
qApp->setEffectEnabled(Qt::UI_AnimateCombo, oldAnimationEffects);
}
Then, for the second problem, I moved the frame in the Show event:
bool MyComboBox::eventFilter(QObject *o, QEvent *e)
{
bool handled = false;
if (e->type() == QEvent::Show)
{
if (o == view())
{
QWidget *frame = findChild<QFrame*>();
//For some reason, the frame's geometry is GLOBAL, not relative to the QComboBox!
frame->move(frame->x(),
mapToGlobal(lineEdit()->geometry().topLeft()).y() - frame->height());
}
}
/*else if other filters here*/
if (!handled)
handled = QComboBox::eventFilter(o, e);
return handled;
}
if you want to force popup to open above only when it is out of view you can do this:
void SteuerQComboBox::showPopup() {
QComboBox::showPopup();
QWidget *popup = this->findChild<QFrame*>();
if((popup->y() + popup->height()) > this->window()->height())
popup->move(popup->x(),popup->y()-this->height()-popup->height());
}

Resources