class BookEditForm(QScrollArea, Ui_BookForm): """ Interface for book edit """ column = { 'id':0, 'barcode':1, 'title':2, 'author':3, 's_author':4, 'publisher':5, 'year':6, 'price':7, 'description':8, 'stock':9, 'image':10, 'availability':11 } IMG_SIZE = (150, 150) def __init__(self, record_id, parent=None): super(BookEditForm, self).__init__(parent) self.setupUi(self) # had to subclass this spinbox to support return grabbing self.edYear = ReturnKeySpinBox(self) self.edYearHolder.addWidget(self.edYear) # configuring id's for radio group self.radioAvailability.setId(self.rdSell,0) self.radioAvailability.setId(self.rdRent,1) self.radioAvailability.setId(self.rdInactive,2) # overlaying a clean button over the image (couldn't do it in designer) self.btnCleanImage = QPushButton() self.btnCleanImage.setIcon(QIcon(":icons/clean.png")) self.btnCleanImage.setFixedWidth(35) self.btnCleanImage.clicked.connect(self.clear_image) self.btnCleanImage.setVisible(False) clean_img_layout = QVBoxLayout(self.edImage) clean_img_layout.addWidget(self.btnCleanImage) clean_img_layout.setAlignment(Qt.AlignTop | Qt.AlignLeft) clean_img_layout.setContentsMargins(2,2,0,0) self._access = statics.access_level # for currency formatting self._locale = QLocale() self._record_id = record_id # had to hardcode these, wouldn't work otherwise: self.contentsLayout.setAlignment(self.groupBox, QtCore.Qt.AlignTop) self.contentsLayout.setAlignment(self.groupBox_2, QtCore.Qt.AlignTop) self.log = logging.getLogger('BookEditForm') self._subject_list = [] self._removed_subjects = [] self._added_subjects = [] self.setup_model() self.fill_form() self.setup_fields() self._old_data = self.extract_input(text_only=True) # flag to indicate whether there were changes to the fields self._dirty = False # user did input an image self._image_set = False # image changed during edit self._image_changed = False def is_dirty(self): return self._dirty def setup_fields(self): """ setting up validators and stuff """ # validators # forcing uppercasing on these fields self.edTitle.setValidator(UppercaseValidator()) self.edAuthor.setValidator(UppercaseValidator()) self.edSAuthor.setValidator(UppercaseValidator()) self.edPublisher.setValidator(UppercaseValidator()) self.edPrice.setValidator(CurrencyValidator(self.edPrice)) self.edBarcode.setValidator(NumericValidator()) self.edYear.setMinimum(1900) self.edYear.setMaximum(date.today().year) self.edYear.setValue(date.today().year) # fixing tab order self.setTabOrder(self.edPublisher, self.edYear) self.setTabOrder(self.edYear, self.edPrice) # connecting return key to tab lineEditList = self.findChildren(QLineEdit) for lineEdit in lineEditList: # had some problem with C++ originated objects if lineEdit.objectName() not in ['qt_spinbox_lineedit', 'edSubject']: lineEdit.returnPressed.connect(lineEdit.focusNextChild) # detect changes on line edits lineEdit.textChanged.connect(self.check_changes) # different behaviour for these self.edBarcode.textChanged.connect(self.check_barcode) self.edSubject.returnPressed.connect(self.on_btnAddSubject_clicked) # completers config_completer(self.edSubject, self._subject_model, "name") config_completer(self.edAuthor, self._author_model, "name") config_completer(self.edSAuthor, self._s_author_model, "name") config_completer(self.edPublisher, self._publisher_model, "name") # making image clickable clickable(self.edImage).connect(self.handle_image) def fill_form(self): # retrieving book info self.edBarcode.setText(self._record.value("barcode")) self.edTitle.setText(self._record.value("title")) self.edYear.setValue(self._record.value("year")) self.edDescription.setPlainText(self._record.value("description")) self.radioAvailability.button(self._record.value("availability")).setChecked(True) # retrieving image ba = QByteArray(self._record.value("image")) if ba: self._image_set = True img = qbytearray_to_qimage(ba) self.set_image(img, clean_visible=True) # currency # TODO: ARRUMAR self.edPrice.setText(self._locale.toString(self._record.value("price"), 'f', 2).replace('.','')) # qcompleter fields self.edAuthor.setText(self._get_name_from_id("author", self._record.value("author_id"))) self.edSAuthor.setText(self._get_name_from_id("s_author", self._record.value("s_author_id"))) self.edPublisher.setText(self._get_name_from_id("publisher", self._record.value("publisher_id"))) # retrieving subjects for subj_record in self._subj_records: self.add_subject([subj_record.value("id"),subj_record.value("name")]) # clearing changes self._added_subjects[:] = [] self.refresh_tableSubjects() def clear_image(self): img = QImage(":icons/no_image.png") self.set_image(img, clean_visible=False) if self._image_set: self._image_set = False self._image_changed = True def handle_image(self): image_path = QFileDialog.getOpenFileName(self, "Escolha uma imagem", os.getenv("HOME"), "Imagens (*.png, *.jpg *.bmp)")[0] if os.path.exists(image_path): self.set_image(QImage(image_path), clean_visible=True) self._image_set = True self._image_changed = True def set_image(self, img, clean_visible=False): pix = QPixmap.fromImage(img) pix = pix.scaled(self.IMG_SIZE[0], self.IMG_SIZE[1], Qt.KeepAspectRatio) self.edImage.setPixmap(pix) self.edImage.setScaledContents(True) self.btnCleanImage.setVisible(clean_visible) def check_changes(self, txt): # getting sender info sender = self.sender().objectName().split('ed')[1].lower() if sender != 'subject' and self._old_data[sender] != txt: self._dirty = True def check_barcode(self, txt): if len(txt) == self.edBarcode.maxLength(): self.edBarcode.focusNextChild() def extract_input(self, text_only=False): data = {} data['barcode'] = self.edBarcode.text() data['title'] = self.edTitle.text() # completer fields for c_field, line_edit in [("author", self.edAuthor), ("s_author", self.edSAuthor), ("publisher", self.edPublisher)]: if not text_only: data[c_field] = self._get_cfield_value(c_field, line_edit.text()) else: data[c_field] = line_edit.text() data['year'] = self.edYear.value() data['price'] = self._locale.toDouble(self.edPrice.text())[0] data['description'] = self.edDescription.toPlainText() if not text_only and self._image_changed and self._image_set: data['image'] = qpixmap_to_qbytearray(self.edImage.pixmap()) data['availability'] = self.radioAvailability.checkedId() return data def setup_model(self): db = Db_Instance("edit_book").get_instance() if not db.open(): self.log.error(db.lastError().text()) message = unicode("Erro de conexão\n\n""Banco de dados indisponível".decode('utf-8')) QMessageBox.critical(self, unicode("Seareiros - Edição de Livro".decode('utf-8')), message) else: # book self._model = QSqlRelationalTableModel(self, db=db) self._model.setTable("book") self._model.setFilter("id = " + str(self._record_id)) # self._model.setRelation(self.column["author"], QSqlRelation("author", "id", "name")) # self._model.setRelation(self.column["s_author"], QSqlRelation("s_author", "id", "name")) # self._model.setRelation(self.column["publisher"], QSqlRelation("publisher", "id", "name")) self._model.select() self._record = self._model.record(0) # models for setting up qcompleters: # book_in_subject self._book_in_subj_model = QSqlTableModel(self, db=db) self._book_in_subj_model.setTable("book_in_subject") # subject self._subject_model = QSqlTableModel(self, db=db) self._subject_model.setTable("subject") self._subject_model.select() # author self._author_model = QSqlTableModel(self, db=db) self._author_model.setTable("author") self._author_model.select() # s_author self._s_author_model = QSqlTableModel(self, db=db) self._s_author_model.setTable("s_author") self._s_author_model.select() # publisher self._publisher_model = QSqlTableModel(self, db=db) self._publisher_model.setTable("publisher") self._publisher_model.select() # retrieving current subjects, should probably place this elsewhere but it's related to models self._subject_records = [] sql_statement = """SELECT id, name FROM subject s, book_in_subject b_s WHERE s.id = b_s.subject_id AND b_s.book_id = %s """ % str(self._record_id) read_only_subject_model = QSqlQueryModel() read_only_subject_model.setQuery(sql_statement, db) # checking query validity if not read_only_subject_model.lastError().isValid(): self._subj_records = iterate_model(read_only_subject_model) def update_data(self): data = self.extract_input() # checking fields that aren't inserted yet for val, model in [('author', self._author_model), ('s_author', self._s_author_model), ('publisher', self._publisher_model)]: if isinstance(data[val], unicode): # needs to be inserted model.insertRow(0) model.setData(model.index(0,1), data[val]) data[val] = submit_and_get_id(self, model, self.log) if not data[val]: # won't proceed if this fails return False for key,val in data.items(): self._model.setData(self._model.index(0, self.column[key]), val) if 'image' not in data and self._image_changed: # user cleared the image ok = self._model.setData(self._model.index(0, self.column['image']), None) print ok # try to commit changes if not self._model.submitAll(): self.log.error(self._model.lastError().text()) message = unicode("Erro de transação\n\n""Não foi possível salvar no banco de dados".decode('utf-8')) QMessageBox.critical(self, "Seareiros - Edição de Livro", message) return False else: # updating subjects error = False # added subjects for subj in self._added_subjects: # the list has the format [id, text] for existing subjects or [None, text] otherwise if not subj[0]: # need to insert the subject before associating it with the book self._subject_model.insertRow(0) self._subject_model.setData(self._subject_model.index(0,1), subj[1]) subj[0] = submit_and_get_id(self, self._subject_model, self.log) if not subj[0]: error = True break # have a valid record id for the subject to be associated self._book_in_subj_model.insertRow(0) self._book_in_subj_model.setData(self._book_in_subj_model.index(0,0), self._record_id) self._book_in_subj_model.setData(self._book_in_subj_model.index(0,1), subj[0]) ok = self._book_in_subj_model.submitAll() if not ok: error = True self.log.error(self._book_in_subj_model.setLastError().text()) break # removed subjects for removed_id in self._removed_subjects: self._book_in_subj_model.setFilter("book_id = %s AND subject_id = %s" % (str(self._record_id),str(removed_id))) self._book_in_subj_model.select() self._book_in_subj_model.removeRow(0) if self._book_in_subj_model.lastError().isValid(): error = True self.log.error(self._book_in_subj_model.lastError().text()) break if not error: message = unicode("Sucesso!\n\n""O livro foi atualizado com êxito no banco de dados".decode('utf-8')) QMessageBox.information(self, unicode("Seareiros - Edição de Livro".decode('utf-8')), message) else: message = unicode("Erro\n\n""Associado alterado, " "porém ocorreu um problema ao salvar suas atividades".decode('utf-8')) QMessageBox.warning(self, unicode("Seareiros - Edição de Livro".decode('utf-8')), message) # if I don't set this flag here it'll trigger a warning for altering data on the form self._dirty = False return True def _get_id_from_name(self, table, name): db = Db_Instance(table + "_fetch_" + name + "_id").get_instance() if not db.open(): return None else: query = QSqlQuery(db) query.prepare("SELECT id FROM %s WHERE name = :name" % table) query.bindValue(":name", name) query.exec_() if query.next(): return query.record().value("id") else: return None def _get_name_from_id(self, table, id): db = Db_Instance(table + "_fetch_" + str(id) + "_name").get_instance() if not db.open(): return None else: query = QSqlQuery(db) query.prepare("SELECT name FROM %s WHERE id = :id" % table) query.bindValue(":name", id) query.exec_() if query.next(): return query.record().value("name") else: return None @QtCore.Slot() def on_btnAddSubject_clicked(self): txt = self.edSubject.text() if txt != '': id = self._get_id_from_name('subject', self.edSubject.text()) if id: # known register data = [id, txt] else: # new data data = [None, txt] not_a_duplicate = self.add_subject(data) if not_a_duplicate: self.refresh_tableSubjects() self.edSubject.clear() self.edSubject.setFocus() @QtCore.Slot() def on_btnCleanSubjects_clicked(self): self.clear_table() self.edSubject.setFocus() def clear_table(self): # can't directly change activity_list here itens = [i for i in self._subject_list] for item in itens: self.remove_subject(item) self._added_subjects[:] = [] def add_subject(self, data): """ adds a subject to the list except for duplicates """ if data in self._subject_list: return False else: if self.is_in_del_queue(data[0]): self._removed_subjects.remove(data[0]) else: self._added_subjects.append(data) self._subject_list.append(data) # sorts by name self._subject_list.sort(key=operator.itemgetter(1)) return True def refresh_tableSubjects(self): if len(self._subject_list) > 0: self.tableSubjects.setColumnCount(len(self._subject_list[0])+1) col_labels = ["", "Nome", ""] self.tableSubjects.setHorizontalHeaderLabels(col_labels) self.tableSubjects.setColumnHidden(0, True) else: self.tableSubjects.setColumnCount(0) self.tableSubjects.setRowCount(len(self._subject_list)) for i, row in enumerate(self._subject_list): for j, col in enumerate(row): item = QTableWidgetItem(col) self.tableSubjects.setItem(i, j, item) # icon to remove rows individually remove_icon = QIcon(":icons/conn_failed.png") remove_btn = QPushButton(remove_icon, "") remove_btn.clicked.connect(partial(self.remove_subject, subject=row)) self.tableSubjects.setCellWidget(i, len(row), remove_btn) self.tableSubjects.resizeColumnsToContents() self.tableSubjects.horizontalHeader().setResizeMode(1, QHeaderView.Stretch) def is_in_del_queue(self, id): return id in self._removed_subjects def is_in_add_queue(self, data): return data in self._added_subjects def remove_subject(self, subject): # remove a row based on its value self._subject_list.remove(subject) if self.is_in_add_queue(subject): # unqueue previously added activity self._added_subjects.remove(subject) else: id = subject[0] if id: self._removed_subjects.append(id) self.refresh_tableSubjects() def _get_cfield_value(self, c_field, text): if text == '': return None id = self._get_id_from_name(c_field, text) if id: return id else: return text
class AssociateAddForm(QScrollArea, Ui_AssociateForm): """ Interface for associate input """ column = { 'fullname':1, 'nickname':2, 'rg':3, 'cpf':4, 'maritalstatus':5, 'email':6, 'streetaddress':7, 'complement':8, 'district':9, 'province':10, 'city':11, 'cep':12, 'phoneres':13, 'phonecom':14, 'phonepriv':15, 'active':17 } def __init__(self, parent=None): super(AssociateAddForm, self).__init__(parent) self.setupUi(self) self._access = statics.access_level # configuring id's for radio group self.radioStatus.setId(self.rdActive,0) self.radioStatus.setId(self.rdInactive,1) # had to hardcode these, wouldn't work otherwise: self.contentsLayout.setAlignment(self.groupBox_5, QtCore.Qt.AlignTop) self.contentsLayout.setAlignment(self.groupBox, QtCore.Qt.AlignTop) self.contentsLayout.setAlignment(self.groupBox_2, QtCore.Qt.AlignTop) self.contentsLayout.setAlignment(self.groupBox_3, QtCore.Qt.AlignTop) self.contentsLayout.setAlignment(self.groupBox_4, QtCore.Qt.AlignTop) self.log = logging.getLogger('AssociateForm') self.setup_fields() self._activity_list = [] self.setup_model() # flag to indicate whether there were changes to the fields self._dirty = False def is_dirty(self): return self._dirty def setup_fields(self): # setting up validators and stuff self.edFullName.setValidator(UppercaseValidator()) self.edNickname.setValidator(UppercaseValidator()) self.edEmail.setValidator(EmailValidator()) self.edRG.setValidator(AlphaNumericValidator()) lineEditList = self.findChildren(QLineEdit) comboBoxList = self.findChildren(QComboBox) for lineEdit in lineEditList: lineEdit.returnPressed.connect(lineEdit.focusNextChild) # detect changes on line edits lineEdit.textChanged.connect(self.check_changes) for comboBox in comboBoxList: comboBox.activated.connect(comboBox.focusNextChild) def check_changes(self, txt): if txt != '': self._dirty = True def setup_model(self): db = Db_Instance("form_associate").get_instance() if not db.open(): self.log.error(db.lastError().text()) message = unicode("Erro de conexão\n\n""Banco de dados indisponível".decode('utf-8')) QMessageBox.critical(self, "Seareiros - Cadastro de Associado", message) else: self._model = QSqlRelationalTableModel(self, db=db) self._model.setTable("associate") self._act_model = QSqlRelationalTableModel(self, db=db) self._act_model.setTable("associate_in_activity") def submit_data(self): self._model.insertRow(0) data = self.extract_input() for key,val in data.items(): self._model.setData(self._model.index(0, self.column[key]), val) # try to commit a record if not self._model.submitAll(): self.log.error(self._model.lastError().text()) message = unicode("Erro de transação\n\n""Não foi possível salvar no banco de dados".decode('utf-8')) QMessageBox.critical(self, "Seareiros - Cadastro de Atividade", message) return False else: # successfully added an associate, adding it's activities activities = self.extract_activities_input() error = False if len(activities) > 0: associate_id = self.get_added_record().value("id") for id in activities: self._act_model.insertRow(0) self._act_model.setData(self._act_model.index(0, 0), associate_id) self._act_model.setData(self._act_model.index(0, 1), id) ok = self._act_model.submitAll() if not ok: error = True self.log.error(self._act_model.lastError().text()) if not error: message = unicode("Sucesso!\n\n""O associado foi salvo com êxito no banco de dados".decode('utf-8')) QMessageBox.information(self, "Seareiros - Cadastro de Associado", message) else: message = unicode("Erro\n\n""Associado cadastrado, " "porém ocorreu um problema ao salvar suas atividades".decode('utf-8')) QMessageBox.warning(self, "Seareiros - Cadastro de Associado", message) return True def clear(self): self._dirty = False lineEditList = self.findChildren(QLineEdit) for lineEdit in lineEditList: lineEdit.clear() self.comboMaritalStatus.setCurrentIndex(0) self.comboProvince.setCurrentIndex(24) self.clear_table() self.edFullName.setFocus() @QtCore.Slot() def on_btnAddActivity_clicked(self): activity_dialog = ActivitySelectDialog() if activity_dialog.exec_() == QDialog.Accepted: record = activity_dialog.get_record() not_a_duplicate = self.add_activity(record) if not_a_duplicate: self.refresh_tableActivities() @QtCore.Slot() def on_btnCleanActivities_clicked(self): self.clear_table() def clear_table(self): self._activity_list = [] self.tableActivities.clear() self.refresh_tableActivities() def refresh_tableActivities(self): if len(self._activity_list) > 0: self.tableActivities.setColumnCount(len(self._activity_list[0])+1) col_labels = ["", unicode("Descrição".decode("utf-8")), "Sala", "Dia", unicode("Horário".decode("utf-8")),""] self.tableActivities.setHorizontalHeaderLabels(col_labels) self.tableActivities.setColumnHidden(0, True) else: self.tableActivities.setColumnCount(0) self.tableActivities.setRowCount(len(self._activity_list)) for i, row in enumerate(self._activity_list): for j, col in enumerate(row): # custom sorting for weekdays if j == 3: item = WeekdayTableWidgetItem(col) else: item = QTableWidgetItem(col) self.tableActivities.setItem(i, j, item) # icon to remove rows individually remove_icon = QIcon(":icons/conn_failed.png") remove_btn = QPushButton(remove_icon, "") remove_btn.clicked.connect(partial(self.remove_activity, activity=row)) self.tableActivities.setCellWidget(i, len(row), remove_btn) self.tableActivities.resizeColumnsToContents() self.tableActivities.horizontalHeader().setResizeMode(1, QHeaderView.Stretch) def add_activity(self, record): """ adds an activity to the list except for duplicates """ # this brings shame to me but meh, faster to hardcode (see model_activity) # id = str(record.value("id")) id = record.value("id") room = record.value("room") if room == 0: room = "Nenhuma" else: room = str(room) weekday = constants.days_of_the_week[record.value("weekday")] weektime = record.value("weektime").toString("HH:mm") entry = (id, record.value("description"), room, weekday, weektime) if entry in self._activity_list: return False else: self._activity_list.append(entry) # sorts by day/time self._activity_list.sort(key=operator.itemgetter(3,4)) # self._activity_list = sorted(self._activity_list, key=lambda dia_hora: (dia_hora[3], dia_hora[2])) return True def remove_activity(self, activity): # remove a row based on its value self._activity_list.remove(activity) self.refresh_tableActivities() def extract_input(self): data = {} data['fullname'] = self.edFullName.text() data['nickname'] = self.edNickname.text() data['rg'] = self.edRG.text() data['cpf'] = self.remove_mask_when_empty(self.edCPF.text()) data['maritalstatus'] = self.comboMaritalStatus.currentIndex() data['email'] = self.edEmail.text() data['streetaddress'] = self.edStreet.text() data['complement'] = self.edComplement.text() data['district'] = self.edDistrict.text() data['province'] = self.comboProvince.currentIndex() data['city'] = self.edCity.text() data['cep'] = self.remove_mask_when_empty(self.edCEP.text()) data['phoneres'] = self.remove_mask_when_empty(self.edPhoneRes.text()) data['phonecom'] = self.remove_mask_when_empty(self.edPhoneCom.text()) data['phonepriv'] = self.remove_mask_when_empty(self.edPhonePriv.text()) if self.radioStatus.checkedId() == 0: data['active'] = True else: data['active'] = False return data def remove_mask_when_empty(self, text): """ I don't want to save a mask when there's no user input """ if text in ['()-', '.-', '..-']: return '' else: return text def extract_activities_input(self): # grab id of selected activities activity_id_list = [] for act in self._activity_list: activity_id_list.append(act[0]) return activity_id_list def get_added_record(self): """ My workaround to get the last inserted id without any postgres specific queries """ db = Db_Instance("add_associate_last_id").get_instance() if not db.open(): return None else: query = QSqlQuery(db) query.prepare("SELECT * FROM associate WHERE fullname = :fullname") query.bindValue(":fullname", self.edFullName.text()) query.exec_() if query.next(): return query.record() else: return None
class AssociateEditForm(QScrollArea, Ui_AssociateForm): """ Interface for associate edit """ column = { 'id':0, 'fullname':1, 'nickname':2, 'rg':3, 'cpf':4, 'maritalstatus':5, 'email':6, 'streetaddress':7, 'complement':8, 'district':9, 'province':10, 'city':11, 'cep':12, 'phoneres':13, 'phonecom':14, 'phonepriv':15, 'active':17 } def __init__(self, record_id, parent=None): super(AssociateEditForm, self).__init__(parent) self.setupUi(self) self._access = statics.access_level self._record_id = record_id # configuring id's for radio group self.radioStatus.setId(self.rdActive,0) self.radioStatus.setId(self.rdInactive,1) # had to hardcode these, wouldn't work otherwise: self.contentsLayout.setAlignment(self.groupBox, QtCore.Qt.AlignTop) self.contentsLayout.setAlignment(self.groupBox_2, QtCore.Qt.AlignTop) self.contentsLayout.setAlignment(self.groupBox_3, QtCore.Qt.AlignTop) self.contentsLayout.setAlignment(self.groupBox_4, QtCore.Qt.AlignTop) self.log = logging.getLogger('AssociateEditForm') self._activity_list = [] self._removed_activities = [] self._added_activities = [] self.setup_model() self.fill_form() self._old_data = self.extract_input() self.setup_fields() # flag to indicate whether there were changes to the fields self._dirty = False def is_dirty(self): return self._dirty def setup_fields(self): # tries to make things easier for user by allowing him to tab with return key # or after selecting something from a combobox self.edFullName.setValidator(UppercaseValidator()) self.edNickname.setValidator(UppercaseValidator()) self.edEmail.setValidator(EmailValidator()) self.edRG.setValidator(AlphaNumericValidator()) lineEditList = self.findChildren(QLineEdit) comboBoxList = self.findChildren(QComboBox) for lineEdit in lineEditList: lineEdit.returnPressed.connect(lineEdit.focusNextChild) # detect changes to the line edits lineEdit.textChanged.connect(self.check_changes) for comboBox in comboBoxList: comboBox.activated.connect(comboBox.focusNextChild) def fill_form(self): # retrieving associate info self.edFullName.setText(self._record.value(1)) self.edNickname.setText(self._record.value(2)) self.edRG.setText(self._record.value(3)) self.edCPF.setText(self._record.value(4)) self.comboMaritalStatus.setCurrentIndex(self._record.value(5)) self.edEmail.setText(self._record.value(6)) self.edStreet.setText(self._record.value(7)) self.edComplement.setText(self._record.value(8)) self.edDistrict.setText(self._record.value(9)) self.comboProvince.setCurrentIndex(self._record.value(10)) self.edCity.setText(self._record.value(11)) self.edCEP.setText(self._record.value(12)) self.edPhoneRes.setText(self._record.value(13)) self.edPhoneCom.setText(self._record.value(14)) self.edPhonePriv.setText(self._record.value(15)) if self._record.value("active"): self.rdActive.setChecked(True) else: self.rdInactive.setChecked(True) # retrieving associate activities for act_record in self._activity_records: self.add_activity(act_record) # clearing changes self._added_activities[:] = [] self.refresh_tableActivities() def check_changes(self, txt): # getting sender info sender = self.sender().objectName().split('ed')[1].lower() if self._old_data[sender] != txt: self._dirty = True def check_activities_changes(self): if self._added_activities and self._removed_activities: return False else: return True def extract_input(self): data = {} data['fullname'] = self.edFullName.text() data['nickname'] = self.edNickname.text() data['rg'] = self.edRG.text() data['cpf'] = self.remove_mask_when_empty(self.edCPF.text()) data['maritalstatus'] = self.comboMaritalStatus.currentIndex() data['email'] = self.edEmail.text() data['streetaddress'] = self.edStreet.text() data['complement'] = self.edComplement.text() data['district'] = self.edDistrict.text() data['province'] = self.comboProvince.currentIndex() data['city'] = self.edCity.text() data['cep'] = self.remove_mask_when_empty(self.edCEP.text()) data['phoneres'] = self.remove_mask_when_empty(self.edPhoneRes.text()) data['phonecom'] = self.remove_mask_when_empty(self.edPhoneCom.text()) data['phonepriv'] = self.remove_mask_when_empty(self.edPhonePriv.text()) if self.radioStatus.checkedId() == 0: data['active'] = True else: data['active'] = False return data def setup_model(self): db = Db_Instance("edit_associate").get_instance() if not db.open(): self.log.error(db.lastError().text()) message = unicode("Erro de conexão\n\n""Banco de dados indisponível".decode('utf-8')) QMessageBox.critical(self, unicode("Seareiros - Edição de Associado".decode('utf-8')), message) else: self._model = QSqlRelationalTableModel(self, db=db) self._model.setTable("associate") self._act_model = QSqlRelationalTableModel(self, db=db) self._act_model.setTable("associate_in_activity") # TODO: Maybe I should validate these # associate self._model.setFilter("id = " + str(self._record_id)) self._model.select() self._record = self._model.record(0) # activities self._activity_records = [] sql_statement = """SELECT id, description, room, weekday, weektime FROM activity a, associate_in_activity a_a WHERE a.id = a_a.id_activity AND a_a.id_associate = %s """ % str(self._record_id) model_activities = QSqlQueryModel() model_activities.setQuery(sql_statement, db) # checking query validity if not model_activities.lastError().isValid(): self._activity_records = iterate_model(model_activities) def update_data(self): data = self.extract_input() for key,val in data.items(): self._model.setData(self._model.index(0, self.column[key]), val) # try to commit changes if not self._model.submitAll(): self.log.error(self._model.lastError().text()) message = unicode("Erro de transação\n\n""Não foi possível salvar no banco de dados".decode('utf-8')) QMessageBox.critical(self, "Seareiros - Cadastro de Atividade", message) return False else: # updating activities error = False for added_id in self._added_activities: self._act_model.insertRow(0) self._act_model.setData(self._act_model.index(0, 0), self._record_id) self._act_model.setData(self._act_model.index(0, 1), added_id) ok = self._act_model.submitAll() if not ok: error = True self.log.error(self._act_model.lastError().text()) break # TODO: raise some kind of exception here for removed_id in self._removed_activities: self._act_model.setFilter("id_associate = %s AND id_activity = %s" % (str(self._record_id), str(removed_id))) self._act_model.select() self._act_model.removeRow(0) if self._act_model.lastError().isValid(): error = True self.log.error(self._act_model.lastError().text()) break if not error: message = unicode("Sucesso!\n\n""O associado foi atualizado com êxito no banco de dados".decode('utf-8')) QMessageBox.information(self, unicode("Seareiros - Edição de Associado".decode('utf-8')), message) else: message = unicode("Erro\n\n""Associado alterado, " "porém ocorreu um problema ao atualizar suas atividades".decode('utf-8')) QMessageBox.warning(self, unicode("Seareiros - Edição de Associado".decode('utf-8')), message) # if I don't set this flag here it'll trigger a warning for altering data on the form self._dirty = False return True @QtCore.Slot() def on_btnAddActivity_clicked(self): activity_dialog = ActivitySelectDialog() if activity_dialog.exec_() == QDialog.Accepted: record = activity_dialog.get_record() not_a_duplicate = self.add_activity(record) if not_a_duplicate: self.refresh_tableActivities() @QtCore.Slot() def on_btnCleanActivities_clicked(self): self.clear_table() def clear_table(self): # can't directly change activity_list here itens = [i for i in self._activity_list] for item in itens: self.remove_activity(item) self._added_activities[:] = [] def refresh_tableActivities(self): if len(self._activity_list) > 0: self.tableActivities.setColumnCount(len(self._activity_list[0])+1) col_labels = ["", unicode("Descrição".decode("utf-8")), "Sala", "Dia", unicode("Horário".decode("utf-8")),""] self.tableActivities.setHorizontalHeaderLabels(col_labels) self.tableActivities.setColumnHidden(0, True) else: self.tableActivities.setColumnCount(0) self.tableActivities.setRowCount(len(self._activity_list)) for i, row in enumerate(self._activity_list): for j, col in enumerate(row): # custom sorting for weekdays if j == 3: item = WeekdayTableWidgetItem(col) else: item = QTableWidgetItem(col) self.tableActivities.setItem(i, j, item) # icon to remove rows individually remove_icon = QIcon(":icons/conn_failed.png") remove_btn = QPushButton(remove_icon, "") remove_btn.clicked.connect(partial(self.remove_activity, activity=row)) self.tableActivities.setCellWidget(i, len(row), remove_btn) self.tableActivities.resizeColumnsToContents() self.tableActivities.horizontalHeader().setResizeMode(1, QHeaderView.Stretch) def is_in_del_queue(self, record): return record in self._removed_activities def is_in_add_queue(self, record): return record in self._added_activities def add_activity(self, record): """ adds an activity to the list except for duplicates """ # this brings shame to me but meh, faster to hardcode (see model_activity) # id = str(record.value("id")) id = record.value("id") room = record.value("room") if room == 0: room = "Nenhuma" else: room = str(room) weekday = constants.days_of_the_week[record.value("weekday")] weektime = record.value("weektime").toString("HH:mm") entry = (id, record.value("description"), room, weekday, weektime) if entry in self._activity_list: return False else: if self.is_in_del_queue(id): # no real need to add as it was queued to be removed self._removed_activities.remove(id) else: self._added_activities.append(id) # print self._added_activities self._activity_list.append(entry) # sorts by day/time self._activity_list.sort(key=operator.itemgetter(3,4)) # self._activity_list = sorted(self._activity_list, key=lambda dia_hora: (dia_hora[3], dia_hora[2])) return True def remove_activity(self, activity): # remove a row based on its value self._activity_list.remove(activity) id = activity[0] if self.is_in_add_queue(id): # unqueue previously added activity self._added_activities.remove(id) else: self._removed_activities.append(id) self.refresh_tableActivities() def remove_mask_when_empty(self, text): """ I don't want to save a mask when there's no user input """ if text in ['()-', '.-', '..-']: return '' else: return text