翻譯|使用教程|編輯:龔雪|2023-11-29 10:39:35.137|閱讀 165 次
概述:本文將為大家介紹Qt Widget的模擬計算器的示例,歡迎下載最新版組件體驗~
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關鏈接:
Qt 是目前最先進、最完整的跨平臺C++開發工具。它不僅完全實現了一次編寫,所有平臺無差別運行,更提供了幾乎所有開發過程中需要用到的工具。如今,Qt已被運用于超過70個行業、數千家企業,支持數百萬設備及應用。
本文將展示如何使用信號和槽來實現計算器小部件的功能,以及如何使用QGridLayout在網格中放置子小部件。在上文中(點擊這里回顧>>)為大家介紹了實現計算器的Calculator類定義,本文將主要介紹Calculator類是如何實現的,持續關注我們哦~
Qt技術交流群:166830288 歡迎一起進群討論
 
 
這個例子由兩個類組成:
我們將從回顧計算器開始,然后再看看按鈕。
Calculator::Calculator(QWidget *parent)
: QWidget(parent), sumInMemory(0.0), sumSoFar(0.0)
, factorSoFar(0.0), waitingForOperand(true)
{
在構造函數中,初始化計算器的狀態。pendingAdditiveOperator和pendingMultiplicativeOperator變量不需要顯式初始化,因為構造函數將它們初始化為空字符串。也可以直接在header文件中初始化這些變量,這稱為成員初始化,避免了長初始化列表。
display = new QLineEdit("0");
display->setReadOnly(true);
display->setAlignment(Qt::AlignRight);
display->setMaxLength(15);
QFont font = display->font();
font.setPointSize(font.pointSize() + 8);
display->setFont(font);
我們創建了表示計算器顯示的 ,并設置了它的一些屬性,特別地我們將其設置為只讀。
我們還將顯示器的字體放大了8個點。
for (int i = 0; i < NumDigitButtons; ++i)
digitButtons[i] = createButton(QString::number(i), &Calculator::digitClicked);
Button *pointButton = createButton(tr("."), &Calculator::pointClicked);
Button *changeSignButton = createButton(tr("\302\261"), &Calculator::changeSignClicked);
Button *backspaceButton = createButton(tr("Backspace"), &Calculator::backspaceClicked);
Button *clearButton = createButton(tr("Clear"), &Calculator::clear);
Button *clearAllButton = createButton(tr("Clear All"), &Calculator::clearAll);
Button *clearMemoryButton = createButton(tr("MC"), &Calculator::clearMemory);
Button *readMemoryButton = createButton(tr("MR"), &Calculator::readMemory);
Button *setMemoryButton = createButton(tr("MS"), &Calculator::setMemory);
Button *addToMemoryButton = createButton(tr("M+"), &Calculator::addToMemory);
Button *divisionButton = createButton(tr("\303\267"), &Calculator::multiplicativeOperatorClicked);
Button *timesButton = createButton(tr("\303\227"), &Calculator::multiplicativeOperatorClicked);
Button *minusButton = createButton(tr("-"), &Calculator::additiveOperatorClicked);
Button *plusButton = createButton(tr("+"), &Calculator::additiveOperatorClicked);
Button *squareRootButton = createButton(tr("Sqrt"), &Calculator::unaryOperatorClicked);
Button *powerButton = createButton(tr("x\302\262"), &Calculator::unaryOperatorClicked);
Button *reciprocalButton = createButton(tr("1/x"), &Calculator::unaryOperatorClicked);
Button *equalButton = createButton(tr("="), &Calculator::equalClicked);
對于每個按鈕,我們使用適當的文本標簽和連接到按鈕的插槽調用私有createButton()函數。
QGridLayout *mainLayout = new QGridLayout;
mainLayout->setSizeConstraint(QLayout::SetFixedSize);
mainLayout->addWidget(display, 0, 0, 1, 6);
mainLayout->addWidget(backspaceButton, 1, 0, 1, 2);
mainLayout->addWidget(clearButton, 1, 2, 1, 2);
mainLayout->addWidget(clearAllButton, 1, 4, 1, 2);
mainLayout->addWidget(clearMemoryButton, 2, 0);
mainLayout->addWidget(readMemoryButton, 3, 0);
mainLayout->addWidget(setMemoryButton, 4, 0);
mainLayout->addWidget(addToMemoryButton, 5, 0);
for (int i = 1; i < NumDigitButtons; ++i) {
int row = ((9 - i) / 3) + 2;
int column = ((i - 1) % 3) + 1;
mainLayout->addWidget(digitButtons[i], row, column);
}
mainLayout->addWidget(digitButtons[0], 5, 1);
mainLayout->addWidget(pointButton, 5, 2);
mainLayout->addWidget(changeSignButton, 5, 3);
mainLayout->addWidget(divisionButton, 2, 4);
mainLayout->addWidget(timesButton, 3, 4);
mainLayout->addWidget(minusButton, 4, 4);
mainLayout->addWidget(plusButton, 5, 4);
mainLayout->addWidget(squareRootButton, 2, 5);
mainLayout->addWidget(powerButton, 3, 5);
mainLayout->addWidget(reciprocalButton, 4, 5);
mainLayout->addWidget(equalButton, 5, 5);
setLayout(mainLayout);
setWindowTitle(tr("Calculator"));
}
布局由單個處理,()調用確保Calculator小部件始終顯示為其最佳大小(其大小提示),從而防止用戶調整計算器的大小,大小提示由子小部件的大小和大小策略決定。
大多數子部件只占用網格布局中的一個單元格,對于這些我們只需要將一行和一列傳遞給QGridLayout::addWidget()。display、backspaceButton、clearButton和clearAllButton小部件占用多于一列,對于這些我們還必須船體一個行空間和一個列空間。
void Calculator::digitClicked()
{
Button *clickedButton = qobject_cast<Button *>(sender());
int digitValue = clickedButton->text().toInt();
if (display->text() == "0" && digitValue == 0.0)
return;
if (waitingForOperand) {
display->clear();
waitingForOperand = false;
}
display->setText(display->text() + QString::number(digitValue));
}
按下計算器的數字按鈕時將發出按鈕的clicked()信號,該信號將觸發digitClicked()插槽。
首先我們先使用()找出哪個按鈕發送了信號,這個函數以QObject指針的形式返回發送方。因為我們知道發送方是一個Button對象,所以可以安全地強制轉換。本來可以使用C風格的強制轉換或c++ static_cast<>(),但作為一種防御性編程技術,我們使用()。這樣做的好處是,如果對象的類型錯誤,則返回空指針。空指針導致的崩潰比不安全強制轉換導致的崩潰更容易診斷。有了按鈕后,我們使用QToolButton::text()提取操作符。
插槽需要特別考慮兩種情況,如果顯示包含“0”,而用戶點擊了“0”按鈕,那么顯示“00”將是愚蠢的。如果計算器處于等待新操作數的狀態,新數字就是新操作數的第一位;在這種情況下,必須首先清除先前計算的任何結果。
最后,我們將新數字附加到顯示的值后面。
void Calculator::unaryOperatorClicked()
{
Button *clickedButton = qobject_cast<Button *>(sender());
QString clickedOperator = clickedButton->text();
double operand = display->text().toDouble();
double result = 0.0;
if (clickedOperator == tr("Sqrt")) {
if (operand < 0.0) {
abortOperation();
return;
}
result = std::sqrt(operand);
} else if (clickedOperator == tr("x\302\262")) {
result = std::pow(operand, 2.0);
} else if (clickedOperator == tr("1/x")) {
if (operand == 0.0) {
abortOperation();
return;
}
result = 1.0 / operand;
}
display->setText(QString::number(result));
waitingForOperand = true;
}
每當單擊一個一元操作符按鈕時,就調用unaryOperatorClicked()插槽,再次使用QObject::sender()獲取指向被單擊按鈕的指針。操作符從按鈕的文本中提取并存儲在clickoperator中,操作數從display中獲得。
然后我們執行這個操作,如果Sqrt應用于負數或1/x到零,調用abortOperation()。如果一切順利,我們將在行編輯中顯示操作的結果,并將waitingForOperand設置為true。這確保了如果用戶鍵入一個新數字,該數字將被視為一個新的操作數,而不是附加到當前值。
void Calculator::additiveOperatorClicked()
{
Button *clickedButton = qobject_cast<Button *>(sender());
if (!clickedButton)
return;
QString clickedOperator = clickedButton->text();
double operand = display->text().toDouble();
當用戶單擊+或-按鈕時調用additiveOperatorClicked()槽。
在對單擊的操作符進行實際操作之前,我們必須處理所有掛起的操作。從乘法運算符開始,因為它們的優先級高于加法運算符:
if (!pendingMultiplicativeOperator.isEmpty()) {
if (!calculate(operand, pendingMultiplicativeOperator)) {
abortOperation();
return;
}
display->setText(QString::number(factorSoFar));
operand = factorSoFar;
factorSoFar = 0.0;
pendingMultiplicativeOperator.clear();
}
如果之前已經單擊了x或÷,而沒有隨后單擊=,則顯示中的當前值是x或÷操作符的右操作數,我們最終可以執行該操作并更新顯示。
if (!pendingAdditiveOperator.isEmpty()) {
if (!calculate(operand, pendingAdditiveOperator)) {
abortOperation();
return;
}
display->setText(QString::number(sumSoFar));
} else {
sumSoFar = operand;
}
如果前面已經單擊了+或-,則sumSoFar是左操作數,而顯示的當前值是操作符的右操作數。如果沒有掛起的加法運算符,則簡單地將sumSoFar設置為顯示中的文本。
pendingAdditiveOperator = clickedOperator; waitingForOperand = true; }
最后,我們可以處理剛剛點擊的操作符。由于還沒有右操作數,所以將單擊的操作符存儲在pendingAdditiveOperator變量中。稍后當有一個右操作數,而左操作數為sumSoFar時,將應用該操作。
void Calculator::multiplicativeOperatorClicked()
{
Button *clickedButton = qobject_cast<Button *>(sender());
if (!clickedButton)
return;
QString clickedOperator = clickedButton->text();
double operand = display->text().toDouble();
if (!pendingMultiplicativeOperator.isEmpty()) {
if (!calculate(operand, pendingMultiplicativeOperator)) {
abortOperation();
return;
}
display->setText(QString::number(factorSoFar));
} else {
factorSoFar = operand;
}
pendingMultiplicativeOperator = clickedOperator;
waitingForOperand = true;
}
multiplicativeOperatorClicked()插槽類似于additiveOperatorClicked(),不需要擔心掛起的加法運算符,因為乘法運算符優先于加法運算符。
void Calculator::equalClicked()
{
double operand = display->text().toDouble();
if (!pendingMultiplicativeOperator.isEmpty()) {
if (!calculate(operand, pendingMultiplicativeOperator)) {
abortOperation();
return;
}
operand = factorSoFar;
factorSoFar = 0.0;
pendingMultiplicativeOperator.clear();
}
if (!pendingAdditiveOperator.isEmpty()) {
if (!calculate(operand, pendingAdditiveOperator)) {
abortOperation();
return;
}
pendingAdditiveOperator.clear();
} else {
sumSoFar = operand;
}
display->setText(QString::number(sumSoFar));
sumSoFar = 0.0;
waitingForOperand = true;
}
與additiveOperatorClicked()一樣,我們首先處理任何掛起的乘法和加法操作符,然后顯示sumSoFar并將變量重置為零。必須將變量重置為零,以避免對值進行兩次計數。
void Calculator::pointClicked()
{
if (waitingForOperand)
display->setText("0");
if (!display->text().contains('.'))
display->setText(display->text() + tr("."));
waitingForOperand = false;
}
pointClicked()槽向顯示的內容添加一個小數點。
void Calculator::changeSignClicked()
{
QString text = display->text();
double value = text.toDouble();
if (value > 0.0) {
text.prepend(tr("-"));
} else if (value < 0.0) {
text.remove(0, 1);
}
display->setText(text);
}
changeSignClicked()槽改變顯示值的符號,如果當前值為正,則在前面加一個負號;如果當前值為負,則從值中刪除第一個字符(負號)。
void Calculator::backspaceClicked()
{
if (waitingForOperand)
return;
QString text = display->text();
text.chop(1);
if (text.isEmpty()) {
text = "0";
waitingForOperand = true;
}
display->setText(text);
}
backspaceclick()將刪除顯示中最右邊的字符,如果得到一個空字符串,則顯示“0”并將waitingForOperand設置為true。
void Calculator::clear()
{
if (waitingForOperand)
return;
display->setText("0");
waitingForOperand = true;
}
clear()槽將當前操作數重置為零,這相當于按退格鍵多次擦除整個操作數。
void Calculator::clearAll()
{
sumSoFar = 0.0;
factorSoFar = 0.0;
pendingAdditiveOperator.clear();
pendingMultiplicativeOperator.clear();
display->setText("0");
waitingForOperand = true;
}
clearAll()槽將計算器重置為初始狀態。
void Calculator::clearMemory()
{
sumInMemory = 0.0;
}
void Calculator::readMemory()
{
display->setText(QString::number(sumInMemory));
waitingForOperand = true;
}
void Calculator::setMemory()
{
equalClicked();
sumInMemory = display->text().toDouble();
}
void Calculator::addToMemory()
{
equalClicked();
sumInMemory += display->text().toDouble();
}
clearMemory()插槽擦除保存在內存中的總和,readMemory()將總和顯示為操作數,setMemory()將內存中的總和替換為當前的總和,addtommemory()將當前值添加到內存中的值。對于setMemory()和addtommemory(),我們首先調用equalClicked()來更新sumSoFar和顯示中的值。
template<typename PointerToMemberFunction>
Button *Calculator::createButton(const QString &text, const PointerToMemberFunction &member)
{
Button *button = new Button(text);
connect(button, &Button::clicked, this, member);
return button;
}
私有的createButton()函數從構造函數中被調用來創建計算器按鈕。
void Calculator::abortOperation()
{
clearAll();
display->setText(tr("####"));
}
私有的abortOperation()函數在計算失敗時被調用,重置計算器狀態,顯示“####”。
bool Calculator::calculate(double rightOperand, const QString &pendingOperator)
{
if (pendingOperator == tr("+")) {
sumSoFar += rightOperand;
} else if (pendingOperator == tr("-")) {
sumSoFar -= rightOperand;
} else if (pendingOperator == tr("\303\227")) {
factorSoFar *= rightOperand;
} else if (pendingOperator == tr("\303\267")) {
if (rightOperand == 0.0)
return false;
factorSoFar /= rightOperand;
}
return true;
}
私有的calculate()函數執行一個二進制操作,右操作數由rightOperand給出。對于加法操作符,左操作數為sumSoFar;對于乘法運算符,左操作數是factorSoFar。如果發生除零,函數返回false。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@ke049m.cn
文章轉載自:慧都網