用Qt寫軟件系列五:一個安全防護軟件的制作(3)


引言

       上一篇中講述了工具箱的添加。通過一個水平布局管理器,我們將一系列的工具按鈕組合到了一起,完成了工具箱的編寫。本文在前面的基礎上實現窗體分割效果、堆棧式窗口以及Tab選項卡。

窗體分割

       窗體分割是一個常見的功能,尤其在一些IDE中用的非常廣泛。主要是窗體分割能夠在視覺上對程序功能進行分組分類,在保證界面美觀的同時還能保證內容井井有條,何樂而不為呢?Qt中提供了一個用於分割窗體的類:QSplitter。這個類的使用也非常簡單,准備好需要分割的窗口,設置好分割方向和比例即可。不過值得注意的是,QSplitter是一個窗口管理類,在沒有添加子控件是看不到QSplitter效果的。這一點在Qt Designer中也可以驗證。

      在我們的項目中,我們增加一個QSplitter類成員,並在主窗口的構造函數中添加如下代碼:

splitter = new QSplitter(Qt::Horizontal, this);
splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
splitter->setHandleWidth(1);

splitter->addWidget(new QWidget(this));
splitter->addWidget(new QWidget(this));
splitter->handle(1)->setDisabled(true);
splitter->setStretchFactor(0, 1);
splitter->setStretchFactor(1, 3);

  在上面的代碼中,我們將左右兩個子窗口的比例設置為1:3。也就是說左邊窗口占25%的空間,右邊占75%。另外,我們還設置了QSplitter的Handle寬度。handle指的就用於分割窗體的那根線。我們將其寬度設置為1個像素寬,setDisabled(true)將其設置為不可拖動的。這樣一來,用戶就無法用鼠標拖拽左右窗口的大小了。看看效果:

      在分割出來的子窗口中,還可以進行進一步的分割,也就是QSplitter的嵌套使用。

堆棧式窗口及Tab選項卡

       堆棧式窗口取義於數據結構中的堆棧,也就是說多個窗口堆疊在一起,當用戶點擊對應層的窗口時進行切換。以騰訊QQ的設置窗口為例,看看到底是怎樣一種效果:

      當用戶點擊“基本設置”時,窗口中的內容全部都是相關的選項卡;當點擊“安全設置”的時候,窗口內容切換為對應的選項卡內容。也就是說一個窗口被另一個窗口“遮住”了。利用這種形式可以很容易的組織邏輯相關的內容。QStackedWidget是Qt為我們提供的一個實現這種功能的類。除此之外,Qt還提供了一個堆棧式窗口布局管理器類:QStackedLayout。而事實上,QStackedWidget的功能正是基於QStackedLayout實現的。那么,我們又該如何去組織這樣一種結構呢?

      基本思路其實也很簡單。QStackedWidget繼承自QWidget,它本身是一個控件容器,但是也可以作為子控件放置於其他的容器中去。那么,我們先構造好一個QStackedWidget,然后再考慮集成到父窗口中去:

TrojanAssessment::TrojanAssessment(QWidget *parent)
	: ShadowWindow(parent)
{
        // 前面省略……
	// create tree widget and stacked widget
	treeWidget = new QTreeWidget(this);
	treeWidget->setFrameShape(QFrame::NoFrame);
	stackedWidget = new QStackedWidget(this);
	stackedWidget->resize(680, 500);
	stackedWidget->setFrameShape(QFrame::NoFrame);
	initStackedWidget();
	initTreeWidget();

	splitter = new QSplitter(Qt::Horizontal, this);
	splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
	splitter->setHandleWidth(1);
	splitter->addWidget(treeWidget);
	splitter->addWidget(stackedWidget);
	splitter->handle(1)->setDisabled(true);
	splitter->setStretchFactor(0, 1);
	splitter->setStretchFactor(1, 3);

	// create title widget and status bar
	titleWidget = new TitleWidget(this);
	icon_label = new QLabel(this);
	icon_label->setPixmap(QPixmap(":/menu/cloud"));
	icon_label->setFixedSize(QPixmap(":/menu/cloud").size());
	lastrun_label = new QLabel(this);
	m_bottomLayout = new QHBoxLayout(this);
	m_bottomLayout->addStretch();
	m_bottomLayout->addWidget(icon_label, 0, Qt::AlignCenter);
	m_bottomLayout->addWidget(lastrun_label, 0, Qt::AlignCenter);
	m_bottomLayout->setSpacing(5);
	m_bottomLayout->setContentsMargins(0, 3, 10, 3);

	// remember the time when the program start
	login_dt = QDateTime::currentDateTime();
	restoreSettings();

	QPalette plt;
	plt.setBrush(QPalette::Window, QBrush(Qt::white));
	treeWidget->setPalette(plt);
	treeWidget->setAutoFillBackground(true);
	stackedWidget->setPalette(plt);
	stackedWidget->setAutoFillBackground(true);
        // 省略更多……

}

