QPluginLoader.instance() - how does it really work? - qt

I am currently working on a Qt project where dynamic plugin loading is central. I am loading DLLs by using Qt's QPluginLoader. The different plugins are accessed through CameraPluginStructs, defined as follows:
struct CameraPluginStruct
{
QString name;
QString filePath;
CameraPluginInterface* plugin;
QPluginLoader* pluginLoader;
CameraPluginStruct(QString filePath=nullptr, QString name=nullptr):
filePath(filepath), name(name), plugin(nullptr), pluginLoader(nullptr){}
};
To load a plugin, the following function is called:
void loadPlugin(CameraPluginStruct& p)
{
p.pluginLoader = new QPluginLoader(p.filePath);
p.pluginLoader->load();
QObject* possiblePlugin = p.pluginLoader->instance(); //QPluginLoader.instance();
if(possiblePlugin)
{
// cast from QObject to correct type:
p.plugin = qobjectcast<CameraPluginInterface>(possiblePlugin);
}
}
And to unload a plugin, I use this function:
void unloadPlugin(CameraPluginStruct& p)
{
p.pluginLoader->unload(); // QPluginLoader.unload();
p.pluginLoader->~QPluginLoader();
p.pluginLoader = nullptr;
p.plugin = nullptr;
}
I have made a few, simple test plugins that write messages to the console when the constructor is called. For simplicity, let's say that I have two test plugins, DLL A and DLL B. When I load A by using the loadPlugin() function, the constructor in the plugin is called, and the corresponding message is written in the console. I can do the same thing with B, and everything seems to work – B's constructor message is written to the console, and the other functions seem to work as well.
Now, the problem occurs when I try to create another CameraPluginStruct connected to either A or B. No message is written to the console, which leads me to think that the constructor is not called. Even though, I am able to call the other test functions in the plugin with success (DoSomething(), see below). If I unload all CameraPlugins connected to A or B, and then load the DLL again, the constructor is called again on the first load.
The QPluginLoader.instance() call is described as follows in the documentation:
"Returns the root component object of the plugin. (...) The component object is a QObject. Use qobject_cast() to access interfaces you are
interested in." http://doc.qt.io/qt-5/qpluginloader.html#instance
Wouldn't it then be natural that the constructor in the DLL would be called each time, and not only the first time?
As I've understood, a DLL is only loaded once for any program. Therefore I have also tried to use only one QPluginLoader per DLL file, with the same result. Qt also says that:
"Multiple instances of QPluginLoader can be used to access the same physical plugin." http://doc.qt.io/qt-5/qpluginloader.html#details
Therefore I can't see how this can be a source to the problem anyway.
I would really appreciate if anyone could clarify how the QPluginLoader.instance() really works. Why is the constructor – at least how it seems – only called the first time I use the instance() call?
Thank you!
Here is the code found in the DLLs (the output texts differ in A and B):
TestDLL::TestDLL()
{
std::cout << "This is written from the constructor in A \n";
}
QString TestDLL::Name() const
{
return "Hello, writing from Name() \n";
}
void TestDLL::DoSomething() const
{
qDebug() << "Hello, this text comes from DoSomething()"\n;
}

When your plugin is loaded (i.e. the first time QPluginLoader::instance() is called), then a single instance of it is created - this is your root instance. The root instance is the only instance QPluginLoader will ever create for you.
If you want more, then you create a createInstance() or clone() method for your plugin class, so that new instances can be created from the root instance. Or more conventionally, make your plugin class a factory for the class type you wish to expose.

Related

How should a Qt-based preferences panel broadcast that a pref has changed?

