PDA

View Full Version : Send and receive a signal between 2 Qt applications.



hakermania
5th February 2011, 12:33
I am running ubuntu linux 10.10.

Well, my program runs only on one instance, so what I need is to open my application's project file while the application is already running. By clicking this project file, the new program instance realize that there is already one running and it shows a message that the program is already open. What I want is to open the project file with the already running process. So, I want
1) If the program is already running and
2) there are arguments (a project file has been opened)
to open the project file with the already running process.

So, the new instance may send the URL of the project file to the already running process and the already running process open it. I have not idea how to do this. Maybe System-Wide Signal-Slot ?

Sorry if this is not a Newbie question. Fell free to move it to the Programming talk.

squidge
5th February 2011, 12:48
If you use QtSingleApplication, you can use isRunning and sendMessage methods of that class. isRunning will tell you if another copy is running in memory, whilst sendMessage will allow you to talk to that instance. The already running instance will receive the signal messageReceived.

docs: http://doc.qt.nokia.com/solutions/4/qtsingleapplication/qtsingleapplication.html

You can download QtSingleApplication from the GIT repository: http://qt.gitorious.org/qt-solutions

hakermania
5th February 2011, 13:46
Hey thanks, I'm not using QtSingleApplication!

tbscope
5th February 2011, 13:54
You might want to look into Inter Process Communication (IPC)

A popular choice is a TCP socket.
Or shared memory.
Depending on the OS, there's also DBus

jfjf
5th February 2011, 13:59
I am using pyQt but maybe this can help you to find a solution also in C++.
I am implementing my SingleInstance Applications with the help of QSharedMemory, QLocalSocket and QLocalServer


#import std
import os, sys

# import stuff for ipc
import getpass, pickle

# Import Qt modules
from PyQt4.QtGui import QApplication
from PyQt4.QtCore import QSharedMemory, QIODevice, SIGNAL
from PyQt4.QtNetwork import QLocalServer, QLocalSocket

class SingletonApp(QApplication):

timeout = 1000

def __init__(self, argv, application_id=None):
QApplication.__init__(self, argv)

self.socket_filename = unicode(os.path.expanduser("~/.ipc_%s"
% self.generate_ipc_id()) )
self.shared_mem = QSharedMemory()
self.shared_mem.setKey(self.socket_filename)

if self.shared_mem.attach():
self.is_running = True
return

self.is_running = False
if not self.shared_mem.create(1):
print >>sys.stderr, "Unable to create single instance"
return
# start local server
self.server = QLocalServer(self)
# connect signal for incoming connections
self.connect(self.server, SIGNAL("newConnection()"), self.receive_message)
# if socket file exists, delete it
if os.path.exists(self.socket_filename):
os.remove(self.socket_filename)
# listen
self.server.listen(self.socket_filename)

def __del__(self):
self.shared_mem.detach()
if not self.is_running:
if os.path.exists(self.socket_filename):
os.remove(self.socket_filename)


def generate_ipc_id(self, channel=None):
if channel is None:
channel = os.path.basename(sys.argv[0])
return "%s_%s" % (channel, getpass.getuser())

def send_message(self, message):
if not self.is_running:
raise Exception("Client cannot connect to IPC server. Not running.")
socket = QLocalSocket(self)
socket.connectToServer(self.socket_filename, QIODevice.WriteOnly)
if not socket.waitForConnected(self.timeout):
raise Exception(str(socket.errorString()))
socket.write(pickle.dumps(message))
if not socket.waitForBytesWritten(self.timeout):
raise Exception(str(socket.errorString()))
socket.disconnectFromServer()

def receive_message(self):
socket = self.server.nextPendingConnection()
if not socket.waitForReadyRead(self.timeout):
print >>sys.stderr, socket.errorString()
return
byte_array = socket.readAll()
self.handle_new_message(pickle.loads(str(byte_arra y)))

def handle_new_message(self, message):
print "Received:", message

# Create a class for our main window
class Main(QtGui.QMainWindow):
def __init__(self):
QtGui.QMainWindow.__init__(self)
# This is always the same
self.ui=Ui_MainWindow()
self.ui.setupUi(self)

if __name__ == "__main__":
app = SingletonApp(sys.argv)
if app.is_running:
# send arguments to running instance
app.send_message(sys.argv)
else:
MyApp = Main()
MyApp.show()
sys.exit(app.exec_())

hakermania
5th February 2011, 18:15
Thank you all for the interest. I am interested in DBUS (because there is QDbus lib), but I'll see other solutions as well! Thank you :)

Added after 1 12 minutes:

I am tryin' got figure it out with QDBus. I was able to send a DBus message(I was able to see the "HELLO" string with dbus-monitor) with this code:


QDBusMessage msg = QDBusMessage::createSignal("/", "open.album", "message");
msg << "HELLO";
QDBusConnection::sessionBus().send(msg);

Where open.album.xml is

<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="open.album">
<signal name="message">
<arg name="album" type="s" direction="out"/>
</signal>
<signal name="action">
<arg name="album" type="s" direction="out"/>
</signal>
</interface>
</node>

Ok, all right till now, But I've no Idea how will I tell the other application to listen to the specific interface for a specific string and do things with this string!
Any help?

hakermania
5th February 2011, 20:18
Ok, it is simple.


//this set it ready to receive a message to a specific chanell, once received, album_slot() will run
QDBusConnection::sessionBus().connect(QString(),QS tring(), "open.album", "MY_message", this, SLOT(album_slot(QString)));
//these 3 lines create a signal and send it to DBus
QDBusMessage msg = QDBusMessage::createSignal("/", "open.album", "MY_message");
msg="text"
QDBusConnection::sessionBus().send(msg);
...
...
void album_slot(const QString &text){
if(text=="text") etc
}