1.功能介绍
沙箱软件的增强版本采用Qt架构开发,核心模块与经典版本相同,本文主要介绍SandMan.exe这个主程序代码。在main.cpp这个入口函数里,有主窗口入口,如下所示:
int main(int argc, char *argv[])
{
......省略一部分初始化代码
CSandMan* pWnd = new CSandMan();
QObject::connect(&app, SIGNAL(messageReceived(const QString&)), pWnd, SLOT(OnMessage(const QString&)));
int ret = app.exec();
delete pWnd;
delete theConf;
theConf = NULL;
return ret;
}
CSandMan这个类继承至QMainWindow,是Qt典型的主窗口。查看主窗口函数的构造函数:
CSandMan::CSandMan(QWidget *parent)
: QMainWindow(parent)
{
#if defined(Q_OS_WIN)
MainWndHandle = (HWND)QWidget::winId();
QApplication::instance()->installNativeEventFilter(new CNativeEventFilter);
#endif
CArchive::Init();
theGUI = this;
m_DarkTheme = false;
m_FusionTheme = false;
QDesktopServices::setUrlHandler("http", this, "OpenUrl");
QDesktopServices::setUrlHandler("https", this, "OpenUrl");
QDesktopServices::setUrlHandler("sbie", this, "OpenUrl");
m_StartMenuUpdatePending = false;
m_ThemeUpdatePending = false;
m_DefaultStyle = QApplication::style()->objectName();
m_DefaultPalett = QApplication::palette();
m_DefaultFontSize = QApplication::font().pointSizeF();
m_DarkPalett.setColor(QPalette::Light, QColor(96, 96, 96));
m_DarkPalett.setColor(QPalette::Midlight, QColor(64, 64, 64));
m_DarkPalett.setColor(QPalette::Mid, QColor(48, 48, 48));
m_DarkPalett.setColor(QPalette::Dark, QColor(53, 53, 53));
m_DarkPalett.setColor(QPalette::Shadow, QColor(25, 25, 25));
m_DarkPalett.setColor(QPalette::Window, QColor(53, 53, 53));
m_DarkPalett.setColor(QPalette::WindowText, Qt::white);
m_DarkPalett.setColor(QPalette::Base, QColor(25, 25, 25));
m_DarkPalett.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
m_DarkPalett.setColor(QPalette::ToolTipBase, Qt::lightGray);
m_DarkPalett.setColor(QPalette::ToolTipText, Qt::white);
m_DarkPalett.setColor(QPalette::Text, Qt::white);
m_DarkPalett.setColor(QPalette::Button, QColor(53, 53, 53));
m_DarkPalett.setColor(QPalette::ButtonText, Qt::white);
m_DarkPalett.setColor(QPalette::BrightText, Qt::red);
m_DarkPalett.setColor(QPalette::Link, QColor(218, 130, 42));
m_DarkPalett.setColor(QPalette::LinkVisited, QColor(218, 130, 42));
m_DarkPalett.setColor(QPalette::Highlight, QColor(42, 130, 218));
m_DarkPalett.setColor(QPalette::HighlightedText, Qt::black);
m_DarkPalett.setColor(QPalette::PlaceholderText, QColor(96, 96, 96));
m_DarkPalett.setColor(QPalette::Disabled, QPalette::WindowText, Qt::darkGray);
m_DarkPalett.setColor(QPalette::Disabled, QPalette::Text, Qt::darkGray);
m_DarkPalett.setColor(QPalette::Disabled, QPalette::Light, Qt::black);
m_DarkPalett.setColor(QPalette::Disabled, QPalette::ButtonText, Qt::darkGray);
LoadLanguage();
if (!theConf->IsWritable()) {
QMessageBox::critical(this, "Sandboxie-Plus", tr("WARNING: Sandboxie-Plus.ini in %1 cannot be written to, settings will not be saved.").arg(theConf->GetConfigDir()));
}
m_bExit = false;
theAPI = new CSbiePlusAPI(this);
connect(theAPI, SIGNAL(StatusChanged()), this, SLOT(OnStatusChanged()));
connect(theAPI, SIGNAL(BoxAdded(const CSandBoxPtr&)), this, SLOT(OnBoxAdded(const CSandBoxPtr&)));
connect(theAPI, SIGNAL(BoxClosed(const CSandBoxPtr&)), this, SLOT(OnBoxClosed(const CSandBoxPtr&)));
connect(theAPI, SIGNAL(BoxCleaned(CSandBoxPlus*)), this, SLOT(OnBoxCleaned(CSandBoxPlus*)));
QString appTitle = tr("Sandboxie-Plus v%1").arg(GetVersion());
this->setWindowTitle(appTitle);
setAcceptDrops(true);
m_pBoxBorder = new CBoxBorder(theAPI, this);
m_SbieTemplates = new CSbieTemplates(theAPI, this);
m_bConnectPending = false;
m_bStopPending = false;
m_pUpdater = new COnlineUpdater(this);
m_pMainWidget = new QWidget(this);
m_pMenuBar = menuBar();
connect(m_pMenuBar, SIGNAL(hovered(QAction*)), this, SLOT(OnMenuHover(QAction*)));
QWidget* pMenuWidget = new QWidget(this);
m_pMenuLayout = new QHBoxLayout(pMenuWidget);
m_pMenuLayout->setContentsMargins(0, 0, 0, 0);
//m_pMenuLayout->addWidget(m_pMenuBar);
m_pMenuLayout->setMenuBar(m_pMenuBar);
//m_pMenuLayout->addWidget(m_pLabel);
//m_pMenuLayout->addStretch(10);
setMenuWidget(pMenuWidget);
CreateUI();
setCentralWidget(m_pMainWidget);
m_pTraceInfo = new QLabel();
m_pDisabledForce = new QLabel();
m_pDisabledRecovery = new QLabel();
m_pDisabledMessages = new QLabel();
statusBar()->addPermanentWidget(m_pTraceInfo);
statusBar()->addPermanentWidget(m_pDisabledForce);
statusBar()->addPermanentWidget(m_pDisabledRecovery);
statusBar()->addPermanentWidget(m_pDisabledMessages);
OnDisablePopUp(); // update statusbar
m_pHotkeyManager = new UGlobalHotkeys(this);
connect(m_pHotkeyManager, SIGNAL(activated(size_t)), SLOT(OnHotKey(size_t)));
SetupHotKeys();
m_BoxColors[CSandBoxPlus::eHardenedPlus] = qRgb(238,35,4);
m_BoxColors[CSandBoxPlus::eHardened] = qRgb(247,125,2);
m_BoxColors[CSandBoxPlus::eDefaultPlus] = qRgb(1,133,248);
m_BoxColors[CSandBoxPlus::eDefault] = qRgb(246,246,2);
m_BoxColors[CSandBoxPlus::eAppBoxPlus] = qRgb(3,232,232);
m_BoxColors[CSandBoxPlus::eAppBox] = qRgb(0,253,0);
m_BoxColors[CSandBoxPlus::eInsecure] = qRgb(244,3,244);
m_BoxColors[CSandBoxPlus::eOpen] = qRgb(255,255,255);
CreateTrayIcon();
LoadState();
m_pProgressDialog = new CProgressDialog("");
m_pProgressDialog->setWindowModality(Qt::ApplicationModal);
connect(m_pProgressDialog, SIGNAL(Cancel()), this, SLOT(OnCancelAsync()));
m_pProgressModal = false;
m_pPopUpWindow = new CPopUpWindow();
bool bAlwaysOnTop = theConf->GetBool("Options/AlwaysOnTop", false);
this->setWindowFlag(Qt::WindowStaysOnTopHint, bAlwaysOnTop);
m_pPopUpWindow->setWindowFlag(Qt::WindowStaysOnTopHint, bAlwaysOnTop);
m_pProgressDialog->setWindowFlag(Qt::WindowStaysOnTopHint, bAlwaysOnTop);
//connect(theAPI, SIGNAL(LogMessage(const QString&, bool)), this, SLOT(OnLogMessage(const QString&, bool)));
connect(theAPI, SIGNAL(LogSbieMessage(quint32, const QStringList&, quint32)), this, SLOT(OnLogSbieMessage(quint32, const QStringList&, quint32)));
connect(theAPI, SIGNAL(NotAuthorized(bool, bool&)), this, SLOT(OnNotAuthorized(bool, bool&)), Qt::DirectConnection);
connect(theAPI, SIGNAL(QueuedRequest(quint32, quint32, quint32, const QVariantMap&)), this, SLOT(OnQueuedRequest(quint32, quint32, quint32, const QVariantMap&)), Qt::QueuedConnection);
connect(theAPI, SIGNAL(FileToRecover(const QString&, const QString&, const QString&, quint32)), this, SLOT(OnFileToRecover(const QString&, const QString&, const QString&, quint32)), Qt::QueuedConnection);
connect(theAPI, SIGNAL(ConfigReloaded()), this, SLOT(OnIniReloaded()));
connect(qApp, &QGuiApplication::commitDataRequest, this, &CSandMan::commitData);
m_uTimerID = startTimer(1000);
bool bAutoRun = QApplication::arguments().contains("-autorun");
if (!bAutoRun && g_PendingMessage.isEmpty())
SafeShow(this);
OnStatusChanged();
if (CSbieUtils::IsRunning(CSbieUtils::eAll) || theConf->GetBool("Options/StartIfStopped", true))
{
SB_RESULT(void*) Status = ConnectSbie();
HandleMaintenance(Status);
}
//qApp->setWindowIcon(GetIcon("IconEmptyDC", false));
}
主窗口不是采用.ui文件生成的,它是由代码动态生成的。主要处理了界面语言问题,界面信号槽问题,界面UI搭配问题,下面重点就这几个问题,进行分析。
2.Qt语言国际化
qt提供了一个翻译机制,只要是代码中用tr()包含起来的文本,程序可以额外提供一个翻译文件(*.ts),有了这个翻译文件可以将tr()中包含的文本人为翻译为任何语言,这样对于不同语言的使用者,只需要选择自己使用的语言,程序界面就会自动完成语言转换。
先创建工程文件,然后编辑.pri文件,在末尾加入:
TRANSLATIONS += yourproject_zh.ts
然后采用工具生成ts文件,注意采用对应VS对台的控制台,命令如下:
"D:\QT\vs2019Qt\Qt5_15_2_VS2019_Static_64\bin\lupdate.exe" –verbose QtConnect.pri
目录之下生成了对应的ts文件,然后使用工具linguist.exe编辑ts文件,如下所示:
可以设置不同语言的版本。
在下面输入对应的中文翻译。
然后采用lrelease.exe工具转成二进制文件,如下所示:
"D:\QT\vs2019Qt\Qt5_15_2_VS2019_Static_64\bin\lrelease.exe" –verbose QtConnect.pri
3.设置软件的语言版本
有2种方法设置软件的语言版:
①在项目启动时设置界面语言
②在项目启动过程中动态切换语言
#include <QTranslator>
#include <QSettings>
QTranslator *trans=NULL;
QString readSetting();
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
trans=new QTranslator;
QString curLang=readSetting();//从注册表读取上次的语言版本
if(curLang=="EN") //若为“EN”,就调用lang_en.qm翻译文件
trans->load("lang_en.qm");
else//否则就调用lang_cn.qm翻译文件
trans->load("lang_cn.qm");
a.installTranslator(trans);//给应用程序安装翻译器,实现需要的界面版本
MainWindow w;
w.show();
return a.exec();
}
//用于从注册表里读取上次设置的界面语言版本
QString readSetting()
{
QString organization="WWB-Qt";
QString appName="samp6_22";
QSettings settings(organization,appName);
QString Language=settings.value("Language","EN").toString();
return Language;
}
在软件运行时可以动态切换语言,无需重启软件就可以切换语言界面
#include <QTranslator>
#include <QSettings>
class MainWindow : public QMainWindow
{
private:
QTranslator *trans=NULL;
};
“中文版”按钮的响应函数
void MainWindow::on_actLang_CN_triggered()
{
qApp->removeTranslator(trans);
delete trans;
trans=new QTranslator;
trans->load("lang_cn.qm");
qApp->installTranslator(trans);
ui->retranslateUi(this);
QSettings settings("WWB-Q","samp6_22");
settings.setValue("Language","CN");
}
“英文版”按钮的响应函数
void MainWindow::on_actLang_EN_triggered()
{
qApp->removeTranslator(trans);
delete trans;
trans=new QTranslator;
trans->load("lang_en.qm");
qApp->installTranslator(trans);
ui->retranslateUi(this);
QSettings settings("WWB-Q","samp6_22");
settings.setValue("Language","EN");
}
代码解释:
因为main()函数已经加载了一个翻译器,所以需要先移除当前的翻译器,才能重新创建新的翻译器,加载翻译文件,并为应用程序重新加载新翻译器。完成后调用UI的retranslateUi函数来刷新界面。retranslateUi函数时窗口的“ui_”头文件自动生成的,这个函数使用QApplication::translate()函数将所有的界面字符串进行翻译,才可以立即更新界面语言。
4.作者答疑
如有疑问,敬请留言。