Найти - Пользователи
Полная версия: PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки
Начало » GUI » PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки
1 2 3
Kyrym
Что ж, полностью переписал все функции. Согласен с тем, что прошлый код был ужасен.
Сейчас мне осталось решить два основных вопроса. Первый напишу сейчас, а второй после решения первого - выложу тогда весь код.

Итак. Первый вопрос.
Хочу оптимизировать вот такой код:
         # сбор данных для 1 строки
        db_tb_i = dict(Первый = self.pole_1.text(),
                       Второй = self.pole_2.text(),
                       Третий = self.pole_3.text(),
                       Четвёртый = self.pole_4.text())
        # конец: сбор данных
Очевидно, что это создание словаря из значений полей.
Дополнительная информация:
columnCount - глобальная переменная, число столбцов табл
columnName - глобальная переменная, список имён столбцов
self.polya - локальная переменная, атрибут текущего класса, это список из полей:
 self.polya = [self.pole_1,self.pole_2,self.pole_3,self.pole_4]

Сначала я хотел сделать словарь таким образом:
 for i in range(0,columnCount):
    db_tb_i = dict(zip(i in columnName, (i in self.polya).text()))
Не получилось. Тогда вот так:
 lst_polya = list(map(text(), self.polya))
for i in range(0,columnCount):
    db_tb_i = dict(zip(i in columnName, i in lst_polya))
Shell пишет вот это: “NameError: name ‘text’ is not defined”
Т.е. методы не работают с map или надо надо написать по-другому?
Почему не работают оба варианта создания словаря?

______________________________________
Кстати, PEHDOM, я подробнее посмотрел пример через диалог. Ну так он всё равно представляет собой вариацию схемы: удалить строку, вставить строку.

На форуме вычитал критику Родегаста по поводу shelve, дескать, ключи со временем стираются, база ломается. Он советовал использовать LevelDB, правда, инфы о ней мало.
А вообще для моих текущих целей pickl'a вполне достаточно. Тем более, что теперь я сохраняю базу по нажатии клавиши.


PEHDOM
Kyrym
Сначала я хотел сделать словарь таким образом:
O_o. Я порям в замешательстве долго пытался понять что эта кнструкция должна делать.
Kyrym
Не получилось. Тогда вот так:
Т.е. методы не работают с map или надо надо написать по-другому?
мап с методами класса так не работает, надо писать по другому
 class Pole:
    # класс моделирующий lineEdit
    def __init__(self, text):
        self._text = text
    def text(self):
        return self._text
columnName = ["Первый", "Второй", "Третий", "Четвёртый"]
polya = list(Pole('Текст в поле {}'.format(i)) for i in range(1, 5))
# первый вариант без зипа
db_tb_i1 = {columnName[i]: polya[i].text() for i in range(len(columnName))}
# второй вариант с зипом
db_tb_i2 = dict(zip((key for key in columnName ),(pole.text() for pole in polya)))
# третий вариант с мапом
lst_polya = list(map(lambda pole: pole.text(), polya))
print('текст в полях:',lst_polya)
db_tb_i3 = dict(zip(columnName, lst_polya))
print('первый вариант:', db_tb_i1)
print('второй вариант:', db_tb_i2)
print('третий вариант:', db_tb_i3)
>>>
текст в полях: ['Текст в поле 1', 'Текст в поле 2', 'Текст в поле 3', 'Текст в поле 4']
первый вариант: {'Первый': 'Текст в поле 1', 'Третий': 'Текст в поле 3', 'Четвёртый': 'Текст в поле 4', 'Второй': 'Текст в поле 2'}
второй вариант: {'Первый': 'Текст в поле 1', 'Третий': 'Текст в поле 3', 'Четвёртый': 'Текст в поле 4', 'Второй': 'Текст в поле 2'}
третий вариант: {'Первый': 'Текст в поле 1', 'Третий': 'Текст в поле 3', 'Четвёртый': 'Текст в поле 4', 'Второй': 'Текст в поле 2'}
но по моему вы страдаете фигней,, словарь неупорядоченый тип, поэтому вам приходиться устанавливать ключи stb_1 … stb_4 и тд… или “Первый” …. “Четвёртый” для доступа. Список же структура упорядоченая, помещайте каждое поле на свое место в списке, и доступ получайте также, в первую конолку первый элемент списка, в вторую - второй, в …надцатый - …надцатый элемент.
Kyrym
А вообще для моих текущих целей pickl'a вполне достаточно. Тем более, что теперь я сохраняю базу по нажатии клавиши.
ну смотрите, если у вас будет с десяток строчек, то пик справиться, а если хртябы пару тысяч, то нужно будет думать на чемто другим..
Kyrym
PEHDOM
Я прям в замешательстве долго пытался понять что эта кнструкция должна делать.
Это не удивительно, если учесть, что я сначала хотел написать вариант через “for i in range(0,columnCount):”, а потом забыл его убрать, когда применил zip.

