1 QLineEdit

QLineEditQt中的单行输入框, 可以输入文本内容, 但无法换行;


1.1 常用属性

属性 说明
text 输入框中的文本
inputMask 输入内容格式约束
maxLength 最大长度
frame 是否添加边框
echoMode 显示方式:1) QLineEdit::Normal - 默认值, 文本框会显示输入的文本2) QLineEdit::Password - 该模式下输入的字符会被隐藏, 通常用*, =或是圆点代替
cursorPosition 光标所在位置
alignment 文本对齐方式, 设置水平和垂直方向对齐
dragEnabled 是否允许拖拽
readOnly 是否只读(不允许修改)
placeHolderText 当然输入框内容为空时, 显示什么信息
clearButtonEnabled 是否显示=="清除"==按钮

1.2 常用信号

信号 说明
void cursorPositionChanged(int old, int new) 鼠标移动时发出该信号, old为先前位置, new为新位置
void editingFinished() 按下返回或回车键时或者行编辑失去焦点触发该信号
void returnPressed() 按下返回或回车键按下时触发该信号, 设置了验证器后需要验证通过才能触发
void selectionChanged() 选中的文本改变时发出信号
void textChanged(const QString &text) 文本框内的文本改变时发出信号, text为新文本(代码修改能够触发信号)
void textEdited(const QString& text) 文本框内的文本改变时发出信号, text为新文本(代码修改无法触发该信号)

1.3 设计程序 - 输入基本信息并在弹窗中显示

设计一个程序, 输入基本信息(Username, Password, Gender, Email)并按下提交按钮后, 弹出弹窗(弹窗使用QMessageBox)显示提交信息;

  • Designer中设计控件

    三个LineEdit, 两个RadioButton与一个PushButton;

    其中几个Label用于信息提示;

  • 初始化RadioButton

    我们希望默认RadioButton对应的Male为选中状态, 在对应的RadioButton中设置setCheckked(true);

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
        ui->radioButton_male->setChecked(true);
    }
    
  • 设计QPushButton槽函数

    该槽函数用于获取LineEdit中的text();

    由于此处的信息获取为英文, 因此不作过多处理, 将多个内容组合为一个QString并传进QMessageBox提示框中;

    QMessageBox在此处不进行解释;

    void Widget::on_pushButton_submit_clicked()
    {
        QString str = "Username: "+ui->lineEdit_un->text()+"\n\nPassword: "+=ui->lineEdit_pw->text()+"\n\nGender: ";
        str+= ui->radioButton_male->isChecked()?ui->radioButton_male->text():ui->radioButton_female->text();
        str+="\n\nEmail: ";
        str+=ui->lineEdit_email->text();
        QMessageBox::information(nullptr, "Info", str);
    }
    
  • 运行结果


1.3.1 placeHolderText 设置

placeHolderText可以设置输入框的提示文本;

设置对应的提示文本且运行程序后, 当文本框内无任何内容将提示对应的提示文本, 当文本框中输入任意内容, 提示文本将消失;

通常使用setPlaceholderText(const QString&)进行设置;

清除文本框内内容后提示文本再次出现;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    // 默认选择Male
    ui->setupUi(this);
    ui->radioButton_male->setChecked(true);
        
   // 设置提示文本
    ui->lineEdit_un->setPlaceholderText("请输入用户名...");
    ui->lineEdit_pw->setPlaceholderText("请输入密码...");
    ui->lineEdit_email->setPlaceholderText("请输入邮箱...");
}

运行结果为:


1.3.2 clearButtonEnabled 设置清除按钮

QLineEdit中可以设置clearButtonEnabled属性来设置对应的清除按钮;

默认该按钮不会显示;

当该LineEdit输入框输入任何内容后, 该按钮将会显示, 点击该按钮可以将清除文本框;

通常使用>setClearButtonEnabled(bool enable)进行设置;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 默认选择Male
	//...
    // 设置提示文本
	//...
    // 设置清除按钮
    ui->lineEdit_un->setClearButtonEnabled(true);
    ui->lineEdit_pw->setClearButtonEnabled(true);
    ui->lineEdit_email->setClearButtonEnabled(true);
}

运行结果为:


1.3.3 echoMode 设置输入模式

QLineEdit中可以设置对应的输入模式;

常用的输入模式有:

  • 常规模式 - QLineEdit::Normal

    输入内容明文显示;

  • 密码模式 - QLineEdit::Password

    输入内容被特殊符号代替显示, 如 =*(根据系统不同, 特殊符号也会不同);

通常可以使用 setEchoMode()来进行设置对应的显示效果, 当然我们通常会把密码设置为 QLineEdit::Password从而对密码进行密文显示;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 默认选择Male
	// ...

    // 设置提示文本
	// ...

    // 设置清除按钮
	// ...

    // 密码输入模式设置为Password
    ui->lineEdit_pw->setEchoMode(QLineEdit::Password);
}

运行结果为:

当然, 我们时常能在应用的密码行旁看见一个复选框来显示或隐藏密码;

对该复选框增加槽函数, 当状态发生切换时更改Password - QLineEdit行的显示模式;

void Widget::on_checkBox_toggled(bool checked)
{
    if(checked) ui->lineEdit_pw->setEchoMode(QLineEdit::Normal);
    else     ui->lineEdit_pw->setEchoMode(QLineEdit::Password);
}

运行结果为:


1.3.4 inputMask 格式控制(简单校验)

通常情况下, 可以对QLineEdit设置输入格式;

假设对一个输入框限制只能输入十个数字, 且每两个数字为一组间隔, 可以使用QLineEdit -> setInputMask("00-00-00-00-00")进行设置;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // 默认选择Male
    // ...

    // 设置提示文本
    // ...

    // 设置清除按钮
    // ...

    // 密码输入模式设置为Password
    // ...

    // 显示密码
    // ...

    // 设置输入格式
    ui->lineEdit_un->setInputMask("00-00-00-00-00");
}

