Implement the GUI
authorIra W. Snyder <devel@irasnyder.com>
Sun, 7 Oct 2007 03:46:42 +0000 (20:46 -0700)
committerIra W. Snyder <devel@irasnyder.com>
Sun, 7 Oct 2007 03:46:42 +0000 (20:46 -0700)
This implements the GUI in it's entirety.

Signed-off-by: Ira W. Snyder <devel@irasnyder.com>
14 files changed:
Makefile
callbutton.cpp [new file with mode: 0644]
callbutton.hpp [new file with mode: 0644]
eclose.png [new file with mode: 0644]
elevatordoor.cpp [new file with mode: 0644]
elevatordoor.hpp [new file with mode: 0644]
elevatorgui.cpp [new file with mode: 0644]
elevatorgui.hpp [new file with mode: 0644]
eopen2.png [new file with mode: 0644]
positionlabel.cpp [new file with mode: 0644]
positionlabel.hpp [new file with mode: 0644]
requestbutton.cpp [new file with mode: 0644]
requestbutton.hpp [new file with mode: 0644]
test.cpp

index 116c3ce..acc9a11 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,14 @@
-test: position.o stop.o elevator.o elevatorcontroller.o test.o
-       g++ -o $@ $^
+CC=g++
+CFLAGS=`pkg-config gtkmm-2.4 --cflags`
+LIBS=`pkg-config gtkmm-2.4 --libs`
+
+SRCS=position.cpp stop.cpp elevator.cpp elevatorcontroller.cpp elevatordoor.cpp callbutton.cpp positionlabel.cpp requestbutton.cpp elevatorgui.cpp test.cpp
+
+test: $(SRCS:.cpp=.o)
+       $(CC) -o $@ $^ $(LIBS)
+
+.cpp.o:
+       $(CC) $(CFLAGS) -c -o $@ $<
 
 run: test
        ./test
