3

I need to create a login dialog like this

enter image description here enter image description here

image files: eyeOn.png, eyeOff.png

Requirements:

  • password is shown only when we CLICK AND HOLD the eyeOn icon (even when we click and hold and drag the mouse to area outside of the dialog, the password is still shown), when we release the mouse, password is covered again.
  • while password is shown, the eye icon is eyeOn. While password is covered, the eye icon is eyeOff.

I have just built the layout.

QGridLayout *mainlogin = new QGridLayout();

QLabel *usernameLabel = new QLabel;
usernameLabel->setWordWrap(true);
usernameLabel->setText("Username");
mainlogin->addWidget(usernameLabel, 0, 0);

QComboBox *usernameLineEdit = new QComboBox;
usernameLineEdit->setEditable(true);
usernameLabel->setBuddy(usernameLineEdit);
mainlogin->addWidget(usernameLineEdit, 0, 1);

QLabel *capslockShow = new QLabel;
capslockShow->setWordWrap(true);
capslockShow->setText(" ");
mainlogin->addWidget(capslockShow, 1, 1);

QLabel *passwordLabel = new QLabel;
passwordLabel->setWordWrap(true);
passwordLabel->setText("Password");
mainlogin->addWidget(passwordLabel, 2, 0);
QLineEdit *passwordLineEdit = new QLineEdit;
passwordLineEdit->setEchoMode(QLineEdit::Password);
QAction *myAction = passwordLineEdit->addAction(QIcon(":/eyeOff.png"), QLineEdit::TrailingPosition);
passwordLabel->setBuddy(passwordLineEdit);
mainlogin->addWidget(passwordLineEdit, 2, 1);

What should I do next? Pls help me with code snippet.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
gnase
  • 580
  • 2
  • 10
  • 25
  • What's `LineEdit` class? A sub class of `QLineEdit`? If yes, you have to handle mouse press/release events in that sub class and set the appropriate echo mode depending on what user does. – vahancho Jul 05 '18 at 13:43
  • @vahancho :Sorry, that is normal QLineEdit, i typed wrong. But if i can build a new class, i can not imagine your idea – gnase Jul 05 '18 at 13:46
  • Ok, so you have to sub class `QLineEdit` and do what I already explained in my initial comment. – vahancho Jul 05 '18 at 13:47
  • @vahancho: could you pls express your idea with code snippet? – gnase Jul 05 '18 at 13:48
  • @vahancho: could you give me an example how i can set the echo mode corresponding to what users do? – gnase Jul 05 '18 at 14:43

2 Answers2

5

The solution is to add a QAction to the QLineEdit, this will create a QToolButton that we can obtain from the associatedWidgets() (it will be the second widget since the first one is the one associated with clearButton). Already having the QToolButton you must use the pressed and released signal.

passwordlineedit.h

#ifndef PASSWORDLINEEDIT_H
#define PASSWORDLINEEDIT_H

#include <QAction>
#include <QLineEdit>
#include <QToolButton>

class PasswordLineEdit: public QLineEdit
{
public:
    PasswordLineEdit(QWidget *parent=nullptr);
private slots:
    void onPressed();
    void onReleased();
protected:
    void enterEvent(QEvent *event);
    void leaveEvent(QEvent *event);
    void focusInEvent(QFocusEvent *event);
    void focusOutEvent(QFocusEvent *event);
private:
    QToolButton *button;
};

#endif // PASSWORDLINEEDIT_H

passwordlineedit.cpp

#include "passwordlineedit.h"

PasswordLineEdit::PasswordLineEdit(QWidget *parent):
    QLineEdit(parent)
{
    setEchoMode(QLineEdit::Password);
    QAction *action = addAction(QIcon(":/eyeOff"), QLineEdit::TrailingPosition);
    button = qobject_cast<QToolButton *>(action->associatedWidgets().last());
    button->hide();
    button->setCursor(QCursor(Qt::PointingHandCursor));
    connect(button, &QToolButton::pressed, this, &PasswordLineEdit::onPressed);
    connect(button, &QToolButton::released, this, &PasswordLineEdit::onReleased);
}