I am trying to design a preferences panel for my multidocument app. When a given pref changes – font size, say – all of the document windows should immediately update to reflect the new pref value. I don't want to construct the preferences panel up front, for all the document windows to connect to, because it contains a QFontComboBox that takes more than a second to set itself up (ouch); that's not a price I want to pay at startup. So then, my question is: what is an elegant design for the prefs panel to let all the document windows know about the change? In Cocoa, which I'm more used to, I'd use NSNotification to broadcast a notification from the prefs panel that all the document windows could observe; that provides the loose coupling required (since objects can add themselves as observers before the broadcaster exists).
Two approaches occur to me so far:
Loop through topLevelWidgets, do a dynamic cast to my document window class, and for all the document windows I thereby find, just call a hard-coded method on them directly.
Make a second class, PreferencesNotifier, that is separate from the UI object that takes so long to load, and construct a singleton object of this class at startup that all of the document windows can connect themselves to. When the preferences panel eventually gets created, it can send signals to slots in PreferencesNotifier, which will then call its own signals to notify the connected document windows.
Neither seems quite as elegant as NSNotification, and I'm wondering if I'm missing something. Thanks for any tips.
First thing, do not try to copy patterns, like Cocoa's NSNotification/NotificationCenter, to other frameworks (or languages, or...). There are various ways to send messages and generally each framework has picked one. Trying to use the one method that was picked by the framework you are using will lead to the most elegant solutions.
If you really want to, you could implement your own set of classes that will do exactly what NSNotification does. It will feel more elegant to you, but only because you are used to using Cocoa. It will feel odd to every other Qt developer. Also, this solution will require you to write a lot of code as you will not be able to leverage all the features of Qt.
The first solution you are suggesting is a bit ugly and looks more like a hack than anything.
What I do, when I have to handle preferences in a program, is something similar to your solution 2. I create a class that is responsible for handling all settings: read/write setting file, change setting, set default values, etc. Generally this class is a singleton. This class has very limited access to other parts of the program, and generally no access at all to the UI. Each components that needs to access the preferences will use this class. If you do it properly (e.g. use Q_PROPERTY), this class can even be accessed by QML, if you ever need to use Qt Quick.
class Settings: public QObject {
Q_OBJECT
Q_PROERTY(bool showX READ showX WRITE setShowX NOTIFY showXChanged)
public:
bool showX() const { return m_showX; }
void setShowX(bool show) {
if (show == m_showX)
return;
m_showX = show;
emit showXChanged(m_showX);
}
signals:
void showXChanged(bool);
public slots:
void save() const; // Save to disk
void load(); // Load from disk
private:
QSettings m_settings; // Handle load/save from/to disk
bool m_showX;
};
class Window {
Window() {
...
m_widgetX->setVisible(settings->showX());
connect(settings, &Settings::showXChanged,
this, [this](bool show) { m_widgetX->setVisible(show); }
);
...
}
};
class PrefWindow {
PrefWindow () {
...
ui->checkBoxShowX->setChecked(settings->showX());
...
}
private slots:
void on_saveButton_clicked() {
settings->setShowX(ui->checkBoxShowX->checked()):
settings->save();
}
};

QT: Getting text content from web page

