讓Qt應用程序只有一個實例
在實際應用中,我們經(jīng)常需要讓應用程序只有一個實例,再打開新的文檔或者頁面時,只是替換現(xiàn)在的窗口或者新打開一個標簽,而不是重新啟動一次應用程序。Qt中是否可以做到這樣呢,答案是肯定的,因為Qt本身可以直接調(diào)用系統(tǒng)API,肯定可以做到,但是我們希望找到一個跨平臺的通用的解決方案。
這就要用到Qt的QLocalSocket,QLocalServer類了,這兩個類從接口上看和網(wǎng)絡通信socket沒有區(qū)別,但是它并不是真正的網(wǎng)絡API,只是模仿了而已。這兩個類在Unix/Linux系統(tǒng)上采用Unix域socket實現(xiàn),而在Windows上則采用有名管道(named pipe)來實現(xiàn)。
既然是網(wǎng)絡API,那么思路就很簡單了,應用程序啟動時首先會去連一個服務器(這里通過應用程序的名字來標識,就像網(wǎng)絡端口一樣),如果連接失敗,那么則自己是第一個實例,就創(chuàng)建這么一個服務器,否則將啟動參數(shù)發(fā)送到服務器,然后自動退出,而服務器會在收到通知以后進行處理。
這些動作我想最好是放在創(chuàng)建Application實例后,因為Qt本身有很多操作沒有Application實例是無法進行操作的(至少事件循環(huán)是在創(chuàng)立Application以后才能啟動吧),因此最好的位置就是通過繼承QApplicaiton或者QCoreApplication自定義一個YourOwnApplication,然后在構(gòu)造函數(shù)中進行,下面是一個示意。
首先是YourOwnApplication構(gòu)造函數(shù):
- QString serverName = QCoreApplication::applicationName();
- QLocalSocket socket;
- socket.connectToServer(serverName);
- if (socket.waitForConnected(500)) { //如果能夠連接得上的話,將參數(shù)發(fā)送到服務器,然后退出
- QTextStream stream(&socket);
- QStringList args = QCoreApplication::arguments();
- if (args.count() > 1)
- stream << args.last();
- else
- stream << QString();
- stream.flush();
- socket.waitForBytesWritten();
- qApp->quit();
- return;
- }
- //運行到這里,說明沒有實例在運行,那么創(chuàng)建服務器。
- m_localServer = new QLocalServer(this);
- connect(m_localServer, SIGNAL(newConnection()),
- this, SLOT(newLocalSocketConnection())); //監(jiān)聽新到來的連接
- if (!m_localServer->listen(serverName)) {
- if (m_localServer->serverError() == QAbstractSocket::AddressInUseError
- && QFile::exists(m_localServer->serverName())) { //確保能夠監(jiān)聽成功
- QFile::remove(m_localServer->serverName());
- m_localServer->listen(serverName);
- }
- }
這樣就保證了新啟動的程序在檢測到有其他實例在運行時就會自動退出,但是它發(fā)出的請求還沒有被處理,下面看一下處理函數(shù),也就是前段代碼中的newLocalSocketConnection()。
- QLocalSocket *socket = m_localServer->nextPendingConnection();
- if (!socket)
- return;
- socket->waitForReadyRead(1000);
- QTextStream stream(socket);
- … … //其他處理
- delete socket;
- mainWindow()->raise();
- mainWindow()->activateWindow(); //記得激活窗口哦
【編輯推薦】