C++ GTKMM gui circular dependencies - c++

I have been attempting to write a GTKMM gui application in C++. In my earlier projects in Java I started by making so-called 'Screen' objects which would each contain the layout of, and objects in, different screens. So I tried that in C++ as well, I derived these different Screen objects from the Gtk::Box so that I could easily append them to a Gtk::Notebook.
However I found out that this approach results in circular dependencies, and after a lot of browsing I couldn't find people with other approaches. I currently have a screen to display data retrieved from a database, and wanted to add filters to that.
I managed to allow the swapping of screens by giving each screen a pointer to the Gtk::Notebook they are in, but I hit a roadblock when I couldn't figure out how to make two screens interact with each other (eg. filter the data in another screen).
The general problem appears like this:
Gui.h:
class Gui {
protected:
// Child object pointers.
Gtk::Notebook *m_screens;
DbConnector *m_db;
// Screen object pointers.
MainScreen *m_mainScreen;
FilterScreen *m_filterScreen;
public:
// Constructors & destructor.
Gui();
virtual ~Gui();
};
Gui.cpp:
Gui::Gui() {
// Create application.
auto app = Gtk::Application::create();
// Db connector
m_db = new DbConnector();
// Create & configure window.
Gtk::Window m_window;
// Window configs.....
// Create notebook & screen objects.
m_screens = new Gtk::Notebook();
m_screens->set_show_tabs(false);
m_mainScreen = new MainScreen(*m_screens);
m_filterScreen = new FilterScreen(*m_screens);
// Add notebook to window.
m_window.add(*m_screens);
//Insert pages.
m_screens->append_page(*m_mainScreen);
m_screens->append_page(*m_filterScreen);
// Show all children & run app.
m_window.show_all_children();
app->run(m_window);
}
MainScreen.h:
class MainScreen : public Gtk::Box {
protected:
// Parent notebook pointer.
Gtk::Notebook* parent;
// Child widgets.
Gtk::Button m_toFilterScreenButton = Gtk::Button("To Filter Screen");
// Constructors & desctructor.
MainScreen(Gtk::Notebook& par);
virtual ~MainScreen();
// Methods.
void addFilter(std::string filterText);
void toFilterScreen();
};
MainScreen.cpp:
MainScreen::MainScreen(Gtk::Notebook& par) : parent(&par) {
// Build screen.
// Packing contents.....
// Configure widgets.
// Things like widget border width.....
// Signal handlers.
m_toFilterScreenButton.signal_clicked().connect(sigc::mem_fun(*this, &MainScreen::toFilterScreen));
}
void MainScreen::addFilter(std::string filterText) {
// Add filter
}
void MainScreen::toFilterScreen() {
notebook->set_current_screen(pagenum_of_filterscreen);
}
The problem I ran into now is when the FilterScreen is up, a filter is selected, and that filter should be applied to the MainScreen. The FilterScreen can't reach the MainScreen via the Gui object because that would require the screens to include Gui.h, which would result in a circular dependency. Trying to retrieve the MainScreen from the Gtk::Notebook returns a Widget&, which will tell you a Gtk::Widget has no function called addFilter(std::string filterText);.
Is anybody aware of a pattern I could use that would allow this type of behavior? So far the only option I can think of is one giant class that sets the screens using functions instead of premade objects, which would be far from optimal...

With the advice given by Sam Varshavchik in the comments I made a tiny example app that can handle multiple screens and switch easily. For those interested: source can be found here: ExampleApp

Related

JUCE - Making a New Window

