Russian Qt Forum

Qt => Базы данных => Тема начата: Пытон от Февраль 10, 2013, 11:29



Название: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Февраль 10, 2013, 11:29
Код
Python
from PyQt4 import QtGui,  QtSql,  QtCore
import os.path
 
class window1(QtGui.QWidget):
   def __init__(self,  titletext):
       QtGui.QWidget.__init__(self)
       self.resize(800,  600)
       self.setWindowTitle(titletext)
 
       self.OpenOrCreateDB() #подключаемся к БД или создаём новую БД
 
       self.layout1 = QtGui.QVBoxLayout() # создаём контейнер для расположения в нём других виджетов
       self.setLayout(self.layout1) # назначаем его основным для нашей формы (окна)
 
       self.model1 = QtSql.QSqlTableModel(None,  self.DB) #создаём модель для хранения строк из БД, указываем с какой БД ей работать
       self.model1.setTable("table1") # указываем нужную таблицу из БД
       self.model1.setEditStrategy(QtSql.QSqlTableModel.OnManualSubmit) # данные сохраняются в БД посредством этой модели, только по команде
       self.model1.select() # запускаем комаду select, для выборки всех записей из указанной таблицы БД
       self.tableView1 = QtGui.QTableView() # создаём грид (таблицу для отображения данных из модели) для формы
       self.tableView1.setModel(self.model1) # указываем гриду из какой модели брать данные для отображения
       self.tableView1.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers) #таким зашибенным способом запрещается редактирование ячеек пользователем
       self.layout1.addWidget(self.tableView1)
 
       self.btnNew = QtGui.QPushButton("Добавить новую строку в БД") #создаём кнопку
       self.btnNew.clicked.connect(self.btnNewClicked) # связываем событие нажатия на кнопку с обработчиком для этого события
       self.layout1.addWidget(self.btnNew) # добавляем её в контейнер на форме
 
       self.btnEdit = QtGui.QPushButton("Редактировать текущую строку БД")
       self.btnEdit.clicked.connect(self.btnEditClicked)
       self.layout1.addWidget(self.btnEdit)
 
   def btnNewClicked(self):
       self.OpenEditWindow("NEW")
 
   def btnEditClicked(self):
       self.OpenEditWindow("EDIT")
 
   def OpenEditWindow(self, NewOrEdit):
       secondwindow = window2(NewOrEdit,  self.tableView1.currentIndex().row())
       result = secondwindow.exec() # если здесь использовать show, то окно не будет модальным! Т.е. из него можно будет спокойно перейти в другое окно.
       print (result)
 
   def OpenOrCreateDB(self):
       #создаём объект для работы с БД
       self.DB = QtSql.QSqlDatabase.addDatabase("QSQLITE", "Base")
       self.DB.setDatabaseName("./SQLiteBase/Lesson16DB.db")
 
       existDB = os.path.exists("./SQLiteBase/Lesson16DB.db")  #проверяем физичиское наличие БД
       if not existDB: print ("БД не существует. Попытка создать новую БД...")
 
       if not self.DB.open(): #попытка открыть БД (она же создание новой БД, если файла БД не существует)
           print("БД не открылась! Ошибка:",  self.DB.lastError().text())
       else: #подключение к БД произошло. Если при этом была создана новая БД, нужно создать в ней таблицы
           if not existDB: self.CreateStructureOfNewDB()
 
   def CreateStructureOfNewDB(self):
       query = QtSql.QSqlQuery(self.DB)
       querytext = ("CREATE TABLE table1 ("
                                          "id INTEGER, "
                                          "fam VARCHAR(20), "
                                          "im VARCHAR(20), "
                                          "ot VARCHAR(20), "
                                          "dr DATE, "
                                          "pol VARCHAR(1), "
                                          "gorod VARCHAR(20)"
                                          ")")
       #print (querytext)
       errors = 0
       tmp = query.exec(querytext)
       if not tmp:
           errors = errors + 1
 
       querytext = ("CREATE TABLE spisok1 ("
                    "id INTEGER, "
                    "text VARCHAR(20)"
                    ")")
 
       tmp = query.exec(querytext)
       if not tmp:
           errors = errors + 1
 
       return tmp
       if errors > 1:
           print ("БД не создана! Ошибка: ",  self.DB.lastError().text())
           return False
       else: return True
 
