Qt 5.8 QTextEdit 文本光标颜色不会改变

Posted

技术标签:

【中文标题】Qt 5.8 QTextEdit 文本光标颜色不会改变【英文标题】:Qt 5.8 QTextEdit Text Cursor Color Won't Change 【发布时间】:2019-09-16 13:37:10 【问题描述】:

我正在尝试将 QTextEdit 上的文本光标设置为红色 (rgb(255,0,0))。尽管我尽了最大的努力,它仍继续闪烁白色。

根据我的发现,样式表的“颜色”属性应该改变光标的颜色。不知道怎么回事。

我的代码:

    textEntry = new QTextEdit();
    textEntry->setFont(QFont("Electrolize", 9, 1));
    textEntry->setMinimumHeight(25);
    textEntry->setMaximumHeight(25);
    textEntry->setLineWrapMode(QTextEdit::NoWrap);
    textEntry->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    textEntry->setStyleSheet("color: rgb(255, 0, 0);"
                             "border: 1px solid rgb(255, 0, 0);");

编辑: 我鼓励完整阅读 Scheff 的回答。这很棒。不过,我注意到用他的解决方案创建的光标没有闪烁,所以我想与我的(没有经验的)添加分享从 Scheff 的代码派生的闪烁版本。

TextEdit.h

#ifndef TEXTEDIT_H
#define TEXTEDIT_H

#include <QTextEdit>
#include <QTimer>

class TextEdit : public TextEdit

    Q_OBJECT
public:
    explicit TextEdit(QWidget *parent = nullptr);

private:
    QTimer *timer;
    QPainter *pPainter;
    bool bCursorVisible;

protected:
    virtual void paintEvent(QPaintEvent *pEvent) override;

signals:
    sendUpdate();

public slots:
    void timerSlot();
;

#endif // TEXTEDIT_H

文本编辑.cpp

#include "textedit.h"

#include <QPainter>
#include <QColor>
#include <QTimer>

TextEdit::TextEdit(QWidget *parent) : QTextEdit(parent) 
    bCursorVisible = true;

    timer = new QTimer(this);
    timer->start(500);
    connect(this, SIGNAL(sendUpdate()), this, SLOT(update()));
    connect(timer, SIGNAL(timeout()), this, SLOT(timerSlot()));


void TextEdit::paintEvent(QPaintEvent *event)

  // use paintEvent() of base class to do the main work
  QTextEdit::paintEvent(event);
  // draw cursor (if widget has focus)
  if (hasFocus()) 
    if(bCursorVisible) 
        const QRect qRect = cursorRect(textCursor());
        QPainter qPainter(viewport());
        qPainter.fillRect(qRect, QColor(255, 0, 0, 255));
     else 
        const QRect qRect = cursorRect(textCursor());
        QPainter qPainter(viewport());
        qPainter.fillRect(qRect, QColor(0, 0, 0, 255));
    
  


void TextEdit::timerSlot() 
    if(bCursorVisible) 
        bCursorVisible = false;
     else 
        bCursorVisible = true;
    

    emit sendUpdate();

【问题讨论】:

你在哪里读到样式表的“color”属性应该改变光标的颜色?我在 Qt 文档中找到的所有内容。 Qt Style Sheets Reference color用于呈现文本的颜色。 @Scheff 这就是我从网上其他问答中听到的。 ***.com/questions/26616401/… forum.qt.io/topic/20769/… 还有其他的,但是和你一样,我在 Qt 样式表中找不到对此的引用 嗯。 1.) 我会考虑 Qt 文档。作为关于 Qt 的原始来源。 2.) 这可能是样式引擎的主题。因此,我不会怀疑这是否可以在一个系统上工作,但不能在另一个系统上工作。不过,我也发现了类似的提示,例如这个:(Solved)Change text cursor color in QTextEdit。这让我想到了另一个想法:你试过QTextEdit::setTextColor()吗? Per 1.) 您是否在 Qt 文档中找到了对文本光标颜色的任何引用?每 2.) 我同意 100%,如果有人想知道,我在 Win7 上。我尝试了 setTextColor(),但这对文本光标没有影响(单独或与样式表颜色属性结合使用)。我在这里不知所措。感觉可能与 QPallette 相关?光标当前在我的黑色背景上是白色的.. 所以有些东西正在努力防止它在黑色背景上变黑。 【参考方案1】:

事先与 OP 进行了一些对话,因为我非常怀疑 QTextEdit 的颜色属性是否也负责文本光标的颜色。

