コード例 #1
0
 def populate_combobox(self):
     self.connect_database()
     model = QSqlTableModel()
     model.setTable('products')
     column = model.fieldIndex('name')
     model.select()
     self.items_combobox.setModel(model)
     self.items_combobox.setModelColumn(column)
コード例 #2
0
ファイル: combo_box.py プロジェクト: mchal821/dars
class FilteringComboBox(QComboBox):
    """Combination of QCombobox and QLineEdit with autocompletionself.
    Line edit and completer model is taken from QSqlTable mod

    Args:
        table (str): db table name containing data for combobox
        column (str): column name containing data for combobox
    """
    def __init__(self, table, column, placeholderText, parent=None):
        super(FilteringComboBox, self).__init__(parent)
        self.parent = parent
        self.setEditable(True)
        self.setFocusPolicy(Qt.StrongFocus)
        self.setInsertPolicy(QComboBox.NoInsert)
        self.lineEdit().setPlaceholderText(placeholderText)

        # setup data model
        self._model = QSqlTableModel(self)
        self._model.setTable(table)
        self._model.select()
        col_num = self._model.fieldIndex(column)
        self._model.sort(col_num, Qt.AscendingOrder)
        self.setModel(self._model)
        self.setModelColumn(col_num)

        # setup completer
        self._proxy = QSortFilterProxyModel(self)
        self._proxy.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self._proxy.setSourceModel(self._model)
        self._proxy.setFilterKeyColumn(col_num)

        self._completer = QCompleter(self)
        self._completer.setModel(self._proxy)
        self._completer.setCompletionColumn(col_num)
        self._completer.activated.connect(self.onCompleterActivated)
        self._completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)
        self.setCompleter(self._completer)
        self.lineEdit().textEdited.connect(self._proxy.setFilterFixedString)

    @pyqtSlot(str)
    def onCompleterActivated(self, text):
        if not text:
            return

        self.setCurrentIndex(self.findText(text))
        self.activated[str].emit(self.currentText())

    def updateModel(self):
        self._model.select()
コード例 #3
0
class PropertiesService(QObject):
    controller = None  # type: PropertiesController
    question_type_model = None  # type: QSqlTableModel
    question_type_filter_proxy_model = None  # type: 'QuestionTypeFilterProxyModel'

    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.connect_slots()

    def connect_slots(self):
        application.app.aboutToQuit.connect(self.on_about_to_quit)

    def dispose(self):
        pass

    def show(self, tab_wanted: str = 'general'):
        from genial.services import document_service
        if document_service.database is not None:
            if self.question_type_model is None:
                self.question_type_model = QSqlTableModel(
                    self, document_service.database)
                self.question_type_model.setTable("question_type")
                self.question_type_model.setEditStrategy(
                    QSqlTableModel.OnManualSubmit)
                self.question_type_filter_proxy_model = QuestionTypeFilterProxyModel(
                )
                self.question_type_filter_proxy_model.setSourceModel(
                    self.question_type_model)
                self.question_type_filter_proxy_model.sort(
                    self.question_type_model.fieldIndex("position"),
                    Qt.AscendingOrder)
                self.question_type_filter_proxy_model.setDynamicSortFilter(
                    True)

        if self.controller is None:
            self.controller = PropertiesController()
            self.controller.start()
        self.controller.show(tab_wanted)

    @pyqtSlot()
    def on_about_to_quit(self):
        self.dispose()
コード例 #4
0
class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.table = QTableView()

        self.model = QSqlTableModel(db=db)

        self.table.setModel(self.model)

        # tag::sortTable[]
        self.model.setTable("Track")
        # idx = self.model.fieldIndex("Milliseconds")
        # self.model.setSort(idx, Qt.DescendingOrder)
        # self.model.setHeaderData(1, Qt.Horizontal, "Name")
        # self.model.setHeaderData(2, Qt.Horizontal, "Album (ID)")
        # self.model.setHeaderData(3, Qt.Horizontal, "Media Type (ID)")
        # self.model.setHeaderData(4, Qt.Horizontal, "Genre (ID)")
        # self.model.setHeaderData(5, Qt.Horizontal, "Composer")

        column_titles = {
            "Name": "Name",
            "AlbumId": "Album (ID)",
            "MediaTypeId": "Media Type (ID)",
            "GenreId": "Genre (ID)",
            "Composer": "Composer",
        }

        for n, t in column_titles.items():
            idx = self.model.fieldIndex(n)
            self.model.setHeaderData(idx, Qt.Horizontal, t)

        self.model.select()
        # end::sortTable[]

        # self.model.setTable("Track")
        # self.model.select()

        self.setMinimumSize(QSize(1024, 600))
        self.setCentralWidget(self.table)
コード例 #5
0
ファイル: opePecheFiltrage.py プロジェクト: CyprienAn/Gedopi
class Filtrage_peche_dialog(QDialog, Ui_dlgPecheRechercheForm):
    '''
    Class de la fenêtre permettant le filtrage attributaire des inventaires de reproduction

    :param QDialog: Permet d'afficher l'interface graphique comme une fenêtre indépendante
    :type QDialog: QDialog

    :param Ui_dlgPecheRechercheForm: Class du script de l'interface graphique du formulaire,
            apporte les éléments de l'interface
    :type Ui_dlgPecheRechercheForm: class
    '''
    def __init__(self, db, dbType, dbSchema, modelPeche, parent=None):
        '''
        Constructeur, récupération de variable, connection des événements et remplissage des combobox

        :param db: définie dans le setupModel(),
                représente la connexion avec la base de données
        :type db: QSqlDatabase

        :param dbType: type de la base de données (postgre)
        :type dbType: str

        :param dbSchema: nom du schéma sous PostgreSQL contenant les données (data)
        :type dbSchema: unicode

        :param modelPeche: modèle pêche qui contient les données de la base de données
        :type modelPeche: QSqlRelationalTableModel

        :param parent: défini que cette fenêtre n'hérite pas d'autres widgets
        :type parent: NoneType
        '''
        super(Filtrage_peche_dialog, self).__init__(parent)
        self.db = db
        self.dbType = dbType
        self.dbSchema = dbSchema
        self.modelPeche = modelPeche
        self.setupUi(self)

        self.btnAnnuler.clicked.connect(self.reject)
        self.btnExec.clicked.connect(self.execution)
        self.btnRaz.clicked.connect(self.raz)
        self.btnEt.clicked.connect(self.et)
        self.btnOu.clicked.connect(self.ou)

        self.btnPrevisualiser.clicked.connect(self.previSql)

        self.btnCode.clicked.connect(self.ajoutCode)
        self.btnId.clicked.connect(self.ajoutId)
        self.btnPdpg.clicked.connect(self.ajoutPdpg)
        self.btnDate.clicked.connect(self.ajoutDate)
        self.btnRiviere.clicked.connect(self.ajoutRiviere)
        self.btnAappma.clicked.connect(self.ajoutAappma)
        self.btnMeau.clicked.connect(self.ajoutMeau)
        self.btnMotif.clicked.connect(self.ajoutMotif)

        self.btnEt.setEnabled(False)
        self.btnOu.setEnabled(False)

        self.aappmaBool = False
        self.motifBool = False
        self.pdpgBool = False
        self.ceauBool = False
        self.meauBool = False
        self.anneeBool = False

        self.wwhere = ""

        self.modelPdpg = QSqlTableModel(self, self.db)
        wrelation = "contexte_pdpg"
        if self.dbType == "postgres":
            wrelation = self.dbSchema + "." + wrelation
        self.modelPdpg.setTable(wrelation)
        self.modelPdpg.setSort(2, Qt.AscendingOrder)
        if (not self.modelPdpg.select()):
            QMessageBox.critical(self, u"Remplissage du modèle PDPG",
                                 self.modelPdpg.lastError().text(),
                                 QMessageBox.Ok)
        self.cmbPdpg.setModel(self.modelPdpg)
        self.cmbPdpg.setModelColumn(self.modelPdpg.fieldIndex("pdpg_nom"))

        self.modelAappma = QSqlTableModel(self, self.db)
        wrelation = "aappma"
        if self.dbType == "postgres":
            wrelation = self.dbSchema + "." + wrelation
        self.modelAappma.setTable(wrelation)
        self.modelAappma.setSort(1, Qt.AscendingOrder)
        if (not self.modelAappma.select()):
            QMessageBox.critical(self, u"Remplissage du modèle AAPPMA",
                                 self.modelAappma.lastError().text(),
                                 QMessageBox.Ok)
        self.cmbAappma.setModel(self.modelAappma)
        self.cmbAappma.setModelColumn(self.modelAappma.fieldIndex("apma_nom"))

        self.modelRiviere = QSqlTableModel(self, self.db)
        wrelation = "cours_eau"
        if self.dbType == "postgres":
            wrelation = self.dbSchema + "." + wrelation
        self.modelRiviere.setTable(wrelation)
        self.modelRiviere.setFilter("ceau_nom <> 'NR'")
        self.modelRiviere.setSort(2, Qt.AscendingOrder)
        if (not self.modelRiviere.select()):
            QMessageBox.critical(self, u"Remplissage du modèle Rivière",
                                 self.modelRiviere.lastError().text(),
                                 QMessageBox.Ok)
        self.cmbRiviere.setModel(self.modelRiviere)
        self.cmbRiviere.setModelColumn(
            self.modelRiviere.fieldIndex("ceau_nom"))

        self.modelMeau = QSqlQueryModel(self)
        wrelation = "masse_eau"
        if self.dbType == "postgres":
            wrelation = self.dbSchema + "." + wrelation
        self.modelMeau.setQuery(
            "select meau_code, meau_code || ' ; ' || meau_nom from " +
            wrelation + " order by meau_code;", self.db)
        if self.modelMeau.lastError().isValid():
            QMessageBox.critical(self, u"Remplissage du modèle Masse d'eau",
                                 self.modelMeau.lastError().text(),
                                 QMessageBox.Ok)
        self.cmbMeau.setModel(self.modelMeau)
        self.cmbMeau.setModelColumn(1)

        self.ModelMotif = QSqlTableModel(self, self.db)
        wrelation = "motif_peche"
        if self.dbType == "postgres":
            wrelation = self.dbSchema + "." + wrelation
        self.ModelMotif.setTable(wrelation)
        self.ModelMotif.setSort(1, Qt.AscendingOrder)
        if (not self.ModelMotif.select()):
            QMessageBox.critical(self, u"Remplissage du modèle Motif",
                                 self.ModelMotif.lastError().text(),
                                 QMessageBox.Ok)
        self.cmbMotif.setModel(self.ModelMotif)
        self.cmbMotif.setModelColumn(self.ModelMotif.fieldIndex("mope_motif"))

    def reject(self):
        '''Ferme la fenêtre si clic sur le bouton annuler'''

        QDialog.reject(self)

    def raz(self):
        '''Réinitialise toutes les variables de la fenêtre afin de recommencer une nouvelle requête'''

        self.spnId.setValue(0)
        self.wrq = ""
        self.txtSql.setText("")
        self.wwhere = ""
        self.datePeche.setDate(QDate(2000, 1, 1))

        self.btnEt.setEnabled(False)
        self.btnOu.setEnabled(False)

        self.btnCode.setEnabled(True)
        self.btnId.setEnabled(True)
        self.btnPdpg.setEnabled(True)
        self.btnDate.setEnabled(True)
        self.btnRiviere.setEnabled(True)
        self.btnAappma.setEnabled(True)
        self.btnMeau.setEnabled(True)
        self.btnMotif.setEnabled(True)

        self.aappmaBool = False
        self.motifBool = False
        self.pdpgBool = False
        self.ceauBool = False
        self.meauBool = False
        self.anneeBool = False

    def et(self):
        '''Change l'état des boutons et ajoute "and" à la requête'''

        self.btnEt.setEnabled(False)
        self.btnOu.setEnabled(False)

        self.btnCode.setEnabled(True)
        self.btnId.setEnabled(True)

        if self.aappmaBool == False:
            self.btnAappma.setEnabled(True)

        if self.motifBool == False:
            self.btnMotif.setEnabled(True)

        if self.pdpgBool == False:
            self.btnPdpg.setEnabled(True)

        if self.ceauBool == False:
            self.btnRiviere.setEnabled(True)

        if self.meauBool == False:
            self.btnMeau.setEnabled(True)

        if self.anneeBool == False:
            self.btnDate.setEnabled(True)

        self.wwhere += " AND "

    def ou(self):
        '''Change l'état des boutons et ajoute "or" à la requête'''

        self.btnEt.setEnabled(False)
        self.btnOu.setEnabled(False)

        self.btnCode.setEnabled(True)
        self.btnId.setEnabled(True)
        self.btnPdpg.setEnabled(True)
        self.btnDate.setEnabled(True)
        self.btnRiviere.setEnabled(True)
        self.btnAappma.setEnabled(True)
        self.btnMeau.setEnabled(True)
        self.btnMotif.setEnabled(True)

        self.aappmaBool = False
        self.motifBool = False
        self.pdpgBool = False
        self.ceauBool = False
        self.meauBool = False
        self.anneeBool = False

        self.wwhere += " OR "

    def ajoutCode(self):
        '''Change l'état des boutons et ajoute un critère de code opération à la requête'''

        self.btnOu.setEnabled(True)

        self.btnCode.setEnabled(False)
        self.btnId.setEnabled(False)
        self.btnPdpg.setEnabled(False)
        self.btnDate.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnMeau.setEnabled(False)
        self.btnMotif.setEnabled(False)

        self.wcode = self.leCodeOpe.text()
        if self.leCodeOpe.text() != "":
            if self.wcode != "":
                self.wwhere += "opep_ope_code ilike '%" + self.wcode + "%'"
        self.leCodeOpe.setText("")
        self.leCodeOpe.setFocus()

    def ajoutId(self):
        '''Change l'état des boutons et ajoute un critère d'id à la requête'''

        self.btnOu.setEnabled(True)

        self.btnCode.setEnabled(False)
        self.btnId.setEnabled(False)
        self.btnPdpg.setEnabled(False)
        self.btnDate.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnMeau.setEnabled(False)
        self.btnMotif.setEnabled(False)

        self.wid = self.spnId.value()
        if self.spnId.value() != "":
            if self.wid != "":
                self.wwhere += "opep_id = '" + str(self.wid) + "'"
        self.spnId.setValue(0)
        self.spnId.setFocus()

    def ajoutDate(self):
        '''Change l'état des boutons et ajoute un critère de date à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.anneeBool = True

        self.btnCode.setEnabled(False)
        self.btnId.setEnabled(False)
        self.btnPdpg.setEnabled(False)
        self.btnDate.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnMeau.setEnabled(False)
        self.btnMotif.setEnabled(False)

        self.wopep_date = self.datePeche.date().toString("yyyy")
        if self.wopep_date != "":
            self.wwhere += "date_part('year', opep_date) = '" + self.wopep_date + "'"

    def ajoutPdpg(self):
        '''Change l'état des boutons et ajoute un critère de pdpg à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnCode.setEnabled(False)
        self.btnId.setEnabled(False)
        self.btnPdpg.setEnabled(False)
        self.btnDate.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnMeau.setEnabled(False)
        self.btnMotif.setEnabled(False)

        self.pdpgBool = True

        wfromOperation = "operation"
        wfromStation = "station"
        wfromPeche = "ope_peche_elec"
        if self.dbType == "postgres":
            self.wfromPdpg = self.dbSchema + "." + wfromPeche + ", " + self.dbSchema + "." + wfromOperation + ", " + self.dbSchema + "." + wfromStation

        wrecord = self.cmbPdpg.model().record(self.cmbPdpg.currentIndex())
        self.wsta_pdpg = wrecord.value(0)
        if self.cmbPdpg.currentText() != "":
            if self.wsta_pdpg != "":
                self.wwhere += " opep_id in (select distinct opep_id from " + self.wfromPdpg + " where (opep_ope_code = ope_code) and (ope_sta_id = sta_id) and sta_pdpg_id = '" + str(
                    self.wsta_pdpg) + "')"

    def ajoutRiviere(self):
        '''Change l'état des boutons et ajoute un critère de cours d'eau à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnCode.setEnabled(False)
        self.btnId.setEnabled(False)
        self.btnPdpg.setEnabled(False)
        self.btnDate.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnMeau.setEnabled(False)
        self.btnMotif.setEnabled(False)

        self.ceauBool = True

        wfromOperation = "operation"
        wfromStation = "station"
        wfromPeche = "ope_peche_elec"
        if self.dbType == "postgres":
            self.wfromCeau = self.dbSchema + "." + wfromPeche + ", " + self.dbSchema + "." + wfromOperation + ", " + self.dbSchema + "." + wfromStation

        wrecord = self.cmbRiviere.model().record(
            self.cmbRiviere.currentIndex())
        self.wsta_riviere = wrecord.value(0)
        if self.cmbRiviere.currentText() != "":
            if self.wsta_riviere != "":
                self.wwhere += " opep_id in (select distinct opep_id from " + self.wfromCeau + " where (opep_ope_code = ope_code) and (ope_sta_id = sta_id) and sta_ceau_id = '" + str(
                    self.wsta_riviere) + "')"

    def ajoutAappma(self):
        '''Change l'état des boutons et ajoute un critère d'AAPPMA à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnCode.setEnabled(False)
        self.btnId.setEnabled(False)
        self.btnPdpg.setEnabled(False)
        self.btnDate.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnMeau.setEnabled(False)
        self.btnMotif.setEnabled(False)

        self.aappmaBool = True

        wfromOperation = "operation"
        wfromStation = "station"
        wfromPeche = "ope_peche_elec"
        if self.dbType == "postgres":
            self.wfromAappma = self.dbSchema + "." + wfromPeche + ", " + self.dbSchema + "." + wfromOperation + ", " + self.dbSchema + "." + wfromStation

        wrecord = self.cmbAappma.model().record(self.cmbAappma.currentIndex())
        self.wsta_aappma = wrecord.value(0)
        if self.cmbAappma.currentText() != "":
            if self.wsta_aappma != "":
                self.wwhere += " opep_id in (select distinct opep_id from " + self.wfromAappma + " where (opep_ope_code = ope_code) and (ope_sta_id = sta_id) and sta_apma_id = '" + str(
                    self.wsta_aappma) + "')"

    def ajoutMeau(self):
        '''Change l'état des boutons et ajoute un critère de Masse d'eau à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnCode.setEnabled(False)
        self.btnId.setEnabled(False)
        self.btnPdpg.setEnabled(False)
        self.btnDate.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnMeau.setEnabled(False)
        self.btnMotif.setEnabled(False)

        self.meauBool = True

        wfromOperation = "operation"
        wfromStation = "station"
        wfromPeche = "ope_peche_elec"
        if self.dbType == "postgres":
            self.wfromMeau = self.dbSchema + "." + wfromPeche + ", " + self.dbSchema + "." + wfromOperation + ", " + self.dbSchema + "." + wfromStation

        wrecord = self.cmbMeau.model().record(self.cmbMeau.currentIndex())
        self.wsta_meau = wrecord.value(0)
        if self.cmbMeau.currentText() != "":
            if self.wsta_meau != "":
                self.wwhere += " opep_id in (select distinct opep_id from " + self.wfromMeau + " where (opep_ope_code = ope_code) and (ope_sta_id = sta_id) and sta_meau_code = '" + str(
                    self.wsta_meau) + "')"

    def ajoutMotif(self):
        '''Change l'état des boutons et ajoute un critère de motif de pêche à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnCode.setEnabled(False)
        self.btnId.setEnabled(False)
        self.btnPdpg.setEnabled(False)
        self.btnDate.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnMeau.setEnabled(False)
        self.btnMotif.setEnabled(False)

        self.motifBool = True

        wrecord = self.cmbMotif.model().record(self.cmbMotif.currentIndex())
        self.wopep_motif = wrecord.value(0)
        if self.cmbMotif.currentText() != "":
            if self.wopep_motif != "":
                self.wwhere += "opep_mope_id = '" + str(self.wopep_motif) + "'"

    def creaRequete(self):
        # def previSql(self):
        '''Regroupe les différentes variables contenant les clauses de la requête SQL et les concatène pour en faire une requête exécutable'''

        self.wrq = ""

        # Construit la clause FROM de la requête
        cfrom = "ope_peche_elec"
        if self.dbType == "postgres":
            cfrom = self.dbSchema + "." + cfrom

        # Construit la clause SELECT et ajoute la clause FROM à la requête
        self.wrq = "SELECT DISTINCT opep_id FROM " + cfrom

        # Construit la clause WHERE et ORDER BY et l'ajoute à la requête
        if self.wwhere != "":
            #Supprime l'opérateur "and" ou "or" si celui-ci n'est pas suivi d'un critère
            operateurs = ["AND", "OR"]
            fin_where = self.wwhere[-5:]
            for ext in operateurs:
                if ext in fin_where:
                    self.wwhere = self.wwhere[:-4]
            self.wrq += " WHERE " + self.wwhere + " ORDER BY opep_id"
        else:
            self.wrq += " ORDER BY opep_id"

    def previSql(self):
        '''Permet de prévisualiser la requête avant de l'éxecuter'''
        self.txtSql.setText("")
        self.creaRequete()

        # Affiche la requête
        self.txtSql.setText(self.wrq)

    def execution(self):
        '''Permet d'éxecuter la requête'''

        # Vérifie la non présence de mot pouvant endommager la base de données
        erreur = False
        interdit = [
            "update", "delete", "insert", "intersect", "duplicate", "merge",
            "truncate", "create", "drop", "alter"
        ]
        if self.txtSql.toPlainText() != "":
            self.requete = self.txtSql.toPlainText()
        else:
            self.creaRequete()
            self.requete = self.wrq
        testRequete = self.requete.lower()
        for mot in interdit:
            if mot in testRequete:
                erreur = True
        if erreur == True:
            QMessageBox.critical(
                self, u"Erreur SQL",
                u"Vous essayez d'exécuter une requête qui peut endommager la base de données !",
                QMessageBox.Ok)
        # Après récupération du contenu de la zone de texte, exécute la requête
        else:
            query = QSqlQuery(self.db)
            query.prepare(self.requete)
            if query.exec_():
                wparam = ""
                while query.next():
                    wparam += str(query.value(0)) + ","
                if (wparam != ""):
                    wparam = "(" + wparam[0:len(wparam) - 1] + ")"
                    if self.modelPeche:
                        # Filtre le modèle des inventaires de reproduction et ferme la fenêtre
                        self.modelPeche.setFilter("opep_id in %s" % wparam)
                        self.modelPeche.select()
                        QDialog.accept(self)
                else:
                    QMessageBox.information(
                        self, "Filtrage",
                        u"Aucun pêche électrique ne correspond aux critères ...",
                        QMessageBox.Ok)
            else:
                QMessageBox.critical(self, u"Erreur SQL",
                                     query.lastError().text(), QMessageBox.Ok)
