1. 程式人生 > >Qt只能執行一個例項的3種方法

Qt只能執行一個例項的3種方法

轉自 http://blog.csdn.net/robertkun/article/details/8518576

1. 共享記憶體的方法
Unix: QSharedMemory "owns" the shared memory segment. When the last thread or process that has an instance of QSharedMemory attached to a particular shared memory segment detaches from the segment by destroying its instance of QSharedMemory, the Unix kernel release the shared memory segment. But if that last thread or process crashes without running the QSharedMemory destructor, the shared memory segment survives the crash.
(據說這種方法在Linux系統下會有一個記憶體釋放的問題,在某種情況下會引起程式的異常或崩潰)

   
// 確保只執行一次   
  
QSystemSemaphore sema("JAMKey",1,QSystemSemaphore::Open);   
sema.acquire();// 在臨界區操作共享記憶體   SharedMemory   
  
QSharedMemory mem("SystemObject");// 全域性物件名   
if (!mem.create(1))// 如果全域性物件以存在則退出   
{   
    QMessageBox::information(0, MESSAGEBOXTXT,"An instance has already been running.");  
   
    sema.release();// 如果是 Unix 系統,會自動釋放。   
  
    return 0;   
}   
  
sema.release();// 臨界區

2. 使用QLocalServer和QLocalSocket類
下面是自已的寫的程式碼,主要是在執行第二例項的時候,有一個提示的作用:
1. 切換到當前程式,並將當前程式最大化顯示到前面。
2.關閉當前程式的程序,開啟新程式。
(注意:需要在你的.pro里加上QT += network)

標頭檔案

[cpp] view plain copy
#ifndef PSA_USR_LOGIN_H  
#define PSA_USR_LOGIN_H  
  
#include <QDialog>  
#include <QTimeLine>  
#include <QLocalServer>  
  
#include "ui_DlgUsrLogin.h"  
  
#define PROCESS_SHOW        1  
#define PROCESS_RESTART     2  
#define PROCESS_STOP        3  
  
const QString PRO_SHOW      = "PRO_SHOW";  
const QString PRO_RESTART   = "PRO_RESTART";  
const QString PRO_STOP      = "PRO_STOP";  
  
class CUsrLogin : public QDialog  
{  
    Q_OBJECT  
  
public:  
    CUsrLogin(const QString& serverName, QWidget* parent = NULL);  
    virtual ~CUsrLogin();  
  
    int GetIndex() const;  
    bool InitServer();  
  
signals:  
    void sig_newOrder(const QString&);  
  
private slots:  
    void slot_ok();  
    void slot_cancel();  
    void slot_btnGroupClicked(int);  
    void slot_newConnection();  
    void slot_readyRead();  
    void slot_timeFinished();  
  
private:  
    int IsServerRun(const QString & servername);  
  
private:  
    Ui::DlgUsrLogin ui;  
  
    int mIndex;  
    QString mServerName;  
    QLocalServer* mpServer;  
    QTimeLine *mpTimeLine;  
};  
  
#endif

原始檔

[cpp] view plain copy
#include "psa_usr_login.h"  
#include <QButtonGroup>  
#include <QLocalSocket>  
  
CUsrLogin::CUsrLogin( const QString& serverName, QWidget* parent /*= NULL*/ )  
    : QDialog(parent)  
    , mpServer(NULL)  
    , mpTimeLine(NULL)  
{  
    ui.setupUi(this);  
  
    mIndex = 1;  
    mServerName = serverName;  
  
    QButtonGroup* btnGroup = new QButtonGroup;  
    btnGroup->addButton(ui.btn_showCurPro,  PROCESS_SHOW);  
    btnGroup->addButton(ui.btn_openNewPro,  PROCESS_RESTART);  
  
    ui.progressBar->setVisible(false);  
    mpTimeLine = new QTimeLine(3000, this);  
    mpTimeLine->setFrameRange(0, 100);  
    connect(mpTimeLine, SIGNAL(frameChanged(int)), ui.progressBar, SLOT(setValue(int)));  
    connect(mpTimeLine, SIGNAL(finished()), this, SLOT(slot_timeFinished()));  
  
    connect(btnGroup, SIGNAL(buttonPressed(int)), this, SLOT(slot_btnGroupClicked(int)));  
    connect(ui.btn_ok, SIGNAL(clicked()), this, SLOT(slot_ok()));  
    connect(ui.btn_cancel, SIGNAL(clicked()), this, SLOT(slot_cancel()));  
}  
  