运行结果为:

从结果可以看出, 当进行输入时, 由于限制输入为数字, 因此其他无论是字母还是符号都无法输入, 且只能输入限制数量的内容, 多的内容无法进行输入(此处需编写代码自行体会);

当然这种方式只能进行简单校验, 无法进行更复杂的校验;


1.3.5 正则表达式

Qt中, 负责校验的除了InputMask以外还有可以使用正则表达式进行校验的校验器;

常用的正则表达式有如下:

  • 正则表达式基础

    正则表达式 描述
    ^ 匹配字符串的开始位置
    $ 匹配字符串的结束位置
    . 匹配除换行符之外的任意单个字符
    ` `
    \ 用于转义特殊字符, 使其失去特殊意义, 被当作普通字符处理
    a 匹配字符a
    ab 匹配字符串ab
  • 量词

    正则表达式 描述
    * 匹配前一个字符0次或多次
    ? 匹配前一个字符0次或1次
    + 匹配前一个字符1次或多次
    {5} 精确匹配前一个字符5次
    {5,} 匹配前一个字符至少5次
    {5,10} 匹配前一个字符至少5次, 但不超过10次
  • 字符类

    正则表达式 描述
    \s 匹配任何空白字符, 包括空格、制表符、换行符等
    \S 匹配任何非空白字符
    \w 匹配任何字母数字字符, 包括下划线
    \W 匹配任何非字母数字字符
    \d 匹配任何数字字符
    \D 匹配任何非数字字符
    [\b] 匹配退格字符
    \c 匹配控制字符
  • 特殊字符

    正则表达式 描述
    \n 匹配换行符
    \t 匹配制表符
    \r 匹配回车符
    \ZZZ 匹配八进制表示的字符
    \xZZ 匹配十六进制表示的字符
    \0 匹配空字符
    \v 匹配垂直制表符
  • 分组

    正则表达式 描述
    (xyz) 将字符组合为一个组, 用于后续的引用或操作
    (?:xyz) 非捕获组, 用于分组但不保存匹配的内容
    [xyz] 匹配方括号内的任一字符
    [^xyz] 匹配不在方括号内的任一字符
    [a-q] 匹配指定范围内的任一字符
    [0-7] 匹配指定范围内的数字
  • 字符串替换

    正则表达式 描述
    `$`` 在匹配的字符串之前插入
    $' 在匹配的字符串之后插入
    $+ 插入最后一次匹配的内容
    $& 插入整个匹配的内容
    $n 插入第n个捕获组的内容
  • 断言

    正则表达式 描述
    (?=xyz) 正向前瞻, 匹配后面跟有xyz的位置
    (?!xyz) 负向前瞻, 匹配后面不跟有xyz的位置
    ?! 或 `?

通常我们不需要去背住对应的正则表达式, 只需要在使用中能够了解对应的正则表达式或者查询即可(网络中存在许多现有可以直接使用的正则表达式);


1.3.6 QRegularExpressionValidator 正则表达式校验器

正则表达式是采用一种特殊字符串来描述一段文本从而约束输入框的输入内容;

Qt 允许QLineEdit设置一个校验器, 该校验器通过传入一个正则表达式对象;

当输入内容时, 由于文本框内容发生改变, QLineEdittextEdited()信号将被发送, 因此对应的槽可以对校验器的校验状态进行判断;

通常校验器的校验判断有三种状态:

  1. (2) - QValidator::Acceptable - 输入完全有效
  2. (1) - QValidator::Intermediate - 输入部分有效
  3. (0) - QValidator::Invalid - 输入无效(不匹配)

因此实际的步骤为如下:

  1. 创建正则表达式对象

  2. 创建校验器并赋值正则表达式对象

  3. 将校验器通过QLineEditsetValidator()成员方法设置进QLineEdit对象

  4. 设置QLineEdit控件textEdited()/textChanged()信号对应的槽

  5. 在槽中判断 validator()->validate(QString content, int pos)的状态(是否校验通过)

    此处的pos表示QLineEdit中文本框中文本内容的指向位置, 通常使用频率较低, 这里不进行解释;

以上文中的1.3程序为基础, 为其Email部分添加正则表达式校验, 其PushButton将根据校验的状态来判断其是否可点击;

此处我们使用的正则表达式为:

"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
  • 该正则表达式的解释为如下

    组件 描述
    ^ 匹配字符串的开始位置 , 确保从字符串开头开始匹配 , 没有前导字符
    [A-Za-z0-9._%+-]+ 匹配用户名部分(@ 前的部分): - [] 定义一个字符类 , 匹配括号内的任意一个字符 - A-Za-z:大写(A-Z)和小写(a-z)字母 - 0-9:数字(0-9) - ._%+-:允许的特殊字符:点、下划线、百分号、加号、减号 - +:量词 , 表示前面的字符类匹配1次或多次(至少一个字符)
    @ 匹配字面上的 @ 符号 , 将用户名和域名分开
    [A-Za-z0-9.-]+ 匹配域名部分(@ 后、顶级域名前的部分 , 如 "example" 在 "example.com" 中): - [] 定义字符类 - A-Za-z:大写和小写字母 - 0-9:数字 - .-:允许点和减号(用于子域名 , 如 "sub-domain") - +:匹配1次或多次(至少一个字符)
    \\. 匹配一个字面上的点(.) - \ 是转义符 , 因为 . 在 regex 中是特殊字符(匹配任意字符) , 所以需要转义为 \. - 在字符串表示中(如代码) , 往往写成 \\. 来转义反斜杠本身
    [A-Za-z]{2,} 匹配顶级域名(如 "com"、"io"): - [] 定义字符类 - A-Za-z:仅允许大写和小写字母(不包括数字或特殊字符) - {2,}:量词 , 表示匹配至少2次 , 没有上限(例如 "com"、"org"、"co.uk" 中的 "uk")
    $ 匹配字符串的结束位置 , 确保没有多余的尾随字符

