系列总链接:
https://blog.csdn.net/qq_22122811/article/details/108007519
字符映射示例展示了如何创建一个既能显示自己的内容又能响应用户输入的自定义小部件。
该示例显示了一个字符数组,用户可以单击该数组在行编辑中输入文本。然后可以将行编辑的内容复制到剪贴板中,并粘贴到其他应用程序中。这类工具的目的是允许用户输入键盘上不可用或难以找到的字符。
该示例由以下类组成:
以当前字体和样式显示可用字符。
MainWindow提供了一个标准的主窗口,其中包含字体和样式信息、字符视图、行编辑以及将文本提交到剪贴板的按钮。
具体讲解在:
Character Map Example | Qt Widgets 5.15.0
https://doc.qt.io/qt-5/qtwidgets-widgets-charactermap-example.html
运用控件:
QCheckBox:
QComcoBox:
QScrollArea:
主要的类:
QMenu:
QFontComboBox:
QFontDatabase:
QSignalBlocker:
QClipboard:
QDialogButtonBox
QToolTip
QFontMetrics
主要过程介绍:
1.点击Filter(过滤指定的字体),如何切换显示字体:
Monospaced:等宽字体:
scalable:一,每一;
proportional:专业的,职业的;
创建过滤下拉框:
QLabel *filterLabel = new QLabel(tr("Filter:"));
filterCombo = new QComboBox;
filterCombo->addItem(tr("All"), QVariant::fromValue(QFontComboBox::AllFonts));
filterCombo->addItem(tr("Scalable"), QVariant::fromValue(QFontComboBox::ScalableFonts));
filterCombo->addItem(tr("Monospaced"), QVariant::fromValue(QFontComboBox::MonospacedFonts));
filterCombo->addItem(tr("Proportional"), QVariant::fromValue(QFontComboBox::ProportionalFonts));
filterCombo->setCurrentIndex(0);
connect(filterCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &MainWindow::filterChanged);
void MainWindow::filterChanged(int f)
{
const QFontComboBox::FontFilter filter =
filterCombo->itemData(f).value<QFontComboBox::FontFilter>();
fontCombo->setFontFilters(filter);
statusBar()->showMessage(tr("%n font(s) found", nullptr, fontCombo->count()));
}
QFontComboBox::setFontFilters(FontFilter):
设置fontComboBox的字体更改,更改之后,会触发QFontComboBox的indexChanged信号,字符表随即绘画;
2.点击Font,如何切换字符表的字体风格显示,宋体,行楷等等:
connect(fontCombo, &QFontComboBox::currentFontChanged,
this, &MainWindow::findStyles);
connect(fontCombo, &QFontComboBox::currentFontChanged,
this, &MainWindow::findSizes);
connect(fontCombo, &QFontComboBox::currentFontChanged,
characterWidget, &CharacterWidget::updateFont);
void CharacterWidget::updateFont(const QFont &font)
{
displayFont.setFamily(font.family());
calculateSquareSize();
adjustSize();
update();
}
3.点击Size,如何切换字符表的字体大小:
建立尺寸下拉框与字符表更新尺寸槽的联系:
connect(sizeCombo, &QComboBox::currentTextChanged,
characterWidget, &CharacterWidget::updateSize);
更新尺寸:
void CharacterWidget::updateSize(const QString &fontSize)
{
displayFont.setPointSize(fontSize.toInt());
calculateSquareSize();
adjustSize();
update();
}
设置计算每个方格的尺寸:
void CharacterWidget::calculateSquareSize()
{
squareSize = qMax(16, 4 + QFontMetrics(displayFont, this).height());
}
QFontMetrics的介绍:
QFontMetrics(Metrics n.度量)类提供字体规格信息。
QFontMetrics函数计算给定字体的字符和字符串的大小。您可以通过三种方式创建QFontMetrics对象:
用QFont调用QFontMetrics构造函数会为屏幕兼容字体创建字体度量对象,即该字体不能为打印机字体。如果以后更改字体,则不会更新字体指标对象。
https://www.cnblogs.com/liuruoqian/p/12901565.html
https://blog.csdn.net/u014597198/article/details/52798239
adjustSize()介绍:Adjusts the size of the widget to fit its contents. 布局之后,更改了字体大小,调用这个函数,重新调整尺寸;
update(): 重新绘画所有的方格和字;
执行update(), 即执行paintEvent():
4.点击Style,如何切换字符表,是粗体,斜体,还是常规体:
styleCombo为style的下拉框控件:
connect(styleCombo, &QComboBox::currentTextChanged,
characterWidget, &CharacterWidget::updateStyle);
当改动style,会调用CharacterWidget的updateStyle():
void CharacterWidget::updateStyle(const QString &fontStyle)
{
QFontDatabase fontDatabase;
const QFont::StyleStrategy oldStrategy = displayFont.styleStrategy();
displayFont = fontDatabase.font(displayFont.family(), fontStyle, displayFont.pointSize());
displayFont.setStyleStrategy(oldStrategy);
calculateSquareSize();
adjustSize();
update();
}
QFontDatabase类:
QFontDatabase类提供了有关底层窗口系统中可用字体的信息。
QFont的styleStrategy():
返回StyleStrategy。
样式策略影响字体匹配算法。有关可用策略的列表,请参阅QFont::StyleStrategy。
QFontDatabase的QFont QFontDatabase::font(const QString &family, const QString &style, int pointSize) const:
返回一个QFont对象,该对象具有家族、样式和点大小pointSize。如果无法创建匹配的字体,则返回一个使用应用程序默认字体的QFont对象。
接着设置样式策略,计算方格尺寸,调整整体布局,重新绘画;
5.字符库如何填充进字符表中:
QFontComboBox类:
QLabel *fontLabel = new QLabel(tr("Font:"));
fontCombo = new QFontComboBox;
QFontComboBox小部件是一个组合框,允许用户选择字体族。
当用户调用show()函数,CharacterWidget(字符显示的部件)部件开始绘画,执行paintEvent():
event的范围是当前所看到的页面,不包括滚动以后的其他页面:
QRect redrawRect = event->rect();
int beginRow = redrawRect.top() / squareSize;
int endRow = redrawRect.bottom() / squareSize;
int beginColumn = redrawRect.left() / squareSize;
int endColumn = redrawRect.right() / squareSize;
画小方格:
painter.setPen(QPen(Qt::gray));
for (int row = beginRow; row <= endRow; ++row)
{
for (int column = beginColumn; column <= endColumn; ++column)
{
painter.drawRect(column * squareSize, row * squareSize, squareSize, squareSize);
}
}
画小方格里的字符:
QFontMetrics fontMetrics(displayFont);
painter.setPen(QPen(Qt::black));
for (int row = beginRow; row <= endRow; ++row)
{
for (int column = beginColumn; column <= endColumn; ++column)
{
int key = row * columns + column;
painter.setClipRect(column * squareSize, row * squareSize, squareSize, squareSize);
if (key == lastKey)
painter.fillRect(column * squareSize + 1, row * squareSize + 1,
squareSize, squareSize, QBrush(Qt::red));
painter.drawText(column * squareSize + (squareSize / 2) -
fontMetrics.horizontalAdvance(QChar(key)) / 2,
row * squareSize + 4 + fontMetrics.ascent(),
QString(QChar(key)));
}
}
我们不需要考虑viewport中显示的区域和我们正在绘制的区域之间的差异,因为可见区域以外的所有东西都将被裁剪。
QPainter::setClipRect():
设置只能在QWidget里的裁剪区域绘图,此区域外绘图都是无效的。
参考链接:https://blog.csdn.net/sinat_33263516/article/details/103238133
QPainter::fillRect():
调用该函数的代码段的含义是:如果是当前鼠标点击的格点,则设置当前格点的背景颜色为红色;
void QPainter::drawText(int x, int y, const QString &text):
这是一个重载函数。
使用画家当前定义的文本方向,在位置(x, y)绘制给定的文本。
默认情况下,QPainter绘制的文本是反锯齿的。
注意:y轴位置作为字体的基线。
其中调用了int QFontMetrics::ascent() const,可参考:https://blog.csdn.net/xjcwzp/article/details/100192114
也调用了QChar::QChar(int code),使用Unicode代码点代码为字符构造QChar。
至此,实现了所见页面下的字符绘画,
接着实现所有的字符显示:
这paintEvent绘画的只是当前我们能看到的字符页面,当滚动QScrollArea的右侧滑轮时,就会重新将当前字符页面绘制一遍,这是paintEvent的特性之一,就是当界面局部更新时,会重新调用paintEvent绘制一遍
可参考:
QT关键问题解决之paintevent理解_u012151242的博客-CSDN博客_paintevent
https://blog.csdn.net/u012151242/article/details/78947024
6.如何点击鼠标,令字符填充入lineEdit中:
点击鼠标左键,调用mousePressEvent():
void CharacterWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
lastKey = (event->y() / squareSize) * columns + event->x() / squareSize;
if (QChar(lastKey).category() != QChar::Other_NotAssigned)
emit characterSelected(QString(QChar(lastKey)));
update();
}
else
QWidget::mousePressEvent(event);
}
QMouseEvent::y(): 调用的是点击点相对坐标,即相对于父控件scrollArea的桌标,所以当scrollarea的滚轮时,每个格点的纵坐标其实都发生了改变;
只有当用户在小部件上单击鼠标左键时,我们才会感兴趣。当发生这种情况时,我们计算选择了哪个字符并发出characterSelected()信号。通过点击的x和y坐标除以每个角色网格的大小,就可以找到角色的数字。由于小部件中的列数是由columns变量定义的,所以我们只需将行索引乘以该值并添加列号即可获得字符号。(有待考究!!!)
触发了信号characterSelected(QString(QChar(lastKey))),调用MainWindow下的槽:
connect(characterWidget, &CharacterWidget::characterSelected,
this, &MainWindow::insertCharacter);
void MainWindow::insertCharacter(const QString &character)
{
lineEdit->insert(character);
}
7.如何令字符存入windows下的剪贴板:
#ifndef QT_NO_CLIPBOARD
connect(clipboardButton, &QAbstractButton::clicked, this, &MainWindow::updateClipboard);
#endif
#ifndef QT_NO_CLIPBOARD
void MainWindow::updateClipboard()
{
//! [11]
QGuiApplication::clipboard()->setText(lineEdit->text(), QClipboard::Clipboard);
//! [11]
QGuiApplication::clipboard()->setText(lineEdit->text(), QClipboard::Selection);
}
#endif
8.将鼠标放置在字符表上,在字符方格正下方显示该字符详细信息;
void CharacterWidget::mouseMoveEvent(QMouseEvent *event)
{
QPoint widgetPosition = mapFromGlobal(event->globalPos());
uint key = (widgetPosition.y() / squareSize) * columns + widgetPosition.x() / squareSize;
QString text = QString::fromLatin1("<p>Character: <span style=\"font-size: 24pt; font-family: %1\">").arg(displayFont.family())
+ QChar(key)
+ QString::fromLatin1("</span><p>Value: 0x")
+ QString::number(key, 16);
QToolTip::showText(event->globalPos(), text, this);
}
给工具提示一个全局坐标定义的位置。