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


引言

       在上一篇中講述了主窗體的創建和設計。主窗體的無邊框效果、陰影效果、拖動事件處理、窗體美化等工作在前面的博客中早就涉及,因此上篇博文中並未花費過多筆墨。這一篇繼續講述工具箱(Tool Button)的實現。另外,在實現的過程中還做了另外一個貼心的小功能:可伸縮的側邊欄。不過后來發現應用起來后效果不佳,於是就沒在主窗體中加入這個功能了,單獨做了一個demo作為示范。

工具箱的實現

       工具箱是將若干的工具按鈕組織在一起,為用戶提供簡便導航功能的一個組件。在Qt中實現這個功能不難,Qt庫本身就提供了QToolButton和QToolBox兩個類用於類似功能。在這里我們從QToolButton類派生一個子類自定義按鈕動作。QToolButton類本身只提供了一些基本功能。因此我們需要實現一些事件處理器來自定義工具按鈕的動作和外觀。

       看碼說話:

CustomToolButton::CustomToolButton(const QString& path, QWidget *parent)
	: QToolButton(parent), m_filePath(path)
{
	// Get the widget's palette, we do have to change the color of the tool button.
	QPalette text_palette = palette();
	text_palette.setColor(QPalette::ButtonText, QColor(230, 230, 230));
	setPalette(text_palette);
	// set the style of QToolButton.
	setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
	// set the font style of tool buttons
	// since the return value has the type of const, we have to remove the
	// constness if we want to modify.
	QFont text_font = const_cast<QFont&>(font());
	text_font.setBold(true);
	setFont(text_font);
	// set the fixed size for tool buttons.
	QPixmap background(m_filePath);
	setIcon(background);
	setIconSize(background.size());
	setFixedSize(background.width()+25, background.height()+25);

	setAutoRaise(true);
	m_mousePressed = false;
	m_mouseHover = false;
}
/*
 * Arguments topColor, centerColor, bottomColor are alpha values for QColor.
 */
void CustomToolButton::doPaintStuff(int topColor, int centerColor, int bottomColor)
{
	QPainter painter(this);
	QPen p(Qt::NoBrush, 1);
	painter.setPen(p);
	// create linear gradient brush to draw the widget
	QLinearGradient linear(rect().topLeft(), rect().bottomLeft());
	linear.setColorAt(0, QColor(230, 230, 230, topColor));
	linear.setColorAt(0.5, QColor(230, 230, 230, centerColor));
	linear.setColorAt(1, QColor(230, 230, 230, bottomColor));

	// paint the widget.
	painter.setBrush(linear);
	painter.drawRect(rect());
}

void CustomToolButton::setButtonPressed(bool isPressed)
{
	m_mousePressed = isPressed;
	update();
}

void CustomToolButton::enterEvent(QEvent *)
{
	m_mouseHover = true;
	update();
}

void CustomToolButton::leaveEvent(QEvent *)
{
	m_mouseHover = false;
	update();
}

void CustomToolButton::paintEvent(QPaintEvent *event)
{
	if (m_mouseHover)
	{
		doPaintStuff(0, 100, 150);
	}
	else
	{
		if (m_mousePressed)
		{
			doPaintStuff(0, 100, 150);
		}
	}
	QToolButton::paintEvent(event);
}

void CustomToolButton::mousePressEvent(QMouseEvent *event)
{
	if (event->button() == Qt::LeftButton)
	{
		emit clicked();
	}
}

   在該子類中我們重寫了enterEvent(),leaveEvent(), paintEvent(), mousePressEvent()這幾個事件處理函數。分別對應鼠標進入、離開、點擊按鈕區域事件,paintEvent()則用於繪制按鈕的外觀。此外,還是用了幾個狀態變量,用於記錄鼠標當前的移動狀態。利用這些狀態,我們就能順利實現不同狀態的外觀繪制。值得注意的是doPaintStuff()這個函數。這個函數實際做的工作是給工具按鈕添加垂直的漸變效果。使用了QLinearGradient這個類,可以實現線性的漸變效果,這在很多界面元素設計中都非常有用。

       在主函數中怎么調用這個自定義的按鈕類呢?

