I would like to set the font color and the background color if I click on a button.
I just added these two functions to the mainwindow.h file
public:
void createGrid();
private slots:
void clickCell(int row, int col);
mainwindow.cpp
QVector<QVector<QPushButton*>> buttons(10);
void MainWindow::createGrid() {
QFrame *frame = new QFrame(this);
QGridLayout *layout = new QGridLayout(frame);
layout->setMargin(0);
layout->setSpacing(0);
for(int i = 0; i < 10; ++i){
buttons[i].resize(10);
for(int j = 0; j < 10; ++j){
QPushButton *button = new QPushButton("0");
button->setMinimumSize(50,50);
button->setMaximumSize(50,50);
connect(button,SIGNAL(released()),this,SLOT(clickCell(i,j)));
layout->addWidget(button,i,j);
buttons[i][j] = button;
}
}
setCentralWidget(frame);
}
void MainWindow::clickCell(int row, int col) {
buttons[row][col]->setStyleSheet("background-color: grey; color: red");
}
When I run my code I am getting the following outputs about 100 times:
QObject::connect: No such slot MainWindow::clickCell(i,j) in ..\untitled\mainwindow.cpp:41
QObject::connect: (receiver name: 'MainWindow')
As per the comment: use the new signal/slot syntax to invoke a lambda as the slot. So your connect call should be something like (untested)...
connect(button, &QPushButton::released, this,
[=, this]
{
clickCell(i, j);
});
Related
I am trying to learn and build a small minesweeper gui app.Here is how this looks like:
The next thing I want to do is after clicking one button, then the button will be set to hide() and a QLabel will appear in the same place.
My code is like this:
.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void setUI();
void clickedBtnInfo();
private:
Ui::MainWindow *ui;
QWidget *centralWidget;
QGridLayout *centralLayout;
QPushButton *btn[81];
QPushButton *btnSender;
QLabel *lbl[81];
QString clickedBtnName;
private slots:
void btnClicked();
};
.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
lbl[81] = new QLabel(centralWidget);
setUI();
for(int i = 0; i < 81; i++) {
connect(btn[i], SIGNAL(clicked(bool)), btn[i], SLOT(hide()));
connect(btn[i], SIGNAL(clicked(bool)), this, SLOT(btnClicked()));
}
centralWidget->setLayout(centralLayout);
}
void MainWindow::setUI()
{
...
centralLayout = new QGridLayout(centralWidget);
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
centralLayout->addWidget(btn[j + i * 9], 0 + i, j);
centralLayout->setSpacing(0);
}
}
...
}
void MainWindow::clickedBtnInfo()
{
btnSender = qobject_cast<QPushButton*>(sender());
clickedBtnName = btnSender->objectName();
}
void MainWindow::btnClicked()
{
clickedBtnInfo();
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
if(btn[j + i * 9]->objectName() == clickedBtnName) {
centralLayout->addWidget(lbl[j + i * 9], 0 + i, j);
centralLayout->setSpacing(0);
}
}
}
}
When I ran this and clicked one of the buttons, the app just force quit(The program has unexpectedly finished.)
So how can I solve this problem and replace QPushButton with QLabel after clicking? Thanks.
The Problem why your code leads to a crash was correctly pointed out by #G.M. - lbl[81] = new QLabel(centralWidget); will create only 1 label, and place it in the 81st array field. That are 2 errors at once:
If your array is 81 elements long, they are numbered: 0, 1, ..., 79, 80. The last element is 80, because you start counting at 0. So placing something at position 81 is not possible
To actually create 81 new labels, you have to create them in a loop:
Sample code:
for(int i = 0; i < 81; i++) { //goes from 0 to 80
lbl[i] = new QLabel(centralWidget);
lbl[i]->setObjectName(QStringLiteral("Label %1").arg(i));
}
The second line gives each label a custom name. See QString::arg for details.
One more tip: Avoid C-arrays, unless you need high performace/low memory (which is not the case for your example). Instead try to use one of the Qt container classes, e.g. QList or QVector. (You can use std::vector etc. as well, but when working with Qt, I would recommend to use the Qt containers)
For your case, I would recommend QVector, as it performs best with fixed-sized arrays. With both of these changes, update your code to:
class MainWindow : public QMainWindow
{
//...
private:
Ui::MainWindow *ui;
QWidget *centralWidget;
QGridLayout *centralLayout;
QVector<QPushButton> btn;
QPushButton *btnSender;
QVector<QLabel> lbl;
QString clickedBtnName;
};
In your cpp file, update the part where you create the arrays:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
lbl.resize(81);
for(int i = 0; i < lbl.size(); i++) { //goes from 0 to 80
lbl[i] = new QLabel(centralWidget);
lbl[i]->setObjectName(QStringLiteral("Label %1").arg(i));
}
setUI();
//...
}
void MainWindow::setUI()
{
//keep your code, but remember to prepare the btn vector with:
btn.resize(81);
//then you can fill the vector just like you are used to:
btn[0] = ui->btn0;
//...
}
And the rest stays the same, as these classes allow you to keep the standard array access syntax you know.
I would like to delete row where QPushButton is clicked how it is possible to I think it is reasonable to use slots but how to do it don't know , if you have any ideas how to get a row of selected button please share, thanks.
It is my table
It is a code where i add rows to my QTableWidget
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
for(int i = 0; i<20;i++)
ui->tableWidget->insertRow(ui->tableWidget->rowCount());
QVector<QString>vec;
vec<<"first"<<"sec"<<"third"<<"for"<<"fif"<<"first"<<"sec"
<<"third"<<"for"<<"fif";
vec<<"first"<<"sec"<<"third"<<"for"<<"fif"<<"first"<<"sec"
<<"third"<<"for"<<"fif";
for(int i = 0; i<ui->tableWidget->rowCount();i++)
{
for(int j = 0; j<ui->tableWidget->columnCount();j++)
{
if(j == 0)
{
QWidget* pWidget = new QWidget();
QPushButton* btn_edit = new QPushButton();
btn_edit->setText("Remove");
QHBoxLayout* pLayout = new QHBoxLayout(pWidget);
pLayout->addWidget(btn_edit);
pLayout->setAlignment(Qt::AlignCenter);
pLayout->setContentsMargins(0, 0, 0, 0);
pWidget->setLayout(pLayout);
ui->tableWidget->setCellWidget(i, j, pWidget);
continue;
}
QTableWidgetItem*item = new QTableWidgetItem(vec[i]);
item->setFlags(item->flags() ^ Qt::ItemIsEditable);
ui->tableWidget->setItem(i, j, item);
}
}
}
This task can be solved using the removeRow() method but before we must get the row. First of all we will connect all the buttons to a slot inside the loop as shown below:
*.h
private slots:
void onClicked();
*.cpp
[...]
QPushButton* btn_edit = new QPushButton();
btn_edit->setText("Remove");
connect(btn_edit, &QPushButton::clicked, this, &MainWindow::onClicked);
[...]
In the slot we can get the button that emits the signal through the sender() method, then we get QWidget parent (created with the name pWidget), this is the widget that is added to the QTableWidget and its position is relative to it, then we use the method indexAt() to get the QModelIndex associated with the cell, and this has the information of the row through the method row(). All of the above is implemented in the following lines:
void MainWindow::onClicked()
{
QWidget *w = qobject_cast<QWidget *>(sender()->parent());
if(w){
int row = ui->tableWidget->indexAt(w->pos()).row();
ui->tableWidget->removeRow(row);
ui->tableWidget->setCurrentCell(0, 0);
}
}
Note: The setCurrentCell() method is used to set the focus since the last cell that has it has been deleted.
The complete example can be found in the following link.
When you are creating QPushButton just add :
btn_delete = new QPushButton("Remove", ui->tableWidget);
btn_delete->setObjectName(QString("%1").arg(ui->tableWidget->rowCount()));
connect(btn_delete, SIGNAL(clicked()), this, SLOT(CellButtonDeleteClicked()));
And create function CellButtonDeleteClicked()
void CellButtonDeleteClicked()
{
// by this line I can get the sender of signal
QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
int row = pb->objectName().toInt();
ui->tableWidget->removeRow(row);
}
I am trying to count how many buttons have been checked
void pracownik2::on_pushButton_4_clicked()
{
this->setWindowTitle("EKRAN");
QWidget *centralWidget = new QWidget;
int licznik=1;
QString licz;
QString kolumny = ui->lineEdit->text();
QString wiersze = ui->lineEdit_2->text();
QPushButton *button[wiersze.toInt()][kolumny.toInt()];
QGridLayout *controlsLayout = new QGridLayout;
for(int i=0;i<wiersze.toInt();i++)
{
for(int j=0;j<kolumny.toInt();j++)
{
licz = QString::number(licznik);
licznik++;
button[i][j] = new QPushButton(licz);
button[i][j]->setCheckable(1);
controlsLayout->addWidget(button[i][j], i, j);
}
}
QPushButton *okej = new QPushButton("Zatwierdź");
QPushButton *anul = new QPushButton("Anuluj");
controlsLayout->addWidget(okej, wiersze.toInt(), 0);
controlsLayout->addWidget(anul, wiersze.toInt(), 1);
controlsLayout->setHorizontalSpacing(0);
controlsLayout->setVerticalSpacing(0);
centralWidget->setLayout(controlsLayout);
setCentralWidget(centralWidget);
for(int i=0;i<wiersze.toInt();i++)
{
for(int j=0;j<kolumny.toInt();j++)
{
connect(button[i][j],SIGNAL(toggled(bool)),this,SLOT(tescik()));
}
}
connect(anul,SIGNAL(clicked()),this,SLOT(close()));
connect(okej,SIGNAL(clicked()),this,SLOT(okay2()));
}
void pracownik2::tescik()
{
miejsca++;
}
void pracownik2::okay2()
{
QString m=QString::number(miejsca);
QMessageBox::information(this,"elo","wybranych miejsc: " + m);
}
If i check buttons number 1,2,3 and press okay button it shows that 3 buttons have been checked BUT if I check button number 1 and uncheck it and press okay it shows that 2 buttons have been checked. How to make my variable increment only if I check button, not also when I uncheck it?
I'm sorry for the code edit, just couldn't make it look better
The signal toggled has a boolean parameter, add it to your slot, and adjust count according to it. Change connect:
connect(button[i][j],SIGNAL(toggled(bool)),this,SLOT(tescik(bool)));
And change slot:
void pracownik2::tescik(bool t) {
if (t) miejsca++;
else miejsca--;
}
I'm working on a project where i'm trying to add a QPushButton into a QTableView.
also i want to connect that button to open a document from a database. So far i added the button and i wrote the connect statement for it but when i click on the button nothing happened.
here is my code
void MainWindow::DocumentTable()
{
tableview = new QTableView;
query = new QSqlQueryModel(this);
signalMapper = new QSignalMapper(this);
foreach(it,treeWidget->selectedItems())
{
for (int col=0; col< it->columnCount(); ++col)
{
qDebug() << col << it->text(col);
QSqlQuery qry;
qry.prepare("select * from document where Folno=:Folno");
qry.bindValue(":Folno", it->text(col));
qry.exec();
query->setQuery(qry);
tableview->setModel(query);
tableview->setEditTriggers(QAbstractItemView::NoEditTriggers);
for (int i = 0; i< 1000; i++)
{
button= new QPushButton("Open Document");
tableview->setIndexWidget(tableview->model()->index(i, 0), button);
signalMapper->setMapping(button, i);
}
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(imageFROMdatabase()));
tableview->show();
Docwidget= new QDockWidget(this);
Docwidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
Docwidget->setWidget(tableview);
addDockWidget(Qt::RightDockWidgetArea,Docwidget);
Docwidget->show();
if(!query->submit())
{
QMessageBox::warning(0,"Error",query->lastError().text());
}
db.close();
}
}
}
and this is the slot function
void MainWindow::imageFROMdatabase()
{
QSqlQuery imageQuery;
imageQuery.prepare("SELECT * from doc_page where doc_no=:doc_no and f_number=:f_number");
imageQuery.bindValue(":doc_no", 1);
imageQuery.bindValue(":f_number",1);
imageQuery.exec();
imageQuery.next();
if( imageQuery.lastError().isValid())
{
QMessageBox::warning(0,"Error",imageQuery.lastError().text());
// QSqlDatabase::database().rollback();
}
else
{
// QByteArray ba1 = imageQuery.value(1).toByteArray();
QPixmap pic;
pic.loadFromData( ba);
scrollArea = new QScrollArea;
scrollArea->setBackgroundRole(QPalette::Dark);
scrollArea->setEnabled(true);
QString fileName = QFileDialog::getOpenFileName(this,"Open Image File",QDir::currentPath());
QImage image(fileName);
scene = new QGraphicsScene();
view = new QGraphicsView(scene);
item = new QGraphicsPixmapItem(QPixmap::fromImage(image));
scene->addItem(item);
xwidget= new QDockWidget(this);
xwidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
xwidget->setWidget(view);
addDockWidget(Qt::RightDockWidgetArea,xwidget);
xwidget->show();
db.close();
}
}
please tell if anything wrong in these codes.
The slot method is not same as signal method. They should have exact equal signatures. qDebug() should have generated some warnings for you. Read outputs carefully. imageFROMDatabase() method should accept an integer as its input too. Signals are not real functions. They are used to trigger another function which is matched in signature with them.
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(imageFROMdatabase()));
Change it like this :
connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(imageFROMdatabase(int)));
ans also :
void MainWindow::imageFROMdatabase( int x ) { ... }
Also if the signals and slots are not in a thread read the manual enum Qt::ConnectionType
My implementation of QTabWidget is not detecting its tabCloseRequested() and currentChanged() signals.
TileSheetManager::TileSheetManager(QWidget *parent)
: QTabWidget(parent)
{
int w = WIDTH;
int h = HEIGHT;
this->setMinimumSize(w, h);
this->setMaximumSize(w, h);
setTabsClosable(true);
setTabShape(QTabWidget::Rounded);
connect(this, SIGNAL(tabCloseRequested(int index)), this, SLOT(closeTileWidget(int index)));
connect(this, SIGNAL(currentChanged(int index)), this, SLOT(tabChanged(int index)));
}
qDebug() was not working for me, so I'm using a QMessageBox for this.
void TileSheetManager::closeTileWidget(int index)
{
QMessageBox msgBox;
msgBox.setText("Tab " + QString::number(index) + " removed!");
msgBox.exec();
TileWidget *t = (TileWidget *) widget(index) ;
t->deleteLater();
removeTab(index);
}
void TileSheetManager::tabChanged(int index)
{
QMessageBox msgBox;
msgBox.setText("Tab was Changed!");
msgBox.exec();
TileWidget *t;
for(int i = 0; i < count(); i++)
{
t = (TileWidget *) widget(i) ;
t->resetSetBrush();
}
}
Tabs aren't getting closed, selected brushes are not being reset, and I get no messages, so I'm concluding the signals aren't being picked up. Which is weird, because I have used similar code for a previous project, in which case it worked.
Don't use variable names in the connect function :
Note that the signal and slots parameters must not contain
any variable names, only the type.
The connection should be
connect(this, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTileWidget(int)));
connect(this, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));