I have a set of widgets to control a parameter in five similar places (or channels).
Now I can enable/disable each of these widgets using a pushbuttons, as follows.
connect(ui->pushButton_currOnOne, &QPushButton::clicked, ui->widget_currentOne, &CurrentButtonOne::setEnabled);
connect(ui->pushButton_currOnTwo, &QPushButton::clicked, ui->widget_currentTwo, &CurrentButtonOne::setEnabled);
connect(ui->pushButton_currOnThree, &QPushButton::clicked, ui->widget_currentThree, &CurrentButtonOne::setEnabled);
connect(ui->pushButton_currOnFour, &QPushButton::clicked, ui->widget_currentFour, &CurrentButtonOne::setEnabled);
connect(ui->pushButton_currOnFive, &QPushButton::clicked, ui->widget_currentFive, &CurrentButtonOne::setEnabled);
Can I use mousePressEvent/mouseReleaseEvent instead of using &QPushButton::clicked in the above scenario?
It will be very helpful if you could show me an example.
Thanks in advance
Some code to get you started.
mybutton.h:
#include <QObject>
#include <QPushButton>
#include <QMouseEvent>
class MyButton : public QPushButton
{
public:
explicit MyButton(QWidget *parent = nullptr);
protected:
void mousePressEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
private:
bool isPressed = false;
signals:
void myButtonPressed();
};
mybutton.cpp:
#include "mybutton.h"
MyButton::MyButton(QWidget *parent) : QPushButton(parent)
{
}
void MyButton::mousePressEvent(QMouseEvent *e)
{
isPressed = true;
emit myButtonPressed();
QPushButton::mousePressEvent(e);
}
void MyButton::mouseReleaseEvent(QMouseEvent *e)
{
isPressed = false;
QPushButton::mouseReleaseEvent(e);
}
I'm not sure, what exactly you are trying to achieve, but this idea should meet your needs.
You create a class, which inherits from QPushButton (or QAbstractButton) and override its mousePressEvent and mouseReleaseEvent. If you want some other widgets to react on this events, you emit signal and connect other widgets to that signal.
Related
I wrote a minimal working example of the problem and I believe it might be a Qt bug. But just in case I wanted to ask.
Here are My classes:
mydialog.h
#include <QDialog>
#include <QVBoxLayout>
#include <QLabel>
class MyDialog : public QDialog
{
public:
MyDialog(QWidget *parent = 0);
};
mydialog.cpp
#include "mydialog.h"
MyDialog::MyDialog(QWidget *parent):QDialog(parent)
{
QLabel *label = new QLabel("Some random dialog",this);
QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(label);
this->setLayout(layout);
}
myitem.h
#include <QGraphicsTextItem>
#include <QPainter>
#include <QDebug>
#include "mydialog.h"
class MyItem : public QGraphicsItem
{
public:
MyItem();
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
QRectF boundingRect() const {return boundingBox;}
void setMyDialog(MyDialog *d){ dialog = d; }
private:
QRectF boundingBox;
MyDialog *dialog;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *e);
};
myitem.cpp
MyItem::MyItem()
{
boundingBox = QRectF(0,0,200,100);
}
void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){
painter->setBrush(QBrush(Qt::red));
painter->drawRect(boundingBox);
}
void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *e){
//dialog->exec(); // BUG
//dialog->open(); // BUG
dialog->show(); // WORKS!
}
test.h
#include "myitem.h"
namespace Ui {
class Test;
}
class Test : public QMainWindow
{
Q_OBJECT
public:
explicit Test(QWidget *parent = 0);
~Test();
protected:
void resizeEvent(QResizeEvent *e);
private:
Ui::Test *ui;
MyDialog *diag;
};
And test.cpp
Test::Test(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::Test)
{
ui->setupUi(this);
ui->graphicsView->setScene(new QGraphicsScene(this));
diag = new MyDialog(this);
}
void Test::resizeEvent(QResizeEvent *e){
ui->graphicsView->setSceneRect(0,0,ui->graphicsView->width(),ui->graphicsView->height());
ui->graphicsView->scene()->clear();
MyItem *item = new MyItem();
item->setMyDialog(diag);
ui->graphicsView->scene()->addItem(item);
}
Test::~Test()
{
delete ui;
}
So here is what happens (tested on Qt 5.7 and Qt 5.6). If the dialog is opened with either exec or open then, after it is closed ALL further mouse clicks ANYWHERE on the screen will open up the dialog again, making it impossible to interact with anything else drawn in there. This happens ONLY after it is opened for the first time. If i resize the screen, the item is recreated and I can click normally again. If I again click on the red box, then again all further clicks anywhere on the screen open up the dialog
However if the Dialog is opened by show, then it works as expected, only showing again if I click on the red rectangle.
Now the obvious problem is that exec make the dialog block execution until it is closed, but show doesn't. I can program around this using signals, but my question is why? and Is this a bug?
It seems that MyItem's reimplementation of mousePressEvent needs some behavior provided by default implementation. Here is the code, works fine in my machine:
void MyItem::mousePressEvent(QGraphicsSceneMouseEvent *event){
dialog->exec(); // WORKS
//dialog->open(); // WORKS
//dialog->show(); // WORKS
QGraphicsItem::mousePressEvent(event);
}
I created a rectangle and want it to move with QTimer , I want to know how the methods of QTimer exactly work. This code is running but the figure i drew is not moving.
.h
Header file
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
void paintEvent(QPaintEvent *event);
~Widget();
private slots:
void update();
private:
Ui::Widget *ui;
};
this is the .cpp file
.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QPainter>
#include<QTimer>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
QTimer *timer = new QTimer(this);
timer->setInterval(1000);
connect(timer, SIGNAL(timeout()), this, SLOT(update()));
timer->start();
}
Widget::~Widget()
{
delete ui;
}
void Widget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawRect(50,80,70,80);
}
void Widget::update()
{
update();
}
You say "the figure i drew is not moving". In your code, I don't see code for a moving figure, I only see a static rectangle being drawn.
In addition, the update() function calls itself, leading to infinite recursion. Remove the update() from your code, the base class implementation QWidget::update() does the correct thing (scheduling a call to paintEvent()), no need to reimplement update().
First of all, update() slot method already has a specific meaning and purpose, you should not override it for other purposes. Further, it is not virtual, which tells you it is not even meant to be overriden (and doing it can lead to very confusing situations). So rename your own method to... updateAnimation() or something.
Then you need to add private member variables for your rectangle position, say rectX, rectY, rectWidth, rectHeight (or just single QRect if you prefer). Some code snippets help you get the idea:
void Widget::paintEvent(QPaintEvent *event)
{
// default setting is that Qt clears the widget before painting,
// so we don't need to worry about erasing previous rectangle,
// just paint the new one
QPainter painter(this);
painter.drawRect(rectX, rectY, rectWidth, rectHeight);
}
void Widget::updateAnimation()
{
// modify rectX, rectY, rectWidth and rectHeight here
update(); // make Qt do redrawing
}
I am having problems with QImages and Qthreads.
I am trying to load big images in a Thread and then display them as QPixmap on a QLabel.
My problem is that as long as I don't use a different thread to load the QImages, everything is perfect but as soon as I use a different thread, nothing is renderder.
Though I still have a valid size for my QImage.
The thing that puzzles me is that, if I just comment the 22nd line in the cpp that moves the loader to the other thread, the label displays nicely.
Does anyone have an idea?
Here is my very simplified code:
Header :
class Loader : public QObject
{
Q_OBJECT
public:
explicit Loader(QObject *parent = 0);
signals:
void imageLoaded(QString, const QImage &);
public slots:
void loadImage(const QString& fichier);
};
namespace Ui {
class MainWindow;
}
class LoaderImages;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
signals:
void loadImage(const QString& dossier);
private slots:
void imageAvailable(const QString& dossier, const QImage& img);
private:
Ui::MainWindow *ui;
//QString mDossier;
Loader* mLoader;
//QMap<QString, QImage*> mMapDesImages;
int mWidth;
};
cpp:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFile>
#include <QPixmap>
#include <QImage>
#include <QDir>
#include <QThread>
#include <QDebug>
#include <QLabel>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow),
mLoader(new Loader(NULL)),
mWidth(0)
{
ui->setupUi(this);
QThread* thread = new QThread(this);
mLoader->moveToThread(thread);
thread->start();
connect(this, SIGNAL(loadImage(QString)), mLoader, SLOT(loadImage(QString)));
connect(mLoader, SIGNAL(imageLoaded(QString,QImage)), this, SLOT(imageAvailable(QString,QImage)));
emit loadImage("C:/img.jpg");
}
void MainWindow::imageAvailable(const QString &dossier, const QImage& img)
{
mWidth += (img.width() + 20);
ui->mScrollContent->setMinimumSize(mWidth,img.height());
QLabel* lab = new QLabel(ui->mScrollContent);
lab->setFixedSize(img.width(), img.height());
lab->setGeometry(mWidth - img.width() + 20, 0, img.width(), img.height());
lab->setPixmap(QPixmap::fromImage(img));
}
MainWindow::~MainWindow()
{
delete mLoader;
delete ui;
}
Loader::Loader(QObject *parent) :
QObject(parent)
{
}
void Loader::loadImage(const QString& fichier)
{
QImage* image = new QImage(fichier);
emit imageLoaded(fichier, *image);
}
Thx!
There are several mistakes:
You're not showing the label. When the image loader is in the GUI thread, the image is loaded and the label added to the contents pane before the main window is shown. Since the parent is shown, the children become visible.
When the loading is done in another thread, you'll be adding image labels to a widget that's already shown. Such child widgets are not visible unless you explicitly show() them.
You're leaking the image in loadImage. There's no reason to put that QImage on the heap.
You're allowing a running QThread to be destructed. That's a common error since QThread is essentially broken by design. Sane C++ classes should be always destructible. QThread isn't. Thus you need a workaround.
You're not setting the minimum height of the contents widget as well.
You might wish to consider the use QtConcurrent::run instead of a dedicated thread. This is especially worthwhile when the operation you're undertaking is a one liner, more or less. I've shown both, the implementations are alternated between at runtime. Note that you need to add the concurrent module and CONFIG += c++11 to the project file.
Style bugs:
There's no reason to pass NULL for default-valued parameters that are already zero.
There's no reason to keep QObject members that have the lifetime of the parent object on the heap, if such members are constructed along with the parent object.
Just because Qt Creator comes with silly template files doesn't mean that you shouldn't be using a std::unique_ptr or QScopedPointer to hold the ui member. Naked pointers should almost never be members unless they're pointers to QObjects with parents.
As quite a bit of the code is missing, I can't really tell what else might be wrong. Below is a complete example.
// https://github.com/KubaO/stackoverflown/tree/master/questions/image-loader-24853687
#include <QtWidgets>
#include <QtConcurrent>
class Thread final : public QThread {
public:
~Thread() { quit(); wait(); }
};
class Loader : public QObject
{
Q_OBJECT
public:
explicit Loader(QObject *parent = nullptr) : QObject(parent) {}
Q_SIGNAL void imageLoaded(const QString &, const QImage &);
Q_SLOT void loadImage(const QString& fichier) {
QImage img(fichier);
if (! img.isNull()) emit imageLoaded(fichier, img);
}
};
class MainWindow : public QWidget
{
Q_OBJECT
Loader m_loader;
Thread m_loaderThread;
QGridLayout m_layout{this};
QPushButton m_open{"Open"};
QScrollArea m_view;
QWidget m_content;
int m_width{};
bool m_threadImpl = true;
Q_SIGNAL void loadImage(const QString &);
Q_SIGNAL void imageLoaded(const QString &, const QImage & img);
Q_SLOT void imageAvailable(const QString &, const QImage & img) {
int spacing = 20;
if (m_width) m_width += spacing;
auto lab = new QLabel(&m_content);
lab->setFixedSize(img.width(), img.height());
lab->setGeometry(m_width, 0, img.width(), img.height());
lab->setPixmap(QPixmap::fromImage(img));
lab->show();
m_width += img.width();
m_content.setMinimumWidth(m_width);
m_content.setMinimumHeight(qMax(m_content.minimumHeight(), img.height()));
}
Q_SLOT void open() {
auto dialog = new QFileDialog(this);
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->show();
if (m_threadImpl)
connect(dialog, &QFileDialog::fileSelected, this, &MainWindow::loadImage);
else
connect(dialog, &QFileDialog::fileSelected, [this](const QString & fichier){
QtConcurrent::run([this, fichier]{
QImage img(fichier);
if (! img.isNull()) emit this->imageLoaded(fichier, img);
});
});
m_threadImpl = !m_threadImpl;
}
public:
explicit MainWindow(QWidget *parent = nullptr) : QWidget(parent) {
m_layout.addWidget(&m_open);
m_layout.addWidget(&m_view);
m_view.setWidget(&m_content);
m_loader.moveToThread(&m_loaderThread);
m_loaderThread.start();
connect(&m_open, &QPushButton::clicked, this, &MainWindow::open);
connect(this, &MainWindow::loadImage, &m_loader, &Loader::loadImage);
connect(this, &MainWindow::imageLoaded, this, &MainWindow::imageAvailable);
connect(&m_loader, &Loader::imageLoaded, this, &MainWindow::imageAvailable);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
#include "main.moc"
I'm using Qt Creator to create a gui for a mineseeper game.
How can I know a QpushButton clicked with rightclick? for flag in the game.
In other word, which signal used for rightclick?
Create your own button with filter at mousePressEvent slot.
qrightclickbutton.h
#ifndef QRIGHTCLICKBUTTON_H
#define QRIGHTCLICKBUTTON_H
#include <QPushButton>
#include <QMouseEvent>
class QRightClickButton : public QPushButton
{
Q_OBJECT
public:
explicit QRightClickButton(QWidget *parent = 0);
private slots:
void mousePressEvent(QMouseEvent *e);
signals:
void rightClicked();
public slots:
};
#endif // QRIGHTCLICKBUTTON_H
qrightclickbutton.cpp
#include "qrightclickbutton.h"
QRightClickButton::QRightClickButton(QWidget *parent) :
QPushButton(parent)
{
}
void QRightClickButton::mousePressEvent(QMouseEvent *e)
{
if(e->button()==Qt::RightButton)
emit rightClicked();
}
Now connect like this
QRightClickButton *button = new QRightClickButton(this);
ui->gridLayout->addWidget(button);
connect(button, SIGNAL(rightClicked()), this, SLOT(onRightClicked()));
Create a slot in MainWindow.cpp.
void MainWindow::onRightClicked()
{
qDebug() << "User right clicked me";
}
It works for me!
I think QPushButton is internally implemented to listen to left mouse clicks only. But you can easily extend QPushButton and re-implement let's say the mouse release event and do your thing if the right mouse button was pressed, e.g. emit a custom rightClicked() signal for example:
signals:
void rightClicked();
protected:
void mouseReleaseEvent(QMouseEvent *e) {
if (e->button() == Qt::RightButton) emit rightClicked();
else if (e->button() == Qt::LeftButton) emit clicked();
}
... or you can create an overload of the clicked signal that forwards the mouseEvent pointer so you can do the same check outside of the button.
signals:
void clicked(QMouseEvent *);
protected:
void mouseReleaseEvent(QMouseEvent *e) {
emit clicked(e);
}
Then you do the check in the slot you connect the button's clicked(QMouseEvent *) signal to and proceed accordingly.
I just wrote this little helper adapter to make any existing button right-clickable with no need to subclass it:
class CRightClickEnabler : public QObject
{
public:
CRightClickEnabler(QAbstractButton * button): QObject(button), _button(button) {
button->installEventFilter(this);
};
protected:
inline bool eventFilter(QObject *watched, QEvent *event) override {
if (event->type() == QEvent::MouseButtonPress)
{
auto mouseEvent = (QMouseEvent*)event;
if (mouseEvent->button() == Qt::RightButton)
_button->click();
}
return false;
}
private:
QAbstractButton* _button;
};
Usage:
connect(ui->pushButton, &QPushButton::clicked, [](){qDebug() << "Button clicked";});
new CRightClickEnabler(ui->pushButton);
From now on, the clicked signal will be triggered by the right click as well as left click. There's no need to delete this object - it uses ui->pushButton as parent and will be auto-deleted by Qt when the parent is destroyed.
Obviously, you can write 2 lines of code (literally) to declare a new signal here and emit that signal upon right click instead of clicked, if desired.
I'd like to suggest this option as well, without need for event filter/other stuffs...
self.button.released.connect(self.doStuff)
self.button.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
self.button.customContextMenuRequested.connect(partial(self.doStuff, False))
def doStuff(self,state=True,p=QPoint()):
print("True for left, False for right!",state)
I want to subclass QLineEdit to add it some signals like mouseEnter() and mouseExit() to become informed when the mouse courser is over the widget and when leaves it.
I could write mouseEnter() like below.
class MyLineEdit:public QLineEdit{
//
// ..
//
protected:
void mouseMoveEvent(QMouseEvent *e);
signals:
void mouseEnter();
}
void MyLineEdit::mouseMoveEvent(QMouseEvent *e)
{
emit mouseEnter();
QLineEdit::mouseMoveEvent(e);
}
It works correctly.
How can I write mouseExit()?
leaveEvent is not a very good name!
#include <QtGui>
#include <QEvent>
class Editor : public QLineEdit
{
Q_OBJECT
public:
void leaveEvent(QEvent *);
signals:
void mouseLeave();
};
void Editor::leaveEvent(QEvent *e);
{
qDebug() << "Mouse has left the building..";
emit mouseLeave();
}