コード例 #6
0
ファイル: tab_Organ.py プロジェクト: fashni/IndoseCT
class OrganTab(QWidget):
    def __init__(self, ctx, *args, **kwargs):
        super(OrganTab, self).__init__(*args, **kwargs)
        self.ctx = ctx
        self.alfas = None
        self.betas = None
        self.organ_dose_db = None
        self.organ_names = []
        self.fig = {}
        self.is_quick_mode = False
        self.show_hk = False
        self.show_dosemap = False
        self.show_distmap = False
        self.show_dosedist = False
        self.initModel()
        self.initVar()
        self.initUI()
        self.sigConnect()

    def initVar(self):
        self.dist_map = None
        self.dose_map = None
        self.poly = None
        self.ssdec = 0
        self.ssdep = 0
        self.organ_dose_mean = 0
        self.organ_dose_std = 0
        self.diameter = 0
        self.ssde = 0
        self.ctdi = 0
        self.ssdecs = {}
        self.ssdeps = {}
        self.means = {}
        self.stds = {}
        self.dist_maps = {}
        self.dose_maps = {}
        self.polys = {}

    def initModel(self):
        self.protocol_model = QSqlTableModel(db=self.ctx.database.ssde_db)
        self.organ_model = QSqlTableModel(db=self.ctx.database.ssde_db)
        self.organ_dose_model = QSqlTableModel(db=self.ctx.database.ssde_db)

        self.protocol_model.setTable("Protocol")
        self.organ_model.setTable("Organ")
        self.organ_dose_model.setTable("Organ_Dose")

        self.protocol_model.setFilter("Group_ID=1")
        self.organ_dose_model.setFilter("Protocol_ID=1")

        self.protocol_model.select()
        self.organ_model.select()
        self.organ_dose_model.select()

    def sigConnect(self):
        self.method_cb.activated[int].connect(self.on_method_changed)
        self.protocol_cb.activated[int].connect(self.on_protocol_changed)
        self.calc_db_btn.clicked.connect(self.on_calculate_db)
        self.calc_cnt_btn.clicked.connect(self.on_calculate_cnt)
        self.add_cnt_btn.clicked.connect(self.on_contour)
        self.is_quick_mode_chk.stateChanged.connect(self.on_quick_mode_check)
        self.show_hk_chk.stateChanged.connect(self.on_show_hk_check)
        self.show_dosemap_chk.stateChanged.connect(self.on_show_dosemap_check)
        self.show_distmap_chk.stateChanged.connect(self.on_show_distmap_check)
        self.show_dosedist_chk.stateChanged.connect(
            self.on_show_dosedist_check)
        self.ctx.app_data.modeValueChanged.connect(self.diameter_mode_handle)
        # self.ctx.app_data.diameterValueChanged.connect(self.diameter_handle)
        # self.ctx.app_data.CTDIValueChanged.connect(self.ctdiv_handle)
        # self.ctx.app_data.SSDEValueChanged.connect(self.ssdew_handle)
        self.ctx.app_data.diametersUpdated.connect(self.update_values)
        self.ctx.app_data.ctdivsUpdated.connect(self.update_values)
        self.ctx.app_data.ssdesUpdated.connect(self.update_values)
        self.ctx.app_data.imgChanged.connect(self.img_changed_handle)
        self.ctx.axes.addPolyFinished.connect(self.add_cnt_handle)

    def initUI(self):
        self.figure = PlotDialog()
        self.method_cb = QComboBox()
        self.method_cb.addItems(['MC Data', 'Direct calculation'])

        self.init_db_method_ui()
        self.init_cnt_method_ui()

        self.main_area = QStackedWidget()
        self.main_area.addWidget(self.db_method_ui)
        self.main_area.addWidget(self.cnt_method_ui)
        self.on_method_changed()

        main_layout = QVBoxLayout()
        main_layout.addWidget(QLabel('Method:'))
        main_layout.addWidget(self.method_cb)
        main_layout.addWidget(self.main_area)
        main_layout.addStretch()

        self.setLayout(main_layout)

    def init_db_method_ui(self):
        self.protocol_cb = QComboBox()
        self.protocol_cb.setModel(self.protocol_model)
        self.protocol_cb.setModelColumn(self.protocol_model.fieldIndex('name'))
        self.calc_db_btn = QPushButton('Calculate')

        self.organ_labels = []
        self.organ_edits = [QLineEdit('0') for i in range(28)]
        [organ_edit.setMaximumWidth(70) for organ_edit in self.organ_edits]
        [organ_edit.setReadOnly(True) for organ_edit in self.organ_edits]
        [
            organ_edit.setAlignment(Qt.AlignRight)
            for organ_edit in self.organ_edits
        ]

        left = QFormLayout()
        right = QFormLayout()
        grid = QHBoxLayout()
        organ_grpbox = QGroupBox('Organ Dose')
        scroll = QScrollArea()

        for idx, organ_edit in enumerate(self.organ_edits):
            name = self.organ_model.record(idx).value('name')
            self.organ_names.append(name[0])
            label = QLabel(name)
            label.setMaximumWidth(100)
            self.organ_labels.append(label)
            left.addRow(label, organ_edit) if idx < 14 else right.addRow(
                label, organ_edit)

        grid.addLayout(left)
        grid.addLayout(right)
        organ_grpbox.setLayout(grid)
        scroll.setWidget(organ_grpbox)
        scroll.setWidgetResizable(True)

        self.db_method_ui = QGroupBox('', self)
        db_method_layout = QVBoxLayout()
        db_method_layout.addWidget(QLabel('Protocol:'))
        db_method_layout.addWidget(self.protocol_cb)
        db_method_layout.addWidget(self.calc_db_btn)
        db_method_layout.addWidget(scroll)
        db_method_layout.addStretch()
        self.db_method_ui.setLayout(db_method_layout)

    def init_cnt_method_ui(self):
        self.calc_cnt_btn = QPushButton('Calculate')
        self.add_cnt_btn = QPushButton('Add Contour')
        self.is_quick_mode_chk = QCheckBox('Quick Mode')
        self.is_quick_mode_chk.setEnabled(False)
        self.show_dosemap_chk = QCheckBox('Show Dose Map')
        self.show_distmap_chk = QCheckBox('Show Distance Map')
        self.show_dosedist_chk = QCheckBox('Show Histogram')
        self.show_hk_chk = QCheckBox('Show Corr. Factor Graph')

        self.diameter_label = QLabel("<b>Diameter (cm)</b>")
        self.diameter_edit = QLineEdit('0')
        self.ctdiv_edit = QLineEdit('0')
        self.ssdew_edit = QLineEdit('0')
        self.ssdec_edit = QLineEdit('0')
        self.ssdep_edit = QLineEdit('0')
        self.mean_edit = QLineEdit('0')
        self.std_edit = QLineEdit('0')

        edits = [
            self.diameter_edit, self.ctdiv_edit, self.ssdew_edit,
            self.ssdec_edit, self.ssdep_edit, self.mean_edit, self.std_edit
        ]
        [edit.setReadOnly(True) for edit in edits]
        self.diameter_mode_handle(DEFF_IMAGE)

        left = QGroupBox('', self)
        right = QGroupBox('', self)
        left_layout = QFormLayout()
        right_layout = QFormLayout()

        left_layout.addRow(self.diameter_label, self.diameter_edit)
        left_layout.addRow(QLabel("<b>CTDI<sub>vol</sub> (mGy)</b>"),
                           self.ctdiv_edit)
        left_layout.addRow(QLabel("<b>SSDE<sub>w</sub> (mGy)</b>"),
                           self.ssdew_edit)
        left_layout.addRow(QLabel("<b>SSDE<sub>c</sub> (mGy)</b>"),
                           self.ssdec_edit)
        left_layout.addRow(QLabel("<b>SSDE<sub>p</sub> (mGy)</b>"),
                           self.ssdep_edit)
        right_layout.addRow(QLabel("<b>Mean (mGy)</b>"), self.mean_edit)
        right_layout.addRow(QLabel("<b>Std. Deviation (mGy)</b>"),
                            self.std_edit)
        left.setLayout(left_layout)
        right.setLayout(right_layout)

        output_area = QHBoxLayout()
        output_area.addWidget(left)
        output_area.addWidget(right)

        opt_grpbox = QGroupBox('Options')
        opt_area = QVBoxLayout()
        opt_area.addWidget(self.is_quick_mode_chk)
        opt_area.addWidget(self.show_distmap_chk)
        opt_area.addWidget(self.show_dosemap_chk)
        opt_area.addWidget(self.show_dosedist_chk)
        opt_area.addWidget(self.show_hk_chk)
        opt_grpbox.setLayout(opt_area)

        btn_layout = QVBoxLayout()
        btn_layout.addStretch()
        btn_layout.addWidget(self.add_cnt_btn)
        btn_layout.addWidget(self.calc_cnt_btn)
        btn_layout.addStretch()

        btn_opt_layout = QHBoxLayout()
        btn_opt_layout.addLayout(btn_layout)
        btn_opt_layout.addWidget(opt_grpbox)

        main_layout = QVBoxLayout()
        main_layout.addLayout(output_area)
        main_layout.addLayout(btn_opt_layout)

        self.cnt_method_ui = QGroupBox('', self)
        self.cnt_method_ui.setLayout(main_layout)

    def plot(self):
        xdict = dict(enumerate(self.organ_names, 1))
        stringaxis = AxisItem(orientation='bottom')
        stringaxis.setTicks([xdict.items()])
        # fm = QFontMetrics(stringaxis.font())
        # minHeight = max(fm.boundingRect(QRect(), Qt.AlignLeft, t).width() for t in xdict.values())
        # stringaxis.setHeight(minHeight + fm.width('     '))

        self.figure = PlotDialog(size=(900, 600), straxis=stringaxis)
        self.figure.setTitle('Organ Dose')
        self.figure.axes.showGrid(False, True)
        self.figure.setLabels('', 'Dose', '', 'mGy')
        self.figure.bar(x=list(xdict.keys()),
                        height=self.organ_dose_db,
                        width=.8,
                        brush='g')
        self.figure.show()

    def plot_cnt(self, dose_vec):
        self.figure = PlotDialog()
        self.figure.actionEnabled(True)
        self.figure.trendActionEnabled(False)
        self.figure.opts_dlg.y_mean_chk.setEnabled(False)
        self.figure.opts_dlg.y_stdv_chk.setEnabled(False)
        self.figure.histogram(dose_vec,
                              fillLevel=0,
                              brush=(0, 0, 255, 150),
                              symbol='o',
                              symbolSize=5)
        self.figure.axes.showGrid(True, True)
        self.figure.setLabels('Organ Dose', 'Frequency', 'mGy', '')
        self.figure.setTitle('Organ Dose')
        self.figure.show()

    def plot_hk(self):
        h, k, dw = self.get_interpolation()
        h_data = h(dw)
        k_data = k(dw)
        diameter = self.diameter
        c = h(diameter)
        p = k(diameter)
        anc_c = (1, 0) if diameter > 13.575512 else (1, 1)
        anc_p = (1, 1) if diameter > 13.575512 else (1, 0)
        self.figure_hk = PlotDialog()
        self.figure_hk.setTitle('Correction Factor')
        self.figure_hk.plot(dw,
                            h_data,
                            pen={
                                'color': "FFFF00",
                                'width': 2
                            },
                            symbol=None,
                            name='h_factor')
        self.figure_hk.plot(dw,
                            k_data,
                            pen={
                                'color': "00FFFF",
                                'width': 2
                            },
                            symbol=None,
                            name='k_factor')
        self.figure_hk.plot([diameter], [c],
                            symbol='o',
                            symbolPen=None,
                            symbolSize=8,
                            symbolBrush=(255, 0, 0, 255))
        self.figure_hk.plot([diameter], [p],
                            symbol='o',
                            symbolPen=None,
                            symbolSize=8,
                            symbolBrush=(255, 0, 0, 255))
        self.figure_hk.annotate(
            'cfh',
            pos=(diameter, c),
            text=f'diameter = {diameter:#.2f}\nh-factor = {c:#.2f}',
            anchor=anc_c)
        self.figure_hk.annotate(
            'cfk',
            pos=(diameter, p),
            text=f'diameter = {diameter:#.2f}\nk-factor = {p:#.2f}',
            anchor=anc_p)
        self.figure_hk.axes.showGrid(True, True)
        self.figure_hk.setLabels('Diameter', 'Correction Factor', 'mm', '')
        self.figure_hk.axes.setXRange(np.min(dw), np.max(dw))
        self.figure_hk.axes.setYRange(np.min([np.min(k_data),
                                              np.min(h_data)]),
                                      np.max([np.max(k_data),
                                              np.max(h_data)]))
        self.figure_hk.show()

    def plot_dosemap(self):
        self.figure_dose = ImageViewDialog(unit='dose')
        self.figure_dose.setTitle('Dose Map')
        self.figure_dose.imshow(self.dose_map)
        self.figure_dose.show()

    def plot_img(self, img, title='figure', unit=(None, None)):
        key = title.lower().replace(' ', '_')
        self.fig[key] = ImageViewDialog(unit=unit)
        self.fig[key].setTitle(title)
        self.fig[key].imshow(img)
        self.fig[key].add_roi(self.poly)
        self.fig[key].show()

    def getData(self):
        self.alfas = np.array([
            self.organ_dose_model.record(n).value('alfa')
            for n in range(self.organ_dose_model.rowCount())
        ])
        self.betas = np.array([
            self.organ_dose_model.record(n).value('beta')
            for n in range(self.organ_dose_model.rowCount())
        ])

    def diameter_mode_handle(self, value):
        self.dist_map = None
        self.dose_map = None
        self.dist_maps = {}
        self.dose_maps = {}
        self.add_cnt_btn.setEnabled(True)
        self.add_cnt_btn.setText('Add Contour')
        if value == DW:
            self.diameter_label.setText('<b>Dw (cm)</b>')
            self.show_distmap_chk.setText('Show Water Eq. Distance Map')
            self.is_quick_mode_chk.setEnabled(True)
        else:
            self.diameter_label.setText('<b>Deff (cm)</b>')
            self.show_distmap_chk.setText('Show Effective Distance Map')
            self.is_quick_mode_chk.setCheckState(Qt.Unchecked)
            self.is_quick_mode_chk.setEnabled(False)

    def diameter_handle(self, value):
        self.diameter_edit.setText(f'{value:#.2f}')

    def ctdiv_handle(self, value):
        self.ctdiv_edit.setText(f'{value:#.2f}')

    def ssdew_handle(self, value):
        self.ssdew_edit.setText(f'{value:#.2f}')

    def add_cnt_handle(self, value):
        self.add_cnt_btn.setEnabled(True)
        self.calc_cnt_btn.setEnabled(True)

    def img_changed_handle(self, value):
        if value:
            self.update_values()

    def on_method_changed(self):
        self.main_area.setCurrentIndex(self.method_cb.currentIndex())

    def on_protocol_changed(self, idx):
        self.protocol_id = self.protocol_model.record(idx).value("id")
        self.organ_dose_model.setFilter(f'Protocol_ID={self.protocol_id}')
        self.getData()
        print(self.protocol_id, self.protocol_model.record(idx).value("name"))

    def on_calculate_db(self):
        self.organ_dose_db = self.ctx.app_data.CTDIv * np.exp(
            self.alfas * self.ctx.app_data.diameter + self.betas)
        [
            self.organ_edits[idx].setText(f'{dose:#.2f}')
            for idx, dose in enumerate(self.organ_dose_db)
        ]
        self.plot()

    def get_ssde(self):
        h, k, _ = self.get_interpolation()
        self.ssdec = h(self.diameter) * self.ssde
        self.ssdep = k(self.diameter) * self.ssde
        self.ssdec_edit.setText(f'{self.ssdec:#.2f}')
        self.ssdep_edit.setText(f'{self.ssdep:#.2f}')
        self.ssdecs[self.ctx.current_img] = self.ssdec
        self.ssdeps[self.ctx.current_img] = self.ssdep

    def build_dose_map(self):
        from functools import partial

        def avg_profile_line(p2, p1):
            x0, y0 = p1
            x1, y1 = p2
            length = int(np.hypot(x1 - x0, y1 - y0))
            x = np.linspace(x0, x1, length).astype(int)
            y = np.linspace(y0, y1, length).astype(int)
            return img[x, y].mean()

        cancel = False
        rd = self.ctx.recons_dim
        row, col = self.ctx.get_current_img().shape
        mask = self.get_img_mask(self.ctx.get_current_img(), largest_only=True)
        if mask is not None:
            mask = mask.astype(float)
        mask_pos = np.argwhere(mask == 1)
        center = get_center(mask)

        dist_vec = np.sqrt(((mask_pos - center)**2).sum(1))
        if self.ctx.app_data.mode == DW:
            img = self.ctx.get_current_img()
            profile_line_vec = np.zeros_like(dist_vec, dtype=float)
            n = mask_pos.shape[0]
            progress = QProgressDialog(f"Building dose map...", "Stop", 0, n,
                                       self)
            progress.setWindowModality(Qt.WindowModal)
            progress.setMinimumDuration(1000)
            profile_line = partial(avg_profile_line, center)
            for idx, pos in enumerate(mask_pos):
                profile_line_vec[idx] = profile_line(pos)
                progress.setValue(idx)
                if progress.wasCanceled():
                    cancel = True
                    break
            progress.setValue(n)
            if np.isnan(profile_line_vec.sum()):
                profile_line_vec = np.nan_to_num(profile_line_vec)
            dist_vec *= ((profile_line_vec / 1000) + 1)
        if cancel:
            return

        dist_vec *= (0.1 * (rd / row))
        dose_vec = ((dist_vec / ((self.diameter * 0.5) - 1)) *
                    (self.ssdep - self.ssdec)) + self.ssdec
        self.dist_map = np.zeros_like(mask, dtype=float)
        self.dose_map = np.zeros_like(mask, dtype=float)
        self.dist_map[tuple(mask_pos.T)] = dist_vec
        self.dose_map[tuple(mask_pos.T)] = dose_vec
        self.dist_maps[self.ctx.current_img] = self.dist_map
        self.dose_maps[self.ctx.current_img] = self.dose_map

    def on_calculate_cnt(self):
        if self.ctx.axes.poly is None:
            QMessageBox.warning(None, "Warning", "Organ contour not found.")
            return

        self.poly = self.ctx.axes.poly
        self.polys[self.ctx.current_img] = self.poly
        self.get_ssde()
        if self.dose_map is None:
            self.build_dose_map()
        if self.dose_map is None:
            return

        organ_dose_map = self.poly.getArrayRegion(self.dose_map,
                                                  self.ctx.axes.image,
                                                  returnMappedCoords=False)
        organ_dose_mask_pos = np.argwhere(organ_dose_map != 0)
        organ_dose_vec = organ_dose_map[tuple(organ_dose_mask_pos.T)]
        self.organ_dose_mean = organ_dose_vec.mean()
        self.organ_dose_std = organ_dose_vec.std()
        self.means[self.ctx.current_img] = self.organ_dose_mean
        self.stds[self.ctx.current_img] = self.organ_dose_std
        self.mean_edit.setText(f'{self.organ_dose_mean:#.2f}')
        self.std_edit.setText(f'{self.organ_dose_std:#.2f}')
        if self.show_hk:
            self.plot_hk()
        if self.show_distmap:
            self.plot_img(self.dist_map, 'Distance Map', ('dist', 'cm'))
        if self.show_dosemap:
            self.plot_img(self.dose_map, 'Dose Map', ('dose', 'mGy'))
        if self.show_dosedist:
            self.plot_cnt(organ_dose_vec)

    def on_contour(self):
        print(self.ctx.axes.rois)
        if not self.ctx.isImage:
            QMessageBox.warning(None, "Warning", "Open DICOM files first.")
            return
        if self.ctx.axes.poly is None:
            self.ctx.axes.addPoly()
            self.add_cnt_btn.setText("Clear Contour")
            self.add_cnt_btn.setEnabled(False)
            self.calc_cnt_btn.setEnabled(False)
        else:
            self.ctx.axes.clearPoly()
            self.poly = None
            self.polys.pop(self.ctx.current_img, None)
            self.means.pop(self.ctx.current_img, None)
            self.stds.pop(self.ctx.current_img, None)
            self.organ_dose_mean = 0
            self.organ_dose_std = 0
            self.mean_edit.setText(f'{self.organ_dose_mean:#.2f}')
            self.std_edit.setText(f'{self.organ_dose_std:#.2f}')
            self.add_cnt_btn.setText("Add Contour")

    def get_organ_mask(self, roi, dose_map):
        img = roi.getArrayRegion(dose_map,
                                 self.ctx.axes.image,
                                 returnMappedCoords=False)
        return roi.renderShapeMask(img.shape[0], img.shape[1])

    def get_img_mask(self, *args, **kwargs):
        mask = get_mask(*args, **kwargs)
        if mask is None:
            QMessageBox.warning(
                None, 'Segmentation Failed',
                'No object found during segmentation process.')
        return mask

    def get_interpolation(self):
        arr = scio.loadmat(self.ctx.hk_data)['A']
        dw = arr[0]
        hf = arr[5]
        kf = arr[11]
        h = interpolate.interp1d(dw, hf, kind='cubic')
        k = interpolate.interp1d(dw, kf, kind='cubic')
        return (h, k, dw)

    def on_quick_mode_check(self, state):
        self.is_quick_mode = state == Qt.Checked

    def on_show_dosemap_check(self, state):
        self.show_dosemap = state == Qt.Checked

    def on_show_distmap_check(self, state):
        self.show_distmap = state == Qt.Checked

    def on_show_dosedist_check(self, state):
        self.show_dosedist = state == Qt.Checked

    def on_show_hk_check(self, state):
        self.show_hk = state == Qt.Checked

    def update_values(self, val=True):
        if not val:
            return
        self.diameter = self.ctx.app_data.diameters[
            self.ctx.
            current_img] if self.ctx.current_img in self.ctx.app_data.diameters.keys(
            ) else 0
        self.ctdi = self.ctx.app_data.CTDIvs[
            self.ctx.
            current_img] if self.ctx.current_img in self.ctx.app_data.CTDIvs.keys(
            ) else 0
        self.ssde = self.ctx.app_data.SSDEs[
            self.ctx.
            current_img] if self.ctx.current_img in self.ctx.app_data.SSDEs.keys(
            ) else 0
        self.ssdec = self.ssdecs[
            self.ctx.current_img] if self.ctx.current_img in self.ssdecs.keys(
            ) else 0
        self.ssdep = self.ssdeps[
            self.ctx.current_img] if self.ctx.current_img in self.ssdeps.keys(
            ) else 0
        self.organ_dose_mean = self.means[
            self.ctx.current_img] if self.ctx.current_img in self.means.keys(
            ) else 0
        self.organ_dose_std = self.stds[
            self.ctx.current_img] if self.ctx.current_img in self.stds.keys(
            ) else 0
        self.dist_map = self.dist_maps[
            self.ctx.
            current_img] if self.ctx.current_img in self.dist_maps.keys(
            ) else None
        self.dose_map = self.dose_maps[
            self.ctx.
            current_img] if self.ctx.current_img in self.dose_maps.keys(
            ) else None

        if self.ctx.current_img in self.polys.keys():
            self.poly = self.polys[self.ctx.current_img]
            self.ctx.axes.applyPoly(self.poly)
            self.add_cnt_btn.setText("Clear Contour")
        else:
            self.poly = None
            self.add_cnt_btn.setText("Add Contour")

        self.diameter_edit.setText(f'{self.diameter:#.2f}')
        self.ctdiv_edit.setText(f'{self.ctdi:#.2f}')
        self.ssdew_edit.setText(f'{self.ssde:#.2f}')
        self.ssdec_edit.setText(f'{self.ssdec:#.2f}')
        self.ssdep_edit.setText(f'{self.ssdep:#.2f}')
        self.mean_edit.setText(f'{self.organ_dose_mean:#.2f}')
        self.std_edit.setText(f'{self.organ_dose_std:#.2f}')

    def reset_fields(self):
        [organ_edit.setText('0') for organ_edit in self.organ_edits]
        self.protocol_cb.setCurrentIndex(0)
        self.on_protocol_changed(0)
        self.initVar()
        self.ctx.axes.cancel_addPoly()
        self.calc_cnt_btn.setEnabled(True)
        self.add_cnt_btn.setEnabled(True)
        self.add_cnt_btn.setText('Add Contour')
        self.update_values()