class window2(QtGui.QDialog): # создаём окно типа Диалог
   def __init__(self,  NewOrEdit,  currentIndex_):
       QtGui.QDialog.__init__(self)
 
       self.mapper = QtGui.QDataWidgetMapper() #маппер для связки данных из модели с контролами
       self.mapper.setModel(windowmain.model1) #связываем его с моделью данных из объекта windowmain
       self.mapper.setSubmitPolicy(QtGui.QDataWidgetMapper.ManualSubmit) #обновление данных в модели будет произведено только по команде
 
       tmpstr = NewOrEdit
       if NewOrEdit == "NEW":
           tmpstr = "Добавление новой записи в БД"
           Record = windowmain.model1.record() #создаём объект типа рекорд для добавления строки в модель
           windowmain.model1.insertRecord(-1,  Record) #insertRow использовать не стоит! Он не поддерживает связанные таблицы!
           self.mapper.toLast() #маппер тоже передвигаем к последней записи!
       elif NewOrEdit == "EDIT":
           tmpstr = "Редактирование записи"
       self.setWindowTitle(tmpstr)
       #self.setWindowModality(QtCore.Qt.WindowModal)
 
       self.layout2 = QtGui.QHBoxLayout()
       self.layout2_1 = QtGui.QVBoxLayout()
       self.layout2.addLayout(self.layout2_1)
       self.layout2_2 = QtGui.QVBoxLayout()
       self.layout2.addLayout(self.layout2_2)
       self.layout2_3 = QtGui.QHBoxLayout()
       self.layout2.addLayout(self.layout2_3)
       self.setLayout(self.layout2)
 
 
       Alignment = QtCore.Qt.AlignTop
 
       self.id_label = QtGui.QLabel("Номер")
       self.id_ = QtGui.QLineEdit()
       self.layout2_1.addWidget(self.id_label,  alignment = Alignment)
       self.layout2_2.addWidget(self.id_,  alignment = Alignment)
       self.mapper.addMapping(self.id_,  windowmain.model1.fieldIndex("id")) #связываем контрол id_ с полем id из маппера, который передаст значение в модель, которая передаст в БД (вот такие тут все передасты!)
 
       self.fam_label = QtGui.QLabel("Фамилия")
       self.fam_ = QtGui.QLineEdit()
       self.layout2_1.addWidget(self.fam_label,  alignment = Alignment)
       self.layout2_2.addWidget(self.fam_,  alignment = Alignment)
       self.mapper.addMapping(self.fam_,  windowmain.model1.fieldIndex("fam"))
 
       self.im_label = QtGui.QLabel("Имя")
       self.im_ = QtGui.QLineEdit()
       self.layout2_1.addWidget(self.im_label,  alignment = Alignment)
       self.layout2_2.addWidget(self.im_,  alignment = Alignment)
       self.mapper.addMapping(self.im_,  windowmain.model1.fieldIndex("im"))
 
       self.ot_label = QtGui.QLabel("Отчество")
       self.ot_ = QtGui.QLineEdit()
       self.layout2_1.addWidget(self.ot_label,  alignment = Alignment)
       self.layout2_2.addWidget(self.ot_,  alignment = Alignment)
       self.mapper.addMapping(self.ot_,  windowmain.model1.fieldIndex("ot"))
 
       self.dr_label = QtGui.QLabel("Дата рождения")
       self.dr_ = QtGui.QLineEdit()
       self.layout2_1.addWidget(self.dr_label,  alignment = Alignment)
       self.layout2_2.addWidget(self.dr_,  alignment = Alignment)
       self.mapper.addMapping(self.dr_,  windowmain.model1.fieldIndex("dr"))
 
       self.pol_label = QtGui.QLabel("Пол")
       self.pol_ = QtGui.QLineEdit()
       self.layout2_1.addWidget(self.pol_label,  alignment = Alignment)
       self.layout2_2.addWidget(self.pol_,  alignment = Alignment)
       self.mapper.addMapping(self.pol_,  windowmain.model1.fieldIndex("pol"))
 
       self.gorod_label = QtGui.QLabel("Город")
       self.gorod_ = QtGui.QComboBox()
       #создаём модель для комбобокса
       self.gorodModel = QtSql.QSqlTableModel(None,  windowmain.DB)
       self.gorodModel.setTable("spisok1")
       self.gorodModel.select()
       self.gorod_.setModel(self.gorodModel)
       self.gorod_.setModelColumn(1)
       self.layout2_1.addWidget(self.gorod_label,  alignment = Alignment)
       self.layout2_2.addWidget(self.gorod_,  alignment = Alignment)
       self.mapper.addMapping(self.gorod_,  windowmain.model1.fieldIndex("gorod"))
 
       self.btnSave = QtGui.QPushButton("Запомнить")
       self.btnSave.clicked.connect(self.SaveData)
       self.layout2_3.addWidget(self.btnSave,  alignment = QtCore.Qt.AlignRight)
 
 
       self.btnCancel = QtGui.QPushButton("Отменить")
       self.btnCancel.clicked.connect(self.Cancel)
       self.layout2_3.addWidget(self.btnCancel,  alignment = QtCore.Qt.AlignRight)
 
       if NewOrEdit == "EDIT":
           self.mapper.setCurrentIndex(currentIndex_) # Этот способ выбора текущей строки из модели нужно проерить. Что будет, если данные в модели будут отсортированы?
 
   def SaveData(self):
       self.mapper.submit() #сохраняем изменения в модели
       windowmain.model1.submitAll() # а теперь сохраняем изменения из модели в БД
       self.close()
 
   def Cancel(self):
       windowmain.model1.revertAll() #отменяем изменения в модели. Иначе вновь добавленная строка останется.
       self.close() #закрываем окно
 
