diff --git a/CMakeLists.txt b/CMakeLists.txt index 1257b08..89806a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,19 +1,22 @@ cmake_minimum_required(VERSION 3.16) -project(Qt-Examples VERSION 0.1 LANGUAGES CXX) +project( + Qt-Examples + VERSION 0.1 + LANGUAGES CXX) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -IF (CMAKE_HOST_WIN32) - list(APPEND CMAKE_PREFIX_PATH "C:\\Qt\\6.5.0\\msvc2019_64") -ENDIF () +if(CMAKE_HOST_WIN32) + list(APPEND CMAKE_PREFIX_PATH "C:\\Qt\\6.5.0\\msvc2019_64") +endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -#qt_standard_project_setup() 这个识别不了 +# qt_standard_project_setup() 这个识别不了 set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -30,6 +33,7 @@ add_subdirectory(DragDrop) add_subdirectory(FlowLayout) add_subdirectory(GridViewModel) add_subdirectory(HttpClient) +add_subdirectory(ImageCarousel) add_subdirectory(LogAsynchronous) add_subdirectory(MulClient) add_subdirectory(MulServer) diff --git a/FlowLayout/CMakeLists.txt b/FlowLayout/CMakeLists.txt index c2cbf9b..faca4bc 100644 --- a/FlowLayout/CMakeLists.txt +++ b/FlowLayout/CMakeLists.txt @@ -1,11 +1,11 @@ set(PROJECT_SOURCES - main.cc - mainwindow.cc - mainwindow.hpp - flowlayout.h - flowlayout.cpp - flowwidget.hpp - flowwidget.cc) + main.cc + mainwindow.cc + mainwindow.hpp + flowlayout.h + flowlayout.cpp + flowwidget.hpp + flowwidget.cc) qt_add_executable(FlowLayout MANUAL_FINALIZATION ${PROJECT_SOURCES}) target_link_libraries(FlowLayout PRIVATE Qt6::Widgets) diff --git a/ImageCarousel/CMakeLists.txt b/ImageCarousel/CMakeLists.txt new file mode 100644 index 0000000..786e546 --- /dev/null +++ b/ImageCarousel/CMakeLists.txt @@ -0,0 +1,12 @@ +set(PROJECT_SOURCES + imagecarousel.cc + imagecarousel.hpp + imagecarouselwidget.hpp + imagecarouselwidget.cc + main.cc + mainwindow.cc + mainwindow.hpp) + +qt_add_executable(ImageCarousel MANUAL_FINALIZATION ${PROJECT_SOURCES}) +target_link_libraries(ImageCarousel PRIVATE Qt6::Widgets) +qt_finalize_executable(ImageCarousel) diff --git a/ImageCarousel/ImageCarousel.pro b/ImageCarousel/ImageCarousel.pro new file mode 100644 index 0000000..a1fc4e2 --- /dev/null +++ b/ImageCarousel/ImageCarousel.pro @@ -0,0 +1,25 @@ +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++17 + +# 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 += \ + imagecarousel.cc \ + imagecarouselwidget.cc \ + main.cc \ + mainwindow.cc + +HEADERS += \ + imagecarousel.hpp \ + imagecarouselwidget.hpp \ + mainwindow.hpp + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/ImageCarousel/imagecarousel.cc b/ImageCarousel/imagecarousel.cc new file mode 100644 index 0000000..1b14692 --- /dev/null +++ b/ImageCarousel/imagecarousel.cc @@ -0,0 +1,137 @@ +#include "imagecarousel.hpp" + +#include +#include + +class ImageCarousel::ImageCarouselPrivate +{ +public: + explicit ImageCarouselPrivate(ImageCarousel *q) + : q_ptr(q) + { + timer = new QTimer(q); + timer->setSingleShot(false); + } + + ImageCarousel *q_ptr; + + QTimer *timer; + int msec = 1000; + int index = 0; + QList items; +}; + +ImageCarousel::ImageCarousel(QWidget *parent) + : QGraphicsView(parent) + , d_ptr(new ImageCarouselPrivate(this)) +{ + setScene(new QGraphicsScene(this)); + setViewportUpdateMode(SmartViewportUpdate); + setFrameShape(QFrame::NoFrame); + setRenderHint(QPainter::SmoothPixmapTransform); + + connect(d_ptr->timer, &QTimer::timeout, this, &ImageCarousel::next); +} + +ImageCarousel::~ImageCarousel() = default; + +auto ImageCarousel::addImage(const QString &filename) -> bool +{ + auto pixmap = QPixmap(filename); + if (pixmap.isNull()) { + return false; + } + + auto *item = new QGraphicsPixmapItem(pixmap); + item->setVisible(false); + scene()->addItem(item); + d_ptr->items.append(item); + + if (d_ptr->items.count() == 1) { + item->setVisible(true); + } + + return true; +} + +auto ImageCarousel::addImages(const QStringList &filenames) -> int +{ + int count = 0; + for (const auto &filename : qAsConst(filenames)) { + if (addImage(filename)) { + ++count; + } + } + return count; +} + +void ImageCarousel::clearImages() +{ + for (auto item : qAsConst(d_ptr->items)) { + scene()->removeItem(item); + delete item; + } + d_ptr->items.clear(); +} + +auto ImageCarousel::imageCount() const -> int +{ + return d_ptr->items.count(); +} + +void ImageCarousel::start() +{ + d_ptr->timer->start(d_ptr->msec); +} + +void ImageCarousel::stop() +{ + d_ptr->timer->stop(); +} + +void ImageCarousel::setInterval(int msec) +{ + d_ptr->msec = msec; +} + +auto ImageCarousel::interval() const -> int +{ + return d_ptr->msec; +} + +void ImageCarousel::onImageChanged(int index) +{ + Q_ASSERT(index >= 0 && index < d_ptr->items.count()); + + d_ptr->items[d_ptr->index]->setVisible(false); + d_ptr->index = index; + d_ptr->items[d_ptr->index]->setVisible(true); + + resetItem(); +} + +void ImageCarousel::next() +{ + if (d_ptr->items.isEmpty()) { + return; + } + + d_ptr->items[d_ptr->index]->setVisible(false); + d_ptr->index = (d_ptr->index + 1) % d_ptr->items.count(); + d_ptr->items[d_ptr->index]->setVisible(true); + + emit imageChanged(d_ptr->index); + + resetItem(); +} + +void ImageCarousel::resetItem() +{ + auto item = d_ptr->items[d_ptr->index]; + scene()->setSceneRect(item->boundingRect()); + resetTransform(); + auto size = item->pixmap().size(); + if (size.width() > width() || size.height() > height()) { + fitInView(item, Qt::KeepAspectRatio); + } +} diff --git a/ImageCarousel/imagecarousel.hpp b/ImageCarousel/imagecarousel.hpp new file mode 100644 index 0000000..26226ac --- /dev/null +++ b/ImageCarousel/imagecarousel.hpp @@ -0,0 +1,43 @@ +#ifndef IMAGECAROUSEL_HPP +#define IMAGECAROUSEL_HPP + +// qt image carousel control class + +#include + +class ImageCarousel : public QGraphicsView +{ + Q_OBJECT +public: + explicit ImageCarousel(QWidget *parent = nullptr); + ~ImageCarousel() override; + + auto addImage(const QString &filename) -> bool; + auto addImages(const QStringList &filenames) -> int; + void clearImages(); + + [[nodiscard]] auto imageCount() const -> int; + + void start(); + void stop(); + + void setInterval(int msec); + [[nodiscard]] auto interval() const -> int; + +public slots: + void onImageChanged(int index); + +signals: + void imageChanged(int index); + +private slots: + void next(); + +private: + void resetItem(); + + class ImageCarouselPrivate; + QScopedPointer d_ptr; +}; + +#endif // IMAGECAROUSEL_HPP diff --git a/ImageCarousel/imagecarouselwidget.cc b/ImageCarousel/imagecarouselwidget.cc new file mode 100644 index 0000000..aca08b2 --- /dev/null +++ b/ImageCarousel/imagecarouselwidget.cc @@ -0,0 +1,120 @@ +#include "imagecarouselwidget.hpp" +#include "imagecarousel.hpp" + +#include + +class ImageCarouselWidget::ImageCarouselWidgetPrivate +{ +public: + ImageCarouselWidgetPrivate(ImageCarouselWidget *q) + : q_ptr{q} + { + carousel = new ImageCarousel{q}; + btnGroup = new QButtonGroup{q}; + btnGroup->setExclusive(true); + layout = new QVBoxLayout{q}; + + layout->addWidget(carousel); + } + ~ImageCarouselWidgetPrivate() = default; + + void resetBtnWidget() + { + auto btns = btnGroup->buttons(); + for (auto btn : btns) { + btnGroup->removeButton(btn); + delete btn; + } + if (!btnWidget.isNull()) { + delete btnWidget.data(); + } + btnWidget = new QWidget(q_ptr); + layout->addWidget(btnWidget.data()); + auto btnLayout = new QHBoxLayout{btnWidget.data()}; + btnLayout->addStretch(); + auto count = carousel->imageCount(); + for (auto i = 0; i < count; ++i) { + auto btn = new QRadioButton{btnWidget.data()}; + btnLayout->addWidget(btn); + btnGroup->addButton(btn, i); + } + btnLayout->addStretch(); + } + + ImageCarouselWidget *q_ptr; + + ImageCarousel *carousel; + QPointer btnWidget; + QButtonGroup *btnGroup; + QVBoxLayout *layout; +}; + +ImageCarouselWidget::ImageCarouselWidget(QWidget *parent) + : QWidget{parent} + , d_ptr{new ImageCarouselWidgetPrivate{this}} +{ + buildConnect(); +} + +ImageCarouselWidget::~ImageCarouselWidget() = default; + +void ImageCarouselWidget::addImage(const QString &filename) +{ + d_ptr->carousel->addImage(filename); + + d_ptr->resetBtnWidget(); +} + +void ImageCarouselWidget::addImages(const QStringList &filenames) +{ + d_ptr->carousel->addImages(filenames); + + d_ptr->resetBtnWidget(); +} + +void ImageCarouselWidget::clearImages() +{ + d_ptr->carousel->clearImages(); + + d_ptr->resetBtnWidget(); +} + +void ImageCarouselWidget::start() +{ + d_ptr->carousel->start(); +} + +void ImageCarouselWidget::stop() +{ + d_ptr->carousel->stop(); +} + +void ImageCarouselWidget::setInterval(int msec) +{ + d_ptr->carousel->setInterval(msec); +} + +auto ImageCarouselWidget::interval() const -> int +{ + return d_ptr->carousel->interval(); +} + +void ImageCarouselWidget::onImageChanged(int index) +{ + auto btn = d_ptr->btnGroup->button(index); + if (btn) { + btn->setChecked(true); + } +} + +void ImageCarouselWidget::buildConnect() +{ + connect(d_ptr->carousel, + &ImageCarousel::imageChanged, + this, + &ImageCarouselWidget::onImageChanged); + connect(d_ptr->btnGroup, + &QButtonGroup::idClicked, + d_ptr->carousel, + &ImageCarousel::onImageChanged); +} diff --git a/ImageCarousel/imagecarouselwidget.hpp b/ImageCarousel/imagecarouselwidget.hpp new file mode 100644 index 0000000..9c30e61 --- /dev/null +++ b/ImageCarousel/imagecarouselwidget.hpp @@ -0,0 +1,38 @@ +#ifndef IMAGECAROUSELWIDGET_HPP +#define IMAGECAROUSELWIDGET_HPP + +#include +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif + +class ImageCarouselWidget : public QWidget +{ + Q_OBJECT +public: + explicit ImageCarouselWidget(QWidget *parent = nullptr); + ~ImageCarouselWidget() override; + + void addImage(const QString &filename); + void addImages(const QStringList &filenames); + void clearImages(); + + void start(); + void stop(); + + void setInterval(int msec); + [[nodiscard]] auto interval() const -> int; + +private slots: + void onImageChanged(int index); + +private: + void buildConnect(); + + class ImageCarouselWidgetPrivate; + QScopedPointer d_ptr; +}; + +#endif // IMAGECAROUSELWIDGET_HPP diff --git a/ImageCarousel/main.cc b/ImageCarousel/main.cc new file mode 100644 index 0000000..5d48a9d --- /dev/null +++ b/ImageCarousel/main.cc @@ -0,0 +1,16 @@ +#include "mainwindow.hpp" + +#include +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif + +auto main(int argc, char *argv[]) -> int +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + return a.exec(); +} diff --git a/ImageCarousel/mainwindow.cc b/ImageCarousel/mainwindow.cc new file mode 100644 index 0000000..80a21e1 --- /dev/null +++ b/ImageCarousel/mainwindow.cc @@ -0,0 +1,68 @@ +#include "mainwindow.hpp" +#include "imagecarouselwidget.hpp" + +#include + +class MainWindow::MainWindowPrivate +{ +public: + MainWindowPrivate(MainWindow *q) + : q_ptr(q) + { + imageCarouselWidget = new ImageCarouselWidget(q); + } + + MainWindow *q_ptr; + + ImageCarouselWidget *imageCarouselWidget; +}; + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , d_ptr(new MainWindowPrivate(this)) +{ + setupUI(); + resize(1000, 618); +} + +MainWindow::~MainWindow() {} + +void MainWindow::onAddImage() +{ + auto path = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation) + .value(0, QDir::homePath()); + auto images = QFileDialog::getOpenFileNames(this, + tr("Open Image"), + path, + tr("Images (*.png *.jpg)")); + if (images.isEmpty()) { + return; + } + + d_ptr->imageCarouselWidget->clearImages(); + d_ptr->imageCarouselWidget->addImages(images); +} + +void MainWindow::setupUI() +{ + auto openImageBtn = new QPushButton(tr("Add Image"), this); + connect(openImageBtn, &QPushButton::clicked, this, &MainWindow::onAddImage); + auto startBtn = new QPushButton(tr("Start"), this); + connect(startBtn, + &QPushButton::clicked, + d_ptr->imageCarouselWidget, + &ImageCarouselWidget::start); + auto stopBtn = new QPushButton(tr("Stop"), this); + connect(stopBtn, &QPushButton::clicked, d_ptr->imageCarouselWidget, &ImageCarouselWidget::stop); + + auto btnLayout = new QHBoxLayout; + btnLayout->addWidget(openImageBtn); + btnLayout->addWidget(startBtn); + btnLayout->addWidget(stopBtn); + + auto widget = new QWidget(this); + auto layout = new QVBoxLayout(widget); + layout->addWidget(d_ptr->imageCarouselWidget); + layout->addLayout(btnLayout); + setCentralWidget(widget); +} diff --git a/ImageCarousel/mainwindow.hpp b/ImageCarousel/mainwindow.hpp new file mode 100644 index 0000000..95dd551 --- /dev/null +++ b/ImageCarousel/mainwindow.hpp @@ -0,0 +1,28 @@ +#ifndef MAINWINDOW_HPP +#define MAINWINDOW_HPP + +#include +#if QT_VERSION >= 0x050000 +#include +#else +#include +#endif + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + explicit MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +private slots: + void onAddImage(); + +private: + void setupUI(); + + class MainWindowPrivate; + QScopedPointer d_ptr; +}; + +#endif // MAINWINDOW_HPP diff --git a/ImageCarousel/picture/ImageCarousel.jpg b/ImageCarousel/picture/ImageCarousel.jpg new file mode 100644 index 0000000..b6c3234 Binary files /dev/null and b/ImageCarousel/picture/ImageCarousel.jpg differ diff --git a/Qt-Examples.pro b/Qt-Examples.pro index ddc7d44..39f3c24 100644 --- a/Qt-Examples.pro +++ b/Qt-Examples.pro @@ -12,6 +12,7 @@ SUBDIRS += \ FlowLayout \ GridViewModel \ HttpClient \ + ImageCarousel \ LogAsynchronous \ MulClient \ MulServer \ diff --git a/README.md b/README.md index 32b6266..9bb9456 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,10 @@ ## [HttpClient ](/HttpClient)——http客户端; +## [ImageCarousel](ImageCarousel/)——简易图片轮播; + +
+ ## [GridViewModel ](/GridViewModel)——基于QListView的自适应宫图;