I've been trying to start with a simple app that retrieves data from a simple HTML page upon clicking a button and stumbled upon a rather helpful tutorial on QT-Project and have been trying to implement it for my own project.
Everything manages to compile until I try to actually try to implement the loadImage function (as found in the tutorial). (I actually had to initialize m_pImgCtrl as Filedownloader * m_pImgCtrl = new FileDownloader(imageUrl, this); and I'm not exactly sure how it's suppose to work without prior object declaration?)
From what I get, m_pImgCtrl isn't actually defined in the loadImage() function since it is initialized outside of the function? Or does the connect() function do something that I'm not too aware of?
Thanks for the help!
The tutorial doesn't tell you the whole story.
The code in section Usage is supposed to be part of a class MainWindow – the controller of your main window (see line 1 of tutorial's last snippet). This class contains a slot loadImaged() called when the NetworkReply has finished. It also has a member FileDownloader * m_pImgCtrl.
For instance, the second Usage snippet could be part of a slot MainWindow::buttonClicked() like
void MainWindow::buttonClicked()
{
QUrl imageUrl("http://qt.digia.com/Documents/1/QtLogo.png");
m_pImgCtrl = new FileDownloader(imageUrl, this);
connect(m_pImgCtrl, SIGNAL(downloaded()), SLOT(loadImage()));
}

qt QTranslator reuse

I noticed that the Qt documentation is not very verbose on some of the aspects of the translations. I was fooling around with it trying to figure out their behaviour using trial & error. The ultimate goal is to get the translation changed on runtime but I am very confused as to what extent the QTranslator object can be re-used.
Consider this (where 'a' is the main instance of the application):
QTranslator translator;
translator.load("mytranslation_cz");
a.installTranslation(&translator);
(...)
a.removeTranslation(&translator)
Now the translator was removed from the application but what happened to the translator object?
In my tests when above code was followed by this again
translator.load("mytranslation_fr");
a.installTranslation(&translator);
it did not do anything in main() and it crashed the application when called from one of the widgets (using pointer to main app).
Therefore I am suspecting that I would need to create one QTranslator object per translation I want to load in the application and that I cannot reuse the QTranslator object. Am I right in this assumption?
And as a side question. Assuming the QTranslator object is untouched by the removeTranslation(), is it possible to simply install it later again like this?
QTranslator translator;
QTranslator translator1;
translator.load("mytranslation_cz");
translator1.load("mytranslation_fr");
a.installTranslation(&translator);
(...)
a.removeTranslation(&translator);
a.installTranslation(&translator1);
(...)
a.removeTranslation(&translator1);
a.installTranslation(&trasnlator); //Will this work?
Thanks for any clarification as I am somewhat confused as to what happens to the QTranslation objects when installing and removing translations from the application and especially if the QTranslation object can be reused for multiple translations somehow on runtime?
QTranslator::load basically in simple sense can be considered as a function that opens a given .qm file, reads in all the translated values and loads it in for a specific language.
Now in general operation you would not want to reuse this for many languages as by "reusing" (even if its allowed) your adding the overhead of parsing this given .qm file for every language every time you switch your UI language, which is just basically an overhead you don't need.
Your assumption of creating a QTranslator for each language is correct. As for your side question, Yes you can also re-use it. Thats the benefit of having individual QTranslator objects per translation. Just call qApp->removeTranslator() with the current translation and then qApp->installTranslator() with the new one. This way you are reusing the loaded translations as and when you please.
The way we structure this is by sub-classing QApplication and adding 2 functions
void Application::CreateTranslators() {
// translators_ is a QMap<QString, QTranslator*>
if (!translators_.isEmpty())
return;
QStringList languages;
languages << "en" << "ar" << "zh";
foreach(QString language, languages) {
QTranslator* translator = new QTranslator(instance());
translator->load(language);
translators_.insert(language, translator);
}
}
Now this function is called at the very start of the application.
2nd function is as following
void Application::SwitchLanguage(QString language) {
// current_translator_ is a QTranslator*
if (current_translator_)
removeTranslator(current_translator_);
current_translator_ = translators_.value(language, nullptr);
if (current_translator_)
installTranslator(current_translator_);
}
That's it. Using the second function you can switch your language at run-time as you please.
Couple things you'll also need to be aware of is changing QTranslator at run-time will update all translations from your .ui file strings marked as translatable automatically, however those set from code will not be. To get that you will have to override QWidget::changeEvent() and then check if the event is of type QEvent::LanguageChange and then set the required strings for that QWidget accordingly (Don't forget the tr() while doing so)

Unit Test & Log4net

