In Qt5 it can be done in QML like this.
MouseArea {
onWheel: {
if (wheel.modifiers & Qt.ControlModifier) {
adjustZoom(wheel.angleDelta.y / 120);
}
}
}
How to achieve the same in Qt4.8?
Looks like I'm reinventing the bicycle but here is how it works for now.
I have a new QWidget that catches the wheelEvent and sends a new signal.
void WheelEventCarrier::wheelEvent(QWheelEvent *event)
{
emit sendWheelEvent(event->delta()/120);
}
I define a new signal in QML and process it ibid.
signal wheelEvent(int delta)
onWheelEvent:
{
if(delta > 0)
tag_meas_mod.zoomIn(true);
else
tag_meas_mod.zoomOut(true);
}
I wrap my widget around all my UI and connect the two signals.
WheelEventCarrier carrier;
UI.setParent(&carrier);
QObject::connect(&carrier,
SIGNAL(sendWheelEvent(int)),
viewer.rootObject(),
SIGNAL(wheelEvent(int)));
carrier.show();
Hope you can point me to a better solution.
Related
I have QGraphicsView, which has many QGraphicsItem. I am trying to create a right click menu on these QGraphicsItem. Right click menu has multiple options. But only 1st option works. It means, if I click on 2nd option, it does not work. If I change the sequence ( means 1st one will go to 2nd position, and 2nd one will come to 1st position ) then still 2nd one will not work.
bool myClass::eventFilter(QObject *watched, QEvent *event)
{
switch(event->type())
{
case QEvent::ContextMenu:
{
foreach(QGraphicsItem* pItem, _scene->items())
{
if(pItem->isUnderMouse())
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent*> (event);
menu = new QMenu(this);
myMenu = menu->addMenu("Copy");
myMenu ->addAction(Name);
myMenu ->addAction(Address);
if(Name == menu->exec(mouseEvent->globalPos()))
{
// logic
}
if(Address == menu->exec(mouseEvent->globalPos()))
{
// logic
}
}
}
}
}
Always works only 1st mouse right click option. Why is so ?
The usual way to do something like this is to override the QGraphicsItem::mouseReleaseEvent() or QGraphicsItem::mousePressEvent() function of your item class.
This way, you won't have to do anything (no looping, etc...), it is already handled by the event loop.
Here you can find a simple example:
void MyItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
if(event->button() == Qt::RightButton)
{
QMenu my_menu;
// Build your QMenu the way you want
my_menu.addAction(my_first_action);
my_menu.addAction(my_second_action);
//...
my_menu.exec(event->globalPos());
}
}
From the Qt documentation:
Note that all signals are emitted as usual. If you connect a QAction to a slot and call the menu's exec(), you get the result both via the signal-slot connection and in the return value of exec().
You just need to QObject::connect() the QActions you added to the context menu to the proper slots (here goes the "logic") and the job is done.
If you prefer to check the returned value by yourself, you just have to get the returned QAction* once and for all (only one call to QMenu::exec()) and branch on it.
For example:
void MyItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
if(event->button() == Qt::RightButton)
{
QMenu my_menu;
// Build your QMenu the way you want
my_menu.addAction(my_first_action);
my_menu.addAction(my_second_action);
//...
QAction * triggered = my_menu.exec(event->globalPos());
if(triggered == my_first_action)
{
// Do something
}
else if(triggered == my_second_action)
{
// Do some other thing
}
//...
}
}
I would personnally prefer to stick with the signal-slot connections instead that manually handling the returned value, especially since each QAction is most likely to be already connected to its corresponding slot.
I have set a large menu in event filter on right click with 45-50 actions
inside and I find that when I right click the response to show the menu is slow
I did try the same code with 5 actions in the menu and the response was fine.
Is there something wrong with this way of coding on a contex menu ?
eventFilter
bool Editor::eventFilter(QObject *o, QEvent *e)
{
Q_UNUSED (o);
QGraphicsSceneMouseEvent *me = (QGraphicsSceneMouseEvent*) e;
switch ((int) e->type()){
case QEvent::GraphicsSceneMousePress:{
switch ((int) me->button()){
case Qt::RightButton:{
QGraphicsItem *item = itemAt(me->scenePos());
showContextMenu(item->scenePos().toPoint());
return true;
}
//more cases here//
}
break;
}
}
return QObject::eventFilter(o, e);
}
showContextMenu
void Editor::showContextMenu(const QPoint &pos)
{
QGraphicsItem *item =itemAt(pos);
// Create main effe menu
effeMenu= new QMenu("Menu");
QString menuStyle(
"QMenu {"
"border:10px };"
//more code here
);
effeMenu->setStyleSheet(menuStyle);
AmpMenu=effeMenu->addMenu(QIcon(":/effectImg/img/effePng/amp.png"),"Amp");
Amp1 =AmpMenu->addAction(QIcon(":/effectImg/img/effePng/amp.png"),"Amp 1");
Amp2 =AmpMenu->addAction(QIcon(":/effectImg/img/effePng/amp.png"),"Amp 2");
CabMenu=effeMenu->addMenu(QIcon(":/effectImg/img/effePng/cab.png"),"Cab");
Cab1 =CabMenu->addAction(QIcon(":/effectImg/img/effePng/cab.png"),"Cab 1");
Cab2 =CabMenu->addAction(QIcon(":/effectImg/img/effePng/cab.png"),"Cab 2");
.
.
.
.
//45 actions more
connect(effeMenu, &QMenu::triggered,this,[this,&item](QAction * k){
menuSelection(k,item);
});
Instead of creating a new QMenu each time you call showContextMenu you could make it a member of the class and build it once. On the other hand it is not necessary to use a signal, you could simply use the exec() method of QMenu:
*.h
class Editor: ...{
...
private:
QMenu effeMenu;
}
*.cpp
Editor::Editor(...){
effeMenu.setTitle("Menu");
QString menuStyle(
"QMenu {"
"border:10px };"
//more code here
);
effeMenu.setStyleSheet(menuStyle);
AmpMenu=effeMenu.addMenu(QIcon(":/effectImg/img/effePng/amp.png"),"Amp");
Amp1 =AmpMenu->addAction(QIcon(":/effectImg/img/effePng/amp.png"),"Amp 1");
Amp2 =AmpMenu->addAction(QIcon(":/effectImg/img/effePng/amp.png"),"Amp 2");
CabMenu=effeMenu.addMenu(QIcon(":/effectImg/img/effePng/cab.png"),"Cab");
Cab1 =CabMenu->addAction(QIcon(":/effectImg/img/effePng/cab.png"),"Cab 1");
Cab2 =CabMenu->addAction(QIcon(":/effectImg/img/effePng/cab.png"),"Cab 2");
...
}
void Editor::showContextMenu(const QPoint &pos){
QGraphicsItem *item =itemAt(pos);
QAction *action = menu.exec(pos);
menuSelection(action, item);
}
There are two things you can do to improve speed:
1 - itemAt(pos) is costly, and you are doing it twice, one in the event, and one in the showContextMenu. From what I could understand from your code you don't need the item in the event, just in the showMenu.
2 - The menu creation that you are doing is expensive: all the actions have pixmaps. this allocs memory for the QPixmap, loads, execute, dumps. Because you told us that you use around 40 actions (and really, that's too much for a menu), this can get costly.
My advice:
Create a class for your menu, create one instance of it, add a setter for the current QGraphicsObject that your menu will work on, and always use that one instance.
I build Qt gui and have many windows to handle with.
I implement this meanwhile with QStackedWidgets (replace the window in buttons click signal), but I am not sure this is the right way.
Can I maintain a lot of windows in this technique ?
what is the prefered way / best practice ?
This is piece of my code (relevant):
ui->pagesWidget->addWidget(new Menu);
ui->pagesWidget->addWidget(new Repetitive);
ui->pagesWidget->addWidget(new SinglePulse);
void MainWindow::on_btnSinglePulse_clicked()
{
ui->pagesWidget->setCurrentIndex(1);
}
void MainWindow::on_btnMenu_clicked()
{
ui->pagesWidget->setCurrentIndex(0);
}
void MainWindow::on_btnPulseGroup_clicked()
{
ui->pagesWidget->setCurrentIndex(2);
}
I think it's fine, but maybe you could simplify your code if you create only one slot, and check the sender() in that.
void onButtonClicked()
{
if ( sender() == ui->button0 )
{
ui->pagesWidget->setCurrentIndex( 0 );
}
else if ( sender() == ui->button1 )
{
ui->pagesWidget->setCurrentIndex( 1 );
}
// ... and so on.
}
Or you could just simply use a QTabWidget.
I'm attempting to forward all key press events from my QGraphicsView to a widget that is currently on the scene.
My QGraphicsView looks like this:
Character_controller::Character_controller(Game_state * game_state) : Game_base_controller(game_state) {
this->character = new Character(this->game_state);
this->scene->addWidget(this->character);
connect(this, SIGNAL(keyPress(QKeyEvent *)), this->character, SLOT(update()));
}
And then, my character which subclasses QWidget, which should recieve all keypress events
Character::Character(Game_state * game_state) : Base_object(game_state) {
}
Character::~Character() {
}
void Character::update() {
cout << "HELLO FROM TIMER CONNECTED ITEM" << endl;
}
For some reason, this isn't working. How can I forward all keypress events from the view to my character?
The error I get is this:
Object::connect: No such signal game::Character_controller::keyPress(QKeyEvent *) in implementation/game_controllers/character_controller.cpp:21
keyPress(QKeyEvent*) doesn't exist as a signal, hence the error message that you're getting. As such, you can't do this:
connect(this, SIGNAL(keyPress(QKeyEvent *)), this->character, SLOT(update()));
In order to capture key press events in your graphics view, you will need to override the keyPressEvent function:
void Character_controller::keyPressEvent(QKeyEvent* event)
{
// Call functions on your character here.
switch (event->key())
{
case Qt::Key_A:
character->moveLeft(); // For example
break;
case Qt::Key_D:
character->moveRight(); // For example
break;
...
}
// Otherwise pass to QGraphicsView.
QGraphicsView::keyPressEvent(event);
}
You could just pass the QKeyEvent to the character to manage its own key presses, but you might find it difficult to ensure that different items in your scene don't rely on the same key(s) if you don't keep all your key press handling code in one place.
You have to override the keyPressEvent event to capture key press events
I have a qdialog, with a buttonbox at the bottom; Why isn't this slot not getting fired when a "signal" occurs? The code look like the following:
std::unique_ptr<MW::GenStd> box(new MW::GenStd(&tOut, &tIn));
box->ui.ChoiceButtons->addButton ("Ask",
QDialogButtonBox::AcceptRole );
box->ui.ChoiceButtons->addButton ("OverWrite",
QDialogButtonBox::AcceptRole );
box->ui.ChoiceButtons->addButton ("merge",
QDialogButtonBox::AcceptRole );
box->ui.ChoiceButtons->addButton ("Skip",
QDialogButtonBox::RejectRole );
QObject::connect(box->ui.ChoiceButtons, SIGNAL(clicked(QPushButton* b)), box.get(), SLOT(OnClick(QPushButton* b)));
return box->exec();
Where MW::GenStd is a dialog box (and ui.ChoicButtons a buttonbox). The modal dialog is correctly displayed - however it doesn't seem to interact at all.. Pressing the buttons doesn't fire the event. The slot is declared like the following:
public slots:
void OnClick(QPushButton* b) {
auto s(b->text());
if (s == "Merge") {
setResult(2);
} else if (s == "Overwrite") {
setResult(1);
} else if (s == "Skip") {
setResult(0);
} else if (s == "Ask") {
setResult(3);
}
}
};
(I know it's terribly to do such a string comparison here, but it's just as a quick mock up test to validate the buttons). But debugging shows the function isn't ever reached!
EDIT: as suggested looking at the output showed a culprit:
Object::connect: No such signal QDialogButtonBox::clicked(QPushButton*) in AskGUISupport.cpp:150
However that seems totally strange as the QDialogButtonBox does have a clicked signal? documentation
Do not use variable names in connect:
QObject::connect(box->ui.ChoiceButtons, SIGNAL(clicked(QPushButton*)),
box.get(), SLOT(OnClick(QPushButton*)));
QDialogButtonBox has a signal clicked ( QAbstractButton * button ) so you need to define a slot void OnClick(QAbstractButton* b) and connect to it. Use QAbstractButton, not QPushButton.
QDialogButtonBox class does not has signal
clicked(QPushButton*).
It has clicked ( QAbstractButton*) insted.
You should be very precise in signatures, when using signals/slots mechanisms. Any casts not allowed, because Qt uses strings internally to check signatures.
You should use clicked (QAbstractButton*) signature and adjust your slot to accpet QAbstractButton*. Make a slot
void OnClick(QAbstractButton* b);