Coming from making single-page applications with the visual WYSISWYG editor in JUCE, I'm having a bit of trouble figuring out how to invoke new windows (outside of the main GUI window). I made a test application that just has a small minimal main GUI that I created with the visual editor. It has a button "Make New Window." My goal is to be able to click that button and have a new window pop up and that this new window is a JUCE "GUI component," (AKA, the graphical / visual GUI editor file). Now, I actually have sort of achieved this, however, its throwing errors and assertions, so it would be great to get a very simple, step-by-step tutorial.
I studied the main.cpp file that the Projucer automatically created in order to get a feel for how they are creating a window. Here's what I did.
1) In my project, I added a new GUI Component (which becomes a class) and called it "InvokedWindow."
2) In my main GUI component class header, I added a new scoped pointer of type InvokedWindow: ScopedPointer<InvokedWindow> invokedWindow;
3) I created a new button in the main GUI editor called "Make New Window" and added this to the handler code:
newWindowPtr = new InvokedWindow; so that any time the button is hit, a new object of type InvokedWindow is created.
4) In the InvokedWindow class, in the constructor, on top of the automatically generated code, I added:
setUsingNativeTitleBar (true);
setCentrePosition(400, 400);
setVisible (true);
setResizable(false, false);
Which I sort of got from the main file of the JUCE application.
I also added a slider to this new window just to add functionality to it.
5) I added an overloaded function to let me close the window:
void InvokedWindow::closeButtonPressed()
{
delete this;
}
So, now when I run the app and click the make new window button, a new window does pop up, but I get an assertion:
/* Agh! You shouldn't add components directly to a ResizableWindow - this class
manages its child components automatically, and if you add your own it'll cause
trouble. Instead, use setContentComponent() to give it a component which
will be automatically resized and kept in the right place - then you can add
subcomponents to the content comp. See the notes for the ResizableWindow class
for more info.
If you really know what you're doing and want to avoid this assertion, just call
Component::addAndMakeVisible directly.
*/
Also, I'm able to close the window once and hit the button in the main GUI to create another instance of a newWindow, but closing it a second time leads to an error:
template <typename ObjectType>
struct ContainerDeletePolicy
{
static void destroy (ObjectType* object)
{
// If the line below triggers a compiler error, it means that you are using
// an incomplete type for ObjectType (for example, a type that is declared
// but not defined). This is a problem because then the following delete is
// undefined behaviour. The purpose of the sizeof is to capture this situation.
// If this was caused by a ScopedPointer to a forward-declared type, move the
// implementation of all methods trying to use the ScopedPointer (e.g. the destructor
// of the class owning it) into cpp files where they can see to the definition
// of ObjectType. This should fix the error.
ignoreUnused (sizeof (ObjectType));
delete object;
}
};
This is all a bit over my head. I was figuring it wouldn't be too bad to be able to create a new window, via a button. A new window that I could edit with the graphical GUI editor, but I'm not able to fully figure it out all on my own, through I did try. Could anyone post a step-by-step guide to doing this the correct way? I did post this at the JUCE forums, but due to my lack of GUI programming, I was unable to understand the solutions posted (my own fault), so I'm hoping to get a very simple guide to this. It would be very much appreciated. Thank you.
I figured it out. I needed to create:
A new GUI component (Remember, this is the visual editor in JUCE)
A class (I called it BasicWindow, based on the JUCE demo code) that acts as a shell to run this new window and holds the GUI component.
A JUCE SafePointer that makes a new object of type BasicWindow whenever the button is clicked and sets the attributes to that window.
Here is my code:
Referring to line 3) Inside the handler section of the button to create the new window:
basicWindow = new BasicWindow("Information", Colours::grey, DocumentWindow::allButtons);
basicWindow->setUsingNativeTitleBar(true);
basicWindow->setContentOwned(new InformationComponent(), true);// InformationComponent is my GUI editor component (the visual editor of JUCE)
basicWindow->centreWithSize(basicWindow->getWidth(), basicWindow->getHeight());
basicWindow->setVisible(true);
Referring to line 2) A .cpp file that defines what the BasicWindow is:
#include "../JuceLibraryCode/JuceHeader.h"
class BasicWindow : public DocumentWindow
{
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BasicWindow)
public:
BasicWindow (const String& name, Colour backgroundColour, int buttonsNeeded)
: DocumentWindow (name, backgroundColour, buttonsNeeded)
{
}
void closeButtonPressed() override
{
delete this;
}
};
And referring to line 1) Make the GUI editor component, which this is easy to do. You just right add a new file in the JUCE file manager. "Add New GUI Component," then visually add all your elements and handler code.
My biggest issue was that I was using a JUCE ScopedPointer, so after deleting the object, the pointer pointing to it wasn't being set back to NULL. The SafePointer does this. If any more explanation is needed, I'm happy to help, as this was terrible for someone with not much GUI development "under his belt."

How to break the tab order chain of widgets in Qt?

