Уведомления

Группа в Telegram: @pythonsu
  • Начало
  • » GUI
  • » PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки [RSS Feed]

#1 Авг. 5, 2017 23:51:47

Kyrym
Зарегистрирован: 2016-12-28
Сообщения: 225
Репутация: +  3  -
Профиль   Отправить e-mail  

PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки

Есть таблица QTableWidget, делаю двойной клик по ячейке, редактирую содержимое и хочу сохранить изменения.
Как называется метод, фиксирующий окончание редактирования ячейки, чтобы поставить на него функцию изменения БД?

Офлайн

#2 Авг. 6, 2017 00:11:20

Kyrym
Зарегистрирован: 2016-12-28
Сообщения: 225
Репутация: +  3  -
Профиль   Отправить e-mail  

PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки

Вопрос снят.

itemChanged

Офлайн

#3 Авг. 7, 2017 23:29:04

Kyrym
Зарегистрирован: 2016-12-28
Сообщения: 225
Репутация: +  3  -
Профиль   Отправить e-mail  

PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки

Вопрос снова открыт.
Нужен сигнал, который будет срабатывать при изменении ячейки пользователем. А itemChanged (и cellChanged) срабатывает при каждой “прорисовке” таблицы.

И ещё вопрос. Хочу сделать перемещение строки по таблице вверх и вниз. Написал 2 функции на соответствующие кнопки. Приведу пример для перехода строки вниз:

     def on_down_row (self): # перемещение строки вниз
        row_index = self.table.currentRow()
        db_tb = self.read_data_file()
        if row_index+1 == len(db_tb):
            pass
        else:
            self.add_in_file(db_tb)
            db_tb.insert(row_index+2,db_tb[row_index])
            del db_tb[row_index]
            self.add_in_file(db_tb)
            self.change_table(db_tb) # В таблицу вставляем значения            
            self.table.selectRow(row_index+1) # выделяет передвинутую строку
Данный вариант мне не нравится двумя моментами:
1) приходится полностью перестраивать таблицу
2) само применение двух кнопок “вверх” и “вниз” не очень удобно.

Отсюда исходит два вопроса:
1) какой существует метод, чтобы просто передвинуть строку в таблице? Или тут надо делать через insertRow, а после удалять дубликат строки на старом месте (со старым индексом) через removeRow.
2) Мне кажется, наиболее красивый метод - это self.table.setDragDropMode(1). Только я не понял, какой сигнал вешать на DragDrop, и как после определять индекс строки после перемещения.


Отредактировано Kyrym (Авг. 7, 2017 23:29:50)

Офлайн

#4 Авг. 8, 2017 10:58:04

PEHDOM
Зарегистрирован: 2016-11-28
Сообщения: 2196
Репутация: +  294  -
Профиль   Отправить e-mail  

PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки

Kyrym сделайте простенький пример котороый можно у себя запустить, так будет проще чтото понять, и чтото конкретное посоветовать.



==============================
Помещайте код в теги:
[code python][/code]
Бериегите свое и чужое время.

Отредактировано PEHDOM (Авг. 8, 2017 10:58:25)

Офлайн

#5 Авг. 8, 2017 12:26:54

Kyrym
Зарегистрирован: 2016-12-28
Сообщения: 225
Репутация: +  3  -
Профиль   Отправить e-mail  

PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки

 # Python 3. PyQt4
