So I am currently writing a Qt app, but am fairly new to it and am unsure of how certain things should be designed. As I add more and more code, my MainWindow.cpp is getting large and unruly. I am curious of what is the proper way to separate my code up in to smaller files. Each of the components that I wish to move to a separate file is making changes to the UI. What I am currently doing is literally just creating a new .cpp file, and then including my MainWindow and also the MainWindow ui. Here is an example of a function that I placed in its own file.
#include <QDebug>
#include <QString>
#include <QPalette>
#include "master_main_window.h"
#include "ui_master_main_window.h"
#include "cmd_net.h"
#include "cmd.h"
/*
* Send a command/data packet to the host
*/
void MasterMainWindow::sendCommand() {
// Disable some GUI components
ui->con_btn_cmd_disc->setEnabled(false);
ui->con_btn_cmd_rec->setEnabled(false);
ui->cmd_edit->setEnabled(false);
ui->cmd_btn_send->setEnabled(false);
// Send the packet through the open socket
sendCmdPacket(ui->cmd_edit->text().toLocal8Bit().data(), cmdSocket);
// Enable the socket notifier
cmdSockNotifier->setEnabled(true);
qDebug() << "Command sent:"
<< (ui->cmd_edit->text().toLocal8Bit().data())
<< "\nSent Packet";
}
As you can see, I have simply included the "master_main_window.h" and "ui_master_main_window.h" which gives me access to all of the different functions/variables/ui available in the MainWindow class. I am curious if I am doing this the proper way, or if there is a better method to achieve my goal of separating the functions into separate files.
If I'm getting what you're asking correctly, since you're working with pointers here you can simply write other classes in different files and pass your variables in ui to them.
For example, let's say you're ui has these variables in it:
QWidget * leftWidget;
QWidget * centerWidget;
QWidget * rightWidget;
You can write classes who inherit QWidget and give these variables to them, like this:
class leftWidgetClass : public QWidget
{
//your code here
}
and so on...
And then in the constructor of your MainWindow you can do this:
leftWidget = new leftWidgetClass();
rightWidget = new rightWidgetClass();
centerWidget = new centerWidgetClass();
The proper way would be to not using ui namespace and mainwindow methods outside of mainwindow class. You should sublass QWidget or any other classes, which functionality you want to extend, create all the functionality you need (like doImportantStuff()) and include this new class header ( let's call it myWidget) in mainwindow.h. Then you can create those myWidget's in mainwindow, add them to ui (ether through disigner, add QWidget, click promote to , select your class, or by adding to layout manually) and use all of their signals and functionality, that you've created, like:
connect(ui->doWorkButtot(),SIGNAL(clicked()), myWidget, SLOT(doImportantStuff()));`
again, changes to ui you can do via signals and slots mechanism; In myWidget when you feel, you have to change ui somehow, emit a signal and catch it in mainwindow with connect. Example:
myWidget.h:
...
signals:
void needUiChange();
....
public slots:
myWidget::doImportantStuff(){
....
emit needUiChange();
// you can emit signals with params also:
// emit setToolTipText(QString("from signal));
...
and in mainwindow catch signal with connect:
connect(myWidget, SIGNAL(needUiChange(),this,SLOT(updateUI());
// actually you can connect directly to let's say `pushButton`'s slot `hide()`
Related
I know this is very clunky and I'm probably doing a lot of wrong things but so far everything I saw on the net gives back the same errors: invalid use of non-static data member ui.
So in the MainWindow, I have a comboBox named hometeam, and I want to display the currentText on a Qlabel named label which is on another Form Class called Dialog
I figured they're both private members so I added friend class MainWindow and friend class dialog in the respective headers (I know this is pretty wrong but it's the last thing I tried), I included the "ui_mainwindow" and "ui_dialog" in the .cpp files, and here's the bit of code I'm trying:
ui->label->setText(MainWindow::ui->hometeam->currentTex());
Keep in mind that I don't want a QDialog, the second window will do a lot more than a display, I just want to access the objects from a different window. Slots and signals give the same error.
Thanks !
I think the proper way to do that, is to add a function to your MainWindow class:
QString hometeamText() const
{
return ui->hometeam->currentTex();
}
This way you can access the information you need without violating encapsulation rules, but you need an instance of MainWindow to do it, and sure must keep a pointer to it in your Dialog class:
class Dialog
{
private:
MainWindow * mainwindow;
public:
void setMainWindow(MainWindow * w) { mainWindow = w; }
then somewhere (e.g. in main) you can do something like:
MainWindow mainwindow;
Dialog dialog;
dialog.setMainWindow(&mainWindow);
and from inside your Dialog class, wherever you need it:
ui->label->setText(window->hometeamText());
I have two objects, one will hold the graph, and the other a few buttons. How to use (connect) so that when you press button 1, the inscription is displayed in debag or the schedule is filled with a new one?
For example, I press the button created by the class BtnBox and my graph is displayed. How to use connect()?
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QThread>
#include "btnbox.h"
#include "plot.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
BtnBox *panel = new BtnBox(&a);
Plot *plot = new Plot();
QObject::connect(panel, SIGNAL(clickedBtn1()), plot, SLOT(slotPrinter()));
// panel->show();
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(plot);
mainLayout->addWidget(panel);
QWidget window;
window.setLayout(mainLayout);
window.show();
return a.exec();
}
Maybe you can use the QPushButton::clicked signal and write something like this:
connect(ui->pushButtonObj, &QPushButton::clicked, plot, &Plot::slotPrinter);
But if you want a custom behavior with your class BtnBox you can create on header file of BtnBox a signal.
signals:
void clickedBtn1();
And use: emit clickedBtn1(); whenever you want to emit it, your connect should work.
There is no need for implementation of the signal, you just have to emit it.
The emit keyword is not really necessary, if you want you can simply use clickedBtn();
Ok, I assume you have class BtnBox, and it already has this in the class definition (in .h file usually):
signals:
void clickedBtn1();
Qt moc will generate the implementation of that method, you don't need to do anything more for it here. But you do need to get that signal emitted. In many cases you would add emit clickedBtn1(); in the right places, but in this case you probably want to do something like this in BtnBox::BtnBox constructor:
connect(ui->button1, SIGNAL(clicked()), this, SIGNAL(clickedBtn1()));
Connecting signal to signal will simply do signal forwarding. Replace ui->button1 with the correct pointer to the button, whatever you have in your code.
Note on how to not do it, just to provide a bit of food for thought: Alternative way would be to expose the button 1 from the class, so in your main() you could then do something like this: QObject::connect(panel->getButton1(), SIGNAL(clickedBtn1()), plot, SLOT(slotPrinter()));. But this is generally considered a bit dirty, exposing internals of BtnBox class like that. It's better to expose the signal, and then the code using the class does not need to care how it gets emitted (for example from several different parts of BtnBox), or how internal implementation of BtnBox might change (for example converting it to QML).
I've created a qt widgets application. Using the design mode I've created a QTextEdit and indicated that in the header file:
...
QT_BEGIN_NAMESPACE
class QAction;
class QMenu;
class QTextEdit;
QT_END_NAMESPACE
...
private:
Ui::MainWindow *ui;
QTextEdit *textEdit_2;
};
There is also a slot which is triggered by pushing a button. What it has to do is to insert some text into textEdit_2 after the button is pushed, still the program crashes.
In mainwindow.cpp:
void MainWindow::on_action_4_triggered()
{
textEdit_2->setText("text");
}
I've also tried
textEdit_2->setText(QString("text"));
which anyway doesn't work. What's the problem?
textEdit_2->setText("text");
The problem is that you are trying to ignore the actual text widget created in QtDesigner and invent another as a class member. This is not going to fly as you seem to want it.
In order to reuse the text widget from the UI that you created with the graphical tool, you would need to reuse the ui object as follows:
ui->textEdit_2->setText("text");
Please also note that you do not need to construct QString explicitly like this:
textEdit_2->setText(QString("text"));
This will be all automatic for you.
iI have a Qt application using QGLWidget for drawing (simply a Viewport for 3D drawing etc...)
There is two main classes in the application.
MainWindow
Inherits from QWidget which holds many GUI widgets (menubar, toolbars, viewport, treeview...etc)
System
Does every other operation from GUI (math, geometry, IO, data processing, etc and holds "Scene" object which has drawable components.) Also it has Singleton pattern to create one global Instance for itself.
I am using Qt signal-slot mechanism to communucate between MainWindow and System, actually MainWindow has the signals and System has the slots. My problem starts here, how can I signal from System to MainWindow slots? When I define MainWindow in System object it gives me lots of error. Normaly System references in MainWindow don't give error. But when I include MainWindow's header file in System.h, System references give error in MainWindow side "'System': the symbol to the left of a '::' must be a type".
Basically my structure is look like this.
// MainWindow.h
#include "System.h"
class MainWindow : public QWidget
{
Q_OBJECT
public:
QToolBar* MyToolBar; // etc...
MainWindow()
{
ConnectSignals();
}
void ConnectSignals() { connect(my_action, SIGNAL(triggered()), System::GetInstance()->Actions, SLOT(action())); }
}
// System.h
#include "MainWindow.h" // if I wrote this, it gives me error in compile time.
class System
{
static bool m_instance;
static System* m_system;
// private constructor
System()
{
Actions = new MyActionList();
}
public:
MyActionList* Actions;
System* GetInstance()
{
if (!m_instance)
{
m_system = new System();
m_instance = true;
return m_system;
}
else { return m_system; }
}
}
// System.cpp
bool System::m_instance = false;
System* System::m_system = NULL;
Of course Actions has slot action()
So how can I access MainWindow from System?
The problem in your approach is the cyclic dependency between MainWindow and System - MainWindow includes System, System includes MainWindow.
In order to pass signals from System to MainWindow you need to make MyActionList of Sytem to emit signals that any receiver (MainWindow in your case) can handle. You absolutely do not need to include MainWindow stuff into the System - keep your backend (System) independent of any GUI component. Just incapsulate System into your MainWindow class and connect MyActionList signals to MainWindow slots. You need to have something like this in your MainWindow:
connect(my_action, SIGNAL(triggered()), System::GetInstance()->Actions, SLOT(action()));
connect(System::GetInstance()->Actions, SIGNAL(systemSignal()), this, SLOT(handleSystemSignal()));
where systemSignal() is a signal emitted from System or its MyActionList component.
As #vahancho states, you should keep a separation between the GUI and other systems. Another method to do this would be to introduce a delegate object to handle the communication between the two.
In addition, if you're inlining code as you've shown in your question, then that will increase the possibility of cyclic dependencies. Move the implementation into the .cpp file and use forward declarations instead of including headers in other header files where possible. This also has the benefit of speeding up compilation, which you'll notice with large projects.
How can i access ui files of children of a class. Lets say MainWindow class has twoa child dialog. I want to access LINEEDIT of dialog so that i can take text from there. Similarly how can i access ui files of parent inside child class in QT. Note: I havn't inherited any thing from Parent class.
I have writen the following code, in order to display a dialog but it won't show!
void MainWindow::displaydialog()
{
ItemDialog dialog= new ItemDialog(this);
dialog->show(); // it is not displaying the dialog
}
and how can i access the ui widgets like check whether ListWidget item has been selected or not.
Here is the code of itemdialog,
#include "itemdialog.h"
#include "ui_itemdialog.h"
#include "mainwindow.h"
ItemDialog::ItemDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::ItemDialog)
{
ui->setupUi(this);
setWindowTitle("Status Dialog");
setFixedSize(QWidget::sizeHint());
}
ItemDialog::~ItemDialog()
{
delete ui;
}
void ItemDialog::on_pushButton_clicked()
{
MainWindow obj;
obj.okbuttonclicked(ui->lineEdit->text());
}
Please review an example such as this: http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
It explains how to use the ui files that you generate from Qt Designer. You shouldn't really think of them as "ui files" in the sense of accessing them on the widgets in your class. The idea is that you include them, and then use their setupUi() function to apply them to your given class. At that point, everything you created in qt designer, and that is in that ui file, is now a member of your class. They can be accessed via the naming you used in qt designer.
As for why your dialog isn't showing...I don't know because you only included 3 lines of code as an example. Theoretically it should show if Mydialog was properly set up. You could just try changing it to a QDialog to make sure you didn't do anything wrong with your custom class.
It depends what you want that dialog for. Either it's a modal dialog - some kind of a information display or retrival that blocks the function of your program until user reacts, or it's somekind of toolbox or similar, in which case you probably should not use QDialog.
If a modal dialog with a line edits and/or additional features is what you want, you should read up on QDialog in the doc. See the exec() function. Basic usage would go like this:
void MainWindow::displaydialog()
{
ItemDialog *dialog = new ItemDialog();
if (dialog->exec() == someApropriateReturnStatus)
{
QString somevalue = dialog->someValue();
int dialog->someOtherValue();
//do something with the value
}
delete dialog;
}
The point is that the ItemDialog class handles the UI internally and implements the getter functions accordingly, you should not (in most typical cases) access it's UI from outside.
If a simple line edit is all you want, you'd be better off using one of the standard dialogs already implemented in Qt, have a look at the Standard Dialogs Example