翻譯|使用教程|編輯:龔雪|2023-11-29 10:39:35.137|閱讀 152 次
概述:本文將為大家介紹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@fc6vip.cn
文章轉載自:慧都網