QTextEdit和光标交互

时间:2014-01-07 16:56:55

标签: qt qtextedit

我正在修改Qt 5终端示例并使用QTextEdit窗口作为终端控制台。我遇到了几个问题。

  1. Qt对传入字符串中的回车('\ r')做了一个奇怪的解释。偶尔,在3-7发送之后,它将('\ r')解释为新行('\ n'),最令人讨厌。当我终于发现我选择从输入数据中过滤掉所有'\ r'。 这种行为是由于某些设置造成的吗?

  2. 让光标交互正常工作有点问题。我希望控制台通过复选框选择自动滚动。我还希望可以在控制台运行时选择文本,而不会在新数据到来时丢失选择。

  3. 这是我当前的打印输出功能,它是一个插槽,连接到任何数据到达时发出的信号:

     void MainWindow::printSerialString(QString& toPrint)
     {
        static int cursPos=0;
    
        //Set the cursorpos to the position from last printout
        QTextCursor c = ui->textEdit_console->textCursor();
        c.setPosition(cursPos);
        ui->textEdit_console->setTextCursor( c );
    
        ui->textEdit_console->insertPlainText(toPrint);
        qDebug()<<"Cursor: " << ui->textEdit_console->textCursor().position();
    
        //Save the old cursorposition, so the user doesn't change it
        cursPos= ui->textEdit_console->textCursor().position();
    
        toPrint.clear();
     }
    

    我遇到的问题是,如果用户在控制台中点击,光标会改变位置,并且后面的传入数据会在错误的位置结束。的问题:

    • 如果用户标记了某个部分,则新数据到来时标记会丢失。

    • 当像这样“强制”指针时,它会得到一个相当丑陋的自动滚动行为,无法禁用。

    • 如果光标被程序的另一部分更改为打印输出,我还必须以某种方式记录。

      • 附加功能听起来像一个更合乎逻辑的解决方案,适用于附加完整的完整字符串,但在打印传入字符串的一部分时会显示不稳定的行为,将字符和新行放在任何地方。

      • 我没有找到关于此的单一设置,但应该有一个?将QTextEdit设置为“readOnly”不会禁用游标交互。

    3.想法是在控制台中有两个游标。一个不可见的,用于打印输出,根本不可能为用户操作,一个是可见的,使用户可以选择文本。但是如何做到这一点击败我:)任何相关的例子,常见问题或指南非常感谢。

2 个答案:

答案 0 :(得分:3)

我为SWI-Prolog pqConsole做了一个基于QTextEdit的终端,它具有一些功能,如ANSI着色序列(子集)解码,命令历史记录管理,多个插入点,完成,提示...... / p>

它在提供模式REPL(读/评估/打印/循环)时运行非阻塞用户界面,这是解释语言最常用的接口,如Prolog。

代码由于线程问题而复杂化(根据用户请求,可能有多个控制台,或主线上的多个线程交互),但核心相当简单。我只是跟踪插入点,并允许光标四处移动,在输出区域禁用编辑。

pqConsole它是一个共享对象(我喜欢这种代码重用),但是对于部署,独立程序swipl-win更方便。

这里有一些选定的片段,用于控制输出的状态变量是 promptPosition fixedPosition

/** display different cursor where editing available
 */
void ConsoleEdit::onCursorPositionChanged() {
    QTextCursor c = textCursor();
    set_cursor_tip(c);
    if (fixedPosition > c.position()) {
        viewport()->setCursor(Qt::OpenHandCursor);
        set_editable(false);
        clickable_message_line(c, true);
    } else {
        set_editable(true);
        viewport()->setCursor(Qt::IBeamCursor);
    }

    if (pmatched.size()) {
        pmatched.format_both(c);
        pmatched = ParenMatching::range();
    }

    ParenMatching pm(c);
    if (pm)
        (pmatched = pm.positions).format_both(c, pmatched.bold());
}

/** strict control on keyboard events required
 */
void ConsoleEdit::keyPressEvent(QKeyEvent *event) {

    using namespace Qt;
...
    bool accept = true, ret = false, down = true, editable = (cp >= fixedPosition);

    QString cmd;

    switch (k) {

    case Key_Space:
        if (!on_completion && ctrl && editable) {
            compinit2(c);
            return;
        }
        accept = editable;
        break;
    case Key_Tab:
        if (ctrl) {
            event->ignore(); // otherwise tab control get lost !
            return;
        }
        if (!on_completion && !ctrl && editable) {
            compinit(c);
            return;
        }
        break;

    case Key_Backtab:
        // otherwise tab control get lost !
        event->ignore();
        return;

    case Key_Home:
        if (!ctrl && cp > fixedPosition) {
            c.setPosition(fixedPosition, (event->modifiers() & SHIFT) ? c.KeepAnchor : c.MoveAnchor);
            setTextCursor(c);
            return;
        }
    case Key_End:
    case Key_Left:
    case Key_Right:
    case Key_PageUp:
    case Key_PageDown:
        break;
}

你可以看到键盘管理最复杂...

/** \brief send text to output
 *
 *  Decode ANSI terminal sequences, to output coloured text.
 *  Colours encoding are (approx) derived from swipl console.
 */
void ConsoleEdit::user_output(QString text) {

#if defined(Q_OS_WIN)
    text.replace("\r\n", "\n");
#endif

    QTextCursor c = textCursor();
    if (status == wait_input)
        c.setPosition(promptPosition);
    else {
        promptPosition = c.position();  // save for later
        c.movePosition(QTextCursor::End);
    }

    auto instext = [&](QString text) {
        c.insertText(text, output_text_fmt);
        // Jan requested extension: put messages *above* the prompt location
        if (status == wait_input) {
            int ltext = text.length();
            promptPosition += ltext;
            fixedPosition += ltext;
            ensureCursorVisible();
        }
    };

// filter and apply (some) ANSI sequence
int pos = text.indexOf('\x1B');
if (pos >= 0) {
    int left = 0;
...

        instext(text.mid(pos));
    }
    else
        instext(text);

    linkto_message_source();
}

我认为你不应该使用静态变量(就像你的代码中出现的那样),而是依赖于QTextCursor接口和一些状态变量,就像我一样。

答案 1 :(得分:0)

通常,对于功能丰富的终端小部件使用QTextEdit似乎是一个坏主意。您需要正确处理转义序列(如光标移动和颜色模式设置),以某种方式将编辑粘贴到当前终端“页面”的左上角等。更好的解决方案可能是继承QScrollArea和自己实现所有需要的绘画选择 - 滚动功能。

作为针对您的某些问题的临时解决方法,我建议您使用ui->textEdit_console->append(toPrint)代替insertPlainText(toPrint)

要自动滚动修改,您可以使用QTextEdit::moveCursor()将光标移至最后并调用QTextEdit::ensureCursorVisible()