if __name__ == "__main__":
   import sys
   app = QtGui.QApplication(sys.argv)
   windowmain = window1("Первое окно")
   windowmain.show()
   sys.exit(app.exec_())
 
 

Смотрим на строку self.mapper.addMapping(self.gorod_, windowmain.model1.fieldIndex("gorod"))

Как указать мапперу, что в поле gorod таблицы table1 нужно записать значение поля text из таблицы spisok1? Чего он мне туда индекс записывает?

Я не использую реляционную модель!


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Февраль 10, 2013, 16:07
Ну тут же явно нужно как-то указать, что комбобокс должен возвратить значение из своего списка, а не его индекс. Ну как это сделать-то?


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Февраль 24, 2013, 11:42
Ненавижу QT. Ничего непонятно и никто помочь не может.


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Bepec от Февраль 24, 2013, 13:15
А ты поменьше такими словами кидайся :D

Связка Qt + python не очень популярна на данный момент.

Я не могу сказать практически ничего, изза незнания синтаксиса и способа испльзования pyQt, но на мой взгляд у вас всё усложнено. Особенно передасты :D


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Февраль 24, 2013, 17:58
Связка Qt + C++ тоже не распространена?
Мне и помощь нарисованная в коде для С++ подойдёт. Уж как-нибудь я её перенесу в питон (алгоритм-то весь тот же), тем более, что на питоне писать проще из-за отсутствия лишних точек-запятучек-закорючек-скобочек-стрелочек и предварительных объявлений типов данных.

Если мой пример на питоне не понятен, то объясню то, что мне надо просто словами:
Имеется таблица (НЕ связанная ни с какой другой, первичных ключей не имеет). Имеется модель для этой таблицы. Имеется маппер, который работает с этой моделью. Нужно: чтобы этот чёртов маппер брал данные из второй таблицы и сохранял их в первую. И чтобы сохранял он не индекс записи, не первичный ключ, а именно текстовые данные из второй таблицы. Т.е. в первой таблице должен храниться не ключ, а одно из слов из второй таблицы.

Первая таблица:
fam          im         ot                  gorod
хрюкин   хрюка   хрюкович     бердск - в этом поле храним ТЕКСТ, никакой не ключ!