CUsrLogin::~CUsrLogin()  
{  
    if(mpServer)  
    {  
        QLocalServer::removeServer(mServerName);  
        delete mpServer;  
        mpServer = NULL;  
    }  
  
    if(mpTimeLine)  
    {  
        mpTimeLine->stop();  
        delete mpTimeLine;  
        mpTimeLine = NULL;  
    }  
}  
  
int CUsrLogin::GetIndex() const  
{  
    return mIndex;  
}  
  
void CUsrLogin::slot_ok()  
{  
    QLocalSocket ls;  
    ls.connectToServer(mServerName);  
  
    if (ls.waitForConnected())  
    {  
        switch(mIndex)  
        {  
        case PROCESS_SHOW:  
            {  
                char msg[25] = {0};  
                memcpy(msg, PRO_SHOW.toStdString().c_str(), PRO_SHOW.length());  
                ls.write(msg);  
                ls.waitForBytesWritten();  
  
                QDialog::accept();  
                break;  
            }  
        case PROCESS_RESTART:  
            {  
                char msg[25] = {0};  
                memcpy(msg, PRO_RESTART.toStdString().c_str(), PRO_RESTART.length());  
                ls.write(msg);  
                ls.waitForBytesWritten();  
  
                ui.progressBar->setVisible(true);  
                mpTimeLine->start();  
                break;  
            }  
        case PROCESS_STOP:  
            {  
                char msg[25] = {0};  
                memcpy(msg, PRO_STOP.toStdString().c_str(), PRO_STOP.length());  
                ls.write(msg);  
                ls.waitForBytesWritten();  
  
                QDialog::accept();  
                break;  
            }  
        default:  
            QDialog::accept();  
            break;  
        }  
    }  
}  
  
void CUsrLogin::slot_cancel()  
{  
    QDialog::reject();  
}  
  
void CUsrLogin::slot_btnGroupClicked( int idx)  
{  
    mIndex = idx;  
}  
  
void CUsrLogin::slot_readyRead()  
{  
    QLocalSocket *local = static_cast<QLocalSocket *>(sender());  
    if (!local)  
        return;  
  
    QTextStream in(local);  
    QString     readMsg;  
  
    readMsg = in.readAll();  
  
    emit sig_newOrder(readMsg);  
}  
  
// 判斷是否有一個同名的伺服器在執行  
int CUsrLogin::IsServerRun(const QString & servername)  
{          
    QLocalSocket ls;  
    ls.connectToServer(servername);  
  
    if (ls.waitForConnected(1000))   
    {  
        ls.disconnectFromServer();  
        ls.close();  
        return 1;  
    }  
  
    return 0;  
}  
  
bool CUsrLogin::InitServer()  
{  
    if (!IsServerRun(mServerName))  
    {  
        mpServer = new QLocalServer;  
        QLocalServer::removeServer(mServerName);  
  
        mpServer->listen(mServerName);  
        connect(mpServer, SIGNAL(newConnection()), this, SLOT(slot_newConnection()));  
  
        return true;  
    }  
  
    return false;  
}  
  
void CUsrLogin::slot_newConnection()  
{  
    QLocalSocket *newsocket = mpServer->nextPendingConnection();  
    connect(newsocket, SIGNAL(readyRead()), this, SLOT(slot_readyRead()));  
}  
  
void CUsrLogin::slot_timeFinished()  
{  
    if(InitServer())  
    {  
        QDialog::accept();  
    }  
}  


主函式:
在主函式中新增CUsrLogin.. 和 訊號槽函式。

