I am using Qt5, and trying to learn on how to make an application scriptable.
For this I created a main window that contains some text edits, labels, etc. I then added an option called "script console" to that forms' menu in order for me to open a second form containing just a text edit and a button called "Evaluate".
What I was aiming at was being able to use that second form and through Qt script engine be able to set or get values from my main form, and generally be able to script various functions.
What I tried doing was set up the engine like this
scriptingconsole::scriptingconsole(QWidget *parent) :
QDialog(parent),
ui(new Ui::scriptingconsole)
{
ui->setupUi(this);
QScriptValue appContext = myScriptEngine.newQObject(parent);
myScriptEngine.globalObject().setProperty("app", appContext);
}
I don't get what I was expecting though.
If I try to evaluate the expression "app" I get null as an output.
This works fine if I use myScriptEngine.newQObject(parent) with an object inside the current class (if instead of parent I enter this), but I want to be able to access object in other classes too (hopefully all public slots that are used by my app in general).
Does anyone know what I am doing wrong here and how can I use my scripting console
class to access public slots from my main window?
What's wrong?
I guess it's because you didn't explicitly pass the pointer, which points to your main form, to the constructor of your scriptingconsole. That's why you got NULL as a result. (NULL is default, as you can see QWidget *parent = 0 in every QWidget constructor)
This happens if your object is not dynamically instantiated.
Solution
Dynamically allocate scriptingconsole in your main form:
scriptingconsole* myScriptConsole;
//...
myScriptConsole = new scriptingconsole(this);
// ^^^^
// pass the pointer which points to parent widget
The Qt documentation of QScriptEngine::newQObject says:
Creates a QtScript object that wraps the given QObject object, using the given ownership. The given options control various aspects of the interaction with the resulting script object.
http://qt-project.org/doc/qt-4.8/qscriptengine.html#newQObject
i.e. it wraps a QObject.. You are probably passing NULL to newQObject, for whatever reason. Try setting a breakpoint and evaluating the value of 'parent'.
Related
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."
I have a couple of widgets in QtCreator that are promoted from the same class. However, I'd like them to have subtle differences between the two, so I'd like to pass some differences in the UI file that the promoted class can use to distinguish itself. Dynamic properties seem like the way to go so in the UI editor I've assigned a dynamic property to each promoted widget. In the code I tried accessing the property, but noticed it seems to only be available post construction (probably because Qt is calling setProperty() after the object is created.
MyWidget::MyWidget(QWidget* parent) : QGLWidget(parent)
{
this->property("someProperty").toString(); // returns blank
}
void MyWidget::initializeGL()
{
this->property("someProperty").toString(); // returns string set in UI file
}
So my question how do people use these properties for constructor-type stuff? I could just do that in initializeGL, but that seems odd since these properties might not be related to initializing OpenGL. I imagine I could also connect to the property changed signal and do it there. Is that the common way to handle this?
If the generated code for setupUi() from your .ui file does something like this:
MyWidget *w = new MyWidget;
w->setProperty(...);
then your constructor is accessing a meta property that does not yet exist.
You can reimplement QObject::event() to capture QDynamicPropertyChangeEvents, letting you act once the property is initialized.
bool MyWidget::event(QEvent *ev)
{
if (ev->type() == QEvent::DynamicPropertyChange) {
if (QDynamicPropertyChangeEvent *propEv = static_cast<QDynamicPropertyChangeEvent *>(ev)) {
if (propEv->propertyName() == "someProperty")
...
}
}
}
Bear in mind that this code will be called every time there is a dynamic property change.
A better approach may be to create a function to perform the necessary initilization on the widget after setupUi() etc. are called and the dynamic property is created.
void setupMyWidget(MyWidget *w)
{
QString s = w->property("someProperty").toString();
...
}
Typically, dynamic properties are assigned a default value in the constructor so that they are always available and non-null later.
setProperty("someProperty", defaultValue);
Is it possible to load an .ui class generated by uic, dynamically by class name? I need to decide which UI class to load dynamically. I do not have that information at compile time. I do not want to use QUiLoader. Instead, I want to combine the direct approach here with QMetaType object instantiation by string.
I.e.
add the UI file to FORMS in project file,
declare the UI classes for QMetaType usage Q_DECLARE_METATYPE(Ui::planar_break) or Q_DECLARE_METATYPE(Ui_planar_break)
then form a string class name dynamically depending on user action: "Ui::planar_break" or "Ui_planar_break"
and invoke the below function to get the UI widget pointer for usage?
QWidget* initiateClassByName(QString name){
int id = QMetaType::type(name.toLatin1());
QWidget* widget=nullptr;
if (id != QMetaType::UnknownType) {
widget=static_cast< QWidget* > (QMetaType::create(id));
//QMetaType::destroy(id, myClassPtr);
//myClassPtr = 0;
}
return widget;
}
I am trying to increase the performance, compared to loading a dozen or so UI files (stored in Qt resource files) dynamically every time a specific dialog is instantiated. When I do this I seem to get a QMetaType::UnknownType every time. Ideas? Thanks.
(Hm, not sure why my function wasn't showing as code block here, until I made it a quotation.)
uic creates C++ code. If you really want to dynamically create a widget/dialog out of an xml file at runtime, you need to use the Qt Ui Tools. The class QUiLoader might be what you are looking for. If you do this, you can query the created QWidget through QWidget::findChild You can interact with the UI items through QObject::findChild(), provided you give your widgets distinct and meaningful object names.
Essentially, based on discussion had in #qt irc channel on Freenode I think what I am asking is actually not feasible.
My understanding is that even if I could get the C++ headers compiled and the classes registered using perhaps also qRegisterMetaType(), and perhaps even get them instantiated using QMetaType, to actually get the UI built I would have to call setupUi() on the instance.
Since UI files do not implement a common interface that includes setupUi, and I don't know compile-time which class I am instantiating, calling setupUi becomes impossible.
How would I go about passing QSqlQueryModel from a class that connects and queries the database through the control class or QMainWindow in my attempt and back to the widget needing the information?
I thought I could pass the reference location to the QSqlQueryModel object, but this is not working or I am doing something wrong.
I haven't found any examples showing what I am doing on the Qt Developer page.
Looks like these are just compiler errors, nothing specifically to do with Qt.
In short you are getting your pointers and references mixed up.
Error #1:
cardList = new List(sqlModel->getListModel());
You are passing a reference when the List takes a pointer. Fix your return type from getListModel or fix the above line.
Next, you are not specifying the second argument, i.e. the parent QWidget. Either specify your MainWindow as the parent, pass 0, or fix your constructor's signature to provide a default (generally 0).
Error #2:
List::List(QSqlQueryModel *model, QWidget *parent) : ListUI(parent){
setListItems(&model);
}
You receive the model as a pointer and then attempted to take the address of the pointer. I.e. You're making a double pointer. Change the line to
setListItems(model);
Hope that helps.
My friend and I have each created parts of a GUI using Qt 4. They both work independently and I am trying to integrate his form with the my main window. As of now this is the code I am using to try and load his form:
//connect buttons and such
connect(exitbtn, SIGNAL(triggered()),this,SLOT(terminated()));
connect(add, SIGNAL(triggered()),this,SLOT(add_rec()));
void MainWindowImpl::add_rec()
{
//form quits as soon as it loads...?
DialogImpl dia;//name of his form
dia.show();
}
I have included his header file. The program compiles but when I hit the trigger his form loads up for maybe half a second and then closes. Does anyone know what I am doing wrong?
You have almost get it right. This is because the RAII of C++. If you allocate the Dialog on stack, it would be destructed as soon as the function return.
Assuming MainWindowImpl inherits publically from QWidget, you're looking for this:
void MainWindowImpl::add_rec()
{
// passing "this" to the constructor makes sure dialog will be cleaned up.
// Note that DialogImpl will need a constructor that takes a
// QObject* parent parameter.
DialogImpl* dialog = new DialogImpl(this);
dialog->show();
}
Look at the Qt documentation for examples of how the constructors should look.
Apparently QT4 only allows one instance of an object at a time, however pointers are another matter. Change both the main.cpp and what ever your main window to look something like this:
DialogImpl *dia=new DialogImpl;
dia->show();