IP Watcher

What is it good for? I have a homepage and dynamic IP address that changes after an arbitrary timespan. When the change happens I can’t reach my server. To get it work again I have to adapt the IP location manually. I know there exists e.g. dyndns… or upgrade to a static IP but I want to save money and have no problems in doing it.

Here is a script that compares the current IP address with a stored one and sends the new by email to a user defined address:

#!/bin/bash
MYIP="/home/user/myip.txt"
GETIP4ADDR="wget -qO- http://ipecho.net/plain | xargs echo"
GETIP6ADDR="wget -qO - icanhazip.com"
IPADDR=$($GETIP4ADDR)
EMAIL="sendmail -f noreply@sender.com receiver@email.com"

if [ -f "$MYIP" ]; then  
  LASTIP="$(cat $MYIP)"
  if [ "$IPADDR" != "$LASTIP" ]; then
    # IP changed
    echo "$IPADDR" > $MYIP
    echo "subject: IP Update $IPADDR" | $($EMAIL)
  fi
else
  # file missing, create file
  echo "$IPADDR" > $MYIP
  echo "subject: IP Update $IPADDR" | $($EMAIL)
fi

Hints

  1. sendmail
    sudo apt install sendmail
    sudo sendmailconf
  2. Adapt
    MYIP file location
    sender email address
    receiver email address
  3. Add script to your cron jobs, e.g. twice a day

Yahoo Mail Forwarding Fix

At the beginning of 2021 Yahoo is tighten up their email settings and therefore only paying customers are allowed to redirect their emails to another account. To bypass this you can do the following steps:

  1. Open your favorite web-browser and login to your Yahoo account
  2. Click on your name and open “Account Information”
  3. Click on “Account Security”
  4. Click on “Manage App Security”
  5. Select e.g. “Outlook Desktop” and a new password is presented. Let this site open or copy/paste it in a text editor. You will need it in a later step.

After you have a new app password, open your favorite web-browser and login to your e.g. Outlook account

  1. Click on “Settings”
  2. Click on “Show All Settings”
  3. Click on “Email”
  4. Click on “Email sychronization”
  5. “Add additional email accounts”
  6. Enter your Yahoo user (full email) and use the app password which was generated in the previous step
    pop.mail.yahoo.com, port 995, SSL
    imap.mail.yahoo.com, port 993, SSL
    smtp.mail.yahoo.com, port 587, SSL, use authentification
  7. Test by sending an email to your Yahoo account
  8. Congratulation you did bypass the new Yahoo restrictions 😉

GitHub to GitLab Fork

Here are the commands to fork a GitHub repository and push it with all the commit comments to a GitLab repository.

>> git clone GitHub-repository.git
>> cd GitHub-repository
>> git remote rename origin old-origin
>> git remote add origin git@your-gitlab.com:your/repo.git
>> git push -u origin --all
>> git push -u origin --tags

That’s all!

Lidl Fotos Album Storage Directory

This time I faced another problem: I had a voucher for a Lidl foto album, created one and wanted to store it on my NAS file server for backup reasons. I searched for the Lidl workspace directory on my harddisk and couldn’t find anything. Also a Google Search and reading the Lidl help site brought no solution. So I had the idea of downloading “Sandboxie” – sandboxed the Lidl app and tracked the folders that were loaded. Here is the result:

C:\Users\your_user_name\AppData\Roaming\PhotoGenie X

Display QImage in QML

Currently I am developing an application that captures images and should display them via a Qt’s QML frontend. I thought that would be an easy task because I am familiar with QML and collected my own experiences in the last months.

So I got the QImages continously from my capturing thread. I updated my QImage member variable and wanted to display it via a QML image structure via the source tag and recognised this does not work….After a research session an trying out different ways to solve this problem I want to share my knowledge with all of you out there that do have the same problem. The order of the solutions is also the ranking, beginning from the best one (for me):

1. QPainter
Here I use a user defined QML item called ImageItem and paint the content of the image by a QPainter. For me it worked best but you have to take care by yourself about the image output.

// ImageItem.h
#ifndef IMAGEITEM_H
#define IMAGEITEM_H
#include <QQuickPaintedItem>
#include <QQuickItem>
#include <QPainter>
#include <QImage>

class ImageItem : public QQuickPaintedItem
{
Q_OBJECT
    Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged)
public:
    ImageItem(QQuickItem *parent = nullptr);
    Q_INVOKABLE void setImage(const QImage &image);
    void paint(QPainter *painter);
    QImage image() const;
signals:
    void imageChanged();
private:
    QImage current_image;
};
#endif // IMAGEITEM_H
// ImageItem.cpp
#include "ImageItem.h"