I have unit test testing an action in my controller, the action writes to log4net.
When I run my action it works well - writes to log4net .
However , When I run the unit test - the action doesn't write to log4net but doesn't throw any exception.
Does anyone have a solution?
// ARRANGE
var memoryAppender = new MemoryAppender();
BasicConfigurator.Configure(memoryAppender);
// ACT
_sut.DoWhatever();
// ASSERT - using xunit - change the expression to fit your purposes
Assert.True(memoryAppender.GetEvents().Any(le => le.Level == Level.Warn), "Expected warning messages in the logs");
You don't need to add in another layer of indirection by using a logging interface (if you don't want to). I have used the abstracted way for years, but now am moving towards just using the MemoryAppender as it is testing what is actually happening. Just be sure to .Clear() the appender after each test.
Log4net does not throw exceptions: http://logging.apache.org/log4net/release/faq.html
Writing to an log on disk or in a database in a unit test is counterproductive; the whole point is automation. You shouldn't have to check the logs every time you run tests.
If you truly need to verify that a call was made to log something, you should mock the ILog interface and assert that the appropriate method was called.
If you are using a mocking framework, this is trivial. If you aren't, you can create a TestLogger class that implements or partially implements ILog and exposes extra properties that show how many times a given method was called. Your assertions will check that the methods were called as expected.
Here is an example of a class to be tested:
public class MyComponent
{
private readonly ILog _log;
public MyComponent(ILog log)
{
_log = log;
}
public string DoSomething(int arg)
{
_log.InfoFormat("Argument was [{0}]", arg);
return arg.ToString();
}
}
and the test (using Rhino.Mocks to mock the ILog):
[TestClass]
public class MyComponentTests
{
[TestMethod]
public void DoSomethingTest()
{
var logger = MockRepository.GenerateStub<ILog>();
var component = new MyComponent(logger);
var result = component.DoSomething(8);
Assert.AreEqual("8", result);
logger.AssertWasCalled(l => l.InfoFormat(Arg<string>.Is.Anything, Arg<int>.Is.Equal(8)));
}
}
Try adding:
[assembly: log4net.Config.XmlConfigurator()]
To the AssemblyInfo.cs (or init log4net any other way).
Or try using AssemblyInitialize as suggested in this answer.
It is your log4net configuration. Right now it might be in your web.config or log4net.config file in the web/bin. You have to place it in a common location and make it discoverable by both web app and test. Or you have to put it into your unittest.project=>app.config file. But if you have many test projects, it would be duplicated in number of places. So the ideal would be to put it in a common place.
Here's another possible solution if none of the other solutions work for you...
Try writing your log file to the root of the c drive. By default, I set log4net to write to the current directory which is always the directory the unit test is running from right?... wrong! I'm running windows 8 with vs 2012 using MS Unit Test, and it writes the file to a local temp directory which gets deleted after the unit test completes. In my setup it writes the file to here:
C:\Users\[myself]\AppData\Local\Temp\TestResults
Bottom line, any unit tests I write for now on, are going to use a full absolute log file path and not a relative one.

qt tr() in static variable

I have problem concerning translations in qt. All translations in my porject work fine, but one, which is in a static variable of a class. Corresponding part of code looks as follows
The header file is similar to this:
typedef struct {
int type;
QString problematicString;
} info;
MyClass::QObject_Descendant
{
Q_OBJECT;
//some functions like constructor, destructor... etc.
....
static info myClassInfo;//class that makes problems
}
and in implementation file I initialize the variable as follows:
info MyClass::myClassInfo={
1,
tr("something to be translated")
};
And whatever I do (trying with QT_TR_NOOP, then tr() and others) I cannot get myClassInfo.problematicString translated. The weirdest thing is that the text "something to be translated"
appears in *.ts file.
If someone has any hints, please share them with me. Thanks in advance.
Chris.
Static variables are instantiated (and thus, constructor code run) before your int main function is run. The translation code is set up in the QApplication constructor (I believe), which isn't run until your int main function has been entered. Thus, you are trying to get the translation of a string before the code to support it has been initialized.
To avoid this, you could either accept that the given string isn't translated and explicitly translate it every time it is used, or use the Construct on First Use idiom instead of a static member variable.

Resources