按照上文中的步骤开始设置, 其步骤为如下:

  1. 将按钮Enable属性设置默认为false

    由于按钮的可点击状态根据邮箱的校验状态来判断, 因此此处需要将按钮的Enable状态设置默认为false;

    ui->pushButton_submit->setEnabled(false);
    
  2. 创建正则表达式对象

    创建一个正则表达式对象, Qt由于版本迭代, 不同版本都将出现不同语法的正则表达式对象;

    此处使用的正则表达式对象为 QRegularExpression;

    QRegularExpression rx_email("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
    

    此处为了不把对应的正则表达式内容被转义字符覆盖, 因此\.需要转化为\\.;

  3. 创建校验器并将正则表达式传入

    校验器将时刻判断正则表达式的状态, 因此需要时刻保持其生命周期, 所以需要在堆中创建, 其次为了保证其内存能够更好的被释放, 需要将其挂在对象树上;

    对应的正则表达式也需要传入该校验器中;

    QRegularExpressionValidator* rxv_email = new QRegularExpressionValidator(rx_email, this);
    
  4. 将校验器设置进QLineEdit控件中

    ui->lineEdit_email->setValidator(rxv_email);
    
  5. 设置槽函数

    槽函数对应的slot函数为textEdited()/textChanged(), 当输入框的内容发生变化时, 该信号将会被emit;

    同时在槽函数中我们需要判断校验是否通过, 此处我们需要完全符合校验规则才能放开PushButtonEnable状态, 同时需要注意该槽函数需要一个const QString&参数, 该参数为输入框内容发生变化的新文本;

    而在进行校验器校验时同样需要传入对应的文本, 本质是传入一个文本, 判断该文本是否符合校验规则;

    void Widget::on_lineEdit_email_textEdited(const QString &arg1)
    {
        QString content = arg1;
        int pos=0;
        if(ui->lineEdit_email->validator()->validate(content, pos) == QValidator::Acceptable) ui->pushButton_submit->setEnabled(true);
        else ui->pushButton_submit->setEnabled(false);
    }
    
  6. 运行结果:

除此之外我们可以通过举一反三的形式, 设置为所有文本框都为必填, 为空则无法Submit, 此处修改无需使用正则表达式, 只需要判断其他LineEdit其是否为空即可;

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    // PushButton默认不可用
    ui->pushButton_submit->setEnabled(false);

    // 默认选择Male
    ui->radioButton_male->setChecked(true);

    // 设置提示文本
    ui->lineEdit_un->setPlaceholderText("请输入用户名...");
    ui->lineEdit_pw->setPlaceholderText("请输入密码...");
    ui->lineEdit_email->setPlaceholderText("请输入邮箱...");

    // 设置清除按钮
    ui->lineEdit_un->setClearButtonEnabled(true);
    ui->lineEdit_pw->setClearButtonEnabled(true);
    ui->lineEdit_email->setClearButtonEnabled(true);

    // 密码输入模式设置为Password
    ui->lineEdit_pw->setEchoMode(QLineEdit::Password);

    // 显示密码
    ui->checkBox->setText("Show password");

    QRegularExpression rx_email("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
    QRegularExpressionValidator* rxv_email = new QRegularExpressionValidator(rx_email, this);
    ui->lineEdit_email->setValidator(rxv_email);

    // 多个信号绑定同一个槽
    connect(ui->lineEdit_un, &QLineEdit::textEdited, this, &Widget::on_lineEdit_email_textEdited);
    connect(ui->lineEdit_pw, &QLineEdit::textEdited, this, &Widget::on_lineEdit_email_textEdited);
    connect(ui->lineEdit_email, &QLineEdit::textEdited, this, &Widget::on_lineEdit_email_textEdited);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_pushButton_submit_clicked()
{
    QString str = "Username: "+ui->lineEdit_un->text()+"\n\nPassword: "+=ui->lineEdit_pw->text()+"\n\nGender: ";
    str+= ui->radioButton_male->isChecked()?ui->radioButton_male->text():ui->radioButton_female->text();
    str+="\n\nEmail: ";
    str+=ui->lineEdit_email->text();
    QMessageBox::information(nullptr, "Info", str);
}

void Widget::on_checkBox_toggled(bool checked)
{
    if(checked) ui->lineEdit_pw->setEchoMode(QLineEdit::Normal);
    else     ui->lineEdit_pw->setEchoMode(QLineEdit::Password);
}

void Widget::on_lineEdit_email_textEdited(const QString &arg1)
{
    (void) arg1; /* 被多个信号绑定, 无法具体判断该QString来自哪一个LineEdit */
    QString content = ui->lineEdit_email->text(); // 直接从LineEdit_email中获取QString
    int pos=0;
    bool unempty = !ui->lineEdit_un->text().isEmpty();
    bool pwempty = !ui->lineEdit_pw->text().isEmpty();
    bool elempty = !ui->lineEdit_email->text().isEmpty();

    if(ui->lineEdit_email->validator()->validate(content, pos) == QValidator::Acceptable && unempty && pwempty && elempty) ui->pushButton_submit->setEnabled(true);
    else ui->pushButton_submit->setEnabled(false);
}

除此之外, 我们可以手动定义一个Validator来重写对应的validate从而实现自定义的验证;


1.4 设计程序 - 两次密码输入一致

通常在实际应用中, 尤其是在账号的注册与密码的修改中, 需要进行密码的两次验证, 以确保两次密码输入为相同;

此处设计的程序为:

两个QLineEdit进行输入, 其中QLineEdit右侧存在QCheckBox复选框来进行密码显示状态切换;

对应的QLineEdit中需要设置PlaceHolderText输入提示词;

存在四个QLabel

  • 显示两次密码的输入情况
  • 两个提示密码框
  • 提示密码所需元素(正则)

一个QPushButtonEnable属性默认为false, 当两次密码相同时Enable属性为true, 点击按钮弹出弹框显示按钮;

密码需要包含大小写字母, 阿拉伯数字, 以及特殊符号且密码长度不少于八位, 其正则表达式为:

^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[^A-Za-z0-9]).{8,}$

  • 初始化控件

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        // 初始化控件显示
        ui->label_tip1->setText("Password");
        ui->label_tip2->setText("Confirm Password");
        ui->label_show->setText("密码为空");
        ui->pushButton_submit->setText("Submit");
        ui->checkBox_pw->setText("Show Password");
        ui->checkBox_cfpw->setText("Show Password");
    
        // LineEdit默认echoMode为Password
        ui->lineEdit_pw->setEchoMode(QLineEdit::Password);
        ui->lineEdit_cfpw->setEchoMode(QLineEdit::Password);
    
        // 设置placeHolderText
        ui->lineEdit_pw->setPlaceholderText("请输入密码...");
        ui->lineEdit_cfpw->setPlaceholderText("请再次输入密码...");
    
        // 设置PushButton的Enable为false
        ui->pushButton_submit->setEnabled(false);
    }
    
  • 设置校验器并绑定槽

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        // 初始化控件显示
        // ...
    
        // LineEdit默认echoMode为Password
        // ...
    
        // 设置placeHolderText
        // ...
    
        // 设置PushButton的Enable为false
        // ...
    
        // 设置密码校验器
        // // 1) 创建校验规则
        QRegularExpression rexp("^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[^A-Za-z0-9]).{8,}$");
        // // 2) 创建校验器
        QRegularExpressionValidator* rev = new QRegularExpressionValidator(rexp, this);
        // // 3) 校验器注册进QLineEdit
        ui->lineEdit_pw->setValidator(rev);
        ui->lineEdit_cfpw->setValidator(rev);
    
        // 绑定LineEdit槽
        connect(ui->lineEdit_pw, &QLineEdit::textEdited, this, &Widget::isAccepted);
        connect(ui->lineEdit_cfpw, &QLineEdit::textEdited, this, &Widget::isAccepted);
    }
    
  • 槽的设置

    // slot for PushButton
    void Widget::on_pushButton_submit_clicked()
    {
        QString str = "\nPassword:  ";
        str+=ui->lineEdit_cfpw->text();
        str+="\n";
        QMessageBox::information(this, "Password", str);
    }
    
    // slots for checkBox
    // 状态变化是否显示密码
    void Widget::on_checkBox_pw_toggled(bool checked)
    {
        ui->lineEdit_pw->setEchoMode(checked?QLineEdit::Normal:QLineEdit::Password);
    }
    void Widget::on_checkBox_cfpw_toggled(bool checked)
    {
        ui->lineEdit_cfpw->setEchoMode(checked?QLineEdit::Normal:QLineEdit::Password);
    }
    
    // slot for checks
    void Widget::isAccepted(){
    
        // 1) 获取两个LineEdit的内容
        QString pw = ui->lineEdit_pw->text();
        QString cfpw = ui->lineEdit_cfpw->text();
    
        // 2) 判断输入框是否为空
        if(pw.isEmpty() && cfpw.isEmpty()){
            ui->label_show->setText("密码为空");
            ui->label_show->setStyleSheet("color: white;\n");
            ui->pushButton_submit->setEnabled(false);
            return;
        }
    
        // 3) 判断两次密码是否为不一致
        if(pw != cfpw){
            ui->label_show->setText("密码不一致");
            ui->pushButton_submit->setEnabled(false);
            ui->label_show->setStyleSheet("color: red;\n");
            return;
        }
    
        // 4) 两次密码一致且不为空时判断是否符合要求
        int pos = 0;
        auto pwaccepted = ui->lineEdit_pw->validator()->validate(pw, pos);
        if(pwaccepted == QValidator::Acceptable){
            ui->pushButton_submit->setEnabled(true);
            ui->label_show->setText("密码一致");
            ui->label_show->setStyleSheet("color: green;");
        } else {
            ui->pushButton_submit->setEnabled(false);
            ui->label_show->setText("密码不符合要求");
            ui->label_show->setStyleSheet("color: red;\n");
        }
    }
    
  • 运行结果


