Ok, I admit it. This is confusing, and I'm not too fond of reading tons of documentation - it takes more time than I usually have. However, I often butt my head against the wall, and I'm doing that now. I'm trying to create an elevator simulator using C++ and GTK. But I'm having problems with the program architecture and the functionality of the program and the GUI, and how to tie these together.
What I originally thought was something like this:
driver (file with main()): This instantiates the GUI from a gui class, and an elevator object from an elevator class. Then it has logic for controlling the elevator (through public functions in the elevator object), and for updating the GUI (through public functions in the GUI class).
gui (.cpp/h): Contains the GTK code for the whole GUI: widgets, signal handlers, public access methods for updating the widgets etc.
elevator (.cpp/h): Contains the elevator functions, like keeping track of the current floor, the queue, moving the elevator etc. Also contains, of course, public access methods for receiving a new floor to add to the queue, return the current floor etc.
driver.cpp:
Code:
#include <iostream>
#include <gtkmm.h>
#include "Egui.h"
#include "Elevator.h"
using namespace Glib;
int main (int argc, char *argv[]) {
Glib::RefPtr<Gtk::Builder> builder;
Gtk::Main kit(argc, argv);
// load the interface
builder = Gtk::Builder::create();
builder->add_from_file("src/Egui.glade");
// Get the main window:
Egui *mwindow;
builder->get_widget_derived("mwindow", mwindow);
Elevator *elevator1; // - Create an object of the Elevator class. This will be our elevator for
elevator1 = new Elevator(); // this simulation.
int f = elevator1->getCurrentFloor(); // - Get the currect floor from the elevator (ask it which floor it's on).
mwindow->setCrrntFloorEnt(5); // - Set the currect floor in the gui (the Egui object *mwindow created above.)
// Everthing above this line is executed BEFORE the GUI is started. Hence, there can be no logic here.
// start the event loop
Gtk::Main::run(*mwindow );
// Everthing after this line never happens, unless the gui is terminated. Hence, there can be no logic here either.
// The logic should be INSIDE the main program loop, but how?
return 0;
}
Egui.cpp:
Code:
#include "Egui.h"
#include "Elevator.h"
#include <iostream>
using namespace Glib;
Egui::Egui (BaseObjectType* base_object, const Glib::RefPtr<Gtk::Builder>& m_refBuilder):Gtk::Window(base_object) {
m_refBuilder->get_widget("mwindow", mwindow);
m_refBuilder->get_widget("f5bttn", f5bttn);
m_refBuilder->get_widget("f4bttn", f4bttn);
m_refBuilder->get_widget("f3bttn", f3bttn);
m_refBuilder->get_widget("f2bttn", f2bttn);
m_refBuilder->get_widget("f1bttn", f1bttn);
m_refBuilder->get_widget("crrntfloorntr", crrntfloorntr);
// Connect the appropriate callbacks... (no signals defined in the Glade file...)
f5bttn->signal_clicked().connect(sigc::mem_fun(*this, &Egui::on_f5bttn_clicked));
f4bttn->signal_clicked().connect(sigc::mem_fun(*this, &Egui::on_f4bttn_clicked));
f3bttn->signal_clicked().connect(sigc::mem_fun(*this, &Egui::on_f3bttn_clicked));
f2bttn->signal_clicked().connect(sigc::mem_fun(*this, &Egui::on_f2bttn_clicked));
f1bttn->signal_clicked().connect(sigc::mem_fun(*this, &Egui::on_f1bttn_clicked));
}
// Signal handler methods:
void Egui::on_f1bttn_clicked () {
std::cout << "Mmmmm.....1......" <<std::endl;
}
void Egui::on_f2bttn_clicked () {
std::cout << "Mmmmm.....2......" <<std::endl;
}
void Egui::on_f3bttn_clicked () {
std::cout << "Mmmmm.....3......" <<std::endl;
}
void Egui::on_f4bttn_clicked () {
std::cout << "Mmmmm.....4......" <<std::endl;
}
void Egui::on_f5bttn_clicked () {
std::cout << "Mmmmm.....5......" <<std::endl;
}
// Set the current floor in the text entry field:
void Egui::setCrrntFloorEnt (int floor) {
std::ostringstream ostr;
ostr << floor;
ustring f = ostr.str();
crrntfloorntr->set_text(f);
}
Egui.h:
Code:
#ifndef EGUI_WINDOW_H
#define EGUI_WINDOW_H
#include <gtkmm.h>
class Egui:public Gtk::Window {
public:
virtual ~Egui ();
Egui (BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& m_refBuilder);
void setCrrntFloorEnt (int);
void testMethod ();
//protected:
Gtk::Window *mwindow;
Gtk::Button *f1bttn,
*f2bttn,
*f3bttn,
*f4bttn,
*f5bttn;
Gtk::Entry *crrntfloorntr;
void on_f1bttn_clicked ();
void on_f2bttn_clicked ();
void on_f3bttn_clicked ();
void on_f4bttn_clicked ();
void on_f5bttn_clicked ();
};
#endif // EGUI_WINDOW_H
Elevator.cpp:
Code:
#include "Elevator.h"
#include <iostream>
using namespace std;
Elevator::Elevator () {
currentfloor = 1;
queue[0] = 2; queue[1] = 5; queue[2] = 3; queue[3] = 4; queue[4] = 2;
}
Elevator::~Elevator () {}
int Elevator::getCurrentFloor () {
return currentfloor;
}
void Elevator::setCurrentFloor (int floor) {
currentfloor = floor;
}
void Elevator::moveElevator () {
setCurrentFloor(queue[0]);
sleep(2);
setCurrentFloor(queue[1]);
cout << "Done." <<endl;*/
}
Elevator.h:
Code:
#ifndef ELEVATOR_H
#define ELEVATOR_H
class Elevator {
public:
Elevator ();
virtual ~Elevator ();
int getCurrentFloor ();
void setCurrentFloor (int);
void moveElevator ();
protected:
int currentfloor;
int queue[5];
};
#endif // ELEVATOR_H
And so on. This is of course just simple test code/functions, created to find out how to tie things together.
This is obviously not working, I'm thinking wrong, and I need professional help.
As I mention in the comments, I cannot have the code that runs the elevator, ie. presses a floor button and sends the floor to the elevator, then tells it to run (or just continue running while blah-blah), in the main() function (driver.cpp). At least, not how the code is structured at the moment.
Also, I cannot have it in the GUI class (Egui), since everything put inside the constructor of that class will run before the main() function (in driver.cpp) runs/starts the GUI.
So, my question here is: How to I structure this? What is the correct architecture here? What should I do (possibly fundamentally) different here?