ImageItem::ImageItem(QQuickItem *parent) : QQuickPaintedItem(parent)
{    
this->current_image = QImage(":/images/no_image.png");
}

void ImageItem::paint(QPainter *painter)
{
    QRectF bounding_rect = boundingRect();
    QImage scaled = this->current_image.scaledToHeight(bounding_rect.height());
    QPointF center = bounding_rect.center() - scaled.rect().center();

    if(center.x() < 0)
        center.setX(0);
    if(center.y() < 0)
        center.setY(0);
   painter->drawImage(center, scaled);
}

QImage ImageItem::image() const
{    return this->current_image;
}

void ImageItem::setImage(const QImage &image)
{
    this->current_image = image;
    update();
}

Don’t forget to register the user defined item:

main.cpp
...
qmlRegisterType<ImageItem>("myextension", 1, 0, "ImageItem");
...

Then use it in your QML:

// main.qml
import myextension 1.0
...
ImageItem {
  id: liveImageItem
  height: parent.height
  width: parent.width
}
...

2. QImageProvider
Here you create your own QQuickImageProvider that is used to display the image. This should be the preferred solution but caused flickering output errors on animations of my user interface. So it got the good 2nd place.

// LiveImageProvider.h
#ifndef LIVEIMAGEPROVIDER_H
#define LIVEIMAGEPROVIDER_H

#include <QImage>
#include <QQuickImageProvider>

class LiveImageProvider : public QObject, public QQuickImageProvider
{
    Q_OBJECT
public:
    LiveImageProvider();

    QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;

public slots:
    void updateImage(const QImage &image);

signals:
    void imageChanged();

private:
    QImage image;
    QImage no_image;
};

#endif // LIVEIMAGEPROVIDER_H
// LiveImageProvider.cpp
#include "LiveImageProvider.h"
#include <QDebug>
/**
* @brief Image provider that is used to handle the live image stream in the QML viewer.
 */
LiveImageProvider::LiveImageProvider() : QQuickImageProvider(QQuickImageProvider::Image)
{
    this->no_image = QImage(":/images/no_image.png");
    this->blockSignals(false);
}

/**
 * @brief Delivers image. The id is not used.
 * @param id The id is the requested image source, with the "image:" scheme and provider identifier removed.
 * For example, if the image source was "image://myprovider/icons/home", the given id would be "icons/home".
 * @param size In all cases, size must be set to the original size of the image. This is used to set the
 * width and height of the relevant Image if these values have not been set explicitly.
 * @param requestedSize The requestedSize corresponds to the Image::sourceSize requested by an Image item.
 * If requestedSize is a valid size, the image returned should be of that size.
 * @return
 */
QImage LiveImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
    QImage result = this->image;

    if(result.isNull()) {
        result = this->no_image;
    }

    if(size) {
        *size = result.size();
    }

    if(requestedSize.width() > 0 && requestedSize.height() > 0) {
        result = result.scaled(requestedSize.width(), requestedSize.height(), Qt::KeepAspectRatio);
    }

    return result;
}

/**
 * @brief Update of the current image.
 * @param image The new image.
 */
void LiveImageProvider::updateImage(const QImage &image)
{
    if(this->image != image) {
        this->image = image;
        emit imageChanged();
    }
}

Keep attention to the reload() function because to update the scene graph that finally displays the content you need to change the (dummy) source.

// main.qml
...
Image {
  id: liveImage
  property bool counter: false

  asynchronous: true
  source: "image://live/image"
  anchors.fill: parent
  fillMode: Image.PreserveAspectFit
  cache: false


  function reload() {
    counter = !counter
    source = "image://live/image?id=" + counter
  }
  }
...

Dont’t forget to register the new image provider.

// main.cpp
...
QScopedPointer<LiveImageProvider> liveImageProvider(new LiveImageProvider());
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("liveImageProvider", liveImageProvider.data());
engine.addImageProvider("live", liveImageProvider.data());
engine.load(QUrl(QLatin1String("qrc:/main.qml")));
...

3. QString
This solution creates a string out of the QImage. This string is set to the source property of the QML image tag. Why this is the worst solution: a lot of conversions and copies, need prior knowledge about the image type (jpg/png/…).

QString ImageViewer::imageData() const
 {
 QByteArray bArray;
 QBuffer buffer(&bArray);
 buffer.open(QIODevice::WriteOnly);
 this->image.save(&buffer, "JPEG");

QString image("data:image/jpg;base64,");
 image.append(QString::fromLatin1(bArray.toBase64().data()));

return image;
 }