# -*- coding: utf-8 -*-
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'")
columnName = ["Первый", "Второй", "Третий", "Четвёртый"] # заголовки табл
# db_tb = [] # Будущая структура данных
rowCount = 1 # число строк
rowHeight = 20 # высота строки
columnCount = 4 # число столбцов
stb = ['stb_1','stb_2','stb_3','stb_4']
# ГРАФИКА
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.table = QtGui.QTableWidget()
        self.createTable(columnName, rowCount, columnCount, rowHeight)        
        # БЛОК РАЗМЕТКИ
        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_button_add)
        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_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.vbox_os.addWidget(self.table) # Таблица 1
        # ---
        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) # Устанавливаем количество столбцов
        # self.table.itemChanged.connect(self.edit_cell) # сохр изменений в ячейке
        self.table.cellChanged.connect(self.edit_cell)
        
        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.setMovement(Free)
        self.table.setDragDropMode(1)
        #self.table.itemClicked.connect(self.on_click)
        #self.table.itemChanged.connect(self.on_click2)
        #self.table.itemDoubleClicked.connect(self.on_double_click)        
        self.db_in_tabl()
    # ЛОГИКА
    
    def on_start(self):
        pass
    def on_button_add (self): # кнопка добавления
        # сбор данных для 1 строки
        db_tb_i = dict(stb_1 = self.pole_1.text(),
                       stb_2 = self.pole_2.text(),
                       stb_3 = self.pole_3.text(),
                       stb_4 = self.pole_4.text())
        # конец: сбор данных        
        db_tb = self.read_data_file() # Читаем исходный файл
        db_tb.append(db_tb_i) # данные для новой строки табл        
        self.add_in_file(db_tb) # Записываем в файл ИД
        self.change_table(db_tb) # В таблицу вставляем значения
        Window.on_clear(self) # очищаем поля
        
    def click_btn_Read(self): # кнопка прочитать базу из файла и вставить в табл
        text = self.read_data_file()
        self.change_table(text) # изменить таблицу
    def on_clear(self): # очистить поля ввода
        for i in range(0,len(self.polya)):
            self.polya[i].clear()
        '''self.pole_1.clear()
        self.pole_2.clear()
        self.pole_3.clear()
        self.pole_4.clear()'''
        
    def add_in_file(self, db_tb):
        # добавление в файл данных и их возврат в вызывющую функцию        
        with open('database3.db', 'wb') as f:
            pickle.dump(db_tb, f)
                
    
    def read_data_file(self): # чтение данных из файла БД
        # Чтение из файла, если его не существует, то создаем его
        try:
            with open('database3.db', 'rb') as f: # пытаемся открыть файл
                try:
                    return pickle.load(f) # возвращаем содержимое файла                
                except EOFError: # если файл пустой
                    return []  # возвращаем пустой список
            
        except FileNotFoundError: # если файл не существует
            f = open('database3.db', 'wb')
            f.close()
            return [] # в файле нет данных
    
    def change_table(self,text):
        for i in range(1,len(text)+1):
            if len(text) >= rowCount: # если не хватает строк
                self.table.setRowCount(i) # добавить строку
                self.table.setRowHeight(i-1, rowHeight)
                
            item1 = QtGui.QTableWidgetItem()
            item1.setText(text[i-1]['stb_1']) # имя - ключ столбца
            self.table.setItem(i-1, 0, item1)
            # self.table.horizontalHeader().setResizeMode(0, QtGui.QHeaderView.ResizeToContents)
            
            item2 = QtGui.QTableWidgetItem()
            item2.setText(text[i-1]['stb_2'])
            self.table.setItem(i-1, 1, item2)
            #self.table.horizontalHeader().setResizeMode(1, QtGui.QHeaderView.ResizeToContents)
            item3 = QtGui.QTableWidgetItem()
            item3.setText(text[i-1]['stb_3'])
            self.table.setItem(i-1, 2, item3)
            #self.table.horizontalHeader().setResizeMode(1, QtGui.QHeaderView.ResizeToContents)
            item4 = QtGui.QTableWidgetItem()
            item4.setText(text[i-1]['stb_4'])
            self.table.setItem(i-1, 3, item4)
            #self.table.horizontalHeader().setResizeMode(1, QtGui.QHeaderView.ResizeToContents)
            
    def edit_cell (self): # изменение данных ячейки
        print('edit_cell =')
    '''def on_up_row (self): # перемещение строки вверх
        row_index = self.table.currentRow()
        if row_index == 0:
            pass
        else:
            pass'''
    def on_up_row (self): # перемещение строки вверх
        row_index = self.table.currentRow()
        if row_index == 0:
            pass
        else:
            db_tb = self.read_data_file()        
            db_tb.insert(row_index-1,db_tb[row_index])
            del db_tb[row_index+1]
            self.add_in_file(db_tb)
            #self.change_table(db_tb) # В таблицу вставляем значения
            self.table.insertRow(row_index-1)
            #self.table.selectRow(row_index-1)
            
    def on_down_row (self): # перемещение строки вниз
        row_index = self.table.currentRow()
        db_tb = self.read_data_file()
        if row_index+1 == len(db_tb):
            pass
        else:
            self.add_in_file(db_tb)
            db_tb.insert(row_index+2,db_tb[row_index])
            del db_tb[row_index]
            self.add_in_file(db_tb)
            self.change_table(db_tb) # В таблицу вставляем значения            
            self.table.selectRow(row_index+1) # выделяет передвинутую строку
                
    def db_in_tabl (self): # вставить базу данных в таблицу
        text = self.read_data_file()
        self.change_table(text)        
    def on_del_row (self):
        db_tb = self.read_data_file()
        row_index = self.table.currentRow()        
        # db_tb.pop(row_index)
        del db_tb[row_index]
        self.table.removeRow(row_index)
        self.add_in_file(db_tb)
    def on_edit_row(self): # правка строки в полях ввода
        row_index = self.table.currentRow()
        db_tb = self.read_data_file()
        db_tb_i = db_tb[row_index]
        
        for i in range(0,len(self.polya)):
            self.polya[i].setText(db_tb_i[stb[i]])
    def on_save_row(self):
        # сбор данных для 1 строки
        db_tb_i = dict(stb_1 = self.pole_1.text(),
                       stb_2 = self.pole_2.text(),
                       stb_3 = self.pole_3.text(),
                       stb_4 = self.pole_4.text())
        # конец: сбор данных
        
        db_tb = self.read_data_file()
        row_index = self.table.currentRow()
        db_tb.pop(row_index)
        db_tb.insert(row_index,db_tb_i)
        self.add_in_file(db_tb)
        self.change_table(db_tb) # В таблицу вставляем значения
        self.on_clear()
    
    def on_prnt(self):
        db_tb = self.read_data_file()
        for i in range(0,len(db_tb)):
            print(i,':',db_tb[i],sep='')
