Enable QMenu access for a disabled QToolButton - qt

I have a QToolButton with an attached QMenu which includes a couple of QActions.
One of those actions is the button's default action. The default action dynamically changes when clicking on the actions and this works great.
Now, these QActions get enabled and disabled by signals. When exactly the (current) default action get's disabled, the QToolButton gets disabled, too.
This results in an inaccessible QMenu which still contains enabled QMenu entries (QActions) that I want to be able to select.
So: Can I somehow still make the Menu available when the default action is getting a setEnabled(false)? Or are there some other Ideas?

I did the following to exactly resolve the issue as described:
In my QToolButton class I override eventFilter as follows:
bool MultiToolButton::eventFilter( QObject* inObject, QEvent* inEvent )
{
MultiToolButton* _multiToolButton =
dynamic_cast<MultiToolButton*>(inObject);
QMouseEvent* _mouseEvent = dynamic_cast<QMouseEvent*>(inEvent);
if(_multiToolButton
&& _mouseEvent && _mouseEvent->type() == QEvent::MouseButtonPress)
{
mMenu.setEnabled(true);
showMenu();
return true;
}
return QToolButton::eventFilter(inObject, inEvent);
}
and add the following line to the contructor:
installEventFilter(this);
Thank you for your answer nonetheless. However, I didn't check it.

Naively what you can do is to write a small function that you call whenever you disable a QAction to see if it is the current default action with somethinglike this:
void MyDialog::on_button_clicked()
{
action2->setEnabled( false );
checkForDefault(action2);
}
void MyDialog::checkForDefault(QAction *action)
{
if ( tButton->defaultAction() == action ) {
QList<QAction*> list = tButton->menu()->actions();
int index = list.indexOf(action);
QAction *newDefault = list.at( ( index+1 ) % list.count() );
tButton->setDefaultAction(newDefault);
tButton->setEnabled(true);
}
}
This will check if the action that was changed is the current default action of the button and if so will select the next QAction within the QMenu as the new default (or the first action if the disabled action is the last in the list).
A different way would probably be to have your own class inheriting from QToolButton and to overload its actionEvent ( QActionEvent * event ) method to perform what you need.
Is this of any help?

The problem of the arrow being disabled can be solved by the following stylesheet, which replaces the standard arrow with an image located in url path_to_arrow. This image does not changed when the button is disabled :
QToolButton::menu-indicator,::menu-button
{
}
QToolButton::menu-arrow
{
image: url("path_to_arrow");
}

Related

How to programmatically close QMenu

