I'm trying to create a puzzle game with the following layout:
It's a basic slide puzzle so clicking on a button will swap its value with an adjacent button only if it's empty.
I'm having trouble adding buttons from the QButtonGroup container to the QGridLayout. I can't seem to find any relevant examples online about this issue. I can access the individual buttons in the button group and modify their values, but I can't seem to get them to display properly in the window. Any help would be appreciated!
Header File:
/** A single tile in the GUI of the 9 puzzle */
class Tile : public QPushButton {
Q_OBJECT
public:
Tile(int tileNumber);
int getNumber() {return m_Number;}
private:
int m_Number;
};
/** A view for the puzzle */
class PuzzleView : public QWidget {
Q_OBJECT
public:
PuzzleView(PuzzleModel* model);
public slots:
void refresh();
void tryToSlide(QAbstractButton* button);
private:
PuzzleModel *m_Model;
QGridLayout *m_Layout;
QButtonGroup m_Tiles;
};
Implementation file:
Tile::Tile(int tileNumber): m_Number(tileNumber) {
}
PuzzleView::PuzzleView(PuzzleModel* model) : m_Model(model) {
// Create the buttons
// This is where I'm having an issue (simplified to one button for clarity)
m_Tiles.addButton(new Tile(1), 0);
m_Layout = new QGridLayout();
m_Layout->addWidget(m_Tiles.button(0), 0, 0);
connect (&m_Tiles, SIGNAL(buttonClicked(QAbstractButton*)),
this, SLOT(tryToSlide(QAbstractButton*)));
}
I have a Wizard class, which creates three pages in the form of QStackedWidget in its constructor. Each page is its own class.
In page one, I have a variety of forms and I've implemented the slot that assigns them when the next button is clicked. They're assigned to their respective variables in the Wizard class. My goal is to pass this information to the PageTwo class to do the appropriate computations. Any pointers on how to do this?
Wizard class
class Wizard : public QDialog
{
Q_Object
public:
Wizard();
QString nameInput;
etc
}
constructor
Wizard::Wizard() : QDialog()
{
pages = new QStackedWidget();
pages->addWidget(pageOne = new PageOne(pages));
pages->addWidget(pageTwo = new PageTwo(pages));
connect(next, SIGNAL(clicked(bool)), this, SLOT(saveFormInfo()));
}
assignment function
void Wizard::saveFormInfo()
{
nameInput = pageOne->nameEdit->text();
etc
}
PageTwo class
class PageTwo : public QWidget
{
public:
PageTwo(QWidget *parent = 0);
void displayOutput(QHboxLayout *layout);
}
is this even the right way going about doing this? This is my first GUI project and I can't seem to figure out how to pass the variables in, I'm assuming because the page is created before the data is passed
There are many ways you could do it. Without changing too much your code, there is a suggestion below.
Wizard class
class Wizard : public QDialog
{
Q_Object
public:
Wizard();
QString nameInput;
etc
}
Wizard::Wizard() : QDialog()
{
pages = new QStackedWidget();
pages->addWidget(pageOne = new PageOne(pages, this));
pages->addWidget(pageTwo = new PageTwo(pages, this));
connect(next, SIGNAL(clicked(bool)), this, SLOT(saveFormInfo()));
}
void Wizard::saveFormInfo()
{
nameInput = pageOne->nameEdit->text();
etc
}
On pagetwo.h
class Wizard;
class PageTwo : public QWidget
{
public:
PageTwo(QWidget *parent = 0, Wizard * wizard);
void displayOutput(QHboxLayout *layout);
private:
Wizard * wizard;
}
On pagetwo.cpp
#include <wizard.h>
PageTwo::PageTwo(QWidget * parent, Wizard * w) :
QWidget(parent), wizard(w)
{
}
Then you can use this->wizard->nameInput to get the value you want on PageTwo
In Qt 4.8.5 32-bit and VS2010, I'm trying to create a Window as shown in this screenshot from QtDesigner:
When I run the application, the widgets get laid down on top of each other :
In the Console, I see this :
Does anybody have any idea why this is happening?
Here is my code :
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(){
ui = new Ui::AView();
ui->setupUi(this);
}
~MainWindow();
...
private:
Ui::AView* ui;
}
From the screenshot of QtDesigner, I see that your UI is for a QWidget, but you are applying it to a QMainWindow. This causes the problem, as the handling of main window is different to other widgets.
I don't know of a way to change the base class for a UI file. Maybe the best way is to create a new file, select the mainwindow template, and copy/paste the content from the old file.
Another option would be to have a QWidget, set it up with the UI, and add it as the main window's central widget:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(){
ui = new Ui::AView();
QWidget *wgt = new QWidget;
ui->setupUi(wgt);
this->setCentralWidget(wgt);
}
~MainWindow();
...
private:
Ui::AView* ui;
}
Try this:
class MainWindow : public QMainWindow, public Ui::AView
{
Q_OBJECT
public:
MainWindow(){
setupUi(this);
}
~MainWindow();
}
Firstly, I have two classes.
The First class called Widget, and the second class called addItem.
The Widget class is the main class(main window) of application ui, but the addItem class is just window, appears when click on add person to add new contact.
Also the Widget class has a child element called tableWidget .
Now I'm in addItem class, How to access to tableWidget element that is a child followed to Widget class?
Widget Class (.h)
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
protected:
void resizeEvent(QResizeEvent *event);
private slots:
void on_delete_btn_clicked();
void on_add_btn_clicked();
private:
Ui::Widget *ui;
};
addItem Class (.h)
namespace Ui {
class addItem;
}
class addItem : public QDialog
{
Q_OBJECT
public:
explicit addItem(QWidget *parent = 0);
~addItem();
private slots:
void on_addBtn_clicked();
private:
Ui::addItem *ui;
};
Edit
The following method belong to addItem class.
addItem.cpp:
void addItem::on_addBtn_clicked(){
emit mySignal(ui->name_txt->text(), ui->address_txt->text(), ui->phoneNum_txt->text(), ui->mobileNum_txt->text());
Widget *widget = new Widget;
connect(this, SIGNAL(mySignal(QString,QString,QString,QString)), widget, SLOT(addMyItem(QString,QString,QString,QString)));
this->close();
}
See also the rest of the code that I wrote :
addItem.h:
signals:
void mySignal(QString, QString, QString, QString);
Widget.h (mainwindow):
private slots:
void addMyItem(QString, QString, QString, QString);
Widget.cpp (mainwindow):
void Widget::addMyItem(QString name, QString address, QString phone_number, QString mobile_number){
qDebug() << name << "\n" << address << "\n" << phone_number << "\n" << mobile_number;
}
Setting
public:
Ui::Widget *ui;
isn't good practice, so you can use getters and setters.
For example, if you want take text from QTableWidget, provide method for example
QString Widget::getCellData(int row, int col)
{
return ui->tableWidget->item(row,col)->text();
}
To set data:
void Widget::setCellData(int row, int col, QString txt)
{
//create item if you need
ui->tableWidget->item(row,col)->setText(txt);
}
and so on. I hope you will understand main idea and how to do it.
But you can use also signals and slots:
For example user clicks on add button and you want add new data in table, then emit from addItem button signal:
emit mySignal(name, address, phone , mobile);
where name is ui->nameLineEdit->text() and so on. Just take text from lineEdit and send it.
Catch it with special slot in Widget class. In this slot you can create new row and set data.
void Widget::mySlot(QString name,QString address,QString phone ,QString mobile)
{
//create cells and set data in it
}
And of course you should connect mySignal from addItem class to mySlot in Widget class.
addItem *mDialog = new addItem;
connect(mDialog,SIGNAL(mySignal(QString,QString,QString,QString)),this,SLOT(mySlot(QString,QString,QString,QString)));
Conclusion: I think, you should use signals and slots.
Edit
Explanation in detail:
You have Dialog class and mainwindow:
dialog.h:
signals:
void mySignal(QString,QString,QString,QString);
dialog.cpp When user clicks we get data, emit signal and close dialog.
void Dialog::on_addItemButton_clicked()
{
emit mySignal(ui->lineEdit_2->text(),ui->lineEdit_3->text(),ui->lineEdit_4->text(),ui->lineEdit_5->text());
this->close();
}
mainwindow.h
private slots:
void addMyItems(QString,QString,QString,QString);
mainwindow.cpp
void MainWindow::addMyItems(QString name,QString address,QString phone ,QString mobile)
{
ui->tableWidget->setRowCount(ui->tableWidget->rowCount() + 1);//increase row count by 1
int row = ui->tableWidget->rowCount() - 1;
//set all data in cells
ui->tableWidget->setItem(row,0,new QTableWidgetItem(name));
ui->tableWidget->setItem(row,1,new QTableWidgetItem(address));
ui->tableWidget->setItem(row,2,new QTableWidgetItem(phone));
ui->tableWidget->setItem(row,3,new QTableWidgetItem(mobile));
}
Slot for button in mainwindow which opens dialog:
void MainWindow::on_Add_clicked()
{
Dialog *dia = new Dialog;//our dialog
dia->setAttribute(Qt::WA_DeleteOnClose);//we don't want memory leak
//connection
connect(dia,SIGNAL(mySignal(QString,QString,QString,QString)),this,SLOT(addMyItems(QString,QString,QString,QString)));
dia->setModal(true);//you can use modal window instead of exe()
dia->show();
}
Result:
I clicked on Add button and get dialog:
Now I set some data and clicked Additem button:
As you can see, our connection works, data properly placed in the tableWidget.
The way I normally handle these things is quite non Qt-ish, but it makes unit testing easier. I have something like MVC (model-view-controller), but in Qt the V and C are one.
For example the model could be:
class Record
{
public:
std::string mName;
std::string mAddress;
std::string mPhoneNumber;
std::string mMobileNumber;
};
class Model
{
public:
void AddRecord(std::unique_ptr<Record> rec)
{
mRecords.emplace_back(std::move(rec));
}
const Record* ItemAt(size_t index) const
{
if (index > mRecords.size()) { return nullptr; }
return mRecords(index);
}
size_t NumRecords() const
{
return mRecords.size();
}
private:
std::vector<std::unique_ptr<Record> mRecords;
};
Then in your main function you can construct a model and pass it to your main/view window:
int main( int argc, char **argv )
{
// Create app instance
QApplication a( argc, argv );
Model model;
MainWindow mainWindow(model); // MainWindow stores a reference to the model
mainWindow.exec();
And the when you create the sub dialog simply pass that the model too. Or have it return a std::unique_ptr<Record>
void MainWindow::on_Add_clicked()
{
Dialog *dia = new Dialog(this, mModel);//Where mModel is the Model instance reference we passed to Widget in main()
dia->exec();
}
Its worth noting that Qt comes with its own model classes for the views. You could take your own Model class and have Qts view model auto update the UI when your view model changes. This is much more advanced though. For now you could just manually keep your model in sync with your UI, i.e if you add a record or remove a record from model, then update your table widget.
Disclaimer: I didn't actually try to compile any of this code, treat it as pesudo code ;)
I have two widgets defined as follows
class mainWindow : public QWidget
{
Q_OBJECT
public:
mainWindow();
void readConfig();
private:
SWindow *config;
QVector <QString> filePath;
QVector <QLabel*> alias,procStatus;
QVector <int> delay;
QGridLayout *mainLayout;
QVector<QPushButton*> stopButton,restartButton;
QVector<QProcess*> proc;
QSignalMapper *stateSignalMapper, *stopSignalMapper, *restartSignalMapper;
public slots:
void openSettings();
void startRunning();
void statusChange(int);
void stopProc(int);
void restartProc(int);
void renew();
};
class SWindow : public QWidget
{
Q_OBJECT
public:
SWindow(QWidget *parent=0);
void readConfig();
void addLine(int);
private:
QVector<QPushButton*> selectButton;
QVector<QLabel*> filePath;
QVector<QLineEdit*> alias;
QSignalMapper *selectSignalMapper;
QVector<QSpinBox*> delay;
QGridLayout *mainLayout;
public slots:
void selectFile(int);
void saveFile();
void addLineSlot();
};
when i create and display SWindow object from mainWindow like this
void mainWindow::openSettings()
{
config = new SWindow();
config->show();
}
everything is ok, but now i need to access the mainWindow from SWindow, and
void mainWindow::openSettings()
{
config = new SWindow(this);
config->show();
}
doesn't display SWindow. How can i display SWindow?
How do i call a function on widget close?
By default a QWidget isn't a window. If it is not a window and you specify a parent, it will be displayed inside the parent (so in your case it is probably hidden by other widgets inside your mainWindow).
Look at windowFlags() too. Or you could make your SWindow inherit from QDialog, depending on what you use it for.
As for calling a function on widget close : you could reimplement closeEvent().
When you do config = new SWindow(this); you're setting the parent of config to be the instance of mainWindow.
This means config is no longer a top-level widget, therefore it won't display outside the mainWindow instance (specifically, it would need to be the central widget or inside the mainWindow instance's layout to be displayed).
EDIT: Sorry - I missed your last question; How do i call a function on widget close
You will want to override the QWidget::closeEvent(QCloseEvent *event) method. This gets called when you close a top-level widget. The most practical thing to do is emit() a signal so that another class can handle it having been closed.
As noted by Leiaz, you can use the windowsFlags flag when you create the widget. It would look like this:
void mainWindow::openSettings()
{
config = new SWindow(this, Qt::window);
config->show();
}
To reimplement the closeEvent:
header:
protected:
virtual void closeEvent ( QCloseEvent * event )
cpp:
void sWindow::closeEvent(QCloseEvent *event)
{
this->parentWidget()->SomeFunction();
qWidget::closeEvent(event);
}
However, its probably better to use signal/slots for your case here. Since you said you want to call the parent's renew method on some button click in sWindow, what you want is to EMIT a signal everytime the button is clicked, and connect this signal in the parent with the parent's refresh slot.
void sWindow::sWindow()
{
...
connect(ui.button, SIGNAL(clicked()), this, SLOT(btnClicked()));
}
void sWindow::btnClicked()
{
// whatever else the button is supposed to do
emit buttonClicked();
}
and in your parent class
void mainWindow::openSettings()
{
config = new SWindow(this, Qt::window);
connect(config, SIGNAL(buttonClicked()), this, SLOT(refresh()));
config->show();
}