コード例 #7
0
ファイル: widgets.py プロジェクト: mchal821/dars
class FilteringComboBox(Widget):
    """Combination of QCombobox and QLineEdit with autocompletionself.
    Line edit and completer model is taken from QSqlTable mod

    Parameters:
        table (str): db table name containing data for combobox
        column (str): column name containing data for combobox
        color (str): 'rgb(r, g, b)' used for primary color
        font_size (int): default text font size in pt
        _model (QSqlTableModel): data model
        _col (int): display data model source coulumn
        _proxy (QSortFilterProxyModel): completer data model.
                                        _proxy.sourceModel() == _model
        _le (QLineEdit): QCombobox LineEdit


    Methods:
        createEditor(): (Widget): returns user input widgets
        value(): (str): returns user input text value
        setValue(value(str)): sets editor widget display value
        style(): (str): Returns CSS stylesheet string for input widget
        updateModel(): updates input widget model

    Args:
        table (str): db table name containing data for combobox
        column (str): column name containing data for combobox
    """
    def __init__(self,
                 parent,
                 placeholderText,
                 table,
                 column,
                 color='rgb(0,145,234)',
                 image=''):
        self.table = table
        self.column = column
        self.color = color
        super().__init__(parent, placeholderText, image)
        self.updateModel()

    def createEditor(self):
        # setup data model
        self._model = QSqlTableModel()
        self._model.setTable(self.table)
        self._col = self._model.fieldIndex(self.column)

        # setup filter model for sorting and filtering
        self._proxy = QSortFilterProxyModel()
        self._proxy.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self._proxy.setSourceModel(self._model)
        self._proxy.setFilterKeyColumn(self._col)

        # setup completer
        self._completer = QCompleter()
        self._completer.setModel(self._proxy)
        self._completer.setCompletionColumn(self._col)
        self._completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion)

        # setup combobox
        editor = QComboBox()
        editor.setModel(self._proxy)
        editor.setModelColumn(self._col)
        editor.setEditable(True)
        editor.setFocusPolicy(Qt.StrongFocus)
        editor.setInsertPolicy(QComboBox.NoInsert)
        editor.setCompleter(self._completer)

        # setup connections
        editor.currentTextChanged[str].connect(self.onActivated)

        # setup editor appearence
        style = self.style()
        editor.setStyleSheet(style)
        editor.lineEdit().setStyleSheet(style)
        font = editor.font()
        self._completer.popup().setFont(font)

        return editor

    @pyqtSlot(str)
    def onActivated(self, text):
        print('combo_box filter text', text)
        if not text:  # placeholder text displayed and label is not visible
            self._editor.setCurrentIndex(-1)
            if self.toggle:
                self._label.showLabel(False)
        else:  # selected text is displayed and label is visible
            # self._editor.showPopup()
            # self._editor.lineEdit().setFocus()
            print('current copmpletion string',
                  self._completer.currentCompletion())
            self._proxy.setFilterFixedString(text)
            if self.toggle:
                self._label.showLabel(True)

    def style(self):
        """Returns stylesheet for editors

        Returns:
            style (str)
        """
        style = """
            QLineEdit {{
                border: none;
                padding-bottom: 2px;
                border-bottom: 1px solid rgba(0,0,0,0.42);
                background-color: white;
                color: rgba(0,0,0,0.42);
                font-size: {font_size}pt;}}

            QLineEdit:editable {{
                padding-bottom: 2px;
                border-bottom: 1px rgba(0,0,0,0.42);
                color: rgba(0,0,0,0.42);}}

            QLineEdit:disabled {{
                border: none;
                padding-bottom: 2px;
                border-bottom: 1px rgba(0,0,0,0.42);
                color: rgba(0,0,0,0.38);}}

            QLineEdit:hover {{
                padding-bottom: 2px;
                border-bottom: 2px solid rgba(0,0,0,0.6);
                color: rgba(0,0,0,0.54);
                }}

            QLineEdit:focus {{
                padding-bottom: 2px;
                border-bottom: 2px solid {color};
                color: rgba(0,0,0,0.87);}}

            QLineEdit:pressed {{
                border-bottom: 2px {color};
                font: bold;
                color: rgba(0,0,0,0.87)}}

            QComboBox {{
                border: none;
                padding-bottom: 2px;
                font-size: {font_size}pt;
                }}

            QComboBox::down-arrow {{
                image: url('dropdown.png');
                background-color: white;
                border: 0px white;
                padding: 0px;
                margin: 0px;
                height:14px;
                width:14px;}}

            QComboBox::drop-down{{
                subcontrol-position: center right;
                border: 0px;
                margin: 0px;
            }}

            QComboBox QAbstractItemView {{
                font: {font_size};}}

        """.format(color=self.color,
                   font_size=self._editor_font_size,
                   dropdown=DROPDOWN_PNG)
        return style

    def updateModel(self):
        model = self._editor.model().sourceModel()
        col = self._editor.modelColumn()
        model.select()
        model.sort(col, Qt.AscendingOrder)

    def value(self):
        return self._editor.currentText()

    def setValue(self, value):
        self._editor.setCurrentText(value)
コード例 #8
0
class QmyMainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)  #调用父类构造函数,创建窗体
        self.ui = Ui_MainWindow()  #创建UI对象
        self.ui.setupUi(self)  #构造UI界面

        self.setCentralWidget(self.ui.splitter)

        ##   tableView显示属性设置
        self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems)
        self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection)
        self.ui.tableView.setAlternatingRowColors(True)
        self.ui.tableView.verticalHeader().setDefaultSectionSize(22)
        self.ui.tableView.horizontalHeader().setDefaultSectionSize(60)

##  ==============自定义功能函数============

    def __getFieldNames(self):  ##获取所有字段名称
        emptyRec = self.tabModel.record()  #获取空记录,只有字段名
        self.fldNum = {}  #字段名与序号的字典
        for i in range(emptyRec.count()):
            fieldName = emptyRec.fieldName(i)
            self.ui.comboFields.addItem(fieldName)
            self.fldNum.setdefault(fieldName)
            self.fldNum[fieldName] = i
        print(self.fldNum)

    def __openTable(self):  ##打开数据表
        self.tabModel = QSqlTableModel(self, self.DB)  #数据模型
        self.tabModel.setTable("employee")  #设置数据表
        self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit
                                      )  #数据保存方式,OnManualSubmit , OnRowChange
        self.tabModel.setSort(self.tabModel.fieldIndex("empNo"),
                              Qt.AscendingOrder)  #排序
        if (self.tabModel.select() == False):  #查询数据失败
            QMessageBox.critical(
                self, "错误信息",
                "打开数据表错误,错误信息\n" + self.tabModel.lastError().text())
            return

        self.__getFieldNames()  #获取字段名和序号

        ##字段显示名
        self.tabModel.setHeaderData(self.fldNum["empNo"], Qt.Horizontal, "工号")
        self.tabModel.setHeaderData(self.fldNum["Name"], Qt.Horizontal, "姓名")
        self.tabModel.setHeaderData(self.fldNum["Gender"], Qt.Horizontal, "性别")
        self.tabModel.setHeaderData(self.fldNum["Birthday"], Qt.Horizontal,
                                    "出生日期")
        self.tabModel.setHeaderData(self.fldNum["Province"], Qt.Horizontal,
                                    "省份")
        self.tabModel.setHeaderData(self.fldNum["Department"], Qt.Horizontal,
                                    "部门")
        self.tabModel.setHeaderData(self.fldNum["Salary"], Qt.Horizontal, "工资")

        self.tabModel.setHeaderData(self.fldNum["Memo"], Qt.Horizontal,
                                    "备注")  #这两个字段不在tableView中显示
        self.tabModel.setHeaderData(self.fldNum["Photo"], Qt.Horizontal, "照片")

        ##      self.tabModel.setHeaderData(self.tabModel.fieldIndex("empNo"),  Qt.Horizontal, "工号")
        ##      self.tabModel.setHeaderData(self.tabModel.fieldIndex("Name"),   Qt.Horizontal, "姓名")
        ##      self.tabModel.setHeaderData(self.tabModel.fieldIndex("Gender"), Qt.Horizontal, "性别")
        ##      self.tabModel.setHeaderData(self.tabModel.fieldIndex("Birthday"),  Qt.Horizontal, "出生日期")
        ##      self.tabModel.setHeaderData(self.tabModel.fieldIndex("Province"),  Qt.Horizontal, "省份")
        ##      self.tabModel.setHeaderData(self.tabModel.fieldIndex("Department"),Qt.Horizontal, "部门")
        ##      self.tabModel.setHeaderData(self.tabModel.fieldIndex("Salary"), Qt.Horizontal, "工资")
        ##      self.tabModel.setHeaderData(self.tabModel.fieldIndex("Memo"),   Qt.Horizontal, "备注")   #这两个字段不在tableView中显示
        ##      self.tabModel.setHeaderData(self.tabModel.fieldIndex("Photo"),  Qt.Horizontal, "照片")

        ##创建界面组件与数据模型的字段之间的数据映射
        self.mapper = QDataWidgetMapper()
        self.mapper.setModel(self.tabModel)  #设置数据模型
        self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit)

        ##界面组件与tabModel的具体字段之间的联系
        self.mapper.addMapping(self.ui.dbSpinEmpNo, self.fldNum["empNo"])
        self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"])
        self.mapper.addMapping(self.ui.dbComboSex, self.fldNum["Gender"])
        self.mapper.addMapping(self.ui.dbEditBirth, self.fldNum["Birthday"])
        self.mapper.addMapping(self.ui.dbComboProvince,
                               self.fldNum["Province"])
        self.mapper.addMapping(self.ui.dbComboDep, self.fldNum["Department"])
        self.mapper.addMapping(self.ui.dbSpinSalary, self.fldNum["Salary"])
        self.mapper.addMapping(self.ui.dbEditMemo, self.fldNum["Memo"])
        self.mapper.toFirst()  #移动到首记录

        self.selModel = QItemSelectionModel(self.tabModel)  #选择模型
        self.selModel.currentChanged.connect(self.do_currentChanged)  #当前项变化时触发
        self.selModel.currentRowChanged.connect(
            self.do_currentRowChanged)  #选择行变化时

        self.ui.tableView.setModel(self.tabModel)  #设置数据模型
        self.ui.tableView.setSelectionModel(self.selModel)  #设置选择模型

        self.ui.tableView.setColumnHidden(self.fldNum["Memo"], True)  #隐藏列
        self.ui.tableView.setColumnHidden(self.fldNum["Photo"], True)  #隐藏列

        ##tableView上为“性别”和“部门”两个字段设置自定义代理组件
        strList = ("男", "女")
        self.__delegateSex = QmyComboBoxDelegate()
        self.__delegateSex.setItems(strList, False)
        self.ui.tableView.setItemDelegateForColumn(
            self.fldNum["Gender"], self.__delegateSex)  #Combbox选择型

        strList = ("销售部", "技术部", "生产部", "行政部")
        self.__delegateDepart = QmyComboBoxDelegate()
        self.__delegateDepart.setItems(strList, True)
        self.ui.tableView.setItemDelegateForColumn(self.fldNum["Department"],
                                                   self.__delegateDepart)

        ##更新actions和界面组件的使能状态
        self.ui.actOpenDB.setEnabled(False)

        self.ui.actRecAppend.setEnabled(True)
        self.ui.actRecInsert.setEnabled(True)
        self.ui.actRecDelete.setEnabled(True)
        self.ui.actScan.setEnabled(True)

        self.ui.groupBoxSort.setEnabled(True)
        self.ui.groupBoxFilter.setEnabled(True)

##  ==========由connectSlotsByName() 自动连接的槽函数==================

    @pyqtSlot()  ##选择数据库,打开数据表
    def on_actOpenDB_triggered(self):
        dbFilename, flt = QFileDialog.getOpenFileName(
            self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)")
        if (dbFilename == ''):
            return

        #打开数据库
        self.DB = QSqlDatabase.addDatabase("QSQLITE")  #添加 SQLITE数据库驱动
        self.DB.setDatabaseName(dbFilename)  #设置数据库名称
        ##    DB.setHostName()
        ##    DB.setUserName()
        ##    DB.setPassword()
        if self.DB.open():  #打开数据库
            self.__openTable()  #打开数据表
        else:
            QMessageBox.warning(self, "错误", "打开数据库失败")

    @pyqtSlot()  ##保存修改
    def on_actSubmit_triggered(self):
        res = self.tabModel.submitAll()
        if (res == False):
            QMessageBox.information(
                self, "消息", "数据保存错误,错误信息\n" + self.tabModel.lastError().text())
        else:
            self.ui.actSubmit.setEnabled(False)
            self.ui.actRevert.setEnabled(False)

    @pyqtSlot()  ##取消修改
    def on_actRevert_triggered(self):
        self.tabModel.revertAll()
        self.ui.actSubmit.setEnabled(False)
        self.ui.actRevert.setEnabled(False)

    @pyqtSlot()  ##添加记录
    def on_actRecAppend_triggered(self):
        self.tabModel.insertRow(self.tabModel.rowCount(),
                                QModelIndex())  #在末尾添加一个记录

        curIndex = self.tabModel.index(self.tabModel.rowCount() - 1,
                                       1)  #创建最后一行的ModelIndex
        self.selModel.clearSelection()  #清空选择项
        self.selModel.setCurrentIndex(
            curIndex, QItemSelectionModel.Select)  #设置刚插入的行为当前选择行

        currow = curIndex.row()  #获得当前行
        self.tabModel.setData(self.tabModel.index(currow,
                                                  self.fldNum["empNo"]),
                              2000 + self.tabModel.rowCount())  #自动生成编号
        self.tabModel.setData(
            self.tabModel.index(currow, self.fldNum["Gender"]), "男")

    @pyqtSlot()  ##插入记录
    def on_actRecInsert_triggered(self):
        curIndex = self.ui.tableView.currentIndex()  #QModelIndex
        self.tabModel.insertRow(curIndex.row(), QModelIndex())
        self.selModel.clearSelection()  #清除已有选择
        self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select)

    @pyqtSlot()  ##删除记录
    def on_actRecDelete_triggered(self):
        curIndex = self.selModel.currentIndex()  #获取当前选择单元格的模型索引
        self.tabModel.removeRow(curIndex.row())  #删除当前行

    @pyqtSlot()  ##清除照片
    def on_actPhotoClear_triggered(self):
        curRecNo = self.selModel.currentIndex().row()
        curRec = self.tabModel.record(curRecNo)  #获取当前记录,QSqlRecord
        curRec.setNull("Photo")  #设置为空值
        self.tabModel.setRecord(curRecNo, curRec)
        self.ui.dbLabPhoto.clear()  #清除界面上的图片显示

    @pyqtSlot()  ##设置照片
    def on_actPhoto_triggered(self):
        fileName, filt = QFileDialog.getOpenFileName(self, "选择图片文件", "",
                                                     "照片(*.jpg)")
        if (fileName == ''):
            return

        file = QFile(fileName)  #fileName为图片文件名
        file.open(QIODevice.ReadOnly)
        try:
            data = file.readAll()  #QByteArray
        finally:
            file.close()

        curRecNo = self.selModel.currentIndex().row()
        curRec = self.tabModel.record(curRecNo)  #获取当前记录QSqlRecord
        curRec.setValue("Photo", data)  #设置字段数据
        self.tabModel.setRecord(curRecNo, curRec)

        pic = QPixmap()
        pic.loadFromData(data)
        W = self.ui.dbLabPhoto.width()
        self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(W))  #在界面上显示

    @pyqtSlot()  ##涨工资,遍历数据表所有记录
    def on_actScan_triggered(self):
        if (self.tabModel.rowCount() == 0):
            return

        for i in range(self.tabModel.rowCount()):
            aRec = self.tabModel.record(i)  #获取当前记录
            ##         salary=aRec.value("Salary").toFloat()      #错误,无需再使用toFloat()函数
            salary = aRec.value("Salary")
            salary = salary * 1.1
            aRec.setValue("Salary", salary)
            self.tabModel.setRecord(i, aRec)

        if (self.tabModel.submitAll()):
            QMessageBox.information(self, "消息", "涨工资计算完毕")

    @pyqtSlot(int)  ##排序字段变化
    def on_comboFields_currentIndexChanged(self, index):
        if self.ui.radioBtnAscend.isChecked():
            self.tabModel.setSort(index, Qt.AscendingOrder)
        else:
            self.tabModel.setSort(index, Qt.DescendingOrder)
        self.tabModel.select()

    @pyqtSlot()  ##升序
    def on_radioBtnAscend_clicked(self):
        self.tabModel.setSort(self.ui.comboFields.currentIndex(),
                              Qt.AscendingOrder)
        self.tabModel.select()

    @pyqtSlot()  ##降序
    def on_radioBtnDescend_clicked(self):
        self.tabModel.setSort(self.ui.comboFields.currentIndex(),
                              Qt.DescendingOrder)
        self.tabModel.select()

    @pyqtSlot()  ##过滤,男
    def on_radioBtnMan_clicked(self):
        self.tabModel.setFilter("Gender='男'")

    ##      print(self.tabModel.filter())
    ##      self.tabModel.select()

    @pyqtSlot()  ##数据过滤,女
    def on_radioBtnWoman_clicked(self):
        self.tabModel.setFilter("Gender='女' ")

    ##      print(self.tabModel.filter())
    ##      self.tabModel.select()

    @pyqtSlot()  ##取消数据过滤
    def on_radioBtnBoth_clicked(self):
        self.tabModel.setFilter("")

    ##      print(self.tabModel.filter())
    ##      self.tabModel.select()

##  =============自定义槽函数===============================

    def do_currentChanged(self, current, previous):  ##更新actPost和actCancel 的状态
        self.ui.actSubmit.setEnabled(self.tabModel.isDirty())  #有未保存修改时可用
        self.ui.actRevert.setEnabled(self.tabModel.isDirty())

    def do_currentRowChanged(self, current, previous):  #行切换时的状态控制
        self.ui.actRecDelete.setEnabled(current.isValid())
        self.ui.actPhoto.setEnabled(current.isValid())
        self.ui.actPhotoClear.setEnabled(current.isValid())

        if (current.isValid() == False):
            self.ui.dbLabPhoto.clear()  #清除图片显示
            return

        self.mapper.setCurrentIndex(current.row())  #更新数据映射的行号
        curRec = self.tabModel.record(current.row())  #获取当前记录,QSqlRecord类型

        if (curRec.isNull("Photo")):  #图片字段内容为空
            self.ui.dbLabPhoto.clear()
        else:
            ##         data=bytearray(curRec.value("Photo"))   #可以工作
            data = curRec.value("Photo")  # 也可以工作
            pic = QPixmap()
            pic.loadFromData(data)
            W = self.ui.dbLabPhoto.size().width()
            self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(W))
コード例 #9
0
ファイル: 0701.py プロジェクト: falomsc/pyqtStudy
class QmyMainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.setCentralWidget(self.ui.splitter)

        self.ui.tableView.setSelectionBehavior(QAbstractItemView.SelectItems)
        self.ui.tableView.setSelectionMode(QAbstractItemView.SingleSelection)
        self.ui.tableView.setAlternatingRowColors(True)
        self.ui.tableView.verticalHeader().setDefaultSectionSize(22)
        self.ui.tableView.horizontalHeader().setDefaultSectionSize(60)

    def __openTable(self):
        self.tabModel = QSqlTableModel(self, self.DB)
        self.tabModel.setTable("employee")
        self.tabModel.setEditStrategy(QSqlTableModel.OnManualSubmit)
        self.tabModel.setSort(self.tabModel.fieldIndex("empNo"), Qt.AscendingOrder)
        if(self.tabModel.select()==False):
            QMessageBox.critical(self, "错误信息", "打开数据表错误,错误信息\n"+self.tabModel.lastError().text())
            return
        self.__getFieldNames()

        self.tabModel.setHeaderData(self.fldNum["empNo"], Qt.Horizontal, "工号")
        self.tabModel.setHeaderData(self.fldNum["Name"], Qt.Horizontal, "姓名")
        self.tabModel.setHeaderData(self.fldNum["Gender"], Qt.Horizontal, "性别")
        self.tabModel.setHeaderData(self.fldNum["Birthday"], Qt.Horizontal, "出生日期")
        self.tabModel.setHeaderData(self.fldNum["Province"], Qt.Horizontal, "省份")
        self.tabModel.setHeaderData(self.fldNum["Department"], Qt.Horizontal, "部门")
        self.tabModel.setHeaderData(self.fldNum["Salary"], Qt.Horizontal, "工资")
        self.tabModel.setHeaderData(self.fldNum["Memo"], Qt.Horizontal, "备注")
        self.tabModel.setHeaderData(self.fldNum["Photo"], Qt.Horizontal, "照片")

        self.mapper = QDataWidgetMapper()
        self.mapper.setModel(self.tabModel)
        self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit)
        self.mapper.addMapping(self.ui.dbSpinEmpNo, self.fldNum["empNo"])
        self.mapper.addMapping(self.ui.dbEditName, self.fldNum["Name"])
        self.mapper.addMapping(self.ui.dbComboSex, self.fldNum["Gender"])
        self.mapper.addMapping(self.ui.dbEditBirth, self.fldNum["Birthday"])
        self.mapper.addMapping(self.ui.dbComboProvince, self.fldNum["Province"])
        self.mapper.addMapping(self.ui.dbComboDep, self.fldNum["Department"])
        self.mapper.addMapping(self.ui.dbSpinSalary, self.fldNum["Salary"])
        self.mapper.addMapping(self.ui.dbEditMemo, self.fldNum["Memo"])
        self.mapper.toFirst()

        self.selModel = QItemSelectionModel(self.tabModel)
        self.selModel.currentChanged.connect(self.do_currentChanged)
        self.selModel.currentRowChanged.connect(self.do_currentRowChanged)

        self.ui.tableView.setModel(self.tabModel)
        self.ui.tableView.setSelectionModel(self.selModel)

        self.ui.tableView.setColumnHidden(self.fldNum["Memo"], True)
        self.ui.tableView.setColumnHidden(self.fldNum["Photo"], True)


        strList = ("男", "女")
        self.__delegatesex = QmyComboBoxDelegate()
        self.__delegatesex.setItems(strList, False)
        self.ui.tableView.setItemDelegateForColumn(self.fldNum["Gender"], self.__delegatesex)

        strList = ("销售部", "技术部", "生产部", "行政部")
        self.__delegateDepart = QmyComboBoxDelegate()
        self.__delegateDepart.setItems(strList, True)
        self.ui.tableView.setItemDelegateForColumn(self.fldNum["Department"], self.__delegateDepart)

        self.ui.actOpenDB.setEnabled(False)
        self.ui.actOpenDB.setEnabled(False)

        self.ui.actRecAppend.setEnabled(True)
        self.ui.actRecInsert.setEnabled(True)
        self.ui.actRecDelete.setEnabled(True)
        self.ui.actScan.setEnabled(True)

        self.ui.groupBoxSort.setEnabled(True)
        self.ui.groupBoxFilter.setEnabled(True)


    def __getFieldNames(self):
        emptyRec = self.tabModel.record()
        self.fldNum = {}
        for i in range(emptyRec.count()):
            fieldName = emptyRec.fieldName(i)
            self.ui.comboFields.addItem(fieldName)
            self.fldNum.setdefault(fieldName)
            self.fldNum[fieldName]=i
        print(self.fldNum)

    def do_currentChanged(self, current, previous):
        self.ui.actSubmit.setEnabled(self.tabModel.isDirty())
        self.ui.actRevert.setEnabled(self.tabModel.isDirty())

    def do_currentRowChanged(self, current, previous):
        self.ui.actRecDelete.setEnabled(current.isValid())
        self.ui.actPhoto.setEnabled(current.isValid())
        self.ui.actPhotoClear.setEnabled(current.isValid())

        if(current.isValid() == False):
            self.ui.dbLabPhoto.clear()
            return

        self.mapper.setCurrentIndex(current.row())
        curRec = self.tabModel.record(current.row())

        if(curRec.isNull("Photo")):
            self.ui.dbLabPhoto.clear()
        else:
            data = curRec.value("Photo")
            pic = QPixmap()
            pic.loadFromData(data)
            w = self.ui.dbLabPhoto.size().width()
            self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(w))

    @pyqtSlot()
    def on_actOpenDB_triggered(self):
        dbFilename ,flt = QFileDialog.getOpenFileName(self, "选择数据库文件", "", "SQL Lite数据库(*.db *.db3)")
        if (dbFilename == ''):
            return
        self.DB = QSqlDatabase.addDatabase("QSQLITE")
        self.DB.setDatabaseName(dbFilename)
        if self.DB.open():
            self.__openTable()
        else:
            QMessageBox.warning(self, "错误", "打开数据库失败")

    @pyqtSlot()
    def on_actSubmit_triggered(self):
        res = self.tabModel.submitAll()
        if(res == False):
            QMessageBox.information(self, "消息", "数据保存错误,错误信息\n" + self.tabModel.lastError().text())
        else:
            self.ui.actSubmit.setEnabled(False)
            self.ui.actRevert.setEnabled(False)


    @pyqtSlot()
    def on_actRevert_triggered(self):
        self.tabModel.revertAll()
        self.ui.actSubmit.setEnabled(False)
        self.ui.actRevert.setEnabled(False)

    @pyqtSlot()
    def on_actRecAppend_triggered(self):
        self.tabModel.insertRow(self.tabModel.rowCount(), QModelIndex())
        curIndex = self.tabModel.index(self.tabModel.rowCount()-1, 1)
        self.selModel.clearSelection()
        self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select)
        currow = curIndex.row()
        self.tabModel.setData(self.tabModel.index(currow, self.fldNum["empNo"]), 2000+self.tabModel.rowCount())
        self.tabModel.setData(self.tabModel.index(currow, self.fldNum["Gender"]), "男")

    @pyqtSlot()
    def on_actRecInsert_triggered(self):
        curIndex = self.ui.tableView.currentIndex()
        self.tabModel.insertRow(curIndex.row(), QModelIndex())
        self.selModel.clearSelection()
        self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select)

    @pyqtSlot()
    def on_actRecDelete_triggered(self):
        curIndex = self.selModel.currentIndex()
        self.tabModel.removeRow(curIndex.row())

    @pyqtSlot()
    def on_actPhotoClear_triggered(self):
        curRecNo = self.selModel.currentIndex().row()
        curRec = self.tabModel.record(curRecNo)
        curRec.setNull("Photo")
        self.tabModel.setRecord(curRecNo, curRec)
        self.ui.dbLabPhoto.clear()

    @pyqtSlot()
    def on_actPhoto_triggered(self):
        fileName, filt = QFileDialog.getOpenFileName(self, "选择图片文件", "", "照片(*.jpg")
        if(fileName==''):
            return
        file=QFile(fileName)
        file.open(QIODevice.ReadOnly)
        try:
            data = file.readAll()
        finally:
            file.close()

        curRecNo = self.selModel.currentIndex().row()
        curRec = self.tabModel.record(curRecNo)
        curRec.setValue("Photo", data)
        self.tabModel.setRecord(curRecNo, curRec)

        pic = QPixmap()
        pic.loadFromData(data)
        w = self.ui.dbLabPhoto.width()
        self.ui.dbLabPhoto.setPixmap(pic.scaledToWidth(w))

    @pyqtSlot()
    def on_actScan_triggered(self):
        if(self.tabModel.rowCount()==0):
            return
        for i in range(self.tabModel.rowCount()):
            aRec = self.tabModel.record(i)
            salary = aRec.value("Salary")
            salary = salary*1.1
            aRec.setValue("Salary", salary)
            self.tabModel.setRecord(i, aRec)

        if(self.tabModel.submitAll()):
            QMessageBox.information(self, "消息", "涨工资计算完毕了")

    @pyqtSlot()
    def on_comboFields_currentIndexChanged(self, index):
        if self.ui.radioBtnAscend.isChecked():
            self.tabModel.setSort(index, Qt.AscendingOrder)
        else:
            self.tabModel.setSort(index, Qt.DescendingOrder)
        self.tabModel.select()

    @pyqtSlot()
    def on_radioBtnAscend_clicked(self):
        self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.AscendingOrder)
        self.tabModel.select()

    @pyqtSlot()
    def on_radioBtnDescend_clicked(self):
        self.tabModel.setSort(self.ui.comboFields.currentIndex(), Qt.DescendingOrder)
        self.tabModel.select()

    @pyqtSlot()
    def on_radioBtnMan_clicked(self):
        self.tabModel.setFilter("Gender='男'")

    @pyqtSlot()
    def on_radioBtnWoman_clicked(self):
        self.tabModel.setFilter("Gender='女'")

    @pyqtSlot()
    def on_radioBtnBoth_clicked(self):
        self.tabModel.setFilter("")
