From ee912d70abd8ba90ee04027e501aa9d26d506c54 Mon Sep 17 00:00:00 2001 From: Jan Grewe Date: Wed, 11 Mar 2020 09:52:56 +0100 Subject: [PATCH] initial commit --- images/connect.png | Bin 0 -> 6575 bytes images/disconnect.png | Bin 0 -> 5771 bytes imageviewer.cpp | 373 ++++++++++++++++++++++++++++++++++++++++++ imageviewer.h | 86 ++++++++++ main.cpp | 22 +++ pylonrecorder.cpp | 71 ++++++++ pylonrecorder.h | 33 ++++ recorder.pro | 21 +++ resources.qrc | 6 + 9 files changed, 612 insertions(+) create mode 100644 images/connect.png create mode 100644 images/disconnect.png create mode 100644 imageviewer.cpp create mode 100644 imageviewer.h create mode 100644 main.cpp create mode 100644 pylonrecorder.cpp create mode 100644 pylonrecorder.h create mode 100644 recorder.pro create mode 100644 resources.qrc diff --git a/images/connect.png b/images/connect.png new file mode 100644 index 0000000000000000000000000000000000000000..29cf8cb13e12d66edff586a94341ef9b4dfae3ce GIT binary patch literal 6575 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�sRoad8K5gwr{>;EAe8=f@C(sv5_ EAEk^&wg3PC literal 0 HcmV?d00001 diff --git a/imageviewer.cpp b/imageviewer.cpp new file mode 100644 index 0000000..726aa93 --- /dev/null +++ b/imageviewer.cpp @@ -0,0 +1,373 @@ +#include "imageviewer.h" + +#include +#include +//nclude +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(QT_PRINTSUPPORT_LIB) +# include + +# if QT_CONFIG(printdialog) +# include +# endif +#endif + +ImageViewer::ImageViewer(QWidget *parent) + : QMainWindow(parent), imageLabel(new QLabel), scrollArea(new QScrollArea) +{ + imageLabel->setBackgroundRole(QPalette::Base); + imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); + imageLabel->setScaledContents(true); + + scrollArea->setBackgroundRole(QPalette::Dark); + scrollArea->setWidget(imageLabel); + scrollArea->setVisible(false); + setCentralWidget(scrollArea); + pylon = new PylonRecorder(); + createActions(); + updateActions(); + resize(QGuiApplication::primaryScreen()->availableSize() * 3 / 5); + +} + +ImageViewer::~ImageViewer(){ + delete pylon; +} + +bool ImageViewer::loadFile(const QString &fileName) { + QImageReader reader(fileName); + reader.setAutoTransform(true); + const QImage newImage = reader.read(); + if (newImage.isNull()) { + QMessageBox::information(this, QGuiApplication::applicationDisplayName(), + tr("Cannot load %1: %2") + .arg(QDir::toNativeSeparators(fileName), reader.errorString())); + return false; + } + + setImage(newImage); + + setWindowFilePath(fileName); + + const QString message = tr("Opened \"%1\", %2x%3, Depth: %4") + .arg(QDir::toNativeSeparators(fileName)).arg(image.width()).arg(image.height()).arg(image.depth()); + statusBar()->showMessage(message); + return true; +} + +void ImageViewer::setImage(const QImage &newImage) { + image = newImage; + // (image.colorSpace().isValid()) + // image.convertToColorSpace(QColorSpace::SRgb); + imageLabel->setPixmap(QPixmap::fromImage(image)); + scaleFactor = 1.0; + + scrollArea->setVisible(true); + printAct->setEnabled(true); + fitToWindowAct->setEnabled(true); + updateActions(); + + if (!fitToWindowAct->isChecked()) + imageLabel->adjustSize(); +} + +bool ImageViewer::saveFile(const QString &fileName) { + QImageWriter writer(fileName); + + if (!writer.write(image)) { + QMessageBox::information(this, QGuiApplication::applicationDisplayName(), + tr("Cannot write %1: %2") + .arg(QDir::toNativeSeparators(fileName)), writer.errorString()); + return false; + } + const QString message = tr("Wrote \"%1\"").arg(QDir::toNativeSeparators(fileName)); + statusBar()->showMessage(message); + return true; +} + +static void initializeImageFileDialog(QFileDialog &dialog, QFileDialog::AcceptMode acceptMode) { + static bool firstDialog = true; + + if (firstDialog) { + firstDialog = false; + const QStringList picturesLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation); + dialog.setDirectory(picturesLocations.isEmpty() ? QDir::currentPath() : picturesLocations.last()); + } + + QStringList mimeTypeFilters; + const QByteArrayList supportedMimeTypes = acceptMode == QFileDialog::AcceptOpen + ? QImageReader::supportedMimeTypes() : QImageWriter::supportedMimeTypes(); + for (const QByteArray &mimeTypeName : supportedMimeTypes) + mimeTypeFilters.append(mimeTypeName); + mimeTypeFilters.sort(); + dialog.setMimeTypeFilters(mimeTypeFilters); + dialog.selectMimeTypeFilter("image/jpeg"); + if (acceptMode == QFileDialog::AcceptSave) + dialog.setDefaultSuffix("jpg"); +} + +void ImageViewer::open() { + QFileDialog dialog(this, tr("Open File")); + initializeImageFileDialog(dialog, QFileDialog::AcceptOpen); + + while (dialog.exec() == QDialog::Accepted && !loadFile(dialog.selectedFiles().first())) {} +} + +void ImageViewer::saveAs() { + QFileDialog dialog(this, tr("Save File As")); + initializeImageFileDialog(dialog, QFileDialog::AcceptSave); + + while (dialog.exec() == QDialog::Accepted && !saveFile(dialog.selectedFiles().first())) {} +} + +void ImageViewer::print() { + Q_ASSERT(imageLabel->pixmap()); +#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printdialog) + + QPrintDialog dialog(&printer, this); + + if (dialog.exec()) { + QPainter painter(&printer); + QRect rect = painter.viewport(); + QSize size = imageLabel->pixmap()->size(); + size.scale(rect.size(), Qt::KeepAspectRatio); + painter.setViewport(rect.x(), rect.y(), size.width(), size.height()); + painter.setWindow(imageLabel->pixmap()->rect()); + painter.drawPixmap(0, 0, *imageLabel->pixmap()); + } +#endif +} + +void ImageViewer::copy() { +#ifndef QT_NO_CLIPBOARD + QGuiApplication::clipboard()->setImage(image); +#endif // !QT_NO_CLIPBOARD +} + +#ifndef QT_NO_CLIPBOARD +static QImage clipboardImage() { + if (const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData()) { + if (mimeData->hasImage()) { + const QImage image = qvariant_cast(mimeData->imageData()); + if (!image.isNull()) + return image; + } + } + return QImage(); +} +#endif // !QT_NO_CLIPBOARD + +void ImageViewer::paste() { +#ifndef QT_NO_CLIPBOARD + const QImage newImage = clipboardImage(); + if (newImage.isNull()) { + statusBar()->showMessage(tr("No image in clipboard")); + } else { + setImage(newImage); + setWindowFilePath(QString()); + const QString message = tr("Obtained image from clipboard, %1x%2, Depth: %3") + .arg(newImage.width()).arg(newImage.height()).arg(newImage.depth()); + statusBar()->showMessage(message); + } +#endif // !QT_NO_CLIPBOARD +} + +void ImageViewer::zoomIn() { + scaleImage(1.25); +} + +void ImageViewer::zoomOut() +{ + scaleImage(0.8); +} + +void ImageViewer::normalSize() { + imageLabel->adjustSize(); + scaleFactor = 1.0; +} + +void ImageViewer::fitToWindow() { + bool fitToWindow = fitToWindowAct->isChecked(); + scrollArea->setWidgetResizable(fitToWindow); + if (!fitToWindow) + normalSize(); + updateActions(); +} + +void ImageViewer::about() { + QMessageBox::about(this, tr("About Image Viewer"), + tr("

The Image Viewer example shows how to combine QLabel " + "and QScrollArea to display an image. QLabel is typically used " + "for displaying a text, but it can also display an image. " + "QScrollArea provides a scrolling view around another widget. " + "If the child widget exceeds the size of the frame, QScrollArea " + "automatically provides scroll bars.

The example " + "demonstrates how QLabel's ability to scale its contents " + "(QLabel::scaledContents), and QScrollArea's ability to " + "automatically resize its contents " + "(QScrollArea::widgetResizable), can be used to implement " + "zooming and scaling features.

In addition the example " + "shows how to use QPainter to print an image.

")); +} + +void ImageViewer::createActions() { + const QIcon connect_icon(":/images/connect.png"); + const QIcon disconnect_icon(":/images/disconnect.png"); + + QMenu *fileMenu = menuBar()->addMenu(tr("&File")); + + QAction *openAct = fileMenu->addAction(tr("&Open..."), this, &ImageViewer::open); + openAct->setShortcut(QKeySequence::Open); + + saveAsAct = fileMenu->addAction(tr("&Save As..."), this, &ImageViewer::saveAs); + saveAsAct->setEnabled(false); + + printAct = fileMenu->addAction(tr("&Print..."), this, &ImageViewer::print); + printAct->setShortcut(QKeySequence::Print); + printAct->setEnabled(false); + + fileMenu->addSeparator(); + + QAction *exitAct = fileMenu->addAction(tr("E&xit"), this, &ImageViewer::quitApplication); + exitAct->setShortcut(tr("Ctrl+Q")); + + QMenu *editMenu = menuBar()->addMenu(tr("&Edit")); + + copyAct = editMenu->addAction(tr("&Copy"), this, &ImageViewer::copy); + copyAct->setShortcut(QKeySequence::Copy); + copyAct->setEnabled(false); + + QAction *pasteAct = editMenu->addAction(tr("&Paste"), this, &ImageViewer::paste); + pasteAct->setShortcut(QKeySequence::Paste); + + QMenu *viewMenu = menuBar()->addMenu(tr("&View")); + + zoomInAct = viewMenu->addAction(tr("Zoom &In (25%)"), this, &ImageViewer::zoomIn); + zoomInAct->setShortcut(QKeySequence::ZoomIn); + zoomInAct->setEnabled(false); + + zoomOutAct = viewMenu->addAction(tr("Zoom &Out (25%)"), this, &ImageViewer::zoomOut); + zoomOutAct->setShortcut(QKeySequence::ZoomOut); + zoomOutAct->setEnabled(false); + + normalSizeAct = viewMenu->addAction(tr("&Normal Size"), this, &ImageViewer::normalSize); + normalSizeAct->setShortcut(tr("Ctrl+S")); + normalSizeAct->setEnabled(false); + + viewMenu->addSeparator(); + + fitToWindowAct = viewMenu->addAction(tr("&Fit to Window"), this, &ImageViewer::fitToWindow); + fitToWindowAct->setEnabled(false); + fitToWindowAct->setCheckable(true); + fitToWindowAct->setShortcut(tr("Ctrl+F")); + + QMenu *camera_menu = menuBar()->addMenu(tr("&Camera")); + connect_camera_action = camera_menu->addAction(connect_icon, tr("&Connect"), this, &ImageViewer::connectCamera); + connect_camera_action->setStatusTip(tr("Connect to to camera and open device")); + disconnect_camera_action = camera_menu->addAction(disconnect_icon, tr("&Disconnect"), this, &ImageViewer::disconnectCamera); + disconnect_camera_action->setStatusTip(tr("Disconnect from the camera device")); + camera_menu->addSeparator(); + grab_still_action = camera_menu->addAction(tr("&Grab still"), this, &ImageViewer::grabStillFromPylon); + grab_still_action->setStatusTip(tr("Grab single image from Pylon camera")); + grab_still_action->setShortcut(tr("Ctrl+ ")); + grab_continuous_action = camera_menu->addAction(tr("&grab continuous"), this, &ImageViewer::startRecording); + grab_continuous_action->setShortcut(tr("Ctrl+Enter")); + + QMenu *helpMenu = menuBar()->addMenu(tr("&Help")); + helpMenu->addAction(tr("&About"), this, &ImageViewer::about); + helpMenu->addAction(tr("About &Qt"), &QApplication::aboutQt); + + QToolBar *toolbar = addToolBar("main toolbar"); + toolbar->addSeparator(); + toolbar->addAction(connect_camera_action); + toolbar->addAction(disconnect_camera_action); + toolbar->addSeparator(); + toolbar->addAction(grab_still_action); +} + +void ImageViewer::updateActions() { + saveAsAct->setEnabled(!image.isNull()); + copyAct->setEnabled(!image.isNull()); + zoomInAct->setEnabled(!fitToWindowAct->isChecked()); + zoomOutAct->setEnabled(!fitToWindowAct->isChecked()); + normalSizeAct->setEnabled(!fitToWindowAct->isChecked()); + disconnect_camera_action->setEnabled(pylon->isOpen()); + connect_camera_action->setEnabled(!pylon->isOpen()); + grab_still_action->setEnabled(pylon->isOpen()); + grab_continuous_action->setEnabled(false); +} + +void ImageViewer::scaleImage(double factor) { + Q_ASSERT(imageLabel->pixmap()); + scaleFactor *= factor; + imageLabel->resize(scaleFactor * imageLabel->pixmap()->size()); + + adjustScrollBar(scrollArea->horizontalScrollBar(), factor); + adjustScrollBar(scrollArea->verticalScrollBar(), factor); + + zoomInAct->setEnabled(scaleFactor < 3.0); + zoomOutAct->setEnabled(scaleFactor > 0.333); +} + +void ImageViewer::quitApplication() { + if (pylon->isOpen()) { + pylon->closeCamera(); + } + //pylon->terminate(); + this->close(); +} + +void ImageViewer::adjustScrollBar(QScrollBar *scrollBar, double factor) { + scrollBar->setValue(int(factor * scrollBar->value() + + ((factor - 1) * scrollBar->pageStep()/2))); +} + +void ImageViewer::connectCamera() { + std::string message; + pylon->openCamera(message); + statusBar()->showMessage(QString::fromStdString(message)); + updateActions(); +} + + +void ImageViewer::disconnectCamera() { + pylon->closeCamera(); + statusBar()->showMessage(tr("Camera closed!")); + updateActions(); +} + +void ImageViewer::startRecording() {} + +void ImageViewer::stopRecording() {} + +void ImageViewer::grabStillFromPylon() { + if (pylon->isOpen()) { + ImageSettings settings = pylon->getImageSettings(); + Pylon::CGrabResultPtr image_ptr = pylon->grabFrame(); + if (image_ptr.IsValid() && image_ptr->GrabSucceeded()) { + size_t stride; + image_ptr->GetStride(stride); + QImage img(static_cast(image_ptr->GetBuffer()), static_cast(settings.width), static_cast(settings.height), + QImage::Format::Format_Grayscale8); + setImage(img); + } + } else { + statusBar()->showMessage(tr("Camera is not open! Connect to camera first!")); + } +} diff --git a/imageviewer.h b/imageviewer.h new file mode 100644 index 0000000..1e9728b --- /dev/null +++ b/imageviewer.h @@ -0,0 +1,86 @@ +#ifndef IMAGEVIEWER_H +#define IMAGEVIEWER_H + +#include +#include "pylonrecorder.h" +#include +#if defined(QT_PRINTSUPPORT_LIB) +# include + +# if QT_CONFIG(printer) +# include +# endif +#endif + +QT_BEGIN_NAMESPACE +class QAction; +class QLabel; +class QMenu; +class QScrollArea; +class QScrollBar; +QT_END_NAMESPACE + +//! [0] +class ImageViewer : public QMainWindow +{ + Q_OBJECT + +public: + ImageViewer(QWidget *parent = nullptr); + ~ImageViewer(); + + bool loadFile(const QString &); + +private slots: + void grabStillFromPylon(); + void open(); + void saveAs(); + void print(); + void copy(); + void paste(); + void zoomIn(); + void zoomOut(); + void normalSize(); + void fitToWindow(); + void about(); + void connectCamera(); + void disconnectCamera(); + void startRecording(); + void stopRecording(); + void quitApplication(); + +private: + void createActions(); + void createMenus(); + void createToolBar(); + void updateActions(); + + bool saveFile(const QString &fileName); + void setImage(const QImage &newImage); + void scaleImage(double factor); + void adjustScrollBar(QScrollBar *scrollBar, double factor); + + QImage image; + QLabel *imageLabel; + QScrollArea *scrollArea; + double scaleFactor = 1; + PylonRecorder *pylon; + +#if defined(QT_PRINTSUPPORT_LIB) && QT_CONFIG(printer) + QPrinter printer; +#endif + + QAction *saveAsAct; + QAction *printAct; + QAction *copyAct; + QAction *zoomInAct; + QAction *zoomOutAct; + QAction *normalSizeAct; + QAction *fitToWindowAct; + QAction *grab_still_action; + QAction *grab_continuous_action; + QAction *connect_camera_action; + QAction *disconnect_camera_action; +}; + +#endif diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..7fd1c93 --- /dev/null +++ b/main.cpp @@ -0,0 +1,22 @@ +#include "imageviewer.h" +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QGuiApplication::setApplicationDisplayName(ImageViewer::tr("Image Viewer")); + QCommandLineParser commandLineParser; + commandLineParser.addHelpOption(); + commandLineParser.addPositionalArgument(ImageViewer::tr("[file]"), ImageViewer::tr("Image file to open.")); + commandLineParser.process(QCoreApplication::arguments()); + ImageViewer imageViewer; + if (!commandLineParser.positionalArguments().isEmpty() + && !imageViewer.loadFile(commandLineParser.positionalArguments().front())) { + return -1; + } + imageViewer.show(); + int ret = app.exec(); + return ret; +} diff --git a/pylonrecorder.cpp b/pylonrecorder.cpp new file mode 100644 index 0000000..be2111e --- /dev/null +++ b/pylonrecorder.cpp @@ -0,0 +1,71 @@ +#include "pylonrecorder.h" + +PylonRecorder::PylonRecorder(): + valid(false) { + Pylon::PylonInitialize(); + camera = new Pylon::CInstantCamera(); +} + +PylonRecorder::~PylonRecorder() { + Pylon::PylonTerminate(); +} + +void PylonRecorder::terminate() { + try { + //Pylon::PylonTerminate(); + } catch (const Pylon::GenericException &e) { + std::cerr << e.GetDescription() << std::endl; + } +} + +bool PylonRecorder::isOpen() { + return valid; +} + +ImageSettings PylonRecorder::getImageSettings() { + ImageSettings settings; + if (valid) { + Pylon::CIntegerParameter width( camera->GetNodeMap(), "Width"); + Pylon::CIntegerParameter height( camera->GetNodeMap(), "Height"); + Pylon::CEnumParameter pixelFormat( camera->GetNodeMap(), "PixelFormat"); + settings.width = width.GetValue(); + settings.height = height.GetValue(); + } + return settings; +} + +Pylon::CGrabResultPtr PylonRecorder::grabFrame() { + Pylon::CPylonImage img; + Pylon::CGrabResultPtr ptrGrabResult; + if (valid) { + camera->StartGrabbing(); + camera->RetrieveResult( 5000, ptrGrabResult, Pylon::TimeoutHandling_ThrowException); + camera->StopGrabbing(); + } + return ptrGrabResult; +} + +bool PylonRecorder::openCamera(std::string &message) { + try { + camera->Attach(Pylon::CTlFactory::GetInstance().CreateFirstDevice()); + camera->Open(); + valid = true; + message = "Successfully opened camera!"; + } catch (const Pylon::GenericException &e) { + message = e.GetDescription(); + std::cerr << "An exception occurred." << std::endl << e.GetDescription() << std::endl; + valid = false; + } + return valid; +} + +void PylonRecorder::closeCamera() { + if (camera->IsOpen()) { + try { + camera->Close(); + valid = false; + } catch (const Pylon::GenericException &e) { + std::cerr << "An exception occurred." << std::endl << e.GetDescription() << std::endl; + } + } +} diff --git a/pylonrecorder.h b/pylonrecorder.h new file mode 100644 index 0000000..bb3d2d8 --- /dev/null +++ b/pylonrecorder.h @@ -0,0 +1,33 @@ +#ifndef PYLONRECORDER_H +#define PYLONRECORDER_H + +#include +#include + +struct ImageSettings { + int64_t width = 0; + int64_t height = 0; +}; + +class PylonRecorder +{ +public: + PylonRecorder(); + ~PylonRecorder(); + + ImageSettings getImageSettings(); + bool isOpen(); + void terminate(); + bool openCamera(std::string &message); + void closeCamera(); + Pylon::CGrabResultPtr grabFrame(); + +private: + Pylon::CInstantCamera *camera; + bool valid; + + + +}; + +#endif // PYLONRECORDER_H diff --git a/recorder.pro b/recorder.pro new file mode 100644 index 0000000..c13e909 --- /dev/null +++ b/recorder.pro @@ -0,0 +1,21 @@ +QT += widgets +requires(qtConfig(filedialog)) +qtHaveModule(printsupport): QT += gui printsupport + +HEADERS = imageviewer.h \ + pylonrecorder.h +SOURCES = imageviewer.cpp \ + main.cpp \ + pylonrecorder.cpp + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/widgets/widgets/imageviewer +INSTALLS += target + +unix:!macx: LIBS += -L$$/opt/pylon5/lib64/ -Wl,-E -lpylonbase -lpylonutility -lGenApi_gcc_v3_1_Basler_pylon -lGCBase_gcc_v3_1_Basler_pylon + +INCLUDEPATH += $$/opt/pylon5/include +DEPENDPATH += $$/opt/pylon5/include + +RESOURCES += \ + resources.qrc diff --git a/resources.qrc b/resources.qrc new file mode 100644 index 0000000..12ca9e0 --- /dev/null +++ b/resources.qrc @@ -0,0 +1,6 @@ + + + images/disconnect.png + images/connect.png + +