Qt5的信号与槽
✨描述:信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式(发布-订阅模式)。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。
✨信号的本质:信号是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时候Qt对应的窗口类会发出某个信号,以此对用户的挑选做出反应。
✨槽的本质:在Qt中槽函数是一类特殊的功能的函数,在编码过程中也可以作为类的普通成员函数来使用。之所以称之为槽函数是因为它们还有一个职责就是对Qt框架中产生的信号进行处理。就是对信号作出的响应,对于打篮球的人来说,信号就是别人在起步投篮,你看到的这个动作就是信号,然后你会条件反射的想对其进行封盖,然后起跳,这个动作就是对应的槽
二者如何关联:使用connect函数关联。
函数原型:
QMetaObject::Connection QObject::connect(
const QObject *sender, PointerToMemberFunction signal,
const QObject *receiver, PointerToMemberFunction method,
Qt::ConnectionType type = Qt::AutoConnection);
参数:
- sender: 发出信号的对象,也可以传入对象的指针
- signal: 属于sender对象, 信号是一个函数, 这个参数的类型是函数
指针, 信号函数地址 - receiver: 信号接收者,也可以是对象指针
- method: 属于receiver对象, 当检测到sender发出了signal信号,
receiver对象调用method方法,信号发出之后的处理动作
// 参数 signal 和 method 都是函数地址, 因此简化之后的 connect() 如下:
connect(const QObject *sender, &QObject::signal,
const QObject *receiver, &QObject::method);
你实际使用过程中,可以直接使用对象,也可以使用对象指针。
计算器程序中的信号与槽
✨我们需要设计数字按键,加减乘除的按键,还有括号,小数点,等于号等等。
✨ 说明:del是删除一个位函数,delall是删除全部位函数,就是计算器的全部清除功能。is是等于号的按键槽函数
✨按钮的槽函数
void MainWindow::pushButton0()
{
if(strin=="#")
ui->textEdit1->clear();
strin+="0";
ui->textEdit1->textCursor().insertText("0");//textCursor()获取文本光标插入点的位置,后接入函数表示向光标后位置插入0
}
✨其他的数字一样。
✨括号函数的编写
//括号函数的编写
void MainWindow::pushButtonzuo()
{
if(strin=="#")
ui->textEdit1->clear();
char* s=strin.toLocal8Bit().data();
if (isdigit(s[strlen(s)-1]))//左括号前面为数字的话,则表示输入错误,前面只能是
QMessageBox::about(this,"输入错误","左括号前面不是直接是数字");
else
{ strin+="(";
bracket=1;//表示前面有左括号
ui->textEdit1->textCursor().insertText("(");}
}
其他括号一样。
✨加减乘除的编写
void MainWindow::pushButton_add()
{
strin+="+";
ui->textEdit1->textCursor().insertText("+");
operror();
}
其他运算符一样
✨删除一位按钮的函数
//删除按钮
void MainWindow::pushButton_del()
{
strin.chop(1);
ui->textEdit1->textCursor().deletePreviousChar();
if(strin=="#")
ui->textEdit1->setText("0");
else
ui->textEdit1->setText(strin.mid(1));
}
✨删除全部位按钮的函数
void MainWindow::pushButton_delall()
{
strin="#";
if(strin=="#")
ui->textEdit1->setText("0");
}
✨运算符检测函数
void MainWindow::operror(int num)
{
char*s=strin.toLocal8Bit().data();
if(!isdigit(s[strlen(s)-2])&&s[strlen(s)-2]!=')')
{
QMessageBox::about(this,"输入有误","您输入的双目运算符无左值");
strin.chop(1);
for(int i=1;i<=num;i++)
ui->textEdit1->textCursor().deletePreviousChar();
}
}
✨计算函数
double MainWindow:: cal(const QString& expression)
{
QStack<double> values;//数值栈
QStack<char> operatorstack;//操作符栈
for(int i=0;i<expression.length();i++)
{
if(expression.at(i)=='(')
operatorstack.push(expression.at(i).toLatin1());//遇到左括号的话就入栈
else if(expression.at(i) == ')')//如果遇到右括号的话,弹出操作符和操作数,直到遇到左括号。
{
values.push(temp.toDouble());//压入
temp="";//复原
while(!operatorstack.isEmpty() && operatorstack.top() != '(')//只要不为空,直到左括号
{
char op = operatorstack.pop();//记录顶部的操作符
double num1=values.pop();//记录取出的第一个数
double num2=values.pop();//记录取出的第2个数
switch(op)//对不同的操作符做不同的处理
{
case '+': values.push( num1+num2);break;
case '-': values.push(num2-num1);break;
case '*': values.push(num1*num2) ;break;
case '/': if (num1==0) QMessageBox::about(this,"除数非法","除数不能为0"); else values.push(num2/num1);break;
default: throw std::invalid_argument("无效的操作符");break;
}
}
operatorstack.pop();//结束之后把左括号弹出
}
else if(expression.at(i) == '+'||expression.at(i) =='-')//按照优先级进行运算
{
values.push(temp.toDouble());//压入
temp="";//复原
if(!operatorstack.empty() && operatorstack.top() != '(' &&
(operatorstack.top() == '*' || operatorstack.top() == '/')) //如果现在的表达式比之前的低就要出栈
{
char op = operatorstack.top();//记录操作符
operatorstack.pop();//弹出操作符
double operand1 = values.pop();
double operand2 = values.pop();
switch(op)//对不同的操作符做不同的处理
{
case '+': values.push(operand1+operand2);break;
case '-': values.push (operand2-operand1);break;
case '*': values.push(operand1*operand2) ;break;
case '/': if (operand1==0) QMessageBox::about(this,"除数非法","除数不能为0"); else values.push(operand2/operand1);break;
default: throw std::invalid_argument("无效的操作符");break;
}
}
operatorstack.push(expression.at(i).toLatin1());//作为运算之后就把这个运算符进行压入
}
else if(expression.at(i) =="*"||expression.at(i) =="/")//一律压入栈
{
operatorstack.push(expression.at(i).toLatin1());
values.push(temp.toDouble());//压入
temp="";//复原
}
else if(expression.at(i).isDigit() || expression.at(i)=='.')//如果是数字或者是小数点
{
temp=temp.append(expression.at(i));
}
else
{
qWarning() << "无法识别的字符:" << expression.at(i);
return 0.0;
}
}
if (!temp.isEmpty())
{
values.push(temp.toDouble());
temp = "";
}
// 执行剩余的操作
while (!operatorstack.isEmpty())
{
char op = operatorstack.top();
operatorstack.pop();
if (values.size() < 2)
{ qDebug()<<"value的大小为="<<values.size();
qWarning() << "表达式格式不正确";
return 0.0;
}
double operand11 = values.pop();
double operand22 = values.pop();
switch(op)//对不同的操作符做不同的处理
{
case '+': values.push(operand11+operand22);break;
case '-': values.push (operand22-operand11);break;
case '*': values.push(operand11*operand22) ;break;
case '/': if (operand11==0) QMessageBox::about(this,"除数非法","除数不能为0"); else values.push(operand22/operand11);break;
default:throw std::invalid_argument("无效的操作符");break;
}
}
// 最终栈中应该只有一个值,即表达式的结果
if (values.size() == 1 && operatorstack.isEmpty())
{
double a=values.top();
strin="#"+strin.setNum(a);
return values.top();
}
else
{ qDebug()<<"value的大小为="<<values.size();
while (!values.isEmpty()) {
qDebug()<<"数值栈的值为="<<values.pop();
}
qWarning() << "表达式格式不正确";
return 0.0; // 处理格式不正确的情况,返回默认值或者适当的错误处理
}
}
结果展示:
初始化的启动界面