# КОНЕЦ
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_())  

Отредактировано Kyrym (Авг. 8, 2017 12:27:10)

Офлайн

#6 Авг. 8, 2017 21:27:44

PEHDOM
Зарегистрирован: 2016-11-28
Сообщения: 2196
Репутация: +  294  -
Профиль   Отправить e-mail  

PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки

мда. ну переместить строку вверх-вниз на 1 достаточно просто :
https://stackoverflow.com/questions/9166087/move-row-up-and-down-in-pyqt4
А вот драг-енд-дроп с “человеческим лицом” тут сложне, нужно наследовать QTableWidget и городить свой колхоз, примерно так:
https://stackoverflow.com/questions/26227885/drag-and-drop-rows-within-qtablewidget
теперь по поводу itemChanged, он генерируется когда элемент в ячейке меняется, при этом QTableWidget не знает меняете вы его “руками” или загружаете из файла данные. Самый просто способ блочить отправку сигналов, перед апдейтом таблицы из файла.

 def change_table(self, text):
   self.table.blockSignals(True)
   # тут заполняем таблицу из данных
   self.table.blockSignals(False)
Ну или Переходить от QTableWidget к QTableView с соответвующей моделью. Тогда это позволит вам различать редактирование (setData ) и обновления (data) вызываемые на модели.

Ну и напоследок, может стоит пересмортеть алгоритм работы программы в целом?



==============================
Помещайте код в теги:
[code python][/code]
Бериегите свое и чужое время.

Отредактировано PEHDOM (Авг. 8, 2017 21:30:55)

Офлайн

#7 Авг. 8, 2017 23:06:57

Kyrym
Зарегистрирован: 2016-12-28
Сообщения: 225
Репутация: +  3  -
Профиль   Отправить e-mail  

PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки

PEHDOM
Ну и напоследок, может стоит пересмортеть алгоритм работы программы в целом?
А можно немного поподробнее? Я уж не жду развёрнутого ответа, но хоть в двух словах, на что обратить внимание?

Так-то вообще эта программа - результат моего изучения QTableWidget, хотя после шлифовки функции пойдут в реальные программы. На QTableWidget я возлагаю большие надежды.

Офлайн

#8 Авг. 8, 2017 23:56:45

Kyrym
Зарегистрирован: 2016-12-28
Сообщения: 225
Репутация: +  3  -
Профиль   Отправить e-mail  

PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки

PEHDOM
мда. ну переместить строку вверх-вниз на 1 достаточно просто :
https://stackoverflow.com/questions/9166087/move-row-up-and-down-in-pyqt4
Ну, если это просто…
Пример через диалог у меня не запустился, шел ругается на ошибки. В любом случае, там кода много, разбираться нет смысла.
А второй пример, который “используя только QTableWidget” по сути то же, что я писал, т.е. добавили копию строки и удалили старый дубликат. Я думал, может, есть метод в одну строчку, ну ладно. Это понятно.
Пример с DragDrop мне понравился, хоть кода там и много. Надо будет разобраться, правда, процесс перетаскивания не могу назвать удобным - на сайтах-сервисах, типа списков дел, как-то поудобнее было.
PEHDOM
blockSignals
Блокировка мне понравилась, просто, удобно и наглядно. Взял на вооружение.

Офлайн

#9 Авг. 9, 2017 12:54:45

PEHDOM
Зарегистрирован: 2016-11-28
Сообщения: 2196
Репутация: +  294  -
Профиль   Отправить e-mail  

PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки

Kyrym
Ну, если это просто…
Пример через диалог у меня не запустился, шел ругается на ошибки. В любом случае, там кода много, разбираться нет смысла.
да нормально оно запускается, только нужно заменить xrange на range, поскольку челове писал на втором пайтоне.
Kyrym
А можно немного поподробнее? Я уж не жду развёрнутого ответа, но хоть в двух словах, на что обратить внимание?
В оновном это никакая масштабируемость, если например захотите добавить еще одну колонку вам придеться переписывать половину кода. Если захоте отказаться от пикла в пользу XLS например, или БД, нужно переписывать еще половину кода.
ну и по мелочи:
1. для начала, на каждый чих вы или пишете в файл или читаете из него..
например on_edit_row вы получаетет row_index = self.table.currentRow() а потом полностью считываете файл, и берете данные из него? почему бы не брать данные из таблицы? Они же всеравно там храняться в памяти.
2. хранение пиклом таблицы, имхо, не самая удачная идея. Если уж вы работаете с таблицей, лучше поискать способ хранить данные так чтобы на изменение одной ячейки не приходилос перезаписывать весь файл. Подумайте над тем чтобы иметь воззможность изменять произвольный участок файла, а не весь файл каждый раз когда у вас меняються данные. Может стоит подумать над использованием embedded BD типа sqlite или firebird embrdded?
3. для хранения строк таблицы используеться словарь.. имхо это избыточно, достаточно списка.
4. много “лишних движений” например:
 def on_button_add (self): # кнопка добавления
        # сбор данных для 1 строки
        db_tb_i = dict(stb_1 = self.pole_1.text(),
                       stb_2 = self.pole_2.text(),
                       stb_3 = self.pole_3.text(),
                       stb_4 = self.pole_4.text())
        # конец: сбор данных        
        db_tb = self.read_data_file() # Читаем исходный файл
        db_tb.append(db_tb_i) # данные для новой строки табл        
        self.add_in_file(db_tb) # Записываем в файл ИД
        self.change_table(db_tb) # В таблицу вставляем значения
        Window.on_clear(self) # очищаем поля
хотя по хорошему достаточно:
 # сбор данных для 1 строки
# В таблицу вставляем строку
#Обновляем файл из таблицы






==============================
Помещайте код в теги:
[code python][/code]
Бериегите свое и чужое время.

Офлайн

#10 Авг. 9, 2017 13:20:55

Kyrym
Зарегистрирован: 2016-12-28
Сообщения: 225
Репутация: +  3  -
Профиль   Отправить e-mail  

PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки

PEHDOM
поскольку челове писал на втором пайтоне
Вот была у меня такая мысль…
PEHDOM
никакая масштабируемость
это я согласен
PEHDOM
на каждый чих вы или пишете в файл или читаете из него.
Это да. Я об этом думал. Вообще хочу заменить pickle на shelve. Я так понял, что shelve как раз и будет записывать не всю БД, а только изменения, да и читать по ключу. Но с этим модулем мне только предстоит разобраться.
sqlite или firebird embrdded - это для меня пока слишком.
PEHDOM
3. для хранения строк таблицы используеться словарь.. имхо это избыточно, достаточно списка.
Да ну эти списки, словари куда более универсальны. Ведь вывести данные в таблицу - этого мало, данные нужно ещё и обработать, а тут использовать словарь гораздо выгоднее.

А так Ваши замечания я понял, буду исправлять. Спасибо!

Офлайн

  • Начало
  • » GUI
  • » PyQt 4 | QTableWidget. Сохранение изменений после редактирования ячейки[RSS Feed]

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version