I am making a new graphical menu interface for a project I am making. I don't want to use the menu system provided by windows APIs and want to make one from scratch.
My question is, what is the best method for setting up the structure?
I'm thinking I will need a menu item object, each of which will have to have their own item array list, etc...
Is it considered sloppy to have recursive coding like that? (Ie an object which contains objects of itself, which contains objects of itself, etc...)
I'm thinking I can give the item object a draw interface which checks itself to see if it has an item array that is not null. If it does, it executes the draw command all the way down, thereby giving me a menu with (for my purposes) unlimited submenu level
In my opinion your approach is fine. In nearly all UI frameworks, views contain views as subviews after all.
But the thing is that writing drawing code is too much work for small projects I think. I would consider using a UI framework such as QT and use its view mechanism as a starting point. You can write your own Menu class which will be a subclass of generic View class in the framework.
Related
I may be bordering on a discussion type question here so I apologise if the question is not specific enough.
I would like to know if I my current application design is inherently weak/flawed. My background is C so I am not using clever c++ patterns to their fullest extent, of this I am sure.
My application is similar to a 3D modelling package, without geometry creation (e.g setting up animations using existing models). The user imports a geometry and can set various parameters on pre existing geometry, and there are time dependent values that relate to the whole system. The output is a text file to be processed by another application.
I am using a QTreeview to render a QStandardItemModel. I simply store pointers to my core classes in the model's items. These have specific UI for each class type, and are all descended from a common base class. They all have a QWidget which is their "mainwidget"
When the user clicks on part of the treeview, the stored class is retrieved and its mainwidget is displayed on a pane on the UI. So - treeview on the left, pane to the right with the current item's contents, and a 3D view showing the geometry.
Most of my data is stored in the classes UI elements themselves; I don't have a central database which stores anything, and when it's time to save the project, I traverse the tree and let every item write itself to a QSettings file. This feels quite naive but it does work, and the reverse happens on project load. The project class generates new classes based on the type information in the settings file and they then read the contents out of the file themselves.
Similarly when writing the output file, each item knows how to write itself and does so. Where other classes can influence the output of others (for example, start and end times), higher level classes process the children and will set start and end times based on the order and duration of each child.
Should I be storing more data in the QStandardItemModel itself, or defining my own model perhaps? Does it sound like I have set myself up for future problems?
At the moment I have modified this system a couple of times to provide customised applications, but I am about to try and make it more generic. I welcome suggestions for improving my design. Go easy, please!
You should try to avoid creating god objects. Split your tasks and duties into smaller chunks. It makes it much easier to maintain and also much easier to extend if you need to.
Your specific use-case would benefit a lot from a more complete use of the Model-View-Controller pattern.
What doesn't make sense in your design is that your data objects hold a UI element. Since only one item can be shown in the right pane, this seems like a waste of resources. It makes more sense to have a single object that then gets passed a data object to display.
What I suggest for your program is the following:
Splitting your data into classes that only have functions for reporting and modifying values. There should be no knowledge of how to display data or store to a file.
Create separate class that handles the reading and writing from a file. If your model is very simple, you could just use single functions to do this using the method shown in the documentation for QDataStream or QTextStream.
Use your QTreeView and QStandardItemModel as Adaptor class between your data objects and the left pannel.
Create a controller class that gets informed by the QTreeView if data needs to be displayed in the right panel. The controller will then retrieve the data item and pass it to the right panel in order to be displayed.
The right panel should act like another View class with the sole responsibility of graphically displaying the data passed in from the controller class.
One advantage of doing it this way is that, if there are different categories of data that get displayed differently in the right panel, the controller could examine the selected data item, determine what the category is, create a view widget to put in the right panel, and then pass it the data to display.
This pattern is open-ended as far as extendability is concerned because you do not need to change your data classes if you need a new display, you merely need to create a new right-panel widget, and then teach your controller class how to determine when the new view should be used.
Another advantage of this pattern is that you only ever need to have a single widget created to display data in your right panel. If you change your selected item, you can just pass it to the view class that already exists and get it to refresh its display with the newly selected data. You only need to distroy the right-panel view widget if a different category of data object is selected and its data needs to be drawn in a different way. The controller class can determine whether a right-panel view widget can be re-used or whether it needs to be swapped out for a different widget.
I have made my own GUI API for games. One assumption that must be made is the user may want to use a derived version of a Widget I have made.
An example of how I dealt with this with ToolTips is, the user allocates a new ToolTip and sets a global one for the GUI. Ex: getGui().setToolTip(customToolTip);
The GUI then calls toolTip->show() when necessary. The problem with menus in general is that you can have many of them appearing at a given time.
I also would hate to have the user implement an interface:
PopUpFactory::createPopUp()
PopUpFactory::destroyPopup()
How is this usually dealt with? Who or how is the memory also managed for these?
There is always the option of limiting to something like 9 nested menus and have the user set an array of 9 PopUps but that seems messy.
Thanks
I've been wanting to program a simple game with a simple GUI using Qt (Its will be a VERY simple game, nothing fancy). What I've been wondering is, how can I create multiple windows and display them when needed? For an example, a battle screen and an inventory screen. The user should only see one of them, but should be able to access the other one when needed. I was using stacked widget but I'm not sure if that's the proper way. Also, is it better to design the windows in the designer or to code them?
A StackWidget certainly would accomplish what you want to do. The reason why it is not always used for this kind of thing, is that it all the screens are pre-created at the beginning and always exist. This means it takes a little longer to initialize, and you are using more resources than you need at any one time
But as you are saying, if this is a simple game, then I don't see a big problem with it. Just as easily, you could also, create an empty layout and swap the inventory and game panels as needed.
Add my vote to everyone else suggesting to use the designer. It is so much easier to manipulate layouts, actions, and such using the designer then through code.
You can see the Designer manual here
So this is what I would suggest:
Create your "battleScreen.ui" - which is the designer file for your battle screen and everything in it, and then create your "inventory.ui". Both of these could be QWidgets, or QFrames, or whatever makes sense.
Then create your "Game.ui" which will be your QMainWindow.
In your Game main window, you can then add your QStackWidget, and place your inventory, and battle screens in the stack widget.
If you don't know how to do that...
1) drag a QWidget into your form (into the stack widget)
2) select the new QWidget and right-click.
3) Select "Promote to..."
4) Fill out the information to promote the QWidget to your inventory class
Promoted Class Name: The name of your inventory class
Header File: The header file of your inventory class
5) Click add
6) Click Promote.
Hope that helps.
Since I'm not sure what your goals are I can't advise whether or not the stacked widget is appropriate but I think you can accomplish quite a lot using the designer and style sheets. If you need to code some parts of the GUI, you can always drop in a place holder widget and either replace it with coded items or make them children of the place holders.
A general answer for a general question:
Use the Designer to create your windows; hide and show the auxiliary windows as needed.
Use a flow manager class to manage the visibility of a related set of windows.
The stacked widget is useful for managing a button/icon whose appearance changes based on state; the different representations live in the stack.
Background
We use the Model-View-Presenter design pattern along with the abstract factory pattern and the "signal/slot" pattern in our application, to fullfill 2 main requirements
Enhance testability (very lightweight GUI, every action can be simulated in unit tests)
Make the "view" totally independant from the rest, so we can change the actual view implementation, without changing anything else
In order to do so our code is divided in 4 layers :
Core : which holds the model
Presenter : which manages interactions between the view interfaces (see bellow) and the core
View Interfaces : they define the signals and slots for a View, but not the implementation
Views : the actual implementation of the views
When the presenter creates or deals with views, it uses an abstract factory and only knows about the view interfaces.
It does the signal/slot binding between views interfaces. It doesn't care about the actual implementation. In the "views" layer, we have a concrete factory which deals with implementations.
The signal/slot mechanism is implemented using a custom framework built upon boost::function.
Really, what we have is something like that : http://martinfowler.com/eaaDev/PassiveScreen.html
Everything works fine.
The problem
However, there's a problem I don't know how to solve.
Let's take for example a very simple drag and drop example.
I have two ContainersViews (ContainerView1, ContainerView2). ContainerView1 has an ItemView1. I drag the ItemView1 from ContainerView1 to ContainerView2.
ContainerView2 must create an ItemView2, of a different type, but which "points" to the same model object as ItemView1.
So the ContainerView2 gets a callback called for the drop action with ItemView1 as a parameter. It calls ContainerPresenterB passing it ItemViewB
In this case we are only dealing with views. In MVP-PV, views aren't supposed to know anything about the presenter nor the model, right ?
How can I create the ItemView2 from the ItemView1, not knowing which model object is ItemView1 representing ?
I thought about adding an "itemId" to every view, this id being the id of the core object the view represents.
So in pseudo code, ContainerPresenter2 would do something like
itemView2=abstractWidgetFactory.createItemView2();
this.add(itemView2,itemView1.getCoreObjectId())
I don't get too much into details. That just work. The problem I have here is that those itemIds are just like pointers. And pointers can be dangling. Imagine that by mistake, I delete itemView1, and this deletes coreObject1. The itemView2 will have a coreObjectId which represents an invalid coreObject.
Isn't there a more elegant and "bulletproof" solution ?
Even though I never did ObjectiveC or macOSX programming, I couldn't help but notice that our framework is very similar to Cocoa framework. How do they deal with this kind of problem ? Couldn't find more in-depth information about that on google. If someone could shed some light on this.
I hope this question isn't too confusing ...
Ok the technique I found is actually coming from Cocoa, so it's objective-C but you can definitely do the same in C++.
The solution is simply to use a PasteBoard (developer.apple.com documentation).
In the hope it helps someone ...
I have been away from C++ for a couple of years now doing AS3/Flex work. I have gotten used to being able to create a component and place it in design mode with very little fuss and I am struggling to get my head around the C++ Builder way of doing the same thing.
I have written many components for C++ Builder in the past, but none of them have been visual. What I would like to do now is create a component for customer search and another for order processing because I want to be able to create a new instance of these on the fly. What I don't want to do is have to place each of the components like the dbgrid and search fields manually in code. I would like to do this (as well as set their properties) in design mode.
How do I go about this? I have browsed the source for other Delphi components and I notice they have dfm files which seems to be what I need. How do I do this in C++ Builder? The only option I see is to add a new form if I want a dfm, but this isn't what I want as my components will be based on TPanel.
Is there a way to do this or do I have to resort to doing it all in code with no visual reference?
Pursuing the DFM idea I did a test this morning where I created a component based on TPanel and added a new form to it which I create and parent in the constructor of the component. In design mode I set the form border to none and placed a grid on it. This all looks OK until I place the component in my application, at that point it looks like a panel with a standard looking form in it and the grid is missing. If I run the app the panel shows as expected, borderless and with a grid. The DFM appears to be ignored in design mode for some reason.
If you know a better way to do this than using components then please give me some pointers.
Any help and advice will be appreciated beyond words
You might want to have a look at frames (look for "Frame objects"). They are "subforms" you can design visually and then place on forms.
Just as an FYI item, you can also drop the panel on a form, put any other controls on it, position them, set captions, etc..
Now, click the panel to select it, and use Component->Create Component Template from the IDE's main menu. This creates a unit you can install as a component which will add the panel and it's sub-controls (as a single component) to the IDE's component palette.
Of course, you can then modify the source for that new component like any other component source.