Форум сайта python.su
Пардон, если тему создал не в том разделе. Думал, куда это в “базы данных” или в “GUI”, решил, что, всё-таки, в последнюю.
———-
Если модели на разные столбцы назначить один и тот же делегат, то всё работает.
Как только пытаешься назначить на разные столбцы разные же делегаты - полный крах питона.
Что за бяка такая? Может, я что-то не так делаю?
Месторасположение назначения делегатов искать по слову # HERE
Кстати, это код я пишу как памятку себе для работы с моделями и представлениями. Может, новичкам будет полезно.
from PyQt4 import QtGui, QtSql, QtCore # Создание делегата для использования виджетов прямо в ячейках таблицы class spinbox_delegate(QtGui.QItemDelegate): def createEditor(self, parent, option, index): editor = QtGui.QSpinBox(parent) return editor def setEditorData(self, spinBox, index): value = index.model().data(index, QtCore.Qt.EditRole) spinBox.setValue(int(value)) def setModelData(self, spinBox, model, index): value = spinBox.value() model.setData(index, value, QtCore.Qt.EditRole) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class spinbox2_delegate(QtGui.QItemDelegate): def createEditor(self, parent, option, index): editor = QtGui.QSpinBox(parent) return editor def setEditorData(self, spinBox, index): value = index.model().data(index, QtCore.Qt.EditRole) spinBox.setValue(int(value)) def setModelData(self, spinBox, model, index): value = spinBox.value() model.setData(index, value, QtCore.Qt.EditRole) def updateEditorGeometry(self, editor, option, index): editor.setGeometry(option.rect) class firstWindow(QtGui.QWidget): def __init__(self, parent = None): QtGui.QWidget.__init__(self) self.resize(800, 800) # создаём объект - соединение с базой данных под именем db (self.db) if not self.create_connection(): return # создаём в базе данных таблицу и заполняем её данными if not self.createTable(): return # создаём объекты-компоновщики (layout) self.mainLayout = QtGui.QVBoxLayout() self.setLayout(self.mainLayout) # создаём модель (self.model) и назначаем ей источник данных self.create_model_and_set_data_for_it() # создаём два представления (self.view1 и self.view2) для отображения данных из модели self.model # также создаём модели-выбора (selectionModel) для представлений. Позволяют работать с выделением ячеек, строк и столбцов в представлении. self.create_views() self.mainLayout.addWidget(self.view1) self.mainLayout.addWidget(self.view2) self.coordLayout = QtGui.QHBoxLayout() self.mainLayout.addLayout(self.coordLayout) # кнопки и прочие контролы # спин-боксы показывающие координаты текущей ячейки для view1 # также, при изменении в них данных пользователем, делают соответствующую ячейку текущей self.lbl_sb_current1 = QtGui.QLabel("Координаты текущей ячейки для view1. Строка:") self.coordLayout.addWidget(self.lbl_sb_current1) self.sb_current1 = QtGui.QSpinBox() self.coordLayout.addWidget(self.sb_current1) self.sb_current1.valueChanged.connect(lambda: self.sb_current_valueChanged(1)) self.lbl_sb_current2 = QtGui.QLabel("Столбец:") self.coordLayout.addWidget(self.lbl_sb_current2) self.sb_current2 = QtGui.QSpinBox() self.coordLayout.addWidget(self.sb_current2) self.sb_current2.valueChanged.connect(lambda: self.sb_current_valueChanged(2)) self.coordLayout.addStretch() # Заполнение таблицы данными по-умолчанию со стиранием всех предыдущих данных self.btn_set_default_values = QtGui.QPushButton("Заполнить таблицу данными по-умолчанию") self.mainLayout.addWidget(self.btn_set_default_values) self.btn_set_default_values.clicked.connect(self.btn_set_default_values_clicked) # Программное изменение данных в модели и сохранение их в таблицу БД self.btn_setValues = QtGui.QPushButton("Заменить все имена и отчества в модели и сохранить в БД") self.mainLayout.addWidget(self.btn_setValues) self.btn_setValues.clicked.connect(self.btn_setValues_clicked) # Создать отображаемые заголовки. Они служат только для отображения. На таблицу это не влияет. Имена полей в таблице останутся прежними. self.btn_set_headers = QtGui.QPushButton("Подставить заголовки") self.mainLayout.addWidget(self.btn_set_headers) self.btn_set_headers.clicked.connect(self.btn_set_headers_clicked) # чек-бокс выбора модели-выбора (одинаковая для обоих представлений или разные) self.checkbox_selmodel = QtGui.QCheckBox("Установить одну модель-выбора для обоих представлений") self.mainLayout.addWidget(self.checkbox_selmodel) self.checkbox_selmodel.clicked.connect(self.checkbox_selmodel_clicked) # Сделать программную выборку ячеек в представлении. Если чекбокс chekbox_selmodel включен, то выбор произойдёт в обоих представлениях self.btn_select_cells = QtGui.QPushButton("Выделить программно ячейки таблицы (в представлении)") self.mainLayout.addWidget(self.btn_select_cells) self.btn_select_cells.clicked.connect(self.btn_select_cells_clicked) # снять выделение с обоих view self.btn_clearselection = QtGui.QPushButton("Снять выделение с обоих представлений") self.mainLayout.addWidget(self.btn_clearselection) self.btn_clearselection.clicked.connect(self.btn_clearselection_clicked) # выдать данные из выделенных ячеек view1 self.btn_print_selected_cells = QtGui.QPushButton("Выдать список из данных выделенных ячеек") self.mainLayout.addWidget(self.btn_print_selected_cells) self.btn_print_selected_cells.clicked.connect(self.btn_print_selected_cells_clicked) # ------------------------------------------------------------------------------------------------------------------------- # ОБЪЕКТ-СОЕДИНЕНИЕ с БАЗОЙ ДАННЫХ (БД) def create_connection(self): self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE") self.db.setDatabaseName("db_for_models_and_views_lesson.sqlite3") #self.db.setDatabaseName(":memory:") # если хотим создать БД в оперативной памяти, чтобы не засорять диск if not self.db.open(): errortext = self.db.lastError().text() QtGui.QMessageBox.critical(None, "Ошибка", "Не удалось создать объект-соединение с БД!\n" + errortext, QtGui.QMessageBox.Cancel) return False return True # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- # СОЗДАНИЕ ТАБЛИЦЫ БАЗЫ ДАННЫХ def createTable(self): commandText = "CREATE TABLE IF NOT EXISTS table1 (FAM TEXT, IM TEXT, OT TEXT, DR DATETIME, NUMBER INTEGER)" sqlCommand = QtSql.QSqlQuery(self.db) if not sqlCommand.exec(commandText): errortext = sqlCommand.lastError().text() QtGui.QMessageBox.critical(None, "Ошибка", "Не удалось создать таблицу в БД!\n" + errortext, QtGui.QMessageBox.Cancel) return False return True # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- # ЗАПОЛНЕНИЕ ТАБЛИЦЫ ДАННЫМИ ПО-УМОЛЧАНИЮ ПРИ ПОМОЩИ BINDVALUE def btn_set_default_values_clicked(self): sqlCommand = QtSql.QSqlQuery(self.db) if not sqlCommand.exec("DELETE FROM table1"): # удаляем все данные из таблицы print ("Не удалось удалить данные из таблицы!") print (sqlCommand.lastError().text()) values = (("Твердохлебов", "Батон", "Булкович", "1980-02-01", 5), ("Ястребов", "Ара", "Чижикович", "1970-12-31", 7), ("Амбалова", "Культуристка", "Качковна", "1975-08-20", 2), ("Волосатов", "Шевелюр", "Причёскович", "1987-02-14", 8)) sqlCommand.prepare("INSERT INTO table1 VALUES (?, ?, ?, ?, ?)") for valueline in values: for value in valueline: sqlCommand.addBindValue(value) if not sqlCommand.exec_(): # обязательно exec_ с подчерком, иначе будет требовать параметр в скобках print ("Команда вставки строки в таблицу не сработала!") print (sqlCommand.lastError().text()) self.model.select() # чтобы обновить данные в модели, т.к. мы обновляли данные в самой БД, то модель об этих изменениях ничего не знает. # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- # СОЗДАЁМ МОДЕЛЬ И НАЗНАЧАЕМ ИСТОЧНИК ДАННЫХ ДЛЯ НЕЁ def create_model_and_set_data_for_it(self): self.model = QtSql.QSqlTableModel(None, self.db) # указываем модели с какой БД работать self.model.setTable("table1") # указываем таблицу из которой будет брать данные модель self.model.select() # получаем данные из этой таблицы # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- # СОЗДАЁМ ПРЕДСТАВЛЕНИЯ И СВЯЗЫВАЕМ ИХ С ОДНОЙ МОДЕЛЬЮ # ЗАОДНО СОЗДАЁМ МОДЕЛИ-ВЫБОРА (ТОЧНЕЕ ССЫЛКИ НА МОДЕЛИ-ВЫБОРА) ДЛЯ КАЖДОГО ИЗ ПРЕДСТАВЛЕНИЙ # И НАЗНАЧАЕМ ДЕЛЕГАТ def create_views(self): self.view1 = QtGui.QTableView() self.view1.setModel(self.model) self.selModel1 = self.view1.selectionModel() spinbox_delegate1 = spinbox_delegate() self.view1.setItemDelegateForColumn(4, spinbox_delegate1) #combobox_delegate1 = combobox_delegate() #self.view1.setItemDelegateForColumn(3, combobox_delegate1) # HERE spinbox_delegate2 = spinbox2_delegate() self.view1.setItemDelegateForColumn(3, spinbox_delegate2) self.view2 = QtGui.QTableView() self.view2.setModel(self.model) self.selModel2 = self.view2.selectionModel() # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- # ИЗМЕНЕНИЕ ДАННЫХ В МОДЕЛИ ПРОГРАММНЫМ ПУТЁМ И СОХРАНЕНИЕ ИЗМЕНЕНИЙ В ТАБЛИЦЕ БД def btn_setValues_clicked(self): # изменение данных по индексу for rowIndex in range(self.model.rowCount()): # также есть и columnCount() item = ("Имя " + str(rowIndex)) modelIndex = self.model.index(rowIndex, 1) # получаем объект-индекс (тип QtCore.QModelIndex) нашей модели с нужными нам "координатами" ячейки self.model.setData(modelIndex, item) # вставляем данные по полученному индексу в модель # Готово. Столбец с индексом 1 (это второй столбец) заполняется словами "Имя " + номер текущей строки (начиная с нуля) # изменение данных с использованием record, чтобы можно было использовать имя столбца recordCount = self.model.rowCount() record = self.model.record() indexColumn = record.indexOf("OT") for indexRow in range(recordCount): self.model.setData(self.model.index(indexRow, indexColumn), "Якубович") self.model.submitAll() # принудительный перенос данных из модели в связанную с ней таблицу # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- def btn_set_headers_clicked(self): # Можно просто назначить заголовок столбцу или строке по индексам self.model.setHeaderData(2, QtCore.Qt.Horizontal, "Отчество") self.model.setHeaderData(0, QtCore.Qt.Vertical, "Верт. загол.") # Почему-то эта строка не срабатывает! Хотя в другой программе срабатывает. Безобразие! # Заменить используемые по-умолчанию заголовки (имена полей из таблицы источника данных) на соответствующие заголовки на русском. slovar = {"FAM":"Фамилия", "IM":"Имя", "DR":"Дата рождения", "NUMBER":"Число"} record = self.model.record() # получаем объект, содержащий описание структуры строки модели из которой можно получить и имена столбцов связанной с моделью таблицы for keyname in slovar: # бежим по именам ключей в словаре column_index = record.indexOf(keyname) # получаем индекс столбца модели с именем совпадающим с текущим именем ключа из словаря (Т.е. к примеру для столбца с заголовком IM, мы получим индекс с номером 1) if column_index >= 0: # это значит, что мы нашли совпадение и получили нужный индекс. Если indexOf не находит нужный индекс, он возвратит -1 self.model.setHeaderData(column_index, QtCore.Qt.Horizontal, slovar[keyname]) # устанавливаем наш русскоязычный заголовок для отображения в представлении (view) #self.model.setHeaderData(index, QtCore.Qt.Horizontal, slovar[index]) # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- def checkbox_selmodel_clicked(self): # УСТАНАВЛИВАЕМ ПРИВЯЗКУ К МОДЕЛИ-ВЫБОРА (selectionModel) ДЛЯ ПРЕДСТАВЛЕНИЙ (view) if self.checkbox_selmodel.isChecked(): print("галочка стоит") self.view2.setSelectionModel(self.selModel1) # устанавливаем для второго представления selectionModel от первого представления #self.view2. else: print("галочка снята") self.view2.setSelectionModel(self.selModel2) # возвращаем второму представлению его изначальную selectionModel pass # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- def btn_select_cells_clicked(self): # ВЫДЕЛЯЕМ ЯЧЕЙКИ В ПРЕДСТАВЛЕНИЯХ ПРОГРАММНО # делаем выделение для первой view1 tmpindex_levo_verh = self.model.index(1, 0) # индекс с координатами левого верхнего угла tmpindex_pravo_niz = self.model.index(2, 3) # индекс с координатами правого нижнего угла selection = QtGui.QItemSelection(tmpindex_levo_verh, tmpindex_pravo_niz) # объект-выделение self.selModel1.select(selection, QtGui.QItemSelectionModel.ClearAndSelect) # собственно делаем выделение # если галочка для checkBox_selModel не установлена, то делаем выделение и во второй view2 if not self.checkbox_selmodel.isChecked(): # так можно выделить сразу всю строку используя только один индекс tmpindex = self.model.index(2, 0) self.selModel2.select(tmpindex, QtGui.QItemSelectionModel.Select|QtGui.QItemSelectionModel.Rows) pass pass # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- def btn_clearselection_clicked(self): # СНЯТИЕ ВСЕХ ВЫДЕЛЕНИЙ В МОДЕЛИ-ВЫБОРА self.selModel1.clearSelection() self.selModel2.clearSelection() # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- def sb_current_valueChanged(self, coord): # ПРОГРАММНОЕ ИЗМЕНЕНИЕ ТЕКУЩЕЙ ЯЧЕЙКИ МОДЕЛИ tmpindex = self.model.index(self.sb_current1.value(), self.sb_current2.value()) self.selModel1.setCurrentIndex(tmpindex, QtGui.QItemSelectionModel.SelectCurrent) # QItemSelectionModel. # NoUpdate # Clear # Select - выделить ячейку, не снимая выделений с других ячеек # Deselect # Toggle # Current # Rows # Columns # SelectCurrent - выделить ячейку и сделать её текущей # ToggleCurrent # ClearAndSelect # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- # ДОСТУП К ДАННЫМ МОДЕЛИ ПО УКАЗАННОМУ ИНДЕКСУ def btn_print_selected_cells_clicked(self): print("------------------------------") print("view1 selModel1") print("------------------------------") ListOfIndexes = self.selModel1.selectedIndexes() for index in ListOfIndexes: print (self.model.data(index)) # напечатать данные по указанному индексу из модели print("------------------------------") print("view2 selModel2") print("------------------------------") ListOfIndexes = self.selModel2.selectedIndexes() for index in ListOfIndexes: print (self.model.data(index)) # напечатать данные по указанному индексу из модели # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- # ============================================================================================================================= if __name__ == "__main__": import sys app = QtGui.QApplication(sys.argv) FirstWindow = firstWindow() FirstWindow.setWindowTitle("Основы работы с моделямим, предствлениями и делегатами") FirstWindow.show() sys.exit(app.exec_())
Отредактировано Pluto (Сен. 7, 2016 06:03:02)
Офлайн
Ну, кто-нибудь запустите хоть у себя этот код. Тоже крашится или нет?
Офлайн
Так работает
self.view1.setItemDelegateForColumn(3, spinbox_delegate()) #self.view1.setItemDelegateForColumn(4, spinbox_delegate())
#self.view1.setItemDelegateForColumn(3, spinbox_delegate()) self.view1.setItemDelegateForColumn(4, spinbox_delegate())
self.view1.setItemDelegateForColumn(3, spinbox_delegate()) self.view1.setItemDelegateForColumn(4, spinbox_delegate())
Офлайн
Так что, на модель больше одного делегата нельзя установить, что ли? Это баг какой-то?
Офлайн
В документации никаких оговорок нет. Скорее всего, баг в PyQt какой-то.
Офлайн
На другом форуме добрый человек сумел разобраться и всё разъяснил:
Объекты делегатов нужно было сделать частью класса.
Т.е. вместо
spinbox_delegate1 = spinbox_delegate() self.view1.setItemDelegateForColumn(4, spinbox_delegate1)
self.spinbox_delegate1 = spinbox_delegate() self.view1.setItemDelegateForColumn(4, self.spinbox_delegate1)
Отредактировано Pluto (Сен. 8, 2016 10:58:43)
Офлайн
Pluto
Объекты делегатов нужно было сделать частью класса.
Офлайн
Pluto
Объекты делегатов нужно было сделать частью класса.
Raule
Три дня долбался башкой о все форумы и документацию, половину методов у половины классов попереопределял, а всё оказалось так элементарно)
Офлайн