2 QTextEdit

Qt中的多行文本框, 可输入多行文本, 其大致熟悉与常用信号与QLineEdit类似, 与之不同的是其可以是一个富文本或者Markdown的编辑器;

该控件在输入过程中可以换行;


2.1 常用属性

属性 说明
markdown 输入框内持有的内容, 支持Markdown格式, 能够自动对Markdown文本进行渲染成html
html 输入框内持有的内容, 可以支持大部分html标签, 包括imgtable
placeHolderText 输入框为空时提示的内容
readOnly 是否只读
undoRedoEnable 是否开启undo/redo功能:undo - ctrl + z``redo - ctrl + y
autoFormation 开启自动格式化
tabstopWidth tab缩进大小
overwriteMode 是否开启覆盖写模式
acceptRichText 是否接收富文本内容
verticalScrollBarPolicy 垂直方向滚动条的出现策略 - Qt::ScrollBarAsNeeded: 根据内容自动决定是否需要滚动条(默认值) - Qt::ScrollBarAlwaysOff: 总是关闭滚动条 - Qt::ScrollBarAlwaysOn: 总是显示滚动条
horizontalScrollBarPolicy 水平方向滚动条的出现策略(与 verticalScrollBarPolicy相同)

2.2 常用信号

信号 说明
textChanged() 文本内容改变时触发
selectionChanged() 选中范围改变时触发
cursorPositionChanged() 光标移动时触发
undoAvailable(bool) 可以进行undo操作时触发
readoAvailable(bool) 可以进行redo操作时触发
copyAvailable(bool) 文本被选中/取消选中时触发

