Adding event to the context menu in QPlainTextEdit - c++

This is my Context Menu after right click on QPlainTextEdit. I want to add function to load data from file in Context Menu. Can I? How?

Method 1: QPlainTextEdit::contextMenuEvent
You should override the QPlainTextEdit::contextMenuEvent as mentioned in the Qt documentation:
void MyQPlainTextEdit::contextMenuEvent(QContextMenuEvent *event)
{
QMenu *menu = createStandardContextMenu();
menu->addAction(tr("My Menu Item"));
//...
menu->exec(event->globalPos());
delete menu;
}
You can connect the QAction::triggered signal to your method (slot) to load the data or you can use one of the QMenu::addAction overloads, which allows you to specify a slot directly.
If you do not want to subclass QPlainTextEdit (to override contextMenuEvent), you can use event filtering in Qt.
Note that contextMenuEvent() is only called when contextMenuPolicy is not set (or set to its default value Qt::DefaultContextMenu)
Method 2: QWidget::customContextMenuRequested
As an alternative, you can use Qt's signal and slot mechanism to create the context menu when requested by the user.
The contextMenuPolicy property should be set to Qt::CustomContextMenu, in which case the QWidget::customContextMenuRequested signal is invoked whenever a context menu is requested by the user. This signal should be connected to your own slot, which should create the context menu as shown in the code above (Method 1).
Using MyQPlainTextEdit in Qt Designer
To use your MyQPlainTextEdit in a .ui file, you should implement it as a promoted QPlainTextEdit and use it in your .ui file instead of a regular QPlainTextEdit. See the Qt documentation for more information.
To be able to use your class in the Qt Designer, you should not forget to implement a constructor accepting a parent QWidget as is done in the AnalogClock example. Note that implementing such a constructor is always a good idea, because Qt typically manages ownership through a child-parent relationship.

