Форум сайта python.su
# Python 3. PyQt4
Пишу программу для хранения учётных записей.
Пока что модель программы выглядит вот так:
Накручивать функционал я буду потом. Сейчас главное отработать общую механику.
Хочу обратить внимание на то, что две левые “колонки” - это QListWidget. Поле с паролем буду менять на поле со звёздочками. База данных записана в виде списка с тройной вложенностью.
Сейчас база загружается с файла “basa.py”, а сохраняется в “basa3.pkl” - такая схема чисто для отработки механики.
В дальнейшем собираюсь шифровать базу данных и использовать мастер-пароль. Однако пока не разбирался с этим (уже предчувствую, как мне будут писать, что я изначально неправильно сохраняю базу данных…).
В общем, если кому интересно, то можете принять участие в разработке.
На данный момент у меня есть вопрос о поведении QListWidget.
Суть проблемы. Выбираю категорию, в листе учёток появляется список учёток. Выбираю учётку, в полях карточки учётной записи появляются данные. Если я меняю учётную запись, то данные в карточке меняются. Всё хорошо. Однако, если поменять категорию, то индекс учётки не сбрасывается (пытаюсь понять, как его всё-таки сбросить), поэтому карточка начинает лагать: в свои поля она начинает вставлять список учёток.
Код программы:
# Python 3. PyQt4 # -*- coding: utf-8 -*- import sys import pickle import res.basa as basa from PyQt4 import QtGui, QtCore '''F_R = open('res/basa3.pkl', 'rb') db = pickle.load(F_R)''' db = basa.db db_кат = basa.db[0] кат_index = 0 уч_index = 0 # ГРАФИКА class Window(QtGui.QWidget): def __init__(self, parent=None): QtGui.QWidget.__init__(self, parent) self.setMinimumSize(450, 400) # Миниамльная ширина и высота окна self.resize(600, 400) # шир / выс окна, работает парал. с setMinimumSize self.setWindowTitle('Хранение учётных записей') # Заголовок self.setWindowIcon(QtGui.QIcon('res\icon.png')) # Иконка # БЛОК СОЗДАНИЯ FRAME self.frame_1 = QtGui.QFrame() self.frame_1.setFrameShape(1) self.frame_1_lay = QtGui.QGridLayout(self.frame_1) # БЛОК СОЗДАНИЯ LABEL self.lbl_кат = QtGui.QLabel('Категории:') self.lbl_уч = QtGui.QLabel('Учётки:') self.lbl = QtGui.QLabel() self.lbl_адрес = QtGui.QLabel('Адрес:') self.lbl_логин = QtGui.QLabel('Логин:') self.lbl_пароль = QtGui.QLabel('Пароль:') self.lbl_почта = QtGui.QLabel('Почта:') self.lbl_имя = QtGui.QLabel('Имя:') self.lbl_фмл = QtGui.QLabel('Фамилия:') self.lbl_дата_откр = QtGui.QLabel('Открыто:') self.lbl_дата_закр = QtGui.QLabel('Закрыто:') # БЛОК СОЗДАНИЯ ПОЛЕЙ self.pole_кат = QtGui.QLineEdit() self.pole_кат.setPlaceholderText('Добавить категорию') self.pole_уч = QtGui.QLineEdit() self.pole_уч.setPlaceholderText('Добавить учётку') self.pole_адрес = QtGui.QLineEdit() self.pole_адрес.textChanged.connect(self.on_st_0) self.pole_логин = QtGui.QLineEdit() self.pole_логин.textChanged.connect(self.on_st_1) self.pole_пароль = QtGui.QLineEdit() self.pole_пароль.textChanged.connect(self.on_st_2) self.pole_почта = QtGui.QLineEdit() self.pole_почта.textChanged.connect(self.on_st_3) self.pole_имя = QtGui.QLineEdit() self.pole_имя.textChanged.connect(self.on_st_4) self.pole_фмл = QtGui.QLineEdit() self.pole_фмл.textChanged.connect(self.on_st_5) self.pole_дата_откр = QtGui.QLineEdit() self.pole_дата_откр.textChanged.connect(self.on_st_6) self.pole_дата_закр = QtGui.QLineEdit() self.pole_дата_закр.textChanged.connect(self.on_st_7) # БЛОК СОЗДАНИЯ КНОПОК self.button_prnt = QtGui.QPushButton('Print db') self.button_prnt.clicked.connect(self.on_prnt) self.button_кат_плюс = QtGui.QPushButton('+') self.button_кат_плюс.clicked.connect(self.on_kat_plus) self.button_кат_минус = QtGui.QPushButton('-') self.button_кат_минус.clicked.connect(self.on_kat_minus) self.button_уч_плюс = QtGui.QPushButton('+') self.button_уч_плюс.clicked.connect(self.on_uch_plus) self.button_уч_минус = QtGui.QPushButton('-') self.button_уч_минус.clicked.connect(self.on_uch_minus) self.button_copy = QtGui.QPushButton('Копировать') self.button_плюс = QtGui.QPushButton('+') self.button_минус = QtGui.QPushButton('-') self.button_адрес = QtGui.QPushButton('Адрес') self.button_логин = QtGui.QPushButton('Логин') self.button_пароль = QtGui.QPushButton('Пароль') self.button_почта = QtGui.QPushButton('Почта') # Q_LIST_WIDGET self.listWidget_кат = QtGui.QListWidget() self.listWidget_кат.addItems(db_кат) self.listWidget_кат.setFixedWidth(150) self.listWidget_кат.currentRowChanged.connect(self.on_kat) # изм номера строки категории self.listWidget_кат.currentRowChanged.connect(self.on_st_clear) # self.listWidget_кат.currentRowChanged.connect(self.on_kat_crnt) # очистка строк учётки self.listWidget_кат.itemDoubleClicked.connect(self.on_kat_edit) # редактирование имени категории self.listWidget_уч = QtGui.QListWidget() self.listWidget_уч.setFixedWidth(150) self.listWidget_уч.currentRowChanged.connect(self.on_uch) # БЛОК РАЗМЕТКИ self.grid_os = QtGui.QGridLayout() # создание сетки self.grid_os.setSpacing(5) self.grid_os.addWidget(self.lbl_кат, 1,0,1,2) self.grid_os.addWidget(self.lbl_уч, 1,2,1,2) self.grid_os.addWidget(self.button_copy, 1, 4) self.grid_os.addWidget(self.listWidget_кат, 2,0,1,2) self.grid_os.addWidget(self.listWidget_уч, 2,2,1,2) self.grid = QtGui.QGridLayout() # создание сетки self.grid.addWidget(self.frame_1,0,0) # Фрейм 1 self.frame_1_lay.addWidget(self.button_адрес,1,0) self.frame_1_lay.addWidget(self.button_логин,2,0) self.frame_1_lay.addWidget(self.button_пароль,3,0) self.frame_1_lay.addWidget(self.button_почта,4,0) # self.frame_1_lay.addWidget(self.lbl_адрес,1,0,1,3) # self.frame_1_lay.addWidget(self.lbl_логин,2,0,1,3) # self.frame_1_lay.addWidget(self.lbl_пароль,3,0,1,3) # self.frame_1_lay.addWidget(self.lbl_почта,4,0,1,3) self.frame_1_lay.addWidget(self.lbl_имя,5,0,1,3) self.frame_1_lay.addWidget(self.lbl_фмл,6,0,1,3) self.frame_1_lay.addWidget(self.lbl_дата_откр,7,0,1,3) self.frame_1_lay.addWidget(self.lbl_дата_закр,8,0,1,3) self.frame_1_lay.addWidget(self.pole_адрес,1,3,1,3) self.frame_1_lay.addWidget(self.pole_логин,2,3,1,3) self.frame_1_lay.addWidget(self.pole_пароль,3,3,1,3) self.frame_1_lay.addWidget(self.pole_почта,4,3,1,3) self.frame_1_lay.addWidget(self.pole_имя,5,3,1,3) self.frame_1_lay.addWidget(self.pole_фмл,6,3,1,3) self.frame_1_lay.addWidget(self.pole_дата_откр,7,3,1,3) self.frame_1_lay.addWidget(self.pole_дата_закр,8,3,1,3) self.grid_os.addWidget(self.pole_кат, 3,0,1,2) self.grid_os.addWidget(self.pole_уч, 3,2,1,2) self.grid_os.addWidget(self.button_кат_плюс, 4,0) self.grid_os.addWidget(self.button_кат_минус, 4,1) self.grid_os.addWidget(self.button_уч_плюс, 4,2) self.grid_os.addWidget(self.button_уч_минус, 4,3) self.grid_os.addWidget(self.button_prnt, 4, 6) self.grid_os.addLayout(self.grid,2,4,1,6) self.setLayout(self.grid_os) # установка рабочей области def basa_save(self): # сохранить базу данных F_W = open('res/basa3.pkl', 'wb') pickle.dump(db,F_W) F_W.close() def on_kat(self): # изм категории кат_index = self.listWidget_кат.currentRow() # принимает номер строки уч_index = None print('кат_index = ',кат_index) уч_lst = db[кат_index+1][0] # print('уч_lst = ',уч_lst) self.listWidget_уч.clear() self.listWidget_уч.addItems(уч_lst) # print('db = ',db) return кат_index def on_uch(self): # изм учётки кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() # принимает номер строки if уч_index == None: уч_index = None else: уч_index = self.listWidget_уч.currentRow() self.pole_адрес.setText(db[кат_index+1][уч_index+1][0]) self.pole_логин.setText(db[кат_index+1][уч_index+1][1]) self.pole_пароль.setText(db[кат_index+1][уч_index+1][2]) self.pole_почта.setText(db[кат_index+1][уч_index+1][3]) self.pole_имя.setText(db[кат_index+1][уч_index+1][4]) self.pole_фмл.setText(db[кат_index+1][уч_index+1][5]) self.pole_дата_откр.setText(db[кат_index+1][уч_index+1][6]) self.pole_дата_закр.setText(db[кат_index+1][уч_index+1][7]) print('кат_index = ',кат_index) print('уч_index = ',уч_index) def on_kat_plus(self): # добавить категорию кат_index = self.listWidget_кат.currentRow() a = self.pole_кат.text() # имя категории if a: self.listWidget_кат.insertItem(кат_index+1, a) db_кат.insert(кат_index+1, a) # к списку категорий db.insert(кат_index+2, [[]]) # пустой список учёток Window().basa_save() self.pole_кат.clear() print('db плюс = ',db) def on_kat_minus(self): # удалить категорию кат_index = self.listWidget_кат.currentRow() self.listWidget_кат.takeItem(кат_index) del db[0][кат_index] del db[кат_index+1] Window().basa_save() print('кат_index = ',кат_index) print('db минус = ',db) def on_uch_plus(self): # добавить учётку кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() b = self.pole_уч.text() # имя учётки if b: self.listWidget_уч.addItem(b) self.listWidget_уч.sortItems() db[кат_index+1][0].append(b) # к списку учёток db[кат_index+1][0].sort() db[кат_index+1][1:].sort() db[кат_index+1].append(['','','','','','','','']) print('db плюс = ') Window().prnt(db) print('db[кат_index+1][уч_index+1][0] = ',db[кат_index+1][уч_index+1][0]) self.pole_уч.clear() Window().basa_save() def on_uch_minus(self): # удалить учётку кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() self.listWidget_уч.takeItem(уч_index) del db[кат_index+1][0][уч_index] del db[кат_index+1][уч_index+1] Window().prnt(db) Window().basa_save() print('db минус = ') def on_kat_crnt(self): 1 def on_kat_edit(self): кат_index = self.listWidget_кат.currentRow() self.listWidget_кат.editItem(0) # ЗАПОЛНЕНИЕ УЧЁТКИ def on_st_0(self): кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() db[кат_index+1][уч_index+1][0] = self.pole_адрес.text() Window().basa_save() def on_st_1(self): кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() db[кат_index+1][уч_index+1][1] = self.pole_логин.text() Window().basa_save() def on_st_2(self): кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() db[кат_index+1][уч_index+1][2] = self.pole_пароль.text() Window().basa_save() def on_st_3(self): кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() db[кат_index+1][уч_index+1][3] = self.pole_почта.text() Window().basa_save() def on_st_4(self): кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() db[кат_index+1][уч_index+1][4] = self.pole_имя.text() Window().basa_save() def on_st_5(self): кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() db[кат_index+1][уч_index+1][5] = self.pole_фмл.text() Window().basa_save() def on_st_6(self): кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() db[кат_index+1][уч_index+1][6] = self.pole_дата_откр.text() Window().basa_save() def on_st_7(self): кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() db[кат_index+1][уч_index+1][7] = self.pole_дата_закр.text() print('db хар = ') Window().basa_save() def on_st_clear(self): # очистка полей учётки '''self.pole_адрес.setText('') self.pole_логин.setText('') self.pole_пароль.setText('') self.pole_почта.setText('') self.pole_имя.setText('') self.pole_фмл.setText('') self.pole_дата_откр.setText('') self.pole_дата_закр.setText('')''' def on_prnt(self, A): Window().prnt(db) def prnt(self, A): for i in A: print(i) # КОНЕЦ if __name__ == "__main__": app = QtGui.QApplication(sys.argv) window = Window() window.show() sys.exit(app.exec_())
db = [['АААА','ББББ'], [['А - учёт 0','А - учёт 1'], ['aaa0.ru','egir0','123456','egir@ex.ru','','','12.01.2017',''], ['aaa1.ru','egir1','123456','egir@ex.ru','','','12.01.2017','']], [['Б - учёт 0','Б - учёт 1'], ['bbb0.ru','fvr','123456','nch@ex.ru','','','15.08.2013',''], ['bbb1.ru','sadw','123456','gfhd@ex.ru','','','19.03.2016','']]]
Отредактировано Kyrym (Март 14, 2017 11:01:10)
Прикреплённый файлы:
Архив_Хранение_учёток.zip (5,7 KБ)
Офлайн
Это потому что если строка в КУвиджетЛисте не выбрана то currentRow() вернет -1 а не None
когда вы делаете listWidget.clear() текущий индекс сбрасываеться в -1 поскольку элемнтов нет и нет выделеного элемента.
поскольку текущий индекс изменился то срабатывает сигнал currentRowChanged котроый у вас присоединен к self.on_uch
теперь что мы видим(коментарри мои):
def on_uch(self): # изм учётки кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() # после смены категории уч_index станет = -1 if уч_index == None: # это условие никогда не выполниться уч_index = None # а это вообще бессмыслено , ведь у вас оно и так должно быть None else: уч_index = self.listWidget_уч.currentRow() # какой в этом смысл? мы же 4 строки назад делаи тоже самое.. self.pole_адрес.setText(db[кат_index+1][уч_index+1][0]) # уч_index у вас -1 вот вы и получили list index out of range self.pole_логин.setText(db[кат_index+1][уч_index+1][1]) self.pole_пароль.setText(db[кат_index+1][уч_index+1][2]) self.pole_почта.setText(db[кат_index+1][уч_index+1][3]) self.pole_имя.setText(db[кат_index+1][уч_index+1][4]) self.pole_фмл.setText(db[кат_index+1][уч_index+1][5]) self.pole_дата_откр.setText(db[кат_index+1][уч_index+1][6]) self.pole_дата_закр.setText(db[кат_index+1][уч_index+1][7]) print('кат_index = ',кат_index) print('уч_index = ',уч_index)
if уч_index == None: уч_index = None else: уч_index = self.listWidget_уч.currentRow()
if уч_index >= 0:
self.pole_адрес = QtGui.QLineEdit() self.pole_адрес.textChanged.connect(self.on_st_0) ............... def on_st_0(self): кат_index = self.listWidget_кат.currentRow() уч_index = self.listWidget_уч.currentRow() db[кат_index+1][уч_index+1][0] = self.pole_адрес.text() Window().basa_save() ............... def basa_save(self): # сохранить базу данных F_W = open('res/basa3.pkl', 'wb') pickle.dump(db,F_W) F_W.close()
db[кат_index+1][уч_index+1][0] = self.pole_адрес.text()
[code python][/code]
Отредактировано PEHDOM (Март 14, 2017 14:33:56)
Офлайн
Kyrym
вообще ты выбираешь трудные пути какие-то
для хранения таких данных есть базы данных
в питоне есть pysqlcipher для шифрования sqlite, поработай с ним для начала
идти всегда надо от структур данных к GUI, а не наоборот
Офлайн
PEHDOMА ведь в логах я видел -1, но не понял откуда он берётся. Теперь одной проблемой меньше )).
когда вы делаете listWidget.clear() текущий индекс сбрасываеться в -1
PEHDOMНа самом деле я так и хотел сделать, но после того, как разберусь с шифрованием.
Я бы воббще делал лайнедиты нередактируемымы
PEHDOMДа, об этом я как-то не подумал… Поправлю.
каждый раз когда вы переключаете учетки, меняется текст в лайнедитах, они генерируют textChanged и идет запись на диск.
PEHDOMПро кириллицу мне везде так говорят, но:
Имена перемнных на кирилице - ЕРЕСЬ
Имена переменных латиница+кирилица ЕРЕСЬ**2
vic57На сколько я понял, у sqlite отсутствует мастер-пароль. Или всё-таки есть?
в питоне есть pysqlcipher для шифрования sqlite, поработай с ним для начала
Отредактировано Kyrym (Март 14, 2017 15:44:50)
Офлайн
Kyrymесть ключ к базе данных, без которого она не читается
На сколько я понял, у sqlite отсутствует мастер-пароль. Или всё-таки есть?
Отредактировано vic57 (Март 14, 2017 15:55:05)
Офлайн
KyrymСмотря что понимать под мастер паролем, для меня это пароль с помощью которого шифруются данные всех учеток в базе, то есть без разницы какая база используется функция шифрования и расшифрования лежит на плечах разработчика, так же как и выбор алгоритма шифрования. Незная этого пароля вся база - это просто мусор.
На сколько я понял, у sqlite отсутствует мастер-пароль. Или всё-таки есть?
Офлайн
MrViktorГлавное, чтобы без пароля, нельзя было воспользоваться базой данных + удобство в использовании, а будет ли шифроваться содержимое файла или сам файл - это, как я понимаю, вторично.
Смотря что понимать под мастер паролем
Отредактировано Kyrym (Март 14, 2017 16:23:42)
Офлайн
KyrymНу тогда Вам надо определиться с базой которую будете использовать, написать две функции или одну универсальную для криптования и декриптования. Перед тем как записать данные из полей в базу надо будет их прогнать через крипто функцию, а результат записать в базу. Расшифрование еcтественно наоборот при нажатии к примеру на кнопку “показать пароль” из базы берется значение прогоняется через декрипт функцию и результат в текстбокс.
Главное, чтобы пароля, нельзя было воспользоваться базой данных + удобство в использовании, а будет ли шифроваться содержимое файла или сам файл - это, как я понимаю, вторично.
Отредактировано MrViktor (Март 14, 2017 16:15:46)
Офлайн
pycrypto подойдёт для этих целей с учётом поста 4?
Офлайн
KyrymНасколько я знаю у любой Embedded БД отсутвует мастер-пароль, Хотя может кто опровергнет?
На сколько я понял, у sqlite отсутствует мастер-пароль. Или всё-таки есть?
Kyrymда пофиг, ну сколько у вас будет храниться учетных записей? Сто? Двести? Тысяча? Да хоть в банальной DBF-ке храните , хоть в csv при таких обьемах.
А какую вообще базу данных лучше использовать, если к ней предъявляются требования:
MrViktor+ еще одну для глобального криптования-декриптования. например поменяли вы мастерпароль, нужно взять все зашифрованые старым ключем пароли , и перешифровать уже с новым ключем.
написать две функции или одну универсальную для криптования и декриптования
[code python][/code]
Отредактировано PEHDOM (Март 14, 2017 17:52:41)
Офлайн