Qt实战之实现图片浏览器
图片浏览器逻辑
实现图片浏览器用到了前面几乎所有的知识,包括窗口部件、布局、事件、对象模型与容器类、图形视图、模型/视图编程以及多线程等。大致流程为:首先定义一个图片类,该类包含图片的路径、文件名、文件id以及获取这些变量的函数。然后定义了一个图片数组类,主要包含添加图像以及获取所有图像以及新加入图像的函数。最后通过将图片名字加入到界面左侧QDockWidget部件中的QtreeView中,通过线程将图片的预览加入界面下侧的窗口部件中。最后通过双击可查看完整图片,以及通过滚轮和鼠标等事件来对图片进行一些操作。
效果图
具体实现
utils.h
#ifndef UTILS_H
#define UTILS_H
#include<QString>
#include<string>
//string to QString
inline QString str2qstr(const std::string& str)
{
return QString::fromLocal8Bit(str.data());
}
//QString to string
inline std::string qstr2str(const QString& qstr)
{
QByteArray cdata = qstr.toLocal8Bit();
return std::string(cdata);
}
#endif // UTILS_H
image.h
#ifndef IMAGE_H
#define IMAGE_H
#include<string>
#include<vector>
using std::vector;
using std::string;
class Image
{
public:
Image() = default;
~Image() = default;
Image(const string& _path,const unsigned& _id):path_(_path),id_(_id){
//从路径中获取图像名称
auto pos = path_.find_last_of('\\') +1;
if(pos == 0)
{
pos = path_.find_last_of('/')+1;
}
name_ = path_.substr(pos,path_.length() - pos);
pos = name_.find_last_of('.');
name_ = name_.substr(0,pos);
}
//设置相机所属的id
void set_cam_id(const unsigned& _id){
cam_id_ = _id;
}
//获取路径
const string& get_path(){
return path_;
}
//获取文件名
const string& get_name(){
return name_;
}
const unsigned &get_id(){
return id_;
}
//获取相机id
const unsigned& get_cam_id(){
return cam_id_;
}
private:
string path_;
string name_;
unsigned id_;
unsigned cam_id_;
};
#endif // IMAGE_H
image_group.h
#ifndef IMAGE_GROUP_H
#define IMAGE_GROUP_H
#include"image.h"
class image_group
{
public:
image_group();
~image_group();
public:
bool addImages(const vector<string>& img_paths);
const vector<Image>& GetAllImages();
const vector<Image>& GetNewAddingImages();
private:
//所有图片数组
vector<Image> all_images_;
//新加入的图片数组
vector<Image> new_images_;
};
#endif // IMAGE_GROUP_H
image_group.cpp
#include "image_group.h"
image_group::image_group()
{
}
image_group::~image_group()
{
}
bool image_group::addImages(const vector<std::string> &img_paths)
{
new_images_.clear();
for(auto& path: img_paths)
{
all_images_.emplace_back(path,all_images_.size());
new_images_.emplace_back(path,all_images_.size());
}
return true;
}
const vector<Image> &image_group::GetAllImages()
{
return all_images_;
}
const vector<Image> &image_group::GetNewAddingImages()
{
return new_images_;
}
qimgviewwidget.h
#ifndef QIMGVIEWWIDGET_H
#define QIMGVIEWWIDGET_H
#include <QWidget>
#include<QImage>
//用于显示2D图像的窗口部件
class QImgViewWidget : public QWidget
{
Q_OBJECT
public:
explicit QImgViewWidget(QWidget *parent = nullptr);
~QImgViewWidget() = default;
//从文件路径加载图片
void SetImage(const QString& img_path);
void ResetTransfORM();
private:
//用来展示的image信息
QImage img_display_;
//原始pixmap信息
QPixmap pix_ori_;
//用来展示的pixmap信息
QPixmap pix_display_;
//图片路径
QString img_path_;
//鼠标滚轮控制的缩放比例
float zoom_scale_;
//由鼠标移动控制的移动
QPoint move_step_;
bool move_start_;
bool is_moving_;
QPoint mouse_point_;
protected:
void paintEvent(QPaintEvent* event)override;
void wheelEvent(QWheelEvent* event)override;
void mousePressEvent(QMouseEvent* event)override;
void mouseReleaseEvent(QMouseEvent* event)override;
void mouseMoveEvent(QMouseEvent* event)override;
void resizeEvent(QResizeEvent* event)override;
};
#endif // QIMGVIEWWIDGET_H
qimgviewwidget.cpp
#include "qimgviewwidget.h"
#include <QPainter>
#include <QWheelEvent>
QImgViewWidget::QImgViewWidget(QWidget *parent) : QWidget(parent)
{
zoom_scale_ = 1.0f;
move_start_ = false;
is_moving_ = false;
}
void QImgViewWidget::SetImage(const QString &img_path)
{
ResetTransform();
QSize view_size = size();
//通过QImage加载图像
img_path_ = img_path;
img_display_.load(img_path_);
pix_ori_ = QPixmap::fromImage(img_display_);
pix_display_ = pix_ori_.scaled(zoom_scale_ *size(),Qt::KeepAspectRatio);
}
void QImgViewWidget::ResetTransform()
{
//重新设置缩放比例和移动位置
zoom_scale_ = 1.0f;
move_step_ = QPoint(0,0);
}
void QImgViewWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawPixmap(move_step_.x()+(width()-pix_display_.width())/2,move_step_.y()+(height()- pix_display_.height()) / 2,pix_display_);
}
void QImgViewWidget::wheelEvent(QWheelEvent *event)
{
//随滚轮缩放
if(event->delta()>0)
{
zoom_scale_ *= 1.1;
}
else {
zoom_scale_ *=0.9;
}
pix_display_ = pix_ori_.scaled(zoom_scale_ * size(),Qt::KeepAspectRatio);
update();
}
void QImgViewWidget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
if(!move_start_)
{
move_start_ = true;
is_moving_ = false;
mouse_point_ = event->globalPos();
}
}
}
void QImgViewWidget::mouseReleaseEvent(QMouseEvent *event)
{
if(event->button() ==Qt::LeftButton)
{
if(move_start_)
{
move_start_ = false;
is_moving_ = false;
}
}
}
void QImgViewWidget::mouseMoveEvent(QMouseEvent *event)
{
if(move_start_)
{
const QPoint mos_pt = event->globalPos();
move_step_ +=mos_pt - mouse_point_;
is_moving_ = true;
mouse_point_ = mos_pt;
repaint();
}
}
void QImgViewWidget::resizeEvent(QResizeEvent *event)
{
pix_display_ = pix_ori_.scaled(zoom_scale_ *size(),Qt::KeepAspectRatio);
update();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include"image_group.h"
#include<QThread>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = Q_NULLPTR);
// ~MainWindow();
image_group* get_image_group(){
return &image_group_;
}
Ui::MainWindow* get_ui(){
return ui;
}
void InitTreeView();
void InitPrewView();
void InitDockView();
void InitTabView();
void InitLayout();
void updateTreeView();
void updatePreView();
private slots:
void OnActionAddImages();
void OnActiondoubleClickedTreeImage(const QModelIndex& index);
protected:
void paintEvent(QPaintEvent* event)override;
void resizeEvent(QResizeEvent* event)override;
private:
Ui::MainWindow *ui;
image_group image_group_;
};
class QUpdatePreviewThread :public QThread
{
Q_OBJECT
public:
QUpdatePreviewThread(MainWindow* mainwindow);
signals:
void SingalAddPreview(const QPixmap& pixmap,const int& img_id);
private slots:
void SlotAddPreview(const QPixmap& pixmap,const int& img_id);
protected:
void run() override;
private:
MainWindow* mainwindow_;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include"utils.h"
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include<QStandardItemModel>
#include<QFileDialog>
#include<QMessageBox>
#include<QResizeEvent>
#include<QListWidgetItem>
#include<QImageReader>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
setwindowstate(Qt::WindowMaximized);
//初始化Ui
InitDockView();
InitTreeView();
InitTabView();
InitPrewView();
InitLayout();
connect(ui->action_addimages,&QAction::triggered,this,&MainWindow::OnActionAddImages);
connect(ui->tree_images,&QTreeView::doubleClicked,this,&MainWindow::OnActiondoubleClickedTreeImage);
connect(ui->list_previews,&QTreeView::doubleClicked,this,&MainWindow::OnActiondoubleClickedTreeImage);
}
void MainWindow::InitTreeView()
{
//初始化tree view
auto tree_imgs = ui->tree_images;
auto* model = new QStandardItemModel(tree_imgs);
tree_imgs->setModel(model);
model->setHorizontalHeaderLabels(QStringList()<<QStringLiteral("images"));
tree_imgs->setEditTriggers(QAbstractItemView::NoEditTriggers);
}
void MainWindow::InitPrewView()
{
//初始化preview view
auto list_preview = ui->list_previews;
list_preview->setIconSize(QSize(100,100));
list_preview->setResizeMode(QListView::Adjust);
list_preview->setViewMode(QListView::IconMode);
list_preview->setMovement(QListView::Static);
list_preview->setSpacing(10);
}
void MainWindow::InitDockView()
{
//初始化dockView
auto dp_workspace = (QDockWidget*)ui->dock_workspace;
dp_workspace->setWindowTitle("Wokspace");
aDDDockWidget(Qt::LeftDockWidgetArea,dp_workspace);
}
void MainWindow::InitTabView()
{
//2d/3D部件的样式表
// auto tab_view = ui->tab_mainview;
// tab_view->setStyleSheet("QTabWidget:pane{ border: 1px ;}\
// QTabBar::tab{width:50px;height:28px; background-color:rgb(36,36,36); color:rgb(200,200,200); margin-right: 2px; margin-bottom:-2px;}\
// QTabBar::tab:selected{border:1px;border-bottom-color: none; background-color:rgb(50,50,50)}\
// QTabBar::tab:!selected{border-bottom: 3px;}\
// ");
// tab_view->setCurrentIndex(0);
// auto wid_model = ui->widget_model;
// wid_model->setStyleSheet("background-color:rgb(80,80,80);");
// auto wid_image = ui->widght_imgview;
// wid_image->setStyleSheet(" background-color:rgb(80,80,80);");
// //照片/控制台部件样式表
// auto tab_info = ui->tab_info;
// tab_info->setStyleSheet("QTabWidget:pane{ border: 1px ;}\
// QTabBar::tab{width:60px;height:28px; background-color:rgb(36,36,36); color:rgb(200,200,200); margin-right: 2px; margin-bottom:-2px;}\
// QTabBar::tab:selected{border:1px;border-bottom-color: none; background-color:rgb(50,50,50)}\
// QTabBar::tab:!selected{border-bottom: 3px;}\
// ");
// auto wid_priviews = ui->widget_previews;
// wid_priviews->setStyleSheet(" background-color:rgb(80,80,80);");
// auto wid_console = ui->widget_console;
// wid_console->setStyleSheet(" background-color:rgb(80,80,80);");
}
void MainWindow::InitLayout()
{
}
void MainWindow::updateTreeView()
{
//相关操作后更新tree view。例如,加载图像
const auto all_images = image_group_.GetAllImages();
const auto new_images = image_group_.GetNewAddingImages();
if(new_images.empty())
return;
const auto num_allimages = all_images.size();
const auto num_images = new_images.size();
auto* tree = ui->tree_images;
auto* model = static_cast<QStandardItemModel*>(tree->model());
QString str_title = QString("All Images (%1 Images)").arg(num_allimages);
if(model->item(0,0) ==nullptr)
{
model->setItem(0,0,new QStandardItem(str_title));
}
else {
auto item = model->item(0,0);
item->setText(str_title);
}
//为tree添加图片的名字
for(auto image:new_images)
{
model->item(0,0)->setChild(image.get_id(),0,new QStandardItem(str2qstr(image.get_name())));
}
tree->setExpanded(model->index(0,0),true);
auto workspace = static_cast<QDockWidget*>(ui->dock_workspace);
auto ws_size = workspace->frameSize();
tree->setFixedWidth(ws_size.width());
}
void MainWindow::updatePreView()
{
auto tab_info = ui->tab_info;
tab_info->setCurrentIndex(0);
//通过线程来创建previews
QUpdatePreviewThread* thread = new QUpdatePreviewThread(this);
thread->start();
}
void MainWindow::OnActionAddImages()
{
QStringList file_paths = QFileDialog::getOpenFileNames(this,tr("Image Path"),"Data\\", tr("Image Files(*png *jpg *tif);"));
//添加图片
vector<string> str_paths;
for(auto path:file_paths)
{
str_paths.push_back(qstr2str(path));
}
image_group_.addImages(str_paths);
//更新tree view
updateTreeView();
//更新preview view
updatePreView();
}
void MainWindow::OnActiondoubleClickedTreeImage(const QModelIndex &index)
{
QMessageBox msg_box;
auto* tree = ui->tree_images;
auto* model = static_cast<QStandardItemModel*>(tree->model());
unsigned img_id = index.row();
const auto all_images = image_group_.GetAllImages();
if(img_id>=all_images.size())
{
msg_box.setText("data error");
msg_box.exec();
return;
}
//获得双击的图片路径
auto image = all_images[img_id];
auto imgpath = str2qstr(image.get_path());
//repaint
auto img_view = ui->widght_imgview;
img_view->SetImage(imgpath);
img_view->repaint();
}
void MainWindow::paintEvent(QPaintEvent* event)
{
}
void MainWindow::resizeEvent(QResizeEvent* event)
{
//自动调整所有小部件的大小
const QSize size = event->size();
const QRect frame_geometry = ui->central_widget->geometry();
const QSize frame_size = ui->central_widget->size();
const QSize menu_size = ui->menuBar->size();
QSize layout_h1(menu_size.width() * 0.12, frame_size.height()),
layout_h2(menu_size.width() * 0.76, frame_size.height()),
layout_h3(menu_size.width() * 0.12, frame_size.height());
QSize layout_v1(menu_size.width(), frame_size.height()*0.8),
layout_v2(menu_size.width(), frame_size.height()*0.2);
// workspace
ui->dock_workspace->setMinimumWidth(layout_h1.width());
ui->dock_workspace->setMaximumHeight(frame_size.height());
// main view(model and image)
ui->tab_mainview->setFixedSize(layout_h2.width() - 10, layout_v1.height());
QSize tab_bar_sz = ui->tab_mainview->tabBar()->size();
ui->widget_model->setFixedSize(layout_h2.width() - 10, layout_v1.height() - tab_bar_sz.height());
ui->widght_imgview->setFixedSize(layout_h2.width() - 10, layout_v1.height() - tab_bar_sz.height());
ui->widget_gl_model->setFixedSize(layout_h2.width() - 10, layout_v1.height() - tab_bar_sz.height());
// info view(photos and console)
QRect view_rt = ui->tab_mainview->frameGeometry();
ui->tab_info->setGeometry(view_rt.x(), view_rt.y() + view_rt.height()+5, layout_h2.width() - 10, layout_v2.height());
auto tab_sz = ui->tab_info->size();
tab_bar_sz = ui->tab_info->tabBar()->size();
ui->list_previews->setGeometry(0, 0, tab_sz.width(), tab_sz.height()- tab_bar_sz.height());
setMinimumSize(500, 500);
}
QUpdatePreviewThread::QUpdatePreviewThread(MainWindow *mainwindow)
{
mainwindow_ = mainwindow;
connect(this, SIGNAL(SingalAddPreview(const QPixmap&, const int&)), this, SLOT(SlotAddPreview(const QPixmap&, const int&)));
}
void QUpdatePreviewThread::SlotAddPreview(const QPixmap &pixmap, const int &img_id)
{
auto ui = mainwindow_->get_ui();
const auto all_images = mainwindow_->get_image_group()->GetAllImages();
if(img_id >=all_images.size())
return;
auto list_preview = ui->list_previews;
const QSize icon_sz = list_preview->iconSize();
auto image = all_images[img_id];
QListWidgetItem* list_item = new QListWidgetItem(QIcon(pixmap),str2qstr(image.get_name()));
list_item->setSizeHint(QSize(icon_sz.width(),icon_sz.height()+10));
list_preview->insertItem(img_id,list_item);
list_preview->repaint();
}
void QUpdatePreviewThread::run()
{
if(mainwindow_ ==nullptr || mainwindow_->get_image_group() ==nullptr)
{
return;
}
auto ui = mainwindow_->get_ui();
const auto new_images = mainwindow_->get_image_group()->GetNewAddingImages();
if(new_images.empty())
{
return;
}
const auto num_images = new_images.size();
QImageReader reader;
for(auto k=0;k<num_images;k++)
{
auto image = new_images[k];
reader.setFileName(str2qstr(image.get_path()));
reader.setAutoTransform(true);
const QSize img_size = reader.size();
const QSize icon_sz = ui->list_previews->iconSize();
const QSize size_scale = img_size.scaled(icon_sz,Qt::KeepAspectRatio);
reader.setScaledSize(size_scale);
auto pixmap = QPixmap::fromImageReader(&reader);
emit SingalAddPreview(pixmap,image.get_id());
}
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int arGC, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
以上就是Qt实战之实现图片浏览器的详细内容,更多关于Qt图片浏览器的资料请关注其它相关文章!
相关文章