I have pretty specific situation. I want to place a QAction into QToolbar and reach following behaviour:
Checkable QAction with icon.
Classic arrow on the right side which is used for showing menu
By pressing this arrow my QDialog should appears on screen instead of QMenu-like one
Now I'm a bit confused with implementing all this things together.
For now I've created QAction added it to toolbar and also created an empty QMenubecause I didn't get the idea of how to add the "dropdown" arrow another way.
So, I also connected my slot to QMenu aboutToShow() signal and now, I can create my dialog and exec() it just before QMenu shows. But here's the main problem appears: after I did everything with my dialog an click OK button QMenu trying to appear, but as it is empty it shows nothing and further actions become available only after I left-click somwhere to "close" this menu.
Is there any way to force QMenu not to show or can inherit from QMenu and reimplemnt its behaviour (I've tried to do such trick with exec() show() popup() methods of QMenu after subclassing from it, but none of them are being called when menu appears on the screen) ?
Here's the solution, which worked for me.
class QCustomMenu : public QMenu
{
Q_OBJECT
public:
QCustomMenu(QObject *parent = 0):QMenu(parent){};
};
In code:
QAction* myActionWithMenu = new QAction ( "ActionText", toolbar);
QCustomMenu* myMenu = new QCustomMenu(toolbar);
connect(myMenu, SIGNAL(aboutToShow()), this, SLOT(execMyMenu()));
execMyMenu() implementation:
void execMyMenu(){
m_activeMenu = (QCustomMenu*)sender(); // m_activeMenu -- private member of your head class, needs to point to active custom menu
QMyDialog* dlg = new QMyDialog();
// setup your dialog with needed information
dlg->exec();
// handle return information
m_myTimer = startTimer(10); // m_myTimer -- private member of your head(MainWindow or smth like that) class
}
Now we have to handle timerEvent and close our menu:
void MyHeadClass::timerEvent(QTimerEvent *event)
{
// Check if it is our "empty"-menu timer
if ( event->timerId()==m_myTimer )
{
m_activeMenu->close(); // closing empty menu
killTimer(m_myTimer); // deactivating timer
m_myTimer = 0; // seting timer identifier to zero
m_activeMenu = NULL; // as well as active menu pointer to NULL
}
}
It works great on every platform and does what I wanted.
Hope, this would help someone. I've spent week trying to find this solution.

Determine the default returned DialogCode for a QDialog with a QDialogButtonBox

I've created a custom dialog class inheriting from QDialog. I allow the user to call exec(timeoutMs) such that the dialog will automatically close after timeout. I'd like the dialog to finish rather than just close (which doesn't finish). Said differently, I'd like the dialog to return as if the default button was pushed if the timeout expires. Is there a good way to determine the default button and it's role in a QDialog? This is what I have now:
void MyDialog::timeout()
{
int result = QDialog::Rejected;
foreach (QAbstractButton *btn, buttonBox()->buttons()) {
QPushButton *pbtn = qobject_cast<QPushButton *>(btn);
if (pbtn
&& pbtn->isDefault()
&& buttonBox()->buttonRole(btn) == QDialogButtonBox::AcceptRole)
result = QDialog::Accepted;
}
done(result);
}
Is there a better way?

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.

QListView - mixed drag mode

I have a QListView with the ViewMode set to IconMode. I would like to achieve the following DnD behavior:
If a list view item is dragged inside the view, only the items position in the view is changed. This is the same as setting DragDropMode equal to InternalMove.
If a list item is moved out of the view, it can be copied to another external view. In this case, DragDropMode is equal to DragOnly.
How do I mix the two modes in such a way that both behaviors are supported by the view?
You might be able to do this by overriding the dropEvent of your view like this:
void MyListView::dropEvent( QDropEvent* e )
{
if( e->source() != this )
{
// something comes from the outside
// what to do? return?
return;
}
else
{
// event comes from the view itself, let's do some stuff
// for example call the base class default event
QAbstractItemView::dropEvent(e);
}
}
I guess the correct flag would be QAbstractItemView::DragDrop to do this.

Should I use KeyPressEvent or QAction to implement keypresses?

In Qt, either implementing keyPressEvent or creating a QAction and assigning it a key combination allow me to act based on the keyboard.
Which of these methods is generally preferred?
You should use QAction whenever the same event that is triggered by the key sequence you want may be triggered through other ways like from a menu, toolbar or other buttons. This way you can use the same action on several widgets that should do the same trick.
Excerpt from QAction doc:
The QAction class provides an abstract
user interface action that can be
inserted into widgets.
In applications many common commands
can be invoked via menus, toolbar buttons, and
keyboard shortcuts. Since the user
expects each command to be performed
in the same way, regardless of the
user interface used, it is useful to
represent each command as an action.
I'd prefer to overwrite the keyPressEvent. I don't like the idea of a QAction "lying around somewhere". Just overwrite the keyPressedEvent. I usually do it with a switch-case in which I check the pressed key. Just don't forget to call the keyPressEvent of the base class if you don't want to disable the standard behaviour of a key. Additionally you can check if a "modifier" is pressed while a keyPressEvent occurs. (e.g. Shift or Ctrl). IMHO for general purposes overwriting the keyPressEvent is better than creating invisible, secret actions, unless you want your application to contain all those actions visible for the user.
void my_widget::keyPressEvent( QKeyEvent* p_event )
{
bool ctrl_pressed = false;
if( p_event->modifiers() == Qt::ControlModifier )
{
ctrl_pressed = true;
}
switch( p_event->key() )
{
case Qt::Key_F:
focus_view();
break;
case Qt::Key_I:
if( ctrl_pressed )
{
toggle_interface();
}
else
{
QWidget::keyPressEvent( p_event );
}
break;
case Qt::Key_Return: // return key
case Qt::Key_Enter: // numpad enter key
update_something();
break;
default:
QSpinBox::keyPressEvent( p_event );
}
}
Would depend on what you need it for.
Is it for a menu like action that may be triggered by a menu, button, toolbar too, then go for the QAction. Especially if this action should work all over your program, not only in a single widget.
Is it more like a local activity in a single widget (say for example controlling movement in a game), I would use the keypress event.

Resources