2.3 使用示例

2.3.1 显示示例 (双 QTextEdit 一个用于编辑 一个用于显示 Markdown 内容)

该示例通过两个QTextEdit控件, 其中一个用于编辑Markdown, 另一个用于显示Markdown渲染后的内容;

这个例子主要涉及一个类似于Typora编辑器的小功能, 即左侧输入框用于输入文本, 右侧输入框用于以Markdown的形式渲染文本;

  • 初始化

    此处的初始化主要是将右侧的QTextEdit设置为只读;

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
        ui->textEdit_2->setReadOnly(true);
    }
    
  • 设置槽函数

    当左侧进行编辑时, 右侧需要进行同步渲染, 因此对应的信号为textChangeded(), 当左侧QTextEdit内的文本被编辑后, 保存进一个临时的QString中, 并将对应的QString输出至右侧的QTextEdit中;

    此处需要注意的是, 保存当前QTextEdit中文本的方式有几种, 分别为:

    • toMarkdown

      将文本框中的内容加上默认的Markdown标签或格式, 保存到QString中并返回;

    • toHtml

      将文本框中的内容加上默认的Html标签或格式, 保存到QString中并返回;

    • toPlainText

      将文本中的内容以纯文本格式保存到QString中并返回;

    除此之外QTextEdit控件内置了几个可以将对应文本内容进行对应格式渲染并植入QTextEdit的成员函数, 分别为:

    • setMarkdown(const QString&)

      传入一个QString对象, 该对象内部的内容将会被解析为Markdown并渲染;

    • setHtml(const QString&)

      传入一个QString对象, 该对象内部的内容将会被解析为Html并渲染;

    • setPlainText(const QString&)

      所传入的QString对象将会以纯文本的形式设置进QTextEdit中;

    因此此次我们槽函数的策略是以PlainText的方式获取文本, 并采用Markdown的格式输出进另一个QTextEdit中;

    void Widget::on_textEdit_textChanged()
    {
        QString _in = ui->textEdit->toPlainText();
        ui->textEdit_2->setMarkdown(_in);
        qDebug()<<_in;
    }
    
  • 运行结果


2.3.1 各个属性的打印

可以将上述的各个属性进行打印:

  • toHtml()

    在上文中说道, 我们可以使用toHtml等方法将对应文本框中的内容自动增加对应的标签;

    那么是否属实?

    此处我们通过上述的代码进行微改造, 将对应的输出的内容变化为toHtml, 且输入至另一个QTextEdit的内容采用setPlainText()来进行展示;

    其他代码不进行修改, 只对槽进行修改;

    void Widget::on_textEdit_textChanged()
    {
        QString _in = ui->textEdit->toHtml();
        ui->textEdit_2->setPlainText(_in);
    }
    

  • toMarkdown()

    对应的toMarkdown()我们也采用同样的方式进行试验;

    void Widget::on_textEdit_textChanged()
    {
        QString _in = ui->textEdit->toMarkdown();
        ui->textEdit_2->setPlainText(_in);
    }
    

    运行结果为:

    从结果可以看出, 结果与预期一致;

  • textChanged()

    该信号演示已经多次, 此处不进行赘述;

其他信号不进行赘述;


3 QComboBox

QComboBox控件是Qt中下拉框的角色, 其可以添加多个下拉选项;

当用户点击下拉按钮时, 若干个下拉选项将会被列举出来, 当然也可以设置显示最多允许的下拉条目数目(一页);


3.1 常用属性

属性 说明
currentText 当前选中的文本
currentIndex 当前选中的条目下标;(下标通常从0开始计算, 未选择时下标为 -1)
editable 是否允许修改true - QComboBox可以修改(行为类似QLineEdit, 同样支持设置 validator)false - QComboBox无法修改
iconSize 下拉框图标(小三角)大小
maxCount 最多允许有多少个条目

3.2 常用方法

方法 说明
addItem(const QString&) 增加一个条目
currentIndex() 获取当前条目下标
currentText() 获取当前条目的文本内容

3.3 核心信号

信号 说明
activated(int)/activated(const QString& text) 当用户正在选择条目时发出(焦点正在条目上, 但未实际选择条目[确认])
currentIndexChanged(int)/currentIndexChanged(const QSTring& text) 选项发生改变时发出(已实际选择条目)用户操作与程序操作都会触发该信号
editTextChanged(const QString& text) 当编辑框中的文本改变时发出(editabletrue时有效)

3.4 使用示例

3.4.1 城市选择器

一个程序存在三个QComboBox, 分别对应起点, 途经点以及终点, 当用户选择后可以通过QPushButton按钮来显示(弹窗)对应的内容;

按钮必须在三个选项都选择后才可以使用, 且途经点不能等于起点, 也不能等于终点;

对应的QStringList为:

