上篇测评,用Qt实现了一个相册浏览器,可以查看特定文件目录下的图片文件,不足之处是,查看图片的目录是固定的,不能灵活指定。
本篇,就来实现一个可以查看任意目录下图片的图片查看器,可以实现OK3568-C板子中任意目录下图片的查看,并且可以通过鼠标滚轮以及鼠标移动来实现图片的灵活放大、缩小,此外,在打开一个图片后,若该目录下还有其它图片,通过左右切换,可以方便的查看同目录下的其它图片,先来看下最终的效果:
- 通过点击下方的图片文件夹图标,可以弹出系统文件选择窗口,可以选定任意目录下的图片
- 选择图片后,图片显示主窗口即可居中显示图片
- 通过滚轮上下滑动,可以放大和缩小图片
- 鼠标左键按下再移动,可以移动图片
- 下方两侧的切换按钮,可以切换上一张、下一张图片
1 图片查看器开发总体结构
整个Qt图片查看器项目的代码结构如下:
- 主代码中是图片查看器相关的代码,包括:
- src:图片查看器主代码
- picview.pro:Qt工程文件
- images:存放各个按钮图标的资源文件
- build中是编译的中间文件和编译结果存储的目录
下面分类介绍了程序的主要代码实现。
2 软件开发
2.1 整体布局
主界面的通过垂直布局,上方是图片显示,下方是按钮操作。下方的3个按钮再通过水平布局实现。
采用自动布局管理,可随窗口大小自动调整显示。
PicViewWidget::PicViewWidget(QMainWindow *parent)
: QMainWindow(parent)
{
m_box = new ImageBox();
QScrollArea *m_scrollArea = new QScrollArea();
m_scrollArea->setMinimumSize(800, 600);
m_scrollArea->setWidgetResizable(true);
m_scrollArea->setWidget(m_box);
QHBoxLayout *layout_view = new QHBoxLayout();
layout_view->addWidget(m_scrollArea);
m_prevBtn = new QPushButton();
m_prevBtn->setIcon(QIcon(":/images/prev.png"));
m_prevBtn->setFlat(true);
m_prevBtn->setIconSize(QSize(50,50));
m_openBtn = new QPushButton();
m_openBtn->setIcon(QIcon(":/images/openimg.png"));
m_openBtn->setFlat(true);
m_openBtn->setIconSize(QSize(70,70));
m_nextBtn = new QPushButton();
m_nextBtn->setIcon(QIcon(":/images/next.png"));
m_nextBtn->setFlat(true);
m_nextBtn->setIconSize(QSize(50,50));
m_nextBtn->setEnabled(false);
m_prevBtn->setEnabled(false);
QHBoxLayout *layout_button = new QHBoxLayout();
layout_button->addWidget(m_prevBtn);
layout_button->addWidget(m_openBtn);
layout_button->addWidget(m_nextBtn);
QVBoxLayout *layout_main = new QVBoxLayout();
layout_main->setSpacing(5);
layout_main->addLayout(layout_view);
layout_main->addLayout(layout_button);
layout_main->setStretch(0,10);
layout_main->setStretch(1,1);
layout_main->setContentsMargins(5,5,5,5);
QWidget *widget = new QWidget();
widget->setLayout(layout_main);
this->setCentralWidget(widget);
connect(m_nextBtn, SIGNAL(clicked(bool)), this, SLOT(show_next()));
connect(m_prevBtn, SIGNAL(clicked(bool)), this, SLOT(show_prev()));
connect(m_openBtn, SIGNAL(clicked(bool)), this, SLOT(open_files()));
}
2.2 操作按钮
下面是3个按钮的槽函数,打开文件按钮、下一张按钮、上一张按钮。
打开指定图片,通过QFileDialog::getOpenFileName来获取图片文件的绝对路径,然后再提取出此图片所处的目录位置,通过QDir的entryInfoList方法再找出此目录下的其它所有图片。
上一张、下一张的操作,就是将对应的图片资源路径更换一下,然后进行对应图片的展示。
void PicViewWidget::open_files()
{
m_file = QFileDialog::getOpenFileName(this, "所有图片", "/home", "Image Files (*.png *.jpg *.bmp)");
qDebug() << m_file;
if (m_file.isEmpty())
{
return;
}
QString file_path = QFileInfo(m_file).absolutePath();
qDebug() << file_path;
QDir dir(file_path);
dir.setFilter(QDir::Files | QDir::NoSymLinks);
QFileInfoList list = dir.entryInfoList(QStringList() << "*.jpg"
<< "*.png"
<< "*.bmp");
m_files.clear();
for (int i = 0; i < list.size(); i++)
{
QFileInfo fileInfo = list.at(i);
qDebug() << fileInfo.absoluteFilePath();
m_files.push_back(fileInfo.absoluteFilePath());
if(m_file == m_files[i])
{
m_idx = i;
}
}
qDebug() << m_files.size();
m_box->setImage(QPixmap(m_files[m_idx]));
m_prevBtn->setEnabled(true);
m_nextBtn->setEnabled(true);
}
void PicViewWidget::show_prev()
{
if (m_idx == 0)
{
m_prevBtn->setEnabled(false);
qDebug() << m_idx;
return;
}
qDebug() << m_idx;
m_nextBtn->setEnabled(true);
m_idx--;
m_box->setImage(QPixmap(m_files[m_idx]));
}
void PicViewWidget::show_next()
{
if (m_idx == m_files.length() - 1)
{
m_nextBtn->setEnabled(false);
qDebug() << m_idx;
return;
}
qDebug() << m_idx;
m_prevBtn->setEnabled(true);
m_idx++;
m_box->setImage(QPixmap(m_files[m_idx]));
}
2.3 图像显示窗口
图像显示窗口,专门写了一个类来实现图片显示,缩放显示等功能。
2.3.1 图像位置计算与显示
由于每个图片的大小都不一样,为了能让图片显示的更合适,需要根据图片的大小和当前显示窗口的大小,计算出图片初始显示时需要缩放的比例,以及居中显示起始位置。
void ImageBox::setImage(QPixmap img)
{
m_img = img;
calcAndShow();
}
void ImageBox::calcAndShow()
{
if (!m_img.isNull())
{
int srcWidth = m_img.width();
int srcHeight = m_img.height();
qDebug() << "srcWidth: " << srcWidth << ", srcHeight: " << srcHeight;
int curWinWidth = this->width();
int curWinHeight = this->height();
qDebug() << "curWinWidth: " << curWinWidth << ", curWinHeight: " << curWinHeight;
if (srcHeight / srcWidth > curWinHeight / curWinWidth)
{
m_newHeight = curWinHeight;
m_newWidth = srcWidth * curWinHeight / srcHeight;
m_scale = (float)curWinWidth / (float)srcHeight;
}
else
{
m_newHeight = srcHeight * curWinWidth / srcWidth;
m_newWidth = curWinWidth;
m_scale = (float)m_newWidth / (float)srcWidth;
}
qDebug() << "m_newWidth: " << m_newWidth << ", m_newHeight: " << m_newHeight;
m_point = QPoint(int((curWinWidth - m_newWidth) * 0.5), int((curWinHeight - m_newHeight) * 0.5));
qDebug() << "m_scale: " << m_scale << "m_point: " << m_point.x() << " " << m_point.y();
update();
}
}
void ImageBox::paintEvent(QPaintEvent *event)
{
if (!m_img.isNull())
{
QPainter painter(this);
painter.scale(m_scale, m_scale);
if (m_wheelFlag)
{
m_wheelFlag = false;
float this_left_x = m_point.x() * m_lastScale;
float this_left_y = m_point.y() * m_lastScale;
float this_scale_width = m_newWidth * m_lastScale;
float this_scale_height = m_newHeight * m_lastScale;
float gap_x = m_x - this_left_x;
float gap_y = m_y - this_left_y;
if (0 < gap_x < this_scale_width && 0 < gap_y < this_scale_height)
{
int new_left_x = int(m_x / m_scale - gap_x / m_lastScale);
int new_left_y = int(m_y / m_scale - gap_y / m_lastScale);
m_point = QPoint(new_left_x, new_left_y);
}
else
{
int true_left_x = int(m_point.x() * m_lastScale / m_scale);
int true_left_y = int(m_point.y() * m_lastScale / m_scale);
m_point = QPoint(true_left_x, true_left_y);
}
}
painter.drawPixmap(m_point, m_img);
}
}
2.3.2 鼠标缩放与移动
图片的显示还之处鼠标操作,可以实现滚轮的放大缩小,移动显示,对应的处理逻辑如下:
void ImageBox::wheelEvent(QWheelEvent *event)
{
float angleY = event->delta()/8;
m_lastScale = m_scale;
m_wheelFlag = true;
m_x = event->x();
m_y = event->y();
if (angleY > 0)
{
m_scale *= 1.08;
}
else
{
m_scale *= 0.92;
}
if (m_scale < 0.1)
{
m_scale = 0.1;
}
else if (m_scale > 100)
{
m_scale = 100;
}
adjustSize();
update();
}
void ImageBox::mouseMoveEvent(QMouseEvent *event)
{
if (m_leftClick)
{
m_endPos = event->pos() - m_startPos;
m_point = m_point + m_endPos / m_scale ;
m_startPos = event->pos();
update();
}
}
void ImageBox::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
m_leftClick = true;
m_startPos = event->pos();
}
}
void ImageBox::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
m_leftClick = false;
}
}
3 交叉编译与测试
Qt程序编写好后,通过交叉编译,来测试图片查看器在K3568-C板子上的实际效果。
使用编译音乐播放器时编写my3568build.sh的编译脚本编译即可,无需修改:
#! /bin/bash
mkdir -p build
cd build
export PATH=/home/xxpcb/myTest/OK3568/sourcecode/OK3568-linux-source/buildroot/output/OK3568/host/bin:$PATH
qmake .. && make
执行该脚本即可编译:
./my3568build.sh
将编译成功的可执行文件picview复制到OK3568-C板子中,,然后就可以测试了:
可以看到在打开一张图片后,会把同目录下的所有图片都找处理,演示截图:
打开图片文件:
显示图片:
实际演示视频见文末。
4 总结
本篇介绍了在OK3568-C开发板上实现一个图片查看器的测评过程,首先使用Qt编写图片查看器的代码,然后在Ubuntu中,使用搭建好的交叉编译环境进行代码编译,最后把编译出的可执行文件放到板子中进行实际测试。
演示视频: