QT Editor smart buttons - c++

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.

Related

Overwrite Copy Text operation from a QTreeWidget

I have a QTreeWidget where I would to overwrite the Copy Text (ctrl+c) from the QTreeWidgetItem.
The default behaviour is to copy the text from the selected column, but I'd like to update the values with more information that I have on the background.
Is there a native way of doing it without capturing the ctrl+c with a keyPressEvent ?
You can try the following approach to store your own stuff in clipboard without overriding QWidget::keyPressEvent():
// Assuming tree widget already exists.
auto shortcut = new QShortcut(QKeySequence("Ctrl+C"), treeWidget);
QObject::connect(shortcut, &QShortcut::activated, [treeWidget] () {
auto selected = treeWidget->selectedItems();
// Get only first selected item's text.
if (selected.size() > 0)
{
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(QString("Custom clipboard text: %1").arg(selected.at(0)->text(0)));
}
});
In the code I used QShortcut to handle Ctrl+C key sequence and store selected tree widget item's text (customized) into the clipboard.

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

Close button only on active tab of QTabWidget

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.

Fail to clear QLineEdit after selecting item from QCompleter

using PopupCompletion mode when you select an item (using arrow keys) and press return - lineEdit should become empty (i clear lineEdit when return is pressed), but lineEdit does not become empty. (If you press 'Enter' again it will empty the lineEdit). So i think pressing return does clear lineEdit, but pressing return also tells QCompleter to insert selected item into lineEdit, so it seems like nothing happens.
But, if you click the item insted of selecting it with arrows - everything works fine.
I tried to find the solution on the internet, but i found only one person that had the same problem: http://lists.trolltech.com/qt-interest/2006-10/thread00985-0.html . Sadly there are no answers. Please read his question because it will help understand my problem.
How can I clean LineEdit after QCompleter inserted selected item? (catching activated signal does not help)
The issue here is that the completer actually contains a pop-up, which is actually a separate QAbstractItemView widget (refer to the QCompleter::popup() documentation). As such, when you press 'Enter' on the QCompleter, the key event actually goes to the pop-up and not the line edit.
There are two different ways to resolve your issue:
Option 1
Connect the completer's activated signal to the line edit's clear slot, but do it as a QueuedConnection:
QObject::connect(completer, SIGNAL(activated(const QString&)),
lineEdit, SLOT(clear()),
Qt::QueuedConnection);
The reason why using a direct connection doesn't work is because your are essentially dependent on the order in which slots get called from a signal. Using a QueuedConnection gets around this. From a code maintenance standpoint, I don't really prefer this solution because it isn't clear what your intention is just by looking at the code.
Option 2
Write an event filter around the pop-up to filter out the 'Enter' key to clear the line edit explicitly. Your event filter would end up looking something like this:
class EventFilter : public QObject
{
Q_OBJECT
public:
EventFilter(QLineEdit* lineEdit, QObject* parent = NULL)
:QObject(parent)
,mLineEdit(lineEdit)
{ }
virtual ~EventFilter()
{ }
bool eventFilter(QObject* watched, QEvent* event)
{
QAbstractItemView* view = qobject_cast<QAbstractItemView*>(watched);
if (event->type() == QEvent::KeyPress)
{
QKeyEvent* keyEvent = dynamic_cast<QKeyEvent*>(event);
if (keyEvent->key() == Qt::Key_Return ||
keyEvent->key() == Qt::Key_Enter)
{
mLineEdit->clear();
view->hide();
return true;
}
}
return false;
}
private:
QLineEdit* mLineEdit;
};
You would then install the event filter on the completer's pop-up:
EventFilter* filter = new EventFilter(lineEdit);
completer->popup()->installEventFilter(filter);
This option is more work, but it's clearer as to what you are doing. Moreover, you can perform additional customization this way, if you prefer.

Disabling a QCheckbox in a tricky way

I want to make a QCheckBox named "Show Captions" disable another QCheckBox named "Show captions if no title" when the first is checked, but my problem is that how I can make it disabled immediately when the user checks the first checkbox.
SetupSlideShow::SetupSlideShow(QWidget* parent)
: QScrollArea(parent), d(new SetupSlideShowPriv)
{
QWidget* panel = new QWidget(viewport());
setWidget(panel);
setWidgetResizable(true);
QVBoxLayout* layout = new QVBoxLayout(panel);
d->showComment = new QCheckBox(i18n("Show captions"), panel);
d->showComment->setWhatsThis( i18n("Show the image caption at the bottom of the screen."));
d->showTitle = new QGroupBox(i18n("Show title"), panel);
d->showTitle->setWhatsThis( i18n("Show the image title at the bottom of the screen."));
d->showTitle->setCheckable(true);
d->showCapIfNoTitle = new QCheckBox(i18n("Show captions if no title"), panel);
d->showCapIfNoTitle->setWhatsThis( i18n("Show the image caption at the bottom of the screen if no titles existed."));
QVBoxLayout *vbox = new QVBoxLayout;
vbox->addWidget(d->showCapIfNoTitle);
d->showTitle->setLayout(vbox);
layout->addWidget(d->showLabels);
layout->addWidget(d->showComment);
layout->addWidget(d->showTitle);
}
Doesn't this work?
connect(d->showComment, SIGNAL(toggled(bool)), d->showCapIfNoTitle, SLOT(setDisabled(bool)));
The call to paintEvent() isn't really doing anything for you regarding immediacy. Nothing will be repainted until control returns to the event loop (after your constructor exits). It is more typical to call update() but even this is unnecessary when changing the properties of built in widgets.
To link the check boxes, define a slot for the stateChanged() signal of showComment, connect the signal to your slot in your constructor above (by calling connect(), and in that slot, call d->showCapIfNoTitle->setCheckState(d->showComment->checkState()).