void PasswordLineEdit::onPressed(){
    QToolButton *button = qobject_cast<QToolButton *>(sender());
    button->setIcon(QIcon(":/eyeOn"));
    setEchoMode(QLineEdit::Normal);
}

void PasswordLineEdit::onReleased(){
    QToolButton *button = qobject_cast<QToolButton *>(sender());
    button->setIcon(QIcon(":/eyeOff"));
    setEchoMode(QLineEdit::Password);
}

void PasswordLineEdit::enterEvent(QEvent *event){
    button->show();
    QLineEdit::enterEvent(event);
}

void PasswordLineEdit::leaveEvent(QEvent *event){
    button->hide();
    QLineEdit::leaveEvent(event);
}

void PasswordLineEdit::focusInEvent(QFocusEvent *event){
    button->show();
    QLineEdit::focusInEvent(event);
}

void PasswordLineEdit::focusOutEvent(QFocusEvent *event){
    button->hide();
    QLineEdit::focusOutEvent(event);
}

The complete example can be downloaded from the following link.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • thanks for your easy and clear solution. I have one more small question. What should I do for 1 requirement: when we hover the mouse into eyeOn or eyeOff, the mouse will be changed into a finger, not cursor any more. – gnase Jul 06 '18 at 09:29
  • I have tried myself and have same solution. Thanks for your support and have a nice day, eyllanesc! :) – gnase Jul 06 '18 at 09:43
  • May I ask you one more question? How should I update the code if I need the eye icons only appear when we hover the mouse onto the LineEdit of password OR the LineEdit is focused? – gnase Jul 07 '18 at 15:47
  • @gnase I have already added that functionality, any other extra question? I think you should be able to move forward on your own. :) – eyllanesc Jul 07 '18 at 23:03
  • thanks a lot, I am totally satisfied and have learnt a lot from you. thanks again eyllanesc :) – gnase Jul 09 '18 at 11:49
3

Rather than trying to make use of the QAction returned by QLineEdit::addAction you could probably use a QWidgetAction for this when combined with a suitable event filter...

class eye_spy: public QWidgetAction {
  using super = QWidgetAction;
public:
  explicit eye_spy (QLineEdit *control, QWidget *parent = nullptr)
    : super(parent)
    , m_control(control)
    , m_on(":/eyeOn")
    , m_off(":/eyeOff")
    , m_pixmap_size(50, 50)
    {
      m_label.setScaledContents(true);
      m_control->setEchoMode(QLineEdit::Password);
      m_label.setPixmap(m_off.pixmap(m_pixmap_size));
      m_label.installEventFilter(this);
      setDefaultWidget(&m_label);
    }
protected:
  virtual bool eventFilter (QObject *obj, QEvent *event) override
    {
      if (event->type() == QEvent::MouseButtonPress) {
        m_control->setEchoMode(QLineEdit::Normal);
        m_label.setPixmap(m_on.pixmap(m_pixmap_size));
      } else if (event->type() == QEvent::MouseButtonRelease) {
        m_control->setEchoMode(QLineEdit::Password);
        m_label.setPixmap(m_off.pixmap(m_pixmap_size));
      }
      return(super::eventFilter(obj, event));
    }
private:
  QLineEdit *m_control;
  QLabel     m_label;
  QIcon      m_on;
  QIcon      m_off;
  QSize      m_pixmap_size;
};

Now, rather than...

QAction *myAction = passwordLineEdit->addAction(QIcon(":/eyeOff.png"), QLineEdit::TrailingPosition);

Use...

eye_spy eye_spy(passwordLineEdit);
passwordLineEdit->addAction(&eye_spy, QLineEdit::TrailingPosition);

Seems to provide the desired behaviour.

G.M.
  • 12,232
  • 2
  • 15
  • 18