Building on #m7913d answer.
The downside to the techniques is you must derive from the QPlainTextEdit class for a very minor extension. My preferred method, especially when using designer based widgets, is to add an eventFilter, and filter out the mouse event that is a mousebutton press
MyWidget::MyWidget(...)
{
...
ui->plainTextEdit->installEventFiler( this )
}
MyWidget::eventFilter( QObject * obj, QEvent * event )
{
if ( ( obj == ui->plainTextEdit )
&& ( event->type() = QEvent::MouseButtonPress )
&& ( dynamic_cast< QMouseEvent * >( event )->buttons() & Qt::MouseButton::RightButton )
{
// create menu
auto menu = ui->plainTextEdit->createStandardContextMenu();
// modify menu
menu->exec( mouseEvent->globalPos() );
delete menu;
return true;
}
return false;
}

Related

Senders objectName is absent | QT & Cpp

I have created a little UI with a simple toolbar. I am initing the toolbar within the initToolbarfunction. This function is within a Class inheriting from QMainWindow.
void Main_Frame::initToolBar() {
rectangle.setText(rectangle_text);
circle.setText(circle_text);
triangle.setText(triangle_text);
triangle.setObjectName(triangle_id);
circle.setObjectName(circle_id);
rectangle.setObjectName(rectangle_id);
toolBar.addAction(&triangle);
toolBar.addAction(&circle);
toolBar.addAction(&rectangle);
connect(
&toolBar, &QToolBar::actionTriggered, this, [=]() {
process_toolbar_ac_evt(*sender());
});
}
I want any tool bar events to be processed through process_toolbar_ac_ect(QObject obj). Within that method, I want to decide what action (within the toolbar) has been triggered.I want to do this by the objectname. Therefore I have given any action an object name. But when I call sender().objectName() I get an empty QString.
My suggestion is, that sender returns a pointer to on of my actions that I put to the toolbar. If this is the case, why I get an empty QString on the sender()?
void Main_Frame::process_toolbar_ac_evt(QObject &evt) {
if (evt.objectName() == circle_id) {
// If circle action has clicked, to this ...
}
}
As you are connecting to one of QToolBar's signals the sender() will be your tool bar object.
Simply use the QAction argument that is passed in the QToolBar::actionTriggered signal. That's what it is for.
NB: Avoid QObject::sender() wherever possible. It virtually breaks the desired decoupling achieved by signals and slots.

How to use Threading in QDialog to fill data in QComboBox?

I have 10 QComboBox in QDialog. And i am filling data into these combobox while opening Dialog, It will take 7 second to load Dialog, how to solved this issue because there is no clicked slot in combobox . I think we can solved this issue by using Thread but i have no idea of QThread.
The solution is not easy because QT does not allow for widgets to be modified outside main thread.
Simple solution:
Use threads to put the data into QStringLists and set it using addItems.
QThreadPool is more simple to use for this usecase.
This solution will not help if the problem is the size of the data itself, not the processing.
Complex solution (nice for learning):
You can however re-implement the model of the widget (QAbstractItemModel), and use a QThread in the model.
Also you can create your own QListView derivered class that on loads data when setVisible is called for the first time with true - just overwrite setVisible(bool).
Set an instance of this class to each combo box with setView().
On how to use QThread, see examples, read tutorials... It is to complex to explain here, but there is full of examples and tutorials on the net.
Later Edit:
There is also possible to use event filters:
// Install event filter
MyDlg::MyDlg()
{
qApp->installEventFilter ( this );
}
// search for the view of the combo to be activated
// and fill it.
bool MyDlg::eventFilter ( QObject * obj, QEvent * event )
{
QSet<QAbstractItemView *> myComboViews;
my_combos.insert(combo1->view());
if ( event->type () == QEvent::Show )
{
QAbstractItemView * view = qobject_cast<QAbstractItemView*>(obj);
if ( myComboViews.contains(obj) && view->isVisible() )
{
fillComboView(view);
}
}
return QDialog::eventFilter ( obj, event );
}
The code is untested, but you can get the ideea.

How to resize a QLabel displayed by a QWidgetAction after changing it's text

I use a QWidgetAction to add a header to a context menu (that will also show up on Windows, no matter what style is used, in contrast to addSection(), which does not always actually display the title).
The action's widget is a QLabel. It's text is changed by each invocation of the context menu. The menu is setup in the constructor of my class, and the QWidgetAction is added like so (all m_ variables are member variables declared in the header):
m_contextMenu = new QMenu(this);
m_menuTitle = new QLabel;
m_menuTitle->setAlignment(Qt::AlignCenter);
m_menuTitle->setMargin(4);
QWidgetAction *titleAction = new QWidgetAction(m_contextMenu);
titleAction->setDefaultWidget(m_menuTitle);
m_contextMenu->addAction(titleAction);
m_contextMenu->addSeparator();
When the menu is requested, the text of the label is changed and the menu is displayed like so:
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
...
m_contextMenu->exec(place_to_display);
When the label's text is set for the first time (with a short text the label's text is set to), everything is fine:
but when it's set to some longer text, the size remains the same and the text is cropped:
I tried to fix this, but the only working solution I found was to define the QActions displayed in the menu in the constructor, owned by this, setting the label's text, clearing the menu and adding the actions again, like so:
m_contextMenu->clear();
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
m_contextMenu->addAction(m_menuTitleAction);
m_contextMenu->addSeparator();
m_contextMenu->addAction(m_editAction);
m_contextMenu->addAction(m_deleteAction);
m_contextMenu->exec(place_to_display);
Is there a way to resize the title without rebuilding the menu each time?
The solution is to send a resize event instead:
m_menuTitle->setText(tr("%1 „%2“").arg(some_variable, some_other_variable));
...
QResizeEvent re(new_size, m_contextMenu->size());
qApp->sendEvent(m_contextMenu, &re);
This will set the QMenu's internal itemsDirty flag and will force geometry recalculation when the menu is shown. Note that the new size in the event does not matter, as the menu will aways resize based on its sizeHint()!
The QResizeEvent solution didn't really work for me (with a more complex widget), I found the generic solution by reading the QMenu and QAction source code.
m_widget->installEventFilter(this);
// and in my case m_widget->layout()->setSizeConstraint(QLayout::SetFixedSize);
bool SomeClass::eventFilter(QObject* watched, QEvent* event)
{
if (watched == m_widget && event->type() == QEvent::Resize) {
// Force QMenu to recalculate the geometry of this item
QActionEvent e(QEvent::ActionChanged, this);
qApp->sendEvent(m_contextMenu, &e);
}
...
}
QActionEvent triggers everything we need in QMenu: recalculating geometries, resizing the menu to its new size (if it's visible), etc.
This answer extends user362515's answer.
There is little more effort required if you change the size of a hidden widget action (e.g., because of its menu is currently collapsed).
Create a new class ActionWidget which derives publicly from QWidget.
Then override the showEvent method and implement it like this:
void ActionWidget::showEvent(QShowEvent* event)
{
QResizeEvent resize_event(QSize(), parentWidget()->size());
parentWidget()->adjustSize();
qApp->sendEvent(parentWidget(), &resize_event);
QWidget::showEvent(event);
}
Notice that adjustSize must be called on the parent widget of the action widget and the event must be sent to the parent widget.
Of course, you must also reimplement QWidgetAction::createWidget such that it returns an instance of the ActionWidget-class and make sure that ActionWidget reports a proper (updated) size hint.

How do I assign a shortcut to a QPushButton?

The documentation on assigning a shortcut to a QPushButton is as follows:
A shortcut key can be specified by preceding the preferred character with an ampersand in the text. For example:
QPushButton *button = new QPushButton("&Download", this);
In this example the shortcut is Alt+D.
What do I do if I don't want an Alt+[A-Z] shortcut? For example, in my case I want my button to be fired when the TAB button is pressed. How can I achieve this effect?
You can use setShortcut method, eg:
pushButton->setShortcut(QKeySequence(Qt::Key_Tab));
It will fire then slots assigned to the clicked() signal
You can use a QShortcut. Another tip: Qt signal / slots mechanism allows you to connect a signal to a signal.
You can overload QWidget::keyPressEvent and trigger your button click directly or through previously specially connected signal
void MainWindow::keyPressEvent(QKeyEvent * pEvent)
{
if (Qt::Key_Tab == pEvent->key())
{
ui->button->click();
}
QMainWindow::keyPressEvent(pEvent);
}
You can define the key, the target object and it's relevant slot in the constructor of QShortcut :
QShortcut * shortcut = new QShortcut(QKeySequence(Qt::Key_Tab),button,SLOT(click()));
shortcut->setAutoRepeat(false);

Clickable menu item with submenu in Qt

I am writing in Qt 4.6. I am wondering if it's possible to achieve such a menu item, that it's possible to be triggered, but also has a submenu. Clicking it triggers associated action, hovering it causes submenu to appear.
Let me start by saying that this is not a good plan of attack. There are corner cases here that will take a rediculous amount of time and code to get just right, and will probably require per-operating system customization.
With that said, however, the actual implementation isn't too complicated. Just subclass the QMenu that you're making your submenu from, and override the event handlers, forcing the parent menu closed when a 'selection' is made. Something like the following basically works:
from PyQt4 import QtCore, QtGui
import sys
app = QtGui.QApplication(sys.argv)
widget = QtGui.QMainWindow()
widget.resize(250,150)
menu = widget.menuBar().addMenu("test")
class submenu(QtGui.QMenu): #Override the submenu class
def __init__(self,name):
QtGui.QMenu.__init__(self,name)
def mouseReleaseEvent(self,event): #catch mouseRelease Events
global menu
QtGui.QMenu.mouseReleaseEvent(self,event)
if not self.rect().contains(event.pos()):
print("Parent Selected")
menu.hide() #If the parent was selected, hide it
else: #Likely ignore these
print("Parent NOT Selected")
def c():
print("Sub-item selected")
cMenu = submenu("Sub-menu")
menu.addMenu(cMenu)
actionC = QtGui.QAction("sub-item",widget)
actionC.triggered.connect(c)
cMenu.addAction(actionC)
widget.show()
sys.exit(app.exec_())
This behavior is a bit confusing, but i am trying to develop a UI with as little clicking as possible. Although a bit unexpected, this behavior makes it a bit faster to use when you get used to it.
I haven't wrote that in my previous message, but i am writing in c++, and i have no idea about python... Anyway i managed to translate idea to c++, and it works but it's quite ugly... I found a bit better approach by looking through qt source (when i was asking this question I was hoping there is some better, "intended" method)
class MyMenu : public QMenu
{
Q_OBJECT
public:
MyMenu(QWidget* parent);
~MyMenu();
virtual void mouseReleaseEvent( QMouseEvent * event );
};
MyMenu::MyMenu(QWidget* parent):QMenu(parent)
{
}
MyMenu::~MyMenu()
{
}
void MyMenu::mouseReleaseEvent( QMouseEvent * event ){
QAction* act = menuAction();
if (act){
QMenu* men = act->menu();
act->setMenu(0);
QMenu::mouseReleaseEvent(event);
act->setMenu(men);
}else{
QMenu::mouseReleaseEvent(event);
}
}
The only disadvantage is that such a menu would react to clicking on all options with submenus, not only desired ones. Perhaps it would be a good idea to check if anything is connected to action's signals?
On my ubuntu it works. However i guess it wouldn't work on windows, where system manages menus exclusively (unless qt uses some windows with menu's look and feel, not system menus), but i am too lazy to instal windows just to check it ;)
void CustomMenu::mouseReleaseEvent( QMouseEvent* event )
{
QAction* action = menuAction();
// Filter out submenus. TODO: Is there a better way to do this?
if ( action && !rect().contains( event->pos() ) ) {
QMenu* menu = action->menu();
action->setMenu( 0 );
QMenu::mouseReleaseEvent( event );
emit customTriggeredSignal();
action->setMenu( menu );
}
else {
QMenu::mouseReleaseEvent( event );
}
}
Combining both helpful answers, from j_kubik and jkerian, this mouseReleaseEvent() in the top-level menu subclassing QMenu ignores unwanted invocations of submenus.
I will report back as to whether the approach is suitable on Windows.