最近在开发中需要添加提词器的功能,在编写这个小功能的过程中遇见很多问题,在这里进行总结一下。
提词器的功能包含:文本滚动、滚动速度、内容翻页、文本切换、文本透明 、窗口透明、窗口隐藏等
问题1. 滚动效果 QTextBrower 实现滚动数据过程中 append数据长度的计算 (跟随窗口的拉伸对append数据的长度实时改变)
在这里我是重新写了一个文本浏览类 继承于QTextBrower,因为有一些东西可能需要用到重载;
要想知道QTextBrower 每一行窗口的字体的长度,就得计算一个字体的长度和宽度:
公式:字体的宽度 (像素大小)= DPI * PointSize / 72
DPI: Dot per inch,在显示器上也就是每英寸包含的像素 , windows为了方便统一点距,为96
PointSize: 字体大小
字体的高度则由下面组成:
这点我是没怎么搞懂这个高度应该如何计算 baseline Asent, 因此跳过。
到这里就知道如何计算append 每一行的长度: 窗口宽度 / 字体宽度 因此每次添加的数据就从文本中读取llen长度的数据append到QTextBrower 中,
void QShowBrower::updateIndex(){
//m_charWidth 字体宽度
uint len =width()/m_charWidth-1 ;
QString data = m_pShowTextParam->getTotalText();//文本数据
if(m_curIndex >= data.size())
{
if(m_pShowTextParam->getLoopStatus()) //进行循环滚动
m_curIndex = 0;
else//关闭定时器不再滚动
m_pTimer->stop();
}
this->append(data.mid(m_curIndex,len));
m_curIndex += len;
}
但这里存在一些不好的缺点:
①每次拉伸的过程中,数据会出现长短不一样的append(实时的)数据,就如一句话通过几段显示(而且长短不一),视感不好;
②数据里面是包含字母、数字、汉字,因此计算的长度也会有偏差,append的数据不能填充整行,总会预留出一两个字符位置,不整齐;
因此这种方法后期我直接丢弃了,直接添加整个文本到QTextBrower中,而滚动的话使用 垂直滚动条 来进行位置的移动(滚动速度通过位置移动的数量来控制),避免上面的缺点。
void QShowBrower::updateIndex()
{
//结束和开头判断
if(verticalScrollBar()->value() == m_curIndex && verticalScrollBar()->value() !=0)
{
if(m_iCount >= TIME_SCALE)//已经结束
{
if(m_pShowTextParam->getLoopStatus()) //进行循环滚动
{
verticalScrollBar()->setValue(0);
}
else//关闭定时器不再滚动
m_pTimer->stop();
}
m_iCount++;
}
m_curIndex = verticalScrollBar()->value();
QScrollBar *scrollbar = verticalScrollBar();
verticalScrollBar()->setValue(scrollbar->value() + 1);
}
在这里,每次根据定时器的时间滚动一行;
问题2:工具栏的隐藏,进入区域显示,移除区域则隐藏(隐藏的快捷键问题)
对于工具栏的隐藏实现很简单,主要就是对QToolBar的隐藏属性设置就行,其中就需要重载enterEvent 和 leaveEvent事件;
void QTeleprompter::enterEvent(QEvent *event)
{
m_pToolBar->setHidden(false);//when mouse enter widget area,show widget and toolbar
}
void QTeleprompter::leaveEvent(QEvent *event)
{
m_pToolBar->setHidden(true);//when mouse leave widget area,hide widget and toolbar
}
在这里就会出现一个问题:当工具栏隐藏后,快捷键是无法响应的,只有显示后才能使用; 原因是没有焦点(目前这个问题未能解决)
问题3:窗口hide()后 是无法通过槽函数进行show()的
原因是上一个问题的快捷键无法响应,因此是无法进入槽函数,执行show()代码的,也是未解决问题。
这里讲解一下hide() show()函数,源码如下:
virtual void setVisible(bool visible);
inline void setHidden(bool hidden) { setVisible(!hidden); }
inline void hide() { setVisible(false); }
#ifndef Q_WS_WINCE
inline void show() { setVisible(true); }
#else
void show(); // 此函数在Qwidget_wince.cpp中实现,最终还是调用了setVisible(true);
#endif
代码一目了然:这四个函数中只有 setVisible 是独立的,它,其他三个都是它的外使得一个Widget可见或不可见套。setVisible(false)代表Widget不在界面上显示,但是并不代表对象被析构!
问题4:字体的透明度控制
这里主要是对QTextBrower的文本字体的透明度控制,主要是对QT的样式表熟悉,样式表的内容还是挺多的,多尝试和看看资料。
setStyleSheet("color:" + QString("rgba(%1,%2,%3,%4);")
.arg(params->getColor().red())
.arg(params->getColor().green())
.arg(params->getColor().blue())
.arg(params->getAlpha()) );
这里就是直接使用rgba的方式直接设置样式,之前也尝试过其他设置透明度的方法,反正结果就不理想,最终还是使用这种方式设置。
问题5:窗口透明度无边框则引发的问题
常用方式有两种:
①setWindowOpacity() 窗口及其上面的控件都半透明
缺点就是会导致子控件也会出现半透明,因此显示文字的效果就不理想,这种方式用法简单就不多解释。
优点就是窗口可以移动和拉伸功能。
②窗口整体透明,但窗口控件不透明
缺点就是会导致窗口无法移动和拉伸,但是这个后面可以通过重载事件进行优化;
// //去掉标题栏 无边框
setWindowFlags(windowFlags() | Qt::FramelessWindowHint);
setAttribute(Qt::WA_TranslucentBackground, true);
//设置窗体样式
this->setStyleSheet(
"QWidget{background-color:rgba(255,255,255,0.1);border-bottom-left-radius:8px;border-bottom-right-radius:8px}");
//重写paint事件
void QTeleprompter::paintEvent(QPaintEvent *event)
{
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
QWidget::paintEvent(event);
}
就可以实现窗口透明,但控件不透明的效果,但有无法移动和拉伸的缺点;
因此进行优化来改善这个缺点,窗口移动功能通过重写mouse鼠标事件来实现
void QTeleprompter::mousePressEvent(QMouseEvent *event)
{
m_lastPoint = event->globalPos();
}
void QTeleprompter::mouseReleaseEvent(QMouseEvent *event) {
int dx = event->globalX() - m_lastPoint.x();
int dy = event->globalY() - m_lastPoint.y();
move(x() + dx, y() + dy);
}
void QTeleprompter::mouseMoveEvent(QMouseEvent *event) {
int dx = event->globalX() - m_lastPoint.x();
int dy = event->globalY() - m_lastPoint.y();
m_lastPoint = event->globalPos();
move(x() + dx, y() + dy);
}
拉伸事件:
bool QTeleprompter::nativeEvent(const QByteArray &eventType, void *message,
long *result)
{
//m_nBorder表示鼠标位于边框缩放范围的宽度,可以设置为5
int m_nBorder = 10;
Q_UNUSED(eventType)
MSG *param = static_cast<MSG *>(message);
switch (param->message) {
case WM_NCHITTEST: {
int nX = GET_X_LPARAM(param->lParam) -
this->geometry().x();
int nY = GET_Y_LPARAM(param->lParam) -
this->geometry().y();
// 如果鼠标位于子控件上,则不进行处理 排除showbrower 窗口
if (childAt(nX, nY) != NULL && childAt(nX, nY) != m_pShowBrower)
return QWidget::nativeEvent(eventType, message,
result);
*result = HTCAPTION;
// 鼠标区域位于窗体边框,进行缩放
if ((nX > 0) && (nX < m_nBorder))
*result = HTLEFT;
if ((nX > this->width() - m_nBorder) &&
(nX < this->width()))
*result = HTRIGHT;
if ((nY > 0) && (nY < m_nBorder))
*result = HTTOP;
if ((nY > this->height() - m_nBorder) &&
(nY < this->height()))
*result = HTBOTTOM;
if ((nX > 0) && (nX < m_nBorder) && (nY > 0) &&
(nY < m_nBorder))
*result = HTTOPLEFT;
if ((nX > this->width() - m_nBorder) &&
(nX < this->width()) && (nY > 0) &&
(nY < m_nBorder))
*result = HTTOPRIGHT;
if ((nX > 0) && (nX < m_nBorder) &&
(nY > this->height() - m_nBorder) &&
(nY < this->height()))
*result = HTBOTTOMLEFT;
if ((nX > this->width() - m_nBorder) &&
(nX < this->width()) &&
(nY > this->height() - m_nBorder) &&
(nY < this->height()))
*result = HTBOTTOMRIGHT;
return true;
}
}
return QWidget::nativeEvent(eventType, message, result);
}
需要引用头文件:
#define Q_OS_WIN
#ifdef Q_OS_WIN
#include <qt_windows.h>
#include <Windowsx.h>
#endif
注意事项:
1)当没有设置Qt::WA_TranslucentBackground属性为true时,会发现没有半透明透明效果。
2)当窗体属性没有设置无边框属性,也就是Qt::FramelessWindowHint时,会发现没有半透明透明效果。
3)当窗体属性没有设置无边框属性,且没有重写paintEvnet事件,会发现窗体变成黑色,哪怕是设置css样式也是一样。
引用链接:https://blog.csdn.net/qq_36939187/article/details/109284758
正常情况下窗口重写native事件是可以实现的,但是此处会受重写的paintEvent事件机制影响,无法进行case里面执行。
因此此事件是无法进行拉伸功能得,因此我在这里进选择另外一种方式,通过配置参数来进行设置窗口的宽高;(提词器本身就一个配置参数弹出框,只需要加入就行),但效果肯定没有直接窗口拉伸的效果好。
问题6:无法取消QTextBrowser的这个显示外框区域
通过调用一下设置来进行隐藏外框发现还是不能彻底的解决外框问题,这里我觉得这个问题是我的背景问题,其实相对于外框应该是隐藏了 的。
//窗口才能实现透明化 隐藏
setWindowFlags(Qt::FramelessWindowHint | this->windowFlags());
setAttribute(Qt::WA_TranslucentBackground);
viewport()->setWindowFlags(Qt::FramelessWindowHint |
viewport()->windowFlags());
viewport()->setAttribute(Qt::WA_TranslucentBackground);
代码还需不断的优化,因自我能力的不足,以上一些问题还没有能力解决,希望大佬能指导一下。
整体效果展示