0

I'm creating a password manager using PySide2 and Qt Designer. The way it works is, I have a class MainWindow. When initiated it reads a .csv file with all my passwords (later I'm gonna switch it to a database), creates an Account object out of each entry and adds them to a list called self.all_accounts

Here is my MainWindow class:

class MainWindow(QMainWindow):
    """The class responsible for showing the main window"""
    def __init__(self):
        with open('passwords.csv', 'r') as f:
            data = [i.strip('\n') for i in f.readlines()]

        self.all_accounts = [] #list that holds all my accounts

        for i in range(1, len(data)):
            values = data[i].split(',')
            a = Account(*values)
            self.all_accounts.append(a)

        QMainWindow.__init__(self)
        self.ui = Ui_MainWindow() # class autogenerated by Qt Designer
        self.ui.setupUi(self)
        ...
        self.load_passwords()

    def edit_account(self, account): #is used in self.load_passwords in editBtn QButton
    """Function responsible for editing an account. Loads a new frame replacing the main one"""
        self.ui.contentContainer.setHidden(True)
        editWindow = EditFrame(self, account)
        editWindow.show_frame()

    def show_hide_password(self, account, label, button): #see Account class
    """Function responsible for showing or hiding the password in a QLabel"""
        if account.hidden:
            label.setText(account.shown_password)
            button.setIcon(self.hide_password_icon)
            account.hidden = False
        else:
            label.setText(account.hidden_password)
            button.setIcon(self.show_password_icon)
            account.hidden = True
        
    def load_passwords():
    """Loads all passwords from self.all_accounts to show on the main window"""
        for account in self.all_accounts:
            ...
            passwordLabel = QLabel(frame) #QLabel that shows the password
            passwordLabel.setFont(font)
            passwordLabel.setStyleSheet(u"border:none")
            passwordLabel.setText(account.hidden_password)
            horizontalLayout.addWidget(passwordLabel, 0, Qt.AlignHCenter)

            copyBtn = QPushButton(frame)
            copyBtn.setMinimumSize(QSize(0, 38))
            copyBtn.setMaximumSize(QSize(60, 16777215))
            copyBtn.setFont(font)
            copyBtn.setStyleSheet(u"QPushButton{border: none;background-color: rgb(26, 156, 124)} QPushButton:hover{background-color: rgb(97, 176, 147);QPushButton:pressed{background-color: rgb(145, 218, 190)}")
            copyBtn.setText('Copy')
            copyBtn.clicked.connect(lambda account=account: pyCopy(account.shown_password)) #from pyperclip import copy as pyCopy
            horizontalLayout.addWidget(copyBtn)

            editBtn = QPushButton(frame)
            editBtn.setMinimumSize(QSize(0, 38))
            editBtn.setMaximumSize(QSize(70, 16777215))
            editBtn.setFont(font)
            editBtn.setStyleSheet(u"QPushButton{border: none; background-color: rgb(26, 156, 124)}QPushButton:hover{background-color: rgb(97, 176, 147)}QPushButton:pressed{background-color: rgb(145, 218, 190)}")
            editBtn.setText('Edit')
            editBtn.clicked.connect(lambda account=account: self.edit_account(account))
            horizontalLayout.addWidget(editBtn)

            showHideBtn = QPushButton(frame)
            showHideBtn.setMinimumSize(QSize(40, 38))
            showHideBtn.setMaximumSize(QSize(40, 16777215))
            showHideBtn.setStyleSheet(u"QPushButton{border: none;background-color: rgb(26, 156, 124)}QPushButton:hover{background-color: rgb(97, 176, 147)}QPushButton:pressed{background-color: rgb(145, 218, 190);}")
            showHideBtn.setIconSize(QSize(96, 96))
            showHideBtn.setIcon(self.show_password_icon)
            showHideBtn.clicked.connect(lambda account=account, label=passwordLabel, button=showHideBtn: self.show_hide_password(account, label, button))
            horizontalLayout.addWidget(showHideBtn)
            ...

My Account class:

class Account:
    __next_id = 1

    def __init__(self, name, domain, username, password, id_=None, date=None):
        self.name = name
        self.hidden_password = self.hide_password(password)
        self.shown_password = password
        self.domain = domain
        self.username = username
        self.logo = 'default.png'
        if id_ == None:
            self.id = self.__next_id
        else:
            self.id = id_
        
        Account.__next_id += 1

        if date == None:
            self.date = datetime.strftime(datetime.now(), '%d %b, %Y; %H:%M:%S')
        else:
            self.date = date

        self.hidden = True

    def hide_password(self, password):
        hidden_password = ''
        for _ in password:
            hidden_password += '\u25cf'
        return hidden_password

Each password is stored in a QFrame and has several QLabels showing the content of an account (name of the website, username, domain, etc.) and 3 buttons:

  • copyBtn: copies the password to the clipboard
  • editBtn: opens a new frame replacing the main one where you can edit the contents of an account. The frame is created via a class I wrote, EditFrame (I didn't post the class because I don't think it's part of the problem, but I will post it if it's necessary)
  • showHideBtn: responsible for showing or "hiding" the password in passwordLabel

The problem is, that while editBtn works fine, copyBtn and editBtn raises AttributeError: 'bool' object has no attribute 'shown_password' whenever click it.

Full traceback:

277 <lambda> main.py
editBtn.clicked.connect(lambda account=account: self.edit_account(account))

102 edit_account main.py
editWindow = EditFrame(self, account)

405 __init__ main.py
self.domainEdit.setText(account.domain)

AttributeError:
'bool' object has no attribute 'domain'

and

259 <lambda> main.py
copyBtn.clicked.connect(lambda account=account: pyCopy(account.shown_password))

AttributeError:
'bool' object has no attribute 'shown_password'

If you're can't trace the errors to some classes please refer to the GitHub repository at the end of the post. I didn't post all the classes because the post will be too long.

I have no idea why. For some reason it reads my Account object as bool, and the weird part for me is that showHideBtn works just fine even though I use it there too.

If you're interested, here's the link to the GitHub repository of the project https://github.com/AyazAhmadov/Password-Manager.git.

I know the code is messy, so if anything is unclear please let me know.

Edit: added traceback

tyro
  • 1
  • 1
  • Please post the full exception traceback. – CristiFati Feb 25 '21 at 17:45
  • please provide a [mre] – eyllanesc Feb 25 '21 at 17:51
  • use `editBtn.clicked.connect(lambda checked, account=account: self.edit_account(account))` and `copyBtn.clicked.connect(lambda checked, account=account: pyCopy(account.shown_password))` – eyllanesc Feb 25 '21 at 18:24
  • @eyllanesc Wouldn't that throw an error since we're passing 1 argument, even though 2 are required? Or am I missing something? – tyro Feb 25 '21 at 18:42
  • clicked has a default argument, please check the docs and the dupe – eyllanesc Feb 25 '21 at 18:45
  • @eyllanesc It worked. What you suggested returned an error, but it pushed me in the right direction. Defining `copy_password` as `def copy_password(self, checked, account): if checked: pyCopy(account.shown_password)` and then `copyBtn.clicked.connect(lambda checked=True, account=account: self.copy_password(True, account))` did the job – tyro Feb 25 '21 at 19:10

0 Answers0