QStringList provinces = {
    "安徽省", "北京市", "重庆市", "福建省", "甘肃省", "广东省", "广西壮族自治区", "贵州省", "海南省",
    "河北省", "河南省", "黑龙江省", "湖北省", "湖南省", "吉林省", "江苏省", "江西省", "辽宁省",
    "内蒙古自治区", "宁夏回族自治区", "青海省", "山东省", "山西省", "陕西省", "上海市", "四川省",
    "天津市", "西藏自治区", "新疆维吾尔自治区", "云南省", "浙江省", "香港特别行政区", "澳门特别行政区", "台湾省"
};
  • 初始化

    对控件显示进行初始化:

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        // 初始化
        ui->label_start->setText("出发地: ");
        ui->label_via->setText("途径地: ");
        ui->label_dest->setText("目的地: ");
        ui->pushButton_submit->setText("确认");
        ui->pushButton_submit->setEnabled(false);
        }
    
  • 初始化QComboBox

    初始化下拉框中的内容, 此处为地名, 直接采用QStringList进行初始化;

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        // 初始化
        // ...
    
        // 初始化三个comboBox
        QStringList provinces = {
            "安徽省", "北京市", "重庆市", "福建省", "甘肃省", "广东省", "广西壮族自治区", "贵州省", "海南省",
            "河北省", "河南省", "黑龙江省", "湖北省", "湖南省", "吉林省", "江苏省", "江西省", "辽宁省",
            "内蒙古自治区", "宁夏回族自治区", "青海省", "山东省", "山西省", "陕西省", "上海市", "四川省",
            "天津市", "西藏自治区", "新疆维吾尔自治区", "云南省", "浙江省", "香港特别行政区", "澳门特别行政区", "台湾省"
        };
        ui->comboBox_start->addItems(provinces);
        ui->comboBox_via->addItems(provinces);
        ui->comboBox_dest->addItems(provinces);
    }
    
  • 设置槽函数

    1. 按钮的槽函数

      按下后将会弹出弹窗显示对应信息;

      void Widget::on_pushButton_submit_clicked()
      {
          QString str = "\n出发地: \t"+ui->comboBox_start->currentText()+"\t"
                        +"\n\n途径地: \t"+ui->comboBox_via->currentText()+"\t"
                        +"\n\n目的地: \t"+ui->comboBox_dest->currentText()+"\t"
                        +"\n";
          QString title = "信息";
          QMessageBox::information(this, title, str);
      }
      
    2. QComboBox的槽函数

      void Widget::canCommit()
      {
          if(ui->comboBox_dest->currentText() == ui->comboBox_via->currentText()
              || ui->comboBox_start->currentText() == ui->comboBox_via->currentText()){
              ui->pushButton_submit->setEnabled(false);
              return;
          }
          ui->pushButton_submit->setEnabled(true);
      }
      

      当途经点与目的地或者起始点不同时才可以点击按钮;

  • 连接槽函数

    三个QComboBox都连接同一个槽函数加以判断;

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        // 初始化
        // ...
    
        // 初始化三个comboBox
        // ...
    
        // 连接槽
        connect(ui->comboBox_start, &QComboBox::currentTextChanged, this, &Widget::canCommit);
        connect(ui->comboBox_via, &QComboBox::currentTextChanged, this, &Widget::canCommit);
        connect(ui->comboBox_dest, &QComboBox::currentTextChanged, this, &Widget::canCommit);
    }
    
  • 运行结果


3.4.2 从文件中加载数据初始化 QComboBox

通常情况下, QComboBox中的数据是从磁盘或者网络中读取的, 这通常伴随着一些网络请求;

在C++中, 我们通常使用各种流来读取对应的数据, 当然可以通过流来获取对应的网络数据或是文件数据;

因此我们将上述的内容进行一个小改动, 将原有的QStringList中的内容转变为从文件中读取;

只需要在当前目录下存在对应的文件, 并且通过fstream流来进行读取数据即可;

对应文件名为 config.txt;

  • 修改初始化QComboBox细节

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        // ...
            
        std::ifstream in;
        in.open("F:\\CppCode\\qt-learning\\2025\\1117\\QComboBox\\config.txt");
        if(!in.is_open()) {
            qDebug()<<"fopen error";
            return;
        }
        std::string line;
        QStringList provinces;
        while(std::getline(in, line)){
            provinces.append(QString::fromStdString(line));
        }
        ui->comboBox_start->addItems(provinces);
        ui->comboBox_via->addItems(provinces);
        ui->comboBox_dest->addItems(provinces);
    
        connect(ui->comboBox_start, &QComboBox::currentTextChanged, this, &Widget::canCommit);
        connect(ui->comboBox_via, &QComboBox::currentTextChanged, this, &Widget::canCommit);
        connect(ui->comboBox_dest, &QComboBox::currentTextChanged, this, &Widget::canCommit);
    }
    

运行结果与 3.4.1 相同;


4 QSpinBox

Qt提供的一个微调按钮, 微调框内能够存储一个数值, 微调框的右侧存在两个按钮(up, down), 当按下这两个按钮时, 对应的数值将发生调整;

其中在Qt中我们将SpinBox分为两类:

  • QSpinBox
  • QDoubleSpin

而实际使用中, QSPinBoxQDoubleSpin除了数值方面以外, 其他内容通常都相同;


4.1 核心属性

属性 说明
value 存储的数值
singleStep 每次调整的步长(数据变换多少)
displayInteger 数字进制, 如displayInterger10则按照十进制, 2则为二进制表示
minimum 最小值
maximum 最大值
suffix 后缀
prefix 前缀
wrapping 是否允许换行
frame 是否带边框
alignment 文字对齐方式
readOnly 是否允许修改
buttonSymbol 按钮上的图标:- UpDownArrows 上下箭头形式- PlusMinus 加减号形式- NoButtons 没有按钮
accelerated 按下按钮是否加速调整
correctionMode 输入有误时如何调整- QAbstractSpinBox::CorrecttToPreviousValue: 如果用户输入了一个无效的值, 将会恢复为上一个有效值- QAbstractSpinBox::CorrectToNearestValue: 输入一个无效的值, 将会恢复为最接近的有效值
keyboardTrack 是否开启键盘跟踪- true 每次输入框内输入一个数字都会触发一次valueChanged()textChanged()信号- false 只有在最终按下Enter或者输入框失去焦点才会触发valueChanged()textChanged()信号

4.2 核心信号

信号 说明
textChanged(QString) 微调框的文本发生改变时触发, 参数QString带有前缀和后缀
valueChanged(int) 微调框文本发生改变时将会触发, 参数int表示当前的数值

