I have a QListView named listView. It is the only widget in the MainWindow. I want to track the double clicks on the listView. So, I did this:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
listView = new QListView(this);
this->setCentralWidget(listView);
connect(listView, &QListView::doubleClicked, this, &MainWindow::onDoubleClicked);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow :: onDoubleClicked(const QModelIndex &index)
{
QMessageBox :: information(this, "Info", "List view was double clicked at\nColumn: " + QString :: number(index.column()) + " and Row: " + QString::number(index.row()));
}
But when I double click the listView a get no message box
If the docs are reviewed:
void QAbstractItemView::doubleClicked(const QModelIndex &index)
This signal is emitted when a mouse button is double-clicked. The item
the mouse was double-clicked on is specified by index. The signal is
only emitted when the index is valid.
In your case, your QListView does not have a model, so when you click there is no valid QModelIndex, so the signal will not be emitted.
If you want to follow the double-click event there are 2 possible solutions:
Create a QListView and overwrite the mouseDoubleClickEvent event.
Or use an event filter.
In my solution I will use the second method:
*.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class QListView;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
bool eventFilter(QObject *watched, QEvent *event);
private:
Ui::MainWindow *ui;
QListView *listView;
};
#endif // MAINWINDOW_H
*.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QEvent>
#include <QListView>
#include <QMouseEvent>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
listView = new QListView;
this->setCentralWidget(listView);
listView->viewport()->installEventFilter(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if(watched == listView->viewport() && event->type() == QEvent::MouseButtonDblClick){
QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
qDebug()<<"MouseButtonDblClick"<<mouseEvent->pos();
}
return QMainWindow::eventFilter(watched, event);
}
Related
I have 4 buttons on my main window. Each button opens its own window with its own data. How to identify the pressed button to open right window? For example: I press sales button and it opens a window that shows information about ticket sales.
Mainwindow ui
Here is my code from mainwindow h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <sales.h>
#include <theatres.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void button_pressed();
private:
Ui::MainWindow *ui;
sales *s;
theatres *t;
};
#endif // MAINWINDOW_H
And here is my code from mainwindow cpp:
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "build/sqlite/sqlite3.h"
#include <QtSql/QSqlDatabase>
#include <QTableView>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect((*ui).pushButton,SIGNAL(released()), this, SLOT(button_pressed()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::button_pressed()
{
s = new sales(this);
s -> show();
}
As Andy Newman already answered
the shortest solution is a lambda function
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPushButton>
#include <QHBoxLayout>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QHBoxLayout *h_layout = new QHBoxLayout;
centralWidget()->setLayout(h_layout);
for(int c =1; c <= 10; c++)
{
QPushButton *button = new QPushButton(this); // create button
button->setText(QString::number(c)); // set button id
h_layout->addWidget(button); // add a button to the form
// lambda magic
/* connecting a button signal to a lambda function that captures a pointer to a
button and invokes an arbitrary type function. */
connect(button, &QPushButton::clicked, [this, button]() {
pressedButton(button->text());
});
}
}
void MainWindow::pressedButton(const QString &id_button)
{
qDebug("Pressed button: %ls", id_button.utf16());
}
MainWindow::~MainWindow()
{
delete ui;
}
#include "widget.h"
#include "./ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->btn_0,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
connect(ui->btn_1,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
connect(ui->btn_2,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
connect(ui->btn_3,&QPushButton::clicked,this,&Widget::SlotButtonClicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::SlotButtonClicked()
{
auto sender = this->sender();
if ( sender == ui->btn_0 ) {
// Click btn_0 to open widget0
} else if ( sender == ui->btn_1 ) {
// Click btn_1 to open widget1
} else if ( sender == ui->btn_2 ) {
// Click btn_2 to open widget2
} else if ( sender == ui->btn_3 ) {
// Click btn_3 to open widget3
}
}
If you can use Qt Designer, the best way to do this is to click with button right on the QPushButton (On .ui file in Qt Designer) and click to "Go to Slot", this will create a private slot to this button! In the header file will create the definition, like this:
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
void on_pushButton_4_clicked();
And in the source file (.cpp) will create the "function" clicked pushButton:
void MainWindow::on_pushButton_clicked()
{
}
void MainWindow::on_pushButton_2_clicked()
{
}
void MainWindow::on_pushButton_3_clicked()
{
}
void MainWindow::on_pushButton_4_clicked()
{
}
Inside of the "function" in .cpp, you put the task that you want this button to do, in this case, to open a new window!
When you click "go to slot" in another button, will create another private slot with the respective number (If is the second QPushButton that you create, the private slot will be called by pushButton_2).
The usual way to do this would be to connect the 4 different buttons to 4 different slots. It looks like you are using QtDesigner so that shouldn't be an issue.
If you were generating an array of buttons at run time you'd run into problems and would need a different solution. You could try something like this to pass an array index to the function, for example:
connect(button[x], &QPushButton::clicked, this, [this, x]() { button_pressed(x); });
Or you could do it the Qt way, which would be to call ::setProperty to store data in the button, and then retrieve it from the event, but it's so esoteric that I can't actually remember how to do that...
How do I send a message from a child widget to a parent window in qt?
I tried sending a signal from the child widget to the parent window in qt. When I call the function test in subwidget.cpp, the signal is sent but the mainwindow slot does not execute. How can I send the message?
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QStackedWidget>
#include <QPushButton>
#include <QProcess>
#include <QDebug>
#include <subwidget.h>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void check_adb_exists();
private:
Ui::MainWindow *ui;
QStackedWidget *stack;
QPushButton *start_btn;
SubWidget *f2;
private slots:
void StartApplication();
void ReceiveCustomMessage(const QString &msg);
};
#endif // MAINWINDOW_H
subwidget.h
#define SUBWIDGET_H
#include <QWidget>
namespace Ui {
class SubWidget;
}
class SubWidget : public QWidget
{
Q_OBJECT
public:
explicit SubWidget(QWidget *parent);
~SubWidget();
void test();
private:
Ui::SubWidget *ui;
signals:
void SendCustomMessage(const QString& msg);
};
#endif // SUBWIDGET_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
// #include <formcustomnew.h>
#include <subwidget.h>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
stack = MainWindow::findChild<QStackedWidget *>("stackedWidget");
start_btn = MainWindow::findChild<QPushButton *>("start_btn");
stack->setCurrentIndex(0);
connect(start_btn,SIGNAL(released()),this,SLOT(StartApplication()));
f2 = new SubWidget(this);
//connect(f2,SIGNAL(SendCustomMessage(QString)),this,SLOT(ReceiveCustomMessage(QString)));
//f2->test();
//auto f1 = new FormCustomNew(this);
//connect(f1,SIGNAL(sendMessageNewMessage(QString)),this,SLOT(receiveMessage(QString)));
//f1->test();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::StartApplication(){
//check_adb_exists();
f2->test();
}
void MainWindow::ReceiveCustomMessage(const QString &msg){
qDebug("Recieved message from child");
qDebug("Message: " + msg.toLatin1());
}
void MainWindow::check_adb_exists(){
QProcess *p = new QProcess();
connect(p,&QProcess::readyReadStandardOutput,[&](){
auto data = p->readAllStandardOutput();
qDebug("Stdout: " + data);
});
connect(p,&QProcess::readyReadStandardError,[&](){
auto data = p->readAllStandardError();
qDebug("Error: " + data);
if(data.toStdString().compare("File Not Found")){
qDebug("File Not Found is the error");
}
});
QStringList args;
args << "/c dir C:\\Users\\%USERNAME%\\AppData\\Local\\Android\\Sdk";
p->setArguments(args);
p->setProgram("C:\\Windows\\System32\\cmd.exe");
p->start();
}
subwidget.cpp
#include "subwidget.h"
#include "ui_subwidget.h"
SubWidget::SubWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::SubWidget)
{
ui->setupUi(this);
qDebug("parent: " + parent->objectName().toLatin1());
connect(this,SIGNAL(SendCustomMessage(QString)),parent,SLOT(ReceiveCustomMessage(QString)));
}
SubWidget::~SubWidget()
{
delete ui;
}
void SubWidget::test(){
emit SendCustomMessage("trial message");
}
void SubWidget::SendCustomMessage(const QString &msg){
qDebug("Sending Message: " + msg.toLatin1());
}
Signals must not be defined in Qt.
From the Qt wiki on Signal & Slots:
Signals are automatically generated by the moc and must not be implemented in the .cpp file
Remove your implementation and this should work. However you should not be binding the signal within subclass as that reduces encapsulation and reusability (parent must have a ReceiveCustomMessage(QString) slot). Instead bind it outside, as you have in your commented out code.
I am making a game requiring qgraphicspixmapitem moving around in the view.
Now, I plan to move my QGraphicsPixmapitem up and down with my keyboard. It works perfectly fine initially.
But since my scene's and view's size are unequal then, which looks pretty wired. So I added some functions to adjust my scene and view's size to be equal.
However, after setting my QGraphicsScene and QGraphicsView same size,
I cannot move my qgraphicspixmapitem with my keyboard anymore.
I've tried adding setFocus, Flag to enable my qgraphicspimapitem stay controlled by my keyboard, but in vain.
Any idea will be very appreciated. Thank you!
main window.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent):
QMainWindow(parent),
ui(new Ui::MainWindow)
{
//scene
scene = new QGraphicsScene(0, 0, 1050, 600);
//player
player = new QGraphicsPixmapItem(QPixmap(":/img/whitedog.png").scaled(100,100));
player->setFlag(QGraphicsPixmapItem:: ItemIsFocusable,true);
player->setFocus();
player->setPos(350, 500);
scene->addItem(player);
playertimer->start(10);
//view
view = new QGraphicsView(this);
view ->setScene(scene);
view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setCentralWidget(view);
view->setFixedSize(1055,605);
}
void MainWindow::keyPressEvent(QKeyEvent *e)
{
if (e->key() == Qt::Key_Down){
player->setPos(player->x(),player->y()+10);
}
else if (e->key() == Qt::Key_Up){
player->setPos(player->x(),player->y()-10);
}
}
}
main window.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsPixmapItem>
#include <QKeyEvent>
#include <QtGui>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public slots:
virtual void keyPressEvent(QKeyEvent *e);
private:
Ui::MainWindow *ui;
QGraphicsScene *scene;
QGraphicsItem *player;
QGraphicsView * view;
};
#endif // MAINWINDOW_H
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
You have never been able to move the item, what you have done is to move the scrollbars making the same effect in the visual part.
What you have to do is use the KeyPress event of the QGraphicsView, one way to do it is to create a new class that inherits from QGraphicsView, but another simpler way is to install an event filter as shown below:
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QKeyEvent>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//scene
scene = new QGraphicsScene(0, 0, 1050, 600);
//player
player = new QGraphicsPixmapItem(QPixmap(":/img/whitedog.png").scaled(100,100));
player->setPos(350, 500);
scene->addItem(player);
//view
view = new QGraphicsView;
view ->setScene(scene);
view->installEventFilter(this);
setCentralWidget(view);
view->setFixedSize(1055,605);
}
bool MainWindow::eventFilter(QObject *watched, QEvent *event)
{
if(watched == view && event->type() == QEvent::KeyPress){
QKeyEvent *kevent = static_cast<QKeyEvent *>(event);
if(kevent->key() == Qt::Key_Down){
player->setPos(player->pos() + QPointF(0, 10));
return true;
}
else if(kevent->key() == Qt::Key_Up){
player->setPos(player->pos() - QPointF(0, 10));
return true;
}
}
return QMainWindow::eventFilter(watched, event);
}
MainWindow::~MainWindow()
{
delete ui;
}
In the following link you can find the complete example
I have a display window like this:
Above display widgets are QGraphicsView widgets (they are in a QGridLayout) and what I want to achieve is that:
when user click in MainWindow, I want to seize that clicked position and decide which QGraphicsView widget contains that position and set the border of that selected QGraphicsView widget to green color. And only one QGraphicView widget can be selected at a time.
Can anyone give me some ideas?
Thanks
You can use installEventFilter for your QGraphicsViews and detect mouse press events on them. So, you can define current view and make border for it as you want. Small example:
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGraphicsView>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
public:
bool eventFilter(QObject* watched, QEvent* event) override;
private:
Ui::MainWindow *ui;
QGraphicsView* view1_;
QGraphicsView* view2_;
QGraphicsView* selectedView_;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGridLayout>
#include <QMessageBox>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
view1_(nullptr),
view2_(nullptr),
selectedView_(nullptr)
{
ui->setupUi(this);
QGridLayout* grid = new QGridLayout(this->centralWidget());
view1_ = new QGraphicsView(this);
view2_ = new QGraphicsView(this);
grid->addWidget(view1_, 0, 0);
grid->addWidget(view2_, 0, 1);
view1_->viewport()->installEventFilter(this);
view2_->viewport()->installEventFilter(this);
}
MainWindow::~MainWindow()
{
delete ui;
}
bool MainWindow::eventFilter(QObject* watched, QEvent* event)
{
qDebug() << event->type();
if (event->type() == QEvent::MouseButtonPress)
{
if (watched == view1_->viewport()){
selectedView_ = view1_;
QMessageBox::information(this, "!", "First");
return false;
}
else if (watched == view2_->viewport()){
selectedView_ = view2_;
QMessageBox::information(this, "!", "Second");
return false;
}
}
return QMainWindow::eventFilter(watched, event);
}
If you only want to change the border color on mouse hover, you wouldn't need such complicated programming. Qt supports style sheets, just like CSS.
In this case, it's enough to attach the following stylesheet to your MainWindow.
QGraphicsView:hover {
border-style: solid;
border-width: 2px;
border-color: green;
}
There's two ways to get this done:
Using the Designer: First select the MainWindow and then in its properties panel click on the styleSheet and copy and paste the style sheet.
Using code: Use setStyleSheet(...) method of QMainWindow and pass the style sheet as a string.
I'm trying to connect a push button to a lineEdit.when click on push button,set text of lineEdit to "Hello".
but i have a problem with signal and slot!
This is my form
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
connect(ui->pushButton,SIGNAL(clicked()),ui->lineEdit,SLOT(setText("Hello")));
}
MainWindow::~MainWindow()
{
delete ui;
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QLineEdit>
#include <QPushButton>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
Application output
Starting C:\Qt\Qt5.3.0\Tools\QtCreator\bin\build-E01S01-Desktop_Qt_5_3_0_MSVC2013_OpenGL_64bit-Debug\debug\E01S01.exe...
QObject::connect: No such slot QLineEdit::setText("Hello") in ..\E01S01\mainwindow.cpp:9
QObject::connect: (sender name: 'pushButton')
QObject::connect: (receiver name: 'lineEdit')
You can only connect a SIGNAL to a SLOT if they has the same signature.
You can use QSignalMapper to accomplish what you want:
QSignalMapper * mapper = new QSignalMapper(this);
QObject::connect(mapper, SIGNAL(mapped(const QString&)), ui->lineEdit, SLOT(setText(const QString&)));
QObject::connect(ui->pushButton, SIGNAL(clicked()), mapper, SLOT(map()));
mapper->setMapping(ui->pushButton, tr("Hello"));
Since you are using Qt5, you can use a lambda expression.
First make sure c++11 is enabled by adding CONFIG += c++11 in your .pro file.
#include <QApplication>
#include <QWidget>
#include <QLayout>
#include <QLineEdit>
#include <QPushButton>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0) : QWidget(parent)
{
setLayout(new QVBoxLayout);
QLineEdit *lineEdit = new QLineEdit("TEXT");
QPushButton *button = new QPushButton("BUTTON");
connect(button, &QPushButton::clicked, this, [=]{lineEdit->setText("PRESSED");});
layout()->addWidget(lineEdit);
layout()->addWidget(button);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#include "main.moc"