diff --git a/callbutton.cpp b/callbutton.cpp
new file mode 100644 (file)
index 0000000..5139130
--- /dev/null
@@ -0,0 +1,21 @@
+#include "callbutton.hpp"
+
+CallButton::CallButton (int floor, Direction direction)
+       : Gtk::ToggleButton()
+       , floor_(floor)
+       , direction_(direction)
+{
+       // Intentionally Left Empty
+}
+
+int CallButton::getFloorNumber () const
+{
+       return floor_;
+}
+
+Direction CallButton::getDirection () const
+{
+       return direction_;
+}
+
+/* vim: set ts=4 sts=4 sw=4 noet tw=112: */
diff --git a/callbutton.hpp b/callbutton.hpp
new file mode 100644 (file)
index 0000000..262c5ff
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef CALLBUTTON_HPP
+#define CALLBUTTON_HPP
+
+#include "direction.hpp"
+#include <gtkmm.h>
+
+class CallButton : public Gtk::ToggleButton
+{
+       public:
+               CallButton (int floor, Direction direction);
+
+               int getFloorNumber () const;
+               Direction getDirection () const;
+
+       private:
+               int floor_;
+               Direction direction_;
+};
+
+#endif /* CALLBUTTON_HPP */
+
+/* vim: set ts=4 sts=4 sw=4 noet tw=112: */
diff --git a/eclose.png b/eclose.png
new file mode 100644 (file)
index 0000000..95047b7
Binary files /dev/null and b/eclose.png differ
diff --git a/elevatordoor.cpp b/elevatordoor.cpp
new file mode 100644 (file)
index 0000000..5edbd4f
--- /dev/null
@@ -0,0 +1,31 @@
+#include "elevatordoor.hpp"
+
+ElevatorDoor::ElevatorDoor (int elevator, int floor)
+       : Gtk::Image (CLOSED_IMAGE)
+       , elevator_(elevator)
+       , floor_(floor)
+{
+       // Intentionally Left Empty
+}
+
+int ElevatorDoor::getElevatorNumber () const
+{
+       return elevator_;
+}
+
+int ElevatorDoor::getFloorNumber () const
+{
+       return floor_;
+}
+
+void ElevatorDoor::open ()
+{
+       Gtk::Image::set (OPENED_IMAGE);
+}
+
+void ElevatorDoor::close ()
+{
+       Gtk::Image::set (CLOSED_IMAGE);
+}
+
+/* vim: set ts=4 sts=4 sw=4 noet tw=112: */
diff --git a/elevatordoor.hpp b/elevatordoor.hpp
new file mode 100644 (file)
index 0000000..49c9a11
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef ELEVATORDOOR_HPP
+#define ELEVATORDOOR_HPP
+
+#include <string>
+#include <gtkmm.h>
+
+
+const std::string OPENED_IMAGE = "eopen2.png";
+const std::string CLOSED_IMAGE = "eclose.png";
+
+
+class ElevatorDoor : public Gtk::Image
+{
+       public:
+               ElevatorDoor (int elevator, int floor);
+
+               int getElevatorNumber() const;
+               int getFloorNumber() const;
+
+               void open ();
+               void close ();
+
+       private:
+               int elevator_;
+               int floor_;
+};
+
+#endif /* ELEVATORDOOR_HPP */
+
+/* vim: set ts=4 sts=4 sw=4 noet tw=112: */
diff --git a/elevatorgui.cpp b/elevatorgui.cpp
new file mode 100644 (file)
index 0000000..f9d03a1
--- /dev/null
@@ -0,0 +1,295 @@
+#include "elevatorgui.hpp"
+
+ElevatorGUI::ElevatorGUI (int floors, int elevators)
+       : Gtk::Window()
+       , number_of_floors_(floors)
+       , number_of_elevators_(elevators)
+       , simulation_status_(STOPPED)
+
+       /* The ElevatorController */
+       , ec_(floors, elevators)
+
+       /* The Timer */
+       , timer_()
+
+       /* GUI Elements */
+       , table_(floors+1, elevators+3)
+       , button_playpause_(Gtk::Stock::MEDIA_PLAY)
+       , button_stop_(Gtk::Stock::STOP)
+       , button_quit_(Gtk::Stock::QUIT)
+
+       /* Storage for GUI elements for later lookup */
+       , call_buttons_()
+       , position_labels_()
+       , request_buttons_()
+       , elevator_doors_()
+{
+       int i, j, e, f, e_num, f_num, f_attach;
+       std::ostringstream str;
+
+       /* Fill in all of the ElevatorDoors and CallButtons */
+       for (f_attach=0, f=floors-1; f>=0; --f, ++f_attach)
+       {
+               std::cout << "at floor: " << f << std::endl;
+
+               /* Create and attach the VBox */
+               Gtk::VBox *box = new Gtk::VBox ();
+               table_.attach (*box, 0, 1, f_attach, f_attach+1);
+
+               /* Only create UP CallButton if we are not on the top floor */
+               if (f != floors-1)
+               {
+                       CallButton *callbutton = new CallButton (f, UP);
+                       callbutton->set_label ("Up");
+
+                       // Connect to the on_call_button_toggled() signal
+                       callbutton->signal_toggled().connect (
+                                       sigc::bind <CallButton*> (
+                                               sigc::mem_fun (*this, &ElevatorGUI::on_call_button_toggled),
+                                               callbutton
+                                       )
+                       );
+
+                       call_buttons_.push_back (callbutton);
+                       box->pack_start (*callbutton);
+               }
+
+               /* Only create the DOWN CallButton if we are not on the bottom floor */
+               if (f != 0)
+               {
+                       CallButton *callbutton = new CallButton (f, DOWN);
+                       callbutton->set_label ("Down");
+
+                       // Connect to the on_call_button_toggled() signal
+                       callbutton->signal_toggled().connect (
+                                       sigc::bind <CallButton*> (
+                                               sigc::mem_fun (*this, &ElevatorGUI::on_call_button_toggled),
+                                               callbutton
+                                       )
+                       );
+
+                       call_buttons_.push_back (callbutton);
+                       box->pack_end (*callbutton);
+               }
+
+               for (e=0; e<elevators; ++e) // run left-to-right
+               {
+                       std::cout << "Attaching ElevatorDoor (e=" << e << ", f=" << f << ")" << std::endl;
+                       ElevatorDoor *door = new ElevatorDoor (e, f);
+                       elevator_doors_.push_back (door);
+                       table_.attach (*door, e+1, e+2, f_attach, f_attach+1);
+               }
+       }
+
+
+
+       /* Fill in all of the Elevator Request Panels */
+       for (e=0; e<elevators; ++e)
+       {
+               // Create a 2-column table with enough slots for each Floor
+               Gtk::Table *panel = new Gtk::Table ((floors+1)/2, 2);
+
+               for (f=0; f<floors; ++f)
+               {
+                       f_attach = f / 2;
+                       // Create the label
+                       str.str ("");
+                       str << f;
+
+                       // Create the button
+                       RequestButton *button = new RequestButton (e, f);
+                       button->set_label (str.str());
+
+                       // Connect the on_request_button_toggled() signal
+                       button->signal_toggled().connect (
+                                       sigc::bind <RequestButton*> (
+                                               sigc::mem_fun (*this, &ElevatorGUI::on_request_button_toggled),
+                                               button
+                                       )
+                       );
+
+                       // save the button
+                       request_buttons_.push_back (button);
+
+                       // If floor is odd, attach in the left col
+                       // Otherwise, attach in the right col
+                       if (f % 2 == 0)
+                               panel->attach (*button, 0, 1, f_attach, f_attach+1);
+                       else
+                               panel->attach (*button, 1, 2, f_attach, f_attach+1);
+               }
+
+               // Attach the Panel
+               table_.attach (*panel, e+1, e+2, floors, floors+1);
+       }
+
+
+
+       /* Fill in all of the PositionLabels */
+       for (e=0; e<elevators; ++e)
+       {
+               PositionLabel *label = new PositionLabel (e);
+               position_labels_.push_back (label);
+               table_.attach (*label, e+1, e+2, floors+1, floors+2);
+       }
+
+
+       /* Fill in the control buttons */
+       Gtk::HBox *box = new Gtk::HBox ();
+
+       button_quit_.signal_clicked().connect (
+                       sigc::mem_fun (*this, &ElevatorGUI::on_quit_button_clicked));
+       button_stop_.signal_clicked().connect (
+                       sigc::mem_fun (*this, &ElevatorGUI::on_stop_button_clicked));
+       button_playpause_.signal_clicked().connect (
+                       sigc::mem_fun (*this, &ElevatorGUI::on_playpause_button_clicked));
+
+       box->pack_start (button_playpause_);
+       box->pack_start (button_stop_);
+       box->pack_start (button_quit_);
+
+       /* Attach the box to the bottom of the GUI */
+       table_.attach (*box, 0, elevators+1, floors+2, floors+3);
+
+       /* Add the table to the window */
+       add (table_);
+
+       /* Show everything, we're up and running! */
+       show_all_children ();
+       show ();
+}
+
+void ElevatorGUI::on_quit_button_clicked ()
+{
+       Gtk::Main::quit ();
+}
+
+void ElevatorGUI::on_playpause_button_clicked ()
+{
+       std::string names[] = { "STOPPED", "RUNNING", "PAUSED" };
+       std::cout << "Play/Pause pressed with status=" << names[simulation_status_] << std::endl;
+
+       switch (simulation_status_)
+       {
+               case STOPPED:
+                       simulation_status_ = RUNNING;
+
+                       // add and start timer
+                       timer_ = Glib::signal_timeout().connect (
+                                               sigc::mem_fun (*this, &ElevatorGUI::on_timer_tick),
+                                               timer_tick_ms_);
+
+                       break;
+               case RUNNING:
+                       simulation_status_= PAUSED;
+
+                       // stop and remove timer
+                       timer_.disconnect ();
+
+                       break;
+               case PAUSED:
+                       simulation_status_ = RUNNING;
+
+                       // add and start timer
+                       timer_ = Glib::signal_timeout().connect (
+                                               sigc::mem_fun (*this, &ElevatorGUI::on_timer_tick),
+                                               timer_tick_ms_);
+
+                       break;
+               default:
+                       std::cout << "Bad Simulation Status in Play/Pause" << std::endl;
+                       break;
+       }
+}
+
+void ElevatorGUI::on_stop_button_clicked ()
+{
+       // FIXME: implement this
+       std::cout << "STOP Button Clicked" << std::endl;
+
+       simulation_status_ = STOPPED;
+}
+
+void ElevatorGUI::on_request_button_toggled (RequestButton *button)
+{
+       // Only send an elevator if we are toggled to on
+       if (button->get_active())
+       {
+               std::cout << "Request elevator=" << button->getElevatorNumber()
+                                 << " to floor=" << button->getFloorNumber() << std::endl;
+               ec_.elevator_request (button->getElevatorNumber(), button->getFloorNumber());
+       }
+}
+
+void ElevatorGUI::on_call_button_toggled (CallButton *button)
+{
+       // Only send an elevator if we are toggled to on
+       if (button->get_active())
+       {
+               std::cout << "Elevator Called on floor=" << button->getFloorNumber()
+                                 << " in direction=" << button->getDirection() << std::endl;
+               ec_.call_elevator_to (button->getFloorNumber(), button->getDirection());
+       }
+}
+
+void ElevatorGUI::gui_update_position_label (int elevator, float new_position)
+{
+       std::ostringstream str;
+
+       // Generate the text
+       str << std::setiosflags (std::ios_base::showpoint | std::ios_base::fixed)
+               << std::setprecision(1) << new_position;
+
+       // Find the correct label and set it
+       PositionLabelVector::iterator it;
+
+       for (it=position_labels_.begin(); it!=position_labels_.end(); it++)
+               if ((*it)->getElevatorNumber() == elevator)
+                       (*it)->set_text (str.str());
+}
+
+void ElevatorGUI::gui_unpress_call_button (int floor, Direction direction)
+{
+       CallButtonVector::iterator it;
+
+       for (it=call_buttons_.begin(); it!=call_buttons_.end(); it++)
+               if ((*it)->getFloorNumber() == floor && (*it)->getDirection() == direction)
+                       (*it)->set_active (false);
+}
+
+void ElevatorGUI::gui_unpress_request_button (int elevator, int floor)
+{
+       RequestButtonVector::iterator it;
+
+       for (it=request_buttons_.begin(); it!=request_buttons_.end(); it++)
+               if ((*it)->getElevatorNumber() == elevator && (*it)->getFloorNumber() == floor)
+                       (*it)->set_active (false);
+}
+
+void ElevatorGUI::gui_open_door (int elevator, int floor)
+{
+       ElevatorDoorVector::iterator it;
+
+       for (it=elevator_doors_.begin(); it!=elevator_doors_.end(); it++)
+               if ((*it)->getElevatorNumber() == elevator && (*it)->getFloorNumber() == floor)
+                       (*it)->open();
+}
+
+void ElevatorGUI::gui_close_door (int elevator, int floor)
+{
+       ElevatorDoorVector::iterator it;
+
+       for (it=elevator_doors_.begin(); it!=elevator_doors_.end(); it++)
+               if ((*it)->getElevatorNumber() == elevator && (*it)->getFloorNumber() == floor)
+                       (*it)->close ();
+}
+
+bool ElevatorGUI::on_timer_tick ()
+{
+       ec_.move_elevators ();
+
+       // Keep going (do NOT disconnect yet)
+       return true;
+}
+
+/* vim: set ts=4 sts=4 sw=4 noet tw=112: */
diff --git a/elevatorgui.hpp b/elevatorgui.hpp
new file mode 100644 (file)
index 0000000..77f4d58
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef ELEVATORGUI_HPP
+#define ELEVATORGUI_HPP
+
+#include <gtkmm.h>
+#include <iostream>
+#include <iomanip>
+#include <vector>
+#include <sstream>
+
+#include "elevatorcontroller.hpp"
+
+#include "elevatordoor.hpp"
+#include "callbutton.hpp"
+#include "positionlabel.hpp"
+#include "requestbutton.hpp"
+
+
+typedef std::vector<CallButton*> CallButtonVector;
+typedef std::vector<PositionLabel*> PositionLabelVector;
+typedef std::vector<RequestButton*> RequestButtonVector;
+typedef std::vector<ElevatorDoor*> ElevatorDoorVector;
+
+
+class ElevatorGUI : public Gtk::Window
+{
+       public:
+               ElevatorGUI (int floors, int elevators);
+
+               /* Functions to be called from Elevator to change GUI status */
+               void gui_update_position_label (int elevator, float new_position);
+               void gui_unpress_call_button (int floor, Direction direction);
+               void gui_unpress_request_button (int elevator, int floor);
+               void gui_open_door (int elevator, int floor);
+               void gui_close_door (int elevator, int floor);
+
+       private:
+               /* Callbacks from button presses */
+               void on_call_button_toggled (CallButton *button);
+               void on_request_button_toggled (RequestButton *button);
+               void on_playpause_button_clicked ();
+               void on_stop_button_clicked ();
+               void on_quit_button_clicked ();
+
+               /* Timer Function */
+               bool on_timer_tick ();
+               sigc::connection timer_;
+               static const int timer_tick_ms_ = 500;
+
+               int number_of_floors_;
+               int number_of_elevators_;
+
+               enum { STOPPED, RUNNING, PAUSED } simulation_status_;
+               
+               ElevatorController ec_;
+
+               // holds custom CallButton which knows it's direction and floor#
+               CallButtonVector call_buttons_;
+
+               // holds custom position label which knows it's elevator#
+               PositionLabelVector position_labels_;
+
+               // holds custom RequestButton which knows it's elevator# and floor#
+               RequestButtonVector request_buttons_;
+
+               // holds custom ElevatorDoor which knows it's elevator# and floor#
+               ElevatorDoorVector elevator_doors_;
+
+               // Holds the Play / Pause button, Stop button, and Quit button
+               Gtk::Button button_playpause_, button_stop_, button_quit_;
+
+               // Holds the Table which holds everything
+               Gtk::Table table_;
+};
+
+#endif /* ELEVATORGUI_HPP */
+
+/* vim: set ts=4 sts=4 sw=4 noet tw=112: */
diff --git a/eopen2.png b/eopen2.png
new file mode 100644 (file)
index 0000000..4521d83
Binary files /dev/null and b/eopen2.png differ
diff --git a/positionlabel.cpp b/positionlabel.cpp
new file mode 100644 (file)
index 0000000..bdbe24d
--- /dev/null
@@ -0,0 +1,15 @@
+#include "positionlabel.hpp"
+
+PositionLabel::PositionLabel (int elevator, const std::string text)
+       : Gtk::Label (text)
+       , elevator_(elevator)
+{
+       // Intentionally Left Empty
+}
+
+int PositionLabel::getElevatorNumber () const
+{
+       return elevator_;
+}
+
+/* vim: set ts=4 sts=4 sw=4 noet tw=112: */
diff --git a/positionlabel.hpp b/positionlabel.hpp
new file mode 100644 (file)
index 0000000..bd90449
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef POSITIONLABEL_HPP
+#define POSITIONLABEL_HPP
+
+#include <gtkmm.h>
+#include <string>
+
+class PositionLabel : public Gtk::Label
+{
+       public:
+               PositionLabel (int elevator, const std::string text="0.0");
+
+               int getElevatorNumber() const;
+
+       private:
+               int elevator_;
+};
+
+#endif /* POSITIONLABEL_HPP */
+
+/* vim: set ts=4 sts=4 sw=4 noet tw=112: */
diff --git a/requestbutton.cpp b/requestbutton.cpp
new file mode 100644 (file)
index 0000000..95fae24
--- /dev/null
@@ -0,0 +1,21 @@
+#include "requestbutton.hpp"
+
+RequestButton::RequestButton (int elevator, int floor, const std::string text)
+       : Gtk::ToggleButton (text)
+       , elevator_(elevator)
+       , floor_(floor)
+{
+       // Intentionally Left Empty
+}
+
+int RequestButton::getElevatorNumber () const
+{
+       return elevator_;
+}
+
+int RequestButton::getFloorNumber () const
+{
+       return floor_;
+}
+
+/* vim: set ts=4 sts=4 sw=4 noet tw=112: */
diff --git a/requestbutton.hpp b/requestbutton.hpp
new file mode 100644 (file)
index 0000000..8c14c31
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef REQUESTBUTTON_HPP
+#define REQUESTBUTTON_HPP
+
+#include <gtkmm.h>
+#include <string>
+
+class RequestButton : public Gtk::ToggleButton
+{
+       public:
+               RequestButton (int elevator, int floor, const std::string text="0");
+
+               int getElevatorNumber () const;
+               int getFloorNumber() const;
+
+       private:
+               int elevator_;
+               int floor_;
+};
+
+#endif /* REQUESTBUTTON_HPP */
+
+/* vim: set ts=4 sts=4 sw=4 noet tw=112: */
index 6f85ea1..bcfe3b3 100644 (file)
--- a/test.cpp
+++ b/test.cpp
@@ -7,10 +7,27 @@ using namespace std;
 #include "position.hpp"
 #include "elevator.hpp"
 #include "elevatorcontroller.hpp"
+#include "elevatorgui.hpp"
+
+#include <gtkmm/main.h>
 
 
 int main (int argc, char *argv[])
 {
+       const int floors = 7;
+       const int elevators = 3;
+
+       // Start GTK
+       Gtk::Main app(argc, argv);
+
+       ElevatorGUI eg(floors, elevators);
+       eg.show ();
+
+       Gtk::Main::run (eg);
+
+
+
+#if TEST_ELEVATORCONTROLLER
        const int floors = 10;
        const int elevators = 2;
 
@@ -30,6 +47,7 @@ int main (int argc, char *argv[])
 
        for (int i=0; i<35; i++)
                ec.move_elevators ();
+#endif
 
 
 #if TEST_ELEVATOR