In Qt you can define the tab order by using the Qt Designer or by using C++. The relationships between widgets are set relatively to each other, so there is no index or such thing. What I want right now is to "break" the circular chain of widgets so that I get a beginning and an end of the chain.
A circular tab order would be:
A - B
| |
D - C
I want (note missing link between A and D):
A - B
|
D - C
which is more like a line instead of a circle:
A - B - C - D
So the user "stops" at one end and has to go back using the other direction.
Update: I have another idea now. What if i reimplement:
bool QWidget::focusNextPrevChild(bool next)
According to the documentation one can use this to implement custom focus behavior.
In my dynamic scenario where buttons in the GUI are adjusted at run-time I will have to overload the function and set, for example, an internal flag allowFocusNext and allowFocusPrev which then ignores the focus request if necessary. I will report back here, when I have tried it. Meanwhile any comments are welcome!? :-)
I found a solution, but it is a bit hacky. The QWidget::setTabOrder will not allow to chain a widget with itself, so this approach won't help (even if you are using focus proxies)
However, you can define a "Focus Forwarder":
class FocusForwarder : public QWidget
{
public:
explicit FocusForwarder(QWidget *proxy) :
QWidget((QWidget *) proxy->parent()),
m_proxy(proxy)
{
setFocusPolicy(Qt::TabFocus);
}
protected:
void focusInEvent(QFocusEvent *) {
m_proxy->setFocus();
}
private:
QWidget *m_proxy;
};
And add them at the beginning and end of you chain:
FocusForwarder *w1 = new FocusForwarder(ui->bA);
FocusForwarder *w2 = new FocusForwarder(ui->bD);
QWidget::setTabOrder(w1, ui->bA);
QWidget::setTabOrder(ui->bA, ui->bB);
QWidget::setTabOrder(ui->bB, ui->bC);
QWidget::setTabOrder(ui->bC, ui->bD);
QWidget::setTabOrder(ui->bD, w2);
Details
For setTabOrder to work, the widgets must be in the same window. To ensure this, the Forwarder is placed in the proxy's parent (in the initializer list).
For this mechanism, the focus direction (Tab or Shit+Tab) does not matter. As soon as a FocusFowarder receives the focus, it will "forward" it to its proxy.
The direction is handled by Qt internally. You just add "sentinels" around your chain.
Use in QtDesigner
When you want to use it in QtDesigner, you'd create a Widget and promote it to the forwarder. As you cannot set the proxy directly, you could add a dynamic property for the proxy's name, like this:
class FocusForwarderDesigner : public QWidget
{
Q_OBJECT
Q_PROPERTY(QString proxyName READ proxyName WRITE setProxyName)
public:
QString proxyName() {
return (m_proxy) ? m_proxy->objectName() : QString::null;
}
void setProxyName(QString name) {
m_proxy = parent()->findChild<QWidget *>(name);
}
explicit FocusForwarderDesigner(QWidget *parent = NULL) :
QWidget(parent) {}
protected:
void focusInEvent(QFocusEvent *) {
if (m_proxy) m_proxy->setFocus();
}
private:
QWidget *m_proxy;
}
In the designer, you would add a string-property with name proxyName and set it to the proxy's name. Don't forget to set the focus policy to Tab Focus in designer.
After some additional thoughts I post an answer to my own question because it is a working solution but it is not ideal. Therefore, I'm still searching for a better one! As a note, my application mainly relies on mouse wheel interactions for changing the focus of widgets.
In my question I mentioned that overriding:
bool focusNextPrevChild(bool next)
could lead to a working system. The "receiving" widget would simply ignore the focus by returning "true" if it is marked as "last item" or "first item" and the "next" parameter would lead to a circular behavior. Although this works for the tab and space+tab key combinations, there are cases where focusNextPrevChild is not called explicitly. In my case it is not called for focus changes related to mouse wheel events.
What I do instead is overriding:
void wheelEvent(QWheelEvent* event)
This gives me direct control over all the focus events related to the mouse wheel. My overridden function looks like this:
void SelectionIconButton::wheelEvent(QWheelEvent* event)
{
bool next = event->delta() > 0;
if (m_IsLastInFocusChain && next) {
event->accept();
return;
}
if (m_IsFirstInFocusChain && !next) {
event->accept();
return;
}
QPushButton::wheelEvent(event);
}
So this system's requirements are:
Each widget has to somehow implement two bools and handle their
state.
Each of those widgets has to be configured either at startup
or in dynamic screens during appliation use
Listening only to
wheelEvent does not allow me to handle tab key and space+tab key
combinations
You see that this solution works but it involves some effort to apply it to a large application. I was thinking about a more general solution. Maybe a global list that is updated when a screen is changing. This global list would then somehow decide if a focus change is allowed or not. Unfortunately, this again is problematic with mouse wheel events because some widgets are "active" and the wheel event does not even want to change focus but alter the value in an input field, for example, instead.
Edit:
I might have to add that the default implementation of QWidget::wheelEvent() and QPushButton::wheelEvent() and many more Qt-Widgets just ignore the event by setting event->ignore().
In my application all those ignored events are caught at a high level widget which then interprets the QWheelEvent and uses its delta to call focusPreNextChild() the right amount of time.

Passing application objects into lower level classes

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.

Handling c++ wxWidgets EVT_MAXIMIZE macro