コード例 #10
0
class ScannerDataDialog(QDialog):
    def __init__(self, ctx, *args, **kwargs):
        super(ScannerDataDialog, self).__init__(*args, **kwargs)
        self.ctx = ctx
        self.setWindowTitle("Add Scanner Data")
        self.initUI()
        self.initModel()
        self.signal_connect()

    def signal_connect(self):
        self.exist_scanner_chk.stateChanged.connect(self.on_exist_scanner)
        self.exist_brand_chk.stateChanged.connect(self.on_exist_brand)
        self.buttons.accepted.connect(self.on_save)
        self.buttons.rejected.connect(self.on_close)
        self.brand_cb.activated[int].connect(self.on_brand_changed)
        self.scanner_cb.activated[int].connect(self.on_scanner_changed)
        [
            btn.toggled.connect(self.on_phantom_select)
            for btn in self.phantom_rbs
        ]

    def initModel(self):
        self.brand_query = QSqlTableModel(db=self.ctx.database.ctdi_db)
        self.scanner_query = QSqlTableModel(db=self.ctx.database.ctdi_db)
        self.volt_query = QSqlTableModel(db=self.ctx.database.ctdi_db)
        self.coll_query = QSqlTableModel(db=self.ctx.database.ctdi_db)

        # fill brand combobox
        self.brand_query.setTable("BRAND")
        self.brand_query.select()
        self.brand_id = self.brand_query.record(0).value("ID")

        # fill scanner combobox
        self.scanner_query.setTable("SCANNER")
        self.scanner_query.setFilter("BRAND_ID=1")
        self.scanner_query.select()
        self.scanner_id = self.scanner_query.record(0).value("ID")

        # fill voltage combobox
        self.volt_query.setTable("CTDI_DATA")
        self.volt_query.setFilter("SCANNER_ID=1")
        self.volt_query.select()
        self.CTDI = self.volt_query.record(0).value("CTDI_HEAD")

        # fill collimation combobox
        self.coll_query.setTable("COLLIMATION_DATA")
        self.coll_query.setFilter("SCANNER_ID=1")
        self.coll_query.select()
        self.coll = self.coll_query.record(0).value("VALUE")

        self.brand_cb.setModel(self.brand_query)
        self.brand_cb.setModelColumn(self.brand_query.fieldIndex("NAME"))
        self.scanner_cb.setModel(self.scanner_query)
        self.scanner_cb.setModelColumn(self.scanner_query.fieldIndex("NAME"))

        self.on_brand_changed(0)

    def initUI(self):
        btns = QDialogButtonBox.Save | QDialogButtonBox.Close
        self.buttons = QDialogButtonBox(btns)
        self.brand_cb = QComboBox()
        self.scanner_cb = QComboBox()
        self.brand_edit = QLineEdit()
        self.scanner_edit = QLineEdit()
        self.volt_edit = QLineEdit()
        self.coll_edit = QLineEdit()
        self.ctdih_edit = QTextEdit()
        self.ctdib_edit = QTextEdit()

        self.ctdi_edits_layout = QStackedWidget(self)
        self.ctdi_edits_layout.addWidget(self.ctdih_edit)
        self.ctdi_edits_layout.addWidget(self.ctdib_edit)

        self.phantom_rbs = [QRadioButton('Head'), QRadioButton('Body')]
        self.phantom_rbs[0].setChecked(True)
        self.exist_brand_chk = QCheckBox('Add existing')
        self.exist_scanner_chk = QCheckBox('Add existing')
        self.exist_scanner_chk.setEnabled(False)

        self.brand_edits_layout = QStackedWidget()
        self.brand_edits_layout.addWidget(self.brand_edit)
        self.brand_edits_layout.addWidget(self.brand_cb)
        self.scanner_edits_layout = QStackedWidget()
        self.scanner_edits_layout.addWidget(self.scanner_edit)
        self.scanner_edits_layout.addWidget(self.scanner_cb)

        self.rb_layout = QHBoxLayout()
        [self.rb_layout.addWidget(btn) for btn in self.phantom_rbs]
        self.rb_layout.addStretch()

        self.main_widget = QWidget()
        self.inner_layout = QFormLayout()
        self.inner_layout.addRow(QLabel('Manufacturer'),
                                 self.brand_edits_layout)
        self.inner_layout.addRow(QLabel(''), self.exist_brand_chk)
        self.inner_layout.addRow(QLabel('Scanner'), self.scanner_edits_layout)
        self.inner_layout.addRow(QLabel(''), self.exist_scanner_chk)
        self.inner_layout.addRow(QLabel('Voltage'), self.volt_edit)
        self.inner_layout.addRow(QLabel('Collimation'), self.coll_edit)
        self.inner_layout.addRow(QLabel('Phantom'), self.rb_layout)
        self.inner_layout.addRow(QLabel('CTDIw'), self.ctdi_edits_layout)
        self.main_widget.setLayout(self.inner_layout)

        self.main_layout = QVBoxLayout()
        self.main_layout.addWidget(self.main_widget)
        self.main_layout.addWidget(self.buttons)
        self.setLayout(self.main_layout)

        # txt = 'Comma-separated values for each collimation for each '
        # self.ctdib_edit.setPlaceholderText('body')
        # self.ctdih_edit.setPlaceholderText('head')

    def on_exist_brand(self, state):
        self.brand_edits_layout.setCurrentIndex(int(state == Qt.Checked))
        self.exist_scanner_chk.setCheckState(Qt.Unchecked)
        self.exist_scanner_chk.setEnabled(state == Qt.Checked)

    def on_exist_scanner(self, state):
        self.scanner_edits_layout.setCurrentIndex(int(state == Qt.Checked))

    def on_brand_changed(self, sel):
        self.brand_id = self.brand_query.record(sel).value("ID")
        self.scanner_query.setFilter(f"BRAND_ID={self.brand_id}")
        self.on_scanner_changed(0)
        self.scanner_items = [
            self.scanner_cb.itemText(i).lower()
            for i in range(self.scanner_cb.count())
        ]

    def on_scanner_changed(self, sel):
        self.scanner_id = self.scanner_query.record(sel).value("ID")
        self.volt_query.setFilter(f"SCANNER_ID={self.scanner_id}")
        self.coll_query.setFilter(f"SCANNER_ID={self.scanner_id}")
        # if self.volt_query.rowCount()>0:
        #   self.volt_items = [float(self.volt_cb.itemText(i)) for i in range(self.volt_cb.count())]
        # if self.coll_query.rowCount()>0:
        #   self.coll_items = [float(self.coll_query.record(i).value("COL_VAL")) for i in range(self.coll_cb.count())]

    def on_phantom_select(self):
        sel = self.sender()
        if sel.isChecked():
            self.phantom = sel.text().lower()
            self.ctdi_edits_layout.setCurrentIndex(int(self.phantom == 'body'))

    def on_save(self):
        # crazy stuffs
        btn_reply = QMessageBox.question(self, 'Add more data',
                                         'Do you want to add more data?')
        self.reset_fields()
        if btn_reply == QMessageBox.No:
            self.accept()

    def on_close(self):
        self.reset_fields()
        self.reject()

    def reset_fields(self):
        self.brand_edit.setText('')
        self.scanner_edit.setText('')
        self.volt_edit.setText('')
        self.coll_edit.setText('')
        self.ctdih_edit.setText('')
        self.exist_brand_chk.setCheckState(Qt.Unchecked)
        self.exist_scanner_chk.setCheckState(Qt.Unchecked)
コード例 #11
0
ファイル: main_db_control.py プロジェクト: qtpy-app/gitpyman
class DBControl(QObject, SqlalchemyDBControl, DB_Util1):
    def __init__(self, parent=None):
        super().__init__()

        import Client

        self.mw: Client.MainWindow = parent

        self.__initDB()
        self.__initUI()

    def __initDB(self):
        self._initDB()
        self._createDB()
        self._createModel()
        self._selectModel()

    # <editor-fold desc="qtdb">
    def _initDB(self):
        self.orm_db = self.create_session()
        self.qt_db = QSqlDatabase.addDatabase('QSQLITE')
        self.qt_db.setDatabaseName(self.db_path)
        if not self.qt_db.open():
            print("数据库连接失败")
            return False

        return self.qt_db

    def _createDB(self):
        self.create_table()
        print(self.qt_db.database().tables())

    def _createModel(self):
        def createModel(tbName):
            model = QSqlTableModel()
            # model.setEditStrategy(QSqlTableModel.OnFieldChange)
            model.setTable(tbName)
            model.select()
            setattr(
                self, tbName + "_model", model
            )  # here will create [self.watching_show_model ... ] and other model
            return model

        def createView(title, model, delegate=None):
            view = QQTableView(self.mw)
            view.setModel(model)
            view.setItemDelegateForColumn(
                0, delegate) if delegate is not None else None
            view.REMOVE_SIGNAL.connect(self.del_watching)
            return view

        def createTab(title):
            tab_form = self.mw.repo_tabWidget
            new_tab = QWidget()
            layout = QHBoxLayout(new_tab)
            layout.setContentsMargins(1, 1, 1, 1)
            tab_form.addTab(new_tab, title)
            return layout

        def createItem(title):
            comb = self.mw.filter_cb

            comb.addItem(title)

        tables = self.qt_db.database().tables()
        for tableName in tables:
            if tableName[-4:] == "show":
                # print(tableName)
                model = createModel(tableName)
                delegate = URLDelegate(self)
                view_ = createView(tableName, model, delegate)
                new_tab = createTab(tableName)
                _ = createItem(tableName)
                new_tab.addWidget(view_)
        # modeln
        self.filterModel = QSqlTableModel()
        # model1
        self.urlTableModel = QSqlTableModel()
        self.urlTableModel.setTable(WEBSITE_TABLE_NAME)
        # model2
        self.unameTableModel = QSqlTableModel()
        self.unameTableModel.setTable(USER_TABLE_NAME) if isinstance(
            self.unameTableModel, QSqlTableModel) else None
        # model3
        self.upwdQueryModel = QSqlQueryModel(self)
        """

        ###self.url_mapper = QDataWidgetMapper()  # 数据映射
        ###self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) # 提交策略
        ###self.url_mapper.setModel(self.urlQueryModel)  # 映射的模型源
        ###self.url_mapper.addMapping(self.mw.web_site_cb,0, b"currentIndex")
        ###self.mw.web_site_cb.setModel(self.urlQueryModel)


        # model4
        self.realationModel = QSqlRelationalTableModel(self)
        self.realationModel.setEditStrategy(QSqlTableModel.OnFieldChange)
        self.realationModel.setTable(REPOSITORIES_TABLE_NAME)
        self.realationModel.setRelation(1, QSqlRelation(WEBSITE_TABLE_NAME, 'id', 'url'))
        # TW_TITLE = [""]
        # for i in range(len(TW_TITLE)):
        #     self.realationModel.setHeaderData(i + 1, Qt.Horizontal, TW_TITLE[i])

        self.realationModel.select()
        """

    def _selectModel(self):

        # <editor-fold desc="1">
        self.mw.web_site_cb.setModel(self.urlTableModel)

        # self.mw.web_user_cb.setModelColumn(1)
        # </editor-fold>

        # <editor-fold desc="2">
        self.mw.web_user_cb.setModel(self.unameTableModel)
        self.mw.web_user_cb.setModelColumn(1)
        # </editor-fold>

        # <editor-fold desc="3">
        self.upwd_mapper = QDataWidgetMapper()  # 数据映射
        self.upwd_mapper.setModel(self.upwdQueryModel)  # 映射的模型源
        self.upwd_mapper.addMapping(self.mw.web_pwd_le, 0)
        ## self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit) # 提交策略
        # </editor-fold>

        # <editor-fold desc="4">
        self.mw.filter_tv.setModel(self.filterModel)
        self.mw.filter_tv.setItemDelegateForColumn(0, URLDelegate())
        # </editor-fold>

    # </editor-fold>

    def __initUI(self):
        self._pre_url_ = "---"
        self._pre_uname_ = "---"

        # 1 .
        self.mw.web_site_cb.activated.connect(self.on_web_uname_query)
        self.mw.web_user_cb.activated.connect(self.on_web_upwd_query)

    def on_web_site_url_query(self):
        print("查网址")
        # self.urlTableModel.setQuery(QSqlQuery(self.ORM2SQL(select([WebSite.url]))))
        self.reloadTableModel(self.urlTableModel, False)
        self.mw.web_site_cb.setModelColumn(1)
        # 如果没有item, setCurrentIndex(0) #qtbug 是不起作用的, index还是-1
        self.mw.web_site_cb.setCurrentIndex(0)
        self.on_web_uname_query()

    def on_web_uname_query(self, index=None):
        """
        网址索引改变的时候触发.
        :return:
        """

        print("查用户")
        c_url = self.get_c_url()
        curl_id = self.get_curl_id(c_url)

        self.mw.web_pwd_le.setText("")
        # 有结果
        if curl_id is not None:
            print("查到用户")
            self.unameTableModel.setFilter(f"""user_info.url_id = {curl_id}""")
            self.reloadTableModel(self.unameTableModel)

            self.mw.web_user_cb.setCurrentIndex(0)
            self.on_web_upwd_query()
        # else:

    def on_web_upwd_query(self):
        print("查密码")
        c_url, c_uname, c_upwd, curl_id = self.get_c_all()

        self.upwdQueryModel.setQuery(
            QSqlQuery(
                self.ORM2SQL(
                    select([UserTable.upwd]).where(
                        and_(UserTable.url_id == curl_id,
                             UserTable.uname == c_uname)))))
        # 设置密码UI
        self.upwd_mapper.toFirst()

        # loadtask

        self._set_pre_cb_text(c_url, c_uname)

    # -------------user------------- ↓
    def create_or_update_webInfo(self):
        c_url, c_uname, c_upwd, curl_id = self.get_c_all()
        if curl_id is None:
            model = self.urlTableModel

            row = model.rowCount()
            ret = model.insertRows(row, 1)
            print('insertRows=%s' % str(ret), model.columnCount())
            record_count = model.record().count()
            print(record_count)
            for col in range(model.columnCount()):
                index = model.index(row, col)
                # record = model.record(row)
                # field = record.field(col)
                field_col = self.urlTableModel.fieldIndex("url")
                if col == field_col:
                    model.setData(index, c_url)
            self.urlTableModel.submit()
            self.mw.web_site_cb.setModelColumn(1)
            # self.urlTableModel.select() # qt bug
            # web_obj = WebSite(url=c_url)
            # self.orm_db.add(web_obj)
            # self.orm_db.flush()
            # curl_id = web_obj.id

            curl_id = model.index(row, 0).data()
        # 法一: 双主键,  有则更新 , 无则创建
        # self.orm_db.merge(User(uname=c_uname, upwd=c_upwd, url_id=curl_id))
        # self.orm_db.commit()
        # 改变索引
        # self.reloadTableModel(self.urlTableModel)
        # self.reloadTableModel(self.unameTableModel)

        ## 法二
        model = self.unameTableModel
        row = model.rowCount()
        ret = model.insertRows(row, 1)
        print('insertRows=%s' % str(ret), model.columnCount())
        for col in range(model.columnCount()):
            index = model.index(row, col)

            if col == 0:
                model.setData(index, curl_id)
            elif col == 1:
                model.setData(index, c_uname)
            elif col == 2:
                model.setData(index, c_upwd)
            elif col == 3:
                model.setData(index, "{}")

        self.unameTableModel.submitAll()

    def delete_webInfo(self):
        """

        :return:
        """

        c_url, c_uname, c_upwd, curl_id = self.get_c_all()
        # 找到网址
        if curl_id is not None:
            # 删网址
            if c_uname == '' and c_upwd == '':
                obj = self.orm_db.query(WebSiteTable).filter_by(
                    url=c_url).one()
                self.orm_db.delete(obj)
                self.orm_db.commit()
                self.on_web_site_url_query()

            # 删账户
            else:
                try:
                    obj = self.orm_db.query(UserTable).filter_by(
                        uname=c_uname, upwd=c_upwd, url_id=curl_id).one()
                    self.orm_db.delete(obj)
                    self.orm_db.commit()
                    self.mw.web_pwd_le.setText("")
                    self.reloadTableModel(self.unameTableModel, False)

                    # self.mw.web_user_cb.setCurrentIndex(0)
                except:
                    pass

    # -------------user------------- ↑

    # -------------table query------------- ↓
    def get_watching(self):
        # try:
        WatchIterator = github.get_watching(self)
        self.mw.thread_pools.spawn(self.get_watching_querydb, WatchIterator)

    def get_watching_querydb(self, WatchIterator):
        for watched_repo in WatchIterator:
            query_field = "/" + watched_repo.full_name
            WatchingTable_obj: WatchingTable = self.orm_db.query(
                WatchingTable).filter_by(query_field=query_field).first()
            if WatchingTable_obj is None:
                uname = self.get_c_uname()
                self.orm_db.add(
                    WatchingTable(query_field=query_field, uname=uname))
                self.orm_db.commit()
                model: QSqlTableModel = self.watching_show_model
                model.select()  # TODO:重新载入, 可能可以改成局部刷新.
            else:
                pass

    def del_watching(self, tv, data_dict):
        data = data_dict.get("data")  # type:list
        del_watching_repo = data[0]
        print(f"{del_watching_repo}")
        print(self.get_c_tableName())
        if self.get_c_tableName() == WATCHING_TABLE_NAME:
            print(f"delete")
            github.del_watching(del_watching_repo)
        else:
            pass

    # TODO:查数据库 ,可能用协程

    def re_select_model(self):
        model = self.get_c_model()
        model.select()

    def filterAll(self):
        """
        过滤
        :return:
        """
        query_fields = self.mw.filter_le.text().strip()  # 小注释 : e.g."py test "
        query_field_list = query_fields.split(" ")  # 小注释 : space split
        tableName = self.mw.filter_cb.currentText()

        # 小注释 : 单表
        if tableName != "All":
            TableClass = getTable(tableName)  ##type:BaseComment
            self.filterModel.setQuery(
                QSqlQuery(
                    self.ORM2SQL(
                        select([TableClass]).where(
                            or_(*tuple(
                                map(
                                    lambda query_field: TableClass.comment.
                                    like(f'%{query_field}%'),
                                    query_field_list))
                                # 小注释 :
                                # TableClass.comment.like(f'%{query_field_list[0]}%'),
                                # TableClass.comment.like(f'%{query_field_list[1]}%'),
                                # TableClass.comment.like(f'%{query_field_list[2]}%'),
                                )))))
        # 小注释 : 多表 union
        else:
            tw = self.mw.repo_tabWidget
            tabCount = tw.count()
            end = tabCount - 1
            sql = ""
            for i in range(tabCount):
                tableName = tw.tabText(i)
                condition = " OR ".join(
                    list(
                        map(
                            lambda query_field:
                            f"""{tableName}.comment LIKE '%{query_field}%' """,
                            query_field_list)))
                sql += f"""SELECT * From {tableName} WHERE {condition}"""
                if i != end:
                    sql += " UNION ALL " if self.mw.filter_ckBox.isChecked(
                    ) else " UNION "
            # print(sql)
            self.filterModel.setQuery(QSqlQuery(sql))

    # -------------table query------------- ↑

    def ORM2SQL(self, statement):
        """

        :param statement:
        :return:
        """
        query = statement.compile(dialect=sqlite.dialect())
        query_str = str(query)
        query_paras: dict = query.params
        query_raw_sql = query_str.replace('?', r"%r") % tuple(
            query_paras.values())
        # print("==query_raw_sql==↓:")
        # print(query_raw_sql)
        # print("==query_raw_sql==↑:")
        return query_raw_sql