На другом ресурсе мне предложили даже более простой (для меня) вариант:
 db_tb_i = dict(zip(columnName, [polya.text() for polya in self.polya]))

____
Почему словарь а не список?
Не знаю, на сколько Вам будет близок такой пример, но всё же. В экселе делать “обычные таблицы” с вычислениями, например, столбец умножить на другой столбец. В питоне идёт аналогия: список умножается на список. И там и там используются фиксированные индексы. Однако, в том же экселе есть понятие “умных таблиц”, где фиксированные индексы заменяются именованными диапазонами (в питоне - это аналог словарей). Так вот использовать именованные диапазоны гораздо эффективней, чем отказываться от них. В своё время я нахлебался со сбивающимися диапазонами, разрушенными связями между ячейками. Речь тут идёт не о маленьких табличках, а о листах со здоровыми таблицами, в которых колонки вычисляются по весьма запутанным трёхэтажным формулам. Таблицы между листами связаны… Короч, ад… )))

Я выбираю словари, потому что:
1) их использование не усложняет код.
2) код значительно более читаемый, отсутствуют непонятные индексы - куда они ведут, кто его знает? много времени тратится на проверку путей. у словарей нет такой проблемы.
3) в базе данных таблицы может появиться новый столбец, если словарь по умолчанию будет подставлять пустые ключи, при вставке в ячейки, то у списков надо думать над перехватом исключений.
Минусом словарей (при таком использовании) будет сложность изменения имени ключей. Но это решаемо.


PEHDOM
Это я протупил, это по сути второй вариант, только я чето затупил что columnName и так список.
Kyrym
Вот что получилось в итоге:
 import sys
import pickle
from PyQt4 import QtGui, QtCore
# ЦВЕТА ПОЛЕЙ
sss_vivod = ("background-color: #456173; color: #f2f2f0; font: 10pt 'Courier New'")
sss = ("background-color: #456173; color: #f2f2f0; font: 10pt 'Arial'")
sss_err = ("background-color: #456173; color: #ff6161; font: bold 10pt 'Courier New'")
# ПОДКЛЮЧЕНИЕ БД
try: # если файл существует
    with open('database4.db', 'rb') as F: # открываем файл
        try:
            db_tb = pickle.load(F) # читаем содержимое в переменную db        
        except EOFError: # если файл пустой
            db_tb = []
except FileNotFoundError: # если файл не существует
    F = open('database4.db', 'wb') # создаём пустой файл 
    F.close()
    db_tb = []  # в файле нет данных
# ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ
columnName = ["Первый", "Второй", "Третий", "Четвёртый"] # заголовки табл
rowCount = len(db_tb) # число строк
columnCount = len(columnName) # число столбцов
rowHeight = 20 # высота строки
# ГРАФИКА
class Window(QtGui.QWidget): # Класс Window  наследует класс QWidget  
    def __init__(self, parent=None): # Создаёт конструктор класса, parent - ссылка на родительский эл-т
        QtGui.QWidget.__init__(self, parent)
        super().__init__(parent, QtCore.Qt.Window)
        
        self.setMinimumSize(400, 600) # Ширина и высота окна
        self.resize(600, 800) # шир / выс окна
        self.setWindowTitle('Возможности таблицы QTableWidget') # Заголовок
        self.setWindowIcon(QtGui.QIcon('icon.png')) # Иконка               
        # БЛОК РАЗМЕТКИ
        self.vbox_os = QtGui.QVBoxLayout() # Создали объект вертикальный контейнер
        self.grid = QtGui.QGridLayout() # создание сетки
        self.grid.setSpacing(5)
        # >>> ИСХОДНЫЕ ДАННЫЕ
        self.pole_1 = QtGui.QLineEdit('')
        self.pole_1.setStyleSheet(sss)
        self.grid.addWidget(self.pole_1, 0,0)
        # ---
        self.pole_2 = QtGui.QLineEdit('')
        self.pole_2.setStyleSheet(sss)
        self.grid.addWidget(self.pole_2, 0,1)
        # ---
        self.pole_3 = QtGui.QLineEdit('')
        self.pole_3.setStyleSheet(sss)
        self.grid.addWidget(self.pole_3, 0,2)
        # ---
        self.pole_4 = QtGui.QLineEdit('')
        self.pole_4.setStyleSheet(sss)
        self.grid.addWidget(self.pole_4, 0,3)
        # ---
        self.button_prnt = QtGui.QPushButton('Принт ДБ')     
        self.button_prnt.clicked.connect(self.on_prnt)
        self.grid.addWidget(self.button_prnt, 1,0)
        # ---
        self.button_add = QtGui.QPushButton('Добавить в табл.')     
        self.button_add.clicked.connect(self.on_add_row)
        self.grid.addWidget(self.button_add, 1,1)
        # ---
        self.button_del_row = QtGui.QPushButton('Удалить стр')
        self.button_del_row.clicked.connect(self.on_del_row)
        self.grid.addWidget(self.button_del_row, 1,2)
        # ---
        self.button_save_db = QtGui.QPushButton('Сохранить БД')     
        self.button_save_db.clicked.connect(self.save_in_file)
        self.grid.addWidget(self.button_save_db, 1,3)
        # --- ---
        self.button_edit_row = QtGui.QPushButton('Редактировать стр')     
        self.button_edit_row.clicked.connect(self.on_edit_row)
        self.grid.addWidget(self.button_edit_row, 2,0)
        # ---
        self.button_save_row = QtGui.QPushButton('Сохранить стр')     
        self.button_save_row.clicked.connect(self.on_save_row)
        self.grid.addWidget(self.button_save_row, 2,1)
        # ---
        self.button_up_row = QtGui.QPushButton('Вверх стр')     
        self.button_up_row.clicked.connect(self.on_up_row)
        self.grid.addWidget(self.button_up_row, 2,2)
        # ---
        self.button_down_row = QtGui.QPushButton('Вниз стр')     
        self.button_down_row.clicked.connect(self.on_down_row)
        self.grid.addWidget(self.button_down_row, 2,3)
        self.polya = [self.pole_1,self.pole_2,self.pole_3,self.pole_4]
        # >>> КОНЕЦ: ИСХОДНЫЕ ДАННЫЕ
        self.vbox_os.addLayout(self.grid)
        # ---
        self.table = QtGui.QTableWidget() # Таблица
        self.createTable(columnName, rowCount, columnCount, rowHeight)
        self.vbox_os.addWidget(self.table) # Таблица 1                        
        # ---
        self.lbl_predup = QtGui.QLabel()
        self.lbl_predup.setStyleSheet(sss_err)
        self.vbox_os.addWidget(self.lbl_predup)
        # ---
        self.pole_vivod = QtGui.QTextEdit()
        self.pole_vivod.setStyleSheet(sss_vivod)
        self.vbox_os.addWidget(self.pole_vivod)
        # ---
        
        self.setLayout(self.vbox_os) # установка рабочей области
    def createTable(self, columnName, rowCount, columnCount, rowHeight):
        # создание таблицы
        '''создаем строки и столбцы в таблице
        columnName - подписи шапки таблицы
        rowCount - количество строк таблицы
        columnCount - количество столбцов таблицы
        rowHeight - высота строки'''
        
        # Режим растяжения таблицы по вертикали и горизонтали
        self.table.verticalHeader().setResizeMode(QtGui.QHeaderView.Fixed)
        self.table.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch)        
        
        # Режим выделения. Выделяем только строки. Выделяем только одну строку.
        self.table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
        self.table.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
        
        # Стилизуем шапку таблицы
        self.table.horizontalHeader().setStyleSheet("QHeaderView::section{font-weight:bold; color:#46647F; height:26px}");
                
        # self.table.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) # Запрет редактирования таблицы
        self.table.setEditTriggers(QtGui.QAbstractItemView.CurrentChanged) # выделение элемента
        self.table.setEditTriggers(QtGui.QAbstractItemView.DoubleClicked)
        
        self.table.setRowCount(rowCount) # Устанавливаем количество строк
        self.table.setColumnCount(columnCount) # Устанавливаем количество столбцов
                
        i = 0 # формируем подписи шапки:
        for name in columnName:
            item = QtGui.QTableWidgetItem()
            item.setText(name)
            self.table.setHorizontalHeaderItem(i,item)
            i+=1
            
        # Устанавливаем высоту строк
        for i in range(0, rowCount):
            self.table.setRowHeight(i, rowHeight)
            
        self.table.setColumnWidth(2, 50)
        # СИГНАЛЫ И ДОП СВОЙСТВА
        self.table.itemChanged.connect(self.cell_tabl_in_db) # сохр изменений в ячейке
        self.table.cellChanged.connect(self.predup) # предупреждение об изменении ячейки
        # АВТОЗАПУСК
        self.on_start()
    # ЛОГИКА
    
    def on_start(self):
        self.table.blockSignals(True) # блокировка сигналов
        for i in range(0,rowCount): # вставить базу данных в таблицу
            for j in range(0,columnCount):
                self.db_in_cell_tabl(i,j)
        self.table.blockSignals(False) # отмена блокировки сигналов
        
    def on_add_row (self): # добавить строку в базу
        # сбор данных для 1 строки
        db_tb_i = dict(zip(columnName, [polya.text() for polya in self.polya]))
        # конец: сбор данных
 
        db_tb.append(db_tb_i) # данные для новой строки табл
        row_index = len(db_tb)-1
        self.table.insertRow(row_index)
        self.paste_row(row_index,db_tb_i)
        self.predup()
        
    def paste_row(self, row,text):
        self.table.blockSignals(True) # блокировка сигналов
        self.table.setRowHeight(row, rowHeight) # высота строки
        for j in range(0,columnCount): # в таблицу вставляем значения
            self.db_in_cell_tabl(row,j)
        self.table.blockSignals(False) # отмена блокировки сигналов
        
        self.clear_polya() # очищаем поля          
     
    def db_in_cell_tabl(self, i,j): # из базы данных в ячейку
        item = QtGui.QTableWidgetItem()
        item.setText(db_tb[i][columnName[j]])
        self.table.setItem(i,j,item)
    def cell_tabl_in_db(self): # из ячейки табл в базу данных        
        i = self.table.currentRow() # индекс выделенной строки
        j = self.table.currentColumn() # индекс выделенной строки
        text = self.table.item(i,j).text() # извлечь текст из ячейки
        db_tb[i][columnName[j]] = text # текст в базу
        
    def predup (self): # предупреждение об изменении данных ячейки
        self.lbl_predup.setText('Изменения не сохранены')
    def on_up_row (self): # перемещение строки вверх
        row_index = self.table.currentRow() # индекс выделенной строки
        if row_index == 0:
            pass
        else:       
            db_tb.insert(row_index-1,db_tb[row_index]) # вставить строку в базу
            del db_tb[row_index+1] # удалить старую строку из базы
            self.table.insertRow(row_index-1) # вставить строку в табл
            self.table.removeRow(row_index+1) # удалить старую строку из табл
            self.paste_row(row_index-1,db_tb[row_index-1])            
            self.table.selectRow(row_index-1)
            self.predup()
            
    def on_down_row (self): # перемещение строки вниз
        row_index = self.table.currentRow() # индекс выделенной строки
        if row_index+1 == len(db_tb):
            pass
        else:
            db_tb.insert(row_index+2,db_tb[row_index]) # вставить строку в базу
            del db_tb[row_index] # удалить старую строку из базы
            self.table.insertRow(row_index+2) # вставить строку в табл
            self.table.removeRow(row_index) # удалить старую строку из табл
            self.paste_row(row_index+1,db_tb[row_index+1])
            self.table.selectRow(row_index+1) # выделяет передвинутую строку
            self.predup()
   
    def on_del_row (self):
        row_index = self.table.currentRow() # индекс выделенной строки
        del db_tb[row_index] # удалить строку в базе
        self.table.removeRow(row_index) # удалить строку в таблице
        self.predup()
    def on_edit_row(self): # правка строки в полях ввода
        row_index = self.table.currentRow() # индекс выделенной строки
        db_tb_i = db_tb[row_index]
        
        for i in range(0,len(self.polya)): # вставить строку из таблицы в поля
            self.polya[i].setText(db_tb_i[columnName[i]])
    def on_save_row(self): # изм строку в полях отправить в таблицу
        # сбор данных для 1 строки
        db_tb_i = dict(zip(columnName, [polya.text() for polya in self.polya]))
        # конец: сбор данных
        
        row_index = self.table.currentRow() # индекс выделенной строки
        db_tb[row_index].update(db_tb_i) # обновление словаря
        self.paste_row(row_index,db_tb_i)
        self.clear_polya()
        self.predup()
    def save_in_file(self): # сохранить базу в файл
        with open('database4.db', 'wb') as f:
            pickle.dump(db_tb, f)
        self.lbl_predup.setText('')
    def clear_polya(self): # очистить поля ввода
        for i in range(0,len(self.polya)):
            self.polya[i].clear()
    def on_prnt(self): # вывод БД в Shell
        for i in range(0,len(db_tb)):
            print(i,':',db_tb[i],sep='')
            self.pole_vivod.append(''.join([str(i),':',str(db_tb[i])]))
