I have a model that is being displayed in a set of widgets using a QDataWidgetMapper. One such field is a QComboBox populated by a QStringList of options, but the mapping doesn't seem to work.
QComboBox's user property is the currentText() function, which has no corresponding setCurrentText() function for writing, so the mapping fails with the warning Warning: QComboBox::setProperty: Property "currentText" invalid, read-only or does not exist.
Therefore, I created a simple custom QComboBox like the following:
class MappingComboBox : public QComboBox
{
Q_OBJECT
public:
Q_PROPERTY(QString mappingText READ currentText WRITE setCurrentText USER true)
explicit MappingComboBox(QWidget *parent = 0) : QComboBox(parent) {}
QString currentText() const { return QComboBox::currentText(); }
public slots:
void setCurrentText(const QString& s) { setCurrentIndex(findText(s); }
};
But I still get the same mapping error Warning: QComboBox::setProperty: Property "currentText" invalid, read-only or does not exist. I'm quite certain that I have promoted my widgets to be MappingComboBoxes, yet the QDataWidgetMapper still appears to be using the default read-only user property currentText instead of the writable custom user property mappingText.
Am I missing something? Can you not override an inherited class's user property?
Edit: I recognize that this issue is fixed in Qt 5.3.1, but I'm stuck in Qt 4 for the time being so I'm trying to come up with a workaround that doesn't involve editing the source.
Related
I have the following classes:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QStringList pluginsToStart, QWidget *parent = 0);
~MainWindow();
// some other stuff
public slots:
void on_timeDataChanged(logging::TimeValueVector<bool>& aData);
void on_importStarted();
}
and
class DataImporterWidget : public PluginWidget
{
Q_OBJECT
public:
explicit DataImporterWidget(QWidget *parent = 0);
~DataImporterWidget();
void initConnections(QMap<QString, PluginWidget*> pluginWidgetMap);
in the method initConnections, I want the widget to init the signal-slot connections like so:
void DataImporterWidget::initConnections(QMap<QString, PluginWidget*> pluginWidgetMap)
{
for(Importer* importer : this->getImporterMap().values())
{
connect(importer, SIGNAL(signal_timeDataChanged(logging::TimeValueVector<bool>&)),
parentWidget(), SLOT(on_timeDataChanged(logging::TimeValueVector<bool>&)));
}
connect(this, SIGNAL(signal_importStarted()), parentWidget(), SLOT(on_importStarted()));
}
Importer is a QGroupBox and a base class for derived sub classes specifying concrete data importer types.
It works like so: If I press a button, an DataImporterWidget is created and added to a QMdiArea as a QMdiSubWindow. When creating the DataImporterWidget I call the initConnections() method which sets up the signal-slot connections.
Now, when I run the program, I get the following message:
QObject::connect: No such slot
QMdiSubWindow::on_timeDataChanged(logging::TimeValueVector<bool>&) in src/dataimporter/DataImporterWidget.cpp:81
QObject::connect: No such slot QMdiSubWindow::on_importStarted() in src/dataimporter/DataImporterWidget.cpp:85
QObject::connect: (sender name: 'DataImporterWidget')
I do not understand why I get it because the slot is there. Even if I cast the parentWidget to the MainWindow, I get the same error.
PluginWidget is just a base class deriving from QWidget that holds some common functionality for my used plugins.
I put Q_OBJECT on each base and derived class but still get this error. However, if I set up the connections in the MainWindow, it works just fine, but I wonder why the above solution won't work.
Don't create the connection from child object, instead create it from parent object code after creating the child object.
This way you won't need to cast any type.
You did not shown a huge chunk of important code (like creating DataImporterWidget, setting MainWindow as its parent, the place where you call initConnections...). However, you said
If I use the new signal slot syntax, my program crashes with a
segmentation fault...
If it crashes, than you have to find a reason why. Using old signal-slot connect syntax does not cure the disease, it just delay its manifestation. According to this, the reason why you get a segfault can be parentWidget() == nullptr or parent is not initialized yet.
My advice, check your code, and make user the parent of DataImporterWidget is created and specified before your call initConnections().
I've found the problem. The reason is, that the MainWidget class holds a QMdiArea where I add my PluginWidgets. So, when I create the PluginWidget, I set the MainWidget as its parent, but as soon as I add it to the QMdiArea, it also becomes a child of QMdiSubWindow. The parentWidget was never null but it was the wrong one ...
I'm using the QT 5.9 WebEngine framework to display web pages. I'm injecting javascript into a page when it loads, and want to allow the javascript to be able to access a QT object.
I get the QWebchannel callback in JS to be invoked but the object method and properties are undefined.
NOTE: I NEED to inject all JS code and cannot change the html code of the loaded page. Meaning i cannot include any js script nor write any js code directly in the html pages.
Here is my code:
I create the QWebEnginePage object with a profile and inject the JS files as below:
// file: webview.cpp, webview extends **QWebEngineView**
QWebEngineScript script;
script.setSourceCode(qwebchannelsource); // source is qwebchannel.js
script.setInjectionPoint(QWebEngineScript::DocumentCreation);
profile->scripts()->insert(script);
QWebEnginePage page* = new QWebEnginePage(profile, this);
QWebChannel *channel = new QWebChannel(page);
page->setWebChannel(channel);
channel->registerObject(QStringLiteral("jshelper"), &JSHelper::instance());
JSHelper
class JSHelper : public QObject
{
public:
static JSHelper &instance();
QString someString;
int someInt;
Q_INVOKABLE int getInt();
Q_PROPERTY(int myIntInCppSide READ getInt)
private:
JSHelper();
};
JS code
// also injected into the page (like qwebchannel.js) at QWebEngineScript::DocumentReady
new QWebChannel(qt.webChannelTransport, function (channel) {
var jshelper = channel.objects.jshelper;
console.warn('qwebchannel triggered');
// do what you gotta do
if (jshelper === undefined) {
console.warn("jshelper is undefined");
}
else {
console.warn("jshelper is awesome");
}
console.warn("jshelper value of string " + jshelper.somestring);
console.warn("jshelper value of int " + jshelper.myIntInCppSide);
jshelper.myIntInCppSide(function (n) {
console.warn("jshelper value of int " + n);
});
});
my output:
qwebchannel triggered"
jshelper is awesome"
jshelper value of string undefined"
jshelper value of int undefined"
Uncaught TypeError: jshelper.myIntInCppSide is not a function"
As you can see I have tried various suggestions from several answers but nothing seems to fix my issue that the function does not exist.
Even when using the remote debugging I can see the jshelper is of type QObject but it does not have the properties nor methods of my class.
QT QWebEnginePage::setWebChannel() transport object
Undefined properties and return types when using QWebChannel
JSHelper class misses Q_OBJECT macro. As the official documentation says,
Notice that the Q_OBJECT macro is mandatory for any object that
implements signals, slots or properties. You also need to run the Meta
Object Compiler on the source file. We strongly recommend the use of
this macro in all subclasses of QObject regardless of whether or not
they actually use signals, slots and properties, since failure to do
so may lead certain functions to exhibit strange behavior.
Also the constructor of this class needs to be made public for Qt's introspection facilities to be able to work with the class.
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 building a notepad and want to count the words in a dialog.
QString input = ui->textEdit->toPlainText();
int spaces = input.count(" ");
ui->NumWordsLabel->setNum(spaces);
This is my attempt so far.
However, I want to execute this code in my dialog so I need to pass the
ui->textEdit->toPlainText()
Into my dialog.
This is how I create my dialog...
void MainWindow::on_actionWord_Count_triggered()
{
word_count = new Word_count();
word_count->show();
}
How would I get the required information into the dialog?
Thanks.
Generally you can pass constructor arguments to pass data to your classes. For example:
Header file:
class Word_count : public QDialog
{
Q_OBJECT
public:
explicit Word_count(QString text, QObject *parent = 0);
...
}
Source file:
Word_count(QString text, QObject *parent)
: QDialog(parent)
{
ui->setup(this);
... figure out word count and set labels ...
}
How to use:
void MainWindow::on_actionWord_Count_triggered()
{
word_count = new Word_count(ui->textEdit->toPlainText());
word_count->show();
}
Important notes:
The QObject *parent argument should always be present in the constructor arguments. Make sure to only place the = 0 in the header file, or else you will get an error.
Your constructor should be marked explicit, unless you know you do not want that. Explicit prevents the C++ compiler from automatically casting to your type using a given constructor.
Pass the parent parameter to your inheriting class, whether that be QDialog, QWidget or QObject, using the constructor initializer list syntax. This is done in the source file example with : QDialog(parent).
You can add as many arguments as you need, but they should be before the parent argument. This is because the parent argument has a default value that can be implied. Because you must specify arguments in order, it can not be implied if there are required parameters after it.
This only will work for creating the dialog. If you want the dialog to dynamically update, you'll need to use a slot or method like suggested by others. Alternatively, if you don't want a dynamically updating dialog, consider using exec instead of show so that users must close your word count dialog before continuing with their work.
Add a slot like void setText( const QString& text ) to your Word_count class.
Then, you can emit a signal like void textChanged( const QString& text ) const from your MainWindowclass.
Don't forget to connect both.
I'm trying to send a QStandardItemModel-derived object to PythonQt, but I'm a little confused on how it needs to be sent. When I was using boost::python I had several controls like boost::noncopyable to ensure I wasn't recreating this object, but sharing it with python. I also had constructs to provide a boost shared pointer to python from inside python.
class Scene : public boost::enable_shared_from_this<Scene>, public QStandardItemModel
In PythonQt, however, I'm not sure what's available. The function call takes a QVariantList for all the function parameters.
QVariant PythonQt::call(PyObject* object, const QString &callable, const QVariantList &args = QVariantList))
What I'm confused about now is how to get my object to python via a QVariant. Since its derived from QStandardItemModel, I figured it would already be register
void MyObject::someFunction(QString fileName)
{
QVariant myObjectV = qVariantFromValue(this);
// send to python
...
}
But this gives me the following error:
'qt_metatype_id' : is not a member of 'QMetaTypeId<MyObject>'
I've tried registering it after I declare my class, but this throws a different error.
class MyObject : public QStandardItemModel
{
Q_OBJECT
...
};
Q_DECLARE_METATYPE(MyObject)
QStandardItemModel::QStandardItemModel(const QStandardItemModel&) is private within this context.
I actually get the error twice--once in header where I add the Q_DECLARE_METATYPE and in another header, which has a class which always derives from QStandardItemModel but is otherwise unrelated.
Is Q_DECLARE_METATYPE even the correct way to go about converting this object to a QVariant?
BOOST_PYTHON_MODULE(scene)
{
class_("Scene");
}
Yes, by default, QVariant can take one of te following types - http://doc.qt.io/qt-4.8/qvariant.html#Type-enum - and they are not enough for your task. You should declare additional types by yourself via qmetatype system. Thus you shoud call qRegisterMetaType() function.