所有我在Qt Style Sheets Reference找到的:

用于渲染文本的颜色。

所有尊重 QWidget::palette 的小部件都支持此属性。

如果未设置此属性,则默认值为在小部件调色板中为 QWidget::foregroundRole 设置的任何内容(通常为黑色)。

出于好奇,我稍微摆弄了一下QTextEdit的颜色。

    我可以重现 OP 描述的内容: 更改QTextEdit 的文本颜色(例如QTextEdit::setTextColor())会影响之后键入的插入文本,但不会更改文本光标颜色(至少在我测试的平台上)。

    在摆弄时,我意识到另一个鼓励我写下这个答案的事实: 恕我直言,文本光标忽略任何颜色设置。相反,它会反转绘制的文本光标栏下的像素。 看看QPainter::RasterOp_NotSource 看看我的意思。

我的示例应用程序testQTextEditCursorColor.cc

#include <QtWidgets>

class ColorButton: public QPushButton 
  private:
    QColor _qColor;

  public:
    explicit ColorButton(
      const QString &text, const QColor &qColor = Qt::black,
      QWidget *pQParent = nullptr):
      QPushButton(text, pQParent)
    
      setColor(qColor);
    
    virtual ~ColorButton() = default;
    ColorButton(const ColorButton&) = delete;
    ColorButton& operator=(const ColorButton&) = delete;

    const QColor& color() const  return _qColor; 
    void setColor(const QColor &qColor)
    
      _qColor = qColor;
      QFontMetrics qFontMetrics(font());
      const int h = qFontMetrics.height();
      QPixmap qPixmap(h, h);
      qPixmap.fill(_qColor);
      setIcon(qPixmap);
    

    QColor chooseColor()
    
      setColor(QColorDialog::getColor(_qColor, this, text()));
      return _qColor;
    
;

int main(int argc, char **argv)

  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  qDebug() << app.style();
  // setup GUI
  QMainWindow qWin;
  qWin.resize(250, 100);
  qWin.setWindowTitle("Test Set Cursor Color");
  QTextEdit qTextEdit;
  qWin.setCentralWidget(&qTextEdit);
  QToolBar qToolBar;
  ColorButton qBtnColor("Text Color", qTextEdit.palette().color(QPalette::Text));
  qToolBar.addWidget(&qBtnColor);
  ColorButton qBtnColorBg("Background", qTextEdit.palette().color(QPalette::Base));
  qToolBar.addWidget(&qBtnColorBg);
  qWin.addToolBar(&qToolBar);
  qWin.show();
  // install signal handlers
  QObject::connect(&qBtnColor, &QPushButton::clicked,
    [&]()  qTextEdit.setTextColor(qBtnColor.chooseColor()); );
  QObject::connect(&qBtnColorBg, &QPushButton::clicked,
    [&]() 
      QPalette qPal = qTextEdit.palette();
      qPal.setColor(QPalette::Base, qBtnColorBg.chooseColor());
      qTextEdit.setPalette(qPal);
    );
  // runtime loop
  return app.exec();

以及对应的Qt工程文件testQTextEditCursorColor.pro

SOURCES = testQTextEditCursorColor.cc

QT += widgets

在 Windows 10 上的 cygwin64 中编译和测试:

$ qmake-qt5 testQTextEditCursorColor.pro

$ make && ./testQTextEditCursorColor
g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQTextEditCursorColor.o testQTextEditCursorColor.cc
g++  -o testQTextEditCursorColor.exe testQTextEditCursorColor.o   -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread 
Qt Version: 5.9.4
QFusionStyle(0x6000e10c0, name = "fusion")