# КОНЕЦ
if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = Window() # создаёт экземпляр окна из класса
    window.move(40, 20) # сдвиг окна от верхнего левого угла экрана
    pal = window.palette()
    pal.setBrush(QtGui.QPalette.Window, QtGui.QBrush(QtGui.QColor("#222831")))
    window.setPalette(pal) # передаёт изменёный цвет окну
    window.show() # запускает окно
    sys.exit(app.exec_())
Выше я писал, что у меня есть второй вопрос. Сейчас я его решил. Так что всё.
Rodegast
 # сбор данных для 1 строки
        db_tb_i = dict(Первый = self.pole_1.text(),
                       Второй = self.pole_2.text(),
                       Третий = self.pole_3.text(),
                       Четвёртый = self.pole_4.text())
ИХМО так словари только наркоманы создают.

> На другом ресурсе мне предложили даже более простой (для меня) вариант

Тогда лучше как-то так:
 db_tb_i = { x:y.text() for x in columnName for y in self.polya }

> Вот что получилось в итоге

А получился просто классический пример того как не надо делать.
Kyrym
Rodegast
ИХМО так словари только наркоманы создают.
Т.е. язык питона написали наркоманы, будем знать…

Rodegast
А получился просто классический пример того как не надо делать.
Rodegast, я Вас прошу в моих темах ничего не писать, потому что, цитируя Вашу подпись, мне “правда не нравится”.
Rodegast
> я Вас прошу в моих темах ничего не писать

Так Я и так стараюсь обходить твои темы стороной, но иной раз сдержаться очень трудно.

> цитируя Вашу подпись, мне “правда не нравится”

Ты бы сначала уточнил за что я “заранее извиняюсь”, а потом бы подумал стоит ли мою подпись цитировать.
Kyrym
Было бы что уточнять… Первое и второе предложение не связаны. Второе предложение, в котором допущено 2 ошибки, означает: “Если кому-то не нравится, что я пишу, то заранее извините”.
Rodegast
> Если кому-то не нравится, что я пишу, то заранее извините

Не совсем так. Это значит что если я к примеру начну писать всю правду о твоём “коде”, то у тебя может начаться лёгкое психическое расстройство. Вот специально для таких случаев и добавлена эта фраза.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB