Ein Designer-Problem / Gedankenproblem

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

ich habe ein kleines Problem mit der gedanklichen Ordnung. Fangen wir erst einmal an. Wir sehen hier zwei Klassen, die diesen Context-Manager-Protokoll unterstützen. Soweit so gut. Was habe ich nun vor? Ich möchte (wie man anhand der View-Klasse sieht) dem Benutzer die Möglichkeit geben, dass er/sie sich komplett von der Datenbank abmelden kann. Das Programm muss aber nicht geschlossen werden. Kann ja sein, dass er sich denkt: "Arbeite später mal weiter, muss jetzt schwimmen gehen". Nur alle Verbindungen sollen also geschlossen werden. Daher habe ich einmal die Engine und die Session in zwei separate Klassen untergebracht. In Zeile 135 bis 139 sehen wir, dass mit der with-Anweisung ein Engine-Objekt erzeugt wird, und anschließend wird ein Session-Objekt erzeugt, das dann das Engine-Objekt übergeben bekommt.

Jetzt kommen wir zum Problem. In meinem Beisiel möchte ich simulieren, dass die Verbindung solange aufrecht erhalten wird, bist die with-Anweisung verlassen wird. Dies soll geschehen, sobald der Benutzer auf "Abmelden" oder "Disconnect" klickt. Bis dahin wird die with-Anweisung der Engine durch die While-Schleife aufrecht erhalten. Wie und wo bringe ich die Engine unter, damit diese während der Arbeit nicht blockiert? Denn ich muss ja auch bedenken, dass ich an unterschiedlichen Stellen meines Programms (wir stellen uns mehrere Eingabefenster vor) die Session brauche. Das heißt, ich stehe jetzt vor dem Überlegungs-Problem. Wie kriege ich diese beiden Klassen unter deinem Hut, so dass die While-Schleife niemanden blockiert, und damit ich auch während dessen mit der Session an verschiedenen Stellen arbeiten kann?

Ich hoffe, ich konnte mein Problem halbwegs erklären.

Code: Alles auswählen

from sys import argv

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.exc import SQLAlchemyError

from PyQt4.QtCore import QObject, pyqtSignal, QThread, Qt
 
from PyQt4.QtGui import QDialog, QApplication, QPushButton, \
     QLineEdit, QFormLayout, QTextEdit 
 

class SessionScope(object):
    def __init__(self, engine):

        self.engine = engine

        # store a sessionmaker for this db connection object
        self._Session = sessionmaker(bind=self.engine)
        self.session = None

    def __enter__(self):
        self.session = self._Session()
        return self._Session()
    
    def __exit__(self, exception, exc_value, traceback):

        try:
            if exception:
                self.session.rollback()
            else:
                self.session.commit()
        finally:

            self.session.close()
            self.session = None
            
class Engine(object):
    def __init__(self, dbms=None, dbdriver=None,
                 dbuser=None, dbuser_pwd=None,
                 db_server_host=None, dbport=None, db_name=None):

        self.dbms = dbms
        self.dbdriver = dbdriver
        self.dbuser = dbuser
        self.dbuser_pwd = dbuser_pwd
        self.db_server_host = db_server_host
        self.dbport = dbport
        self.db_name = db_name
        
        url = '{}+{}://{}:{}@{}:{}/{}'.format(
           self.dbms, self.dbdriver, self.dbuser, self.dbuser_pwd, self.db_server_host, self.dbport, self.db_name)

        self._Engine = create_engine(url, encoding='utf8', echo=True)

    def __enter__(self):
        return self._Engine
    
    def __exit__(self, exception, exc_value, traceback):
        '''
            Make sure the dbconnection gets closed
        '''
        self._Engine.dispose()

class Form(QDialog):
    finish = pyqtSignal()
    test_it = pyqtSignal()
    
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        self.engine = None
        self.logged_in = True
        
        self.init_ui()

    def init_ui(self):

        self.db_localhost = QLineEdit()
        self.db_localhost.setText("localhost")
        
        self.db_user = QLineEdit()
        self.db_user.setText("root")
        
        self.db_user_pwd = QLineEdit()
        self.db_user_pwd.setText("password")
        
        self.db_name = QLineEdit()
        self.db_name.setText("test")
        
        self.db_port = QLineEdit()
        self.db_port.setText("3306")

        self.text_edit_status = QTextEdit()
        
        self.pushButton_connection = QPushButton()
        self.pushButton_connection.setText("Connect") 
        
        self.pushButton_disconnection = QPushButton()
        self.pushButton_disconnection.setText("Disconnect")

        self.pushButton_close = QPushButton()
        self.pushButton_close.setText("Close")
        
        layout = QFormLayout()
        layout.addWidget(self.db_localhost)
        layout.addWidget(self.db_user)
        layout.addWidget(self.db_user_pwd)
        layout.addWidget(self.db_name)
        layout.addWidget(self.db_port)
        layout.addWidget(self.text_edit_status)
        
        layout.addWidget(self.pushButton_connection)
        layout.addWidget(self.pushButton_disconnection)
        layout.addWidget(self.pushButton_close)
        
        self.setLayout(layout)
        self.setWindowTitle("Log In")

        self.init_signal_slot_pushButton()
         
    def log_in(self):
        try:
            self._engine = Engine(dbms='mysql',
                                 dbdriver='pymysql',
                                 dbuser=unicode(self.db_user.text()),
                                 dbuser_pwd=unicode(self.db_user_pwd.text()),
                                 db_server_host=unicode(self.db_localhost.text()),
                                 dbport=unicode(self.db_port.text()),
                                 db_name=unicode(self.db_name.text()))

            
            self.text_edit_status.setText("Connecting to the database... please wait!")
            
            with self._engine as engine:
                while logged_in:
                    with Session(engine) as session:
                        pass
                        # do_stuff()
            
        except TypeError as err:
            self.text_edit_status.setText((str(err)))
        
    def log_out(self):
        pass

    def init_signal_slot_pushButton(self):

        self.pushButton_connection.clicked.connect(self.log_in)
        
        self.pushButton_disconnection.clicked.connect(self.log_out)
        
        self.pushButton_close.clicked.connect(self.close)

    def button_click(self):
        pass


app = QApplication(argv)
form = Form()
form.show()
app.exec_()
BlackJack

@Sophus: Dafür ist ``with`` nicht gedacht/geeignet.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Ich habe einen gedanklichen Knoten. Wie würdest du es denn bewerkstelligen? Engine und Session in eine Klasse unterbringen und in der Klasse zusätzlich zu den (__enter__ und __exit__)-Methoden eine weitere Methode einbauen, die sich um das trennen der Verbindung kümmert?
BlackJack

@Sophus: Eigentlich braucht man doch nur ein SqlAlchemy-Engine-Objekt und eventuell eine `Session`-Factory die mit `sessionmaker()` erstellt wurde. Das kann man natürlich in einem Objekt kapseln wenn man möchte. Login braucht man nur einmal, wenn das Objekt erstellt wird, und Logout wäre einfach nur ein `dispose()`-Aufruf auf dem `Engine`-Objekt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@BlackJack: In Etwas so? Ich habe aus zwei separate Klasse eine gemacht, die allerdings den Context-Manager-Protokoll unterstützt. In der SessionScope()-Klasse habe ich dann die disconnect()-Methode hinzugefügt. Meine Befürchtung war/ist nur, dass dadurch die Kapselung aufgehoben wird? Aus diesem Grund bin ich zu Anfang auf zwei separate Klasse übergesprungen.

Code: Alles auswählen

from sys import argv

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.exc import SQLAlchemyError

from PyQt4.QtCore import Qt
 
from PyQt4.QtGui import QDialog, QApplication, QPushButton, \
     QLineEdit, QFormLayout, QTextEdit 
 