вторая таблица:
gorod
бердск - вот прямо это значение и записываем в поле gorod первой таблицы
братск
салатск
урюпинск


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Bepec от Февраль 24, 2013, 19:48
Попытаюсь посмотреть завтра, если будет свободное время на работе ( :D ).

 


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: panAlexey от Февраль 25, 2013, 11:28
Ненавижу QT. Ничего непонятно и никто помочь не может.
Ты не на Qt лабаешь, а на купитоне.
Qt без прослоек весьма съедобна.


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Bepec от Февраль 25, 2013, 17:47
Времени на работе не було к сожалению. Понял в принципе ваш код, попытаюсь воссоздать на плюсах.


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Февраль 25, 2013, 18:49
Ненавижу QT. Ничего непонятно и никто помочь не может.
Ты не на Qt лабаешь, а на купитоне.
Qt без прослоек весьма съедобна.


Я в курсе на чём я лабаю. С прослойками, без прослоек ли, а виджеты и объекты Qt одни и те же вне зависимости от прослоек. Что писал бы я на С++, что пишу сейчас на питоне - одинаково непонятно как заставить маппер делать то, что мне нужно.

Или от прослоек у виджетов и объектов Qt вдруг изменяются свойства, методы, сигналы, слоты и т.д?


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: panAlexey от Февраль 25, 2013, 18:58
Я в курсе на чём я лабаю. С прослойками, без прослоек ли, а виджеты и объекты Qt одни и те же вне зависимости от прослоек.
Не одни и те же а бинденные. Т.е. с налетиком...


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Bepec от Февраль 25, 2013, 19:03
update:

У вас уникальное стечение обстоятельств, что хоть что-то работает :)

У вас комбобокс изменяется при хождении по записям? У меня лично нет (хотя мб это фишка пикуте).

Стандартное значение комбобокса - индекс. Вот его он туда и записывает. Чтобы записывал текст, необходимо написать вот так
Код:
mapper_->addMapping(ui.comboBox, 2, "currentText");
или если на пикуте, если я не ошибаюсь, вот так
Код:
self.mapper.addMapping(self.gorod_,  windowmain.model1.fieldIndex("gorod"), "currentText")

Единственно что, у меня при таком раскладе не отображается текст при хождении через маппер (пропертя только для чтения, он не может её установить). Но, думаю, это вполне обходится, если отнаследоваться от комбобокса и чуточку изменить его проперти (сомнения есть, что это возможно, не пробовал).

PS Искреннее спасибо вам за тему. Этот компонент мной был не охвачен, но ОЧЕНЬ ОЧЕНЬ нужен в ближайшее время :D Теперь во всеоружии :D


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Февраль 26, 2013, 18:54
Ткните мне, пожалуйста, нерадивому пальцем, и хде вы нашли этот самый "currentText"?
В qt ассистенте я такого не видел. Где это можно увидеть, чтобы я не лазил по форумам и сайтам?
currentText - это, получается, свойство самого комбобокса.


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Февраль 26, 2013, 18:57
update:

У вас уникальное стечение обстоятельств, что хоть что-то работает :)

PS Искреннее спасибо вам за тему. Этот компонент мной был не охвачен, но ОЧЕНЬ ОЧЕНЬ нужен в ближайшее время :D Теперь во всеоружии :D

Бесконечно рад, что смог доставить вам удовольствие в возможности поупражняться в сарказме. Это было целью всей моей жизни до сего момента. Теперь не знаю, что и делать, как дальше жить... ;)

П.С. За потраченное на меня время конечно же спасибо.


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Февраль 26, 2013, 19:05
update:

так
Код:
mapper_->addMapping(ui.comboBox, 2, "currentText");
или если на пикуте, если я не ошибаюсь, вот так
Код:
self.mapper.addMapping(self.gorod_,  windowmain.model1.fieldIndex("gorod"), "currentText")
На PyQt можно написать практически также как в вашем варианте для C++. Просто я не использовал конструктор форм, и вместо прямого указания индекса поля, велел PyQt определять индекс поля по его имени.

П.С. self'ы меня тоже раздражают, но ещё больше раздражает синтаксис С++ с его фигурными скобками, стрелочками, двойными двоеточиями и командами типа: типобъекта имяобъекта = новый типобъекта (указано ж заразе один раз, какой тип у объекта, нафига после new снова его указывать?)


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Февраль 26, 2013, 19:17
А можно вопрос не по теме?
Почему для model = SqlRelationalTableModel не срабатывает model.insertRecord(Record), где Record = model.record()?

