diff options
Diffstat (limited to 'qt')
-rw-r--r-- | qt/Makefile.am.inc | 19 | ||||
-rw-r--r-- | qt/QZBar.cpp | 364 | ||||
-rw-r--r-- | qt/QZBarThread.cpp | 358 | ||||
-rw-r--r-- | qt/QZBarThread.h | 192 |
4 files changed, 933 insertions, 0 deletions
diff --git a/qt/Makefile.am.inc b/qt/Makefile.am.inc new file mode 100644 index 0000000..fba4fdf --- /dev/null +++ b/qt/Makefile.am.inc @@ -0,0 +1,19 @@ +lib_LTLIBRARIES += qt/libzbarqt.la +qt_libzbarqt_la_CPPFLAGS = -Iqt $(QT_CFLAGS) $(AM_CPPFLAGS) +qt_libzbarqt_la_LDFLAGS = -version-info $(ZQT_LIB_VERSION) $(AM_LDFLAGS) $(LIBQT_EXTRA_LDFLAGS) +qt_libzbarqt_la_LIBADD = $(QT_LIBS) zbar/libzbar.la $(AM_LIBADD) + +qt_libzbarqt_la_SOURCES = qt/QZBar.cpp qt/QZBarThread.h qt/QZBarThread.cpp +nodist_qt_libzbarqt_la_SOURCES = qt/moc_QZBar.cpp qt/moc_QZBarThread.cpp +BUILT_SOURCES += $(nodist_qt_libzbarqt_la_SOURCES) +DISTCLEANFILES += $(nodist_qt_libzbarqt_la_SOURCES) + +clean-local: + -rm -vf qt/moc_*.cpp + +qt/moc_%.cpp: qt/%.h + $(MOC) $(qt_libzbarqt_la_CPPFLAGS) $< -o $@ + +qt/moc_%.cpp: include/zbar/%.h + $(mkdir_p) qt + $(MOC) $(qt_libzbarqt_la_CPPFLAGS) $< -o $@ diff --git a/qt/QZBar.cpp b/qt/QZBar.cpp new file mode 100644 index 0000000..14bdc56 --- /dev/null +++ b/qt/QZBar.cpp @@ -0,0 +1,364 @@ +//------------------------------------------------------------------------ +// Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#include <QX11Info> +#include <qevent.h> +#include <qurl.h> +#include <zbar/QZBar.h> +#include "QZBarThread.h" + +using namespace zbar; + +QZBar::QZBar(QWidget *parent, int verbosity) + : QWidget(parent), thread(NULL), _videoDevice(), _videoEnabled(false), + _attached(false) +{ + setAttribute(Qt::WA_OpaquePaintEvent); + setAttribute(Qt::WA_PaintOnScreen); +#if QT_VERSION >= 0x040400 + setAttribute(Qt::WA_NativeWindow); + setAttribute(Qt::WA_DontCreateNativeAncestors); +#endif + + QSizePolicy sizing(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizing.setHeightForWidth(true); + setSizePolicy(sizing); + + thread = new QZBarThread(verbosity); + if (testAttribute(Qt::WA_WState_Created)) { +#if QT_VERSION >= 0x050000 + thread->window.attach(QX11Info::display(), winId()); +#else + thread->window.attach(x11Info().display(), winId()); +#endif + _attached = 1; + } + connect(thread, SIGNAL(videoOpened(bool)), this, SIGNAL(videoOpened(bool))); + connect(this, SIGNAL(videoOpened(bool)), this, SLOT(sizeChange())); + connect(thread, SIGNAL(update()), this, SLOT(update())); + connect(thread, SIGNAL(decoded(int, const QString &)), this, + SIGNAL(decoded(int, const QString &))); + connect(thread, SIGNAL(decodedText(const QString &)), this, + SIGNAL(decodedText(const QString &))); + thread->start(); +} + +QZBar::~QZBar() +{ + if (thread) { + thread->pushEvent(new QEvent((QEvent::Type)QZBarThread::Exit)); + thread->wait(); + delete thread; + thread = NULL; + } +} + +QPaintEngine *QZBar::paintEngine() const +{ + return (NULL); +} + +QString QZBar::videoDevice() const +{ + return (_videoDevice); +} + +void QZBar::setVideoDevice(const QString &videoDevice) +{ + if (!thread) + return; + if (_videoDevice != videoDevice) { + _videoDevice = videoDevice; + _videoEnabled = _attached && !videoDevice.isEmpty(); + if (_attached) + thread->pushEvent(new QZBarThread::VideoDeviceEvent(videoDevice)); + } +} + +int QZBar::get_controls(int index, char **name, char **group, + enum ControlType *type, int *min, int *max, int *def, + int *step) +{ + if (!thread) + return 0; + + return thread->get_controls(index, name, group, type, min, max, def, step); +} + +void QZBar::request_size(unsigned width, unsigned height, bool trigger) +{ + if (!thread) + return; + + thread->request_size(width, height); + if (trigger) + thread->pushEvent(new QEvent((QEvent::Type)QZBarThread::ReOpen)); +} + +int QZBar::get_resolution(int index, unsigned &width, unsigned &height, + float &max_fps) +{ + if (!thread) + return 0; + + return thread->get_resolution(index, width, height, max_fps); +} + +unsigned QZBar::videoWidth() +{ + if (!thread) + return 0; + + return thread->reqWidth; +} + +unsigned QZBar::videoHeight() +{ + if (!thread) + return 0; + + return thread->reqHeight; +} + +QVector<QPair<int, QString> > QZBar::get_menu(int index) +{ + if (!thread) { + QVector<QPair<int, QString> > empty; + return empty; + } + + return thread->get_menu(index); +} + +int QZBar::set_control(char *name, bool value) +{ + if (!thread) + return 0; + + return thread->set_control(name, value); +} + +int QZBar::set_control(char *name, int value) +{ + if (!thread) + return 0; + + return thread->set_control(name, value); +} + +int QZBar::get_control(char *name, bool *value) +{ + if (!thread) + return 0; + + return thread->get_control(name, value); +} + +int QZBar::get_control(char *name, int *value) +{ + if (!thread) + return 0; + + return thread->get_control(name, value); +} + +int QZBar::set_config(std::string cfgstr) +{ + if (!thread) + return 0; + + return thread->set_config(cfgstr); +} + +int QZBar::set_config(zbar_symbol_type_t symbology, zbar_config_t config, + int value) +{ + if (!thread) + return 0; + + return thread->set_config(symbology, config, value); +} + +int QZBar::get_config(zbar_symbol_type_t symbology, zbar_config_t config, + int &value) +{ + if (!thread) + return 0; + + return thread->get_config(symbology, config, value); +} + +int QZBar::request_dbus(bool enabled) +{ + if (!thread) + return 0; + + return thread->request_dbus(enabled); +} + +bool QZBar::isVideoEnabled() const +{ + return (_videoEnabled); +} + +void QZBar::setVideoEnabled(bool videoEnabled) +{ + if (!thread) + return; + if (_videoEnabled != videoEnabled) { + _videoEnabled = videoEnabled; + thread->pushEvent(new QZBarThread::VideoEnabledEvent(videoEnabled)); + } +} + +bool QZBar::isVideoOpened() const +{ + if (!thread) + return (false); + QMutexLocker locker(&thread->mutex); + return (thread->_videoOpened); +} + +void QZBar::scanImage(const QImage &image) +{ + if (!thread) + return; + thread->pushEvent(new QZBarThread::ScanImageEvent(image)); +} + +void QZBar::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasImage() || event->mimeData()->hasUrls()) + event->acceptProposedAction(); +} + +void QZBar::dropEvent(QDropEvent *event) +{ + if (event->mimeData()->hasImage()) { + QImage image = qvariant_cast<QImage>(event->mimeData()->imageData()); + scanImage(image); + event->setDropAction(Qt::CopyAction); + event->accept(); + } else { + // FIXME TBD load URIs and queue for processing +#if 0 + std::cerr << "drop: " + << event->mimeData()->formats().join(", ").toStdString() + << std::endl; + QList<QUrl> urls = event->mimeData()->urls(); + for(int i = 0; i < urls.size(); ++i) + std::cerr << "[" << i << "] " + << urls.at(i).toString().toStdString() + << std::endl; +#endif + } +} + +QSize QZBar::sizeHint() const +{ + if (!thread) + return (QSize(640, 480)); + QMutexLocker locker(&thread->mutex); + return (QSize(thread->reqWidth, thread->reqHeight)); +} + +int QZBar::heightForWidth(int width) const +{ + if (thread) { + QMutexLocker locker(&thread->mutex); + int base_width = thread->reqWidth; + int base_height = thread->reqHeight; + if (base_width > 0 && base_height > 0) + return (base_height * width / base_width); + } + return (width * 3 / 4); +} + +void QZBar::paintEvent(QPaintEvent *event) +{ + try { + if (thread) + thread->window.redraw(); + } catch (Exception &) { + // sometimes Qt attempts to paint the widget before it's parented(?) + // just ignore this (can't throw from event anyway) + } +} + +void QZBar::resizeEvent(QResizeEvent *event) +{ + QSize size = event->size(); + try { + if (thread) + thread->window.resize(size.rwidth(), size.rheight()); + } catch (Exception &) { /* ignore */ + } +} + +void QZBar::changeEvent(QEvent *event) +{ + try { + QMutexLocker locker(&thread->mutex); + if (event->type() == QEvent::ParentChange) +#if QT_VERSION >= 0x050000 + thread->window.attach(QX11Info::display(), winId()); +#else + thread->window.attach(x11Info().display(), winId()); +#endif + + } catch (Exception &) { /* ignore (FIXME do something w/error) */ + } +} + +void QZBar::attach() +{ + if (_attached) + return; + + try { +#if QT_VERSION >= 0x050000 + thread->window.attach(QX11Info::display(), winId()); +#else + thread->window.attach(x11Info().display(), winId()); +#endif + thread->window.resize(width(), height()); + _attached = 1; + + _videoEnabled = !_videoDevice.isEmpty(); + if (_videoEnabled) + thread->pushEvent(new QZBarThread::VideoDeviceEvent(_videoDevice)); + } catch (Exception &) { /* ignore (FIXME do something w/error) */ + } +} + +void QZBar::showEvent(QShowEvent *event) +{ + if (thread && !_attached) + attach(); +} + +void QZBar::sizeChange() +{ + update(); + updateGeometry(); +} diff --git a/qt/QZBarThread.cpp b/qt/QZBarThread.cpp new file mode 100644 index 0000000..7675fa3 --- /dev/null +++ b/qt/QZBarThread.cpp @@ -0,0 +1,358 @@ +//------------------------------------------------------------------------ +// Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ + +#include "QZBarThread.h" +#include <iostream> + +using namespace zbar; + +static const QString textFormat("%1:%2"); + +QZBarThread::QZBarThread(int verbosity) + : _videoOpened(false), reqWidth(DEFAULT_WIDTH), reqHeight(DEFAULT_HEIGHT), + video(NULL), image(NULL), running(true), videoRunning(false), + videoEnabled(false) +{ + zbar_set_verbosity(verbosity); + scanner.set_handler(*this); +} + +void QZBarThread::image_callback(Image &image) +{ + for (Image::SymbolIterator sym = image.symbol_begin(); + sym != image.symbol_end(); ++sym) + if (!sym->get_count()) { + QString data = QString::fromStdString(sym->get_data()); + emit decoded(sym->get_type(), data); + + emit decodedText(textFormat.arg( + QString::fromStdString(sym->get_type_name()), data)); + } +} + +void QZBarThread::processImage(Image &image) +{ + { + scanner.recycle_image(image); + Image tmp = image.convert(*(long *)"Y800"); + scanner.scan(tmp); + image.set_symbols(tmp.get_symbols()); + } + window.draw(image); + if (this->image && this->image != &image) { + delete this->image; + this->image = NULL; + } + emit update(); +} + +void QZBarThread::enableVideo(bool enable) +{ + if (!video) { + videoRunning = videoEnabled = false; + return; + } + try { + scanner.enable_cache(enable); + video->enable(enable); + videoRunning = enable; + } catch (std::exception &e) { + std::cerr << "ERROR: " << e.what() << std::endl; + } + if (!enable) { + // release video image and revert to logo + clear(); + emit update(); + } +} + +void QZBarThread::openVideo(const QString &device) +{ + if (videoRunning) + enableVideo(false); + + { + QMutexLocker locker(&mutex); + videoEnabled = _videoOpened = false; + } + + // ensure old video doesn't have image ref + // (FIXME handle video destroyed w/images outstanding) + clear(); + emit update(); + + if (video) { + delete video; + video = NULL; + emit videoOpened(false); + } + + if (device.isEmpty()) + return; + + try { + std::string devstr = device.toStdString(); + video = new Video(devstr); + + if (reqWidth != DEFAULT_WIDTH || reqHeight != DEFAULT_HEIGHT) + video->request_size(reqWidth, reqHeight); + + negotiate_format(*video, window); + { + QMutexLocker locker(&mutex); + videoEnabled = _videoOpened = true; + reqWidth = video->get_width(); + reqHeight = video->get_height(); + } + currentDevice = device; + + emit videoOpened(true); + } catch (std::exception &e) { + std::cerr << "ERROR: " << e.what() << std::endl; + emit videoOpened(false); + } +} + +void QZBarThread::videoDeviceEvent(VideoDeviceEvent *e) +{ + openVideo(e->device); +} + +void QZBarThread::videoEnabledEvent(VideoEnabledEvent *e) +{ + if (videoRunning && !e->enabled) + enableVideo(false); + videoEnabled = e->enabled; +} + +void QZBarThread::scanImageEvent(ScanImageEvent *e) +{ + if (videoRunning) + enableVideo(false); + + try { + image = new QZBarImage(e->image); + processImage(*image); + } catch (std::exception &e) { + std::cerr << "ERROR: " << e.what() << std::endl; + clear(); + } +} + +bool QZBarThread::event(QEvent *e) +{ + switch ((EventType)e->type()) { + case VideoDevice: + videoDeviceEvent((VideoDeviceEvent *)e); + break; + case VideoEnabled: + videoEnabledEvent((VideoEnabledEvent *)e); + break; + case ScanImage: + scanImageEvent((ScanImageEvent *)e); + break; + case Exit: + if (videoRunning) + enableVideo(false); + running = false; + break; + case ReOpen: + openVideo(currentDevice); + break; + default: + return (false); + } + return (true); +} + +void QZBarThread::run() +{ + QEvent *e = NULL; + while (running) { + if (!videoEnabled) { + QMutexLocker locker(&mutex); + while (queue.isEmpty()) + newEvent.wait(&mutex); + e = queue.takeFirst(); + } else { + // release reference to any previous QImage + clear(); + enableVideo(true); + + while (videoRunning && !e) { + try { + Image image = video->next_image(); + processImage(image); + } catch (std::exception &e) { + std::cerr << "ERROR: " << e.what() << std::endl; + enableVideo(false); + openVideo(""); + } + QMutexLocker locker(&mutex); + if (!queue.isEmpty()) + e = queue.takeFirst(); + } + + if (videoRunning) + enableVideo(false); + } + if (e) { + event(e); + delete e; + e = NULL; + } + } + clear(); + openVideo(""); +} + +QVector<QPair<int, QString> > QZBarThread::get_menu(int index) +{ + QVector<QPair<int, QString> > vector; + struct video_controls_s *ctrl; + + if (!video) + return vector; + + ctrl = video->get_controls(index); + if (!ctrl) + return vector; + + for (unsigned int i = 0; i < ctrl->menu_size; i++) + vector.append(qMakePair((int)ctrl->menu[i].value, + QString::fromUtf8(ctrl->menu[i].name))); + + return vector; +} + +int QZBarThread::get_controls(int index, char **name, char **group, + enum QZBar::ControlType *type, int *min, int *max, + int *def, int *step) +{ + struct video_controls_s *ctrl; + + if (!video) + return 0; + + ctrl = video->get_controls(index); + if (!ctrl) + return 0; + + if (name) + *name = ctrl->name; + if (group) + *group = ctrl->group; + if (min) + *min = ctrl->min; + if (max) + *max = ctrl->max; + if (def) + *def = ctrl->def; + if (step) + *step = ctrl->step; + + if (type) { + switch (ctrl->type) { + case VIDEO_CNTL_INTEGER: + *type = QZBar::Integer; + break; + case VIDEO_CNTL_MENU: + *type = QZBar::Menu; + break; + case VIDEO_CNTL_BUTTON: + *type = QZBar::Button; + break; + case VIDEO_CNTL_INTEGER64: + *type = QZBar::Integer64; + break; + case VIDEO_CNTL_STRING: + *type = QZBar::String; + break; + case VIDEO_CNTL_BOOLEAN: + *type = QZBar::Boolean; + break; + default: + *type = QZBar::Unknown; + break; + } + } + + return 1; +} + +int QZBarThread::set_control(char *name, bool value) +{ + if (!video) + return 0; + + return video->set_control(name, value); +} + +int QZBarThread::set_control(char *name, int value) +{ + if (!video) + return 0; + + return video->set_control(name, value); +} + +int QZBarThread::get_control(char *name, bool *value) +{ + if (!video) + return 0; + + return video->get_control(name, value); +} + +int QZBarThread::get_control(char *name, int *value) +{ + if (!video) + return 0; + + return video->get_control(name, value); +} + +void QZBarThread::request_size(unsigned width, unsigned height) +{ + reqWidth = width; + reqHeight = height; +} + +int QZBarThread::get_resolution(int index, unsigned &width, unsigned &height, + float &max_fps) +{ + struct video_resolution_s *res; + + if (!video) + return 0; + + res = video->get_resolution(index); + if (!res) + return 0; + + width = res->width; + height = res->height; + max_fps = res->max_fps; + + return 1; +} diff --git a/qt/QZBarThread.h b/qt/QZBarThread.h new file mode 100644 index 0000000..a54ecc8 --- /dev/null +++ b/qt/QZBarThread.h @@ -0,0 +1,192 @@ +//------------------------------------------------------------------------ +// Copyright 2008-2009 (c) Jeff Brown <spadix@users.sourceforge.net> +// +// This file is part of the ZBar Bar Code Reader. +// +// The ZBar Bar Code Reader is free software; you can redistribute it +// and/or modify it under the terms of the GNU Lesser Public License as +// published by the Free Software Foundation; either version 2.1 of +// the License, or (at your option) any later version. +// +// The ZBar Bar Code Reader is distributed in the hope that it will be +// useful, but WITHOUT ANY WARRANTY; without even the implied warranty +// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser Public License for more details. +// +// You should have received a copy of the GNU Lesser Public License +// along with the ZBar Bar Code Reader; if not, write to the Free +// Software Foundation, Inc., 51 Franklin St, Fifth Floor, +// Boston, MA 02110-1301 USA +// +// http://sourceforge.net/projects/zbar +//------------------------------------------------------------------------ +#ifndef _QZBARTHREAD_H_ +#define _QZBARTHREAD_H_ + +#include <QEvent> +#include <QMutex> +#include <QThread> +#include <QWaitCondition> +#include <zbar.h> +#include <zbar/QZBar.h> +#include <zbar/QZBarImage.h> + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + +namespace zbar +{ +class QZBarThread : public QThread, public Image::Handler +{ + Q_OBJECT + +public: + enum EventType + { + VideoDevice = QEvent::User, + VideoEnabled, + ScanImage, + ReOpen, + Exit = QEvent::MaxUser + }; + + class VideoDeviceEvent : public QEvent + { + public: + VideoDeviceEvent(const QString &device) + : QEvent((QEvent::Type)VideoDevice), device(device) + { + } + const QString device; + }; + + class VideoEnabledEvent : public QEvent + { + public: + VideoEnabledEvent(bool enabled) + : QEvent((QEvent::Type)VideoEnabled), enabled(enabled) + { + } + bool enabled; + }; + + class ScanImageEvent : public QEvent + { + public: + ScanImageEvent(const QImage &image) + : QEvent((QEvent::Type)ScanImage), image(image) + { + } + const QImage image; + }; + + QMutex mutex; + QWaitCondition newEvent; + + // message queue for events passed from main gui thread to processor. + // (NB could(/should?) be QAbstractEventDispatcher except it doesn't + // work as documented!? ): + // protected by mutex + QList<QEvent *> queue; + + // shared state: + // written by processor thread just after opening video or + // scanning an image, read by main gui thread during size_request. + // protected by mutex + + bool _videoOpened; + unsigned reqWidth, reqHeight; + + // window is also shared: owned by main gui thread. + // processor thread only calls draw(), clear() and negotiate_format(). + // protected by its own internal lock + + Window window; + + QZBarThread(int verbosity = 0); + + int get_controls(int index, char **name = NULL, char **group = NULL, + enum QZBar::ControlType *type = NULL, int *min = NULL, + int *max = NULL, int *def = NULL, int *step = NULL); + QVector<QPair<int, QString> > get_menu(int index); + int set_control(char *name, bool value); + int set_control(char *name, int value); + int get_control(char *name, bool *value); + int get_control(char *name, int *value); + + int set_config(std::string cfgstr) + { + return scanner.set_config(cfgstr); + } + + int set_config(zbar_symbol_type_t symbology, zbar_config_t config, + int value) + { + return scanner.set_config(symbology, config, value); + } + + int get_config(zbar_symbol_type_t symbology, zbar_config_t config, + int &value) + { + return scanner.get_config(symbology, config, value); + } + + void request_size(unsigned width, unsigned height); + + int get_resolution(int index, unsigned &width, unsigned &height, + float &max_fps); + + int request_dbus(bool enabled) + { + return scanner.request_dbus(enabled); + } + + void pushEvent(QEvent *e) + { + QMutexLocker locker(&mutex); + queue.append(e); + newEvent.wakeOne(); + } + +Q_SIGNALS: + void videoOpened(bool opened); + void update(); + void decoded(int type, const QString &data); + void decodedText(const QString &data); + +protected: + void run(); + + void openVideo(const QString &device); + void enableVideo(bool enable); + void processImage(Image &image); + + void clear() + { + window.clear(); + if (image) { + delete image; + image = NULL; + } + } + + virtual void image_callback(Image &image); + + virtual bool event(QEvent *e); + virtual void videoDeviceEvent(VideoDeviceEvent *event); + virtual void videoEnabledEvent(VideoEnabledEvent *event); + virtual void scanImageEvent(ScanImageEvent *event); + +private: + Video *video; + ImageScanner scanner; + QZBarImage *image; + QString currentDevice; + bool running; + bool videoRunning; + bool videoEnabled; +}; + +}; // namespace zbar + +#endif |