コード例 #12
0
class Filtrage_bope_dialog(QDialog, Ui_dlgBopeRechercheForm):
    '''
    Class de la fenêtre permettant le filtrage attributaire des baux de pêche

    :param QDialog: Permet d'afficher l'interface graphique comme une fenêtre indépendante
    :type QDialog: QDialog

    :param Ui_dlgBopeRechercheForm: Class du script de l'interface graphique du formulaire,
            apporte les éléments de l'interface
    :type Ui_dlgBopeRechercheForm: class
    '''
    def __init__(self, db, dbType, dbSchema, modelBauxPe, parent=None):
        '''
        Constructeur, récupération de variable, connection des événements et remplissage des combobox

        :param db: définie dans le setupModel(),
                représente la connexion avec la base de données
        :type db: QSqlDatabase

        :param dbType: type de la base de données (postgre)
        :type dbType: str

        :param dbSchema: nom du schéma sous PostgreSQL contenant les données (data)
        :type dbSchema: unicode

        :param modelBauxPe: modèle droit de pêche qui contient les données de la base de données
        :type modelBauxPe: QSqlRelationalTableModel

        :param parent: défini que cette fenêtre n'hérite pas d'autres widgets
        :type parent: NoneType
        '''
        super(Filtrage_bope_dialog, self).__init__(parent)
        self.db = db
        self.dbType = dbType
        self.dbSchema = dbSchema
        self.modelBauxPe = modelBauxPe
        self.setupUi(self)

        self.btnAnnuler.clicked.connect(self.reject)
        self.btnExec.clicked.connect(self.execution)
        self.btnRaz.clicked.connect(self.raz)
        self.btnEt.clicked.connect(self.et)
        self.btnOu.clicked.connect(self.ou)

        self.btnPrevisualiser.clicked.connect(self.previSql)

        self.btnId.clicked.connect(self.ajoutId)
        self.btnRiviere.clicked.connect(self.ajoutRiviere)
        self.btnAappma.clicked.connect(self.ajoutAappma)
        self.btnPossession.clicked.connect(self.ajoutPossession)
        self.btnSign.clicked.connect(self.ajoutDateSign)
        self.btnFin.clicked.connect(self.ajoutDateFin)
        self.btnC.clicked.connect(self.ajoutCommune)
        self.btnCS.clicked.connect(self.ajoutComSection)
        self.btnCSP.clicked.connect(self.ajoutComSecParcelle)
        self.btnProprio.clicked.connect(self.ajoutProprio)
        self.btnAdresse.clicked.connect(self.ajoutAdresse)

        self.btnEt.setEnabled(False)
        self.btnOu.setEnabled(False)

        self.leTel.setInputMask("#9999999999999")

        self.possessionBool = False
        self.aappmaBool = False
        self.anneeSignBool = False
        self.anneeFinBool = False
        self.CBool = False
        self.CSBool = False
        self.CSPBool = False

        self.wwhere = ""
        self.wwherePossession = ""
        self.wwhereProprio = ""

        self.modelAappma = QSqlTableModel(self, self.db)
        wrelation = "aappma"
        if self.dbType == "postgres":
            wrelation = self.dbSchema + "." + wrelation
        self.modelAappma.setTable(wrelation)
        self.modelAappma.setSort(1, Qt.AscendingOrder)
        if (not self.modelAappma.select()):
            QMessageBox.critical(self, u"Remplissage du modèle AAPPMA",
                                 self.modelAappma.lastError().text(),
                                 QMessageBox.Ok)
        self.cmbAappma.setModel(self.modelAappma)
        self.cmbAappma.setModelColumn(self.modelAappma.fieldIndex("apma_nom"))

        self.modelRiviere = QSqlTableModel(self, self.db)
        wrelation = "cours_eau"
        if self.dbType == "postgres":
            wrelation = self.dbSchema + "." + wrelation
        self.modelRiviere.setTable(wrelation)
        self.modelRiviere.setFilter("ceau_nom <> 'NR'")
        self.modelRiviere.setSort(2, Qt.AscendingOrder)
        if (not self.modelRiviere.select()):
            QMessageBox.critical(self, u"Remplissage du modèle Rivière",
                                 self.modelRiviere.lastError().text(),
                                 QMessageBox.Ok)
        self.cmbRiviere.setModel(self.modelRiviere)
        self.cmbRiviere.setModelColumn(
            self.modelRiviere.fieldIndex("ceau_nom"))

        self.modelCommune = QSqlTableModel(self, self.db)
        wrelation = "commune"
        if self.dbType == "postgres":
            wrelation = self.dbSchema + "." + wrelation
        self.modelCommune.setTable(wrelation)
        self.modelCommune.setSort(2, Qt.AscendingOrder)
        if (not self.modelCommune.select()):
            QMessageBox.critical(self, u"Remplissage du modèle Commune",
                                 self.modelCommune.lastError().text(),
                                 QMessageBox.Ok)
        self.cmbCommune.setModel(self.modelCommune)
        self.cmbCommune.setModelColumn(self.modelCommune.fieldIndex("com_nom"))
        self.cmbCommune.setCurrentIndex(1)

        self.modelSection = QSqlQueryModel(self)
        self.modelParcelle = QSqlQueryModel(self)

        self.cmbSection.setModel(self.modelSection)
        self.cmbSection.setModelColumn(2)

        self.cmbParcelle.setModel(self.modelParcelle)
        self.cmbParcelle.setModelColumn(1)

        self.cmbCommune.currentIndexChanged.connect(self.changeCmbCommune)
        self.cmbSection.currentIndexChanged.connect(self.changeCmbSection)

        self.cmbCommune.setCurrentIndex(0)

    def reject(self):
        '''Ferme la fenêtre si clic sur le bouton annuler'''

        QDialog.reject(self)

    def changeCmbCommune(self, newInd):
        '''
        Filtre la combobox section en fonction de la commune affichée dans celle des communes

        :param newInd: index courant dans la combobox
        :type newInd: int
        '''
        self.modelParcelle.clear()
        self.cmbParcelle.clear()
        self.cmbParcelle.setModel(self.modelParcelle)
        self.cmbParcelle.setModelColumn(1)

        record = self.modelCommune.record(newInd)
        wcom_insee = record.value(self.modelCommune.fieldIndex("com_insee"))

        self.modelSection.clear()
        wrelation = "section"
        if self.dbType == "postgres":
            wrelation = self.dbSchema + "." + wrelation
        self.modelSection.setQuery(
            "select sec_id, sec_nom || ' ; ' || sec_com_abs from " +
            wrelation +
            " where sec_com_insee = '%s' order by sec_nom;" % str(wcom_insee),
            self.db)
        if self.modelSection.lastError().isValid():
            QMessageBox.critical(self, u"Remplissage du modèle Section",
                                 self.modelSection.lastError().text(),
                                 QMessageBox.Ok)

        self.cmbSection.setModel(self.modelSection)
        self.cmbSection.setModelColumn(1)

        self.cmbSection.setCurrentIndex(0)

    def changeCmbSection(self, newInd):
        '''
        Filtre la combobox parcelle en fonction de la section affichée dans celle des sections

        :param newInd: index courant dans la combobox
        :type newInd: int
        '''
        record = self.modelSection.record(newInd)
        wsec_id = record.value(0)
        self.cmbParcelle.clear()

        self.modelParcelle.clear()
        wrelation = "parcelle"
        if self.dbType == "postgres":
            wrelation = self.dbSchema + "." + wrelation
        self.modelParcelle.setQuery(
            "select par_id, par_numero from " + wrelation +
            " where par_sec_id = '%s' order by par_numero;" % str(wsec_id),
            self.db)
        if self.modelParcelle.lastError().isValid():
            QMessageBox.critical(self, u"Remplissage du modèle Parcelle",
                                 self.modelParcelle.lastError().text(),
                                 QMessageBox.Ok)

        self.cmbParcelle.setModel(self.modelParcelle)
        self.cmbParcelle.setModelColumn(1)

    def raz(self):
        '''Réinitialise toutes les variables de la fenêtre afin de recommencer une nouvelle requête'''

        self.possessionBool = False
        self.aappmaBool = False
        self.anneeSignBool = False
        self.anneeFinBool = False
        self.CBool = False
        self.CSBool = False
        self.CSPBool = False

        self.spnId.setValue(0)
        self.wrq = ""
        self.txtSql.setText("")
        self.wwhere = ""
        self.wwherePossession = ""
        self.wwhereProprio = ""

        self.chkPossession.setChecked(False)
        self.dateSign.setDate(QDate(2000, 1, 1))
        self.dateFin.setDate(QDate(2000, 1, 1))

        self.btnEt.setEnabled(False)
        self.btnOu.setEnabled(False)

        self.btnId.setEnabled(True)
        self.btnSign.setEnabled(True)
        self.btnFin.setEnabled(True)
        self.btnRiviere.setEnabled(True)
        self.btnAappma.setEnabled(True)
        self.btnPossession.setEnabled(True)
        self.btnC.setEnabled(True)
        self.btnCS.setEnabled(True)
        self.btnCSP.setEnabled(True)
        self.btnProprio.setEnabled(True)
        self.btnAdresse.setEnabled(True)

    def et(self):
        '''Change l'état des boutons et ajoute "and" à la requête'''

        self.btnEt.setEnabled(False)
        self.btnOu.setEnabled(False)

        self.btnId.setEnabled(True)
        self.btnRiviere.setEnabled(True)
        self.btnProprio.setEnabled(True)
        self.btnAdresse.setEnabled(True)

        if self.possessionBool == False:
            self.btnPossession.setEnabled(True)

        if self.aappmaBool == False:
            self.btnAappma.setEnabled(True)

        if self.anneeSignBool == False:
            self.btnSign.setEnabled(True)

        if self.anneeFinBool == False:
            self.btnFin.setEnabled(True)

        if self.CBool == False:
            self.btnC.setEnabled(True)

        if self.CSBool == False:
            self.btnCS.setEnabled(True)

        if self.CSPBool == False:
            self.btnCSP.setEnabled(True)

        self.wwhere += " AND "

    def ou(self):
        '''Change l'état des boutons et ajoute "or" à la requête'''

        self.btnEt.setEnabled(False)
        self.btnOu.setEnabled(False)

        self.btnId.setEnabled(True)
        self.btnSign.setEnabled(True)
        self.btnFin.setEnabled(True)
        self.btnRiviere.setEnabled(True)
        self.btnAappma.setEnabled(True)
        self.btnC.setEnabled(True)
        self.btnCS.setEnabled(True)
        self.btnCSP.setEnabled(True)
        self.btnProprio.setEnabled(True)
        self.btnAdresse.setEnabled(True)
        self.btnPossession.setEnabled(True)

        self.possessionBool = False
        self.aappmaBool = False
        self.anneeSignBool = False
        self.anneeFinBool = False
        self.CBool = False
        self.CSBool = False
        self.CSPBool = False

        self.wwhere += " OR "

    def ajoutId(self):
        '''Change l'état des boutons et ajoute un critère d'id à la requête'''

        self.btnOu.setEnabled(True)

        self.btnId.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnFin.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnPossession.setEnabled(False)
        self.btnC.setEnabled(False)
        self.btnCS.setEnabled(False)
        self.btnCSP.setEnabled(False)
        self.btnProprio.setEnabled(False)
        self.btnAdresse.setEnabled(False)

        self.wid = self.spnId.value()
        if self.spnId.value() != "":
            if self.wid != "":
                self.wwhere += "bope_id = '" + str(self.wid) + "'"
        self.spnId.setValue(0)
        self.spnId.setFocus()

    def ajoutRiviere(self):
        '''Change l'état des boutons et ajoute un critère de cours d'eau à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnId.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnFin.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnPossession.setEnabled(False)
        self.btnC.setEnabled(False)
        self.btnCS.setEnabled(False)
        self.btnCSP.setEnabled(False)
        self.btnProprio.setEnabled(False)
        self.btnAdresse.setEnabled(False)

        wfrombce = "bail_cours_eau"
        if self.dbType == "postgres":
            self.wfromCeau = self.dbSchema + "." + wfrombce

        wrecord = self.cmbRiviere.model().record(
            self.cmbRiviere.currentIndex())
        self.wCeau = wrecord.value(0)
        if self.cmbRiviere.currentText() != "":
            if self.wCeau != "":
                self.wwhere += "bope_id in (select distinct bce_bope_id from " + self.wfromCeau + " where bce_ceau_id = '" + str(
                    self.wCeau) + "')"

    def ajoutAappma(self):
        '''Change l'état des boutons et ajoute un critère d'aappma à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnId.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnFin.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnPossession.setEnabled(False)
        self.btnC.setEnabled(False)
        self.btnCS.setEnabled(False)
        self.btnCSP.setEnabled(False)
        self.btnProprio.setEnabled(False)
        self.btnAdresse.setEnabled(False)

        self.aappmaBool = True

        wrecord = self.cmbAappma.model().record(self.cmbAappma.currentIndex())
        self.wbope_aappma = wrecord.value(0)
        if self.cmbAappma.currentText() != "":
            if self.wbope_aappma != "":
                self.wwhere += "bope_apma_id = '" + str(
                    self.wbope_aappma) + "'"

    def ajoutPossession(self):
        '''Change l'état des boutons et ajoute un critère de possession à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnId.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnFin.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnPossession.setEnabled(False)
        self.btnC.setEnabled(False)
        self.btnCS.setEnabled(False)
        self.btnCSP.setEnabled(False)
        self.btnProprio.setEnabled(False)
        self.btnAdresse.setEnabled(False)

        self.possessionBool = True

        if self.chkPossession.isChecked() == True:
            self.wwherePossession = "bope_existe = True"
        else:
            self.wwherePossession = "bope_existe = False"

        self.wwhere += self.wwherePossession

    def ajoutDateSign(self):
        '''Change l'état des boutons et ajoute un critère de date de signature à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnId.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnFin.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnPossession.setEnabled(False)
        self.btnC.setEnabled(False)
        self.btnCS.setEnabled(False)
        self.btnCSP.setEnabled(False)
        self.btnProprio.setEnabled(False)
        self.btnAdresse.setEnabled(False)

        self.anneeSignBool = True

        self.wbope_date_sign = self.dateSign.date().toString("yyyy")
        if self.wbope_date_sign != "":
            self.wwhere += "date_part('year', bope_date_sign) = '" + str(
                self.wbope_date_sign) + "'"

    def ajoutDateFin(self):
        '''Change l'état des boutons et ajoute un critère de date d'expiration à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnId.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnFin.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnPossession.setEnabled(False)
        self.btnC.setEnabled(False)
        self.btnCS.setEnabled(False)
        self.btnCSP.setEnabled(False)
        self.btnProprio.setEnabled(False)
        self.btnAdresse.setEnabled(False)

        self.anneeFinBool = True

        self.wbope_date_fin = self.dateFin.date().toString("yyyy")
        if self.wbope_date_fin != "":
            self.wwhere += "date_part('year', bope_date_fin) = '" + str(
                self.wbope_date_fin) + "'"

    def ajoutCommune(self):
        '''Change l'état des boutons et ajoute un critère de commune à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnId.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnFin.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnPossession.setEnabled(False)
        self.btnC.setEnabled(False)
        self.btnCS.setEnabled(False)
        self.btnCSP.setEnabled(False)
        self.btnProprio.setEnabled(False)
        self.btnAdresse.setEnabled(False)

        self.CBool = True

        wrecord = self.cmbCommune.model().record(
            self.cmbCommune.currentIndex())
        self.wcommune = wrecord.value(0)
        if self.cmbCommune.currentText() != "":
            if self.wcommune != "":
                self.wwhere += "bope_id in (select bope_id from data.droit_peche, data.parcelle, data.section where bope_id = par_bope_id and par_sec_id = sec_id and sec_com_insee = '" + str(
                    self.wcommune) + "')"

    def ajoutComSection(self):
        '''Change l'état des boutons et ajoute un critère de commune et section à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnId.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnFin.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnPossession.setEnabled(False)
        self.btnC.setEnabled(False)
        self.btnCS.setEnabled(False)
        self.btnCSP.setEnabled(False)
        self.btnProprio.setEnabled(False)
        self.btnAdresse.setEnabled(False)

        self.CSBool = True

        wrecord = self.cmbSection.model().record(
            self.cmbSection.currentIndex())
        self.wsection = wrecord.value(0)
        if self.cmbSection.currentText() != "":
            if self.wsection != "":
                self.wwhere += "bope_id in (select bope_id from data.droit_peche, data.parcelle where par_bope_id = bope_id and par_sec_id = '" + str(
                    self.wsection) + "')"

    def ajoutComSecParcelle(self):
        '''Change l'état des boutons et ajoute un critère de commune, section et parcelle à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnId.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnFin.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnPossession.setEnabled(False)
        self.btnC.setEnabled(False)
        self.btnCS.setEnabled(False)
        self.btnCSP.setEnabled(False)
        self.btnProprio.setEnabled(False)
        self.btnAdresse.setEnabled(False)

        self.CSPBool = True

        wrecord = self.cmbParcelle.model().record(
            self.cmbParcelle.currentIndex())
        self.wparcelle = wrecord.value(0)
        if self.cmbParcelle.currentText() != "":
            if self.wparcelle != "":
                self.wwhere += "bope_id in (select par_bope_id from data.parcelle where par_id = '" + str(
                    self.wparcelle) + "')"

    def ajoutProprio(self):
        '''
        Change l'état des boutons et ajoute un critère de nom de propriétaire
        et / ou de mail
        et / ou de téléphone à la requête
        '''
        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnId.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnFin.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnPossession.setEnabled(False)
        self.btnC.setEnabled(False)
        self.btnCS.setEnabled(False)
        self.btnCSP.setEnabled(False)
        self.btnProprio.setEnabled(False)
        self.btnAdresse.setEnabled(False)

        self.wwhereProprio = ""
        self.wnom = self.leNom.text()
        if "'" in self.wnom and "''" not in self.wnom:
            self.wnom = self.wnom.replace("'", "''")
        self.wmail = self.leMail.text()
        self.wtel = self.leTel.text()
        if self.leNom.text() != "":
            if self.wnom != "":
                self.wwhereProprio += " bope_id in (select bope_id from data.droit_peche, data.proprietaire where bope_pro_id = pro_id and pro_nom ilike '%" + self.wnom + "%')"
        if self.leMail.text() != "":
            if self.wmail != "":
                if self.wnom != "":
                    self.wwhereProprio += " and "
                self.wwhereProprio += " bope_id in (select bope_id from data.droit_peche, data.proprietaire where bope_pro_id = pro_id and pro_mail = '" + self.wmail + "')"
        if self.leTel.text() != "":
            if self.wtel != "":
                if self.wnom != "" or self.wmail != "":
                    self.wwhereProprio += " and "
                self.wwhereProprio += " bope_id in (select bope_id from data.droit_peche, data.proprietaire where bope_pro_id = pro_id and pro_telephone = '" + str(
                    self.wtel) + "')"

        if self.wwhereProprio != "":
            self.wwhere += self.wwhereProprio

        self.leNom.setText("")
        self.leMail.setText("")
        self.leTel.setText("")
        self.leNom.setFocus()

    def ajoutAdresse(self):
        '''Change l'état des boutons et ajoute un critère d'adresse à la requête'''

        self.btnEt.setEnabled(True)
        self.btnOu.setEnabled(True)

        self.btnId.setEnabled(False)
        self.btnSign.setEnabled(False)
        self.btnFin.setEnabled(False)
        self.btnRiviere.setEnabled(False)
        self.btnAappma.setEnabled(False)
        self.btnPossession.setEnabled(False)
        self.btnC.setEnabled(False)
        self.btnCS.setEnabled(False)
        self.btnCSP.setEnabled(False)
        self.btnProprio.setEnabled(False)
        self.btnAdresse.setEnabled(False)

        self.wadresse = self.leAdresse.text()
        if "'" in self.wadresse and "''" not in self.wadresse:
            self.wadresse = self.wadresse.replace("'", "''")
        if self.leAdresse.text() != "":
            if self.wadresse != "":
                self.wwhere += " bope_id in (select distinct bope_id from data.droit_peche, data.proprietaire where (bope_pro_id = pro_id ) and (pro_adresse ilike '%" + self.wadresse + "%'))"
        else:
            if self.leAdresse.text() == "":
                if self.wadresse == "":
                    self.wwhere += " bope_id in (select distinct bope_id from data.droit_peche, data.proprietaire where (bope_pro_id = pro_id )"

        self.leAdresse.setText("")
        self.leAdresse.setFocus()

    def creaRequete(self):
        # def previSql(self):
        '''Regroupe les différentes variables contenant les clauses de la requête SQL et les concatène pour en faire une requête exécutable'''

        self.wrq = ""

        # Construit la clause FROM de la requête
        cfrom = "droit_peche"
        if self.dbType == "postgres":
            cfrom = self.dbSchema + "." + cfrom

        # Construit la clause SELECT et ajoute la clause FROM à la requête
        self.wrq = "SELECT DISTINCT bope_id FROM " + cfrom

        # Construit la clause WHERE et ORDER BY et l'ajoute à la requête
        if self.wwhere != "":
            #Supprime l'opérateur "and" ou "or" si celui-ci n'est pas suivi d'un critère
            operateurs = ["AND", "OR"]
            fin_where = self.wwhere[-5:]
            for ext in operateurs:
                if ext in fin_where:
                    self.wwhere = self.wwhere[:-4]
            self.wrq += " WHERE " + self.wwhere + " ORDER BY bope_id"
        else:
            self.wrq += " ORDER BY bope_id"

    def previSql(self):
        '''Permet de prévisualiser la requête avant de l'éxecuter'''
        self.txtSql.setText("")
        self.creaRequete()

        # Affiche la requête
        self.txtSql.setText(self.wrq)

    def execution(self):
        '''Permet d'éxecuter la requête'''

        # Vérifie la non présence de mot pouvant endommager la base de données
        erreur = False
        interdit = [
            "update", "delete", "insert", "intersect", "duplicate", "merge",
            "truncate", "create", "drop", "alter"
        ]
        if self.txtSql.toPlainText() != "":
            self.requete = self.txtSql.toPlainText()
        else:
            self.creaRequete()
            self.requete = self.wrq
        testRequete = self.requete.lower()
        for mot in interdit:
            if mot in testRequete:
                erreur = True
        if erreur == True:
            QMessageBox.critical(
                self, u"Erreur SQL",
                u"Vous essayez d'exécuter une requête qui peut endommager la base de données !",
                QMessageBox.Ok)
        # Après récupération du contenu de la zone de texte, exécute la requête
        else:
            query = QSqlQuery(self.db)
            query.prepare(self.requete)
            if query.exec_():
                wparam = ""
                while query.next():
                    wparam += str(query.value(0)) + ","
                if (wparam != ""):
                    wparam = "(" + wparam[0:len(wparam) - 1] + ")"
                    if self.modelBauxPe:
                        # Filtre le modèle des droits de pêche et ferme la fenêtre
                        self.modelBauxPe.setFilter("bope_id in %s" % wparam)
                        self.modelBauxPe.select()
                        QDialog.accept(self)
                else:
                    QMessageBox.information(
                        self, "Filtrage",
                        u"Aucun bail de pêche ne correspond aux critères ...",
                        QMessageBox.Ok)
            else:
                QMessageBox.critical(self, u"Erreur SQL",
                                     query.lastError().text(), QMessageBox.Ok)