При этом в БД появляется новая запись (это видно через сторонний просмотрщик SQLite и даже новое значение первичного ключа генерируется), заполненная null, но эта запись не отображается во view связанной с model!

Кто-нибудь вообще использует SqlRelationalTableModel или все дружно используют для работы с БД исключительно SQL-запросы?


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Bepec от Февраль 26, 2013, 19:58
Пишу из пробитого танка запасного браузера :D

Я лично с SQL столкнулся недавно. Про реляционную модель ничего сказать не могу.

 Собственно сарказма в моём спасибке не было, искренняя благодарность.

А currentText - это указывается пропертя виджета, об этом написано в документации по QDataWidgetMapper'у (у него метод перегруженный такой).

А само свойство я вытянул из исходников комбобокса в приступе святого рвения помощи близким.

Собственно вот доказательства.

Ассистент QDataWidgetMapper:
Цитировать
void QDataWidgetMapper::addMapping ( QWidget * widget, int section, const QByteArray & propertyName )

Ассистент QComboBox:
Цитировать
currentText : const QString
This property holds the text of the current item.
By default, for an empty combo box or a combo box in which no current item is set, this property contains an empty string.
Access functions:
QString   currentText () const

PS цитаты сам только что читал впервые. Как то странно, но у меня получилось добиться только с помощью исходников + ассист подсказывает.


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Февраль 28, 2013, 18:13
При помощи добавленного "currentText", этот самый текст замечательно сохраняется в таблицу БД.
Вот только теперь, при открытии окна с этим комбобоксом, в комбобоксе всегда выбрана первая запись. И чхать он хотел на то, что в строке модели совсем другой город был.

Теперь каким-то макаром нужно указать мапперу, чтобы он отыскивал в своём источнике данных (таблица городов) текст соответствующий тексту из главной таблицы и делал его текущим


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Bepec от Февраль 28, 2013, 21:47
Я ж написал ) Что это можно обойти, если :
1) отнаследоваться от комбобокса и добавить ему свойство )
2) плюнуть на виджетмаппер и написать собственную реакцию для комбобокса.

PS правда я не представляю себе возможно ли в пикуте наследование и переопределение.

PPS Осторожно! Простое решение!
Есть возможность костыля быстрого. Это создание двух комбобоксов. Один будет получать данные без currentText индекс. Тем самым получать корректное значение записи в базе. И если его скрыть, но связать сигналом с вторым комбобоксом, который будет связан c currentText, тогда у тебя получится связка, получающая корректные значения и записывающая текст в базу.
Данная импровизация построена на логике и здравом смысле, но не гарантирует 100% решаемости проблемы.


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Март 02, 2013, 06:20
Всё в "пикуте" возможно, только я толком не соображаю каким макаром мне всё это делать. Какое свойство добавлять, от чего оно должно зависеть, как в нём должно появляться требуемое значение...


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Bepec от Март 02, 2013, 09:08
*Троль вкл* Быгыгы *Троль выкл*

У виджетов есть свойства = QProperty.
Они определены в H файле виджета.
У них указывается их тип, а так же функции для чтения и изменения пропертей.
Вот у комбобокса есть функция для чтения. А вот для записи нет :)

Правда я ни разу не пытался заменить/добавить проперти в стандартные виджеты. Потому и не могу сказать, разрешено это иль нет :)


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Март 02, 2013, 10:08

Тролль он
Люблю фокспро - язык для работы с собственной БД. В нём всё для этого реализовано, и всё так просто. Ненавижу все остальные языки в которых всё то, что есть в фокспро "искаропки" нужно долго и нудно реализовывать самому.
Тролль офф


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Bepec от Март 02, 2013, 16:29
Это называется узко ориентированными языками :)

С++ позволяет сделать всё и немножечко больше. А Qt лишь библиотека С++.

PS напишите свой маппер для работы с БД.


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Пытон от Март 03, 2013, 17:56
Это называется узко ориентированными языками :)

А именно это мне и нужно.


Название: Re: QComboBox + mapper сохраняет в таблицу индекс записи вместо значения из записи.
Отправлено: Bepec от Март 03, 2013, 21:01
Так напишите под себя ) С++ это как раз и позволяет сделать :D