MainWin::MainWin(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);
	
	setWindowTitle("Tool Button");
	QStringList string_list;
	string_list<<":/toolWidget/tiJian"<<":/toolWidget/muMa"<<":/toolWidget/repair"<<":/toolWidget/qingLi"
		<<":/toolWidget/jiaSu"<<":/toolWidget/expert"<<":/toolWidget/menZhen"<<":/toolWidget/gongNeng";

	QVBoxLayout *layout = new QVBoxLayout(this);
	QHBoxLayout *button_layout = new QHBoxLayout(this);

	QSignalMapper *signal_mapper = new QSignalMapper(this);
	for(int i=0; i< string_list.size(); i++)
	{
		CustomToolButton *tool_button = new CustomToolButton(string_list.at(i));
		tool_button->setText("Test");
		button_list.append(tool_button);
		connect(tool_button, SIGNAL(clicked()), signal_mapper, SLOT(map()));
		signal_mapper->setMapping(tool_button, QString::number(i, 10));

		button_layout->addWidget(tool_button, 0, Qt::AlignBottom);
	}
	layout->addLayout(button_layout);
	layout->addStretch();
	setLayout(layout);

}

   從代碼中看,我們用了一個循環生成了若干個自定義按鈕,然后全部放到水平布局管理器中進行管理。這個很容易理解,重點內容是QSignalMapper類的應用。QSignalMapper類是一個工具類,它主要的功能是將一組無參數信號集中管理,將信號用整型值或字符串值表示,然后再以一種統一的形式發送出去。其好處是,當有很多的信號需要統一管理的時候非常方便,不用手動調用connect()為信號綁定槽函數,因此代碼結構也更為簡練。在上面的代碼中,我們將按鈕點擊信號轉換為數值形式表示。這樣也是很自然的做法,一方面形式簡單,另一方面水平排列的工具按鈕按序編號符合人類習慣。

 
        

可伸縮的側邊欄

       還是看看什么叫做可伸縮的側邊欄,這樣的功能在QQ的聊天窗口就可以看見:

       側邊欄的收縮可以在需要的時候隱藏部分組件,從而為其他組件提供更為廣闊的視角。如上圖中的側邊欄收縮為文本框組件提供更多的空間,整個界面上看起來也更為清爽。稍微一剖析:這個邊欄要能點擊,點擊之后要切換圖標,響應的組件要隱藏。如此一分析,代碼可如下編寫:

TestSideBar::TestSideBar(QWidget *parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);
	flag = false;
	mainLayout = new QHBoxLayout(this);

	mainSplitter = new QSplitter(Qt::Horizontal, this);
	mainSplitter->setFrameStyle(QFrame::NoFrame);
	mainSplitter->setHandleWidth(1);
	mainSplitter->setChildrenCollapsible(false);

	zoomButton = new QPushButton(this);
	zoomButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
	zoomButton->setIcon(QIcon(":/right"));
	zoomButton->setFixedWidth(6);
	zoomButton->setFocusPolicy(Qt::NoFocus);
	zoomButton->setStyleSheet("background: #E8E8E8; border: none; padding: 0px;");

	leftWidget = new QWidget(this);
	leftWidget->setStyleSheet("background: yellow;");
	rightWidget = new QWidget(this);
	rightWidget->setStyleSheet("background: blue;");

	mainSplitter->addWidget(leftWidget);
	mainSplitter->addWidget(rightWidget);

	mainLayout->addWidget(zoomButton);
	mainLayout->addWidget(mainSplitter);
	mainLayout->setSpacing(0);
	mainLayout->setContentsMargins(0, 0, 0, 0);

	QWidget* w = new QWidget(this);
	w->setLayout(mainLayout);
	setCentralWidget(w);

	connect(zoomButton, SIGNAL(clicked()), this, SLOT(onZoomClicked()));
}

void TestSideBar::onZoomClicked()
{
	if (flag)   // 根據當前的展開狀態,進行切換
	{
		flag = false;
		zoomButton->setIcon(QIcon(":/left"));
		leftWidget->setVisible(true);
	}
	else
	{
		flag = true;
		zoomButton->setIcon(QIcon(":/right"));
		leftWidget->setVisible(false);
	}
}

  

       可以發現這里的側邊欄果然一直固定在最左側,要達到QQ聊天界面那要的效果呢,只需要改幾行代碼就好了:

TestSideBar::TestSideBar(QWidget *parent)
	: QMainWindow(parent)
{
        // 其他保持不變,省略……

	mainSplitter->addWidget(leftWidget);
	mainSplitter->addWidget(zoomButton);
	mainSplitter->addWidget(rightWidget);

	mainLayout->addWidget(mainSplitter, 1);
	mainLayout->setSpacing(0);
	mainLayout->setContentsMargins(0, 0, 0, 0);

        // 其他保持不變,省略……
}

  

小結

       這一篇主要講了上篇遺留的一個功能,工具按鈕組的開發。另外,實現了另外一個功能:側邊欄的伸縮。下一篇繼續樹形控件(tree widget)、堆棧式窗口布局(stacked layout)的講解。


免責聲明!

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



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