Code Monkey home page Code Monkey logo

Comments (19)

jonaski avatar jonaski commented on June 2, 2024 1

Templates and Qt with MOC do not mix sadly.

You can create a base class that inherits QObject, and a templated non-Q_OBJECT derived class which inherits the base class.

from singleapplication.

itay-grudev avatar itay-grudev commented on June 2, 2024
  • It enforces that SingleApplication is the first thing initialized and run. Because it may terminate your app, it's better for it to have priority and run all of its code before anything else.
  • For convenience - every Qt app needs an event loop which requires calling QxApplication which needs to be the first thing initialized, so to allow SingleApplication to run immediately afterwards, merging them in one is convenient.
  • To closely resemble the way QtSingleApplication works, so migration is not hard.

from singleapplication.

Scanframe avatar Scanframe commented on June 2, 2024

Thanks for te reply.
All arguments may be true, but with a class like SingleInstance the SingleApplication class can be build too.
Instead of using a define, I would have implemented all Q[|Gui|Core]Application types using the SingleInstance class as a data member and so not having duplicate code.
Finally having classes SingleGuiApplication, SingleCoreApplication and SingleApplication in the library.
Making the library more versatile and easier to use.

I forked your repo into SingleInstance and transformed it using Clion. (CMake only)

Check it out please?
Maybe you see the merits in my suggest approch :)

Currently adding a compatible SingleApplication class as suggested (...)
And just finished and pushed :)

from singleapplication.

Shatur avatar Shatur commented on June 2, 2024

Is your only problem with define?
If yes, we could turn SingleApplication into a templated class.

from singleapplication.

Scanframe avatar Scanframe commented on June 2, 2024

Templates and Qt with MOC do not mix sadly.

from singleapplication.

Shatur avatar Shatur commented on June 2, 2024

Oh, right, completely forgot about it.
I looked at your code and actually your approach makes sense to me. I would even don't make applications instances and use the class directly.

from singleapplication.

Scanframe avatar Scanframe commented on June 2, 2024

Indeed

I would even don't make applications instances and use the class directly.

Currently I'm building a cross platform modular application
as I did back in 2004 using C+ Builder 5/6 but now in Qt.

I want to be able to configure in the application itself if only a single instance is allowed.
This is because the application drives hardware where only one application at the time can connect to to acquired data and drive a motion controller.

On the same system the same application with a different configuration is used read the acquired data but that one (not connected to hardware) can have multiple instances.

By the way the the application uses global shortcuts to allow a motion controller emergency stop even when the app has no input focus. Therefore you can not have multiple instance of the application either.

from singleapplication.

Shatur avatar Shatur commented on June 2, 2024

I agree with your idea of having independent class that manages messaging. This just feels right because we don't need anything from Q[|Gui|Core]Application, but let's see what @itay-grudev and @jonaski think about it.

from singleapplication.

itay-grudev avatar itay-grudev commented on June 2, 2024

from singleapplication.

Scanframe avatar Scanframe commented on June 2, 2024

Seems that I needed to break the constructor in 2 parts to be able to use it in my application.
To conform to Qt's all QObject derived classes have default a constructor passing a single QObject* parent argument.

	explicit SingleInstance(QObject* parent = nullptr);

	explicit SingleInstance(bool allowSecondary, Options options = Mode::User, 
		int timeout = 1000, const QString& userData = {}, QObject* parent = nullptr);

	void initialize(bool allowSecondary = false, Options options = Mode::User, int timeout = 1000, 
		const QString& userData = {});

Enfin, I think this finally is it, but maybe a namespace to wrap it all in would also not harm :)

from singleapplication.

Shatur avatar Shatur commented on June 2, 2024

Seems that I needed to break the constructor in 2 parts to be able to use it in my application.

Why? It's completely okay to have a class that accepts parent as a last parameter.

from singleapplication.

Scanframe avatar Scanframe commented on June 2, 2024

Why? It's completely okay to have a class that accepts parent as a last parameter.

True.
But I need to read settings from a config file in my Application class to pass to the SingleInstance object.
I do not read application settings (QSettings) while constructing.
But I want to setup connections to the signals.

So the constructor explicit SingleInstance(QObject* parent = nullptr) is used to create
and further on a call is made to initialize(...) with the required settings.

Having an initialize(...) method allows for a bit more flexibility.

from singleapplication.

Shatur avatar Shatur commented on June 2, 2024

You can use heap if you want to initialize it later or just recreate the object. initialize looks hacky.

from singleapplication.

Scanframe avatar Scanframe commented on June 2, 2024

You can use heap

You can, but I prefer the separation.
As I said like to do signal connections in the constructor.
And when the SingleInstance instance is not created in the constructor,
I need to make the signal connection somewhere else.
This make it less readable to me.

Also familiar with FetchContent feature in CMake?

My file cmake/SingleInstanceConfig.cmake to import my forked repo in my current project.

# FetchContent added in CMake 3.11, downloads during the configure step
include(FetchContent)
# Import SingleInstance library.
FetchContent_Declare(
	SingleInstance
	GIT_REPOSITORY https://github.com/Scanframe/SingleInstance.git
#	GIT_TAG v????
)
# Adds SingleInstance::SingleInstance
FetchContent_MakeAvailable(SingleInstance)

In CMakeLists.txt file for example?

find_package(SingleInstance REQUIRED)

target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets SingleInstance)

from singleapplication.

Shatur avatar Shatur commented on June 2, 2024

You can, but I prefer the separation.

It is difficult for me to understand your problem without context.

Also familiar with FetchContent feature in CMake?

Yes, I am. But what are you trying to say?

If you want to upstream something - send changes peace by peace. You can't refactor the whole library at once and upstream it because it impossible to review the changes.
We liked the idea of having a separate class to control the behavior instead of application class inheritance. If you want to upstream it - try to make minimal possible changes and send a PR with them. And then we can discuss / review other improvements.

from singleapplication.

Scanframe avatar Scanframe commented on June 2, 2024

Well I'm done with the changes for now.
Its working in my Application :)

It is difficult for me to understand your problem without context.

Is this below the context you seek?

Application Constructor

Connecting a handler for messages from secondary instances.

If you wonder about some "weird" coding.
It is probably because some of my base libraries are deliberate only STL dependent.

Application::Application(int& argc, char** argv, int flags)
	:QApplication(argc, argv, flags)
	 , _singleInstance(new SingleInstance(this))
	 , _sustainInterval(500)
{
	// Call some static methods.
	setWindowIcon(QIcon(":logo/ico/scanframe"));
	// Ignore desktop settings because it gives the wrong icon colors.
	setDesktopSettingsAware(false);
	// Install a handler for calls to getConfigLocation() .
	setConfigLocationHandler(ConfigLocationClosure().assign(this, &Application::ConfigLocationHandler, std::placeholders::_1));
	// Initialize using the application file path get the binary name.
	QFileInfo fi(sf::Application::applicationFilePath());
	// Set the instance to change the extension.
	fi.setFile(QString::fromStdString(sf::getConfigLocation()), fi.completeBaseName() + ".ini");
	// Create settings instance.
	_settings = new QSettings(fi.absoluteFilePath(), QSettings::Format::IniFormat, QApplication::instance());
	//_settings = new QSettings(QSettings::Scope::UserScope, QCoreApplication::organizationName(), QCoreApplication::applicationName());
	// Read the settings.
	settingsReadWrite(false);
	// Set the global settings for indirect opened dialogs to save and restore size and/or position.
	setGlobalSettings(_settings);
	// Create module configuration
	_moduleConfiguration = new ModuleConfiguration(_settings, this);
	// Connect the message handler for opening files passed on the other application.
	connect(_singleInstance, &SingleInstance::receivedMessage, this, &Application::handleInstanceMessage);
	// When libraries are loaded create the module instances.
	connect(_moduleConfiguration, &ModuleConfiguration::libraryLoaded, [&](bool startup)
	{
		// Create the interface implementations (that are missing).
		AppModuleInterface::instantiate(_settings, this);
		// When not starting up the library is loaded from the dialog.
		if (!startup)
		{
			// Then initialization stages need to be called on the new loaded library.
			AppModuleInterface::initializeInstances(AppModuleInterface::Initialize);
			AppModuleInterface::initializeInstances(AppModuleInterface::Finalize);
		}
	});
	// Set the file path to the application settings instance for the style sheet file watcher.
	appSettings.setFilepath(fi.absoluteFilePath(), true);
	// Install the sustain timer.
	setSustainTimer(_sustainInterval);
}

