Уведомления

Группа в Telegram: @pythonsu
  • Начало
  • » GUI
  • » PyQt4. Python3. Крах питона при попытке назначить модели второй делегат! Почему? [RSS Feed]

#1 Сен. 7, 2016 06:00:26

Pluto
Зарегистрирован: 2012-05-29
Сообщения: 177
Репутация: +  2  -
Профиль   Отправить e-mail  

PyQt4. Python3. Крах питона при попытке назначить модели второй делегат! Почему?

Пардон, если тему создал не в том разделе. Думал, куда это в “базы данных” или в “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)

Офлайн

#2 Сен. 7, 2016 08:18:22

Pluto
Зарегистрирован: 2012-05-29
Сообщения: 177
Репутация: +  2  -
Профиль   Отправить e-mail  

PyQt4. Python3. Крах питона при попытке назначить модели второй делегат! Почему?

Ну, кто-нибудь запустите хоть у себя этот код. Тоже крашится или нет?

Офлайн

#3 Сен. 7, 2016 09:07:21

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9873
Репутация: +  853  -
Профиль   Отправить e-mail  

PyQt4. Python3. Крах питона при попытке назначить модели второй делегат! Почему?

Так работает

  
self.view1.setItemDelegateForColumn(3, spinbox_delegate())
#self.view1.setItemDelegateForColumn(4, spinbox_delegate())

Так работает
  
#self.view1.setItemDelegateForColumn(3, spinbox_delegate())
self.view1.setItemDelegateForColumn(4, spinbox_delegate())

Так не работает (segfault)
  
self.view1.setItemDelegateForColumn(3, spinbox_delegate())
self.view1.setItemDelegateForColumn(4, spinbox_delegate())



Офлайн

#4 Сен. 7, 2016 09:17:34

Pluto
Зарегистрирован: 2012-05-29
Сообщения: 177
Репутация: +  2  -
Профиль   Отправить e-mail  

PyQt4. Python3. Крах питона при попытке назначить модели второй делегат! Почему?

Так что, на модель больше одного делегата нельзя установить, что ли? Это баг какой-то?

Офлайн

#5 Сен. 7, 2016 11:56:11

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 9873
Репутация: +  853  -
Профиль   Отправить e-mail  

PyQt4. Python3. Крах питона при попытке назначить модели второй делегат! Почему?

В документации никаких оговорок нет. Скорее всего, баг в PyQt какой-то.



Офлайн

#6 Сен. 8, 2016 10:58:31

Pluto
Зарегистрирован: 2012-05-29
Сообщения: 177
Репутация: +  2  -
Профиль   Отправить e-mail  

PyQt4. Python3. Крах питона при попытке назначить модели второй делегат! Почему?

На другом форуме добрый человек сумел разобраться и всё разъяснил:
Объекты делегатов нужно было сделать частью класса.
Т.е. вместо

 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)

Офлайн

#7 Март 12, 2018 21:10:59

Raule
Зарегистрирован: 2018-03-12
Сообщения: 1
Репутация: +  0  -
Профиль   Отправить e-mail  

PyQt4. Python3. Крах питона при попытке назначить модели второй делегат! Почему?

Pluto
Объекты делегатов нужно было сделать частью класса.

Спасибо тебе огромное. Три дня долбался башкой о все форумы и документацию, половину методов у половины классов попереопределял, а всё оказалось так элементарно)

Офлайн

#8 Дек. 1, 2019 07:54:51

Lekks
Зарегистрирован: 2019-11-30
Сообщения: 12
Репутация: +  2  -
Профиль   Отправить e-mail  

PyQt4. Python3. Крах питона при попытке назначить модели второй делегат! Почему?

Pluto
Объекты делегатов нужно было сделать частью класса.

Raule, поддерживаю целиком и полностью. Большое, огромное спасибо Pluto.

Raule
Три дня долбался башкой о все форумы и документацию, половину методов у половины классов попереопределял, а всё оказалось так элементарно)

Аналогично, только на пару дней дольше.


Офлайн

  • Начало
  • » GUI
  • » PyQt4. Python3. Крах питона при попытке назначить модели второй делегат! Почему?[RSS Feed]

Board footer

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

Powered by DjangoBB

Lo-Fi Version