I don't often have to create GUI's but today I do so I was hoping for some design input.
Basically I have a backend which I intend to add a GUI too using the MVC pattern. The issue is I feel whatever class encapsulates the main GUI window is going to have A LOT of state (all of the sub elements); and on top of that it's going to have a lot of setters, and possibly getter, clear, colour, size, position and refresh functions too.
One option is to march ahead with this idea and have a very large public interface which deals with the types the GUI uses (std::string, std::vector<std::string>...) the more control I want over the UI the more public member function I am going to need.
The other option would be to pass the program state to the GUI and have it decide how it display it, I fear doing this would mean it would give me less fine detail control and would break down the separation of concerns and would mean any changes to the representation of the program state would require changes in the GUI too.
Any input on the matter would be of great help.
If it makes any difference this is a C++ gui using an ncurses abstraction.
It sounds like to me you've thought alot about the M and the V, but not much about the C. The pattern should really be called MCV because the whole idea is that the controller IS the bridge between your model (data) and view (GUI). It sounds like you need a controller with most of the functionality you've mentioned.
Simply put though, your model obviously should know nothing about display and your display (view) should not know how to access the model. You need a controller that reads the data (model) and gives instructions to the display (view). If you have user interaction within the view, the controller can interpret that and modify the model as necessary.
The idea is that you never have to change all 3, but if you change the model or the view, you almost always have to update the controller.
Hope that helps...
There is at least one alternative to the giant interface. Instead of having a function that handles each thing (size, font, color, what-to-display, etc...) have a singular function that accepts a "role" and data that represents the role. This requires some sort of wrapper that can contain multiple data types.
QT's QAbstractItemModel Class Reference has a good example:
QVariant QAbstractItemModel::data ( const QModelIndex & index, int
role = Qt::DisplayRole ) const [pure virtual]
What that function will do is return the QVariant that represents the role indicated at the index provided.
The downside of this approach, is you have to know what roles exist, and what they do. QT's default roles are shown here.
I like to have parts of the model able to instrument themselves:
class Model {
private:
int value;
public:
void instrument(Instrumenter& instrumenter);
};
The Instrumenter manages the creation of controls. The model will tell it how it can be controlled and give it access to the data.
void Model::instrument(Instrumenter& instrumenter) {
instrumenter.addRangeController(0, 100, 5, value);
}
Then for different input devices (e.g keyboard, touchscreen) you can create appropriate controls:
class KeyboardInstrumenter : public Instrumenter {
public:
void addRangeController(int min, int max, int increments, int& controlled) {
// create 3 widgets, up arrow, down arrow, and value
}
};
class TouchscreenInstrumenter : public Instrumenter {
public:
void addRangeController(int min, int max, int increments, int& controlled) {
// create slider with min, max and increments
}
};
Instead of passing in the int directly we could have wrapped it in a controlling object, to aid encapsulation.
Related
I am writing GUI applicaton in Qt. Currently I waste too much time on routine. It seems something wrong with my architecture. Please tell me how can I change my approach to improve code.
What I am doing:
My program can be decomposed as hierarchy of classes (not inheritance but composition). In example:
class D { /* something */ };
class C { /* something */ };
class B { C c1; C c2; D d1; };
class A { D d1; C c1; };
So, actually it is a tree hierarchy where leaf nodes (class C, class D) are "models" in Qt terminology which hold data. At the top of hierarchy MainWindow (class A) is placed, which holds first level of "views" (class D, i.e. subwidget) and leaf node with data (class C, i.e. text field).
To pass information down from main window to data I use function calls from mainwindow (pushbuttons) to leaf nodes. After that data changes and tells parents about it with signal slot mechanism. Parents continue to pass message up with signaling.
I am really bored by establishing all these communication. Now I have up to 5 levels, however it is not that much in usual code when using composition. Please tell me how can I change my approach. Due to complexity of these connections, development of the code extremely slow, almost stopped.
It is hard to give a concrete example, because there are a lot of code, but the idea of problem which is very difficult to solve is following:
There are two QTreeView, which differently shows data from own model inherited from QAbstractItemModel (tree inside model is not that tree in previous discussion, this tree is only single level of hierarchy). I want to select objects in one QTreeView and change by that selection in second QTreeView. There are total 2 QTreeView, 2 different QAbstractItemModel instances, 2 trees of own objects (for each QAbstractItemModel), and single data.
Sounds like you might have become a victim of going through too many examples. Examples tend to cram functionality where it doesn't belong, creating the possibility to develop bad programming habits.
In actual production things need to be more compartmentalized. The main window should not be the container of the "application logic", all it needs to concern itself with is holding together the main widgets.
But that doesn't seem to be your case, judging by the necessity to delegate things "from mainwindow (pushbuttons) to leaf nodes" as you put it.
On a grander scale, it is not advisable to mix application logic with UI at all, much less cram it all in the main window. The application logic should be its own layer, designed so that it can work without any GUI whatsoever, and then the GUI is another layer that simply hooks up to the logic core.
The logic core should not be monolith either, it should be made of individual components focusing on their particular task.
Your use case doesn't really require any crazy amount of connections, just some basic handlers for the UI elements, which should target the logic core API rather than GUI elements as you appear to be doing now.
Your clarification unfortunately makes absolutely no sense to me, it is still completely unclear what you exactly you want to do.
Let's assume your situation is something like this:
Tree 1 shows a folder structure.
Tree 2 shows the file content of the folder, selected in tree 1.
Data is an editor for the file, assuming a text file, selected in tree 2.
So, in pseudocode, presuming that app is your application core logic object:
Clicking an item in tree 1 says app.setFolder(tree1.selectedItem())
Clicking an item in tree 2 says app.setFile(tree2.selectedItem())
Clicking the editor "save" button says app.save(editorUI.dataField.text())
logic layer gui layer
app mainWindow
folder <-----------select----------- tree1
file <-----------select----------- tree2
save(newData) { editor
if (file) file.rewrite(newData) textField
} saveBtn: app.save(textField.text())
Since there is only a single data source, you could do the following:
Create a general model for that data source. The model should represent the data source generally, without consideration of what the views need.
Create two proxy viewmodels that adapt the general model to the needs of the views.
Couple the selection models of the views that display the viewmodels.
Given the selection models on top of the two proxy models that map to the same source, we can propagate the selection change between them. We leverage the selection mapping provided by the proxy. The QAbstractProxyModel has a functional implementation of mapSelectionxxxx.
void applySel(const QItemSelectionModel *src, const QItemSelection &sel,
const QItemSelection &desel, const QItemSelectionModel *dst) {
// Disallow reentrancy on the selection models
static QHash<QObject*> busySelectionModels;
if (busySelectionModels.contains(src) || busySelectionModels.contains(dst))
return;
busySelectionModels.insert(src);
busySelectionModels.insert(dst);
// The models must be proxies
auto *srcModel = qobject_cast<QAbstractProxyItemModel*>(src->model());
auto *dstModel = qobject_cast<QAbstractProxyItemModel*>(dst->model());
Q_ASSERT(srcModel && dstModel);
// The proxies must refer to the same source model
auto *srcSourceModel = srcModel->sourceModel();
auto *dstSourceModel = dstModel->sourceModel();
Q_ASSERT(srcSourceModel && (srcSourceModel == dstSourceModel));
// Convey the selection
auto const srcSel = srcModel->mapSelectionToSource(sel);
auto const srcDesel = srcModel->mapSelectionToSource(desel);
auto const dstSel = dstModel->mapSelectionFromSource(srcSel);
auto const dstDesel = dstModel->mapSelectionFromSource(srcDesel);
// we would re-enter in the select calls
dst->select(dstSel, QItemSelectionModel::Select);
dst->select(dstDesel, QItemSelectionModel::Deselect);
// Allow re-entrancy
busySelectionModels.remove(src);
busySelectionModels.remove(dst);
}
The above could be easily adapted for a list of destination item selection models, in case you had more than two views.
We can use this translation to couple the selection models of the views:
void coupleSelections(QAbstractItemView *view1, QAbstractItemView *view2) {
auto *sel1 = view1->selectionModel();
auto *sel2 = view2->selectionModel();
Q_ASSERT(sel1 && sel2);
connect(sel1, &QItemSelectionModel::selectionChanged,
[=](const QItemSelection &sel, const QItemSelection &desel){
applySel(sel1, sel, desel, sel2);
});
connect(sel2, &QItemSelectionModel::selectionChanged,
[=](const QItemSelection &sel, const QItemSelection &desel){
applySel(sel2, sel, desel, sel1);
});
}
The above is untested and written from memory, but hopefully will work without much ado.
Say I have two main classes, Application and ApplicationGUI. Application does lots of things and can happily run without any knowledge that ApplicationGUI exists. ApplicationGUI is linked to Application in many ways, it has maybe 50 or 100 different knobs that can change Application's behavior.
ApplicationGUI is a hierarchical structure such that it has many instances of ControlGroup, each containing an arbitrary number of Buttons and Knobs, or even another ControlGroup.
Current design: Upon instantiation of the ApplicationGUI (Application was already running with some set of default parameters), I pass pointers of Application's parameters to various components of the GUI. For example:
my_gui.sound_controlgroup.knob.link_to_param(&(my_application.volume));
If I need to do something more complex, say call a member function of Application, my_application.update_something(), how is this done?
The easy answer is to pass a pointer to my_application to my_gui.sound_controlgroup.knob, but if I only ever need to call one of my_application's functions, it seems like I am giving my knob an option to change all kinds of things that it should even know about (my_application.update_something_unrelated(), for instance). What is the cleanest thing to do in this case?
Additionally, this either requires making all subcomponents of ApplicationGUI public or having a function at each stage of the hierarchy to forward that pointer to the bottom level. This leads to quite a lot of functions. Is this a necessary consequence of a UI with a lot of knobs?
Quick Short Answer
In order to implement interaction between your non GUI related Application object and your GUIApplication object I suggest apply the "Property and Method and Event Handler" paradigm.
Extended Complex Answer
G.U.I. development is one of the most practical implementation of the O.O.P. theory.
What is the "Property and Method and Event Handler" paradigm ?
That means build, both Non GUI Classes, and GUI Classes, should have:
Properties
Methods
Event handlers
"Events" (Handlers) are also called "Signals", and are implemented with functions pointers. Not sure, but, I think your "knob" (s) are like Event Handlers.
It's a technique to apply the my_application.update_something_unrelated(), you have in your question.
Since, C++, like Java, does not have property syntax, you may use "getter" and "setter" methods, or use a "property" template.
For example, if your application has a Close method, you may declare something like the following examples.
Note: They are not full programs, just an idea:
// Applications.hpp
public class BaseApplicationClass
{
// ...
};
public class BaseApplicationClientClass
{
// ...
};
typedef
void (BaseApplicationClientClass::*CloseFunctor)
(BaseApplicationClass App);
public class ApplicationClass: public BaseApplicationClass
{
// ...
public:
Vector<BaseApplicationClientClass::CloseFunctor>
BeforeCloseEventHandlers;
Vector<BaseApplicationClientClass::CloseFunctor>
AfterCloseEventHandlers;
protected:
void ConfirmedClose();
public:
virtual void Close();
} Application;
// Applications.cpp
void ApplicationClass::ConfirmedClose()
{
// do close app. without releasing from memory yet.
} // void ApplicationClass::ConfirmedClose()
void ApplicationClass::Close()
{
// Execute all handlers in "BeforeCloseEventaHandlers"
this.ConfirmedClose();
// Execute all handlers in "AfterCloseEventaHandlers"
} // void ApplicationClass::Close()
// AppShells.cpp
public class AppShell: public BaseApplicationClientClass
{
// ...
};
void AppShell::CloseHandler(ApplicationClass App)
{
// close GUI
} // void AppShell.CloseHandler(ApplicationClass App)
void AppShell::setApp(ApplicationClass App)
{
App->BeforeCloseEventHandlers->add(&this.CloseHandler);
} // void AppShell.setApp(ApplicationClass App)
void main (...)
{
ApplicationClass* AppKernel = new ApplicationClass();
ApplicationGUIClass* AppShell = new ApplicationGUIClass();
AppShell.setApp(App);
// this executes "App->Run();"
AppShell->Run();
free AppShell();
free AppKernel();
}
UPDATE: Fixed type declaration from global function pointer (a.k.a. "global functor") to object function pointer (a.k.a. "method functor").
Cheers.
Do you know about the model-view-controller (MVC) paradigm? Think of the Application class as the model, the entire hierarchy of GUI controls as the view, and the ApplicationGUI class as the controller. You don't want Application to know about the controls, and you don't want the controls to know about Application; they should both talk only to the controller, ApplicationGUI.
Using ApplicationGUI as the conduit for communication between controls and Application means that you can test either Application or controls by replacing the other with a mock object, for example. More importantly, you can change either the controls or Application without impacting the other. Individual controls don't need to know anything about Application -- they only need to know where to send their value when it changes. And Application shouldn't care whether an input comes from a knob or a slider or a text field. Keeping those two areas separate will simplify each of them.
Additionally, this either requires making all subcomponents of
ApplicationGUI public or having a function at each stage of the
hierarchy to forward that pointer to the bottom level. This leads to
quite a lot of functions. Is this a necessary consequence of a UI with
a lot of knobs?
A given control shouldn't care what value it manages. It doesn't need to know whether the value determines the number of alien invaders on the screen or the coolant level in a nuclear reactor. It does needs to know things like the minimum and maximum values, label to display, scale to use (linear, log, etc.), and other things that directly impact the way the control works. It also needs to know who to tell when something changes, and it might need some way to identify itself.
With that in mind, ApplicationGUI doesn't need to expose accessors for every possible parameter of Application. Instead, it should have a general method that lets controls send it updates. When a control changes, it should send a message to ApplicationGUI containing the new value(s) along with its identifier, and ApplicationGUI takes care of mapping that identifier to some particular parameter of Application. A control's identifier could be some identifying number that's given to it, or it could just be a pointer to the control.
Of course, sometimes communication has to go the other way, too... a GUI usually has both inputs and outputs, so you'll want some means for ApplicationGUI to get updates from Application and update the state of the GUI. For the same reasons described above, Application should send those updates to ApplicationGUI and let the latter find the actual UI components that need to be changed.
I wasn't really sure how to search for this question.
I'm doing an embedded system design with the following scenario.
I have a main application class that needs to create a bunch of hardware interfaces such as a keypad, display, communication ports, etc... a whole slew of stuff
Now I have all these objets in the main application that I can use which is great
The application class contains a few sub classes that it can go into and stay for a while. One example is a menu class that it enters and runs inside that class until the menu is closed
I need the menu class to also interact with a lot of a hardware objects that were created at the application level
What is the best way to go about this without using global variables? Is there a good solution to this problem?
I could pass each object into the menu class, but I don't want to create a constructor with 20 arguments. My current solution is to put all the objects into a structure and pass that structure into the sub-class constructor. That way they also have access.
The part that bugs me about this approach is that I have to define the structure outside of the application which I don't really like. Something just keeps telling me it's not the best solution.
Open to any suggestions.
Presumably, there is ONE keypad - thus only one "Keypad Interface Object", right? Similarly with Display [ok, there may be two displays, but still].
So my suggestion would be to have a registration and a "container" that holds the registered interfaces something like this:
class KeyPad
{
public:
int getKeyPressed();
};
class Display
{
public:
OutputText(std::string msg);
};
... bunch of other stuff ...
class HardwareRegistry
{
priviate:
Keypad *keypad;
Display *display;
static HardwareRegistry *myself;
public:
Keypad* GetKeypad() { return keypad; }
Display* GetDisplay() { return display; }
void RegisterKeypad(Keypad *akeypad) { keypad = akeypad; }
void RegisterDisplay(Display *adisplay) { display = adisplay; }
static HardwareRegistry* GetHwRegistry()
{
if (!myself) myself = new HardwareRegistry;
ASSERT(myself); // If we don't have a pointer now, panic!
return myself;
}
};
Then you just have a Singleton Pattern to provide your HardwareRegistry, and register the devices as you create them during hardware initialization.
Of course, if you support different kinds of Keypads, Displays, etc, then you would implement those with a "interface baseclass", and the registry returns the KeypadBase type, for example.
I'm writing a Qt application to allow the visualization of very heavy data sets.
So, I have a SourceDataModel class, inheriting from QAbstractItemModel that seems to work properly (currently, I only display it in QTableView/QTreeView but later on, I'll create some custom views).
Now, I would like to be able to filter this data, that is
being able to have different data
resolution (i.e. only exposing 1
data item out of 2)
being able to
apply some filters on the data (i.e.
displaying unix timestamps as
dd/MM/yyyy hh:mm:ss)
So I started to create a ProxySourceDataModel class, which inherits from my SourceDataModel and stores one instance, and basically delegates everything to the instance. From this ProxySourceDataModel, I noticed that no data was displayed when I used it in a QTableView. After some investigation, it seems that it was because i had to forward the signals and slots from the underlying SourceDataModel. No problem, i did it.
But still 2 problems remain, and I can't figure out how to handle them:
I am not able to select the data in the views. If I use the SourceDataModel directly, no problem. But using the ProxySourceDataModel i can't select anything.
The data is not filtered at all! I overloaded data() in ProxySourceDataModel, and forward all the other calls to the underlying SourceDataModel. But still, only SourceDataModel::data() is called.
Here is some code to illustrate what I'm doing:
class SourceDataModel : public QAbstractItemModel
{
//...
};
class ProxySourceDataModel : public SourceDataModel
{
public:
ProxySourceDataModel(SourceDataModel& model)
: model_(model)
{
// For all QAbstractItemModel's signals emitted by the underlying model,
// I propagate them like this
QObject::connect( &model_, SIGNAL( the_signal()),
this, SLOT (forward_the_signal())) ;
}
slots:
void forward_the_signal()
{
emit the_signal();
}
public:
// For all QAbstractItemModel's virtual function, I do something like this
virtual void the_function()
{
model_.the_function();
}
// This is where I was hoping to do the filtering
virtual QVariant data( const QModelIndex& index,int role=Qt::DisplayRole )
{
return filter( model_.data(index,role) );
}
private:
SourceDataModel& model_;
};
SourceDataModel sourceDataModel;
QTableView view;
view.setModel( new ProxySourceDataModel(sourceDataModel) );
Any help or advice greatly appreciated, thanks for reading!
-------------------- EDIT ------------------------
I found it!
The problem was that the view do not use QAbstractItemModel::data() from its model, but rather calls QModelIndex::data() on its items, which in turn calls the QAbstractItemModel::data() of the item's underlying model.
And since my proxy returned model indexes from the underlying model, that is why the SourceDataModel::data() was always called instead of ProxySourceDataModel()!
I just reimplemented ProxySourceDataModel::index() to return local indexes, and it works like a charm.
Visit In QT, chaining models does not work as expected for more informations.
Thanks!
The problem was that the view do not use QAbstractItemModel::data() from its model, but rather calls QModelIndex::data() on its items, which in turn calls the QAbstractItemModel::data() of the item's underlying model.
And since my proxy returned model indexes from the underlying model, that is why the SourceDataModel::data() was always called instead of ProxySourceDataModel()!
I just reimplemented ProxySourceDataModel::index() to return local indexes, and it works like a charm.
Visit In QT, chaining models does not work as expected for more informations.
I was studying the Model-View-Controller design pattern and i understand the concept behind the pattern theorotically, but I wanted to get a peek at how one would actually put it to practice.
Wikipedia mentions Wt - Web toolkit, CppCMS and some other standard implementations which use the pattern however I have not been familiar with these, and I was just hoping and
will be really grateful If anyone can provide some sample code(hopefully C++) which implements the pattern and explains the theory of the pattern being put to practice.
Here's a quick example I made (didn't try compiling it, let me know if there's errors):
class Button; // Prewritten GUI element
class GraphGUI {
public:
GraphGUI() {
_button = new Button("Click Me");
_model = new GraphData();
_controller = new GraphController(_model, _button);
}
~GraphGUI() {
delete _button;
delete _model;
delete _controller;
}
drawGraph() {
// Use model's data to draw the graph somehow
}
...
private:
Button* _button;
GraphData* _model;
GraphController* _controller;
};
class GraphData {
public:
GraphData() {
_number = 10;
}
void increaseNumber() {
_number += 10;
}
const int getNumber() { return _number; }
private:
int _number;
};
class GraphController {
public:
GraphController(GraphData* model, Button* button) {
__model = model;
__button = button;
__button->setClickHandler(this, &onButtonClicked);
}
void onButtonClicked() {
__model->increaseNumber();
}
private:
// Don't handle memory
GraphData* __model;
Button* __button;
};
Ignoring the implementation of Button, basically this program will use GraphGUI to display a graph that will change when a button is pressed. Let's say it's a bar graph and it will get taller.
Since the model is independent of the view (the button), and the controller handles the communication between the two, this follows the MVC pattern.
When the button is clicked, the controller modifies the model via the onButtonClicked function, which the Button class knows to call when it is clicked.
The beauty of this is since the model and view are completely independent, the implementation of each can drastically change and it won't affect the other, the controller might simply have to make a few changes. If the model in this case calculated some result based off some database data, then clicking the button could cause this to happen, but the button implementation wouldn't have to change. Or, instead of telling the controller when a click occurs, maybe it can tell the controller when the button is moused-over. The same changes are applied to model, regardless of what triggered the changes.
A simple text editor could be designed based on MVC. Think of the string class as the model, where data is stored. We might have a class called SimpleTextView which displays the text in the string attached to it, as it is. A class called KeyboardEventHandler can act as the controller. The controller will notify the view about new keyboard events. The view in turn modifies the model (like appending or removing text). The changes in the model is reflected on all views attached to it. For instance, there might be another view called HtmlView attached to the string object manipulated from within the SimpleTextView. If the user enters valid HTML tags in the SimpleTextView, the HtmlView will display the formatted output - real-time.
There are couple of complete MVC examples, plus discussion, in ch 2 of an introduction to programming in Python 3.x that I wrote (I've not completed ch 3 etc., that project's been on ice for some time -- Python community really like angry swarm of bees when discovered I'd written that Python was perhaps not suitable for very large scale development, so it became difficult to get sensible feedback). It's available in PDF format from Google Docs. I don't know how well it maps to common MVC implementations, I was mostly concerned with getting the general idea across. :-)
Cheers & hth.,
PS: There's a nice table of contents in the PDF file but Google Docs doesn't show it. You'd need to dl and use Foxit or Acrobat or some other PDF viewer. I think there's a separate viewable TOC at Google Docs, though, haven't checked and don't remember whether updated.
PPS: Forgot to mention, the MVC image processing example near the end has nice pic of Lena Söderberg! :)
Code is the best approach to understand and learn Model View Controller:
Here is a simple JS example (from Wiki)
/** Model, View, Controller */
var M = {}, V = {}, C = {};
/** Model stores data */
M.data = "hello world";
/** View controls what to present */
V.render = (M) => { alert(M.data); }
/** Controller bridges View and Model */
C.handleOnload = () => { V.render(M); }
/** Controller on Windows OnLoad event */
window.onload = C.handleOnload;
Here is a detailed post in C/C++
Model-View-Controller Explained in C++