因此,黑色生成白色光标,白色生成黑色光标(与任何颜色设置无关)。假设我上面的陈述是正确的,青色背景(#00ffff)应该是红色光标(#ff0000):

为了比较,我写了一个CMake脚本CMakeLists.txt

project(QTextEditCursorColor)

cmake_minimum_required(VERSION 3.10.0)

set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

find_package(Qt5Widgets CONFIG REQUIRED)

include_directories("$CMAKE_SOURCE_DIR")

add_executable(testQTextEditCursorColor testQTextEditCursorColor.cc)

target_link_libraries(testQTextEditCursorColor Qt5::Widgets)

再次在VS2017中编译测试:

Qt Version: 5.11.2
QWindowsVistaStyle(0x1c1ed936690, name = "windowsvista")

(请注意,不同风格的引擎。)

Windows GDI 中的渲染很明显,字形像素也被反转了(但我在上面的 X11 测试中注意到了同样的情况):


考虑到以上,很明显使用中间灰色作为背景色是个坏主意。例如的按位非#808080#7f7f7f 并且这两种颜色之间几乎没有对比。 (我没有提供快照,因为我无法识别为绘制文本光标的快照按下 Print 键的正确时间。)


OP 提到了另一个问答:SO: Qt 5.3 QPlainTextEdit Change the QTextCursor color。虽然,这个答案被接受并赞成,但如上所述以任何其他方式改变我这边的光标颜色并没有帮助。这些是修改,我在我的示例上尝试过:

QTextEdit 替换为QPlainTextEditqTextEdit.setCursorWidth()改变文本光标宽度 使用样式表而不是修改调色板中的颜色

包括在“字面上”的链接答案中使用公开的代码。


在与 thuga(SO: Qt 5.3 QPlainTextEdit Change the QTextCursor color 已接受答案的作者)进行了一番交谈后,似乎有一个关于 Qt 5.8 的错误报告:

Qt 5.8 no longer allows QPlainTextEdit's cursor color to be set

在撰写本文时标记为Unresolved。 (目前Qt5.12是最新版本。)


在解释了为什么它不能开箱即用之后,最后一个示例如何使用自定义绘制的光标来实现 OP 意图:

#include <QtWidgets>

class TextEdit: public QTextEdit 
  protected:
    virtual void paintEvent(QPaintEvent *pEvent) override;
;

void TextEdit::paintEvent(QPaintEvent *pQEvent)

  // use paintEvent() of base class to do the main work
  QTextEdit::paintEvent(pQEvent);
  // draw cursor (if widget has focus)
  if (hasFocus()) 
    const QRect qRect = cursorRect(textCursor());
    QPainter qPainter(viewport());
    qPainter.fillRect(qRect, textColor());
  


class ColorButton: public QPushButton 
  private:
    QColor _qColor;

  public:
    explicit ColorButton(
      const QString &text, const QColor &qColor = Qt::black,
      QWidget *pQParent = nullptr):
      QPushButton(text, pQParent)
    
      setColor(qColor);
    
    virtual ~ColorButton() = default;
    ColorButton(const ColorButton&) = delete;
    ColorButton& operator=(const ColorButton&) = delete;

    const QColor& color() const  return _qColor; 
    void setColor(const QColor &qColor)
    
      _qColor = qColor;
      QFontMetrics qFontMetrics(font());
      const int h = qFontMetrics.height();
      QPixmap qPixmap(h, h);
      qPixmap.fill(_qColor);
      setIcon(qPixmap);
    

    QColor chooseColor()
    
      setColor(QColorDialog::getColor(_qColor, this, text()));
      return _qColor;
    
;

int main(int argc, char **argv)

  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  qDebug() << app.style();
  // setup GUI
  QMainWindow qWin;
  qWin.resize(250, 100);
  qWin.setWindowTitle("Test Set Cursor Color");
  TextEdit qTextEdit;
  qWin.setCentralWidget(&qTextEdit);
  qTextEdit.setCursorWidth(QFontMetrics(qTextEdit.font()).averageCharWidth());
  QToolBar qToolBar;
  ColorButton qBtnColor("Text Color",
    qTextEdit.palette().color(QPalette::Text));
  qToolBar.addWidget(&qBtnColor);
  ColorButton qBtnColorBg("Background",
    qTextEdit.palette().color(QPalette::Base));
  qToolBar.addWidget(&qBtnColorBg);
  qWin.addToolBar(&qToolBar);
  qWin.show();
  // install signal handlers
  QObject::connect(&qBtnColor, &QPushButton::clicked,
    [&]()  qTextEdit.setTextColor(qBtnColor.chooseColor()); );
  QObject::connect(&qBtnColorBg, &QPushButton::clicked,
    [&]() 
      QPalette qPal = qTextEdit.palette();
      qPal.setColor(QPalette::Base, qBtnColorBg.chooseColor());
      qTextEdit.setPalette(qPal);
    );
  // runtime loop
  return app.exec();

QTextEdit 被派生的TextEdit 替换为被覆盖的paintEvent()

TextEdit::paintEvent() 中调用QTextEdit::paintEvent() 来完成主要工作。之后,光标在textColor 中用矩形(重新)绘制。 (这只是覆盖了已经渲染的内置文本光标。)

注意:

小陷阱是在TextEdit::paintEvent() 中使用QPainter。因为QTextEdit 派生自QAbstractScrollArea,所以QPainter qPainter(this); 是错误的。相反,必须使用QPainter qPainter(viewport());。这在 Qt 文档中有所提及。对于QAbstractScrollArea::paintEvent()

注意:如果你打开一个painter,一定要在viewport()上打开它。

【讨论】:

我很想看看那个样本。如果可能的话,我想要一个解决方案。 你是冠军。非常感谢! 如何在 python 3 pyqt5 中做同样的事情? @VictorVosMottorthanksMonica 我添加了another answer。 (SO 不是代码编写服务。不过,我承认 Python 可能比 C++ 更受欢迎,并且发现将我的示例代码移植到 Python 是值得的(挑战)。);-) 谢谢,我知道“SO不是代码编写服务”,但我想添加pyqt5版本非常有用,因为它是广泛使用的库。【参考方案2】:

