I'm making the board game Puerto Rico in C++ and Qt. One of the features will be that after they've chosen the Major Role (button 1) each player (3 players in total) kan build a building. So i have a button for each building, is there a way that i get the information (like the name of the button) when the player clicked on it?
Kind regards
If you connect a signal of QPushButton to a slot, you can call sender() inside the slot to get the object which sent the signal.
Example slot code:
void MainWindow::onButtonPress()
{
QObject* senderObj = sender();
if (senderObj->isWidgetType())
{
QPushButton* button = qobject_cast<QPushButton*>(senderObj);
if (button)
{
// button is the QPushButton who emmited this signal
}
}
}
If you created building1Button, building2Button, building3Button buttons in the designer, you can try the followings.
In your class constructor you need to connect the clicked() SIGNAL from all building buttons to the same SLOT.
Game::Game()
{
// ...
connect( ui->building1Button, SIGNAL( clicked() ), this, SLOT( onBuildingClicked() ) );
connect( ui->building2Button, SIGNAL( clicked() ), this, SLOT( onBuildingClicked() ) );
connect( ui->building3Button, SIGNAL( clicked() ), this, SLOT( onBuildingClicked() ) );
// ...
}
And in that SLOT you can use sender() function to check which button was clicked.
void Game::onBuildingClicked()
{
QPushButton* clickedButton = qobject_cast< QPushButton* >( sender() );
if ( clickedButton )
{
const QString clickedBuilding = clickedButton->text();
const QString clickedButtonName = clickedButton->objectName();
}
}
Related
I have a qplaintextedit that is loaded dynamically with text contents, with QString.
I want to add an option to the menu that appears when we right click on the qplaintextedit. How can I do it programmatically so that I am able to add my own menu item to the default menu item? Any ideas would be appreciated.
You can sublcass QPlainTextEdit and reimplement the method contextMenuEvent(QContextMenuEvent *event). Alternatively you can add an event filter to a QPlainTextEdit and catch the QContextMenuEvent.
In your implementation, you can call QMenu *QPlainTextEdit::createStandardContextMenu(const QPoint &position) to create the default menu of the text edit and than add your custom items to it.
Example (subclass):
void MyTextEdit::contextMenuEvent(QContextMenuEvent *event)
{
QMenu* menu = createStandardContextMenu(event->pos());
QAction* clearAction = menu->addAction("Clear");
QAction* choosedAction = menu->exec(event->globalPos());
//...
delete menu;
}
See:
Event handlers
Event filters
QPlainTextEdit::contextMenuEvent
QPlainTextEdit::createStandardContextMenu
You have to reimplement the function : void QPlainTextEdit::contextMenuEvent(QContextMenuEvent *event)
More details in the doc here : http://doc.qt.io/qt-5/qplaintextedit.html#contextMenuEvent
It is not necessary to subclass QPlainTextEdit, nor to use an event filter. You can do it all in the main widget of your application. The following worked for me using Qt versions 4.7.4 and 4.8.5.
In the Designer:
You add a QPlainTextEdit widget whose name is "textbox".
In MainWindow.h:
// Auto generated from MainWindow.ui
#include "ui_MainWindow.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
/// The auto generated user interface.
Ui::MainWindowClass ui;
...
}
In MainWindow.cpp:
MainWindow::MainWindow( QWidget * pParent, Qt::WFlags flags )
: QMainWindow( pParent, flags )
, ui( )
{
ui.textbox->setContextMenuPolicy( Qt::CustomContextMenu );
connect( ui.textbox, SIGNAL( customContextMenuRequested( QPoint ) ),
this, SLOT( onTextboxCustomContextMenuRequested( QPoint ) ) );
}
void MainWindow::onTextboxCustomContextMenuRequested( QPoint p )
{
// Start with the standard menu.
QMenu * pMenu = ui.textbox->createStandardContextMenu();
QAction * pAction;
// Clear.
// Because QPlainTextEdit::clear() is a slot method, I can connect directly to it.
pAction = new QAction( "Clear", this );
connect( pAction, SIGNAL( triggered() ), ui.textbox, SLOT( clear() ) );
pMenu->addAction( pAction );
// Show the menu.
QPoint q = ui.textbox->mapToGlobal( p );
pMenu->exec( q );
// The menu's ownership is transferred to me, so I must destroy it.
delete pMenu;
}
I am using C++/Qt 5.5 and in my software, I want the Qt::Key_Space to trigger the opening of a menu, and the same button, to trigger the closing of the same menu. The problem is that if I implement it as:
this->button = new QPushButton();
this->button->setShortcut( QKeySequence( Qt::Key_Space ) );
connect(this->button, &QPushButton::clicked, this, &MenuClass::startMenuButtonClicked);
...
this->in_menu_button = new QPushButton();
this->in_menu_button->setShortcut( QKeySequence( Qt::Key_Space ) );
connect(this->in_menu_button, &QPushButton::clicked, this, &InTheMenuClass::startMenuButtonClicked);
If i do it like this, only one way to the menu works. Do you have any ideas on how to solve this issue?
You need to create only one button and assign the shortcut to it. Then depending on the state of the menu (open/close) perform an opposite action in the button's handler.
I solved it using a state machine. I created an enumaration with 2 states and made a static instance of it.
public:
enum STATE {MAIN_WINDOW, MENU};
static STATE state;
Then, i initialized it by default to have the MAIN_WINDOW state.
MyClass::STATE MyClass::state = STATE::MAIN_WINDOW;
Then, I connected both slots of the shortcuts for both buttons to call the same slot which checks and sets the current state.
QShortcut * shortcut = new QShortcut( QKeySequence( Qt::Key_Space ), back_to_main );
connect( shortcut, &QShortcut::activated, this, &MyClass::checkState);
QShortcut * shortcut = new QShortcut( QKeySequence( Qt::Key_Space ), go_to_menu );
connect( shortcut, &QShortcut::activated, this, &MyClass::checkState);
void MyClass::checkState()
{
if (MyClass::state == MyClass::MAIN_WINDOW )
{
MyClass::goToMenu();
MyClass::state = MyClass::STATE::MENU;
}
else if ( MyClass::state == MyClass::MENU )
{
MyClass::goBackToMain();
MyClass::state = MyClass::STATE::MAIN_APP;
}
}
*MyClass can be any class.
My QValueSpaceSubscriber cannot find the public slot datachanged I created for some reason,
public slots: void datachanged();
suc = new QValueSpaceSubscriber ("/Allstar/external/", this);
QObject::connect(suc, SIGNAL( contentsChanged()),this,
SLOT( datachanged();) );
I always get this message:
[Qt Message] Object::connect: No such slot ExternalAccess::datachanged();
ExternalAccess::ExternalAccess(QObject *parent):
QObject(parent)
{
qDebug() << "Loading.......";
suc = new QValueSpaceSubscriber ("/Allstar/external/", this);
QObject::connect(suc, SIGNAL( contentsChanged()),this,
SLOT( datachanged();) );
qDebug() << suc->value("/url").toString();
}
void ExternalAccess::datachanged()
{
//stuff
}
I've ran a million cleans/builds ect but it still won't connect to the slot, does anyone know why it may be doing this?
You need to remove the ; inside the SLOT() call.
QObject::connect(suc, SIGNAL( contentsChanged()),this,
SLOT( datachanged();) );
should be
QObject::connect(suc, SIGNAL( contentsChanged()),this,
SLOT( datachanged()) );
Also make sure datachanged() is declared in ExternalAccess. The this in your connect call indicates to look in the current object.
I do this in Qt Creator. I want to change my QStackedLayout with only QPushButton, not QComboBox. Is this possible? Anybody has implemented this? I got many example from Qt Documentation but all examples use QComboBox (now QPushButton like I need). This is my code:
#mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
Dialog::Dialog()
{
QVBoxLayout *mainlayout = new QVBoxLayout;
QVBoxLayout *layouta = new QVBoxLayout;
QVBoxLayout *layoutb = new QVBoxLayout;
QPushButton *tombola = new QPushButton("A");
QPushButton *tombolb = new QPushButton("B");
QPushButton *tombolc = new QPushButton("C");
QFrame *framea = new QFrame;
QFrame *frameb = new QFrame;
QStackedLayout *stackia = new QStackedLayout;
layouta->addWidget(tombola);
layoutb->addWidget(tombolb);
framea->setLayout(layouta);
frameb->setLayout(layoutb);
framea->setMinimumSize(88,88);
frameb->setMinimumSize(88,88);
//building frame
framea->setFrameShape(QFrame::StyledPanel);
framea->setFrameShadow(QFrame::Raised);
frameb->setFrameShape(QFrame::StyledPanel);
frameb->setFrameShadow(QFrame::Raised);
//get c button smaller
tombolc->setMaximumWidth(33);
stackia->addWidget(framea);
stackia->addWidget(frameb);
stackia->addWidget(tombolc);
mainlayout->addLayout(stackia);
QPushButton *tombold = new QPushButton("D");
mainlayout->addWidget(tombold);
setLayout(mainlayout);
connect(tombold, SIGNAL(clicked()), stackia, SLOT(setCurrentIndex(1))); //CONNECTOR
}
RESULT
Qt Creator says:
Object::connect: No such slot QStackedLayout::setCurrentIndex(1)
What is my mistake?
At second chance after searching and asking for 4 days, I have changed my connect() and function code into:
CONNECTOR:
connect(tombold, SIGNAL(clicked()), stackia, SLOT(change_stack()));
FUNCTION:
void Dialog::change_stack()
{
stackia->setCurrentIndex(1);
}
RESULT
But Qt Creator says:
Object::connect: No such slot QStackedLayout::change_stack()
and immediately the window closed.
At my sight, my code has error. But I don't know what error so I can't change QStackLayout content/page into another page. What is my mistake? I believe this is actually quite simple but I just don't know where is the error.
Any suggestion?
You should add change_stack function to your Dialog class and connect to it like this:
class Dialog : public QWidget
{
...
private slots:
void change_stack();
private:
QStackedLayout *stackia;
}
Dialog::Dialog
{
...
connect(tombold, SIGNAL(clicked()), this, SLOT(change_stack()));
...
}
void Dialog::change_stack()
{
stackia->setCurrentIndex(1);
}
Concerning
connect(tombold, SIGNAL(clicked()), stackia, SLOT(setCurrentIndex(1)));
A slot can only have as many parameters or less parameters than a signal.
E.g. you can do
connect( sender , SIGNAL( somethingHappened( const QString& ) ),
receiver, SLOT ( doSomething ( const QString& ) ) );
and you can do
// connect signal and ignore the parameter
connect( sender , SIGNAL( somethingHappened( const QString& ) ),
receiver, SLOT ( doSomethingElse ( ) ) );
but you cannot do
// this will not work
connect( sender , SIGNAL( somethingElseHappened( ) ),
receiver, SLOT ( doSomething ( const QString& ) ) );
What you probably want is something like this:
Dialog::Dialog
{
...
// This must be a member variable
_stackia = new QStackedLayout();
tombola->setProperty( "tabpage", 1 );
tombolb->setProperty( "tabpage", 2 );
tombolc->setProperty( "tabpage", 3 );
connect( tombola, SIGNAL( clicked () ),
this , SLOT ( tombol_clicked() ) );
connect( tombolb, SIGNAL( clicked () ),
this , SLOT ( tombol_clicked() ) );
connect( tombolc, SIGNAL( clicked () ),
this , SLOT ( tombol_clicked() ) );
}
// This must be defined as slot in the header
void Dialog::tombol_clicked()
{
int index = sender()->property( "tabpage" ).toInt();
_stackia->setCurrentIndex( index );
}
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