参考
Qt中自适应的label+pixmap充满窗口后,无法缩小只能放大
[解决:[QWidget::paintEngine: Should no longer be called QPainter::begin: Paint device returned engine == 0, type: 1] ]
可以显示jpg、jpeg、png、bmp。可以从电脑上拖动图到窗口并显示出来或者打开文件选择,定时滚动图片
重载实现dragEnterEvent(拖拽)、dropEvent(拖拽放下)、resizeEvent(窗口大小改变)
解决:
[QWidget::paintEngine: Should no longer be called QPainter::begin: Paint device returned engine == 0, type: 1]
需要在哪个控件上绘制,就要在哪个控件类中重写 paintEvent() ,所以本项目 需要使用自定义的MyQLabel继承QLabel没有透明过渡动画版本Qt图片定时滚动播放器,没有透明过渡动画
结构
PicturePlay.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
myqlabel.cpp \
pictureplay.cpp
HEADERS += \
myqlabel.h \
pictureplay.h
FORMS += \
pictureplay.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${
TARGET}/bin
else: unix:!android: target.path = /opt/$${
TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
res.qrc
main.cpp
#include "pictureplay.h"
#include <QApplication>
#include<QFile>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
// 加载样式文件
QFile file(":/style.qss");
/*判断文件是否存在*/
if(file.exists())
{
file.open(QFile::ReadOnly);// 以只读的方式打开
//QString styleSheet=QLatin1String(file.readAll());// 以Latin-1编码的字符串的方式保存读出的结果
QString styleSheet=QString(file.readAll());// 以默认编码的字符串的方式保存读出的结果
qApp->setStyleSheet(styleSheet);// 设置全局样式
file.close();// 关闭文件
}
//在窗口显示前判断使用
PicturePlay w;
w.show();
return a.exec();
}
myqlabel.h 自定义QLabel
#ifndef MYQLABEL_H
#define MYQLABEL_H
#include <QWidget>
#include <QLabel>
#include <QPainter>
#include <QPropertyAnimation>
class MyQLabel : public QLabel
{
Q_OBJECT
public:
explicit MyQLabel(QWidget *parent = nullptr);
void paintEvent(QPaintEvent *event)override;// 绘图事件;
// 图片切换动画类;
QPropertyAnimation* m_opacityAnimation;
QPixmap currentPixmap;
QPixmap nextPixmap;
bool paintEvent_Flag;
signals:
};
#endif // MYQLABEL_H
myqlabel.cpp自定义QLabel
#include "myqlabel.h"
#include<QVariant>
#include<QDebug>
MyQLabel::MyQLabel(QWidget *parent) : QLabel(parent)
{
this->setProperty("ImageOpacity",1.0);
this->paintEvent_Flag=false;
// 动画切换类;
m_opacityAnimation = new QPropertyAnimation(this, "ImageOpacity");
// 这里要设置的动画时间小于图片切换时间;
m_opacityAnimation->setDuration(1500);
// 设置ImageOpacity属性值的变化范围;
m_opacityAnimation->setStartValue(1.0);
m_opacityAnimation->setEndValue(0.0);
// 透明度变化及时更新绘图;
connect(m_opacityAnimation, SIGNAL(valueChanged(const QVariant&)),this, SLOT(update()));
m_opacityAnimation->stop();
}
int i=0;
void MyQLabel::paintEvent(QPaintEvent *event)
{
if(!currentPixmap.isNull()&&!nextPixmap.isNull()&&paintEvent_Flag)
{
this->clear();
QPainter painter(this);
QRect imageRect = this->rect();
float imageOpacity = this->property("ImageOpacity").toFloat();
//qDebug()<<"i=["<<i++<<"]imageOpacity="<<imageOpacity;
painter.setOpacity(1);
painter.drawPixmap(imageRect, nextPixmap.scaled(imageRect.size()));
painter.setOpacity(imageOpacity);
painter.drawPixmap(imageRect, currentPixmap.scaled(imageRect.size()));// 透明度减到0
}
QLabel::paintEvent(event);
}
pictureplay.h
#ifndef PICTUREPLAY_H
#define PICTUREPLAY_H
#include <QWidget>
#include <QFile>
#include <QDir>
#include <QTimer>
#include <QThread>
#include <QFileDialog>
#include <QScrollArea>
#include <QPropertyAnimation>
QT_BEGIN_NAMESPACE
namespace Ui {
class PicturePlay; }
QT_END_NAMESPACE
class PicturePlay : public QWidget
{
Q_OBJECT
public:
PicturePlay(QWidget *parent = nullptr);
~PicturePlay();
private slots:
void myautoPhoto(); //自动播放函数
void showDinamicPhoto(QString path);//动态图播放(格式为gif)
void on_pathBtn_clicked();//浏览:打开目录
void on_autoPhoto_clicked(bool checked);//自动播放选择
void on_photoList_clicked(const QModelIndex &index);//单击播放图片
void my_currentRowChanged(int Row);// // 当前项变化
void on_cleanBtn_clicked();//清除
private:
QString Dir;//打开文件的路径
QString tempDir; //照片的绝地路径
QVector<QString> photoPath;//存放照片相对路径的容器
QTimer *mtime; //定时器
QString delayTime; //延时间隔
bool autoFlag; //判断是否进入的自动播放格式
int num; // 当前显示图片index;
// 如果文件的后缀名是jpg、jpeg、bmp或png,则接受拖放事件,否则忽略拖放事件
QStringList acceptedFileTypes;
void dragEnterEvent(QDragEnterEvent *event)override;//拖进事件
void dropEvent(QDropEvent *event)override;// 拖进放下事件
void resizeEvent(QResizeEvent *event)override;//用于在窗口大小改变时处理事件
void paintEvent(QPaintEvent *event)override;// 绘图事件;
// 切换图片;
QPixmap pixmap;
// QPixmap currentPixmap;
// QPixmap nextPixmap;
// 用来做图片切换滑动效果,目前以透明度作为切换效果;
QScrollArea* m_imagePlayWidget;
// // 图片切换动画类;
// QPropertyAnimation* m_opacityAnimation;
private:
Ui::PicturePlay *ui;
};
#endif // PICTUREPLAY_H
pictureplay.cpp
#include "pictureplay.h"
#include "ui_pictureplay.h"
#include<QDebug>
#include<QMessageBox>
#include<QMovie>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMimeData>
#include <QPainter>
PicturePlay::PicturePlay(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PicturePlay)
{
ui->setupUi(this);
this->setWindowTitle("图片播放器");
this->setWindowIcon(QIcon(":/ImageEye.jpg"));
this->setAcceptDrops(true);//设置允许向窗口拖入图片
this->setMinimumSize(QSize(400,300));// 设置最小值
ui->photoShow->setAlignment(Qt::AlignCenter); //居中显示
//自适应的label+pixmap充满窗口后,无法缩小只能放大
ui->photoShow->setSizePolicy(QSizePolicy::Ignored,QSizePolicy::Ignored);// Ignored忽略
// 内容是否自动缩放,参数true自动缩放
ui->photoShow->setScaledContents(true);//显示图片的全部
// 设置拉伸因子(默认缩到最小时失效:一样大小)
ui->horizontalLayout_2->setStretch(1,3);
// 设置水平布局控件到边框距离(左,上,右,下)
ui->horizontalLayout_2->setContentsMargins(5,0,5,0);
//设置窗口布局,(此时控件会随窗口缩放)
this->setLayout(ui->horizontalLayout_2);
ui->photoPath->setFocusPolicy(Qt::NoFocus);//无法获得焦点,自然无法输入,其他文本控件类似
ui->photoPath->setPlaceholderText("绝对路径");
ui->delayEdit->setPlaceholderText("秒");
//不显示行向滚动条,子项文本过长自动显示...
ui->photoList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
num=0; //照片张数
delayTime.clear(); //延时间隔
mtime = new QTimer(this);
//连接自动播放槽函数
connect(mtime,SIGNAL(timeout()),this,SLOT(myautoPhoto()));
connect(ui->photoList,SIGNAL(currentRowChanged(int)),this,SLOT(my_currentRowChanged(int)));
// 如果文件的后缀名是jpg、jpeg、bmp或png,则接受拖放事件,否则忽略拖放事件
acceptedFileTypes.append("jpg");
acceptedFileTypes.append("jpeg");
acceptedFileTypes.append("bmp");
acceptedFileTypes.append("png");
acceptedFileTypes.append("gif");
}
PicturePlay::~PicturePlay()
{
delete ui;
}
//自动播放函数
void PicturePlay::myautoPhoto()
{
tempDir.clear();
tempDir=photoPath.at(num); //从容器中找到要播放的照片的相对路径
ui->photoList->setCurrentRow(num);// QListWidget项跳动
if(tempDir.endsWith(".gif") || tempDir.endsWith(".Gif"))
{
showDinamicPhoto(tempDir);
num++;
}
else if(!(tempDir.endsWith(".gif") || tempDir.endsWith(".Gif")))
{
ui->photoShow->paintEvent_Flag=true;
ui->photoShow->currentPixmap=pixmap;// 1.先赋值
pixmap.load(tempDir);// 2.再更新全局图片
// 先设置下一个的图片;
ui->photoShow->nextPixmap=pixmap;//3.最后需要立即显示的图片
// 动画类重新开始;
ui->photoShow->m_opacityAnimation->start();
//判断自动播放的时候是否播放到了最后一张图片,如果是则停止自动播放
if(num == (photoPath.count()-1))
{
//qDebug()<<num;
if(ui->loopPlayback->checkState()==Qt::Checked)//循环重放
{
num = -1;// 下面num++,为了循环重放回到第一张图片
qDebug()<<num;
}
else
{
mtime->stop();
num = 0;
if(autoFlag)//选中自动播放的情况
{
autoFlag = false;
}
//qDebug()<<num;
ui->autoPhoto->setCheckState(Qt::Unchecked);//把自动播放按钮置于没有选择的状态
}
}
if(autoFlag)
{
num++;
}
}
}
//动态图播放(格式为gif)
void PicturePlay::showDinamicPhoto(QString path)
{
ui->photoShow->clear();
ui->photoShow->paintEvent_Flag=false;
QMovie *movie = new QMovie(path); // path图片路径
movie->start(); //开始播放动态图
ui->photoShow->setMovie(movie); //将图片设置为为动态
ui->photoShow->setScaledContents(true); //尽可能完整的播放整张动图 ,此处要设置为true
}
//浏览:打开目录
void PicturePlay::on_pathBtn_clicked()
{
Dir = QFileDialog::getExistingDirectory(this);//获取本文件所在的具体路径
//qDebug()<<"路径:"<<Dir;
ui->photoPath->setText(Dir);//显示打开的文件的具体路径
QDir dir(Dir);
QStringList file;
QFileInfoList fileList = dir.entryInfoList(file,QDir::Files); //获取目录下的文件
QString fileDir; //保存图片所在的路径
//列出目录下的文件
for(int i=0;i<fileList.count();i++)
{
if(i==50)
{
break;
}
QFileInfo info = fileList.at(i);
fileDir.clear();
if(info.fileName() == "." || info.fileName() == "..") //跳过这两个目录
{
continue;
}
// 检查文件的后缀名是否在接受的文件类型列表中;(获取文件的后缀名,并将其转换为小写字母)
if(!acceptedFileTypes.contains(info.suffix().toLower()))
{
continue;
}
fileDir+=Dir + "/";
QString filename = info.fileName();
fileDir+=filename;
photoPath.append(fileDir);// 把图片的路径装到容器中
QListWidgetItem *item = new QListWidgetItem(QIcon(fileDir),info.fileName());//建立文件缩小图标
item->setToolTip(info.fileName());// tip提示
item->setTextAlignment(Qt::AlignCenter);//设置item项中的文字位置
ui->photoList->addItem(item);//把图片相对路径显示到窗口中
}
if(ui->photoList->count()==0)
{
QListWidgetItem *item = new QListWidgetItem("无合适图片");
item->setTextAlignment(Qt::AlignCenter);//设置item项中的文字位置
ui->photoList->addItem(item);//把图片相对路径显示到窗口中
}
}
//自动播放选择
void PicturePlay::on_autoPhoto_clicked(bool checked)
{
if(ui->delayEdit->text().isEmpty())
{
QMessageBox::warning(this,"提示","请输入需要间隔的播放时间(秒)");
//把按钮重新置于没有被选中的状态;不要用setCheckable()函数[不可用],很容易出错
ui->autoPhoto->setCheckState(Qt::Unchecked);
return;
}
else if(ui->photoList->count() == 0)
{
QMessageBox::warning(this,"警告","还没有可以播放的图片");
ui->autoPhoto->setCheckState(Qt::Unchecked); //把按钮重新置于没有被选中的状态
return;
}
else if(ui->delayEdit->text().toInt()<2)// 图片切换时间大于动画时间
{
QMessageBox::warning(this,"警告","请输入间隔的播放时间大于等于 2 (秒)");
ui->autoPhoto->setCheckState(Qt::Unchecked); //把按钮重新置于没有被选中的状态
return;
}
else if(checked) //启动定时器
{
delayTime = ui->delayEdit->text();
mtime->start(delayTime.toInt()*1000);//启动定时器并设置播放时间间隔
autoFlag = true;// 选中自动播放
// 第一次:立即显示,无需等待
ui->photoList->setCurrentRow(num);// QListWidget项跳动
pixmap.load(photoPath.at(num));// 先更新全局图片
ui->photoShow->paintEvent_Flag=true;
ui->photoShow->currentPixmap=ui->photoShow->nextPixmap=pixmap;//再赋值
update();// 手动刷新
}
else if(!checked)//停止定时器
{
mtime->stop();//停止定时器
delayTime.clear();
autoFlag = false;
ui->photoShow->paintEvent_Flag=false;
}
}
//单击播放图片
void PicturePlay::on_photoList_clicked(const QModelIndex &index)
{
qDebug()<<"单击播放图片";
Q_UNUSED(index);//忽略编译器发出的警告,表明变量event未使用
//如果选中了自动播放的情况下,点击列表中的内容,则停止自动播放
if(autoFlag) //选中自动播放的情况
{
mtime->stop();
//把按钮重新置于没有被选中的状态;不要用setCheckable()函数[不可用],很容易出错
ui->autoPhoto->setCheckState(Qt::Unchecked);
autoFlag = false;
}
num = ui->photoList->row(ui->photoList->currentItem()); //获取当前点击的内容的行号
//在没有选中自动播放的情况下,判断当前是否点击了最后一张照片,如果是最后一张照片,在选中自动播放的情况下让它返回到第一张照片
if(!autoFlag&&num == ui->photoList->count())
{
num = 0;
}
tempDir.clear();
/*tempDir+=Dir+"/";
QString path = ui->photoList->currentItem()->text();
tempDir+=path*/;
tempDir=photoPath.at(num); //从容器中找到要播放的照片的相对路径
//判断是否是动态图;endsWith()字符串结尾
if(tempDir.endsWith(".gif") || tempDir.endsWith(".Gif"))
{
showDinamicPhoto(tempDir);
}
else
{
if(pixmap.isNull())
{
pixmap.load(tempDir);// 更新全局图片
ui->photoShow->currentPixmap=ui->photoShow->nextPixmap=pixmap;
}else
{
ui->photoShow->currentPixmap=pixmap;
pixmap.load(tempDir);// 更新全局图片
ui->photoShow->nextPixmap=pixmap;
}
ui->photoShow->paintEvent_Flag=true;
// 动画类重新开始;
ui->photoShow->m_opacityAnimation->start();
}
}
// 当前项变化
void PicturePlay::my_currentRowChanged(int Row)
{
qDebug()<<"当前项变化:"<<Row;
}
//拖进事件
void PicturePlay::dragEnterEvent(QDragEnterEvent *event)
{
// 用于检查拖放的数据是否包含URL,并且获取拖放事件中的URL数量==1
if(event->mimeData()->hasUrls()&&event->mimeData()->urls().count()==1)
{
// 获取拖放事件中的第一个URL的本地文件路径
QFileInfo file(event->mimeData()->urls().at(0).toLocalFile());
// 检查文件的后缀名是否在接受的文件类型列表中;(获取文件的后缀名,并将其转换为小写字母)
if(acceptedFileTypes.contains(file.suffix().toLower()))
{
event->acceptProposedAction();//表明用户可以在窗口部件上拖放对象[接受拖放事件的操作]
}
}
}
// 拖进放下事件
void PicturePlay::dropEvent(QDropEvent *event)
{
// 获取拖放事件中的第一个URL的本地文件路径
QFileInfo file(event->mimeData()->urls().at(0).toLocalFile());
//qDebug()<<"绝对路径:"<<file.absoluteFilePath();
//从文件中加载图片,参数file.absoluteFilePath()表示包含文件名的绝对路径,load()返回一个bool值,表示是否加载成功
if(pixmap.load(file.absoluteFilePath()))
{
ui->photoShow->paintEvent_Flag=true;
ui->photoShow->currentPixmap=ui->photoShow->nextPixmap=pixmap;
update();// 手动刷新,否则鼠标拖进放下,如果鼠标不在窗口不会刷新,图片不会显示
ui->photoPath->setText(file.absolutePath());//显示打开的文件的绝对路径,这不包括文件名。
photoPath.append(file.absoluteFilePath());// 把图片的路径装到容器中
QListWidgetItem *item = new QListWidgetItem(QIcon(file.absoluteFilePath()),file.fileName());//建立文件缩小图标
item->setToolTip(file.fileName());// tip提示
item->setTextAlignment(Qt::AlignCenter);//设置item项中的文字位置
ui->photoList->addItem(item);//把图片相对路径显示到窗口中
}else
{
// 错误消息框
QMessageBox::critical(this,tr("Error"),tr("The image file count not be read"));
}
}
//用于在窗口大小改变时处理事件
int j=0;
void PicturePlay::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event);//忽略编译器发出的警告,表明变量event未使用
qDebug()<<"窗口大小改变:"<<j++;
if(!pixmap.isNull())
{
// 在MyQLaBel的paintEvent绘图事件,会重绘
//ui->photoShow->setPixmap(pixmap.scaled(ui->photoShow->size(),Qt::KeepAspectRatio,Qt::SmoothTransformation));
}
}
//绘图事件
void PicturePlay::paintEvent(QPaintEvent *event)
{
//QPainter painter(ui->photoShow);
/*报错
QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
*/
//想要在哪个控件上绘制,就要在哪个控件类中重写 paintEvent() 方法才行。
//比如想要在 QLabel 上绘图,就需要新建一个类继承于 QLabel,然后重写 paintEvent() 方法
//也就是在此文件只能用QPainter painter(this);
Q_UNUSED(event);//忽略编译器发出的警告,表明变量event未使用
}
// 清除
void PicturePlay::on_cleanBtn_clicked()
{
mtime->stop();
num = 0;
if(autoFlag)//选中自动播放的情况
{
autoFlag = false;
}
pixmap.fill(Qt::transparent);//函数用于将QPixmap对象pixmap清空,参数Qt::transparent表示填充透明色,无返回值
ui->autoPhoto->setCheckState(Qt::Unchecked);//把自动播放按钮置于没有选择的状态
ui->loopPlayback->setCheckState(Qt::Unchecked);//把循环重放钮置于没有选择的状态
photoPath.clear();
ui->photoPath->clear();
ui->photoList->clear();
ui->photoShow->clear();
ui->photoShow->nextPixmap=ui->photoShow->currentPixmap=pixmap;
ui->photoShow->paintEvent_Flag=false;
}
pictureplay.ui
style.qss
QListWidget
{
border:1px solid gray; /*边界线:宽度、颜色*/
/*background:gray;*/ /*表格背景色*/
color:black; /*前景色:文字颜色*/
/*margin:5px,5px,0px,50px;*/ /*上、下、左、右,间距*/
}
QListWidget{
/*去掉选中时的虚线框*/
outline:0px;
}
QListWidget::item
{
padding-top:12px;
padding-bottom:4px;
}
QListWidget::item:hover/*悬停*/
{
show-decoration-selected:5;
background:skyblue;/*天蓝色*/
}
QListWidget::item:selected
{
/*border:0px;*/
background:lightgray;/*浅灰色*/
padding:0px;
margin:0px;
color:red;
}
/*上次选择后保留的状态,鼠标离开后显示*/
QListWidget::item:selected:!active
{
border:none;/*无边框*/
background:lightgreen;/*浅绿色*/
}
效果
启动
运行
源码
有道云:Qt图片定时滚动播放器