Форум сайта python.su
Есть таблица QTableWidget, делаю двойной клик по ячейке, редактирую содержимое и хочу сохранить изменения.
Как называется метод, фиксирующий окончание редактирования ячейки, чтобы поставить на него функцию изменения БД?
Офлайн
Вопрос снят.
itemChanged
Офлайн
Вопрос снова открыт.
Нужен сигнал, который будет срабатывать при изменении ячейки пользователем. А 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) # выделяет передвинутую строку
Отредактировано Kyrym (Авг. 7, 2017 23:29:50)
Офлайн
Kyrym сделайте простенький пример котороый можно у себя запустить, так будет проще чтото понять, и чтото конкретное посоветовать.
[code python][/code]
Отредактировано PEHDOM (Авг. 8, 2017 10:58:25)
Офлайн
# 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)
Офлайн
мда. ну переместить строку вверх-вниз на 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)
[code python][/code]
Отредактировано PEHDOM (Авг. 8, 2017 21:30:55)
Офлайн
PEHDOMА можно немного поподробнее? Я уж не жду развёрнутого ответа, но хоть в двух словах, на что обратить внимание?
Ну и напоследок, может стоит пересмортеть алгоритм работы программы в целом?
Офлайн
PEHDOMНу, если это просто…
мда. ну переместить строку вверх-вниз на 1 достаточно просто :
https://stackoverflow.com/questions/9166087/move-row-up-and-down-in-pyqt4
PEHDOMБлокировка мне понравилась, просто, удобно и наглядно. Взял на вооружение.
blockSignals
Офлайн
Kyrymда нормально оно запускается, только нужно заменить xrange на range, поскольку челове писал на втором пайтоне.
Ну, если это просто…
Пример через диалог у меня не запустился, шел ругается на ошибки. В любом случае, там кода много, разбираться нет смысла.
KyrymВ оновном это никакая масштабируемость, если например захотите добавить еще одну колонку вам придеться переписывать половину кода. Если захоте отказаться от пикла в пользу XLS например, или БД, нужно переписывать еще половину кода.
А можно немного поподробнее? Я уж не жду развёрнутого ответа, но хоть в двух словах, на что обратить внимание?
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]
Офлайн
PEHDOMВот была у меня такая мысль…
поскольку челове писал на втором пайтоне
PEHDOMэто я согласен
никакая масштабируемость
PEHDOMЭто да. Я об этом думал. Вообще хочу заменить pickle на shelve. Я так понял, что shelve как раз и будет записывать не всю БД, а только изменения, да и читать по ключу. Но с этим модулем мне только предстоит разобраться.
на каждый чих вы или пишете в файл или читаете из него.
PEHDOMДа ну эти списки, словари куда более универсальны. Ведь вывести данные в таблицу - этого мало, данные нужно ещё и обработать, а тут использовать словарь гораздо выгоднее.
3. для хранения строк таблицы используеться словарь.. имхо это избыточно, достаточно списка.
Офлайн