This is a bit tricky to explain but I will try my best to explain it.
I'm making a Gui API for games and it has Themes.
First I'll explain how Widgets work.
A Button for example, inherits from Widget.
Themes work similarly.
ButtonTheme inherits from WidgetTheme.
Inside each widget class, there is an instance of its corrosponding Theme.
Widget class has:
private:
static WidgetTheme widgetTheme;
public:
static WidgetTheme& getWidgetTheme();
button class has:
private:
static ButtonTheme buttonTheme;
public:
static ButtonTheme& getButtonTheme();
the Widget constructor, builds itself from its theme ex:
Widget()
{
setFont(getWidgetTheme().getFont());
}
the Button, inheriting from WidgetTheme, has to do the same ones because the internal widget will not know to construct from ButtonTheme, so my button ends up having to do:
Button()
{
setFont(getButtonTheme().getFont());
setButtonPadding(getButtonTheme().getButtonPadding());
}
This is where my problem is. It really feels wrong that I have to reprovide all the WidgetTheme ones and redirect them to ButtonTheme's parameters for Widget. If I do not do this, a SuperButton would inherit the styles of Button which would also inherit the styles of Widget, but what I want is for SuperButton to use its version of ButtonTheme and WidgetTheme because SuperButtonTheme would inherit from ButtonTheme and WidgetTheme.
Is there a way I could redesign this so that the constructor only has to set parts of the theme that it brings, and not have to set those of its parents?
Thanks
A virtual getTheme() (as drewish suggests) but using covariant return types ought to solve your problem without requiring casts.
The Widget constructor can accept a WidgetTheme and use that.
Widget(const WidgetTheme& theme)
{
setFont(theme.getFont());
}
Button() : Widget(getButtonTheme())
{
setButtonPadding(getButtonTheme().getButtonPadding());
}
I'm not quite clear on where getButtonTheme() and getWidgetTheme() live in your object hierarchy but it seems like it should be up to the class to know what its theme is so why not have a getTheme() method on your class? Maybe I'm too used to scripting languages and not appreciating some issues with strict typing.
Related
I have a class hierarchy similar to this:
class Widget {
// a lot of virtual members
};
class Button : public Widget {
// new stuff + overrides
};
class MySuperButton : public Button {
// ...
};
And I would like to hide the fact that MySuperButton inherit from Button, but not from Widget. Basically making the inheritance from Button private while keeping all its base classes public.
Why?
I have a complicated widget build on Button, which needs to maintain some invariant with its button state. Exposing it has a Button might allow something to
modify the button directly breaking these invariants.
Example:
MySuperButton button;
button.setText("Click me!") // calls Button::setText
// Oh no, MySuperButton set some special text which has now been overriden =(
What doesn't work
Making MySuperButton inherit from Button privately also hides Widget, preventing me from doing Widget things with my button.
Using access specifiers does not prevent MySuperButton to be converted into a Button.
So void doButtonStuff(Button& b); will accept a MySuperButton& just fine.
Using compositon forces me to reimplement a bunch of stuff that Button already reinmplements, just to forward it which is a PITA. Especially since the actual hierarchy is rather deep and these are big classes.
Virtual inheritance doesn't seem to work as the base isn't visible (not sure why that would be a problem though). See Godbolt
I can not modify the Button or Widget classes as they are from an external library (Qt in this case). Also the actual code is somewhat more complicated, the provided hierarchy is for illustration.
Is there any way to do this, or do I need to accept that my widget can be broken if I am not careful ?
What you are asking is not really possible.
A possible Qt-specific solution is the following:
class MySuperButton : public Widget {
public:
MySuperButton () {
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(button = new Button());
setLayout(layout);
}
private:
Button *button;
}
I have my class, which should represent buttons (pads, if I need to be specific). Right now I am wondering if it's possible to draw them in my QMainWindow class.
In first version of app I created buttons in QtCreators designer. But right now I Would like to create them by code. As said before, already got class Pad, where I want to have position, text and other behaviors. Right now my class is pretty poor:
#include <QObject>
class Pad
{
public:
Pad(int x, int y);
private:
int m_xPosition;
int m_yPosition;
};
And here is how I create them (where m_pads is std::vector )
void PadsWindow::createPads(const int &numberOfPads)
{
enablePadsWindow();
for (int index = 0; index < numberOfPads; ++index)
{
m_Pads.push_back(new Pad(10, 100));
}
}
Exactly here I would like to draw buttons in my QMainWindow and have it like normal QPushButton.
Could you help guys ?
To use a custom class as UI widget it must inherit from QWidget, which provides the necessary interface and already a lot of implementations (which you can override to customize the behaviour).
To make a widget behave like a button it is recommandable to use QAbstractButton or one of its derivations as base class, because these already provide much of the behaviour, and customizing is easier than adding all functionality to a plain QWidget.
This class can be added as custom widget to Qt Designer, so you can use it like any original Qt widget in dialogs and layouts.
You can find a lot of tutorials and FAQs regarding custom widgets on the net and here, so getting started should be easy enough for anyone who's got a grasp of the basics of Qt and C++.
I know that cast should be avoided and I´m trying to do it, but I can´t see how to do it using a QToolBox.
The window I´m building has a QToolBox for the user chose which operation he wants to do and inside the toolbox are the specific parameters to each operation. The apply button is outside the QToolBox.
When the user clicks the apply button I have to get which operation he has chosen and its parameters.
The problem is that QToolBox currentWidget() method returns a QWidget that is a class that I can´t change. So I can´t use virtual methods or something like that. The only way I see to get the parameters is using cast.
Here is some code to show my problem:
class BaseOperation : QWidget {
public:
virtual int getParameter() = 0;
}
class Operation1 : public BaseOperation {
...
}
class Operation2 : public BaseOperation {
...
}
...
_ui->toolBox->addItem(new Operation1(this), "OP 1");
_ui->toolBox->addItem(new Operation2(this), "OP 2");
...
QWidget* curItem = _ui->toolBox->currentWidget();
BaseOperation* op = dynamic_cast<BaseOperation*>(op);
op.getParameter();
Is there a better way to do what I want? I thought of using the item index in the toolbox and a hash map to do it, but this does not seem very OOP.
You can track widget changes, map index to concrete Operation class (not using hashmap, but simple switch-case) and then static_cast<> it, but that's what dynamic cast does for you, basically.
Using dynamic cast is perfectly OK in this case, IMO.
A possible different approach that avoids casts entirely is to use setProperty("name", QVariant(value)) to associate the data you need to the widgets, and then get the data back from the current widget using property("name").toInt() - this avoids casts and defining separate classes if what you need is an integer value (or something else reasonably simple)
I have system that has classes derived from QGraphicsWidget. I manage derived class objects in layouts on QGraphicsScene. Now I need a compound item that contain two or more QGraphicsWidget in it and also I need to put that item inside my layout. So I choose QGraphicsItemGroup and write I class like this.
class CompositeItem : public QGraphicsItemGroup,public QGraphicsLayoutItem
{
...
};
I only implemented sizeHint function again.
When add CompositeItem instance to layout it does not shown.
What may cause this? Where I made wrong?
Call show() on either the QGraphicsItemGroup or QGraphicsWidgets after adding to the layout.
Add setGraphicsItem( this ) to your constructor.
I have a bunch of questions to post regarding the issue of separating the view from logic when creating a GUI.
The following is a minimal example of what I would do for a simple dialog that has a label and a button using the "Humble Dialog" approach. Pressing the button should show some text on the label. I have used C++ an Qt that I am comfortable with but I guess it is readable by all other audiences.
In any case I am interested in possible side effects because of the choice of language (I am using C++ in the project I am interested in introducing this).
class IView {
public:
IView(){}
virtual ~IView(){}
virtual void showResult(const QString &text)=0;
};
class Presenter {
public:
Presenter(IView *view){
m_View = view;
}
~Presenter(){}
void buttonPressed(){
QString text;
// Evaluate text
m_View->showResult(text);
}
private:
IView *m_View;
}
// Multiple inheritance. Is this OK?
class MyView : public QDialog, public IView {
public:
MyView(){
m_Presenter = new Presenter(this);
m_Button = new QPushbutton(this);
m_Label = new QLabel(this);
// Ui event handled inside view but then directly
// propagated to the Presenter
connect(m_Button,SIGNAL(clicked()),this,SLOT(buttonPressed()));
}
~MyView(){
delete m_Presenter;
// Qt will automatically delete m_Button and m_Label;
}
void showResult(const QString &text){
m_Label->setText(text);
}
protected slots:
void buttonPressed(){
m_Presenter->buttonPressed();
}
private:
Presenter *m_Presenter;
QPushbutton *m_Button;
QLabel *m_Label;
}
class TestView : public IView {
public:
TestView(){}
~TestView(){}
void showResult(const QString &text){
m_LabelText = text;
}
QString getResult(){
return m_LabelText;
}
private:
QString m_LabelText;
}
// Test code
TestView view;
Presenter presenter(&view);
presenter.buttonPressed();
EXPECT_EQ(view.getResult(),"Expected Result");
// Procuction code
MyView view;
view.show();
Now this is what I got by following the initial work on the Humble dialog by Feathers. The approach that I would get from Fowler's implentation would be to avoid creating the instance of the Presenter class in the constructor of MyView but pass it as a parameter instead so the production code would look like the test code. I personally like the approach I present here.
So,
Is it meant to be used with multiple inheritance (see my comment in MyView class)?
Should the events be propagated directly to the Presenter or should they be handled in the view that will call the respective presenter action (as I have done here to avoid having to make the Presenter a QObject so it can handle UI events)?
Are there any other remarks?
When you do multiple inheritance with QObjects, the first class in the inheritance list needs to be the QObject-derived class. This is only strictly required if you plan to add signals and slots to your class, but is good practice anyway. So your class declaration:
class MyView : public IView , public QDialog {
needs to become this instead:
class MyView : public QDialog , public IView {
Again, this will only bite you if you ever add a slot or signal to "MyView".
Other than that, I think this is a fine implementation, albeit huge amounts of overkill for a dialog. :)
I'm using Fowler's MVP with Qt, and it's working okay. Things are more testable (nUnit style), but it is a bit more complex IMO.
I usually use the same pattern for my UI in C# Winforms stuff.
You're actually not really doing multiple inheritance here. One of the classes you're inheriting from is just an empty interface. The only problem here is that C++ doesn't know the difference between a class and an interface.
I don't think there's a problem with creating the presenter inside the view like this either. It's not the most testable design but you're probably not going test the view anyway because there's nothing to test there if you're using humble dialog. Or you could do "poor man's DI" by adding a second constructor that injects a presenter instead of creating it for testing purposes.
In C# I usually have views that don't know about the presenter at all and just throw events instead of calling the presenter. This adds some decoupling but might be overkill in most situations.
Overall this is a good implementation. If I'll ever have to write a C++ app with a UI i'm going to check this post
Looks OK to me. But I would not use QString in the IView-Interface. Use some presentation independend type, if possible. That way, you can change the GUI-toolkit, without affecting the program logic and tests. Keep QString only if it is really painful to convert between QString and std::string or char* (I have no idea...).