class SessionScope(object):
    def __init__(self, dbms=None, dbdriver=None,
                 dbuser=None, dbuser_pwd=None,
                 db_server_host=None, dbport=None, db_name=None):

        self.dbms = dbms
        self.dbdriver = dbdriver
        self.dbuser = dbuser
        self.dbuser_pwd = dbuser_pwd
        self.db_server_host = db_server_host
        self.dbport = dbport
        self.db_name = db_name
        
        url = '{}+{}://{}:{}@{}:{}/{}'.format(
           self.dbms, self.dbdriver, self.dbuser, self.dbuser_pwd, self.db_server_host, self.dbport, self.db_name)

        self._Engine = create_engine(url, encoding='utf8', echo=True)

        # store a sessionmaker for this db connection object
        self._Session = sessionmaker(bind=self._Engine)
        self.session = None

    def disconnect(self):
        '''
            Make sure the dbconnection gets closed
        '''
        self._Engine.dispose()

    def __enter__(self):
        self.session = self._Session()
        return self._Session()
    
    def __exit__(self, exception, exc_value, traceback):

        try:
            if exception:
                self.session.rollback()
            else:
                self.session.commit()
        finally:

            self.session.close()
            self.session = None
            

class Form(QDialog):
    
    def __init__(self, parent=None):
        QDialog.__init__(self, parent)

        self._session_scope = None
        
        self.init_ui()

    def init_ui(self):

        self.db_localhost = QLineEdit()
        self.db_localhost.setText("localhost")
        
        self.db_user = QLineEdit()
        self.db_user.setText("root")
        
        self.db_user_pwd = QLineEdit()
        self.db_user_pwd.setText("danny5658")
        
        self.db_name = QLineEdit()
        self.db_name.setText("test")
        
        self.db_port = QLineEdit()
        self.db_port.setText("3306")

        self.text_edit_status = QTextEdit()
        
        self.pushButton_connection = QPushButton()
        self.pushButton_connection.setText("Connect") 
        
        self.pushButton_disconnection = QPushButton()
        self.pushButton_disconnection.setText("Disconnect")

        self.pushButton_close = QPushButton()
        self.pushButton_close.setText("Close")
        
        layout = QFormLayout()
        layout.addWidget(self.db_localhost)
        layout.addWidget(self.db_user)
        layout.addWidget(self.db_user_pwd)
        layout.addWidget(self.db_name)
        layout.addWidget(self.db_port)
        layout.addWidget(self.text_edit_status)
        
        layout.addWidget(self.pushButton_connection)
        layout.addWidget(self.pushButton_disconnection)
        layout.addWidget(self.pushButton_close)
        
        self.setLayout(layout)
        self.setWindowTitle("Log In")

        self.init_signal_slot_pushButton()
         
    def log_in(self):
        try:
            self._session_scope = SessionScope(dbms='mysql',
                                 dbdriver='pymysql',
                                 dbuser=unicode(self.db_user.text()),
                                 dbuser_pwd=unicode(self.db_user_pwd.text()),
                                 db_server_host=unicode(self.db_localhost.text()),
                                 dbport=unicode(self.db_port.text()),
                                 db_name=unicode(self.db_name.text()))
            
            self.text_edit_status.setText("Connecting to the database... please wait!")

            self.testing_connection(self._session_scope)

        except TypeError as err:
            
            self.text_edit_status.setText((str(err)))
        
    def log_out(self):
        self._session_scope.disconnect()

    def testing_connection(self, session_object):
        try:
            with session_object as session:
                session.execute("SELECT Version();")

            self.text_edit_status.setText("Connection established successfully.")

        except SQLAlchemyError as err:
            self.text_edit_status.setText((str(err[0])))

    def init_signal_slot_pushButton(self):

        self.pushButton_connection.clicked.connect(self.log_in)
        
        self.pushButton_disconnection.clicked.connect(self.log_out)
        
        self.pushButton_close.clicked.connect(self.close)

    def button_click(self):
        pass


app = QApplication(argv)
form = Form()
form.show()
app.exec_()

Antworten