Russian Qt Forum
Апрель 23, 2024, 23:24 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: PyQt4. Python3. Крах питона при попытке назначить модели второй делегат! Почему?  (Прочитано 4746 раз)
Пытон
Гость
« : Сентябрь 08, 2016, 05:56 »

Если модели на разные столбцы назначить один и тот же делегат, то всё работает.
Как только пытаешься назначить на разные столбцы разные же делегаты - полный крах питона.
Что за бяка такая? Может, я что-то не так делаю?
Windows 7 32bit. Python 3.3 PyQt4.9.6

Месторасположение назначения делегатов искать по слову # HERE
Код
Python
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_())
 
Записан
gil9red
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 1805



Просмотр профиля WWW
« Ответ #1 : Сентябрь 08, 2016, 07:43 »

Возможно произошло исключение в одном из слотов. В начало файла добавьте:
Код
C++ (Qt)
def log_uncaught_exceptions(ex_cls, ex, tb):
   text = '{}: {}:\n'.format(ex_cls.__name__, ex)
   text += ''.join(traceback.format_tb(tb))
 
   log.critical(text)
   QMessageBox.critical(None, 'Error', text)
   quit()
 
sys.excepthook = log_uncaught_exceptions
 
Записан

Пытон
Гость
« Ответ #2 : Сентябрь 08, 2016, 09:11 »

Куда в начало?
Разместил сразу после if __name__ == "__main__"
Ничего интересного не увидел. Всё тот же крах pythonw.exe

Сигнатура проблемы:
  Имя события проблемы:   APPCRASH
  Имя приложения:   pythonw.exe
  Версия приложения:   0.0.0.0
  Отметка времени приложения:   5676f350
  Имя модуля с ошибкой:   QtGui4.dll
  Версия модуля с ошибкой:   4.8.7.0
  Отметка времени модуля с ошибкой:   55662e12
  Код исключения:   c0000005
  Смещение исключения:   0043212f
  Версия ОС:   6.1.7601.2.1.0.256.48
  Код языка:   1049

Записан
gil9red
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 1805



Просмотр профиля WWW
« Ответ #3 : Сентябрь 08, 2016, 09:24 »

Я в начало файла засовываю.

UPDATE.
Все, разобрался.

Проблема была в функции create_views. Дело похоже в том, что объекты делегатов после работы функции были убиты сборщиком мусора, добавьте их в поля класса, чтобы их не потерли. Предполагаю, что питон решил убить обертку тех делегатов, а обертка собиралась убить qt-шные объекты и тут как раз и произошла фигня, даже sys.excepthook не помог.

Код
Python
self.spinbox_delegate1 = spinbox_delegate()
self.spinbox_delegate2 = spinbox2_delegate()
 


И немного не по теме, есть стандарт оформления кода на питоне -- pep8, который позволяет писать читабельный код. У меня ide -- PyCharm, который имеет в редакторе проверку стиля кода и он почти все строки вашего кода выделил -- отсутствие пустых строк между классами, между методами, между функциями, переименные написанные в верхнем регистре, классы, написанные в нижнем регистре и т.п.
Правила pep8 несложные и код становится легче читаемым другим программистам, следующим этому стандарту. Правда, если этот код является частью большого проекта, тогда стандарт не рекомендует форматировать код, а просто следовать стилю проекта.
« Последнее редактирование: Сентябрь 08, 2016, 09:46 от gil9red » Записан

Пытон
Гость
« Ответ #4 : Сентябрь 08, 2016, 09:42 »

Куда б я этот код ни запихнул, ничего в итоге не получаю. Всё такой же крах питона и всё.
Если вас не затруднит, попробуйте выполнить эту программу с вашей добавкой на своём компьютере, и расскажите, что произошло.
Записан
gil9red
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 1805



Просмотр профиля WWW
« Ответ #5 : Сентябрь 08, 2016, 09:47 »

Уже проверил и ответил, обновив предыдущее сообщение.
Записан

Пытон
Гость
« Ответ #6 : Сентябрь 08, 2016, 10:54 »

Огромное спасибо, добрый человек!
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.171 секунд. Запросов: 22.