コード例 #13
0
class CTDIVolTab(QDialog):
    def __init__(self, ctx, *args, **kwargs):
        super(CTDIVolTab, self).__init__(*args, **kwargs)
        self.ctx = ctx
        self.prev_mode = 0
        self.method = 0
        self.calc_3d_method = 'slice step'
        self.dcm_3d_method = 'slice step'
        self.all_slices = False
        self.all_slices_dcm = False
        self.all_slices_man = False
        self.show_graph_ctd = False
        self.show_graph_tcm = False
        self.show_graph_ctd_dcm = False
        self.show_graph_tcm_dcm = False
        self.adjust_tcm = False
        self.initVar()
        self.initModel()
        self.initUI()
        self.setModel()
        self.sigConnect()

    def initVar(self):
        self.CTDI = 0
        self.DLP = 0
        self.tube_current = 100
        self.rotation_time = 1
        self.pitch = 1
        self.coll = 0
        self.scan_length = 10
        self.mAs = 0
        self.eff_mAs = 0
        self.CTDIw = 0
        self.CTDIv = 0
        self.idxs = []
        self.currents = []
        self.ctdivs = []
        self.disable_warning = False

    def initModel(self):
        self.brand_query = QSqlTableModel(db=self.ctx.database.ctdi_db)
        self.scanner_query = QSqlTableModel(db=self.ctx.database.ctdi_db)
        self.volt_query = QSqlTableModel(db=self.ctx.database.ctdi_db)
        self.coll_query = QSqlTableModel(db=self.ctx.database.ctdi_db)

        # fill brand combobox
        self.brand_query.setTable("BRAND")
        self.brand_query.select()
        self.brand_id = self.brand_query.record(0).value("ID")

        # fill scanner combobox
        self.scanner_query.setTable("SCANNER")
        self.scanner_query.setFilter("BRAND_ID=1")
        self.scanner_query.select()
        self.scanner_id = self.scanner_query.record(0).value("ID")

        # fill voltage combobox
        self.volt_query.setTable("CTDI_DATA")
        self.volt_query.setFilter("SCANNER_ID=1")
        self.volt_query.select()
        self.CTDI = self.volt_query.record(0).value("CTDI_HEAD")

        # fill collimation combobox
        self.coll_query.setTable("COLLIMATION_DATA")
        self.coll_query.setFilter("SCANNER_ID=1")
        self.coll_query.select()
        self.coll = self.coll_query.record(0).value("VALUE")

    def setModel(self):
        self.brand_cb.setModel(self.brand_query)
        self.brand_cb.setModelColumn(self.brand_query.fieldIndex("NAME"))
        self.scanner_cb.setModel(self.scanner_query)
        self.scanner_cb.setModelColumn(self.scanner_query.fieldIndex("NAME"))
        self.volt_cb.setModel(self.volt_query)
        self.volt_cb.setModelColumn(self.volt_query.fieldIndex("VOLTAGE"))
        self.coll_cb.setModel(self.coll_query)
        self.coll_cb.setModelColumn(self.coll_query.fieldIndex("COL_OPTS"))

        self.on_brand_changed(0)
        self.brand_items = [
            self.brand_cb.itemText(i).lower()
            for i in range(self.brand_cb.count())
        ]

    def sigConnect(self):
        self.opts.activated[int].connect(self.on_set_method)
        self.brand_cb.activated[int].connect(self.on_brand_changed)
        self.scanner_cb.activated[int].connect(self.on_scanner_changed)
        self.volt_cb.activated[int].connect(self.on_volt_changed)
        self.coll_cb.activated[int].connect(self.on_coll_changed)
        self.tube_current_edit.textChanged[str].connect(
            self.on_tube_current_changed)
        self.rotation_time_edit.textChanged[str].connect(
            self.on_rotation_time_changed)
        self.pitch_edit.textChanged[str].connect(self.on_pitch_changed)
        self.scan_length_c_edit.textChanged[str].connect(
            self.on_scan_length_changed)
        self.ctdiv_m_edit.textChanged[str].connect(self.on_ctdiv_changed)
        self.dlp_m_edit.textChanged[str].connect(self.on_dlp_changed)
        self.tcm_btn.clicked.connect(self.on_get_tcm)
        self.scn_btn.clicked.connect(self.get_scan_length_dicom)
        self.get_info_btn.clicked.connect(self.on_get_info)
        self.calc_btn.clicked.connect(self.calculate_method)
        self.calc_dcm_btn.clicked.connect(self.calculate_dcm)
        self.calc_man_btn.clicked.connect(self.calculate_man)
        self.ctx.app_data.imgChanged.connect(self.img_changed_handle)
        self.ctx.app_data.imgLoaded.connect(self.img_loaded_handle)
        self.show_graph_ctd_chk.stateChanged.connect(
            self.on_show_graph_ctd_state)
        self.show_graph_tcm_chk.stateChanged.connect(
            self.on_show_graph_tcm_state)
        self.show_graph_ctd_dcm_chk.stateChanged.connect(
            self.on_show_graph_ctd_dcm_state)
        self.show_graph_tcm_dcm_chk.stateChanged.connect(
            self.on_show_graph_tcm_dcm_state)
        self.tcm_correction_chk.stateChanged.connect(
            self.on_tcm_correction_state)
        self.all_slices_man_chk.stateChanged.connect(self.on_mode_man_changed)
        self.mode_calc_cb.activated[int].connect(self.on_mode_calc_changed)
        self.mode_dcm_cb.activated[int].connect(self.on_mode_dcm_changed)
        [
            btn.toggled.connect(self.on_calc_3d_opts_changed)
            for btn in self.calc_d3opts_rbtns
        ]
        [
            btn.toggled.connect(self.on_dcm_3d_opts_changed)
            for btn in self.dcm_d3opts_rbtns
        ]

    def initUI(self):
        self.figure = PlotDialog()
        self.opts = QComboBox()
        self.opts.addItems(['Calculation', 'Get from DICOM', 'Input Manually'])

        self.brand_cb = QComboBox()
        self.scanner_cb = QComboBox()
        self.volt_cb = QComboBox()
        self.coll_cb = QComboBox()
        self.mode_calc_cb = QComboBox(self)
        self.mode_dcm_cb = QComboBox(self)

        self.mode_calc_cb.addItems(['One slice', 'Z-axis'])
        self.mode_dcm_cb.addItems(['One slice', 'Z-axis'])

        self.tube_current_edit = QLineEdit(f'{self.tube_current}')
        self.rotation_time_edit = QLineEdit(f'{self.rotation_time}')
        self.pitch_edit = QLineEdit(f'{self.pitch}')
        self.scan_length_c_edit = QLineEdit(f'{self.scan_length}')
        self.scan_length_d_edit = QLineEdit(f'{self.scan_length}')

        self.mas_edit = QLineEdit('0')
        self.mas_eff_edit = QLineEdit('0')
        self.ctdiw_edit = QLineEdit('0')
        self.ctdiv_c_edit = QLineEdit('0')
        self.ctdiv_m_edit = QLineEdit('0')
        self.ctdiv_d_edit = QLineEdit('0')
        self.dlp_c_edit = QLineEdit('0')
        self.dlp_m_edit = QLineEdit('0')
        self.dlp_d_edit = QLineEdit('0')

        self.ctdiv_d_label = QLabel('CTDI<sub>vol</sub> (mGy)')
        # self.ctdiv_d2_label = QLabel('CTDI<sub>vol</sub> (mGy)')

        self.get_info_btn = QPushButton('Get Info')
        self.calc_btn = QPushButton('Calculate')
        self.calc_dcm_btn = QPushButton('Calculate')
        self.calc_man_btn = QPushButton('Input')
        self.tcm_btn = QPushButton('TCM')
        self.scn_btn = QPushButton('Scn Len')
        self.next_tab_btn = QPushButton('Next')
        self.prev_tab_btn = QPushButton('Previous')
        self.prev_tab_btn.setVisible(False)

        self.show_graph_tcm_chk = QCheckBox('Show mA Graph')
        self.show_graph_ctd_chk = QCheckBox('Show CTDIvol Graph')
        self.show_graph_tcm_dcm_chk = QCheckBox('Show mA Graph')
        self.show_graph_ctd_dcm_chk = QCheckBox('Show CTDIvol Graph')
        self.tcm_correction_chk = QCheckBox('Adjust CTDIvol with mA')
        self.all_slices_man_chk = QCheckBox('All Slices')

        self.calc_slice1_sb = QSpinBox()
        self.calc_slice2_sb = QSpinBox()
        self.calc_to_lbl = QLabel('to')
        self.calc_d3opts_rbtns = [
            QRadioButton('Slice Step'),
            QRadioButton('Slice Number'),
            QRadioButton('Regional')
        ]
        self.calc_d3opts_rbtns[0].setChecked(True)

        self.dcm_slice1_sb = QSpinBox()
        self.dcm_slice2_sb = QSpinBox()
        self.dcm_to_lbl = QLabel('to')
        self.dcm_d3opts_rbtns = [
            QRadioButton('Slice Step'),
            QRadioButton('Slice Number'),
            QRadioButton('Regional')
        ]
        self.dcm_d3opts_rbtns[0].setChecked(True)

        self.calc_slice1_sb.setMaximum(self.ctx.total_img)
        self.calc_slice1_sb.setMinimum(1)
        self.calc_slice1_sb.setMinimumWidth(50)
        self.calc_slice2_sb.setMaximum(self.ctx.total_img)
        self.calc_slice2_sb.setMinimum(1)
        self.calc_slice2_sb.setMinimumWidth(50)
        self.calc_to_lbl.setHidden(True)
        self.calc_slice2_sb.setHidden(True)
        self.dcm_slice1_sb.setMaximum(self.ctx.total_img)
        self.dcm_slice1_sb.setMinimum(1)
        self.dcm_slice1_sb.setMinimumWidth(50)
        self.dcm_slice2_sb.setMaximum(self.ctx.total_img)
        self.dcm_slice2_sb.setMinimum(1)
        self.dcm_slice2_sb.setMinimumWidth(50)
        self.dcm_to_lbl.setHidden(True)
        self.dcm_slice2_sb.setHidden(True)

        self.brand_cb.setPlaceholderText('[Unavailable]')
        self.scanner_cb.setPlaceholderText('[Unavailable]')
        self.volt_cb.setPlaceholderText('[Unavailable]')
        self.coll_cb.setPlaceholderText('[Unavailable]')

        cbs = [
            self.brand_cb, self.scanner_cb, self.volt_cb, self.coll_cb,
            self.mode_calc_cb, self.mode_dcm_cb
        ]

        edits = [
            self.tube_current_edit,
            self.rotation_time_edit,
            self.pitch_edit,
            self.scan_length_c_edit,
            self.scan_length_d_edit,
            self.mas_edit,
            self.mas_eff_edit,
            self.ctdiw_edit,
            self.ctdiv_c_edit,
            self.ctdiv_m_edit,
            self.ctdiv_d_edit,
            self.dlp_c_edit,
            self.dlp_m_edit,
            self.dlp_d_edit,
        ]

        [edit.setValidator(QDoubleValidator()) for edit in edits]
        [edit.setMinimumWidth(100) for edit in edits]
        [edit.setMinimumWidth(100) for edit in cbs]
        [edit.setAlignment(Qt.AlignRight) for edit in edits]

        self.mas_edit.setReadOnly(True)
        self.mas_eff_edit.setReadOnly(True)
        self.ctdiw_edit.setReadOnly(True)
        self.ctdiv_c_edit.setReadOnly(True)
        self.dlp_c_edit.setReadOnly(True)

        self.switch_button_default()
        self.set_layout()

    def set_layout(self):
        font = QFont()
        font.setBold(True)
        self.ctdiv_c_edit.setFont(font)
        self.dlp_c_edit.setFont(font)

        manual_grpbox = QGroupBox('')
        manual_layout = QFormLayout()
        manual_layout.addRow(QLabel('CTDI<sub>vol</sub> (mGy)'),
                             self.ctdiv_m_edit)
        manual_layout.addRow(QLabel('DLP (mGy-cm))'), self.dlp_m_edit)
        manual_layout.addRow(self.calc_man_btn, QLabel(''))
        manual_layout.addRow(self.all_slices_man_chk, QLabel(''))
        manual_grpbox.setLayout(manual_layout)
        manual_grpbox.setFont(font)

        dcm_slice_layout = QHBoxLayout()
        dcm_slice_layout.addWidget(self.dcm_slice1_sb)
        dcm_slice_layout.addWidget(self.dcm_to_lbl)
        dcm_slice_layout.addWidget(self.dcm_slice2_sb)
        dcm_slice_layout.addStretch()

        self.dcm_d3opts_grpbox = QGroupBox('Z-axis Options')
        dcm_d3opts_layout = QVBoxLayout()
        [dcm_d3opts_layout.addWidget(btn) for btn in self.dcm_d3opts_rbtns]
        dcm_d3opts_layout.addLayout(dcm_slice_layout)
        dcm_d3opts_layout.addWidget(self.show_graph_ctd_dcm_chk)
        dcm_d3opts_layout.addWidget(self.show_graph_tcm_dcm_chk)
        dcm_d3opts_layout.addStretch()
        self.dcm_d3opts_grpbox.setLayout(dcm_d3opts_layout)
        self.dcm_d3opts_grpbox.setVisible(False)

        dicom_grpbox = QGroupBox('Parameter')
        dicom_layout = QFormLayout()
        dicom_layout.addRow(QLabel('Scan Length (cm)'),
                            self.scan_length_d_edit)
        dicom_layout.addRow(self.ctdiv_d_label, self.ctdiv_d_edit)
        dicom_layout.addRow(QLabel('DLP (mGy-cm)'), self.dlp_d_edit)
        # dicom_layout.addRow(QLabel('Option'), self.mode_dcm_cb)
        dicom_layout.addRow(self.calc_dcm_btn, self.tcm_correction_chk)
        dicom_grpbox.setLayout(dicom_layout)
        # dicom_grpbox.setFont(font)

        dcm_widget = QWidget(self)
        dcm_layout = QHBoxLayout()
        dcm_layout.addWidget(dicom_grpbox)
        dcm_layout.addWidget(self.dcm_d3opts_grpbox)
        dcm_layout.setContentsMargins(0, 0, 0, 0)
        dcm_widget.setLayout(dcm_layout)

        calc_btn_layout = QHBoxLayout()
        calc_btn_layout.addWidget(self.calc_btn)
        calc_btn_layout.addWidget(self.get_info_btn)

        calci_grpbox = QGroupBox('Parameter')
        calci_outer_layout = QVBoxLayout()
        calci_inner_layout = QFormLayout()
        calci_inner_layout.addRow(QLabel('Manufacturer'), self.brand_cb)
        calci_inner_layout.addRow(QLabel('Scanner'), self.scanner_cb)
        calci_inner_layout.addRow(QLabel('Voltage (kV)'), self.volt_cb)
        calci_inner_layout.addRow(QLabel('Tube Current (mA)'),
                                  self.tube_current_edit)
        calci_inner_layout.addRow(QLabel('Rotation Time (s)'),
                                  self.rotation_time_edit)
        calci_inner_layout.addRow(QLabel('Pitch'), self.pitch_edit)
        calci_inner_layout.addRow(QLabel('Collimation (mm)'), self.coll_cb)
        calci_inner_layout.addRow(QLabel('Scan Length (cm)'),
                                  self.scan_length_c_edit)
        # calci_inner_layout.addRow(QLabel('Option'), self.mode_calc_cb)
        calci_outer_layout.addLayout(calci_inner_layout)
        calci_outer_layout.addSpacerItem(QSpacerItem(1, 10))
        calci_outer_layout.addLayout(calc_btn_layout)
        calci_outer_layout.addStretch()
        calci_grpbox.setLayout(calci_outer_layout)

        calco_grpbox = QGroupBox('Output')
        calco_layout = QFormLayout()
        calco_layout.addRow(QLabel('mAs'), self.mas_edit)
        calco_layout.addRow(QLabel('Effective mAs'), self.mas_eff_edit)
        calco_layout.addRow(QLabel('CTDI<sub>w</sub> (mGy)'), self.ctdiw_edit)
        calco_layout.addRow(QLabel('<b>CTDI<sub>vol</sub> (mGy)</b>'),
                            self.ctdiv_c_edit)
        calco_layout.addRow(QLabel('<b>DLP (mGy-cm)</b>'), self.dlp_c_edit)
        calco_layout.setContentsMargins(11, 3, 11, 3)
        calco_layout.setVerticalSpacing(5)
        calco_grpbox.setLayout(calco_layout)
        # print(calco_layout.verticalSpacing())

        calc_slice_layout = QHBoxLayout()
        calc_slice_layout.addWidget(self.calc_slice1_sb)
        calc_slice_layout.addWidget(self.calc_to_lbl)
        calc_slice_layout.addWidget(self.calc_slice2_sb)
        calc_slice_layout.addStretch()
        calc_chk_layout = QVBoxLayout()
        calc_chk_layout.addWidget(self.show_graph_ctd_chk)
        calc_chk_layout.addWidget(self.show_graph_tcm_chk)
        calc_chk_layout.setSpacing(3)
        calc_radio_layout = QVBoxLayout()
        [calc_radio_layout.addWidget(btn) for btn in self.calc_d3opts_rbtns]
        calc_radio_layout.setSpacing(3)

        self.calc_d3opts_grpbox = QGroupBox('Z-axis Options')
        calc_d3opts_layout = QVBoxLayout()
        calc_d3opts_layout.addLayout(calc_radio_layout)
        calc_d3opts_layout.addLayout(calc_slice_layout)
        calc_d3opts_layout.addLayout(calc_chk_layout)
        calc_d3opts_layout.addStretch()
        calc_d3opts_layout.setContentsMargins(11, 3, 11, 3)
        calc_d3opts_layout.setSpacing(0)
        self.calc_d3opts_grpbox.setLayout(calc_d3opts_layout)
        self.calc_d3opts_grpbox.setVisible(False)

        calcr_layout = QVBoxLayout()
        calcr_layout.addWidget(calco_grpbox)
        calcr_layout.addWidget(self.calc_d3opts_grpbox)
        calcr_layout.setSpacing(0)
        # calcr_layout.addWidget(self.calc_graph_opts_grpbox)
        calcr_layout.addStretch()

        calc_widget = QWidget(self)
        calc_layout = QHBoxLayout()
        calc_layout.addWidget(calci_grpbox)
        calc_layout.addLayout(calcr_layout)
        calc_layout.setContentsMargins(0, 0, 0, 0)
        calc_widget.setLayout(calc_layout)

        self.stacks = QStackedLayout()
        self.stacks.addWidget(calc_widget)
        self.stacks.addWidget(dcm_widget)
        self.stacks.addWidget(manual_grpbox)

        self.emp = QLabel('')
        self.stackz = QStackedLayout()
        self.stackz.addWidget(self.mode_calc_cb)
        self.stackz.addWidget(self.mode_dcm_cb)
        self.stackz.addWidget(self.emp)
        self.emp.setVisible(False)

        tab_nav = QHBoxLayout()
        tab_nav.addWidget(self.prev_tab_btn)
        tab_nav.addStretch()
        tab_nav.addWidget(self.next_tab_btn)

        main_layout = QVBoxLayout()
        main_layout.addWidget(QLabel('Methods:'))
        main_layout.addWidget(self.opts)
        main_layout.addLayout(self.stackz)
        main_layout.addStretch()
        main_layout.addLayout(self.stacks)
        main_layout.addStretch()
        main_layout.addLayout(tab_nav)

        self.setLayout(main_layout)

    def switch_button_default(self, method=0):
        if method == 0:
            if self.method == 0:
                self.calc_btn.setAutoDefault(True)
                self.calc_btn.setDefault(True)
                self.calc_dcm_btn.setAutoDefault(False)
                self.calc_dcm_btn.setDefault(False)
            elif self.method == 1:
                self.calc_dcm_btn.setAutoDefault(True)
                self.calc_dcm_btn.setDefault(True)
                self.calc_btn.setAutoDefault(False)
                self.calc_btn.setDefault(False)
            self.next_tab_btn.setAutoDefault(False)
            self.next_tab_btn.setDefault(False)
        elif method == 1:
            self.next_tab_btn.setAutoDefault(True)
            self.next_tab_btn.setDefault(True)
            self.calc_btn.setAutoDefault(False)
            self.calc_btn.setDefault(False)
            self.calc_dcm_btn.setAutoDefault(False)
            self.calc_dcm_btn.setDefault(False)
        else:
            return
        self.prev_tab_btn.setAutoDefault(False)
        self.prev_tab_btn.setDefault(False)
        self.tcm_btn.setAutoDefault(False)
        self.tcm_btn.setDefault(False)
        self.scn_btn.setAutoDefault(False)
        self.scn_btn.setDefault(False)
        self.get_info_btn.setAutoDefault(False)
        self.get_info_btn.setDefault(False)

    def plot_ctdiv(self):
        xlabel = 'CTDIvol'
        title = 'CTDIvol'
        self.figure_ctdi = PlotDialog()
        self.figure_ctdi.actionEnabled(True)
        self.figure_ctdi.trendActionEnabled(False)
        self.figure_ctdi.plot(self.idxs,
                              self.ctdivs,
                              pen={
                                  'color': "FFFF00",
                                  'width': 2
                              },
                              symbol='o',
                              symbolPen=None,
                              symbolSize=8,
                              symbolBrush=(255, 0, 0, 255))
        self.figure_ctdi.axes.showGrid(True, True)
        self.figure_ctdi.setLabels('slice', xlabel, '', 'mGy')
        self.figure_ctdi.setTitle(f'Slice - {title}')
        self.figure_ctdi.show()

    def plot_tcm(self):
        xlabel = 'Tube Current'
        title = 'Tube Current'
        self.figure = PlotDialog()
        self.figure.actionEnabled(True)
        self.figure.trendActionEnabled(False)
        self.figure.plot(self.idxs,
                         self.currents,
                         pen={
                             'color': "FFFF00",
                             'width': 2
                         },
                         symbol='o',
                         symbolPen=None,
                         symbolSize=8,
                         symbolBrush=(255, 0, 0, 255))
        self.figure.axes.showGrid(True, True)
        self.figure.setLabels('slice', xlabel, '', 'mA')
        self.figure.setTitle(f'Slice - {title}')
        self.figure.show()

    def on_get_tcm(self):
        if not self.ctx.isImage and not self.disable_warning:
            QMessageBox.warning(None, "Warning",
                                "Open DICOM files first, or input manually")
            self.opts.setCurrentIndex(0)
            return
        self.currents = []
        self.idxs = []
        try:
            for idx, dcm in enumerate(self.ctx.dicoms):
                self.currents.append(float(dcm.XRayTubeCurrent))
                self.idxs.append(idx + 1)
        except Exception as e:
            self.currents = []
            self.idxs = []
            if not self.disable_warning:
                QMessageBox.warning(None, 'Exception Occured', str(e))
            return
        tube_current = sum(self.currents) / self.ctx.total_img
        self.tube_current_edit.setText(f'{tube_current:#.2f}')
        self.tube_current = tube_current
        self.plot_tcm()

    def get_ctdiv_dicom(self, idx):
        try:
            ctdiv = float(self.ctx.dicoms[idx - 1].CTDIvol)
        except:
            if not self.disable_warning:
                QMessageBox.warning(
                    None, "Warning",
                    "The DICOM does not contain the value of CTDIvol.\nPlease try different method."
                )
            ctdiv = 0
        return ctdiv

    def get_ctdiv_dicom_3d(self, idxs, dss):
        ctdivs = []
        currents = []
        to_remove = []
        for idx, dcm in zip(idxs, dss):
            if not hasattr(dcm, 'CTDIvol') or not hasattr(
                    dcm, 'XRayTubeCurrent'):
                to_remove.append(idx)
                continue
            ctdivs.append(float(dcm.CTDIvol))
            currents.append(float(dcm.XRayTubeCurrent))

        if len(to_remove) > 0:
            [idxs.remove(idx) for idx in to_remove if idx in idxs]
        self.currents = np.array(currents)
        self.ctdivs = np.array(ctdivs)
        return idxs

    def get_scan_length_dicom(self):
        if self.method == 0 and not self.ctx.isImage and not self.disable_warning:
            QMessageBox.warning(None, "Warning",
                                "Open DICOM files first, or input manually")
            self.opts.setCurrentIndex(0)
            return
        try:
            first = float(self.ctx.dicoms[0].SliceLocation)
            last = float(self.ctx.dicoms[-1].SliceLocation)
            width = float(self.ctx.dicoms[0].SliceThickness)
            try:
                second = float(self.ctx.dicoms[1].SliceLocation)
            except:
                second = last
        except Exception as e:
            if not self.disable_warning:
                QMessageBox.warning(None, 'Exception Occured', str(e))
            return

        lf = abs(0.1 * (last - first))
        sf = abs(0.1 * (second - first))
        print(lf, sf, width / 10)
        scan_length = (abs((last - first)) + abs(
            (second - first)) + width) * .1
        self.scan_length = scan_length
        if self.method == 0:
            self.scan_length_c_edit.setText(f'{self.scan_length:#.2f}')

    def on_get_info(self):
        if not self.ctx.isImage:
            QMessageBox.warning(None, "Warning",
                                "Open DICOM files first, or input manually")
            return

        def find_closest_value(array, number):
            return min(array, key=lambda x: abs(x - number))

        attrs = [
            'Manufacturer', 'ManufacturerModelName', 'KVP', 'XRayTubeCurrent',
            'ExposureTime', 'SpiralPitchFactor', 'TotalCollimationWidth'
        ]
        kv_pairs = {}
        missing_data = {}
        missing_attr = []
        for attr in attrs:
            try:
                kv_pairs[attr] = self.ctx.dicoms[self.ctx.current_img -
                                                 1][attr].value
            except KeyError:
                kv_pairs[attr] = 0
                missing_attr.append(attr)

        if kv_pairs[attrs[0]].lower() in self.brand_items:
            brand_id = self.brand_items.index(kv_pairs[attrs[0]].lower())
            self.brand_cb.setCurrentIndex(brand_id)
            self.on_brand_changed(brand_id)
            if kv_pairs[attrs[1]].lower() in self.scanner_items:
                scanner_id = self.scanner_items.index(
                    kv_pairs[attrs[1]].lower())
                self.scanner_cb.setCurrentIndex(scanner_id)
                self.on_scanner_changed(scanner_id)
                volt_id = self.volt_items.index(
                    find_closest_value(self.volt_items, kv_pairs[attrs[2]]))
                coll_id = self.coll_items.index(
                    find_closest_value(self.coll_items, kv_pairs[attrs[6]]))
                self.volt_cb.setCurrentIndex(volt_id)
                self.on_volt_changed(volt_id)
                self.coll_cb.setCurrentIndex(coll_id)
                self.on_coll_changed(coll_id)
            else:
                missing_data[attrs[1]] = kv_pairs[attrs[1]]
        else:
            missing_data[attrs[0]] = kv_pairs[attrs[0]]

        self.tube_current_edit.setText(f"{kv_pairs['XRayTubeCurrent']:#.2f}")
        self.rotation_time_edit.setText(
            f"{kv_pairs['ExposureTime']/1000:#.2f}")
        self.pitch_edit.setText(f"{kv_pairs['SpiralPitchFactor']:#.2f}")
        self.get_scan_length_dicom()

        if missing_attr:
            QMessageBox.information(
                None, 'Missing Attribute',
                f"The image has no attribute '{', '.join(missing_attr)}'.\nPlease input them manually."
            )
        if missing_data:
            QMessageBox.information(
                None, 'No Data',
                f"Data for '{list(missing_data.keys())[0]}: {list(missing_data.values())[0]}' is unavailable."
            )

    def on_set_method(self, idx):
        self.disable_warning = False
        self.prev_mode = self.method
        self.method = idx
        self.stacks.setCurrentIndex(idx)
        self.stackz.setCurrentIndex(idx)
        if self.method == 2:
            pass
            # self.calculate()
        elif self.method == 0:
            self.on_mode_calc_changed(self.mode_calc_cb.currentIndex())
        elif self.method == 1:
            self.on_mode_dcm_changed(self.mode_dcm_cb.currentIndex())

    def on_brand_changed(self, sel):
        self.brand_id = self.brand_query.record(sel).value("ID")

        self.scanner_query.setFilter(f"BRAND_ID={self.brand_id}")
        self.on_scanner_changed(0)
        self.scanner_items = [
            self.scanner_cb.itemText(i).lower()
            for i in range(self.scanner_cb.count())
        ]

    def on_scanner_changed(self, sel):
        self.scanner_id = self.scanner_query.record(sel).value("ID")

        self.volt_query.setFilter(f"SCANNER_ID={self.scanner_id}")
        self.coll_query.setFilter(f"SCANNER_ID={self.scanner_id}")
        if self.volt_cb.count() == 0:
            QMessageBox.warning(None, 'No Data',
                                f'There is no CTDI data for this scanner.')
        if self.coll_cb.count() == 0:
            QMessageBox.warning(
                None, 'No Data',
                'There is no collimation data for this scanner.')
        self.on_volt_changed(0)
        self.on_coll_changed(0)
        self.volt_items = [
            float(self.volt_cb.itemText(i))
            for i in range(self.volt_cb.count())
        ]
        self.coll_items = [
            float(self.coll_query.record(i).value("COL_VAL"))
            for i in range(self.coll_cb.count())
        ]

    def on_volt_changed(self, sel):
        phantom = 'head' if self.ctx.phantom == HEAD else 'body'
        self.CTDI = self.volt_query.record(sel).value(
            f"CTDI_{phantom.upper()}")
        if not self.CTDI and self.volt_cb.count() != 0:
            QMessageBox.warning(
                None, 'No Data',
                f'There is no {phantom.capitalize()} CTDI value for this voltage value.'
            )
        # self.calculate(False)

    def on_coll_changed(self, sel):
        self.coll = self.coll_query.record(sel).value("VALUE")
        if not self.coll and self.coll_cb.count() != 0:
            QMessageBox.warning(
                None, 'No Data',
                'There is no collimation data for this option.')
        # self.calculate(False)

    def on_tube_current_changed(self, sel):
        try:
            self.tube_current = float(sel)
        except ValueError:
            self.tube_current = 0
        # self.calculate(False)

    def on_rotation_time_changed(self, sel):
        try:
            self.rotation_time = float(sel)
        except ValueError:
            self.rotation_time = 1
        # self.calculate(False)

    def on_pitch_changed(self, sel):
        try:
            self.pitch = float(sel)
        except ValueError:
            self.pitch = 1
        # self.calculate(False)

    def on_scan_length_changed(self, sel):
        try:
            self.scan_length = float(sel)
        except ValueError:
            self.scan_length = 0
        # self.calculate(False)

    def on_dlp_changed(self, sel):
        try:
            self.DLP = float(sel)
        except ValueError:
            self.DLP = 0
        self.set_app_data()

    def on_ctdiv_changed(self, sel):
        try:
            self.CTDIv = float(sel)
        except ValueError:
            self.CTDIv = 0
        self.set_app_data()

    def on_dicom_manual(self):
        try:
            self.CTDIv = float(self.ctdiv_d_edit.text())
        except:
            self.CTDIv = 0
        try:
            self.scan_length = float(self.scan_length_d_edit.text())
        except:
            self.scan_length = 0
        self.DLP = self.CTDIv * self.scan_length
        self.dlp_d_edit.setText(f'{self.DLP:#.2f}')

    def img_changed_handle(self, value):
        if value:
            self.switch_button_default()

    def img_loaded_handle(self, state):
        if not state:
            self.all_slices_man = False
            self.all_slices_man_chk.setChecked(False)
        self.all_slices_man_chk.setEnabled(state)

    def on_mode_man_changed(self, state):
        self.all_slices_man = state == Qt.Checked
        self.ctx.app_data.c_mode = int(self.all_slices_man)

    def on_mode_dcm_changed(self, idx):
        self.all_slices_dcm = idx == 1
        if self.all_slices_dcm:
            self.ctdiv_d_label.setText("Avg. CTDI<sub>vol</sub> (mGy)")
        else:
            self.ctdiv_d_label.setText("CTDI<sub>vol</sub> (mGy)")
        self.show_graph_tcm_dcm_chk.setCheckState(Qt.Unchecked)
        self.show_graph_ctd_dcm_chk.setCheckState(Qt.Unchecked)
        self.dcm_d3opts_grpbox.setVisible(self.all_slices_dcm)
        self.show_graph_tcm_dcm = False
        self.show_graph_ctd_dcm = False
        self.ctx.app_data.c_mode = idx
        self.reset_dcm()
        self.mode_calc_cb.setCurrentIndex(idx)

    def on_mode_calc_changed(self, idx):
        self.all_slices = idx == 1
        self.show_graph_tcm_chk.setCheckState(Qt.Unchecked)
        self.show_graph_ctd_chk.setCheckState(Qt.Unchecked)
        self.calc_d3opts_grpbox.setVisible(self.all_slices)
        self.show_graph_tcm = False
        self.show_graph_ctd = False
        self.ctx.app_data.c_mode = idx
        self.mode_dcm_cb.setCurrentIndex(idx)

    def on_show_graph_ctd_state(self, state):
        self.show_graph_ctd = state == Qt.Checked

    def on_show_graph_tcm_state(self, state):
        self.show_graph_tcm = state == Qt.Checked

    def on_show_graph_ctd_dcm_state(self, state):
        self.show_graph_ctd_dcm = state == Qt.Checked

    def on_show_graph_tcm_dcm_state(self, state):
        self.show_graph_tcm_dcm = state == Qt.Checked

    def on_tcm_correction_state(self, state):
        self.adjust_tcm = state == Qt.Checked

    def on_calc_3d_opts_changed(self, sel):
        sel = self.sender()
        if sel.isChecked():
            self.calc_3d_method = sel.text().lower()
            if self.calc_3d_method == 'regional':
                self.calc_to_lbl.setHidden(False)
                self.calc_slice2_sb.setHidden(False)
                self.calc_slice1_sb.setMinimum(1)
                self.calc_slice1_sb.setMaximum(self.ctx.total_img)
            else:
                self.calc_to_lbl.setHidden(True)
                self.calc_slice2_sb.setHidden(True)

    def on_dcm_3d_opts_changed(self, sel):
        sel = self.sender()
        if sel.isChecked():
            self.dcm_3d_method = sel.text().lower()
            if self.dcm_3d_method == 'regional':
                self.dcm_to_lbl.setHidden(False)
                self.dcm_slice2_sb.setHidden(False)
                self.dcm_slice1_sb.setMinimum(1)
                self.dcm_slice1_sb.setMaximum(self.ctx.total_img)
            else:
                self.dcm_to_lbl.setHidden(True)
                self.dcm_slice2_sb.setHidden(True)

    def calc_all_slices(self, idxs, dss):
        if not self.ctx.isImage:
            QMessageBox.warning(None, "Warning", "Open DICOM files first.")
            return
        c = []
        e = []
        to_remove = []
        for idx, dcm in zip(idxs, dss):
            if not hasattr(dcm, 'XRayTubeCurrent') or not hasattr(
                    dcm, 'ExposureTime'):
                to_remove.append(idx)
                continue
            c.append(float(dcm.XRayTubeCurrent))
            e.append(float(dcm.ExposureTime) / 1000)
        if len(to_remove) > 0:
            [idxs.remove(idx) for idx in to_remove if idx in idxs]

        currents = np.array(c)
        exp_time = np.array(e)
        mAs = currents * exp_time
        eff_mAs = mAs / self.pitch if self.pitch > 0 else mAs
        ctdiw = self.coll * self.CTDI * mAs / 100
        ctdiv = ctdiw / self.pitch
        # print(idx+1, currents[idx], exp_time[idx], mAs[idx], eff_mAs[idx], ctdiw[idx], ctdiv[idx], sep='\t')

        self.currents = currents
        self.ctdivs = ctdiv
        # self.idxs = np.arange(1, self.ctx.total_img+1, dtype=int)

        self.tube_current_edit.setText(f"{currents.mean():#.2f}")
        self.rotation_time_edit.setText(f"{exp_time.mean():#.2f}")
        self.mAs = mAs.mean()
        self.eff_mAs = eff_mAs.mean()
        self.CTDIw = ctdiw.mean()
        self.CTDIv = ctdiv.mean()
        return idxs

    def calculate_method(self):
        if not self.all_slices:
            self.mAs = self.tube_current * self.rotation_time
            try:
                self.eff_mAs = self.mAs / self.pitch
            except ZeroDivisionError:
                self.eff_mAs = self.mAs
            try:
                self.CTDIw = self.coll * self.CTDI * self.mAs / 100
            except TypeError:
                self.CTDIw = 0
            try:
                # self.CTDIv = self.coll*self.CTDI*self.eff_mAs / 100
                self.CTDIv = self.CTDIw / self.pitch
            except:
                self.CTDIv = 0
            self.ctx.app_data.CTDIvs[self.ctx.current_img] = self.CTDIv
            self.ctx.app_data.emit_c_changed()
        else:
            self.idxs = []
            nslice = self.calc_slice1_sb.value()
            dcms = np.array(self.ctx.dicoms)
            index = list(range(len(dcms)))
            self.ctx.app_data.mode3d = self.calc_3d_method
            self.ctx.app_data.slice1 = nslice
            if self.calc_3d_method == 'slice step':
                idxs = index[::nslice]
                imgs = dcms[::nslice]
            elif self.calc_3d_method == 'slice number':
                tmps = np.array_split(np.arange(len(dcms)), nslice)
                idxs = [tmp[len(tmp) // 2] for tmp in tmps]
                imgs = dcms[idxs]
            elif self.calc_3d_method == 'regional':
                nslice2 = self.calc_slice2_sb.value()
                self.ctx.app_data.slice2 = nslice2
                first = nslice if nslice <= nslice2 else nslice2
                last = nslice2 if nslice <= nslice2 else nslice
                idxs = index[first - 1:last]
                imgs = dcms[first - 1:last]
            else:
                imgs = dcms
                idxs = index
            idxs = self.calc_all_slices(idxs, imgs)
            if idxs is None:
                return
            self.idxs = [i + 1 for i in idxs]
            for k, v in zip(self.idxs, self.ctdivs):
                self.ctx.app_data.CTDIvs[k] = v
            self.ctx.app_data.emit_c_changed()

        self.DLP = self.CTDIv * self.scan_length
        self.mas_edit.setText(f'{self.mAs:#.2f}')
        self.mas_eff_edit.setText(f'{self.eff_mAs:#.2f}')
        self.ctdiw_edit.setText(f'{self.CTDIw:#.2f}')
        self.ctdiv_c_edit.setText(f'{self.CTDIv:#.2f}')
        self.dlp_c_edit.setText(f'{self.DLP:#.2f}')

        self.ctx.app_data.CTDIv = self.CTDIv
        self.ctx.app_data.DLP = self.DLP

        if self.show_graph_tcm:
            self.plot_tcm()
        if self.show_graph_ctd:
            self.plot_ctdiv()

        self.switch_button_default(method=1)

    def calculate_dcm(self):
        self.idxs = []
        if not self.ctx.isImage and not self.disable_warning:
            QMessageBox.warning(None, "Warning", "Open DICOM files first.")
            return

        self.get_scan_length_dicom()
        # curCTDIv = self.get_ctdiv_dicom(self.ctx.current_img)
        dcms = np.array(self.ctx.dicoms)
        index = list(range(len(dcms)))
        index = self.get_ctdiv_dicom_3d(index, dcms)
        if self.all_slices_dcm:
            nslice = self.dcm_slice1_sb.value()
            self.ctx.app_data.mode3d = self.dcm_3d_method
            self.ctx.app_data.slice1 = nslice
            if self.dcm_3d_method == 'slice step':
                idxs = index[::nslice]
            elif self.dcm_3d_method == 'slice number':
                tmps = np.array_split(np.arange(len(dcms)), nslice)
                idxs = [tmp[len(tmp) // 2] for tmp in tmps]
            elif self.dcm_3d_method == 'regional':
                nslice2 = self.dcm_slice2_sb.value()
                self.ctx.app_data.slice2 = nslice2
                first = nslice if nslice <= nslice2 else nslice2
                last = nslice2 if nslice <= nslice2 else nslice
                idxs = index[first - 1:last]
            else:
                idxs = index
        else:
            idxs = index

        if self.adjust_tcm:
            self.ctdivs = (self.ctdivs * self.currents) / self.currents.mean()

        self.currents = self.currents[idxs]
        self.ctdivs = self.ctdivs[idxs]
        self.CTDIv = self.ctdivs.mean()
        curidx = idxs.index(self.ctx.current_img -
                            1) if self.ctx.current_img - 1 in idxs else None
        if self.all_slices_dcm:
            ctdi = self.CTDIv
        else:
            ctdi = self.ctdivs[curidx] if curidx is not None else 0
        scan_length = self.scan_length
        DLP = ctdi * scan_length

        self.ctdiv_d_edit.setText(f'{ctdi:#.2f}')
        self.scan_length_d_edit.setText(f'{scan_length:#.2f}')
        self.dlp_d_edit.setText(f'{DLP:#.2f}')

        self.CTDIv = ctdi
        self.scan_length = scan_length
        self.DLP = DLP

        if self.all_slices_dcm:
            self.idxs = [i + 1 for i in idxs]
            for k, v in zip(self.idxs, self.ctdivs):
                self.ctx.app_data.CTDIvs[k] = v
        else:
            self.ctx.app_data.CTDIvs[self.ctx.current_img] = ctdi
        self.ctx.app_data.emit_c_changed()
        self.ctx.app_data.CTDIv = self.CTDIv
        self.ctx.app_data.DLP = self.DLP

        if self.show_graph_ctd_dcm:
            self.plot_ctdiv()
        if self.show_graph_tcm_dcm:
            self.plot_tcm()
        self.switch_button_default(method=1)

    def calculate_man(self):
        try:
            ctdi = float(self.ctdiv_m_edit.text())
        except:
            ctdi = 0
        try:
            self.DLP = float(self.dlp_m_edit.text())
        except:
            self.DLP = 0
        if self.all_slices_man:
            for idx in range(1, self.ctx.total_img + 1):
                self.ctx.app_data.CTDIvs[idx] = ctdi
        else:
            self.ctx.app_data.CTDIvs[self.ctx.current_img] = ctdi
        self.ctx.app_data.DLP = self.DLP

    def calculate(self, auto=True):
        if self.method == 2:
            self.ctx.app_data.c_mode = 0
            try:
                self.CTDIv = float(self.ctdiv_m_edit.text())
            except:
                self.CTDIv = 0
            try:
                self.DLP = float(self.dlp_m_edit.text())
            except:
                self.DLP = 0
            self.idxs = range(1, self.ctx.total_img + 1)
            self.ctdivs = [self.CTDIv for _ in self.idxs]
            self.ctx.app_data.CTDIv = self.CTDIv
            self.ctx.app_data.DLP = self.DLP

        self.switch_button_default(method=1)

    def set_app_data(self):
        self.ctx.app_data.CTDIv = self.CTDIv
        self.ctx.app_data.DLP = self.DLP

    def reset_dcm(self):
        self.scan_length = 10
        self.scan_length_d_edit.setText(f"{self.scan_length}")
        self.ctdivs = []
        self.CTDIv = 0
        self.DLP = 0
        self.ctdiv_d_edit.setText(f"{self.CTDIv}")
        self.dlp_d_edit.setText(f"{self.DLP}")

    def reset_fields(self):
        self.initVar()
        self.switch_button_default()
        self.disable_warning = True
        self.reset_dcm()
        self.ctdiv_m_edit.setText('0')
        self.dlp_m_edit.setText('0')
        self.tube_current_edit.setText(f'{self.tube_current:#.2f}')
        self.rotation_time_edit.setText(f'{self.rotation_time:#.2f}')
        self.pitch_edit.setText(f'{self.pitch:#.2f}')
        self.scan_length_c_edit.setText(f'{self.scan_length:#.2f}')
        self.mas_edit.setText(f'{self.mAs:#.2f}')
        self.mas_eff_edit.setText(f'{self.eff_mAs:#.2f}')
        self.ctdiw_edit.setText(f'{self.CTDIw:#.2f}')
        self.ctdiv_c_edit.setText(f'{self.CTDIv:#.2f}')
        self.dlp_c_edit.setText(f'{self.DLP:#.2f}')
        self.disable_warning = False
コード例 #14
0
class SSDETab(QDialog):
  def __init__(self, ctx, *args, **kwargs):
    super(SSDETab, self).__init__(*args, **kwargs)
    self.ctx = ctx
    self.show_graph = False
    self.show_ssde_graph = False
    self.all_slices = False
    self.d3_method = 'slice step'
    self.current_idx = 0
    self.initVar()
    self.initModel()
    self.initUI()
    self.sigConnect()

  def initVar(self):
    self.diameter = 0
    self.CTDIv = 0
    self.convf = 0
    self.SSDE = 0
    self.DLPc = 0
    self.effdose = 0
    self.ctdivs = []
    self.diameters = []
    self.idxs = []
    self.ssdes = []
    self.display = {
      'ctdi': None,
      'diameter': None,
      'cf': None,
      'ssde': None,
      'dlp': None,
      'dlpc': None,
      'effdose': None,
    }

  def set_data(self):
    self.display['ctdi'] = self.CTDIv
    self.display['diameter'] = self.diameter
    self.display['cf'] = self.convf
    self.display['ssde'] = self.SSDE
    self.display['dlp'] = self.ctx.app_data.DLP
    self.display['dlpc'] = self.DLPc
    self.display['effdose'] = self.effdose

  def initModel(self):
    self.protocol_model = QSqlTableModel(db=self.ctx.database.ssde_db)
    self.protocol_model.setTable("Protocol")
    self.protocol_model.setFilter("Group_ID=1")
    self.protocol_model.select()

    self.report_model = QSqlTableModel(db=self.ctx.database.ssde_db)
    self.report_model.setTable("Report")
    self.report_model.select()

    self.cf_model = QSqlTableModel(db=self.ctx.database.ssde_db)
    self.cf_model.setTable("ConversionFactor")
    self.cf_model.setFilter("report_id=1")
    self.cf_model.select()

    self.effdose_model = QSqlTableModel(db=self.ctx.database.ssde_db)
    self.effdose_model.setTable("Effective_Dose")
    self.effdose_model.select()

  def initUI(self):
    self.hidden_btn = QPushButton('check') # for debug
    self.hidden_btn.setVisible(False)

    self.figure = PlotDialog()
    self.protocol_cb = QComboBox()
    self.protocol_cb.setModel(self.protocol_model)
    self.protocol_cb.setModelColumn(self.protocol_model.fieldIndex('name'))
    self.report_cb = QComboBox()
    self.report_cb.setModel(self.report_model)
    self.report_cb.setModelColumn(self.report_model.fieldIndex('name'))
    self.plot_chk = QCheckBox('Show Graph')
    self.show_ssde_graph_chk = QCheckBox('Show SSDE Graph')

    self.d3_opts_cb = QComboBox()
    self.d3_opts_cb.addItems(['One slice', 'Z-axis'])
    self.d3opts_rbtns = [QRadioButton('Slice Step'), QRadioButton('Slice Number'), QRadioButton('Regional')]
    self.d3opts_rbtns[0].setChecked(True)
    self.slice1_sb = QSpinBox()
    self.slice2_sb = QSpinBox()
    self.to_lbl = QLabel('to')

    self.slice1_sb.setMaximum(self.ctx.total_img)
    self.slice1_sb.setMinimum(1)
    self.slice1_sb.setMinimumWidth(50)
    self.slice2_sb.setMaximum(self.ctx.total_img)
    self.slice2_sb.setMinimum(1)
    self.slice2_sb.setMinimumWidth(50)
    self.to_lbl.setHidden(True)
    self.slice2_sb.setHidden(True)

    self.calc_btn = QPushButton('Calculate')
    self.save_btn = QPushButton('Save')

    self.calc_btn.setAutoDefault(True)
    self.calc_btn.setDefault(True)
    self.save_btn.setAutoDefault(False)
    self.save_btn.setDefault(False)

    self.ctdiv_edit = QLineEdit(f'{self.ctx.app_data.CTDIv}')
    self.diameter_edit = QLineEdit(f'{self.ctx.app_data.diameter}')
    self.convf_edit = QLineEdit(f'{self.ctx.app_data.convf}')
    self.ssde_edit = QLineEdit(f'{self.ctx.app_data.SSDE}')
    self.dlp_edit = QLineEdit(f'{self.ctx.app_data.DLP}')
    self.dlpc_edit = QLineEdit(f'{self.ctx.app_data.DLPc}')
    self.effdose_edit = QLineEdit(f'{self.ctx.app_data.effdose}')

    self.current_dlabel = '<b>Deff (cm)</b>'
    self.diameter_label = QLabel(self.current_dlabel)
    self.ctdiv_label = QLabel('<b>CTDI<sub>vol</sub> (mGy)</b>')
    self.ssde_label = QLabel('<b>SSDE (mGy)</b>')

    self.diameter_mode_handle(DEFF_IMAGE)

    self.next_tab_btn = QPushButton('Next')
    self.prev_tab_btn = QPushButton('Previous')

    self.next_tab_btn.setAutoDefault(False)
    self.next_tab_btn.setDefault(False)
    self.prev_tab_btn.setAutoDefault(False)
    self.prev_tab_btn.setDefault(False)
    self.next_tab_btn.setVisible(False)

    edits = [
      self.ctdiv_edit,
      self.diameter_edit,
      self.convf_edit,
      self.ssde_edit,
      self.dlp_edit,
      self.dlpc_edit,
      self.effdose_edit,
    ]

    [edit.setReadOnly(True) for edit in edits]
    [edit.setAlignment(Qt.AlignRight) for edit in edits]

    slice_layout = QHBoxLayout()
    slice_layout.addWidget(self.slice1_sb)
    slice_layout.addWidget(self.to_lbl)
    slice_layout.addWidget(self.slice2_sb)
    slice_layout.addStretch()

    self.d3opts_grpbox = QGroupBox('Z-axis Options')
    dcm_d3opts_layout = QVBoxLayout()
    [dcm_d3opts_layout.addWidget(btn) for btn in self.d3opts_rbtns]
    dcm_d3opts_layout.addLayout(slice_layout)
    dcm_d3opts_layout.addWidget(self.show_ssde_graph_chk)
    dcm_d3opts_layout.addStretch()
    dcm_d3opts_layout.setContentsMargins(11,3,11,3)
    self.d3opts_grpbox.setLayout(dcm_d3opts_layout)
    self.d3opts_grpbox.setVisible(False)

    left_grpbox = QGroupBox()
    left_layout = QFormLayout()
    left_layout.addRow(self.ctdiv_label, self.ctdiv_edit)
    left_layout.addRow(self.diameter_label, self.diameter_edit)
    left_layout.addRow(QLabel('<b>Conv Factor</b>'), self.convf_edit)
    left_layout.addRow(self.ssde_label, self.ssde_edit)
    # left_layout.addRow(QLabel('<b>Option</b>'), self.d3_opts_cb)
    left_layout.addRow(self.calc_btn, self.plot_chk)
    left_layout.addRow(QLabel(''))
    # left_layout.addRow(QLabel(''), self.plot_chk)
    left_layout.addRow(self.save_btn, QLabel(''))
    left_grpbox.setLayout(left_layout)

    right_grpbox = QGroupBox()
    right_layout = QFormLayout()
    right_layout.addRow(QLabel('<b>DLP (mGy-cm)</b>'), self.dlp_edit)
    right_layout.addRow(QLabel('<b>DLP<sub>c</sub> (mGy-cm)</b>'), self.dlpc_edit)
    right_layout.addRow(QLabel('<b>Effective Dose (mSv)</b>'), self.effdose_edit)
    right_grpbox.setLayout(right_layout)

    r_layout = QVBoxLayout()
    r_layout.addWidget(right_grpbox)
    r_layout.addWidget(self.d3opts_grpbox)

    h = QHBoxLayout()
    h.addWidget(left_grpbox)
    h.addLayout(r_layout)

    tab_nav = QHBoxLayout()
    tab_nav.addWidget(self.prev_tab_btn)
    tab_nav.addWidget(self.hidden_btn)
    tab_nav.addStretch()
    tab_nav.addWidget(self.next_tab_btn)

    main_layout = QVBoxLayout()
    main_layout.addWidget(QLabel('Based on:'))
    main_layout.addWidget(self.report_cb)
    main_layout.addWidget(QLabel('Protocol:'))
    main_layout.addWidget(self.protocol_cb)
    main_layout.addWidget(HSeparator())
    main_layout.addWidget(self.d3_opts_cb)
    main_layout.addLayout(h)
    main_layout.addStretch()
    main_layout.addLayout(tab_nav)

    self.setLayout(main_layout)

  def sigConnect(self):
    self.hidden_btn.clicked.connect(self.on_check)
    self.protocol_cb.activated[int].connect(self.on_protocol_changed)
    self.report_cb.activated[int].connect(self.on_report_changed)
    self.calc_btn.clicked.connect(self.on_calculate)
    self.ctx.app_data.modeValueChanged.connect(self.diameter_mode_handle)
    self.ctx.app_data.diametersUpdated.connect(self.update_values)
    self.ctx.app_data.ctdivsUpdated.connect(self.update_values)
    self.ctx.app_data.DLPValueChanged.connect(self.dlp_handle)
    self.ctx.app_data.imgChanged.connect(self.img_changed_handle)
    self.ctx.app_data.sliceOptChanged.connect(self.sliceopt_handle)
    self.ctx.app_data.mode3dChanged.connect(self.mode3d_changed_handle)
    self.ctx.app_data.slice1Changed.connect(self.slice1_changed_handle)
    self.ctx.app_data.slice2Changed.connect(self.slice2_changed_handle)
    self.plot_chk.stateChanged.connect(self.on_show_graph_check)
    self.show_ssde_graph_chk.stateChanged.connect(self.on_show_ssde_graph_check)
    self.d3_opts_cb.activated[int].connect(self.on_mode_changed)
    [btn.toggled.connect(self.on_3d_opts_changed) for btn in self.d3opts_rbtns]

  def plot(self, data):
    x = self.diameter
    y = self.convf
    xlabel = 'Dw' if self.ctx.app_data.mode==DW else 'Deff'
    title = 'Water Equivalent Diameter' if self.ctx.app_data.mode==DW else 'Effective Diameter'
    self.figure = PlotDialog()
    self.figure.actionEnabled(True)
    self.figure.trendActionEnabled(False)
    self.figure.plot(data, pen={'color': "FFFF00", 'width': 2}, symbol=None)
    self.figure.plot([x], [y], symbol='o', symbolPen=None, symbolSize=8, symbolBrush=(255, 0, 0, 255))
    self.figure.annotate('cf', pos=(x,y), text=f'{xlabel}: {x:#.2f} cm\nConv. Factor: {y:#.2f}', anchor=(0,1))
    self.figure.axes.showGrid(True,True)
    self.figure.setLabels(xlabel,'Conversion Factor','cm','')
    self.figure.setTitle(f'{title} - Conversion Factor')
    self.figure.show()

  def plot_ssde(self, idxs, ssdes):
    xlabel = 'SSDE'
    title = 'SSDE'
    self.figure_ssde = PlotDialog()
    self.figure_ssde.actionEnabled(True)
    self.figure_ssde.trendActionEnabled(False)
    self.figure_ssde.plot(idxs, ssdes, pen={'color': "FFFF00", 'width': 2}, symbol='o', symbolPen=None, symbolSize=8, symbolBrush=(255, 0, 0, 255))
    self.figure_ssde.axes.showGrid(True,True)
    self.figure_ssde.setLabels('slice',xlabel,'','mGy')
    self.figure_ssde.setTitle(f'Slice - {title}')
    self.figure_ssde.show()

  def switch_button_default(self, mode=0):
    if mode==0:
      self.calc_btn.setAutoDefault(True)
      self.calc_btn.setDefault(True)
      self.save_btn.setAutoDefault(False)
      self.save_btn.setDefault(False)
    elif mode==1:
      self.save_btn.setAutoDefault(True)
      self.save_btn.setDefault(True)
      self.calc_btn.setAutoDefault(False)
      self.calc_btn.setDefault(False)
    else:
      return
    self.next_tab_btn.setAutoDefault(False)
    self.next_tab_btn.setDefault(False)
    self.prev_tab_btn.setAutoDefault(False)
    self.prev_tab_btn.setDefault(False)

  def diameter_mode_handle(self, value):
    self.current_dlabel = '<b>Dw (cm)</b>' if value == DW else '<b>Deff (cm)</b>'
    self.diameter_label.setText(self.current_dlabel)

  def diameter_handle(self, value):
    self.diameter_edit.setText(f'{value:#.2f}')

  def ctdiv_handle(self, value):
    self.ctdiv_edit.setText(f'{value:#.2f}')

  def dlp_handle(self, value):
    self.dlp_edit.setText(f'{value:#.2f}')

  def on_protocol_changed(self, idx):
    self.protocol_id = self.protocol_model.record(idx).value("id")
    self.alfa = self.effdose_model.record(self.protocol_id-1).value("alfaE")
    self.beta = self.effdose_model.record(self.protocol_id-1).value("betaE")

  def on_report_changed(self, idx):
    self.report_id = self.report_model.record(idx).value("id")
    self.cf_model.setFilter(f"report_id={self.report_id} AND phantom_id={self.ctx.phantom}")
    self.cf_model.select()

    a_val = self.cf_model.record(0).value("a")
    b_val = self.cf_model.record(0).value("b")
    self.cf_eq = lambda x: a_val*np.exp(-b_val*x)

  def on_show_graph_check(self, state):
    self.show_graph = state == Qt.Checked

  def on_show_ssde_graph_check(self, state):
    self.show_ssde_graph = state == Qt.Checked

  def sliceopt_handle(self, value):
    self.d3_opts_cb.setCurrentIndex(value)
    self.on_mode_changed(value)

  def mode3d_changed_handle(self, value):
    rb = [r for r in self.d3opts_rbtns if r.text().lower()==value]
    rb[0].setChecked(True)

  def slice1_changed_handle(self, value):
    self.slice1_sb.setValue(value)

  def slice2_changed_handle(self, value):
    self.slice2_sb.setValue(value)

  def on_3d_opts_changed(self, sel):
    sel = self.sender()
    if sel.isChecked():
      self.d3_method = sel.text().lower()
      if self.d3_method == 'regional':
        self.to_lbl.setHidden(False)
        self.slice2_sb.setHidden(False)
        self.slice1_sb.setMinimum(1)
        self.slice1_sb.setMaximum(self.ctx.total_img)
      else:
        self.to_lbl.setHidden(True)
        self.slice2_sb.setHidden(True)

  def on_mode_changed(self, idx):
    self.all_slices = idx==1
    label_d = self.current_dlabel[:3]+'Avg. '+self.current_dlabel[3:] if self.all_slices else self.current_dlabel
    label_c = '<b>Avg. CTDI<sub>vol</sub> (mGy)</b>' if self.all_slices else '<b>CTDI<sub>vol</sub> (mGy)</b>'
    label_s = '<b>Avg. SSDE (mGy)</b>' if self.all_slices else '<b>SSDE (mGy)</b>'
    self.diameter_label.setText(label_d)
    self.ctdiv_label.setText(label_c)
    self.ssde_label.setText(label_s)
    self.d3opts_grpbox.setVisible(self.all_slices)
    self.ctx.app_data.s_mode = idx
    self.update_values()

  def get_idxs(self):
    if not self.ctx.isImage:
      self.idxs = [0]
      return
    dcms = np.array(self.ctx.dicoms)
    index = list(range(len(dcms)))
    idxs = index
    if self.all_slices:
      nslice = self.slice1_sb.value()
      if self.d3_method  == 'slice step':
        idxs = index[::nslice]
      elif self.d3_method  == 'slice number':
        tmps = np.array_split(np.arange(len(dcms)), nslice)
        idxs = [tmp[len(tmp)//2] for tmp in tmps]
      elif self.d3_method  == 'regional':
        nslice2 = self.slice2_sb.value()
        first = nslice if nslice<=nslice2 else nslice2
        last = nslice2 if nslice<=nslice2 else nslice
        idxs = index[first-1:last]
    self.idxs = [idx+1 for idx in idxs]

  def on_calculate(self):
    try:
      self.ctx.app_data.convf = self.cf_eq(self.ctx.app_data.diameter)
    except:
      QMessageBox.warning(None, "No Data", f"There are no data in {self.report_cb.currentText()} report for {self.ctx.phantom_name} phantom.")
      return

    if not self.all_slices:
      # if self.use_avg:
        # self.ctx.app_data.SSDE = self.ctx.app_data.convf * self.ctx.app_data.CTDIv
      # else:
      try:
        self.diameter = self.ctx.app_data.diameters[self.ctx.current_img]
        self.CTDIv = self.ctx.app_data.CTDIvs[self.ctx.current_img]
      except:
        QMessageBox.warning(None, "No Data", f"No CTDIv and diameter data for slice {self.ctx.current_img}.\nCalculate them first.")
        return
      self.convf = self.cf_eq(self.diameter)
      self.SSDE = self.convf * self.CTDIv
      self.DLPc = self.convf * self.ctx.app_data.DLP
      self.effdose = self.ctx.app_data.DLP * np.exp(self.alfa*self.diameter + self.beta)

      self.ctx.app_data.convfs[self.ctx.current_img] = self.convf
      self.ctx.app_data.SSDEs[self.ctx.current_img] = self.SSDE
      self.ctx.app_data.dlpcs[self.ctx.current_img] = self.DLPc
      self.ctx.app_data.effdoses[self.ctx.current_img] = self.effdose
      self.ctx.app_data.emit_s_changed()
    else:
      ctdivs = []
      diameters = []
      cond = self.d3_method==self.ctx.app_data.mode3d and self.slice1_sb.value()==self.ctx.app_data.slice1
      if self.d3_method=='regional':
        cond = cond and self.slice2_sb.value()==self.ctx.app_data.slice2
      self.get_idxs()
      idx_to_remove = []
      for idx in self.idxs:
        if idx not in self.ctx.app_data.CTDIvs.keys() or idx not in self.ctx.app_data.diameters.keys():
          idx_to_remove.append(idx)
          continue
        ctdivs.append(self.ctx.app_data.CTDIvs[idx])
        diameters.append(self.ctx.app_data.diameters[idx])

      if len(idx_to_remove)>0:
        [self.idxs.remove(idx) for idx in idx_to_remove if idx in self.idxs]

      self.ctdivs = np.array(ctdivs)
      self.diameters = np.array(diameters)
      convfs = self.cf_eq(self.diameters)
      self.ssdes = convfs * self.ctdivs
      dlpcs = convfs * self.ctx.app_data.DLP
      effdoses = self.ctx.app_data.DLP * np.exp(self.alfa*self.diameters + self.beta)

      self.diameter = self.diameters.mean()
      self.CTDIv = self.ctdivs.mean()
      self.SSDE = self.ssdes.mean()
      self.convf = self.cf_eq(self.diameter)
      self.DLPc = self.convf * self.ctx.app_data.DLP
      self.effdose = self.ctx.app_data.DLP * np.exp(self.alfa*self.diameter + self.beta)

      for idx, v in enumerate(self.idxs):
        self.ctx.app_data.convfs[v] = convfs[idx]
        self.ctx.app_data.SSDEs[v] = self.ssdes[idx]
        self.ctx.app_data.dlpcs[v] = dlpcs[idx]
        self.ctx.app_data.effdoses[v] = effdoses[idx]
      self.ctx.app_data.emit_s_changed()

      if self.show_ssde_graph:
        self.plot_ssde(self.idxs, self.ssdes)

    self.diameter_edit.setText(f'{self.diameter:#.2f}')
    self.ctdiv_edit.setText(f'{self.CTDIv:#.2f}')
    self.convf_edit.setText(f'{self.convf:#.2f}')
    self.ssde_edit.setText(f'{self.SSDE:#.2f}')
    self.dlpc_edit.setText(f'{self.DLPc:#.2f}')
    self.effdose_edit.setText(f'{self.effdose:#.2f}')

    self.ctx.app_data.effdose = self.effdose
    self.ctx.app_data.DLPc = self.DLPc

    self.set_data()

    if self.show_graph:
      minv = 6 if self.ctx.phantom == HEAD else 8
      maxv = 55 if self.ctx.phantom == HEAD else 45
      deff = np.arange(minv, maxv+1, 1)
      cf = self.cf_eq(deff)
      data = np.array([deff, cf]).T
      self.plot(data)

    self.switch_button_default(mode=1)

  def update_values(self, val=True):
    if not val:
      return
    try:
      self.CTDIv = self.ctx.app_data.CTDIvs[self.ctx.current_img] if not self.all_slices else self.ctdivs.mean()
    except:
      self.CTDIv = 0
    try:
      self.diameter = self.ctx.app_data.diameters[self.ctx.current_img] if not self.all_slices else self.diameters.mean()
    except:
      self.diameter = 0
    try:
      self.convf = self.ctx.app_data.convfs[self.ctx.current_img] if not self.all_slices else self.cf_eq(self.diameter)
    except:
      self.convf = 0
    try:
      self.SSDE = self.ctx.app_data.SSDEs[self.ctx.current_img] if not self.all_slices else self.ssdes.mean()
    except:
      self.SSDE = 0
    try:
      self.DLPc = self.ctx.app_data.dlpcs[self.ctx.current_img] if not self.all_slices else self.cf_eq(self.diameter) * self.ctx.app_data.DLP
    except:
      self.DLPc = 0
    try:
      self.effdose = self.ctx.app_data.effdoses[self.ctx.current_img] if not self.all_slices else self.ctx.app_data.DLP * np.exp(self.alfa*self.diameter + self.beta)
    except:
      self.effdose = 0
    self.convf_edit.setText(f'{self.convf:#.2f}')
    self.ctdiv_edit.setText(f'{self.CTDIv:#.2f}')
    self.diameter_edit.setText(f'{self.diameter:#.2f}')
    self.ssde_edit.setText(f'{self.SSDE:#.2f}')
    self.dlpc_edit.setText(f'{self.DLPc:#.2f}')
    self.effdose_edit.setText(f'{self.effdose:#.2f}')
    self.set_data()

  def img_changed_handle(self, value):
    if value:
      self.update_values()
      # self.reset_fields()

  def reset_fields(self):
    self.initVar()
    self.ctx.app_data.convf = 0
    self.ctx.app_data.SSDE = 0
    self.ctx.app_data.DLPc = 0
    self.ctx.app_data.effdose = 0
    self.show_graph = False
    # self.d3_opts_cb.setCurrentIndex(0)
    # self.on_mode_changed(0)
    self.plot_chk.setCheckState(Qt.Unchecked)
    self.switch_button_default()
    self.ctdiv_edit.setText(f'{0:#.2f}')
    self.diameter_edit.setText(f'{0:#.2f}')
    self.dlp_edit.setText(f'{0:#.2f}')
    self.convf_edit.setText(f'{0:#.2f}')
    self.ssde_edit.setText(f'{0:#.2f}')
    self.dlpc_edit.setText(f'{0:#.2f}')
    self.effdose_edit.setText(f'{0:#.2f}')

  def on_check(self):
    print('ctdi', self.ctx.app_data.CTDIvs)
    print('diameter', self.ctx.app_data.diameters)
    print('ssde', self.ctx.app_data.SSDEs)
    print('cf', self.ctx.app_data.convfs)
    print('dlpc', self.ctx.app_data.dlpcs)
    print('efd', self.ctx.app_data.effdoses)
コード例 #15
0
class ApiDatabaseDialog(QDialog):
    def __init__(self):
        super().__init__()

        self.setMinimumSize(QSize(800, 400))

        config_folder = os.path.join(os.path.expanduser("~"), '.config',
                                     'CSV_Viewer')
        os.makedirs(config_folder, exist_ok=True)
        db_file = "apilinks.sqlite"
        db_path = os.path.join(config_folder, db_file)

        self.db = QSqlDatabase("QSQLITE")
        self.db.setDatabaseName(db_path)
        if self.db.open():
            if os.path.getsize(db_path) == 0:
                query = QSqlQuery(db=self.db)
                query.exec_("CREATE TABLE links(address TEXT)")
                demo = "http://climatedataapi.worldbank.org/climateweb/rest/v1/country/cru/tas/year/POL.csv"
                query.exec_(f"INSERT INTO links VALUES ('{demo}')")
                self.db.commit()
        else:
            QMessageBox.warning(self, "Error", "API links database not open.")

        self.table = QTableView()
        self.model = QSqlTableModel(db=self.db)
        self.model.setTable('links')
        self.model.setEditStrategy(QSqlTableModel.OnFieldChange)
        self.model.select()

        # set headers
        column_titles = {"address": "API Address"}
        for n, t in column_titles.items():
            idx = self.model.fieldIndex(n)
            self.model.setHeaderData(idx, Qt.Horizontal, t)

        self.table.setModel(self.model)
        self.table.setSelectionBehavior(QtWidgets.QTableView.SelectRows)
        self.table.setSelectionMode(QtWidgets.QTableView.SingleSelection)
        self.table.setColumnWidth(0, 750)
        self.table.selectRow(0)
        self.table.setFocus()

        self.layout = QVBoxLayout()
        QBtn = QDialogButtonBox.Ok
        self.buttonBox = QDialogButtonBox(QBtn)

        style_add = self.buttonBox.style()
        icon = style_add.standardIcon(QStyle.SP_DialogYesButton)
        self.button_add = QPushButton(icon, "&Add")
        self.button_add.setStatusTip("Add new api link")
        self.button_add.clicked.connect(self.add_link)

        style_del = self.buttonBox.style()
        icon = style_del.standardIcon(QStyle.SP_DialogCloseButton)
        self.button_del = QPushButton(icon, "&Delete")
        self.button_del.setStatusTip("Delete api link")
        self.button_del.clicked.connect(self.del_link)

        self.buttonBox.accepted.connect(self.accept)

        self.layout.addWidget(self.table)
        layout_btn = QHBoxLayout()
        layout_btn.addWidget(self.button_add)
        layout_btn.addWidget(self.button_del)
        layout_btn.addSpacerItem(QSpacerItem(150, 10, QSizePolicy.Expanding))
        layout_btn.addWidget(self.buttonBox)

        self.layout.addLayout(layout_btn)
        self.setLayout(self.layout)

    def closeEvent(self, event) -> None:
        """ Quit dialog """
        self.db.close()

    def add_link(self):
        self.model.insertRows(self.model.rowCount(), 1)
        self.table.setFocus()
        self.table.selectRow(self.model.rowCount() - 1)
        index = self.table.currentIndex()
        self.table.edit(index)

    def del_link(self):
        if self.model.rowCount() > 0:
            index = self.table.currentIndex()
            self.model.removeRow(index.row())
            self.model.submitAll()
            self.table.setRowHidden(index.row(), True)
            if index.row() == 0:
                current = 0
            else:
                current = index.row() - 1
            self.table.selectRow(current)
コード例 #16
-1
class PropertiesService(QObject):
    controller = None  # type: PropertiesController
    question_type_model = None  # type: QSqlTableModel
    question_type_filter_proxy_model = None  # type: 'QuestionTypeFilterProxyModel'

    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.connect_slots()

    def connect_slots(self):
        application.app.aboutToQuit.connect(
            self.on_about_to_quit
        )

    def dispose(self):
        pass

    def show(self, tab_wanted: str = 'general'):
        from genial.services import document_service
        if document_service.database is not None:
            if self.question_type_model is None:
                self.question_type_model = QSqlTableModel(
                    self,
                    document_service.database
                )
                self.question_type_model.setTable("question_type")
                self.question_type_model.setEditStrategy(
                    QSqlTableModel.OnManualSubmit
                )
                self.question_type_filter_proxy_model = QuestionTypeFilterProxyModel()
                self.question_type_filter_proxy_model.setSourceModel(
                    self.question_type_model
                )
                self.question_type_filter_proxy_model.sort(
                    self.question_type_model.fieldIndex("position"),
                    Qt.AscendingOrder
                )
                self.question_type_filter_proxy_model.setDynamicSortFilter(True)

        if self.controller is None:
            self.controller = PropertiesController()
            self.controller.start()
        self.controller.show(tab_wanted)

    @pyqtSlot()
    def on_about_to_quit(self):
        self.dispose()