void TrojanAssessment::initStackedWidget()
{
	/* initialize the stacked pages */
	fmp = new FileMonitorPage(this);
	iep = new IEPage(this);
	mp = new MemoryPage(this);
	np = new NetworkPage(this);
	pp = new ProcessPage(this);
	rp = new RegisterPage(this);
	scp = new SecurityCenterPage(this);

	//add page widgets to StackedWidgets
	stackedWidget->addWidget(fmp);
	stackedWidget->addWidget(iep);
	stackedWidget->addWidget(mp);
	stackedWidget->addWidget(np);
	stackedWidget->addWidget(pp);
	stackedWidget->addWidget(rp);
	stackedWidget->addWidget(scp);
	// set File Monitoring as the default page.
	stackedWidget->setCurrentWidget(fmp);

	connect(this, SIGNAL(changeTabFMP(int)), fmp, SLOT(onChangeTab(int)));
	connect(this, SIGNAL(changeTabPP(int)), pp, SLOT(onChangeTab(int)));
	connect(this, SIGNAL(changeTabMP(int)), mp, SLOT(onChangeTab(int)));
	connect(this, SIGNAL(changeTabNP(int)), np, SLOT(onChangeTab(int)));
	connect(this, SIGNAL(changeTabRP(int)), rp, SLOT(onChangeTab(int)));
	connect(this, SIGNAL(changeTabSCP(int)), scp, SLOT(onChangeTab(int)));
	connect(this, SIGNAL(changeTabIEP(int)), iep, SLOT(onChangeTab(int)));
}

   在構造函數中我們構造了一個QStackedWidget實例,在initStackedWidget()中,用addWidget陸續添加了7個子控件。這里需要注意的是:我們添加的每一個控件都是堆棧式窗口中的“一頁”了,setCurrentWidget()用於設置當前可見的“頁”。那么,Tab選項卡又是如何實現的呢?繼承QTabWidget類。QTabWidget也是一個容器類,可以添加很多子控件。每一個控件都是一個Tab了。以File monitor這一頁為例:

class FileMonitorPage : public QTabWidget
{
	Q_OBJECT
public:
	FileMonitorPage(QWidget *parent = 0);
	~FileMonitorPage(){}
private slots:
	void onChangeTab(int index);
private:
	FileMonitorPage& operator=(const FileMonitorPage& obj);
	FileMonitorPage(const FileMonitorPage& obj);
	
private:
	//QTabWidget* m_tabWidget;
	DataFileTab* m_dataFileTab;
	ExecFileTab* m_execFileTab;
	FileBrowserTab* m_browserTab;
};
//////////////////////////////////////////////////////////////////////////
//Tab for data file monitoring
class DataFileTab : public QWidget
{
	Q_OBJECT
public:
	DataFileTab(QWidget* parent = 0);
	~DataFileTab(){}

private:
	DataFileTab(const DataFileTab& obj);
	DataFileTab& operator=(const DataFileTab& obj);
private:
	CustomItemModel* m_model;
	QSortFilterProxyModel* m_proxy;
	QTableView* m_view;
	
	QHBoxLayout* m_topLayout;
	QLineEdit* m_filter;
	QPushButton* m_clearBtn;
	QPushButton* m_exportBtn;

	QHBoxLayout* m_statusLayout;
	QLabel* m_status;
	QLineEdit* m_status_info;
	QPushButton* m_chooseDir;
	QPushButton* m_startBtn;
	QPushButton* m_stopBtn;

	QVBoxLayout* m_mainLayout;
};

//////////////////////////////////////////////////////////////////////////
// Tab for executable file monitoring
class ExecFileTab : public QWidget
{
	Q_OBJECT

public:
	ExecFileTab(QWidget* parent = 0);
	~ExecFileTab(){}

private:
	ExecFileTab(const ExecFileTab& obj);
	ExecFileTab& operator=(const ExecFileTab& obj);
private:
	QTableView* m_view;
	CustomItemModel* m_model;

	QHBoxLayout* m_topLayout;
	QPushButton* m_clearBtn;
	QPushButton* m_startBtn;
	QPushButton* m_stopBtn;

	QVBoxLayout* m_mainLayout;
};

//////////////////////////////////////////////////////////////////////////
// Tab for file browser file monitoring
class FileBrowserTab : public QWidget
{
	Q_OBJECT
public:
	FileBrowserTab(QWidget* parent = 0);
	~FileBrowserTab(){}

private:
	FileBrowserTab(const FileBrowserTab& obj);
	FileBrowserTab& operator=(const FileBrowserTab& obj);
private:
	QTreeView* m_view;
	QFileSystemModel* m_model;
	QVBoxLayout* m_layout;
};

       在File Monitor中我們添加了三個TAB:DataFileTab,ExecFileTab,FileBrowserTab,這三個類每一個都有自己的布局管理器和子控件。這么說來,QTabWidget和QStackedWidget的結構是非常相似的。其實,編寫Qt程序的時候,我們要組合一個窗口其實是非常簡單的。QWidget可以通過布局管理器嵌套任意多的子窗口,從而構建負責的UI元素。最終的效果看起來是這樣的:

小結

       本文重點實現了三個功能:窗體分割(QSplitter),堆棧式窗口(QStackedWidget),Tab選項卡(QTabWidget)。通過這三個功能,一個窗口能同時展示多項內容,並按邏輯功能分類。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM