I have a Qt OpenGL application that uses a QOpenGLWidget to render content. In another class (let's call it Resources), I want to create OpenGL resources like VBOs, VAO, shader programs, etc. for this widget. This creation method is not called by the QOpenGLWidget, but by an external caller.
For some reason, there exist two OpenGL contexts in my application (one is probably used for GUI stuff and the other for the QOpenGLWidget). Hence, when the resource creation method is called, I cannot be sure that the correct context is active. So when I call
QOpenGLVertexArrayObject vao;
vao.create();
in the Resources class, I cannot be sure that this VAO is created on the right context. The Resources class does not have access to the widget. Thus, context.makeCurrent() cannot be called (because I do not know the surface).
Is there a direct way to specify the context, on which the resources should be created? Storing the surface in the Resources file (along with the context) seems very untidy.
Apparently, there is no way to create resources on a specific context. I worked around this issue with the following structure:
I created an interface OpenGLContextProvider, which is very simple:
class OpenGLContextProvider
{
public:
virtual void MakeOpenGLContextCurrent() = 0;
};
The OpenGL widget implements this interface:
class GLView : public QOpenGLWidget, public OpenGLContextProvider
{
//...
};
void GLView::MakeOpenGLContextCurrent()
{
makeCurrent();
}
As such, the OpenGLContextProvider (i.e. the OpenGL widget) is injected into the Resource object's constructor. Before it needs the context, it calls the according method:
void Resources::LoadSomeData()
{
//Load data...
//Create OpenGL resources
ctx->MakeOpenGLContextCurrent(); //ctx is of type OpenGLContextProvider*
vao.create(); //is now on the correct context
//etc.
}
Related
In QML you can use Animator type to "animate on the scene graph's rendering thread even when the UI thread is blocked."
How can I achieve the same thing for Qt Widgets?
Basically, I want something like:
1) start loading screen / splash-screen
2) start GUI-blocking operation
3) stop loading screen / splash-screen
It is not possible to move the ui-blocking operation to a separate thread (Widgets are being created). I cannot modify this part.
I tried QQuickWidget and QQuickView containing the splash-screen scene with an Animator inside but it didn't work - they got blocked as well.
EDIT: In the separate thread I read the file containing the UI description. Then I recursively create hundreds of Widgets (including QQuickWidgets, QLabels for images, Web views etc.).
Point of the question was to see if there is a "workaround" for that (e.g. displaying the aforementioned QML scene in some separate window with an own event loop). Unfortunately at this point not much more can be done about the overall design of the above.
Probably the widgets you're creating do too much work. You have to specify exactly how many widgets you're creating, and how. Show some example code. In general, the GUI thread is for cooperative multitasking - if you have something that "blocks", break it down into tiny chunks. For example, suppose that you're processing some XML or json file to build the UI. You could have that task do it one widget at a time, and be invoked each time the event loop is about to block (i.e. use a zero-duration "timer" and invert control).
You should also do the maximum possible amount of work outside of the gui thread. I.e. the UI description should be read and converted to an efficient representation that encapsulates the work to be done in the main thread. This conversion has to be done asynchronously.
The simplest way to accomplish that is to encapsulate each widget's creation in a lambda that refers to some context object. Such a lambda would have the signature [...](BatchContext &ctx). The vector of those lambdas would be kept by the CreationContext object as well:
class BatchContext : public QObject {
Q_OBJECT
public:
using Op = std::function<void(CreationContext &)>;
using QObject::QObject;
// useful for Op to keep track of where things go
void push(QWidget *w) { m_stack.push_back(w); }
QWidget *pop() { return m_stack.isEmpty() ? nullptr : m_stack.takeLast(); }
QWidget *top() const { return m_stack.isEmpty() ? nullptr : m_stack.last(); }
int stackSize() const { return m_stack.size(); }
bool stackEmpty() const { return m_stack.isEmpty(); }
Q_SLOT void startExec() {
if (m_execIndex < ops.size())
m_execTimer.start(0, this);
}
template <typename F>
void addOp(F &&op) { m_ops.push_back(std::forward<F>(op)); }
...
private:
QVector<Op> m_ops;
QVector<QWidget *> m_stack;
QBasicTimer m_execTimer;
int m_execIndex = 0;
void timerEvent(QTimerEvent *ev) override {
if (ev->timerId() == m_execTimer.timerId())
if (!exec())
m_execTimer.stop();
}
/// Does a unit of work, returns true if more work is available
bool exec() {
if (m_execIndex < m_ops.size())
m_ops.at(m_execIndex++)(*this);
return m_execIndex < ops.size();
}
};
After the context is created asynchronously, it can be passed to the main thread where you can then invoke startExec() and the widgets will be created one-at-a-time. The stack is but an example of how you might implement one aspect of widget creation process - the tracking of what widget is the "current parent".
We are in the process of converting C# code to C++, but we need to do so in phases. I am at a point now where I need to instantiate several native objects from within managed code. These native objects I cannot change, and their declaration looks like this:
public class NativeA();
public class NativeB(std::shared_ptr<NativeA> obj);
Both NativeA and NativeB need to be instantiated from managed code as:
void main() {
ManagedA ObjectA = gcnew ManagedA();
ManagedB ObjectB = gcnew ManagedB(ObjectA);
}
The problem comes in with getting the shared_ptr of NativeA in the constructor of NativeB. Niether NativeA nor NativeB will be manipulated in managed code, they just need to be instantiated. Ideally, something like this:
public ref class ManagedA {
public:
ManagedA() { _object = new NativeA(); }
~ManagedA() { delete _object; }
NativeA * Get() { return _object; }
private:
NativeA *_object;
};
public ref class ManagedB {
public:
ManagedB(ManagedA^ objectA ) {
_object = new NativeB(std::make_shared<NativeA>(*objectA->Get());
}
~ManagedB() { delete _object; }
private:
NativeB *_object;
};
But, this is not allowed in c++/cli because native types are declared as private. Defining #pragma make_public(NativeA) does not solve this either.
My intent is not to work with the native objects in managed code, they just need to be instantiated, so I really don't care about trying to marshal the native pointers and deal with .NET GC if I don't have to, and I don't want to perform a copy. I just want to wrap the classes in order to pass them around.
Is there a clean and simple way to do this?
It appears that the answer was not due to a syntax or usage problem. The two managed objects were in different DLLs and could not be passed across them via .NET. Once the code was compiled in the same project, the issue was resolved.
Although the error message indicated the problem was an accessibility issue in VS 2015, and because it reported it during the link phase, I suspect the cause was because the linker would not have known about the implementation of the NativeA in NativeB without declaring an extern. Being wrapped in CLR, it surfaced as a different issue.
I have written a customer Class (inheriting from QObject) in C++ and registered it's type successfully with QML. Currently I'm creating objects of this class statically in C++ and storing a pointer to them in a Model which implements QAbstractListModel. In QML in I can access the objects perfectly as items of the Model.
The customObject is a non-visual object.
I'm visualising in another part of the GUI application (QML) the objects in a ListView with a delegate.
However now I would like to create objects from my custom Class dynamically in QML and store them also in the Model. This is where I'm struggling. I hoped I could create a customObject like this:
import com.myProject.myCustomStuff 1.0
...
Button{
id: createObjBtn
text: "create new CustomObj"
onClicked:{
var obj = MyCustomObj;
myObjectManager.addObj(obj); // object holding the implemented QAbstactListModel
console.log(typeof(obj)); // returns [Object object]
console.log(Qt.isQtObject(obj)) // returns false
}
}
I would appreciate your thoughts. Maybe someone knows a way to do this correctly?
Thanks!
Update 1
As requested by Simon-Warta, here is the Constructor implementation of MyCustomObj.
MyCustomObj.cpp
MyCustomObj::MyCustomObj(QObject *parent) : QObject(parent)
{
QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
}
You are confusing the functionality intent of the classes. The QAbstractListModel is intended as a wrapper around a container, yes, you could put the container inside the QAbstractListModel derived class, but you don't really have to, the container can be just about any C++ class, not necessarily even QObject derived, it can be just a QVector<Something> that you can reach from the model via a pointer. Which is good for cases where you have many objects, and not all need to have models at all the time, since those models are pretty heavy.
You don't really need to concern yourself with the ownership, leave that at the C++ side, the same goes for the actual object creation, have a slot called that adds the new object to the container while also using the model's beginInsertRows() and endInsertRows() so that any views will be notified to update efficiently, the new object should also be created in that slot, you can pass any data needed for it from QML, just make sure all data is registered with the Qt meta system so it can work with QVariant for QML-C++ interop.
So it should be something like:
myObjectManager.create(/* any needed data goes here */)
And create() passes eventual data to the C++ side, where you create the object, call beginInsertRows(), add the object to the model's underlying storage, then call endInsertRows() and you are done.
I'd prefer to keep the ownership in the C++ side (and I don't mean explicitly), where I have control over it. Qt kind of sucks in a big way when dealing with object ownership shared between C++ and QML. Ideally, there should be a single shared pointer class that will work across both C++ and QML, so the object is deleted once all reference to it are gone. But that is just not the "Qt way" - the Qt shared pointers do not work with QML, nor do the standard C++ shared pointers, there is actually an entirely different shared reference class for QML, which is not even part of the public API IIRC, very ugly and shortsighted design that only widens the gap between C++ and QML and the associated inconvenience factor.
I don't know if this is the shortest way but this should do for you. I am starting with the basics for those other up-voters.
MyCustomObj.h
class MyCustomObj : public QObject
{
Q_OBJECT
public:
// ...
Q_INVOKABLE void funfunction();
MyCustomObj.cpp
void MyCustomObj::funfunction()
{
qDebug("Fun with QML");
}
main.cpp
qmlRegisterType<MyCustomObj>("com.myProject.myCustomStuff", 1, 0, "MyCustomObj");
app.qml
import com.myProject.myCustomStuff 1.0
ApplicationWindow {
id: mainWindow
Component {
id: myComponent
MyCustomObj {
}
}
Component.onCompleted: {
var obj = myComponent.createObject(mainWindow)
if (!obj) console.error("Error creating object")
console.log(typeof(obj))
console.log(Qt.isQtObject(obj))
obj.funfunction()
}
}
createObject optionally takes properties to be passed to the component.
Storing
Since you are responsible for deleting the objects now, I'd recommend to use shared pointers, such that the objects get destroyed when the List is destroyed.
Your implementation of QAbstactListModel, let's call it MyModel has an adder function like that:
#include <memory> // for std::shared_ptr
class MyModel : public QAbstractListModel
{
Q_OBJECT
public:
// ..
Q_INVOKABLE addObj(MyCustomObj* obj)
{
objectlist_.append(std::shared_ptr<MyCustomObj>(obj));
}
private:
QList<std::shared_ptr<MyCustomObj>> objectlist_
}
I'm wondering what might be the best approach to the following situation:
I have a QML file that is load from a HTTP server to a Qt/QML android app to display the UI.
The user can tap on thumbnails of catalogs and make the app download another QML file for each catalog. The catalog QML is downloaded and stored on the device for offline use.
This means I have a number of directories that have a unique ID to store the catalog QML and assets. Something like this:
/my/app_data_path/catalogs/CATALOG_001
/my/app_data_path/catalogs/CATALOG_007
/my/app_data_path/catalogs/CATALOG_010
In the UI I'd like to show an indicator that tells if a catalog has been downloaded already to the device. What would be the best approach within QML to show/hide an indicator depending on that?
Rectangle {
id: indicator
visible: MyApp.catalogIsLoaded('some ID here')
}
This is something that came to my mind, but I don't think it's the best way to do this since I'd need a method to pass the catalog ID in order to check if the data directory exists. Also I'd have to figure out a way to re-evaluate the visible state/call the catalogIsLoaded method from time to time - especially after downloading or deleting catalogs.
Is there a better/cleaner approach to this?
A better way would be to associate your indicator visibility with a property binding.
As you wish to find a catalog by its id, provide an invokable method on your 'MyApp' class to return a catalog reference.
Q_INVOKABLE Catalog* findCatalogById(const QString& id);
Catalog will be a QObject, expose a isLoaded property to QML.
class Catalog : public QObject {
Q_OBJECT
Q_PROPERTY(bool isLoaded READ isLoaded NOTIFY isLoadedChanged)
public:
Catalog(QObject* parent = 0) : QObject(parent) {
}
bool isLoaded() const { return m_isLoaded; }
void setIsLoaded(bool loaded) {
if (m_isLoaded != loaded) {
m_isLoaded = loaded;
emit isLoadedChanged();
}
}
signals:
void isLoadedChanged();
private:
bool m_isLoaded; //should probably be initialized to false
};
Of course, the catalog reference provided by your context should be memorize somewhere (like in a QHash<QString, Catalog*>), and be updated accordingly when its status changed.
I am new to .NET programming and I am trying to implement sessions in my already screwed sample. So here are the details of my sample i had done till now.
I have types of solutions in my project. A Class library, web application and console application.
I created a static class which has a bunch of get/properties and using these properties as global variables to use in my Class library. These get/set properties have a mixed set of data structures like list and dictionaries.
My Web app has only one page with a bunch of list boxes and button. I am using all the get set properties from my class library in to my Web application for some data storing and retrieving purposes. Now when the web page is opened in two different browsers then the data is over written from one user to the other as I am using all static variables and storing data in those static variables.
My best solution to this is using sessions but I am a little confused of using them in my project. Can any one please help me in this regard.
Here is a small sample of my explanation:
a XMLData Class in Class Library has a bunch of these get/set properties.
public Dictionary<string, List<string>> Content
{
get { return _testContent; }
set { _testContent = value;}
}
Now how do I use HttpContext Sessions to use in my Class Library to move these static properties to sessions so that every user who uses my site have their set of data. FYI. The web project is basically used for call in methods to the class library and do a little selection on the list box of the UI which are kind of inputs to my test.
Thanks In Advance.
The simple way is that you can access the current context in a class in your class library using
HttpContext.Current
this has Session available on it.
There are more complex ways that your application could be architected, but we'll start with the simplest :)
To elaborate, your class library may declare an interface for a component that is able to access session
public interface ISessionStore
{
object Get(string key);
void Save(string key, object value);
}
now, define a concrete implementation of ISessionStore
public class HttpContextSessionStore : ISessionStore
{
public object Get(string key)
{
var context = HttpContext.Current;
if (context == null)
throw new InvalidOperationException("this class is intended to work only within web applications");
return context.Session(key);
}
public void Save(string key, object value)
{
var context = HttpContext.Current;
if (context == null)
throw new InvalidOperationException("this class is intended to work only within web applications");
// note that this will overwrite anything already set
// against this key in session
context.Session[key] = value;
}
}
Now you can program against the interface ISessionStore and use the HttpContextSessionStore in your application.
I'd recommend looking at the MVP pattern if you're working with web forms or alternatively, take a look at the MVC framework.
In addition to Russ Cam's comments above, you should also check that HttpContext.Current != null in methods in your class library. It's guaranteed to bite you in the ass if you start writing unit tests, or for any code which isn't being executed within ASP.Net