根据要求,我的另一个answer中的示例程序的Python3 / PyQt5端口:

#!/usr/bin/python3

import sys
from PyQt5.QtCore import QT_VERSION_STR, QRect
from PyQt5.QtWidgets import QApplication, QMainWindow, QToolBar
from PyQt5.QtGui import QPainter, QIcon, QPixmap, QFontMetrics, QPalette
from PyQt5.QtWidgets import QTextEdit
from PyQt5.QtWidgets import QPushButton, QColorDialog


class TextEdit(QTextEdit):

    def __init__(self, parent=None):
        QTextEdit.__init__(self, parent)

    def paintEvent(self, event):
        # use paintEvent() of base class to do the main work
        QTextEdit.paintEvent(self, event)
        # draw cursor (if widget has focus)
        if self.hasFocus():
            rect = self.cursorRect(self.textCursor())
            painter = QPainter(self.viewport())
            painter.fillRect(rect, self.textColor())


class ColorButton(QPushButton):

    def __init__(self, text, custom_color, parent=None):
        QPushButton.__init__(self, text, parent)
        self.setColor(custom_color)

    def color(self):
        return self.custom_color

    def setColor(self, custom_color):
        self.custom_color = custom_color
        font_metrics = QFontMetrics(self.font())
        h = font_metrics.height()
        pixmap = QPixmap(h, h)
        pixmap.fill(self.custom_color)
        self.setIcon(QIcon(pixmap))

    def chooseColor(self):
        self.setColor(QColorDialog().getColor(self.custom_color))
        return self.custom_color


if __name__ == '__main__':
    print("Qt Version: ".format(QT_VERSION_STR))
    app = QApplication(sys.argv)
    print(app.style())
    # build GUI
    win = QMainWindow()
    win.resize(250, 100)
    win.setWindowTitle("Test Set Cursor Color")
    text_edit = TextEdit()
    text_edit.setCursorWidth(QFontMetrics(text_edit.font()).averageCharWidth())
    win.setCentralWidget(text_edit)
    tool_bar = QToolBar()
    btn_color = ColorButton(
        "Text Color", text_edit.palette().color(QPalette.Text))
    tool_bar.addWidget(btn_color)
    btn_color_bg = ColorButton(
        "Background", text_edit.palette().color(QPalette.Base))
    tool_bar.addWidget(btn_color_bg)
    win.addToolBar(tool_bar)
    win.show()
    # install signal handlers
    btn_color.clicked.connect(
        lambda state: text_edit.setTextColor(btn_color.chooseColor()))

    def on_click(state):
        palette = text_edit.palette()
        palette.setColor(QPalette.Base, btn_color_bg.chooseColor())
        text_edit.setPalette(palette)

    btn_color_bg.clicked.connect(on_click)
    # runtime loop
    sys.exit(app.exec_())

输出:

Qt Version: 5.9.3
<PyQt5.QtWidgets.QCommonStyle object at 0x6ffffd8dc18>

【讨论】:

以上是关于Qt 5.8 QTextEdit 文本光标颜色不会改变的主要内容,如果未能解决你的问题,请参考以下文章

具有不同文本颜色的 QTextEdit (Qt / C++)

在Qt中,QTextEdit内容清空后字体和颜色也变回原来的状态

在 QT4.8.5 中的 QTextEdit 上显示光标

在qt QTextEdit控件中,怎么得到光标所在行的数据?

android 上 QTextEdit 的选择

QTextEdit实现自定义关键字着色(代码块着色)