diff --git a/INSTALL.md b/INSTALL.md index a0d4680de..831d3c3fa 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -15,7 +15,7 @@ | OpenCV | >= 2.4.9 | core, highgui, imgproc | | OpenAL Soft | >= 1.16.0 | | | filter_audio | most recent | | - +| qrencode | >= 3.0.3 | | ##Linux @@ -59,22 +59,22 @@ git clone https://github.com/tux3/qTox.git qTox The following steps assumes that you cloned the repository at "/home/user/qTox". If you decided to choose another location, replace corresponding parts. -###GCC, Qt, OpenCV and OpanAL Soft +###GCC, Qt, OpenCV, OpanAL Soft and QRCode Arch Linux: ```bash -sudo pacman -S --needed base-devel qt5 opencv openal libxss +sudo pacman -S --needed base-devel qt5 opencv openal libxss qrencode ``` Debian / Ubuntu: ```bash -sudo apt-get install build-essential qt5-qmake qt5-default qttools5-dev-tools libqt5opengl5-dev libqt5svg5-dev libopenal-dev libopencv-dev libxss-dev +sudo apt-get install build-essential qt5-qmake qt5-default qttools5-dev-tools libqt5opengl5-dev libqt5svg5-dev libopenal-dev libopencv-dev libxss-dev qrencode ``` Fedora: ```bash yum groupinstall "Development Tools" -yum install qt-devel qt-doc qt-creator qt5-qtsvg opencv-devel openal-soft-devel libXScrnSaver-devel +yum install qt-devel qt-doc qt-creator qt5-qtsvg opencv-devel openal-soft-devel libXScrnSaver-devel qrencode ``` Slackware: @@ -87,6 +87,8 @@ http://slackbuilds.org/repository/14.1/libraries/qt5/ http://slackbuilds.org/repository/14.1/libraries/opencv/ +http://slackbuilds.org/slackbuilds/14.1/graphics/qrencode/ + ###Tox Core diff --git a/qtox.pro b/qtox.pro index d1fe9def5..aec2d9b50 100644 --- a/qtox.pro +++ b/qtox.pro @@ -134,7 +134,7 @@ win32 { LIBS += -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lsodium -lvpx -lpthread LIBS += -L$$PWD/libs/lib -lopencv_core249 -lopencv_highgui249 -lopencv_imgproc249 -lOpenAL32 -lopus LIBS += -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -lws2_32 -liphlpapi -lz - + LIBS += -lqrencode contains(DEFINES, QTOX_FILTER_AUDIO) { contains(STATICPKG, YES) { LIBS += -Wl,-Bstatic -lfilteraudio @@ -149,6 +149,7 @@ win32 { QMAKE_INFO_PLIST = osx/info.plist QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.7 LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lsodium -lvpx -lopus -framework OpenAL -lopencv_core -lopencv_highgui -mmacosx-version-min=10.7 + LIBS += -lqrencode contains(DEFINES, QTOX_PLATFORM_EXT) { LIBS += -framework IOKit -framework CoreFoundation } contains(DEFINES, QTOX_FILTER_AUDIO) { LIBS += -lfilteraudio } } else { @@ -165,8 +166,10 @@ win32 { LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic LIBS += -Wl,-Bstatic -ljpeg -ltiff -lpng -ljasper -lIlmImf -lIlmThread -lIex -ldc1394 -lraw1394 -lHalf -lz -llzma -ljbig LIBS += -Wl,-Bdynamic -lv4l1 -lv4l2 -lavformat -lavcodec -lavutil -lswscale -lusb-1.0 + LIBS += -lqrencode } else { LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lvpx -lsodium -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc + LIBS += -lqrencode } contains(DEFINES, QTOX_PLATFORM_EXT) { @@ -424,7 +427,8 @@ SOURCES += \ src/video/netvideosource.cpp \ src/video/videoframe.cpp \ src/widget/gui.cpp \ - src/toxme.cpp + src/toxme.cpp \ + src/misc/qrwidget.cpp HEADERS += \ src/audio.h \ @@ -445,4 +449,5 @@ HEADERS += \ src/video/videoframe.h \ src/video/videosource.h \ src/widget/gui.h \ - src/toxme.h + src/toxme.h \ + src/misc/qrwidget.h diff --git a/simple_make.sh b/simple_make.sh index 8557f6d81..c845dc26f 100755 --- a/simple_make.sh +++ b/simple_make.sh @@ -2,12 +2,12 @@ if which apt-get; then sudo apt-get install build-essential qt5-qmake qt5-default libopenal-dev libopencv-dev \ - libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev qttools5-dev-tools qtchooser libxss-dev libqt5svg5* + libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev qttools5-dev-tools qtchooser libxss-dev libqt5svg5* qrencode elif which pacman; then - sudo pacman -S --needed base-devel qt5 opencv openal opus libvpx libxss qt5-svg + sudo pacman -S --needed base-devel qt5 opencv openal opus libvpx libxss qt5-svg qrencode elif which yum; then sudo yum groupinstall "Development Tools" - sudo yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel libtool autoconf automake check check-devel libXScrnSaver-devel qt5-qtsvg + sudo yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel libtool autoconf automake check check-devel libXScrnSaver-devel qt5-qtsvg qrencode else echo "Unknown package manager, attempting to compile anyways" fi diff --git a/src/misc/qrwidget.cpp b/src/misc/qrwidget.cpp new file mode 100644 index 000000000..0ee1d0c0d --- /dev/null +++ b/src/misc/qrwidget.cpp @@ -0,0 +1,103 @@ +#include "qrwidget.h" +#include +#include +#include +#include +#include "qrencode.h" + +QRWidget::QRWidget(QWidget *parent) : QWidget(parent), data("0") +//Note: The encoding fails with empty string so I just default to something else. +//Use the setQRData() call to change this. +{ + //size of the qimage might be problematic in the future, but it works for me + size.setWidth(480); + size.setHeight(480); + image = new QImage(size, QImage::Format_RGB32); +} + +void QRWidget::setQRData(QString data) +{ + this->data = "tox:" + data; + paintImage(); +} + +QImage* QRWidget::getImage() +{ + return image; +} + +/** + * @brief QRWidget::saveImage + * @param path Full path to the file with extension. + * @return indicate if saving was successful. + */ +bool QRWidget::saveImage(QString path) +{ + return image->save(path, 0, 75); //0 - image format same as file extension, 75-quality, png file is ~6.3kb +} + +QString QRWidget::getImageAsText() +{ + paintImage(); + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + image->save(&buffer, "PNG"); // writes the image in PNG format inside the buffer + + QString iconBase64 = QString::fromLatin1(ba.toBase64().data()); + QString base64Image = ""; + + return QString(base64Image); +} + +//http://stackoverflow.com/questions/21400254/how-to-draw-a-qr-code-with-qt-in-native-c-c +void QRWidget::paintImage() +{ + QPainter painter(image); + //NOTE: I have hardcoded some parameters here that would make more sense as variables. + // ECLEVEL_M is much faster recognizable by barcodescanner any any other type + QRcode *qr = QRcode_encodeString(data.toStdString().c_str(), 1, QR_ECLEVEL_M, QR_MODE_8, 0); + + if(0 != qr) + { + QColor fg("black"); + QColor bg("white"); + painter.setBrush(bg); + painter.setPen(Qt::NoPen); + painter.drawRect(0, 0, size.width(), size.height()); + painter.setBrush(fg); + const int s = qr->width > 0 ? qr->width : 1; + const double w = width(); + const double h = height(); + const double aspect = w / h; + const double scale = ((aspect > 1.0) ? h : w) / s; + + for(int y = 0; y < s; y++) + { + const int yy = y * s; + for(int x = 0; x < s; x++) + { + const int xx = yy + x; + const unsigned char b = qr->data[xx]; + if(b & 0x01) + { + const double rx1 = x * scale, + ry1 = y * scale; + QRectF r(rx1, ry1, scale, scale); + painter.drawRects(&r, 1); + } + } + } + QRcode_free(qr); + painter.save(); + } + else + { + QColor error("red"); + painter.setBrush(error); + painter.drawRect(0, 0, width(), height()); + qDebug() << "QR FAIL: " << strerror(errno); + } + + qr = 0; +} diff --git a/src/misc/qrwidget.h b/src/misc/qrwidget.h new file mode 100644 index 000000000..75674da6c --- /dev/null +++ b/src/misc/qrwidget.h @@ -0,0 +1,26 @@ +#ifndef QRWIDGET_H +#define QRWIDGET_H + +// https://stackoverflow.com/questions/21400254/how-to-draw-a-qr-code-with-qt-in-native-c-c + +#include + +class QRWidget : public QWidget +{ + Q_OBJECT + +public: + explicit QRWidget(QWidget *parent = 0); + void setQRData(QString data); + QString getImageAsText(); + QImage* getImage(); + bool saveImage(QString path); + +private: + QString data; + void paintImage(); + QImage *image; + QSize size; +}; + +#endif // QRWIDGET_H diff --git a/src/widget/form/profileform.cpp b/src/widget/form/profileform.cpp index 41d3cccd8..4cb407eb5 100644 --- a/src/widget/form/profileform.cpp +++ b/src/widget/form/profileform.cpp @@ -36,7 +36,6 @@ #include #include - void ProfileForm::refreshProfiles() { bodyUI->profiles->clear(); @@ -76,9 +75,11 @@ ProfileForm::ProfileForm(QWidget *parent) : toxId->setReadOnly(true); toxId->setFrame(false); toxId->setFont(Style::getFont(Style::Small)); + + QVBoxLayout *toxIdGroup = qobject_cast(bodyUI->toxGroup->layout()); + toxIdGroup->replaceWidget(bodyUI->toxId, toxId); + bodyUI->toxId->hide(); - bodyUI->toxGroup->layout()->addWidget(toxId); - profilePicture = new MaskablePixmapWidget(this, QSize(64, 64), ":/img/avatar_mask.png"); profilePicture->setPixmap(QPixmap(":/img/contact_dark.png")); profilePicture->setClickable(true); @@ -169,6 +170,11 @@ void ProfileForm::setToxId(const QString& id) { toxId->setText(id); toxId->setCursorPosition(0); + + qr = new QRWidget(); + qr->setQRData(id); + bodyUI->qrCode->setPixmap(QPixmap::fromImage(qr->getImage()->scaledToWidth(150))); + bodyUI->qrCode->setToolTip(qr->getImageAsText()); } void ProfileForm::onAvatarClicked() @@ -369,3 +375,32 @@ void ProfileForm::showEvent(QShowEvent *event) refreshProfiles(); QWidget::showEvent(event); } + +void ProfileForm::on_copyQr_clicked() +{ + QApplication::clipboard()->setImage(*qr->getImage()); +} + +void ProfileForm::on_saveQr_clicked() +{ + QString current = bodyUI->profiles->currentText() + ".png"; + QString path = QFileDialog::getSaveFileName(0, tr("Save", "save qr image"), + QDir::home().filePath(current), + tr("Save QrCode (*.png)", "save dialog filter")); + if (!path.isEmpty()) + { + bool success; + if (QFile::exists(path)) + { + success = QFile::remove(path); + if (!success) + { + QMessageBox::warning(this, tr("Failed to remove file"), tr("The file you chose to overwrite could not be removed first.")); + return; + } + } + success = qr->saveImage(path); + if (!success) + QMessageBox::warning(this, tr("Failed to copy file"), tr("The file you chose could not be written to.")); + } +} diff --git a/src/widget/form/profileform.h b/src/widget/form/profileform.h index 830e486db..82cd829ef 100644 --- a/src/widget/form/profileform.h +++ b/src/widget/form/profileform.h @@ -22,6 +22,7 @@ #include #include #include "src/core.h" +#include "src/misc/qrwidget.h" class CroppingLabel; class Core; @@ -36,7 +37,7 @@ class ClickableTE : public QLineEdit { Q_OBJECT public: - + signals: void clicked(); protected: @@ -73,7 +74,10 @@ private slots: void onNewClicked(); void disableSwitching(); void enableSwitching(); - + void on_copyQr_clicked(); + + void on_saveQr_clicked(); + protected: virtual void showEvent(QShowEvent *); @@ -85,6 +89,7 @@ private: Core* core; QTimer timer; bool hasCheck = false; + QRWidget *qr; ClickableTE* toxId; }; diff --git a/src/widget/form/profileform.ui b/src/widget/form/profileform.ui index 58633511a..0b02d718a 100644 --- a/src/widget/form/profileform.ui +++ b/src/widget/form/profileform.ui @@ -6,8 +6,8 @@ 0 0 - 439 - 472 + 442 + 659 @@ -39,8 +39,8 @@ 0 0 - 419 - 452 + 422 + 639 @@ -97,6 +97,52 @@ Share it with your friends to communicate. + + + + + + + + + + QRCODE + + + Qt::AlignCenter + + + + + + + + + + Share this code, so friends can add you + + + + + + + Save image + + + + + + + Copy image + + + + + + + + + @@ -212,7 +258,7 @@ Profile does not contain your history. CroppingLabel QLabel -
src/widget/croppinglabel.h
+
src/widget/croppinglabel.h