[cpp] view plain copy
int main(int argc, char * argv[])  
{  
    QApplication app(argc, argv);  
  
    QString name = "******"; // 自定義程式名稱   
    if(!login.InitServer())  
    {  
        int ret = login.exec();  
        if(QDialog::Accepted == ret)  
        {  
            if( login.GetIndex() == PROCESS_SHOW ||  
                login.GetIndex() == PROCESS_STOP )  
            {  
                return 1;  
            }  
        }  
        else  
        {  
            return 1;  
        }   
    MainWindow mainWin;  
    mainWin.show();  
  
}  

MainWindow中實現:
[cpp] view plain copy
 
{  
    if(order == PRO_SHOW)  
    {  
        #ifdef WIN32  
        {  
            activateWindow();  
            showMinimized();  
            showMaximized();  
        }  
        #else  
        {  
            activateWindow();  
  
            if(windowState () & Qt::WindowMinimized)  
            {     
                    setWindowState(windowState() & ~Qt::WindowMinimized | Qt::WindowActive);  
                    showMaximized();  
                    show();  
            }  
            else if(windowState() & Qt::WindowMaximized)  
            {     
                    setWindowState(windowState() & Qt::WindowMaximized | Qt::WindowActive);  
            }     
            else  
            {     
                    setWindowState(windowState() & Qt::WindowMaximized | Qt::WindowActive);  
            }  
        }  
        #endif  
    }  
    else if(order == PRO_RESTART)  
    {  
        close();  
    }  
    else if(order == PRO_STOP)  
    {  
        close();  
    }  
} 


3. QSingleApplication類
實現原理應該和QLocalServer和QLocalSocket相同。
使用Qt中的QSharedMemory,QLocalServer和QLocalSocket實現(不過需要在你的.pro里加上QT += network)
具體說明如下:
一介紹
通過編寫一個QSingleApplication類,來實現Qt程式的單例化,原文的作者是在Windows Vista +Qt4.4 下實現的,不過應用在其他平臺上是沒問題的。(本文是我在http://www.qtcentre.org/wiki/index.php?title=SingleApplication上看到的)

二程式碼
方案一:使用Qt中的QSharedMemory,QLocalServer和QLocalSocket實現(不過需要在你的.pro里加上QT += network)

別的沒翻譯,就是大概說了一下,直接來程式碼吧:

// "single_application.h"

#ifndef SINGLE_APPLICATION_H
#define SINGLE_APPLICATION_H

#include <QApplication>
#include <QSharedMemory>
#include <QLocalServer>

class SingleApplication : public QApplication

{
	Q_OBJECT
public:

	SingleApplication(int &argc, char *argv[], const QString uniqueKey);
	bool isRunning();
	bool sendMessage(const QString &message);
public slots:
	void receiveMessage();
signals:
	void messageAvailable(QString message);
private:
	bool _isRunning;
	QString _uniqueKey;
	QSharedMemory sharedMemory;
	QLocalServer *localServer;
	static const int timeout = 1000;
};

#endif // SINGLE_APPLICATION_H

// "single_application.cpp"
#include <QLocalSocket>
#include "single_application.h"

SingleApplication::SingleApplication(int &argc, char *argv[], const QString uniqueKey) : QApplication(argc, argv), _uniqueKey(uniqueKey)

{
	sharedMemory.setKey(_uniqueKey);
	if (sharedMemory.attach())
		_isRunning = true;
	else
	{
		_isRunning = false;
		// create shared memory.
		if (!sharedMemory.create(1))
		{
			qDebug("Unable to create single instance.");
			return;
		}

		// create local server and listen to incomming messages from other instances.
		localServer = new QLocalServer(this);
		connect(localServer, SIGNAL(newConnection()), this, SLOT(receiveMessage()));
		localServer->listen(_uniqueKey);
	}
}

// public slots.
void SingleApplication::receiveMessage()
{
	QLocalSocket *localSocket = localServer->nextPendingConnection();
	if (!localSocket->waitForReadyRead(timeout))
	{
		qDebug(localSocket->errorString().toLatin1());
		return;
	}

	QByteArray byteArray = localSocket->readAll();
	QString message = QString::fromUtf8(byteArray.constData());
	emit messageAvailable(message);
	localSocket->disconnectFromServer();
}

// public functions.
bool SingleApplication::isRunning()
{
	return _isRunning;
}

bool SingleApplication::sendMessage(const QString &message)
{
	if (!_isRunning)
		return false;
	QLocalSocket localSocket(this);
	localSocket.connectToServer(_uniqueKey, QIODevice::WriteOnly);
	if (!localSocket.waitForConnected(timeout))
	{
		qDebug(localSocket.errorString().toLatin1());
		return false;
	}

	localSocket.write(message.toUtf8());
	if (!localSocket.waitForBytesWritten(timeout))
	{
		qDebug(localSocket.errorString().toLatin1());
		return false;
	}

	localSocket.disconnectFromServer();
	return true;


方案二:使用Qt中的QSharedMemory,和QTimert實現,別的也沒翻譯,還是直接來程式碼吧:

// "single_application.h"
#ifndef SINGLE_APPLICATION_H
#define SINGLE_APPLICATION_H

#include <QApplication>
#include <QSharedMemory>

class SingleApplication : public QApplication
{
	Q_OBJECT
public:
	SingleApplication(int &argc, char *argv[], const QString uniqueKey);
	bool isRunning();
	bool sendMessage(const QString &message);
public slots:
	void checkForMessage();
signals:
	void messageAvailable(QString message);
private:
	bool _isRunning;
	QSharedMemory sharedMemory;
};

#endif // SINGLE_APPLICATION_H

// "single_application.cpp"
#include <QTimer>
#include <QByteArray>
#include "single_application.h"

SingleApplication::SingleApplication(int &argc, char *argv[], const QString uniqueKey) : QApplication(argc, argv)
{
	sharedMemory.setKey(uniqueKey);
	if (sharedMemory.attach())
		_isRunning = true;
	else
	{
		_isRunning = false;
		// attach data to shared memory.
		QByteArray byteArray("0"); // default value to note that no message is available.
		if (!sharedMemory.create(byteArray.size()))
		{
			qDebug("Unable to create single instance.");
			return;
		}
		sharedMemory.lock();
		char *to = (char*)sharedMemory.data();
		const char *from = byteArray.data();
		memcpy(to, from, qMin(sharedMemory.size(), byteArray.size()));
		sharedMemory.unlock();

                // start checking for messages of other instances.
		QTimer *timer = new QTimer(this);
		connect(timer, SIGNAL(timeout()), this, SLOT(checkForMessage()));
		timer->start(1000);
	}
}

// public slots.
void SingleApplication::checkForMessage()
{
	sharedMemory.lock();
	QByteArray byteArray = QByteArray((char*)sharedMemory.constData(), sharedMemory.size());
	sharedMemory.unlock();
	if (byteArray.left(1) == "0")
		return;
	byteArray.remove(0, 1);
	QString message = QString::fromUtf8(byteArray.constData());
	emit messageAvailable(message);

        // remove message from shared memory.
	byteArray = "0";
	sharedMemory.lock();
	char *to = (char*)sharedMemory.data();
	const char *from = byteArray.data();
	memcpy(to, from, qMin(sharedMemory.size(), byteArray.size()));
	sharedMemory.unlock();
}

// public functions.
bool SingleApplication::isRunning()
{
	return _isRunning;
}

bool SingleApplication::sendMessage(const QString &message)
{
	if (!_isRunning)
		return false;

	QByteArray byteArray("1");
	byteArray.append(message.toUtf8());
	byteArray.append('/0'); // < should be as char here, not a string!
	sharedMemory.lock();
	char *to = (char*)sharedMemory.data();
	const char *from = byteArray.data();
	memcpy(to, from, qMin(sharedMemory.size(), byteArray.size()));
	sharedMemory.unlock();
	return true;
}


三使用

// "main.cpp"
#include "single_application.h"
int main(int argc, char *argv[])
{
	SingleApplication app(argc, argv, "some unique key string");
	if (app.isRunning())
	{
		app.sendMessage("message from other instance.");
		return 0;
	}

	MainWindow *mainWindow = new MainWindow();

        // connect message queue to the main window.
	QObject::connect(&app, SIGNAL(messageAvailable(QString)), mainWindow, SLOT(receiveMessage(QString)));

        // show mainwindow.
	mainWindow->show();
	return app.exec();

}