I've made some QPushbuttons like QPushButton **btn and I want to know when the user clicks on one of them using QMouseEvent here is the code but this idea does not work at all any ideas??
void Game::mousePressEvent(QMouseEvent *ev)
{
if(ev->button() == Qt::LeftButton)
{
btn[ev->x()][ev->y()].setStyleSheet("background-color : black;");
}
else
{
btn[ev->x()][ev->y()].setStyleSheet("background-color : red;");
}
that else part is for right click
and here is the code that generates the buttons
void Game::MakeButton()
{
btn = new ApButton*[column];
hrztl = new QHBoxLayout[column];
hrztl->setSpacing(0);
for(int i=0; i<column;i++)
{
btn[i] = new ApButton[row];
for(int j=0; j<row; j++)
{
btn[i][j].setRowCol(i,j);
btn[i][j].setFixedSize(50,50);
hrztl[i].addWidget(&btn[i][j]);
}
ui->MainLayout->addLayout(&hrztl[i]);
}
ui->MainLayout->setSpacing(0);
}
ApButton is a class that inherits QPushButton
This is a good example of use for a QSignalMapper, as seen there: http://qt-project.org/doc/qt-5.0/qtcore/qsignalmapper.html#details
ButtonWidget::ButtonWidget(QStringList texts, QWidget *parent)
: QWidget(parent)
{
signalMapper = new QSignalMapper(this);
QGridLayout *gridLayout = new QGridLayout;
for (int i = 0; i < texts.size(); ++i) {
QPushButton *button = new QPushButton(texts[i]);
connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));
signalMapper->setMapping(button, texts[i]);
gridLayout->addWidget(button, i / 3, i % 3);
}
connect(signalMapper, SIGNAL(mapped(QString)),
this, SIGNAL(clicked(QString)));
setLayout(gridLayout);
}
In that example, every button is identified by its title, as a QString. The mapper allows you to retrieve the corresponding button's title when one of them is clicked.
Switch your
Game::mousePressEvent(QMouseEvent *e)
to
ApButton::mousePressEvent(QMouseEvent *e)
since you are trying to implement the Press Event of the Button. If you only want to have the moment of the button being pressed and not changing Button behaviour with this, use a SIGNAL/SLOT connection instead of reimplementing the event (add to your creation):
connect(btn[i][j], SIGNAL(pressed()), this, SLOT(slotButtonPressed());
void Game::slotButtonPressed(){
//doSomething
}
use a QButtonGroup or the QSignalMapper if you need to identify more then one Button in a single method or use QObject::sender(), but this can be tricky sometimes.
I had a similar situations some times ago.. I had a QDialog and I had to dinamically add some QPushButton.. then I need to know on which button the user pressed.. so I needed something like:
connect( btn, SIGNAL( clicked(int) ),
this, SLOT( handle(int) ));
for instance a signal-slot connection with the id of the clicked button and a function for handle the click. The function is the same for each buttons and can handle the specific button because of the id..
Implementing this is quite simple subclassing QPushButton adding an id and a new signal..
Hope it's some help!
If Apbutton inherits QPushButton, why don't connect to clicked()?
then you can call QObject::sender()
On slot:
ApButton *but = dynamic_cast<ApButton*>QObject::sender()
if(but)
{
but->setStyleSheet("background-color : black;");
}
to get the clicked buttonbutton and set its stylesheet
Related
I am writing a small utility composed by :
1) 4 QToolBar
2) 1 QToolBox
3) 1 QMenu
4) 1 QGraphicsView
I have some buttons on the QToolBox, each button represents a grid with different spacing.
Everytime a user clicks a button on the QToolBox the spacing on the QGraphicsScene` changes.
I use daily the new notation and never had any problems with it until I had to use an "abstract" entity such as the QAbstractButton approach.
the problem I have is that I don't seem to properly set the connection signal/slot of the QAbstractButton:
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void backgroundButtonClicked(QAbstractButton *button);
private:
void createToolBox();
QButtonGroup *buttonGroup;
QButtonGroup *backgroundButtonGroup;
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
createToolBox();
scene = new DiagramScene(itemMenu,this);
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(toolBox);
view = new QGraphicsView(scene);
QWidget *widget = new QWidget;
widget->setLayout(layout);
setCentralWidget(widget);
}
void MainWindow::backgroundButtonClicked(QAbstractButton *button)
{
QList<QAbstractButton*> buttons = backgroundButtonGroup->buttons();
foreach (QAbstractButton *myButton, buttons) {
if(myButton != button)
button->setChecked(false);
}
QString text = button->text();
if(text == tr("Blue Grid"))
scene->setBackgroundBrush(QPixmap(":/images/background1.png"));
else if(text == tr("White Grid"))
scene->setBackgroundBrush(QPixmap(":/images/background2.png"));
else if(text == tr("Gray Grid"))
scene->setBackgroundBrush(QPixmap(":/images/background3.png"));
else
scene->setBackgroundBrush(QPixmap(":/images/background4.png"));
scene->update();
view->update();
}
void MainWindow::createToolBox()
{
buttonGroup = new QButtonGroup(this);
buttonGroup->setExclusive(false);
QGridLayout *layout = new QGridLayout;
layout->addWidget(createCellWidget(tr("Conditional"), DiagramItem::Conditional), 0, 0);
layout->addWidget(createCellWidget(tr("Process"), DiagramItem::Step), 0, 1);
layout->addWidget(createCellWidget(tr("Input/Output"), DiagramItem::Io), 1, 0);
QToolButton *textButton = new QToolButton;
textButton->setCheckable(true);
buttonGroup->addButton(textButton, InsertTextButton);
textButton->setIcon(QIcon(QPixmap(":/images/textpointer.png")));
textButton->setIconSize(QSize(50, 50));
QWidget *itemWidget = new QWidget;
itemWidget->setLayout(layout);
backgroundButtonGroup = new QButtonGroup(this);
// This is my first attempt but it does not work:
QObject::connect(backgroundButtonGroup, &QAbstractButton::clicked, this, &QAbstractButton::backgroundButtonClicked());
}
Different options I tried are:
1) via static_cast<> trying to cast it into the QAbstractButton as shown below:
QObject::connect(static_cast<QAbstractButton*>(backgroundButtonGroup), this, static_cast<QAbstractButton>(backgroundButtonClicked(void)));
2) Also I tried the following according to official documentation but that also didn't work:
QObject::connect(backgroundButtonGroup, &QAbstractButton::clicked, this, &QAbstractButton::backgroundButtonClicked);
Adding to that I was digressing towards the following structure below (heading to Q_INVOKE) but I am afraid I was over complicating the issue, but just wanted to include the additional solution I was trying to explore:
const bool connected = connect(sender, &Sender::aSignal,
receiver, &Receiver::aSlot);
Q_ASSERT(connected);
Q_UNUSED(connected);
Thanks for pointing to the right direction for solving this issue.
You have the following errors:
QButtonGroup does not have the clicked signal but buttonClicked.
You must not use ().
The syntax in general is: obj, & Class_of_obj, in your case backgroundButtonGroup is QButtonGroup and this is MainWindow so preliminarily the syntax is:
QObject::connect(backgroundButtonGroup, &QButtonGroup::buttonClicked, this, &MainWindow::backgroundButtonClicked);
But buttonClicked is overload then you must use QOverload to indicate the signature.
Considering the above, the solution is:
QObject::connect(backgroundButtonGroup, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), this, &MainWindow::backgroundButtonClicked);
I recommend you check:
the QButtonGroup docs where it shows the example of how to make the connection.
In addition to New Signal Slot Syntax that indicates the various cases
I have listwidgetitem's that have a custom widget (step_widget) which contains 3 QPushButtons and a QLabel.
When I press one of the buttons in the widget, it is not triggering itemClicked which i need to see the index of the listwidget that was clicked. If i click the QLabel, the signal is triggered and i am able to obtain the index. How can trigger the signal when one of the buttons is pressed? Why does it not trigger?
QListWidgetItem *item = new QListWidgetItem();
stepWidget *step_widget = new stepWidget();
ui->listWidget->addItem(item);
ui->listWidget->setItemWidget(item,step_widget);
The itemClicked() signal is not emmited because the QPushButton consumes the click event.
There is the simplest solution I've found.
First, in your class stepWidget add a signal that will be emitted when any QPushButton is clicked, for example:
signals:
void widgetClicked();
Then connect the clicked() signal of every QPushButton with that signal. This code is in the constructor of the widget stepWidget:
connect(ui->pushButton1, &QPushButton::clicked, this, &stepWidget::widgetClicked);
connect(ui->pushButton2, &QPushButton::clicked, this, &stepWidget::widgetClicked);
connect(ui->pushButton3, &QPushButton::clicked, this, &stepWidget::widgetClicked);
To test it I added 10 widgets to a QListWidget that reside in the MainWindow class. You must connect that signal to a slot (in this case I use a C++11 Lambda Function, but you can create a slot instead, it's fine) where you establish the current item as the item under the widget. This code is located in the constructor of MainWindow:
for (int i = 0; i < 10; ++i) {
QListWidgetItem *item = new QListWidgetItem;
stepWidget *step_Widget = new stepWidget;
ui->listWidget->addItem(item);
ui->listWidget->setItemWidget(item, step_Widget);
connect(step_Widget, &stepWidget::widgetClicked, [=]() {
ui->listWidget->setCurrentItem(item);
});
}
Now, this will not make the itemClicked() signal to be emitted because the user not clicked the item. But I advice you to connect instead the signal currentRowChanged() as it will be emitted with this code and also it has the advantage that it allows you to directly obtain the row. Once you have the row you can obtain the item and the widget, evidently.
However, this procedure has the inconvenient that the currentRowChanged() signal will be emitted even when the user selects a row with the keyboard or when the user press and slide the mouse over the QListWidget without releasing it.
On workaround could be, for example, using a flag as an attribute for your MainWindow class that is always false except during this connection:
connect(ste_pWidget, &stepWidget ::widgetClicked, [=]() {
buttonPressed = true;
ui->listWidget->setCurrentItem(item);
buttonPressed = false;
});
Then you must manipulate two signals from the QListWidget: clicked() and currentRowChanged(). I choose clicked() instead of itemClicked() because it gives almost directly access to the row:
void MainWindow::on_listWidget_clicked(const QModelIndex &index)
{
ui->statusBar->showMessage(tr("Row clicked: %1").arg(index.row()), 1000);
}
void MainWindow::on_listWidget_currentRowChanged(int currentRow)
{
if (buttonPressed) {
ui->statusBar->showMessage(tr("Row clicked: %1").arg(currentRow), 1000);
}
}
what you can simply do is, make your qpushbutton to qlabel and then qpushbutton will become clickable.
You can then override this method :- on_listWidget_itemClicked(QListWidgetItem *item) for double click on qpushbutton.
I tried it & it worked perfectly, its quite simple workaround.
This is my first time to ask on Stack Overflow. If any suggestion about asking question, please let me know.
I am very new to Qt, and have some problems while using event. Hope someone can provide any thoughts.
Background:
I have my custom pushButton, which is rxPushButton in rx.h and rx.cpp. I use on_rxPushButton_clicked to change the image and it works pretty well.
In MainWindow, I need to use some rx so I include the class rx and I want to detect if I press left button of the mouse, I need to know which rx has been pressed and record its id in int rxId in MainWindow.
Problem:
I tried two ways to achieve my goal, including mousePressEvent and eventFilter. I found that I can't detect the mouse pressed signal on any rx, but I can detect it outside rx in other places in Mainwindow. I wonder if the events will conflict, but when I comment on_rxPushButton_clicked in rx.cpp, MainWindow still doesn't work for the problem. So I presume that maybe the space in the screen occupied by rx will not be in control of MainWindow (I can get Debug message "test1" but not "test2" in my code, check below).
How should I do to solve this problem if I need both things (change image in rx and modify one variable in MainWindow)? Or maybe it's just something wrong with my code and how to modify?
I hope to separate them if possible because I still need to include many objects in MainWindow in the future.
Here are some of my related codes:
rx.cpp
void rx::on_rxPushButton_clicked(void)
{
startLoading();
}
void rx::startLoading(void)
{
state = 1;
connect(timer, SIGNAL(timeout()), this, SLOT(loading1()));
timer->start(LOADING_INTERVAL);
}
void rx::loading1(void)
{
if(state == 1)
{
state = 2;
ui->rxPushButton->setStyleSheet("border-image: url(:/images/Rx/Rxloading1.png);");
disconnect(timer, SIGNAL(timeout()), this, SLOT(loading1()));
}
}
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
for(int i = 0 ; i < rxSize ; i++)
{
rxList << new rx(this);
int j = i/rxHorizontalCount;
rxList[i]->setGeometry(500+(i-j*8)*110,10+j*90,100,90);
}
//rxList[0]->installEventFilter(this);
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
qDebug() << "test1";
if(rxList[0]->rect().contains(event->x(), event->y()))
qDebug() << "test2";
}
}
Change image in rx
Use QSignalMapper class that bundles signals from QWidgets (class rx) and re-emits them with widget (class rx) parameters corresponding to the object that sent the signal.
We connect each button's clicked() signal to the signal mapper's map() slot, and create a mapping in the signal mapper from each button to the button itself.
Finally we connect the signal mapper's mapped() signal to the custom widget's(rx object) clicked() signal. When the user clicks a button, the custom widget(rx object) will emit a single clicked() signal whose argument is the button (rx object) the user clicked.
Handle the button clicked event in MainWindow::slotButtonsClicked(QWidget *p) function.
Modify n variable in MainWindow:
You can update variable of MainWindow in mousePressEvent function
class MainWindow : public QWidget
{
public :
MainWindow (QWidget *parent);
QSignalMapper * mapper;
slots:
void slotButtonsClicked(QWidget *);
// ...
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
mapper = new QSignalMapper(this);
for(int i = 0 ; i < rxSize ; i++)
{
rxList << new rx(this);
int j = i/rxHorizontalCount;
rxList[i]->setGeometry(500+(i-j*8)*110,10+j*90,100,90);
QObject::connect(rxList[i], SIGNAL(clicked()),mapper,SLOT(map()));
//Adds a mapping so that when map() is signalled from the sender, the signal mapped(widget ) is emitted.
mapper->setMapping(rxList[i], rxList[i]);
}
QObject::connect(mapper,SIGNAL(mapped(QWidget *)),this,SLOT(slotButtonsClicked(QWidget *)));
}
void MainWindow::mousePressEvent(QMouseEvent *event)
{
if(event->buttons() & Qt::LeftButton)
{
qDebug() << "Clicked outside the button";
//Now you can modify n variable in MainWindow
}
MainWindow::mousePressEvent(event);
}
void MainWindow::slotButtonsClicked(QWidget *p)
{
rx *button = dynamic_cast<rx *>(p);
button->on_rxPushButton_clicked();
qDebug() << button <<" clicked on button";
//Now you can change image in rx
}
Hope this works :)
Event propagating prevent you to do it. When mouse is over Button, event sends to button, not to the MainWindow, thats why you never see test2 in debug output.
Since I don't know what do you need it's hard to say what you should do. You can process event in Button and send some signals or whatever, or set event filter and check if target object is your button, and so on.
Any way, read Qt event system docs for better understanding.
In a QWidget like QLabel, how can we set a "?" button, such that when clicked (or hovered) it should show some help text.
Also you can use QMenu instead of QPushButton + QLabel.
// Constructor
setContextMenuPolicy(Qt::CustomContextMenu);
connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotCustomMenu(QPoint)));
// slotCustomMenu(QPoint)
QMenu menu(this);
menu.addAction(this->toolTip());
menu.addAction(this->whatsThis());
menu.exec(QCursor::pos());
Easiest way to show help when hover QWidget: setToolTip(QString) and setToolTipDuration(int).
If you want a "?" button, just implement your own QWidget. Then via ui designer or directly in your code add QPushButton and QLabel on layout, and show your QLabel with help text in position of cursor when clicked(). Something like this:
{
// Constructor
...
m_mainLabel = new QLabel("Main text");
m_button = new QPushButton("?");
m_helpLabel = new QLabel("Help text");
connect(m_button, SIGNAL(clicked(bool)),
this, SLOT(slotShowOrHideHelpLabel(bool)));
QHBoxLayout *hBoxLayout = new QHBoxLayout;
hBoxLayout->addWidget(m_mainLabel);
hBoxLayout->addWidget(m_button);
setLayout(hBoxLayout);
}
void slotShowOrHideHelpLabel(bool showHelpLabel)
{
if (showHelpLabel)
{
m_helpLabel->show();
m_helpLabel->move(QCursor::pos());
}
else
{
m_helpLabel->hide();
}
}
I have QTableWidget in my Qt application, and I add buttons to it like that:
QPushButton *startButton = new QPushButton("start");
ui->tableWidget_tvConnection->setCellWidget(row, col, startButton);
connect(startButton, SIGNAL(clicked()), this, SLOT(startButtonPressed()));
And when it is pressed I need to get text from it, so I tried this:
void MainWindow::startButtonPressed()
{
...
QPushButton *button = ui->tableWidget_tvConnection->cellWidget(row, col);
qDebug() << button->text();
}
But compiler doesn't allow me to convert from QWidget* to QPushButton*:
error: invalid conversion from 'QWidget*' to 'QPushButton*' [-fpermissive]
So Is it even possible to get text from button inside QTableWidget?
If it isn't I have another way how to handle this in my application, but this would be really nice.
You get QWidget, so you should cast it to QPushButton. After that, you will be able to use this as a normal pushbutton. Try this:
QPushButton *button = qobject_cast<QPushButton *>(ui->tableWidget_tvConnection->cellWidget(row, col));
if(button) {
//success
} else {
//bad
}