4.3 使用示例

设计一个程序, 用于计算BMI并进行显示;

其中需要一个QSpinBox表示身高, 一个QDoubleSpinBox表示体重(kg);

每次调整数值都将会计算对应的BMI并显示到Label标签中, 对应的字体颜色也会根据情况发生变化, 当BMI正常时显示绿色, 超重显示橙色, 肥胖显示红色, 偏瘦显示蓝色;

  • 初始化控件

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        // 初始化Label
        ui->label_height->setText("身高");
        ui->label_weight->setText("体重");
    
        // 初始化上限与下限
        ui->spinBox_height->setMinimum(100);
        ui->spinBox_height->setMaximum(300);
    
        // 设置初始值与步长
        ui->doubleSpinBox_weight->setMinimum(20.0);
        ui->doubleSpinBox_weight->setMaximum(200.0);
        ui->doubleSpinBox_weight->setSingleStep(0.5);
    
        // 对Label进行初始化(动态)
        BMI();
        ui->label_show->setAlignment(Qt::AlignCenter);
    }
    

    该处调用了一个BMI()函数为自定义函数, 主要是更新Label中显示的内容;

  • 设计更新BMI函数

    该函数用于Label中显示的BMI值, 主要通过获取对应的数值并进行BMI计算;

    对计算结果进行判断从而显示对应的结果;

    void Widget::BMI()
    {
        int height_cm = ui->spinBox_height->value();      // 直接获取int值
        double weight_kg = ui->doubleSpinBox_weight->value();  // 直接获取double值
    
        if (height_cm > 0) {
            double height_m = height_cm / 100.0;  // 转换为米
            double bmi = weight_kg / (height_m * height_m);  // 标准公式(或用pow(height_m, 2))
    
            // 简单分类(可选,根据WHO标准)
            QString category;
            if (bmi < 18.5) {
                category = "偏瘦";
                ui->label_show->setStyleSheet("color: #5DE2E7;\nfont-size: 15px");
            }
            else if (bmi < 25) {
                category = "正常";
                ui->label_show->setStyleSheet("color: green;\nfont-size: 15px");
            }
            else if (bmi < 30) {
                category = "超重";
                ui->label_show->setStyleSheet("color: #FE9900;\nfont-size: 15px");
            }
            else {
                category = "肥胖";
                ui->label_show->setStyleSheet("color: red;\nfont-size: 15px");
            }
    
            // 更新UI标签
            ui->label_show->setText(QString("BMI: %1 (%2)").arg(bmi, 0, 'f', 2).arg(category));
    
            qDebug() << "BMI:" << bmi;  // 保留调试输出
        } else {
            ui->label_show->setText("BMI: N/A");
        }
    }
    
  • 连接槽函数

    除此之外还需要一个槽函数与对应的信号 QSpinBox::textChanged() 进行连接;

    当信号触发时调用该槽, 对应的槽将去调用BMI();

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
    {
        ui->setupUi(this);
    
        // 初始化Label
        // ...
    
        // 初始化上限与下限
        // ...
    
        // 设置初始值与步长
        // ...
    
        // 对Label进行初始化(动态)
        // ...
    
        // 连接槽
        connect(ui->doubleSpinBox_weight, &QDoubleSpinBox::textChanged, this, &Widget::showBMI);
        connect(ui->spinBox_height, &QSpinBox::textChanged, this, &Widget::showBMI);
    }
    
    void Widget::showBMI()
    {
        BMI();
    }
    
  • 运行结果


5 QDateEdit & QTimeEdit & QDateTimeEdit

QDateEdit, QTimeEditQDateTimeEdit三个控件都是针对日期与时间的微调框, 其本质是QSPinBox的变型控件;

  • QDateTimeEdit - 时间日期微调框
  • QDateEdit - 日期微调框
  • QTimeEdit - 时间微调框

由于三个控件类似, 且QDateTimeEdit控件结合了QDateEditQTimeEdit, 因此以QDateTimeEdit为例进行介绍;


5.1 核心属性

属性 说明
dateTime 时间日期的值, 形如 2000/1/1 0:00:00
date 单纯日期的值, 形如 2000/1/1
time 单纯时间的值, 形如 0:00:00
displayFormat 时间日期格式, 形如 yyyy/M/d H:mm- y表年份- M表月份- d表日期- H表小时- m表分钟- s表秒[ 此处的格式化符号含义可能在不同的库有不同的含义 ]
minimumDateTime 最小时间日期
maximumDateTime 最大时间日期
timeSpec Qt::LocalTime - 显示本地时间Qt::UTC - 显示协调世界时(UTC)Qt::OffsetFromUTC - 显示相对于UTC的偏移量(时差)

5.2 核心信号

信号 说明
dateChanged(QDate) 日期改变时触发
timeChanged(QTime) 时间改变时触发
dateTimeChanged(QDateTime) 日期或时间任意一个改变时触发

5.3 使用示例 - 两个日期相差时间

设计一个用例程序, 两个QDateTimeEdit控件(起始日期, 结束日期), 计算当前时间间隔差, 并展示在QLabel中显示时显示 XXX 天 XXX 小时 xxx 分钟;

