der nachfolgende, ausführbare Quelltext wirkt auf dem ersten Blick ein wenig "groß". Aber keine Bange, bestimmte Aspekte könnt ihr getrost ignorieren. Warum ich das Programm so groß gestaltet habe? Auf diese Weise möchte ich meinem Problem ziemlich nahe kommen
Zeilen, die ihr ignorieren könnt:
- Zeile 18 - 72: hierbei handelt es sich nur um Tabellen
- Zeile 180 - 224: hier handelt es sich von einer Session, die ich in ein Context-Manager umgerüstet habe.
- Zeile 336 - 413: hier werden nur Daten eingetragen, wenn der Anwender es sich wünscht. Mit gefüllten Tabellen lässt sich besser arbeiten, als mit leeren.
- Zeile 415 - 447: der übliche Start eines programm
Was erwarte ich?
Auf der derzeitigen GUI sind insgesamt acht QComboBox(), für jede Kategorie eine QComboBox().
Wenn ihr einen Blick in die start_all_selection()-Methode (Zeile 272) der MyCustomDialog()-Klasse werft, dann seht ihr, dass ich
in Zeile 295-296 vor habe mehrere Threads zu starten. Warum ich der start_thread()-Methode die QComboBox() und bestimmte String übergebe, wird erst in der select_all()-Methode (Zeile 80) der MasterDataManipulation()-Klasse deutlich. Meine Ausgangsidee war, dass ich die Session einmal mittels der with-Anweisung öffne, nach bestimmten Kategorien suche, und dann die Abfrage erstelle, damit diese dann später über die die jeweilige QComboBox() ausgegeben werden. Noch einmal kurz zurück. Wenn ihr einen Blick in der start_thread()-Methode (Zeile 305) der MyCustomDialog()-Klasse werft, seht ihr, dass nicht nur jedesmal ein neuer Qthread und Worker() erstellt wird, sondern auch gleich dazu die MasterDataManipulation()-Klasse, der die aktuelle Session übergeben wird.
Auf diese Weise habe ich erwartet, dass bei jeder Abfrage eine neue Session eröffnet wird, und dann wieder geschlossen wird. Das heißt, die Abfrage für die Kategorie Gender wird eine neue Session geöffnet, dann für die Kategorie Religion etc. Also für jede Kategorie nicht nur einen neuen Thread, sondern auch neue Sitzung.
Problem
So wie der Quelltext jetzt ist, funktioniert es einwandfrei - zumindest augenscheinlich. Jetzt gehen wir in die Worker()-Klasse und ändern in Zeile 152 den Interval des QTimer() von derzeit 1000 auf 1. Wir wollen ja, dass das Programm ein bisschen zügiger die Daten holt. Wenn ich dann das Programm ausführe, verhält sich mein Programm ein wenig merkwürdig, als ob es ein Eigenleben besitzt.
Manchmal kommt es vor, dass zwar keine Ausnahmen geworfen werden, jedoch werden NICHT ALLE QComboBox()-Objekte gefüllt. Mal bleibt nur eine leer oder auch mehrere.
Des Weiteren passiert auch hin und wieder mal, dass mir von seiten SQLAlchemy gesagt wird, dass die Verbindung geschlossen wurde. Dazu habe ich eine Fehlermeldung:
Traceback (most recent call last):
File "D:\Dan\Python\Xarphus\xarphus\subclass_master_data_load_data_item.py", line 151, in populate_item
self.populate_item_signal.emit(next(self._element))
File "D:\Dan\Python\Xarphus\xarphus\core\manage_data_manipulation_master_data.py", line 232, in select_all
yield record.id, record.relationship
File "D:\Dan\Python\Xarphus\xarphus\core\manage_db_connection.py", line 245, in __exit__
self.session.commit()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\session.py", line 906, in commit
self.transaction.commit()
File "C:\Python27\lib\site-packages\sqlalchemy\orm\session.py", line 465, in commit
t[1].commit()
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1632, in commit
self._do_commit()
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1663, in _do_commit
self.connection._commit_impl()
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 726, in _commit_impl
self.connection._reset_agent is self.__transaction:
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 351, in connection
self._handle_dbapi_exception(e, None, None, None, None)
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 1405, in _handle_dbapi_exception
util.reraise(*exc_info)
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 349, in connection
return self._revalidate_connection()
File "C:\Python27\lib\site-packages\sqlalchemy\engine\base.py", line 429, in _revalidate_connection
raise exc.ResourceClosedError("This Connection is closed")
ResourceClosedError: This Connection is closed
Code: Alles auswählen
import sys
from PyQt4.QtCore import QObject, QThread, pyqtSignal, pyqtSlot, QTimer
from PyQt4.QtGui import QApplication, QPushButton, QVBoxLayout, QDialog, \
QComboBox, QLabel
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy import Table, Column, Integer, String, MetaData
from traceback import format_exc
from sys import exc_info
Base = declarative_base()
class PERSON_GENDER(Base):
__tablename__ = "person_gender"
id = Column(Integer, primary_key=True)
gender = Column(String(50), nullable=False, unique=True)
class PERSON_NATIONALITY(Base):
__tablename__ = "person_nationality"
id = Column(Integer, primary_key=True)
nationality = Column(String(100), nullable=False, unique=True)
class PERSON_SALUTATION(Base):
__tablename__ = "person_salutation"
id = Column(Integer, primary_key=True)
salutation = Column(String(50), nullable=False, unique=True)
class PERSON_TITLE(Base):
__tablename__ = "person_title"
id = Column(Integer, primary_key=True)
title = Column(String(50), nullable=False, unique=True)
class PERSON_HAIR_COLOR(Base):
__tablename__ = "person_hair_color"
id = Column(Integer, primary_key=True)
hair_color = Column(String(50), nullable=False, unique=True)
class PERSON_EYE_COLOR(Base):
__tablename__ = "person_eye_color"
id = Column(Integer, primary_key=True)
eye_color = Column(String(50), nullable=False, unique=True)
class PERSON_RELIGION(Base):
__tablename__ = "person_religion"
id = Column(Integer, primary_key=True)
religion = Column(String(50), nullable=False, unique=True)
class PERSON_RELATIONSHIP_STATUS(Base):
__tablename__ = "person_relationship_status"
id = Column(Integer, primary_key=True)
relationship_status = Column(String(100), nullable=False, unique=True)
class MasterDataManipulation(object):
def __init__(self, session_object=None):
self._session_scope = session_object
def select_all(self, category):
try:
with self._session_scope as session:
dict_store_session_query = {'person_gender': lambda: session.query(PERSON_GENDER),
'person_nationality': lambda: session.query(PERSON_NATIONALITY),
'person_salutation': lambda: session.query(PERSON_SALUTATION),
'person_title': lambda: session.query(PERSON_TITLE),
'person_hair_color': lambda: session.query(PERSON_HAIR_COLOR),
'person_eye_color': lambda: session.query(PERSON_EYE_COLOR),
'person_religion': lambda: session.query(PERSON_RELIGION),
'person_relationship_status': lambda: session.query(PERSON_RELATIONSHIP_STATUS)}
for record in dict_store_session_query[category]():
if category == 'person_gender':
yield record.id, record.gender
if category == 'person_nationality':
yield record.id, record.nationality
if category == 'person_salutation':
yield record.id, record.salutation
if category == 'person_title':
yield record.id, record.title
if category == 'person_hair_color':
yield record.id, record.hair_color
if category == 'person_eye_color':
yield record.id, record.eye_color
if category == 'person_religion':
yield record.id, record.religion
if category == 'person_relationship_status':
yield record.id, record.relationship_status
except AttributeError:
print "select all, desired_trace", format_exc(exc_info())
return
class Worker(QObject):
finish_progress = pyqtSignal()
populate_item_signal = pyqtSignal(object, object, object)
def __init__(self,
category=None,
combo_box=None,
query_data=None,
parent=None):
QObject.__init__(self, parent)
self.category = category
self.query_data = query_data
self.combo_box=combo_box
''' Create attributes '''
self._run_semaphore = 1
def init_object(self):
'''
Storing new generator object, will reuse it.
That means you have to create one generator.
'''
self._element = self.query_data(self.category)
self.timer = QTimer()
# assoziiert select_all_data() mit TIMEOUT Ereignis
self.timer.setSingleShot(False)
self.timer.setInterval(1000)
self.timer.timeout.connect(self.populate_item)
self.timer.start()
def populate_item(self):
try:
if self._run_semaphore == 0:
self._run_semaphore = 1
raise StopIteration
else:
self.populate_item_signal.emit(next(self._element), self.category, self.combo_box)
except StopIteration:
print "StopIteration is raised"
self.timer.stop()
def stop(self):
self._run_semaphore == 0
#self.timer.stop()
class SessionScope(object):
def __init__(self, dbms=None, dbdriver=None,
dbuser=None, dbuser_pwd=None,
db_server_host=None, dbport=None, db_name=None,
admin_database=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
self.admin_database = admin_database
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)
self.session = None
self._session_factory = sessionmaker(bind=self._Engine)
self._Session = scoped_session(sessionmaker(bind=self._Engine, expire_on_commit=False))
Base.metadata.create_all(self._Engine)
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()
class MyCustomDialog(QDialog):
finish = pyqtSignal()
def __init__(self, scoped_session=None, parent=None):
QDialog.__init__(self, parent)
self._session_scope = scoped_session
self._list_threads = []
self.init_ui()
self.start_all_selection()
def init_ui(self):
layout = QVBoxLayout(self)
self.combo_person_title = QComboBox(self)
self.combo_person_salutation = QComboBox(self)
self.combo_person_gender = QComboBox(self)
self.combo_person_religion = QComboBox(self)
self.combo_person_relationship_status = QComboBox(self)
self.combo_person_nationality = QComboBox(self)
self.combo_person_eye_color = QComboBox(self)
self.combo_person_hair_color = QComboBox(self)
self.pushButton_populate_combo = QPushButton("Re-populate", self)
self.pushButton_stopp = QPushButton("Stopp", self)
self.pushButton_close = QPushButton("Close", self)
layout.addWidget(self.combo_person_title)
layout.addWidget(self.combo_person_salutation)
layout.addWidget(self.combo_person_gender)
layout.addWidget(self.combo_person_religion)
layout.addWidget(self.combo_person_nationality)
layout.addWidget(self.combo_person_relationship_status)
layout.addWidget(self.combo_person_eye_color)
layout.addWidget(self.combo_person_hair_color)
layout.addWidget(self.pushButton_populate_combo)
layout.addWidget(self.pushButton_stopp)
layout.addWidget(self.pushButton_close)
self.pushButton_stopp.clicked.connect(self.on_finish)
self.pushButton_populate_combo.clicked.connect(self.start_all_selection)
self.pushButton_close.clicked.connect(self.close)
def start_all_selection(self):
list_comboxes = self.findChildren(QComboBox)
for combox in list_comboxes:
combox.clear()
list_start_threads = [('person_gender',self.combo_person_gender),
('person_nationality', self.combo_person_nationality),
('person_salutation', self.combo_person_salutation),
('person_title', self.combo_person_title),
('person_hair_color',self.combo_person_hair_color),
('person_eye_color', self.combo_person_eye_color),
('person_religion', self.combo_person_religion),
('person_relationship_status', self.combo_person_relationship_status)]
for category, combo_box in list_start_threads:
self.start_thread(category=category, combo_box=combo_box)
def fill_combo_boxt(self, item, category, combo_box):
_, text = item
combo_box.addItem(text)
def on_label(self, i):
self.label.setText("Result: {}".format(i))
def start_thread(self, category=None, combo_box=None):
master_data_manipulation = MasterDataManipulation(session_object=self._session_scope)
query_data=master_data_manipulation.select_all
task_thread = QThread(self)
task_thread.work = Worker(query_data=query_data,
combo_box=combo_box,
category=category)
''' We need to store threads '''
self._list_threads.append(task_thread)
task_thread.work.moveToThread(task_thread)
task_thread.work.populate_item_signal.connect(self.fill_combo_boxt)
self.finish.connect(task_thread.work.stop)
task_thread.started.connect(task_thread.work.init_object)
task_thread.finished.connect(task_thread.deleteLater)
''' This will emit 'started' and start thread's event loop '''
task_thread.start()
@pyqtSlot()
def abort_workers(self):
self.finish.emit()
for thread in self._list_threads:
''' this will quit **as soon as thread event loop unblocks** '''
thread.quit()
''' so you need to wait for it to *actually* quit'''
thread.wait()
def on_finish(self):
self.finish.emit()
def closeEvent(self, event):
''' Re-implementaate to handle with created threads '''
self.abort_workers()
def populate_database(sess=None):
try:
with sess as session:
genders = [PERSON_GENDER(gender="male"),
PERSON_GENDER(gender="female"),
PERSON_GENDER(gender="hybrid"),
PERSON_GENDER(gender="trans")]
session.add_all(genders)
nationalitys = [PERSON_NATIONALITY(nationality="german"),
PERSON_NATIONALITY(nationality="english"),
PERSON_NATIONALITY(nationality="french"),
PERSON_NATIONALITY(nationality="spanish"),
PERSON_NATIONALITY(nationality="greek"),
PERSON_NATIONALITY(nationality="mexican"),
PERSON_NATIONALITY(nationality="sweden"),]
session.add_all(nationalitys)
salutations = [PERSON_SALUTATION(salutation="Mister"),
PERSON_SALUTATION(salutation="Miss"),
PERSON_SALUTATION(salutation="Lady"),
PERSON_SALUTATION(salutation="Ma'am"),
PERSON_SALUTATION(salutation="Sir"),
PERSON_SALUTATION(salutation="Queen"),
PERSON_SALUTATION(salutation="Grandma"),]
session.add_all(salutations)
titles = [PERSON_TITLE(title="Prof."),
PERSON_TITLE(title="Dr."),
PERSON_TITLE(title="Sir"),
PERSON_TITLE(title="B.A."),
PERSON_TITLE(title="M.A."),
PERSON_TITLE(title="Bishop"),
PERSON_TITLE(title="God"),]
session.add_all(titles)
hair_colors = [PERSON_HAIR_COLOR(hair_color="blond."),
PERSON_HAIR_COLOR(hair_color="gray"),
PERSON_HAIR_COLOR(hair_color="blue"),
PERSON_HAIR_COLOR(hair_color="white"),
PERSON_HAIR_COLOR(hair_color="black"),
PERSON_HAIR_COLOR(hair_color="violet"),
PERSON_HAIR_COLOR(hair_color="brunette"),]
session.add_all(hair_colors)
eye_colors = [PERSON_EYE_COLOR(eye_color="blue."),
PERSON_EYE_COLOR(eye_color="blue-gray"),
PERSON_EYE_COLOR(eye_color="green"),
PERSON_EYE_COLOR(eye_color="white"),
PERSON_EYE_COLOR(eye_color="black"),
PERSON_EYE_COLOR(eye_color="violet"),
PERSON_EYE_COLOR(eye_color="brunette"),]
session.add_all(eye_colors)
religions = [PERSON_RELIGION(religion="Catholic."),
PERSON_RELIGION(religion="Protestant"),
PERSON_RELIGION(religion="Jew"),
PERSON_RELIGION(religion="Muslim"),
PERSON_RELIGION(religion="Islam"),
PERSON_RELIGION(religion="Hindu"),
PERSON_RELIGION(religion="Buddha"),]
session.add_all(religions)
relationship_status = [PERSON_RELATIONSHIP_STATUS(relationship_status="Single."),
PERSON_RELATIONSHIP_STATUS(relationship_status="In a relationship"),
PERSON_RELATIONSHIP_STATUS(relationship_status="Married"),
PERSON_RELATIONSHIP_STATUS(relationship_status="In a open relationship"),
PERSON_RELATIONSHIP_STATUS(relationship_status="Engaged"),
PERSON_RELATIONSHIP_STATUS(relationship_status="Divorced"),
PERSON_RELATIONSHIP_STATUS(relationship_status="Separate"),]
session.add_all(relationship_status)
session.commit()
except SQLAlchemyError:
print "SQLAlchemyError", format_exc(exc_info())
def main():
dbms = raw_input('Enter database type: ')
dbdriver = raw_input('Enter database driver: ')
dbuser = raw_input('Enter user name: ')
dbuser_pwd = raw_input('Enter user password: ')
db_server_host = raw_input('Enter server host: ')
dbport = raw_input('Enter port: ')
db_name = raw_input('Enter database name: ')
try:
session_scope = SessionScope(dbms = dbms,
dbdriver = dbdriver,
dbuser = dbuser,
dbuser_pwd = dbuser_pwd,
db_server_host = db_server_host,
dbport = dbport,
db_name = db_name)
answer = raw_input('Do you want to populate database? Type yes or no: ')
if answer.lower() == 'yes':
populate_database(sess=session_scope)
app = QApplication(sys.argv)
window = MyCustomDialog(scoped_session = session_scope)
window.show()
sys.exit(app.exec_())
except TypeError:
print "ERROR", format_exc(exc_info())
if __name__ == "__main__":
main()