I have a (maybe) simple question. I'd like to resize my program window after the user clicked on "maximize" since I want to fit the data to the window, without leaving "grey patches" (hope it does make sense lol, since I'm from Italy I'm not sure it does) . The problem is if I try to show a simple message, it doesn't show up. This is the piece of code I think you need to look at of my class (cpp file)
void EBCFrame::OnMaximize(wxMaximizeEvent& event)
{
// _window->Fit();
wxMessageBox(_T("maximize test"));
}
// Event table for EBCFrame
BEGIN_EVENT_TABLE(EBCFrame, wxFrame)
...
EVT_MAXIMIZE(EBCFrame::OnMaximize)
END_EVENT_TABLE()
And here's the header file
class EBCFrame : public wxFrame
{
public:
// Constructor
EBCFrame(const wxString& title);
// Event handlers
.....
void OnMaximize(wxMaximizeEvent& event);
private:
// This class handles events
DECLARE_EVENT_TABLE()
wxScrolledWindow* _window;
....
};
I apologize if it's not enough and you need the full code; in that case, I'll provide as soon as I read this question again. Thank you for your support!
Judging from your earlier question's code: wxWidgets: can't inherit from wxListCtrl;
You seem to be using a wxScrolledWindow for a wxListCtrl which is not needed (the wxListCtrl already manages the scrolling of its content) If that is the case, remove the wxScolledWindow from your code.
If that wxListCtrl (EBCList) is the only client window in your frame (EBCFrame), wxWidgets will size it automatically.
If you have added more widgets to your frame in the meantime, you may want to look into the sizer classes to compose your layout (e.g wxBoxSizer).

Accessing variable from one project to another in visual studio c++

I have a solution and it has two projects in it. When I got the code they told one project handles the visual part and the other has the logic part. Now I added one button to the window. To do that i edited the project which handles the visual part. I am very new to this but creating and adding buttons is fairly straightforward in visual studio 2010. Now the problem is I want to detect if the button is pressed from the other project. I am sure that the projects are sharing some data, but I am not being able to capture it. For now I am changing a value in a file and reading the same data from the other project to check if the button is pressed. But I think there is a better way to do it. Can anyone help?
I don't think the two projects are sharing automatically. You will have to define the interface that the two projects communicates. For instance, in your solution above the "a value in a file" is the "interface" you have defined. What sounds like you are trying to achieve is to separate the controller (logic part) and view (visual part) separately, which seems to indicate that your project is using MVC model.
I would suggest defining an abstract class (interface) that defines the interaction you want between the two projects. All they have to share is a single header file.
For example:
// Solution A (Controller - logic part)
// MyUIHandler.h
class IMyUIHandler //You can also use microsoft's interface keyword for something similar.
{
public:
HRESULT onButtonPressed() = 0; // Note that you can also add parameters to onButtonPressed.
};
HRESULT getMyUIHandler(IMyUIHandler **ppHandler);
Then implement this interface:
// Solustion A (Controller - logic part)
// MyUIHandler.cpp
#include "MyUIHandler.h"
class CMyUIHandler : public IMyUIHandler
{
private:
// Add your private parameter here for anything you need
public:
HRESULT onButtonPressed();
}
HRESULT getMyUIHandler(IMyUIHandler **ppHandler)
{
// There are many ways to handle it here:
// 1. Define a singleton object CMyUIHandler in your project A. Just return a pointer
// to that object here. This way the client never releases the memory for this
// object.
// 2. Create an instance of this object and have the client release it. The client
// would be responsible for releasing the memory when it's done with the object.
// 3. Create an instance of this object and have a class/method in Solution A release
// the memory.
// 4. Reference count the object, for example, make IUnknown the parent class of
// IMyUIHandler, and implement the IUnknown interace (which is boiler plate code).
// Since I don't know your project it's hard for me to pick the most suitable one.
...
*ppHandler = myNewHandler;
...
return S_OK;
}
CMyUIHandler can simply be your existing class that already handles some of the logic.
In solution B you should will call getMyUIHandler in some initialize function, for example the controller of the UI class, save that as your member. Then "Button clicked" event handler that VS creates for you.
// Solution B (View - visual part)
// MyUIView.h
class MyUIView
{
protected:
IMyUIHandler *m_pHandler;
}
// MyUIView.cpp
CMyUIView::CMyUIView(...)
{
...
hr = getMyUIHandler(&m_pHandler);
// error handler, etc...
...
}
// In the function that is created for you when button is clicked (I'm not sure if I get the signature right below.
void OnClick(EventArgs^ e)
{
...
hr = m_pHandler->onButtonPressed();
...
}
Then you can pass any parameter you define for the function onButtonPressed as soon as the button is clicked.