添加一个CheckBox设为now, 当勾选时, 结束日期无法进行手动编辑, 终止日期将自动更新;

  • 前置条件

    在这个程序的功能中出现了一些自动的功能, 因此我们需要使用QTimer来设置自动更新的功能, 其次QTimer掌管着自动更新的核心操作, 因此其生命周期必须要与Widget相同, 需要将其设置为成员函数并进行初始化;

    /* widget.h */
    
    class Widget : public QWidget
    {
        Q_OBJECT
    
    public:
    	// ...
    private slots:
    	// ...
    private:
        Ui::Widget *ui;
        QTimer *timer; // timer
    
    };
    #endif // WIDGET_H
    
    /* widget.cpp */
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
        , timer(new QTimer(this))
    {
        ui->setupUi(this);
    
          // ...
    }
    

    除此之外我们需要控制终止时间必须在起始时间之后, 因此需要控制其对应的终止时间的 MinimumDateTime 必须要大于起始时间, 因此设置了一个UpdateEnd函数;

    void Widget::UpdateEnd()
    {
        QDateTime begin = ui->dateTimeEdit_begin->dateTime();
        ui->dateTimeEdit_end->setMinimumDateTime(begin);
    }
    

    对应的我们已经存在了一个QTimer成员, 该成员将调用start(int)函数, 每隔int时间将会发送一个timeout信号, 这是在checkBox被选中时的操作, 但此处我们只在意这个timeout的槽, 这个槽中, 我们需要时刻的更新终止时间的DateTime, 因此对应的槽为:

    void Widget::updateEndToCurrent()
    {
        ui->dateTimeEdit_end->setDateTime(QDateTime::currentDateTime());
    }
    
  • 初始化控件

    首先我们需要对控件进行初始化, 初始化显示;

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
        , timer(new QTimer(this))
    {
        ui->setupUi(this);
    
        // 初始化控件
        ui->label_begin->setText("起始时间");
        ui->label_end->setText("结束时间");
        ui->checkBox_lock->setText("当前时间");
        ui->label_show->setAlignment(Qt::AlignCenter);
    	
        // 对初始的时间显示进行
        UpdateEnd();
        UpdateShow();
    }
    

    在初始化时我们避免不了对首次时间的计算与显示, 因此先调用对应的函数进行更新;

  • 槽函数

    在前置中我们已经定义了一些槽函数, 但仍有槽函数未定义;

    1. 更新显示

      每次发生变化时, 我们都要对时间进行计算并显示, 因此需要一个槽函数来进行计算和更新显示;

      void Widget::UpdateShow()
      {
          // 获取当前时间
          QDateTime begin = ui->dateTimeEdit_begin->dateTime();
          QDateTime end = ui->dateTimeEdit_end->dateTime();
      
          // 计算天时分
          int days = begin.secsTo(end)/(3600*24); // 天
          int hours = begin.secsTo(end)/3600%24; // 小时
          int minus = begin.secsTo(end)/60%60; // 分钟
      
          QString restime = "过去了 "+ QString::number(days) +
                            " 天 "+QString::number(hours) +
                            " 小时 " + QString::number(minus) + " 分钟";
          ui->label_show->setText(restime);
      }
      
    2. 锁定时间

      checkBox被选中时, 将立马将对应的QDateTimeEdit设置为已读情况, 并时刻更新其值;

      更新值的槽函数我们已经进行了定义, 当该checkBox被选中时, 我们需要首先立马对终止时间进行更新, 更新后启动QTimer的计时, 每一秒更新一次;

      void Widget::on_checkBox_lock_toggled(bool checked)
      {
          if(checked){
              ui->dateTimeEdit_end->setReadOnly(true);
              updateEndToCurrent();
              timer->start(1000); // 隔一秒发送一次timeout信号
              return;
          }
          ui->dateTimeEdit_end->setReadOnly(false);
          timer->stop();
      }
      
  • 完整代码

    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        , ui(new Ui::Widget)
        , timer(new QTimer(this))
    {
        ui->setupUi(this);
    
        // 初始化控件
        ui->label_begin->setText("起始时间");
        ui->label_end->setText("结束时间");
        ui->checkBox_lock->setText("当前时间");
        ui->label_show->setAlignment(Qt::AlignCenter);
    
    
        UpdateShow();
        UpdateEnd();
    
        connect(ui->dateTimeEdit_begin, &QDateTimeEdit::dateTimeChanged, this, &Widget::UpdateShow);
        connect(ui->dateTimeEdit_end, &QDateTimeEdit::dateTimeChanged, this, &Widget::UpdateShow);
    
    
        connect(ui->dateTimeEdit_begin, &QDateTimeEdit::dateTimeChanged, this, &Widget::UpdateEnd);
        connect(ui->dateTimeEdit_end, &QDateTimeEdit::dateTimeChanged, this, &Widget::UpdateEnd);
    
        connect(timer, &QTimer::timeout, this, &Widget::updateEndToCurrent);
    }
    
    Widget::~Widget()
    {
        delete ui;
    }
    
    void Widget::UpdateShow()
    {
        // 获取当前时间
        QDateTime begin = ui->dateTimeEdit_begin->dateTime();
        QDateTime end = ui->dateTimeEdit_end->dateTime();
    
        // 计算天时分
        int days = begin.secsTo(end)/(3600*24);
        int hours = begin.secsTo(end)/3600%24;
        int minus = begin.secsTo(end)/60%60;
    
        QString restime = "过去了 "+ QString::number(days) +
                          " 天 "+QString::number(hours) +
                          " 小时 " + QString::number(minus) + " 分钟";
    
        ui->label_show->setText(restime);
    }
    void Widget::UpdateEnd()
    {
        QDateTime begin = ui->dateTimeEdit_begin->dateTime();
        ui->dateTimeEdit_end->setMinimumDateTime(begin);
    }
    void Widget::on_checkBox_lock_toggled(bool checked)
    {
        if(checked){
            ui->dateTimeEdit_end->setReadOnly(true);
            updateEndToCurrent();
            timer->start(1000); // 隔一秒发送一次timeout信号
            return;
        }
        ui->dateTimeEdit_end->setReadOnly(false);
        timer->stop();
    }
    void Widget::updateEndToCurrent()
    {
        ui->dateTimeEdit_end->setDateTime(QDateTime::currentDateTime());
    }
    
  • 运行结果


标题:『 QT 』输入类控件 (一)
作者:orion
地址:http://orionpeng.top/articles/2025/11/18/1763448894052.html