Application initialization method

Is called multiple times when starting up which has to do with dependencies between modules.

void Application::initialize(AppModuleInterface::InitializeStage stage)
{
	if (stage)
	{
		// Initialize and/or check if application is allowed to run.
		if (stage == AppModuleInterface::Initialize)
		{
			// exit() is called when not allowed. (TODO: Exception is preferred since it does destructor cleanup.)
			_singleInstance->initialize(_allowSecondary);
		}
		// Load the missing modules from the configuration and pass true for this load is at startup.
		_moduleConfiguration->load(true);
		// Create the main window when not done already.
		if (!_mainWindow)
		{
			_mainWindow = new MainWindow(_settings, this);
		}
	}
	// Initializes all module instances.
	AppModuleInterface::initializeInstances(stage);
}

main.cpp

int main(int argc, char* argv[])
{
	Q_INIT_RESOURCE(resource);
	// Initialize the application with some defaults.
	Application::setOrganizationName("Scanframe");
	Application::setApplicationName("Modular Application");
	Application::setApplicationDisplayName("Modular Base Application");
	Application::setApplicationVersion(QT_VERSION_STR);
	Application app(argc, argv);
	// This call exists the application when
	app.parseCommandline();
	app.initialize(AppModuleInterface::Initialize);
	app.getMainWindow()->show();
	app.initialize(AppModuleInterface::Finalize);
	app.processCommandLine();
	auto rv = Application::exec();
	app.initialize(AppModuleInterface::Uninitialize);
	return rv;
}

Configuration Property Sheet

This property sheet gets all the pages from all dynamic loaded modules.
The Application is the one always present containing a setting
to allow multiple application instances.

image

from singleapplication.

Shatur avatar Shatur commented on June 2, 2024

Personally I'm not a big fan of methods like initialize. In my opinion, they make the code reading worse.
I would just recreate _singleInstance and call connect for it in Application::initialize. But it's up to you.

I think the question can be considered closed. Feel free to open a new issue if you have other questions to ask.

from singleapplication.

Scanframe avatar Scanframe commented on June 2, 2024

You can create a base class that inherits QObject, and a templated non-Q_OBJECT derived class which inherits the base class.

I did that too already with another class.

Personally I'm not a big fan of methods like initialize. In my opinion, they make the code reading worse.
I would just recreate _singleInstance and call connect for it in Application::initialize. But it's up to you.

But then again you need to use the pointer as a sentry which can easily be avoided to construct most stuff in the constructor which already has the name for it :)
Constructors and exceptions is also a tricky thing to intercept it and to handle it.
From the initialize() method you know at least that the class (Application) you called it from
was fully constructed and has no surprises there.

But as you can see I comment almost every line of code when it matters.
So you can read between the lines of code what is the actually purpose.
Many times I wrote in the comment what the plan was but the code did something different.

So in my opinion put code in the place where it logically belongs, not just because you
like it, that all of it is neatly grouped together in a single function, and just document it in the comment.

from singleapplication.

Scanframe avatar Scanframe commented on June 2, 2024

You can create a base class that inherits QObject, and a templated non-Q_OBJECT derived class which inherits the base class.

Yes, but no new signals or slots from the template.
My solution I used is a template having QObject data member to attach signals to and use
this template in multiple inheritance situation on a QWidget derived class.

Lets face it, what is C++ if you do not use templates and multiple inheritance? :)

from singleapplication.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.