From 14122770ef1c8338124d3c8e578d4b5d5d307870 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Sat, 3 Oct 2009 21:05:41 +0300 Subject: [PATCH 01/17] Make hud's menu-area text configurable By default the hud positions a little box on top-left corner of each table. Make the text in this box user-modifiable without touching the source. Most likely useful for active users and those who play with smaller tables. On shrunk table the default box may cover some of the players' cards. --- pyfpdb/Configuration.py | 26 ++++++++++++++++++++++++++ pyfpdb/Hud.py | 3 ++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/pyfpdb/Configuration.py b/pyfpdb/Configuration.py index b8022a45..dbe973c1 100755 --- a/pyfpdb/Configuration.py +++ b/pyfpdb/Configuration.py @@ -278,6 +278,15 @@ class Import: return " interval = %s\n callFpdbHud = %s\n hhArchiveBase = %s\n saveActions = %s\n fastStoreHudCache = %s\n" \ % (self.interval, self.callFpdbHud, self.hhArchiveBase, self.saveActions, self.fastStoreHudCache) +class HudUI: + def __init__(self, node): + self.node = node + self.label = node.getAttribute('label') + + def __str__(self): + return " label = %s\n" % self.label + + class Tv: def __init__(self, node): self.combinedStealFold = node.getAttribute("combinedStealFold") @@ -389,6 +398,10 @@ class Config: imp = Import(node = imp_node) self.imp = imp + for hui_node in doc.getElementsByTagName('hud_ui'): + hui = HudUI(node = hui_node) + self.ui = hui + for tv_node in doc.getElementsByTagName("tv"): tv = Tv(node = tv_node) self.tv = tv @@ -598,6 +611,19 @@ class Config: try: tv['combinedPostflop'] = self.tv.combinedPostflop except: tv['combinedPostflop'] = True return tv + + # Allow to change the menu appearance + def get_hud_ui_parameters(self): + hui = {} + default_text = 'FPDB Menu - Right click\nLeft-Drag to Move' + try: + hui['label'] = self.ui.label + if self.ui.label == '': # Empty menu label is a big no-no + hui['label'] = default_text + except: + hui['label'] = default_text + return hui + def get_import_parameters(self): imp = {} diff --git a/pyfpdb/Hud.py b/pyfpdb/Hud.py index 77143d79..6d88e6d4 100644 --- a/pyfpdb/Hud.py +++ b/pyfpdb/Hud.py @@ -80,6 +80,7 @@ class Hud: (font, font_size) = config.get_default_font(self.table.site) self.colors = config.get_default_colors(self.table.site) + self.hud_ui = config.get_hud_ui_parameters() self.backgroundcolor = gtk.gdk.color_parse(self.colors['hudbgcolor']) self.foregroundcolor = gtk.gdk.color_parse(self.colors['hudfgcolor']) @@ -114,7 +115,7 @@ class Hud: win.set_opacity(self.colors["hudopacity"]) eventbox = gtk.EventBox() - label = gtk.Label("FPDB Menu - Right click\nLeft-Drag to Move") + label = gtk.Label(self.hud_ui['label']) win.add(eventbox) eventbox.add(label) From f6eb365b3c3d80bda88253932de2e024021356f7 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Sat, 3 Oct 2009 21:10:09 +0300 Subject: [PATCH 02/17] Add the default box text to example config Now that the text on HUD's box is configurable, move the default text from code to default/sample config. --- pyfpdb/HUD_config.xml.example | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example index adc8141c..de2f1bba 100644 --- a/pyfpdb/HUD_config.xml.example +++ b/pyfpdb/HUD_config.xml.example @@ -4,6 +4,9 @@ + + Date: Mon, 26 Oct 2009 17:49:37 +0200 Subject: [PATCH 03/17] Bump version to 0.12 in packaging --- packaging/debian/changelog | 6 ++++++ setup.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packaging/debian/changelog b/packaging/debian/changelog index ce102bf5..ec66f4a7 100644 --- a/packaging/debian/changelog +++ b/packaging/debian/changelog @@ -1,3 +1,9 @@ +free-poker-tools (0.12-1) unstable; urgency=low + + * New release + + -- Mika Bostrom Mon, 26 Oct 2009 17:49:07 +0200 + free-poker-tools (0.11.3+git20091023) unstable; urgency=low * Snapshot release diff --git a/setup.py b/setup.py index 0ba21bf0..689191bd 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ from distutils.core import setup setup(name = 'fpdb', description = 'Free Poker Database', - version = '0.11.3', + version = '0.12', author = 'FPDB team', author_email = 'fpdb-main@lists.sourceforge.net', packages = ['fpdb'], From 2be3ab88975478fded91970bf7fe1682b857b664 Mon Sep 17 00:00:00 2001 From: Worros Date: Tue, 27 Oct 2009 13:18:51 +0800 Subject: [PATCH 04/17] Add Stars icon to folder --- gfx/img-PokerStars-Small.xpm | 92 ++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 gfx/img-PokerStars-Small.xpm diff --git a/gfx/img-PokerStars-Small.xpm b/gfx/img-PokerStars-Small.xpm new file mode 100644 index 00000000..f4fd457d --- /dev/null +++ b/gfx/img-PokerStars-Small.xpm @@ -0,0 +1,92 @@ +/* XPM */ +/* C:\Program Files\PokerStars\PokerStarsUpdate.exe */ +static char *icon[] = { +"48 48 40 2", +"01 c #800000", +"08 c #C0DCC0", +"4C c #7F1F00", +"6C c #AA0000", +"6D c #AA0055", +"70 c #AA1F00", +"90 c #D40000", +"91 c #D40055", +"94 c #D41F00", +"95 c #D41F55", +"99 c #D43F55", +"9D c #D45F55", +"9E c #D45FAA", +"A1 c #D47F55", +"A2 c #D47FAA", +"A5 c #D49F55", +"A6 c #D49FAA", +"AA c #D4BFAA", +"AE c #D4DFAA", +"AF c #D4DFFF", +"B4 c #FF0055", +"B6 c #FF1F00", +"B7 c #FF1F55", +"BA c #FF3F00", +"BB c #FF3F55", +"BE c #FF5F00", +"BF c #FF5F55", +"C3 c #FF7F55", +"C4 c #FF7FAA", +"C7 c #FF9F55", +"C8 c #FF9FAA", +"CC c #FFBFAA", +"CD c #FFBFFF", +"D0 c #FFDFAA", +"D1 c #FFDFFF", +"D5 c #FFCCFF", +"F6 c #FFFBF0", +"F9 c #FF0000", +"FF c #FFFFFF", +" c None", +"707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070707070", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C", +"6CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAAD1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6949DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6BFBBF999F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"6CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A6A2C7B6F9F99DD1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A2C8CCBFB6B4F99099F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6C8CCC3BEB7F9F9F99099F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2C8D0C7BFBBB6F9B4F9F99099F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"6CFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A6AAD0C8C3C3BBF9F9F9F9B4F99099D1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFA6C8D0CCC7C3BFBAB6B4F9F9F99090909DF6FFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFA2CCF6CCC8C3BFBEBBF9F9F9B4F9F9B4909099F6FFFFFFFFFFFFFFFFFFFFFFFFFF6C", +"70FFFFFFFFFFFFFFFFFFFFFFFFA6CCD1CCC7C3C3BFBAB6B7F9F9F9B490F990906C99FFFFFFD1FFFFFFD1FFFFFFD1FF70", +"6CFFFFFFFFFFFFFFFFFFFFFFAAC8F6CCC7C8C3BFBFBFB6C4B7F9F9F9F9F99190906C9DFFFFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFD1FFFFFFD1FFFFFFAAC8F6CCCCC8C3C3C3BEBBB6F6BFF9B4F9B490F99091906CA2FFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFD1A6F6CCCCC7C7C3C3BFBBBABFFFC8F9F9F990F9909090906C90AAFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFF6A6D0D1CCC8C7C3C3BFBEBFB6CCFFF6F9F9F9B4F9B49090906C6C95F6FFFFFFFFFFFFFFFFFF6C", +"6CFFFFFFFFFFFFFFFFAACCF6CCCCC7C4C3BFBEBBBABBD1FFFFBFF9F9F990F9F99190956C6C99FFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFD1C8F6CCCCC8C7C7C8C7C8C7C3C8F6FFF6CDBFBBBFBBBFBF9090906C6C6CA6FFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFAAF6D0CCC8C7C3C4D0FFFFFFFFF6F6FFFFF6FFFFFFFFD199F9909090706C99F6FFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFD5CCD1CCCCC7C3C3C3BFC8F6FFFFFFFFFFFFFFFFFFFFC895F990B490916C016CA2FFFFFFFFFFFFFF6C", +"6CFFFFFFFFFFF6AAD1D0CCC7C8C3C3C3BFBEBFD1FFFFFFFFFFFFFFFFA1F9F990F9909090906C0170F6FFFFFFFFFFFF70", +"70FFFFFFFFFFD1CCD0CCC8C8C3C3BFBFBFBABBBACCFFFFFFFFFFD1BBF9F990B490B490906D6C6C6CA6FFFFFFFFFFFF70", +"70FFFFFFFFF6AACCD1CCC7C7C3C3BFBEBFBABABBD1FFFFFFFFFFF699F990B4F990F99090906C700199FFFFFFFFFFFF70", +"70FFFFFFFFFFA6D0CCC8C7C3C3BFBFBFBABBB6BFF6FFFFFFFFFFFFC8F9F990F99091F99190906C0194D1FFFFFFFFFF6C", +"6CFFFFFFFFF6C8CCCCC7C4C3BFBFBEBFBABBB6C8FFFFCCBBC4FFFFD091F9F991F9F990F9906C6D016CAAFFFFFFFFFF70", +"70FFFFFFF6D5A5CCC8C7C3C3BFBEBBBABBBAB6F6F6C4F9F9F9BBD1FFBBF990F9F991F99091906C016CA2FFFFFFFFFF70", +"70FFFFFFFFAEC4C7C7C3BFBFBEBFBABBB6B6BBD1BBF9F9F9F9F9B6CCC8F9B4F990F990909090706C6CA2F6FFFFFFFF70", +"70FFFFFFFFCCC3C8C3C3BFBEBBBABBBAB6B7BBB6F9F9F9B4F9F9F9B4BBB690F9B490B49090916C016CA1FFFFFFFFFF6C", +"6CFFFFFFFFD19DC3BFBFBFBBBABBB6B7B6F9F9F9F9B4F9F9F9B4F9F9F9B4F9F9F9F990B4906C6C016CC8FFFFFFFFFF70", +"70FFFFFFFFF69DBEBFBEBABABBB6B6B6F9F9F9B4F990F9B4F9F9F9F9F9F9F991F990F99090706C016CAAFFFFFFFFFF70", +"70FFFFFFFFFFA2B6BBBAB7B6B6B6B4F9B4F991909090F9F9F990B4F9B4F9F9F9F9B49090906D6C0199FFFFFFFFFFFF70", +"70FFFFFFFFF6F694F9B6B6B4F9F9F9F990F9909090C8A2F999CC95F9F9F9B4F9909090916C6C0190A6FFFFFFFFFFFF6C", +"6CFFFFFFFFFFFFCC91F990F9B4F991F991906C909DFFCCF9BFFFCCF9F9F990F9F991906C70016CA2FFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFC895909090909090906C909EFFFFA2F995FFFFA6B790F99190906C6C6C6CC4F6F6FFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFAEBF9590906C906C99C8FFF6FFBFB69008FFFFF69DB7906C6C6C9499D5F6F6FFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFD0A6A2C3A6CCFFFFFFFF08BBF990A2F6FFFFFFF6CCC8C8CCAFFFF6FFFFFFFFFFFFFFFFFF6C", +"6CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6BFF99095F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6D1A1BFF9906CA6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA2C3BBF9F96C95F6F6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA6A1C3B6F9B4906C99FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C", +"6CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAAC3C3BAB4F9F990906C9DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD19DBBBBB6F9F9F990F990909099AAFFFFFFFFFFFFFFFFFFF6FFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFF6A2A1BF9D9EBFA2BFA2BFA29DA2BFA2CCFFFFFFFFF6FFFFFFFFFFFFFFFFFFFF70", +"70FFFFFFFFFFFFFFFFFFFFFFFFFFFFF6FFFFFFFFFFFFFFFFFFFFFFFFF6FFF6FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C", +"6CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF70", +"704C706C7001706C7001706C70706C7070707070707070706C7070707070704C707001707001706C704C6C7070017070"}; From 30103c5cb2a45a520afd8c03154ec71d930b1e53 Mon Sep 17 00:00:00 2001 From: fpdb-mme Date: Tue, 27 Oct 2009 09:01:06 +0100 Subject: [PATCH 05/17] second try... Signed-off-by: fpdb-mme --- pyfpdb/DatabaseManager.py | 419 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 419 insertions(+) create mode 100644 pyfpdb/DatabaseManager.py diff --git a/pyfpdb/DatabaseManager.py b/pyfpdb/DatabaseManager.py new file mode 100644 index 00000000..130035cb --- /dev/null +++ b/pyfpdb/DatabaseManager.py @@ -0,0 +1,419 @@ + +import os +import pygtk +pygtk.require('2.0') +import gtk + +#******************************************************************************************************* +class DatabaseManager(object): + DatabaseTypes = {} + + def __init__(self, defaultDatabaseType=None): + self._defaultDatabaseType = defaultDatabaseType + def set_default_database_type(self, databaseType): + self._defaultDatabaseType = defaultDatabaseType + def get_default_database_type(self): + return self._defaultDatabaseType + +class DatabaseTypeMeta(type): + def __new__(klass, name, bases, kws): + newKlass = type.__new__(klass, name, bases, kws) + if newKlass.Type is not None: + if newKlass.Type in DatabaseManager.DatabaseTypes: + raise ValueError('data base type already registered for: %s' % newKlass.Type) + DatabaseManager.DatabaseTypes[newKlass.Type] = newKlass + return newKlass + +class DatabaseTypeBase(object): + __metaclass__ = DatabaseTypeMeta + Type = None + + DBHasHost = 0x1 + DBHasFile = 0x2 + DBHasPort = 0x4 + DBHasUser = 0x8 + DBHasPassword = 0x10 + DBHasDatabase = 0x20 + DBHasName = 0x40 + + DBFlagsFileSystem = DBHasFile|DBHasName + DBFlagsServer = DBHasHost|DBHasPort|DBHasUser|DBHasPassword|DBHasDatabase|DBHasName + + def __init__(self, host='', file='', port=0, user='', password='', database='', table='', name=''): + self.host = host + self.file = file + self.port = port + self.user = user + self.password = password + self.database = database + self.name = name + +class DatabaseTypePostgres(DatabaseTypeBase): + Type = 'postgres' + Flags = DatabaseTypeBase.DBFlagsServer + def __init__(self, host='localhost', file='', port=5432, user='postgres', password='', database='fpdb', name=''): + DatabaseTypeBase.__init__(self, host=host, file=file, port=port, user=user, password=password, database=database, name=name) + +class DatabaseTypeMysql(DatabaseTypeBase): + Type = 'mysql' + Flags = DatabaseTypeBase.DBFlagsServer + def __init__(self, host='localhost', file='root', port=3306, user='', password='', database='fpdb', name=''): + DatabaseTypeBase.__init__(self, host=host, file=file, port=port, user=user, password=password, database=database, name=name) + +class DatabaseTypeSqLite(DatabaseTypeBase): + Type = 'sqlite' + Flags = DatabaseTypeBase.DBFlagsFileSystem + def __init__(self, host='', file='', port=0, user='', password='',database='', name=''): + DatabaseTypeBase.__init__(self, host=host, file=file, port=port, user=user, password=password, database=database, name=name) + +#*************************************************************************************************************************** +class MyFileChooserButton(gtk.HBox): + #NOTE: for some weird reason it is impossible to let the user choose a non exiting filename with gtk.FileChooserButton, so impl our own on the fly + def __init__(self): + gtk.HBox.__init__(self) + self.set_homogeneous(False) + + self.entry = gtk.Entry() + self.button = gtk.Button('...') + self.button.connect('clicked', self.on_button_clicked) + + # layout widgets + self.pack_start(self.entry, True, True) + self.pack_start(self.button, False, False) + + def get_filename(self): + return self.entry.get_text() + + def set_filename(self, name): + self.entry.set_text(name) + + def on_button_clicked(self, button): + dlg = gtk.FileChooserDialog( + title='Choose an exiting database file or type in name of a new one', + parent=None, + action=gtk.FILE_CHOOSER_ACTION_SAVE, + buttons=( + gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, + gtk.STOCK_OK, gtk.RESPONSE_OK, + ), + backend=None + ) + dlg.connect('confirm-overwrite', self.on_dialog_confirm_overwrite) + dlg.set_default_response(gtk.RESPONSE_OK) + dlg.set_do_overwrite_confirmation(True) + if dlg.run() == gtk.RESPONSE_OK: + self.set_filename(dlg.get_filename()) + dlg.destroy() + + #TODO: when the user selects a sqLite database file we got three possible actions + # 1. user types in a new filename. easy one, create the file + # 2. user selectes a file with the intention to overwrite it + # 3. user selects a file with the intention to plug an existing database file in + #IDEA: impl open_existing as plug in, never overwrite, cos we can not guess + #PROBLEMS: how to validate an existing file is a database? + def on_dialog_confirm_overwrite(self, dlg): + print dlg.get_filename() + + gtk.FILE_CHOOSER_CONFIRMATION_CONFIRM + #The file chooser will present its stock dialog to confirm overwriting an existing file. + + gtk.FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME + #The file chooser will terminate and accept the user's choice of a file name. + + gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN + # + + + + + +class DialogDatabaseProperties(gtk.Dialog): + def __init__(self, databaseManager, database=None,parent=None): + gtk.Dialog.__init__(self, + title="My dialog", + parent=parent, + flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + buttons=( + gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, + gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, + ) + ) + self.connect('response', self.on_dialog_response) + + # setup widget + self.widgetDatabaseProperties = WidgetDatabaseProperties(databaseManager,database=database) + self.vbox.pack_start(self.widgetDatabaseProperties, True, True) + self.widgetDatabaseProperties.show_all() + + def get_database(self): + return self.widgetDatabaseProperties.get_database() + + def on_dialog_response(self, dlg, responseId): + if responseId == gtk.RESPONSE_REJECT: + pass + elif responseId == gtk.RESPONSE_ACCEPT: + pass + + + +class WidgetDatabaseProperties(gtk.VBox): + def __init__(self, databaseManager, database=None): + gtk.VBox.__init__(self) + + self.fieldWidgets = ( #fieldName--> fieldHandler + { + 'label': gtk.Label('Name:'), + 'widget': gtk.Entry(), + 'getter': lambda widget, database: setattr(database, 'name', widget.get_text() ), + 'setter': lambda widget, database: widget.set_text(database.name), + 'isSensitive': lambda database: bool(database.Flags & database.DBHasName), + 'tooltip': '', + }, + { + 'label': gtk.Label('File:'), + 'widget': MyFileChooserButton(), + 'getter': lambda widget, database: lambda widget, database: setattr(database, 'file', widget.get_filename() ), + 'setter': lambda widget, database: widget.set_filename(database.file), + 'isSensitive': lambda database: bool(database.Flags & database.DBHasFile), + 'tooltip': '', + }, + { + 'label': gtk.Label('Host:'), + 'widget': gtk.Entry(), + 'getter': lambda widget, database: setattr(database, 'host', widget.get_text() ), + 'setter': lambda widget, database: widget.set_text(database.host), + 'isSensitive': lambda database: bool(database.Flags & database.DBHasHost), + 'tooltip': '', + }, + { + 'label': gtk.Label('Port:'), + 'widget': gtk.SpinButton(adjustment=gtk.Adjustment(value=0, lower=0, upper=999999, step_incr=1, page_incr=10) ), + 'getter': lambda widget, database: setattr(database, 'port', widget.get_value() ), + 'setter': lambda widget, database: widget.set_value(database.port), + 'isSensitive': lambda database: bool(database.Flags & database.DBHasPort), + 'tooltip': '', + }, + { + 'label': gtk.Label('User:'), + 'widget': gtk.Entry(), + 'getter': lambda widget, database: setattr(database, 'user', widget.get_text() ), + 'setter': lambda widget, database: widget.set_text(database.user), + 'isSensitive': lambda database: bool(database.Flags & database.DBHasUser), + 'tooltip': '', + }, + { + 'label': gtk.Label('Pwd:'), + 'widget': gtk.Entry(), + 'getter': lambda widget, database: setattr(database, 'password', widget.get_text() ), + 'setter': lambda widget, database: widget.set_text(database.password), + 'isSensitive': lambda database: bool(database.Flags & database.DBHasPassword), + 'tooltip': '', + }, + { + 'label': gtk.Label('DB:'), + 'widget': gtk.Entry(), + 'getter': lambda widget, database: setattr(database, 'database', widget.get_text() ), + 'setter': lambda widget, database: widget.set_text(database.database), + 'isSensitive': lambda database: bool(database.Flags & database.DBHasDatabase), + 'tooltip': 'enter name of the database to create', + }, + ) + + # setup database type combo + self.comboType = gtk.ComboBox() + listStore= gtk.ListStore(str, str) + self.comboType.set_model(listStore) + cell = gtk.CellRendererText() + self.comboType.pack_start(cell, True) + self.comboType.add_attribute(cell, 'text', 0) + # fill out combo with database type. we store (displayName, databaseType) in our model for later lookup + for dbType, dbDisplayName in sorted([(klass.Type, klass.Type) for klass in databaseManager.DatabaseTypes.values()]): + listStore.append( (dbDisplayName, dbType) ) + self.comboType.connect('changed', self.on_combo_type_changed) + + # init and layout field widgets + self.pack_start(self.comboType, False, False, 2) + table = gtk.Table(rows=len(self.fieldWidgets) +1, columns=2, homogeneous=False) + self.pack_start(table, False, False, 2) + for i,fieldWidget in enumerate(self.fieldWidgets): + fieldWidget['widget'].set_tooltip_text(fieldWidget['tooltip']) + + table.attach(fieldWidget['label'], 0, 1, i, i+1, xoptions=gtk.FILL) + table.attach(fieldWidget['widget'], 1, 2, i, i+1) + + # init widget + + # if a database has been passed user is not allowed to change database type + if database is None: + self.comboType.set_button_sensitivity(gtk.SENSITIVITY_ON) + else: + self.comboType.set_button_sensitivity(gtk.SENSITIVITY_OFF) + + # set current database + self.databaseManager = databaseManager + self.database= None + if database is None: + databaseType = self.databaseManager.get_default_database_type() + if databaseType is not None: + database = databaseType() + if database is not None: + self.set_database(database) + + def on_combo_type_changed(self, combo): + i = self.comboType.get_active() + if i > -1: + # change database if necessary + currentDatabaseType = self.comboType.get_model()[i][1] + if currentDatabaseType != self.database.Type: + newDatabase = self.databaseManager.DatabaseTypes[currentDatabaseType]() + self.set_database(newDatabase) + + def set_database(self, database): + self.database = database + + # adjust database type combo if necessary + i = self.comboType.get_active() + if i == -1: + currentDatabaseType = None + else: + currentDatabaseType = self.comboType.get_model()[i][1] + if currentDatabaseType != self.database.Type: + for i, row in enumerate(self.comboType.get_model()): + if row[1] == self.database.Type: + self.comboType.set_active(i) + break + else: + raise ValueError('unknown database type') + + # adjust field widgets to database + for fieldWidget in self.fieldWidgets: + isSensitive = fieldWidget['isSensitive'](self.database) + fieldWidget['widget'].set_sensitive(isSensitive) + fieldWidget['label'].set_sensitive(isSensitive) + fieldWidget['setter'](fieldWidget['widget'], self.database) + + def get_database(self): + for fieldWidget in self.fieldWidgets: + fieldWidget['getter'](fieldWidget['widget'], self.database) + return self.database + + +#TODO: just boilerplate code +class DialogDatabase(gtk.Dialog): + def __init__(self, databaseManager, parent=None): + gtk.Dialog.__init__(self, + title="My dialog", + parent=parent, + flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + buttons=( + gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, + gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, + )) + #self.set_size_request(260, 250) + + self.databaseManager = databaseManager + + #TODO: dono how to make word wrap work as expected + self.labelInfo = gtk.Label('database management') + self.labelInfo.set_line_wrap(True) + self.labelInfo.set_selectable(True) + self.labelInfo.set_single_line_mode(False) + self.labelInfo.set_alignment(0, 0) + + # database management buttons + + #TODO: bit messy the distinction New/Add/Edit. we'd have to pass three flags to DialogDatabaseProperties + # to handle this. maybe drop Edit (is just a Remove + Add), to keep things simple + self.buttonDatabaseNew = gtk.Button("New..") + self.buttonDatabaseNew.set_tooltip_text('creates a new database') + self.buttonDatabaseNew.connect('clicked', self.on_button_database_new_clicked) + self.buttonDatabaseAdd = gtk.Button("Add..") + self.buttonDatabaseAdd.set_tooltip_text('adds an existing database') + self.buttonDatabaseAdd.connect('clicked', self.on_button_database_add_clicked) + self.buttonDatabaseEdit = gtk.Button("Edit..") + self.buttonDatabaseEdit.set_tooltip_text('edit database settings') + #self.buttonDatabaseEdit.connect('clicked', self.on_button_database_edit_clicked) + self.buttonDatabaseRemove = gtk.Button("Remove") + self.buttonDatabaseRemove.set_tooltip_text('removes the database from the list') + + #TODO: i dont think we should do any real database management here. maybe drop it + self.buttonDatabaseDelete = gtk.Button("Delete") + self.buttonDatabaseDelete.set_tooltip_text('removes the database from the list and deletes it') + + # database tree + self.treeDatabases = gtk.TreeView() + store = gtk.ListStore(str, str, str) + self.treeDatabases.set_model(store) + columns = ('Name', 'Status', 'Type') + for i, column in enumerate(columns): + col = gtk.TreeViewColumn(column, gtk.CellRendererText(), text=i) + self.treeDatabases.append_column(col) + + # layout widgets + self.vbox.pack_start(self.labelInfo, False, False, 2) + self.vbox.pack_start(gtk.HSeparator(), False, False, 2) + + hbox = gtk.HBox() + self.vbox.add(hbox) + hbox.set_homogeneous(False) + vbox = gtk.VBox() + hbox.pack_start(vbox, False, False, 2) + vbox.pack_start(self.buttonDatabaseNew, False, False, 2) + vbox.pack_start(self.buttonDatabaseAdd, False, False, 2) + vbox.pack_start(self.buttonDatabaseEdit, False, False, 2) + vbox.pack_start(self.buttonDatabaseRemove, False, False, 2) + vbox.pack_start(self.buttonDatabaseDelete, False, False, 2) + box = gtk.VBox() + vbox.pack_start(box, True, True, 0) + + hbox.pack_start(gtk.VSeparator(), False, False, 2) + hbox.pack_end(self.treeDatabases, True, True, 2) + + self.show_all() + + + #TODO: for some reason i have to click OK/Cancel twice to close the dialog + def on_button_database_new_clicked(self, button): + dlg = DialogDatabaseProperties(self.databaseManager, parent=self) + if dlg.run() == gtk.RESPONSE_REJECT: + pass + if dlg.run() == gtk.RESPONSE_ACCEPT: + database = dlg.get_database() + self.treeDatabases.get_model().append( (database.name, 'foo', database.Type) ) + + dlg.destroy() + + def on_button_database_add_clicked(self, button): + dlg = DialogDatabaseProperties(self.databaseManager, parent=self) + if dlg.run() == gtk.RESPONSE_REJECT: + pass + if dlg.run() == gtk.RESPONSE_ACCEPT: + database = dlg.get_database() + self.treeDatabases.get_model().append( (database.name, 'foo', database.Type) ) + + dlg.destroy() + + #def on_button_database_edit_clicked(self, button): + # dlg = DialogDatabaseProperties(self.databaseManager, parent=self) + # if dlg.run() == gtk.RESPONSE_REJECT: + # pass + # if dlg.run() == gtk.RESPONSE_ACCEPT: + # database = dlg.get_database() + # self.treeDatabases.get_model().append( (database.name, 'foo', database.Type) ) + # + # dlg.destroy() + + +#************************************************************************************************** +if __name__ == '__main__': + #d = DialogDatabaseProperties( + # DatabaseManager(defaultDatabaseType=DatabaseTypeSqLite), + #database=DatabaseTypePostgres(), + # database=None, + # ) + d = DialogDatabase(DatabaseManager(defaultDatabaseType=DatabaseTypeSqLite)) + d.connect("destroy", gtk.main_quit) + d.run() + #gtk.main() + + From e732b749561c97f8f52d1ab5c1a199ff721af202 Mon Sep 17 00:00:00 2001 From: fpdb-mme Date: Tue, 27 Oct 2009 11:04:24 +0100 Subject: [PATCH 06/17] too many changes to remember Signed-off-by: fpdb-mme --- pyfpdb/DatabaseManager.py | 839 ++++++++++++++++++++------------------ 1 file changed, 452 insertions(+), 387 deletions(-) diff --git a/pyfpdb/DatabaseManager.py b/pyfpdb/DatabaseManager.py index 130035cb..a9269654 100644 --- a/pyfpdb/DatabaseManager.py +++ b/pyfpdb/DatabaseManager.py @@ -6,414 +6,479 @@ import gtk #******************************************************************************************************* class DatabaseManager(object): - DatabaseTypes = {} - - def __init__(self, defaultDatabaseType=None): - self._defaultDatabaseType = defaultDatabaseType - def set_default_database_type(self, databaseType): - self._defaultDatabaseType = defaultDatabaseType - def get_default_database_type(self): - return self._defaultDatabaseType - + DatabaseTypes = {} + + def __init__(self, defaultDatabaseType=None): + self._defaultDatabaseType = defaultDatabaseType + def set_default_database_type(self, databaseType): + self._defaultDatabaseType = defaultDatabaseType + def get_default_database_type(self): + return self._defaultDatabaseType + class DatabaseTypeMeta(type): - def __new__(klass, name, bases, kws): - newKlass = type.__new__(klass, name, bases, kws) - if newKlass.Type is not None: - if newKlass.Type in DatabaseManager.DatabaseTypes: - raise ValueError('data base type already registered for: %s' % newKlass.Type) - DatabaseManager.DatabaseTypes[newKlass.Type] = newKlass - return newKlass + def __new__(klass, name, bases, kws): + newKlass = type.__new__(klass, name, bases, kws) + if newKlass.Type is not None: + if newKlass.Type in DatabaseManager.DatabaseTypes: + raise ValueError('data base type already registered for: %s' % newKlass.Type) + DatabaseManager.DatabaseTypes[newKlass.Type] = newKlass + return newKlass class DatabaseTypeBase(object): - __metaclass__ = DatabaseTypeMeta - Type = None - - DBHasHost = 0x1 - DBHasFile = 0x2 - DBHasPort = 0x4 - DBHasUser = 0x8 - DBHasPassword = 0x10 - DBHasDatabase = 0x20 - DBHasName = 0x40 - - DBFlagsFileSystem = DBHasFile|DBHasName - DBFlagsServer = DBHasHost|DBHasPort|DBHasUser|DBHasPassword|DBHasDatabase|DBHasName - - def __init__(self, host='', file='', port=0, user='', password='', database='', table='', name=''): - self.host = host - self.file = file - self.port = port - self.user = user - self.password = password - self.database = database - self.name = name + __metaclass__ = DatabaseTypeMeta + Type = None class DatabaseTypePostgres(DatabaseTypeBase): - Type = 'postgres' - Flags = DatabaseTypeBase.DBFlagsServer - def __init__(self, host='localhost', file='', port=5432, user='postgres', password='', database='fpdb', name=''): - DatabaseTypeBase.__init__(self, host=host, file=file, port=port, user=user, password=password, database=database, name=name) + Type = 'postgres' + def __init__(self, host='localhost', port=5432, user='postgres', password='', database='fpdb', name=''): + self.host = host + self.port = port + self.user = user + self.password = password + self.database = database + self.name = name class DatabaseTypeMysql(DatabaseTypeBase): - Type = 'mysql' - Flags = DatabaseTypeBase.DBFlagsServer - def __init__(self, host='localhost', file='root', port=3306, user='', password='', database='fpdb', name=''): - DatabaseTypeBase.__init__(self, host=host, file=file, port=port, user=user, password=password, database=database, name=name) - + Type = 'mysql' + def __init__(self, host='localhost', port=3306, user='root', password='', database='fpdb', name=''): + self.host = host + self.port = port + self.user = user + self.password = password + self.database = database + self.name = name + class DatabaseTypeSqLite(DatabaseTypeBase): - Type = 'sqlite' - Flags = DatabaseTypeBase.DBFlagsFileSystem - def __init__(self, host='', file='', port=0, user='', password='',database='', name=''): - DatabaseTypeBase.__init__(self, host=host, file=file, port=port, user=user, password=password, database=database, name=name) - + Type = 'sqlite' + def __init__(self, host='', file='', name=''): + self.file = file + self.name = name + #*************************************************************************************************************************** class MyFileChooserButton(gtk.HBox): - #NOTE: for some weird reason it is impossible to let the user choose a non exiting filename with gtk.FileChooserButton, so impl our own on the fly - def __init__(self): - gtk.HBox.__init__(self) - self.set_homogeneous(False) - - self.entry = gtk.Entry() - self.button = gtk.Button('...') - self.button.connect('clicked', self.on_button_clicked) - - # layout widgets - self.pack_start(self.entry, True, True) - self.pack_start(self.button, False, False) - - def get_filename(self): - return self.entry.get_text() - - def set_filename(self, name): - self.entry.set_text(name) - - def on_button_clicked(self, button): - dlg = gtk.FileChooserDialog( - title='Choose an exiting database file or type in name of a new one', - parent=None, - action=gtk.FILE_CHOOSER_ACTION_SAVE, - buttons=( - gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, - gtk.STOCK_OK, gtk.RESPONSE_OK, - ), - backend=None - ) - dlg.connect('confirm-overwrite', self.on_dialog_confirm_overwrite) - dlg.set_default_response(gtk.RESPONSE_OK) - dlg.set_do_overwrite_confirmation(True) - if dlg.run() == gtk.RESPONSE_OK: - self.set_filename(dlg.get_filename()) - dlg.destroy() - - #TODO: when the user selects a sqLite database file we got three possible actions - # 1. user types in a new filename. easy one, create the file - # 2. user selectes a file with the intention to overwrite it - # 3. user selects a file with the intention to plug an existing database file in - #IDEA: impl open_existing as plug in, never overwrite, cos we can not guess - #PROBLEMS: how to validate an existing file is a database? - def on_dialog_confirm_overwrite(self, dlg): - print dlg.get_filename() - - gtk.FILE_CHOOSER_CONFIRMATION_CONFIRM - #The file chooser will present its stock dialog to confirm overwriting an existing file. + #NOTE: for some weird reason it is impossible to let the user choose a non exiting filename with gtk.FileChooserButton, so impl our own on the fly + def __init__(self): + gtk.HBox.__init__(self) + self.set_homogeneous(False) + + self.entry = gtk.Entry() + self.button = gtk.Button('...') + self.button.connect('clicked', self.on_button_clicked) + + # layout widgets + self.pack_start(self.entry, True, True) + self.pack_start(self.button, False, False) + + def get_filename(self): + return self.entry.get_text() + + def set_filename(self, name): + self.entry.set_text(name) + + def on_button_clicked(self, button): + dlg = gtk.FileChooserDialog( + title='Choose an exiting database file or type in name of a new one', + parent=None, + action=gtk.FILE_CHOOSER_ACTION_SAVE, + buttons=( + gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, + gtk.STOCK_OK, gtk.RESPONSE_OK, + ), + backend=None + ) + dlg.connect('confirm-overwrite', self.on_dialog_confirm_overwrite) + dlg.set_default_response(gtk.RESPONSE_OK) + dlg.set_do_overwrite_confirmation(True) + if dlg.run() == gtk.RESPONSE_OK: + self.set_filename(dlg.get_filename()) + dlg.destroy() + + #TODO: when the user selects a sqLite database file we got three possible actions + # 1. user types in a new filename. easy one, create the file + # 2. user selectes a file with the intention to overwrite it + # 3. user selects a file with the intention to plug an existing database file in + #IDEA: impl open_existing as plug in, never overwrite, cos we can not guess + #PROBLEMS: how to validate an existing file is a database? + def on_dialog_confirm_overwrite(self, dlg): + print dlg.get_filename() + + gtk.FILE_CHOOSER_CONFIRMATION_CONFIRM + #The file chooser will present its stock dialog to confirm overwriting an existing file. - gtk.FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME - #The file chooser will terminate and accept the user's choice of a file name. + gtk.FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME + #The file chooser will terminate and accept the user's choice of a file name. - gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN - # - - - + gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN + # + + +class WidgetDatabaseProperties(gtk.VBox): + ModeEdit = 0x1 + ModeAdd = 0x2 + ModeNew = 0x4 + + class FieldWidget(object): + def __init__(self, text='', attrDatabase='', widget=None, attrGet=None, attrSet=None, defaultValue=None, canEdit=False, tooltip=''): + """ + @param canEdit: True if the user can edit the attr in edit mode, False otherwise + """ + self._label = gtk.Label(text) + self._attrDatabase = attrDatabase + self._widget = widget + self._defaultValue = defaultValue + self._attrGetter=None, + self._attrGet = attrGet + self._attrSet = attrSet + self._canEdit = canEdit + + self._label.set_tooltip_text(tooltip) + self._widget.set_tooltip_text(tooltip) + + def widget(self): + return self._widget + def label(self): + return self._label + def is_sensitive(self, database): + return hasattr(database, self._attrDatabase) + def can_edit(self): + return self._canEdit + def set_sensitive(self, flag): + self._label.set_sensitive(flag) + self._widget.set_sensitive(flag) + def value_from_database(self, database): + getattr(self._widget, self._attrSet)( getattr(database, self._attrDatabase) ) + def value_to_database(self, database): + setattr(database, self._attrDatabase, getattr(self._widget, self._attrGet)() ) + def reset_value(self): + getattr(self._widget, self._attrSet)(self._defaultValue) + + def __init__(self, databaseManager, database=None, flags=ModeEdit): + gtk.VBox.__init__(self) + + self.flags = flags + self.fieldWidgets = ( #fieldName--> fieldHandler + self.FieldWidget( + text='Name:', + attrDatabase='name', + widget=gtk.Entry(), + defaultValue='', + attrGet='get_text', + attrSet='set_text', + canEdit=True, + tooltip='' + ), + self.FieldWidget( + text='File:', + attrDatabase='file', + widget=MyFileChooserButton(), + defaultValue='', + attrGet='get_filename', + attrSet='set_filename', + canEdit=False, + tooltip='' + ), + self.FieldWidget( + text='Host:', + attrDatabase='host', + widget=gtk.Entry(), + defaultValue='', + attrGet='get_text', + attrSet='set_text', + canEdit=False, + tooltip='' + ), + self.FieldWidget( + text='Port:', + attrDatabase='port', + widget=gtk.SpinButton(adjustment=gtk.Adjustment(value=0, lower=0, upper=999999, step_incr=1, page_incr=10) ), + defaultValue=0, + attrGet='get_value', + attrSet='set_value', + canEdit=False, + tooltip='' + ), + self.FieldWidget( + text='User:', + attrDatabase='user', + widget=gtk.Entry(), + defaultValue='', + attrGet='get_text', + attrSet='set_text', + canEdit=False, + tooltip='' + ), + self.FieldWidget( + text='Pwd:', + attrDatabase='password', + widget=gtk.Entry(), + defaultValue='', + attrGet='get_text', + attrSet='set_text', + canEdit=False, + tooltip='' + ), + self.FieldWidget( + text='Db:', + attrDatabase='database', + widget=gtk.Entry(), + defaultValue='', + attrGet='get_text', + attrSet='set_text', + canEdit=False, + tooltip='' + ), + ) + + # setup database type combo + self.comboType = gtk.ComboBox() + listStore= gtk.ListStore(str, str) + self.comboType.set_model(listStore) + cell = gtk.CellRendererText() + self.comboType.pack_start(cell, True) + self.comboType.add_attribute(cell, 'text', 0) + # fill out combo with database type. we store (displayName, databaseType) in our model for later lookup + for dbType, dbDisplayName in sorted([(klass.Type, klass.Type) for klass in databaseManager.DatabaseTypes.values()]): + listStore.append( (dbDisplayName, dbType) ) + self.comboType.connect('changed', self.on_combo_type_changed) + + # init and layout field widgets + self.pack_start(self.comboType, False, False, 2) + table = gtk.Table(rows=len(self.fieldWidgets) +1, columns=2, homogeneous=False) + self.pack_start(table, False, False, 2) + for i,fieldWidget in enumerate(self.fieldWidgets): + table.attach(fieldWidget.label(), 0, 1, i, i+1, xoptions=gtk.FILL) + table.attach(fieldWidget.widget(), 1, 2, i, i+1) + + # init widget + + # if a database has been passed user is not allowed to change database type + if database is None: + self.comboType.set_button_sensitivity(gtk.SENSITIVITY_ON) + else: + self.comboType.set_button_sensitivity(gtk.SENSITIVITY_OFF) + + # set current database + self.databaseManager = databaseManager + self.database= None + if database is None: + databaseType = self.databaseManager.get_default_database_type() + if databaseType is not None: + database = databaseType() + if database is not None: + self.set_database(database) + + def on_combo_type_changed(self, combo): + i = self.comboType.get_active() + if i > -1: + # change database if necessary + currentDatabaseType = self.comboType.get_model()[i][1] + if currentDatabaseType != self.database.Type: + newDatabase = self.databaseManager.DatabaseTypes[currentDatabaseType]() + self.set_database(newDatabase) + + def set_database(self, database): + self.database = database + + # adjust database type combo if necessary + i = self.comboType.get_active() + if i == -1: + currentDatabaseType = None + else: + currentDatabaseType = self.comboType.get_model()[i][1] + if currentDatabaseType != self.database.Type: + for i, row in enumerate(self.comboType.get_model()): + if row[1] == self.database.Type: + self.comboType.set_active(i) + break + else: + raise ValueError('unknown database type') + + # adjust field widgets to database + for fieldWidget in self.fieldWidgets: + isSensitive = fieldWidget.is_sensitive(self.database) + if isSensitive: + fieldWidget.value_from_database(self.database) + else: + fieldWidget.reset_value() + if self.flags & self.ModeEdit: + isSensitive = isSensitive and fieldWidget.can_edit() + fieldWidget.set_sensitive(isSensitive) + + def get_database(self): + for fieldWidget in self.fieldWidgets: + if fieldWidget.is_sensitive(self.database): + fieldWidget.value_to_database(self.database) + return self.database class DialogDatabaseProperties(gtk.Dialog): - def __init__(self, databaseManager, database=None,parent=None): - gtk.Dialog.__init__(self, - title="My dialog", - parent=parent, - flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - buttons=( - gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, - gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, - ) - ) - self.connect('response', self.on_dialog_response) - - # setup widget - self.widgetDatabaseProperties = WidgetDatabaseProperties(databaseManager,database=database) - self.vbox.pack_start(self.widgetDatabaseProperties, True, True) - self.widgetDatabaseProperties.show_all() + def __init__(self, databaseManager, database=None,parent=None, flags=WidgetDatabaseProperties.ModeEdit): + if flags & WidgetDatabaseProperties.ModeEdit: + title = '[Edit database] - database properties' + elif flags & WidgetDatabaseProperties.ModeAdd: + title = '[Add database] - database properties' + elif flags & WidgetDatabaseProperties.ModeNew: + title = '[New database] - database properties' + else: + title = 'database properties' + + gtk.Dialog.__init__(self, + title=title, + parent=parent, + flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + buttons=( + gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, + gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, + ) + ) + self.connect('response', self.on_dialog_response) + + # setup widget + self.flags = flags + self.widgetDatabaseProperties = WidgetDatabaseProperties(databaseManager,database=database, flags=self.flags) + self.vbox.pack_start(self.widgetDatabaseProperties, True, True) + self.widgetDatabaseProperties.show_all() - def get_database(self): - return self.widgetDatabaseProperties.get_database() - - def on_dialog_response(self, dlg, responseId): - if responseId == gtk.RESPONSE_REJECT: - pass - elif responseId == gtk.RESPONSE_ACCEPT: - pass - - - -class WidgetDatabaseProperties(gtk.VBox): - def __init__(self, databaseManager, database=None): - gtk.VBox.__init__(self) - - self.fieldWidgets = ( #fieldName--> fieldHandler - { - 'label': gtk.Label('Name:'), - 'widget': gtk.Entry(), - 'getter': lambda widget, database: setattr(database, 'name', widget.get_text() ), - 'setter': lambda widget, database: widget.set_text(database.name), - 'isSensitive': lambda database: bool(database.Flags & database.DBHasName), - 'tooltip': '', - }, - { - 'label': gtk.Label('File:'), - 'widget': MyFileChooserButton(), - 'getter': lambda widget, database: lambda widget, database: setattr(database, 'file', widget.get_filename() ), - 'setter': lambda widget, database: widget.set_filename(database.file), - 'isSensitive': lambda database: bool(database.Flags & database.DBHasFile), - 'tooltip': '', - }, - { - 'label': gtk.Label('Host:'), - 'widget': gtk.Entry(), - 'getter': lambda widget, database: setattr(database, 'host', widget.get_text() ), - 'setter': lambda widget, database: widget.set_text(database.host), - 'isSensitive': lambda database: bool(database.Flags & database.DBHasHost), - 'tooltip': '', - }, - { - 'label': gtk.Label('Port:'), - 'widget': gtk.SpinButton(adjustment=gtk.Adjustment(value=0, lower=0, upper=999999, step_incr=1, page_incr=10) ), - 'getter': lambda widget, database: setattr(database, 'port', widget.get_value() ), - 'setter': lambda widget, database: widget.set_value(database.port), - 'isSensitive': lambda database: bool(database.Flags & database.DBHasPort), - 'tooltip': '', - }, - { - 'label': gtk.Label('User:'), - 'widget': gtk.Entry(), - 'getter': lambda widget, database: setattr(database, 'user', widget.get_text() ), - 'setter': lambda widget, database: widget.set_text(database.user), - 'isSensitive': lambda database: bool(database.Flags & database.DBHasUser), - 'tooltip': '', - }, - { - 'label': gtk.Label('Pwd:'), - 'widget': gtk.Entry(), - 'getter': lambda widget, database: setattr(database, 'password', widget.get_text() ), - 'setter': lambda widget, database: widget.set_text(database.password), - 'isSensitive': lambda database: bool(database.Flags & database.DBHasPassword), - 'tooltip': '', - }, - { - 'label': gtk.Label('DB:'), - 'widget': gtk.Entry(), - 'getter': lambda widget, database: setattr(database, 'database', widget.get_text() ), - 'setter': lambda widget, database: widget.set_text(database.database), - 'isSensitive': lambda database: bool(database.Flags & database.DBHasDatabase), - 'tooltip': 'enter name of the database to create', - }, - ) - - # setup database type combo - self.comboType = gtk.ComboBox() - listStore= gtk.ListStore(str, str) - self.comboType.set_model(listStore) - cell = gtk.CellRendererText() - self.comboType.pack_start(cell, True) - self.comboType.add_attribute(cell, 'text', 0) - # fill out combo with database type. we store (displayName, databaseType) in our model for later lookup - for dbType, dbDisplayName in sorted([(klass.Type, klass.Type) for klass in databaseManager.DatabaseTypes.values()]): - listStore.append( (dbDisplayName, dbType) ) - self.comboType.connect('changed', self.on_combo_type_changed) - - # init and layout field widgets - self.pack_start(self.comboType, False, False, 2) - table = gtk.Table(rows=len(self.fieldWidgets) +1, columns=2, homogeneous=False) - self.pack_start(table, False, False, 2) - for i,fieldWidget in enumerate(self.fieldWidgets): - fieldWidget['widget'].set_tooltip_text(fieldWidget['tooltip']) - - table.attach(fieldWidget['label'], 0, 1, i, i+1, xoptions=gtk.FILL) - table.attach(fieldWidget['widget'], 1, 2, i, i+1) - - # init widget - - # if a database has been passed user is not allowed to change database type - if database is None: - self.comboType.set_button_sensitivity(gtk.SENSITIVITY_ON) - else: - self.comboType.set_button_sensitivity(gtk.SENSITIVITY_OFF) - - # set current database - self.databaseManager = databaseManager - self.database= None - if database is None: - databaseType = self.databaseManager.get_default_database_type() - if databaseType is not None: - database = databaseType() - if database is not None: - self.set_database(database) - - def on_combo_type_changed(self, combo): - i = self.comboType.get_active() - if i > -1: - # change database if necessary - currentDatabaseType = self.comboType.get_model()[i][1] - if currentDatabaseType != self.database.Type: - newDatabase = self.databaseManager.DatabaseTypes[currentDatabaseType]() - self.set_database(newDatabase) - - def set_database(self, database): - self.database = database - - # adjust database type combo if necessary - i = self.comboType.get_active() - if i == -1: - currentDatabaseType = None - else: - currentDatabaseType = self.comboType.get_model()[i][1] - if currentDatabaseType != self.database.Type: - for i, row in enumerate(self.comboType.get_model()): - if row[1] == self.database.Type: - self.comboType.set_active(i) - break - else: - raise ValueError('unknown database type') - - # adjust field widgets to database - for fieldWidget in self.fieldWidgets: - isSensitive = fieldWidget['isSensitive'](self.database) - fieldWidget['widget'].set_sensitive(isSensitive) - fieldWidget['label'].set_sensitive(isSensitive) - fieldWidget['setter'](fieldWidget['widget'], self.database) - - def get_database(self): - for fieldWidget in self.fieldWidgets: - fieldWidget['getter'](fieldWidget['widget'], self.database) - return self.database - - + def get_database(self): + return self.widgetDatabaseProperties.get_database() + + def on_dialog_response(self, dlg, responseId): + if responseId == gtk.RESPONSE_REJECT: + pass + elif responseId == gtk.RESPONSE_ACCEPT: + pass + + #TODO: just boilerplate code class DialogDatabase(gtk.Dialog): - def __init__(self, databaseManager, parent=None): - gtk.Dialog.__init__(self, - title="My dialog", - parent=parent, - flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - buttons=( - gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, - gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, - )) - #self.set_size_request(260, 250) - - self.databaseManager = databaseManager - - #TODO: dono how to make word wrap work as expected - self.labelInfo = gtk.Label('database management') - self.labelInfo.set_line_wrap(True) - self.labelInfo.set_selectable(True) - self.labelInfo.set_single_line_mode(False) - self.labelInfo.set_alignment(0, 0) - - # database management buttons - - #TODO: bit messy the distinction New/Add/Edit. we'd have to pass three flags to DialogDatabaseProperties - # to handle this. maybe drop Edit (is just a Remove + Add), to keep things simple - self.buttonDatabaseNew = gtk.Button("New..") - self.buttonDatabaseNew.set_tooltip_text('creates a new database') - self.buttonDatabaseNew.connect('clicked', self.on_button_database_new_clicked) - self.buttonDatabaseAdd = gtk.Button("Add..") - self.buttonDatabaseAdd.set_tooltip_text('adds an existing database') - self.buttonDatabaseAdd.connect('clicked', self.on_button_database_add_clicked) - self.buttonDatabaseEdit = gtk.Button("Edit..") - self.buttonDatabaseEdit.set_tooltip_text('edit database settings') - #self.buttonDatabaseEdit.connect('clicked', self.on_button_database_edit_clicked) - self.buttonDatabaseRemove = gtk.Button("Remove") - self.buttonDatabaseRemove.set_tooltip_text('removes the database from the list') - - #TODO: i dont think we should do any real database management here. maybe drop it - self.buttonDatabaseDelete = gtk.Button("Delete") - self.buttonDatabaseDelete.set_tooltip_text('removes the database from the list and deletes it') - - # database tree - self.treeDatabases = gtk.TreeView() - store = gtk.ListStore(str, str, str) - self.treeDatabases.set_model(store) - columns = ('Name', 'Status', 'Type') - for i, column in enumerate(columns): - col = gtk.TreeViewColumn(column, gtk.CellRendererText(), text=i) - self.treeDatabases.append_column(col) - - # layout widgets - self.vbox.pack_start(self.labelInfo, False, False, 2) - self.vbox.pack_start(gtk.HSeparator(), False, False, 2) - - hbox = gtk.HBox() - self.vbox.add(hbox) - hbox.set_homogeneous(False) - vbox = gtk.VBox() - hbox.pack_start(vbox, False, False, 2) - vbox.pack_start(self.buttonDatabaseNew, False, False, 2) - vbox.pack_start(self.buttonDatabaseAdd, False, False, 2) - vbox.pack_start(self.buttonDatabaseEdit, False, False, 2) - vbox.pack_start(self.buttonDatabaseRemove, False, False, 2) - vbox.pack_start(self.buttonDatabaseDelete, False, False, 2) - box = gtk.VBox() - vbox.pack_start(box, True, True, 0) - - hbox.pack_start(gtk.VSeparator(), False, False, 2) - hbox.pack_end(self.treeDatabases, True, True, 2) - - self.show_all() - - - #TODO: for some reason i have to click OK/Cancel twice to close the dialog - def on_button_database_new_clicked(self, button): - dlg = DialogDatabaseProperties(self.databaseManager, parent=self) - if dlg.run() == gtk.RESPONSE_REJECT: - pass - if dlg.run() == gtk.RESPONSE_ACCEPT: - database = dlg.get_database() - self.treeDatabases.get_model().append( (database.name, 'foo', database.Type) ) - - dlg.destroy() + def __init__(self, databaseManager, parent=None): + gtk.Dialog.__init__(self, + title="My dialog", + parent=parent, + flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + buttons=( + gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, + gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, + )) + #self.set_size_request(260, 250) + + self.databaseManager = databaseManager + + #TODO: dono how to make word wrap work as expected + self.labelInfo = gtk.Label('database management') + self.labelInfo.set_line_wrap(True) + self.labelInfo.set_selectable(True) + self.labelInfo.set_single_line_mode(False) + self.labelInfo.set_alignment(0, 0) + + # database management buttons + + #TODO: bit messy the distinction New/Add/Edit. we'd have to pass three flags to DialogDatabaseProperties + # to handle this. maybe drop Edit (is just a Remove + Add), to keep things simple + self.buttonDatabaseNew = gtk.Button("New..") + self.buttonDatabaseNew.set_tooltip_text('creates a new database') + self.buttonDatabaseNew.connect('clicked', self.on_button_database_new_clicked) + self.buttonDatabaseAdd = gtk.Button("Add..") + self.buttonDatabaseAdd.set_tooltip_text('adds an existing database') + self.buttonDatabaseAdd.connect('clicked', self.on_button_database_add_clicked) + self.buttonDatabaseEdit = gtk.Button("Edit..") + self.buttonDatabaseEdit.set_tooltip_text('edit database settings') + self.buttonDatabaseEdit.connect('clicked', self.on_button_database_edit_clicked) + self.buttonDatabaseEdit.set_sensitive(False) + self.buttonDatabaseRemove = gtk.Button("Remove") + self.buttonDatabaseRemove.set_tooltip_text('removes the database from the list') + self.buttonDatabaseRemove.set_sensitive(False) + + #TODO: i dont think we should do any real database management here. maybe drop it + self.buttonDatabaseDelete = gtk.Button("Delete") + self.buttonDatabaseDelete.set_tooltip_text('removes the database from the list and deletes it') + self.buttonDatabaseDelete.set_sensitive(False) + + # database tree + self.treeDatabases = gtk.TreeView() + store = gtk.ListStore(str, str, str) + self.treeDatabases.set_model(store) + columns = ('Name', 'Status', 'Type') + for i, column in enumerate(columns): + col = gtk.TreeViewColumn(column, gtk.CellRendererText(), text=i) + self.treeDatabases.append_column(col) + self.treeDatabases.get_selection().connect('changed', self.on_tree_databases_selection_changed) + + # layout widgets + self.vbox.pack_start(self.labelInfo, False, False, 2) + self.vbox.pack_start(gtk.HSeparator(), False, False, 2) + + hbox = gtk.HBox() + self.vbox.add(hbox) + hbox.set_homogeneous(False) + vbox = gtk.VBox() + hbox.pack_start(vbox, False, False, 2) + vbox.pack_start(self.buttonDatabaseNew, False, False, 2) + vbox.pack_start(self.buttonDatabaseAdd, False, False, 2) + vbox.pack_start(self.buttonDatabaseEdit, False, False, 2) + vbox.pack_start(self.buttonDatabaseRemove, False, False, 2) + vbox.pack_start(self.buttonDatabaseDelete, False, False, 2) + box = gtk.VBox() + vbox.pack_start(box, True, True, 0) + + hbox.pack_start(gtk.VSeparator(), False, False, 2) + hbox.pack_end(self.treeDatabases, True, True, 2) + + self.show_all() + + + #TODO: for some reason i have to click OK/Cancel twice to close the dialog + def on_button_database_new_clicked(self, button): + dlg = DialogDatabaseProperties(self.databaseManager, parent=self, flags=WidgetDatabaseProperties.ModeNew) + if dlg.run() == gtk.RESPONSE_REJECT: + pass + if dlg.run() == gtk.RESPONSE_ACCEPT: + database = dlg.get_database() + self.treeDatabases.get_model().append( (database.name, 'foo', database.Type) ) + dlg.destroy() - def on_button_database_add_clicked(self, button): - dlg = DialogDatabaseProperties(self.databaseManager, parent=self) - if dlg.run() == gtk.RESPONSE_REJECT: - pass - if dlg.run() == gtk.RESPONSE_ACCEPT: - database = dlg.get_database() - self.treeDatabases.get_model().append( (database.name, 'foo', database.Type) ) - - dlg.destroy() - - #def on_button_database_edit_clicked(self, button): - # dlg = DialogDatabaseProperties(self.databaseManager, parent=self) - # if dlg.run() == gtk.RESPONSE_REJECT: - # pass - # if dlg.run() == gtk.RESPONSE_ACCEPT: - # database = dlg.get_database() - # self.treeDatabases.get_model().append( (database.name, 'foo', database.Type) ) - # - # dlg.destroy() - + def on_button_database_add_clicked(self, button): + dlg = DialogDatabaseProperties(self.databaseManager, parent=self, flags=WidgetDatabaseProperties.ModeAdd) + if dlg.run() == gtk.RESPONSE_REJECT: + pass + if dlg.run() == gtk.RESPONSE_ACCEPT: + database = dlg.get_database() + self.treeDatabases.get_model().append( (database.name, 'foo', database.Type) ) + dlg.destroy() + + def on_button_database_edit_clicked(self, button): + dlg = DialogDatabaseProperties(self.databaseManager, parent=self, flags=WidgetDatabaseProperties.ModeEdit) + if dlg.run() == gtk.RESPONSE_REJECT: + pass + if dlg.run() == gtk.RESPONSE_ACCEPT: + database = dlg.get_database() + selection = self.treeDatabases.get_selection() + if selection is not None: + model, iter = selection.get_selected() + model.set_value(iter, 0, database.name) + dlg.destroy() + + def on_tree_databases_selection_changed(self, treeSelection): + hasSelection = bool(treeSelection.count_selected_rows()) + + # enable/disable selection dependend widgets + self.buttonDatabaseEdit.set_sensitive(hasSelection) + self.buttonDatabaseRemove.set_sensitive(hasSelection) + self.buttonDatabaseDelete.set_sensitive(hasSelection) + + + #************************************************************************************************** if __name__ == '__main__': - #d = DialogDatabaseProperties( - # DatabaseManager(defaultDatabaseType=DatabaseTypeSqLite), - #database=DatabaseTypePostgres(), - # database=None, - # ) - d = DialogDatabase(DatabaseManager(defaultDatabaseType=DatabaseTypeSqLite)) - d.connect("destroy", gtk.main_quit) - d.run() - #gtk.main() + #d = DialogDatabaseProperties( + # DatabaseManager(defaultDatabaseType=DatabaseTypeSqLite), + #database=DatabaseTypePostgres(), + # database=None, + # ) + d = DialogDatabase(DatabaseManager(defaultDatabaseType=DatabaseTypeSqLite)) + d.connect("destroy", gtk.main_quit) + d.run() + #gtk.main() From e25e4cf9b3c92113396bfff39703b43f522140c3 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Tue, 27 Oct 2009 15:24:49 +0200 Subject: [PATCH 07/17] Use custom tray icon instead of GTK stock Use the "Anonymous Cards" image from openclipart.org (http://openclipart.org/media/files/Anonymous/12230) This same image is used in other new FPDB icons, but the combinations with looking-glass don't look good in systray. This icon is plain enough to work properly even when scaled really small. --- gfx/fpdb-cards.png | Bin 0 -> 3711 bytes pyfpdb/fpdb.py | 7 ++++++- setup.py | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 gfx/fpdb-cards.png diff --git a/gfx/fpdb-cards.png b/gfx/fpdb-cards.png new file mode 100644 index 0000000000000000000000000000000000000000..4d14be623637d200a58c0011f9076bc7c843a3df GIT binary patch literal 3711 zcmV-_4uJ8AP)Px#24YJ`L;wH)0002_L%V+f000SaNLh0L01ejw01ejxLMWSf00007bV*G`2iXc6 z4HXXTn0Gh;01f|1L_t(&-tAg@a1~{iKi}7{8*XkMgb2wP3fA2PlHoA{0e31w$S|x7 z4+|2BFeU{7AC*CCiyAbpRaBW;;!I)5Lzrmegf`};vLqt4x?p$oaf(5?sRc`{sI zUr%0na!F8C|n~ zzv~|+O;UC&Ui|gbU0r7~GBdsZ77G2{yR&9Z+49aiGbB-z%soWY^>Kg&lHJeU{@FnCgMCkq5o0Nt^3=ZH}` zIhMO92#_H#rY!I2@vgXjy$=+{iHHtFRDh7o3SIZixOnk|_wL=G$sb8603r^s0wDxe zu3Q;#yWNV#wEbX=35+oskH?Mn_IB^_yf zDU}dW0RRUf#DRzk0w}DjD~hpLa7YUz)3Ia6LWd6@&M;kvA|m0O3yd+r5&{t^=bSnm z4*tRmFWdzHZqv{p3uI+yqnk0XX_8nBn_71eLL7t;#k{TnfJ4sBbpfT0RIZ**0KRqW zRza3!${0&%+;k<9Yz5A_;P?AuhYlSI+T3uE1&Z?Wl+%TUVcCYfk1%YxN$>7#Dtd3LogYqb}KN(sG=yerKKe_R15$BJpB6W;@5tE z%$7+AA;eQC)KYthJ& zBZb1kLe3ZybzP^iSWL*z&*x*tjG>G%s_QzPI(4c;mSv)eMUWbabf6d&c8&{@QD=ZLIJKlC z!-6M6Ayqoei?F%5Il6S|((HTp?paxhFvh517-Z+po&2e%o(iOS2^kI|BO;C&JzB|s z@4aYuD8#@pgd;!yIW7I!&s;+&By}ZLRdrD2@caGv%rnnWGZ4jiJT63|Q9)HzK~+_9 z@7_Ip>#es~b8~a5Dv93*=OZHic*2A%-|E$|eFX*K$2V`zO1B;ni696(B^teP;|Blk zyYJcz!i9}MXA3LRDWyKYA9`-zwmf5i0yyW<9Xoa$I&$R5 zf2Pz@ad9zRzI+(~z#&Oerqk&Ji{W87)?*Ska0GNp|xp|J}!ED!figmoCq$G0b(j{i@HyQv&4Uqv!3SN@$hXLT~ zprV`n(87fa-`l!%>yo^@Ji{;y!Gh3?F)$1xq-mOJ;vc~<4ABO$t-k~jVa}X6zW{*4 z001jitax_&_U$il&J(=?r4(&$!p+f()s}L53{y(E*Xva_ZrphMz<~ox%@NCBTE2XF zt)^)P4ndbYIwAqDH;%(lPEJl`x=L&(kH-_=y?eLkrI%j1yLt2GzRy4ZJZyD$$rO*r zvAVjtF0BO~e)!=j03f8RfEqyj-16l&OB)+U{-mm^V5myT7#p&XAtJ_NF?j5;$L@A_ zcgF-l;Diu%=FFLJWo2d7nl)=iC9g+BGHceX$@}*08{E{JenwMOKtx0afCrv_+Fh5G z6%M@lX5XEjp2$!`E_-%LwT2KvPMtdCZ*6TwL=?O}= z+Xx}#!UrGt7kv4p28_vbRW;bPZQJeJy}kNySjB|r*{QB~c6Nd+%V6~tR>S1;`H08k ziKW3*m=+5WLU<}oUA=fQN`L)pk|ziv5F%qFB9;H~kKbK>^UYgoBx0n~vPI^YGSMvg zd_Eow2C=-nT+%d6w0C%%bDEQr!*g?UGXjAC3xz_=Fbtk9YYq?~91e#p2`z>$eDaAJ zU%674o|~&6fQ*PTAw+_3nEv$e;j1T~f4=>{+-^3EI}reYo}M1#(MKPR+<*W5q4DF# z`x_b>^z7_xuy=TdVTfbKjLE31to-wg88f;TE?jv1?YH0ds;bJq4;p0_4}=i%@gM%+ z8MR@9QXT^34%a;KA*m8*RH!~&z=pY@>ZGxUGeVo=VK>o zYC>hd`AyzfheJRBgnkewA)*WbG9g4lL=h3CC0=jO=kw+r`1aVbn~_Llkhu{N7-RV1 zhaaMCqG1ilkW$Lcx+2K3tee=!8sCs;G#cN(e}706#i0ctgn%Fj^xOv@sHIx+-{yYV*ciAf%V?{f#GK(sCNPuDb1`Tb%IkP51U>HUs10f`(%r`9|_cRaH*LzJ2}ixgOZt(;Gv96We1P+D3V znmv2Ax_9qhwWX!Sm6esnO=f9DMTOGY*_pdx!vK2@bo z6irP{FGZu#K~n||k)3$_@vt#%8t0{@45v@`_m-60KKK6nzI0QJxpU{%Tl>EF;*0-o zea%Ux=H_OZQ9NfBfi`3WGYd3G+4A|?D z^^PHfI29k(gZU#zD*Bc!>Yu}5ernPrR=sg!{tzjDfKdG%<^Z6_A9>`F$iagLbIc-^ zNSV5>)7IA3cvV%Earf?BEm=9{SUznf4w&BwQqDF1Y1XXlZ=QWNSh#+@kfxnXJR6I} z4BKdt+3$6BcCw>KkB%^}QO1~{>$(t)M#XSAEcW*HifwIe!n^OjYb0AT7K??_YE2Zy z*}c8JLHrx(+O=yz0GML!>vTGohr?mR?$41(Bo>K8qSg@(hhxcyuImH(4!7H_&6zW2 zQQCgMFpTq+m6dDPty}k0Mn*=K-4c{iDoK)y5F%M)ahnAaqjFVMeOtC{c@+TuR9ILD zckbMQNF;Kgs;X*zZEfvZhr^Nkec~nwAd*-t<~@4!=-a1HpZ<8*o>-D3I&tDe!Z`;* z2p}RTiULJNMIg&Eu~4=(VzHf*&dyHk=;%lZyo!s9h2rAkVHqD(tSE){_IB* Date: Tue, 27 Oct 2009 15:28:11 +0200 Subject: [PATCH 08/17] Typofix in status icon load --- pyfpdb/fpdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py index c3d7e399..5370bccb 100755 --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -628,7 +628,7 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt") self.statusIcon = gtk.StatusIcon() if os.path.exists('../gfx/fpdb-cards.png'): self.statusIcon.set_from_file('../gfx/fpdb-cards.png') - elif os.path.exits('/usr/share/pixmaps/fpdb-cards.png'): + elif os.path.exists('/usr/share/pixmaps/fpdb-cards.png'): self.statusIcon.set_from_file('/usr/share/pixmaps/fpdb-cards.png') else: self.statusIcon.set_from_stock(gtk.STOCK_HOME) From 9886b618404429a387dfb5dbb355033464568cd9 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Tue, 27 Oct 2009 19:45:52 +0200 Subject: [PATCH 09/17] Tray icon click toggles window visibility Many of the tools that place an icon in systray allow to toggle the application visibility by left-clicking on the icon. Why not follow suit? --- pyfpdb/fpdb.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pyfpdb/fpdb.py b/pyfpdb/fpdb.py index 5370bccb..0b3c4f4a 100755 --- a/pyfpdb/fpdb.py +++ b/pyfpdb/fpdb.py @@ -678,8 +678,14 @@ This program is licensed under the AGPL3, see docs"""+os.sep+"agpl-3.0.txt") pass def statusicon_activate(self, widget, data = None): - self.window.show() - self.window.present() + # Let's allow the tray icon to toggle window visibility, the way + # most other apps work + shown = self.window.get_property('visible') + if shown: + self.window.hide() + else: + self.window.show() + self.window.present() def warning_box(self, str, diatitle="FPDB WARNING"): diaWarning = gtk.Dialog(title=diatitle, parent=None, flags=0, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK)) From 626ff49eb41e5bb28d8db33277f3fe726d90aef5 Mon Sep 17 00:00:00 2001 From: Worros Date: Thu, 29 Oct 2009 13:17:17 +0800 Subject: [PATCH 10/17] Print out actual exception when import fails --- pyfpdb/GuiGraphViewer.py | 3 ++- pyfpdb/GuiSessionViewer.py | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index 4011ba00..739a8f18 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -32,11 +32,12 @@ try: from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar from numpy import arange, cumsum from pylab import * -except ImportError: +except ImportError as inst: print """Failed to load libs for graphing, graphing will not function. Please in stall numpy and matplotlib if you want to use graphs.""" print """This is of no consequence for other parts of the program, e.g. import and HUD are NOT affected by this problem.""" + print "ImportError: %s" % inst.args import fpdb_import import Database diff --git a/pyfpdb/GuiSessionViewer.py b/pyfpdb/GuiSessionViewer.py index 7695cf73..5711cb89 100755 --- a/pyfpdb/GuiSessionViewer.py +++ b/pyfpdb/GuiSessionViewer.py @@ -37,11 +37,10 @@ try: # from matplotlib.dates import DateFormatter, WeekdayLocator, HourLocator, \ # DayLocator, MONDAY, timezone -except: - err = traceback.extract_tb(sys.exc_info()[2])[-1] - print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) +except ImportError as inst: print """Failed to load numpy in Session Viewer""" - print """This is of no consequence as the module currently doesn't do anything.""" + print """This is of no consequence as the page is broken and only of interest to developers.""" + print "ImportError: %s" % inst.args import Card import fpdb_import From a872769b2b44b1a2c2079d8e8627858efe25647a Mon Sep 17 00:00:00 2001 From: Worros Date: Thu, 29 Oct 2009 16:37:33 +0800 Subject: [PATCH 11/17] Fix cli to Everleaf --- pyfpdb/EverleafToFpdb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyfpdb/EverleafToFpdb.py b/pyfpdb/EverleafToFpdb.py index 2835b6ff..fcd76c25 100755 --- a/pyfpdb/EverleafToFpdb.py +++ b/pyfpdb/EverleafToFpdb.py @@ -304,5 +304,5 @@ if __name__ == "__main__": LOG_FILENAME = './logging.out' logging.basicConfig(filename=LOG_FILENAME,level=options.verbosity) - e = Everleaf(in_path = options.ipath, out_path = options.opath, follow = options.follow, autostart=True, debugging=True) + e = Everleaf(in_path = options.ipath, out_path = options.opath, follow = options.follow, autostart=True) From 2a878d0a83c711a91d3b961219f9beb953a544ea Mon Sep 17 00:00:00 2001 From: fpdb-mme Date: Thu, 29 Oct 2009 19:32:41 +0100 Subject: [PATCH 12/17] many changes Signed-off-by: fpdb-mme --- pyfpdb/DatabaseManager.py | 91 +++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 18 deletions(-) diff --git a/pyfpdb/DatabaseManager.py b/pyfpdb/DatabaseManager.py index a9269654..3c176634 100644 --- a/pyfpdb/DatabaseManager.py +++ b/pyfpdb/DatabaseManager.py @@ -8,13 +8,40 @@ import gtk class DatabaseManager(object): DatabaseTypes = {} - def __init__(self, defaultDatabaseType=None): + @classmethod + def from_fpdb(klass, data, defaultDatabaseType=None): + #TODO: parse whatever data is + databases = ( + DatabaseTypePostgres(name='myDb'), + DatabaseTypeSqLite(name='myDb2'), + + ) + return klass(databases=databases, defaultDatabaseType=defaultDatabaseType) + + + def __init__(self, databases=None, defaultDatabaseType=None): self._defaultDatabaseType = defaultDatabaseType + self._databases = [] if databases is None else list(databases) + def __iter__(self): + return iter(self._databases) def set_default_database_type(self, databaseType): self._defaultDatabaseType = defaultDatabaseType def get_default_database_type(self): return self._defaultDatabaseType - + def database_from_id(self, idDatabase): + for database in self._databases: + if idDatabase == id(database): + return database + def database_id(self, database): + return id(database) + def create_database(self, databaseType, **kws): + databaseKlass = self.DatabaseTypes[databaseType] + database = databaseType(**kws) + self._databases.append(database) + return database + def remove_database(self, database): + self._databases.remove(database) + class DatabaseTypeMeta(type): def __new__(klass, name, bases, kws): newKlass = type.__new__(klass, name, bases, kws) @@ -30,29 +57,29 @@ class DatabaseTypeBase(object): class DatabaseTypePostgres(DatabaseTypeBase): Type = 'postgres' - def __init__(self, host='localhost', port=5432, user='postgres', password='', database='fpdb', name=''): + def __init__(self, name='', host='localhost', port=5432, user='postgres', password='', database='fpdb'): + self.name = name self.host = host self.port = port self.user = user self.password = password self.database = database - self.name = name - + class DatabaseTypeMysql(DatabaseTypeBase): Type = 'mysql' - def __init__(self, host='localhost', port=3306, user='root', password='', database='fpdb', name=''): + def __init__(self, name='', host='localhost', port=3306, user='root', password='', database='fpdb'): + self.name = name self.host = host self.port = port self.user = user self.password = password self.database = database - self.name = name class DatabaseTypeSqLite(DatabaseTypeBase): Type = 'sqlite' - def __init__(self, host='', file='', name=''): - self.file = file + def __init__(self, name='', host='', file=''): self.name = name + self.file = file #*************************************************************************************************************************** class MyFileChooserButton(gtk.HBox): @@ -111,7 +138,7 @@ class MyFileChooserButton(gtk.HBox): gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN # - +#TODO: rewrite to use idDatabase + create and remove databases through DatabaseManager class WidgetDatabaseProperties(gtk.VBox): ModeEdit = 0x1 ModeAdd = 0x2 @@ -156,7 +183,7 @@ class WidgetDatabaseProperties(gtk.VBox): gtk.VBox.__init__(self) self.flags = flags - self.fieldWidgets = ( #fieldName--> fieldHandler + self.fieldWidgets = ( self.FieldWidget( text='Name:', attrDatabase='name', @@ -394,14 +421,27 @@ class DialogDatabase(gtk.Dialog): self.buttonDatabaseDelete.set_tooltip_text('removes the database from the list and deletes it') self.buttonDatabaseDelete.set_sensitive(False) - # database tree + # init database tree self.treeDatabases = gtk.TreeView() - store = gtk.ListStore(str, str, str) + self.treeDatabaseColumns = ( #NOTE: column names starting with '_' will be hidden + 'Name', + 'Status', + 'Type', + '_id', + ) + + store = gtk.ListStore(str, str, str, int) self.treeDatabases.set_model(store) - columns = ('Name', 'Status', 'Type') + columns = ('Name', 'Status', 'Type', '_id') for i, column in enumerate(columns): col = gtk.TreeViewColumn(column, gtk.CellRendererText(), text=i) self.treeDatabases.append_column(col) + if column.startswith('_'): + col.set_visible(False) + + self.treeDatabaseColumns = dict([(name, i) for (i, name) in enumerate(self.treeDatabaseColumns)]) + + self.treeDatabases.get_selection().connect('changed', self.on_tree_databases_selection_changed) # layout widgets @@ -425,6 +465,11 @@ class DialogDatabase(gtk.Dialog): hbox.pack_end(self.treeDatabases, True, True, 2) self.show_all() + + # init widget + for database in self.databaseManager: + self.treeDatabases.get_model().append( (database.name, 'foo', database.Type, self.databaseManager.database_id(database)) ) + #TODO: for some reason i have to click OK/Cancel twice to close the dialog @@ -434,7 +479,7 @@ class DialogDatabase(gtk.Dialog): pass if dlg.run() == gtk.RESPONSE_ACCEPT: database = dlg.get_database() - self.treeDatabases.get_model().append( (database.name, 'foo', database.Type) ) + self.treeDatabases.get_model().append( (database.name, 'foo', database.Type, self.databaseManager.database_id(database)) ) dlg.destroy() def on_button_database_add_clicked(self, button): @@ -443,11 +488,19 @@ class DialogDatabase(gtk.Dialog): pass if dlg.run() == gtk.RESPONSE_ACCEPT: database = dlg.get_database() - self.treeDatabases.get_model().append( (database.name, 'foo', database.Type) ) + print self.treeDatabases.get_model().append( (database.name, 'foo', database.Type, self.databaseManager.database_id(database)) ) dlg.destroy() def on_button_database_edit_clicked(self, button): - dlg = DialogDatabaseProperties(self.databaseManager, parent=self, flags=WidgetDatabaseProperties.ModeEdit) + selection = self.treeDatabases.get_selection() + if selection is None: + return + + model, iter = selection.get_selected() + idDatabase = model.get_value(iter, self.treeDatabaseColumns['_id']) + database = self.databaseManager.database_from_id(idDatabase) + + dlg = DialogDatabaseProperties(self.databaseManager, database=database, parent=self, flags=WidgetDatabaseProperties.ModeEdit) if dlg.run() == gtk.RESPONSE_REJECT: pass if dlg.run() == gtk.RESPONSE_ACCEPT: @@ -471,12 +524,14 @@ class DialogDatabase(gtk.Dialog): #************************************************************************************************** if __name__ == '__main__': + databaseManager = DatabaseManager.from_fpdb('', defaultDatabaseType=DatabaseTypeSqLite) + #d = DialogDatabaseProperties( # DatabaseManager(defaultDatabaseType=DatabaseTypeSqLite), #database=DatabaseTypePostgres(), # database=None, # ) - d = DialogDatabase(DatabaseManager(defaultDatabaseType=DatabaseTypeSqLite)) + d = DialogDatabase(databaseManager) d.connect("destroy", gtk.main_quit) d.run() #gtk.main() From ca5fea5cc594b76c163c7bf5d1bc8d3b800853d6 Mon Sep 17 00:00:00 2001 From: Mika Bostrom Date: Fri, 30 Oct 2009 01:51:57 +0200 Subject: [PATCH 13/17] Fix HUD start from debianized install --- packaging/debian/python-fpdb.postinst | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/debian/python-fpdb.postinst b/packaging/debian/python-fpdb.postinst index 1f618958..9680be90 100644 --- a/packaging/debian/python-fpdb.postinst +++ b/packaging/debian/python-fpdb.postinst @@ -3,3 +3,4 @@ # When installed into .../fpdb/ the script gets mode 644 # Note: "dh_fixperms -Xfpdb.py" did not work, hence this hack chmod 755 /usr/bin/fpdb +chmod 755 /usr/share/pyshared/fpdb/HUD_main.py From 636727ebb6a68da9416ffbdf8fc946e9e720ce3b Mon Sep 17 00:00:00 2001 From: Worros Date: Fri, 30 Oct 2009 14:35:45 +0800 Subject: [PATCH 14/17] Start of Partouche support, shift NEWIMPORT functions --- pyfpdb/Database.py | 116 +++++++++++++++++----------------- pyfpdb/HUD_config.xml.example | 1 + 2 files changed, 60 insertions(+), 57 deletions(-) diff --git a/pyfpdb/Database.py b/pyfpdb/Database.py index 1533c05c..ba71310b 100755 --- a/pyfpdb/Database.py +++ b/pyfpdb/Database.py @@ -1145,6 +1145,7 @@ class Database: c.execute("INSERT INTO Sites (name,currency) VALUES ('Betfair', 'USD')") c.execute("INSERT INTO Sites (name,currency) VALUES ('Absolute', 'USD')") c.execute("INSERT INTO Sites (name,currency) VALUES ('PartyPoker', 'USD')") + c.execute("INSERT INTO Sites (name,currency) VALUES ('Partouche', 'EUR')") if self.backend == self.SQLITE: c.execute("INSERT INTO TourneyTypes (id, siteId, buyin, fee) VALUES (NULL, 1, 0, 0);") elif self.backend == self.PGSQL: @@ -1264,63 +1265,6 @@ class Database: print "Error during fdb.lock_for_insert:", str(sys.exc_value) #end def lock_for_insert - def getGameTypeId(self, siteid, game): - c = self.get_cursor() - #FIXME: Fixed for NL at the moment - c.execute(self.sql.query['getGametypeNL'], (siteid, game['type'], game['category'], game['limitType'], - int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100))) - tmp = c.fetchone() - if (tmp == None): - hilo = "h" - if game['category'] in ['studhilo', 'omahahilo']: - hilo = "s" - elif game['category'] in ['razz','27_3draw','badugi']: - hilo = "l" - tmp = self.insertGameTypes( (siteid, game['type'], game['base'], game['category'], game['limitType'], hilo, - int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100), 0, 0) ) - return tmp[0] - - def getSqlPlayerIDs(self, pnames, siteid): - result = {} - if(self.pcache == None): - self.pcache = LambdaDict(lambda key:self.insertPlayer(key, siteid)) - - for player in pnames: - result[player] = self.pcache[player] - # NOTE: Using the LambdaDict does the same thing as: - #if player in self.pcache: - # #print "DEBUG: cachehit" - # pass - #else: - # self.pcache[player] = self.insertPlayer(player, siteid) - #result[player] = self.pcache[player] - - return result - - def insertPlayer(self, name, site_id): - result = None - c = self.get_cursor() - q = "SELECT name, id FROM Players WHERE siteid=%s and name=%s" - q = q.replace('%s', self.sql.query['placeholder']) - - #print "DEBUG: name: %s site: %s" %(name, site_id) - - c.execute (q, (site_id, name)) - - tmp = c.fetchone() - if (tmp == None): #new player - c.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)".replace('%s',self.sql.query['placeholder']) - ,(name, site_id)) - #Get last id might be faster here. - #c.execute ("SELECT id FROM Players WHERE name=%s", (name,)) - tmp = [self.get_last_insert_id(c)] - return tmp[0] - - def insertGameTypes(self, row): - c = self.get_cursor() - c.execute( self.sql.query['insertGameTypes'], row ) - return [self.get_last_insert_id(c)] - def store_the_hand(self, h): """Take a HandToWrite object and store it in the db""" @@ -1668,6 +1612,64 @@ class Database: # street4CheckCallRaiseChance, # street4CheckCallRaiseDone) + def getGameTypeId(self, siteid, game): + c = self.get_cursor() + #FIXME: Fixed for NL at the moment + c.execute(self.sql.query['getGametypeNL'], (siteid, game['type'], game['category'], game['limitType'], + int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100))) + tmp = c.fetchone() + if (tmp == None): + hilo = "h" + if game['category'] in ['studhilo', 'omahahilo']: + hilo = "s" + elif game['category'] in ['razz','27_3draw','badugi']: + hilo = "l" + tmp = self.insertGameTypes( (siteid, game['type'], game['base'], game['category'], game['limitType'], hilo, + int(Decimal(game['sb'])*100), int(Decimal(game['bb'])*100), 0, 0) ) + return tmp[0] + + def getSqlPlayerIDs(self, pnames, siteid): + result = {} + if(self.pcache == None): + self.pcache = LambdaDict(lambda key:self.insertPlayer(key, siteid)) + + for player in pnames: + result[player] = self.pcache[player] + # NOTE: Using the LambdaDict does the same thing as: + #if player in self.pcache: + # #print "DEBUG: cachehit" + # pass + #else: + # self.pcache[player] = self.insertPlayer(player, siteid) + #result[player] = self.pcache[player] + + return result + + def insertPlayer(self, name, site_id): + result = None + c = self.get_cursor() + q = "SELECT name, id FROM Players WHERE siteid=%s and name=%s" + q = q.replace('%s', self.sql.query['placeholder']) + + #print "DEBUG: name: %s site: %s" %(name, site_id) + + c.execute (q, (site_id, name)) + + tmp = c.fetchone() + if (tmp == None): #new player + c.execute ("INSERT INTO Players (name, siteId) VALUES (%s, %s)".replace('%s',self.sql.query['placeholder']) + ,(name, site_id)) + #Get last id might be faster here. + #c.execute ("SELECT id FROM Players WHERE name=%s", (name,)) + tmp = [self.get_last_insert_id(c)] + return tmp[0] + + def insertGameTypes(self, row): + c = self.get_cursor() + c.execute( self.sql.query['insertGameTypes'], row ) + return [self.get_last_insert_id(c)] + + ################################# # Finish of NEWIMPORT CODE diff --git a/pyfpdb/HUD_config.xml.example b/pyfpdb/HUD_config.xml.example index 34a8f16d..48ef3ea0 100644 --- a/pyfpdb/HUD_config.xml.example +++ b/pyfpdb/HUD_config.xml.example @@ -567,6 +567,7 @@ Left-Drag to Move" + From a3a72712ac1a8cf92ed4ea0223c555559e4e4c27 Mon Sep 17 00:00:00 2001 From: fpdb-mme Date: Fri, 30 Oct 2009 14:23:27 +0100 Subject: [PATCH 15/17] again, many many changes + still ugly code Signed-off-by: fpdb-mme --- pyfpdb/DatabaseManager.py | 257 +++++++++++++++++++++----------------- 1 file changed, 143 insertions(+), 114 deletions(-) diff --git a/pyfpdb/DatabaseManager.py b/pyfpdb/DatabaseManager.py index 3c176634..55e37e20 100644 --- a/pyfpdb/DatabaseManager.py +++ b/pyfpdb/DatabaseManager.py @@ -12,7 +12,7 @@ class DatabaseManager(object): def from_fpdb(klass, data, defaultDatabaseType=None): #TODO: parse whatever data is databases = ( - DatabaseTypePostgres(name='myDb'), + DatabaseTypeSqLite(name='myDb'), DatabaseTypeSqLite(name='myDb2'), ) @@ -34,11 +34,10 @@ class DatabaseManager(object): return database def database_id(self, database): return id(database) - def create_database(self, databaseType, **kws): - databaseKlass = self.DatabaseTypes[databaseType] - database = databaseType(**kws) + def add_database(self, database): + if database in self._databases: + raise ValueError('database already registered') self._databases.append(database) - return database def remove_database(self, database): self._databases.remove(database) @@ -54,9 +53,13 @@ class DatabaseTypeMeta(type): class DatabaseTypeBase(object): __metaclass__ = DatabaseTypeMeta Type = None + Params = () class DatabaseTypePostgres(DatabaseTypeBase): Type = 'postgres' + @classmethod + def display_name(klass): + return 'Postgres' def __init__(self, name='', host='localhost', port=5432, user='postgres', password='', database='fpdb'): self.name = name self.host = host @@ -64,9 +67,12 @@ class DatabaseTypePostgres(DatabaseTypeBase): self.user = user self.password = password self.database = database - + class DatabaseTypeMysql(DatabaseTypeBase): Type = 'mysql' + @classmethod + def display_name(klass): + return 'MySql' def __init__(self, name='', host='localhost', port=3306, user='root', password='', database='fpdb'): self.name = name self.host = host @@ -74,13 +80,25 @@ class DatabaseTypeMysql(DatabaseTypeBase): self.user = user self.password = password self.database = database - + class DatabaseTypeSqLite(DatabaseTypeBase): Type = 'sqlite' + @classmethod + def display_name(klass): + return 'SqLite' def __init__(self, name='', host='', file=''): self.name = name self.file = file +#TODO: how do we want to handle unsupported database types? +# ..uncomment to remove unsupported database types +#try: import psycopg2 +#except ImportError: del DatabaseManager.DatabaseTypes['postgres'] +#try: import MySQLdb +#except ImportError: del DatabaseManager.DatabaseTypes['mysql'] +#try: import sqlite3 +#except ImportError: del DatabaseManager.DatabaseTypes['sqlite'] + #*************************************************************************************************************************** class MyFileChooserButton(gtk.HBox): #NOTE: for some weird reason it is impossible to let the user choose a non exiting filename with gtk.FileChooserButton, so impl our own on the fly @@ -124,8 +142,9 @@ class MyFileChooserButton(gtk.HBox): # 1. user types in a new filename. easy one, create the file # 2. user selectes a file with the intention to overwrite it # 3. user selects a file with the intention to plug an existing database file in - #IDEA: impl open_existing as plug in, never overwrite, cos we can not guess - #PROBLEMS: how to validate an existing file is a database? + # + # if we create a new one, choice is create_new or overwrite + # if we add a database we may have to sanity check user picked one def on_dialog_confirm_overwrite(self, dlg): print dlg.get_filename() @@ -138,12 +157,14 @@ class MyFileChooserButton(gtk.HBox): gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN # -#TODO: rewrite to use idDatabase + create and remove databases through DatabaseManager + +#TODO: derrive from gtk.VBox? class WidgetDatabaseProperties(gtk.VBox): - ModeEdit = 0x1 - ModeAdd = 0x2 - ModeNew = 0x4 + ModeNew = 0 + ModeEdit = 1 + ModeAdd = 2 + class FieldWidget(object): def __init__(self, text='', attrDatabase='', widget=None, attrGet=None, attrSet=None, defaultValue=None, canEdit=False, tooltip=''): """ @@ -179,10 +200,13 @@ class WidgetDatabaseProperties(gtk.VBox): def reset_value(self): getattr(self._widget, self._attrSet)(self._defaultValue) - def __init__(self, databaseManager, database=None, flags=ModeEdit): + def __init__(self, databaseManager, database, mode=ModeEdit): gtk.VBox.__init__(self) - self.flags = flags + self.databaseManager = databaseManager + self.database = database + self.mode = mode + self.fieldWidgets = ( self.FieldWidget( text='Name:', @@ -263,10 +287,18 @@ class WidgetDatabaseProperties(gtk.VBox): cell = gtk.CellRendererText() self.comboType.pack_start(cell, True) self.comboType.add_attribute(cell, 'text', 0) - # fill out combo with database type. we store (displayName, databaseType) in our model for later lookup - for dbType, dbDisplayName in sorted([(klass.Type, klass.Type) for klass in databaseManager.DatabaseTypes.values()]): - listStore.append( (dbDisplayName, dbType) ) self.comboType.connect('changed', self.on_combo_type_changed) + + # fill database type combo with available database klasses. we store (databaseDisplayName, databaseType) in our model for later lookup + iCurrentDatabase = 0 + databaseTypes = [(klass.display_name(), klass.Type) for klass in databaseManager.DatabaseTypes.values()] + databaseTypes.sort() + for i, (databaseDisplayName, databaseType) in enumerate(databaseTypes): + listStore.append( (databaseDisplayName, databaseType) ) + if databaseType == self.database.Type: + iCurrentDatabase = i + if self.mode == self.ModeEdit or len(databaseTypes) < 2: + self.comboType.set_button_sensitivity(gtk.SENSITIVITY_OFF) # init and layout field widgets self.pack_start(self.comboType, False, False, 2) @@ -277,60 +309,37 @@ class WidgetDatabaseProperties(gtk.VBox): table.attach(fieldWidget.widget(), 1, 2, i, i+1) # init widget - - # if a database has been passed user is not allowed to change database type - if database is None: - self.comboType.set_button_sensitivity(gtk.SENSITIVITY_ON) + self.comboType.set_active(iCurrentDatabase) + self._adjust_widgets(self.database) + + def _adjust_widgets(self, database): + for fieldWidget in self.fieldWidgets: + isSensitive = fieldWidget.is_sensitive(database) + if isSensitive: + fieldWidget.value_from_database(database) else: - self.comboType.set_button_sensitivity(gtk.SENSITIVITY_OFF) - - # set current database - self.databaseManager = databaseManager - self.database= None - if database is None: - databaseType = self.databaseManager.get_default_database_type() - if databaseType is not None: - database = databaseType() - if database is not None: - self.set_database(database) + fieldWidget.reset_value() + if self.mode == self.ModeEdit: + isSensitive = isSensitive and fieldWidget.can_edit() + fieldWidget.set_sensitive(isSensitive) + def on_combo_type_changed(self, combo): i = self.comboType.get_active() - if i > -1: - # change database if necessary - currentDatabaseType = self.comboType.get_model()[i][1] - if currentDatabaseType != self.database.Type: - newDatabase = self.databaseManager.DatabaseTypes[currentDatabaseType]() - self.set_database(newDatabase) - - def set_database(self, database): - self.database = database - - # adjust database type combo if necessary - i = self.comboType.get_active() - if i == -1: - currentDatabaseType = None - else: - currentDatabaseType = self.comboType.get_model()[i][1] - if currentDatabaseType != self.database.Type: - for i, row in enumerate(self.comboType.get_model()): - if row[1] == self.database.Type: - self.comboType.set_active(i) - break - else: - raise ValueError('unknown database type') - - # adjust field widgets to database - for fieldWidget in self.fieldWidgets: - isSensitive = fieldWidget.is_sensitive(self.database) - if isSensitive: - fieldWidget.value_from_database(self.database) - else: - fieldWidget.reset_value() - if self.flags & self.ModeEdit: - isSensitive = isSensitive and fieldWidget.can_edit() - fieldWidget.set_sensitive(isSensitive) - + if i < 0: + return + + # check if we need to init a new database + currentDatabaseType = self.comboType.get_model()[i][1] + if currentDatabaseType == self.database.Type: + return + + # create new empty database + #NOTE: we dont register it in DatabaseManager + self.database = self.databaseManager.DatabaseTypes[currentDatabaseType]() + self._adjust_widgets(self.database) + + def get_database(self): for fieldWidget in self.fieldWidgets: if fieldWidget.is_sensitive(self.database): @@ -339,16 +348,7 @@ class WidgetDatabaseProperties(gtk.VBox): class DialogDatabaseProperties(gtk.Dialog): - def __init__(self, databaseManager, database=None,parent=None, flags=WidgetDatabaseProperties.ModeEdit): - if flags & WidgetDatabaseProperties.ModeEdit: - title = '[Edit database] - database properties' - elif flags & WidgetDatabaseProperties.ModeAdd: - title = '[Add database] - database properties' - elif flags & WidgetDatabaseProperties.ModeNew: - title = '[New database] - database properties' - else: - title = 'database properties' - + def __init__(self, databaseManager, database, parent=None, mode=WidgetDatabaseProperties.ModeEdit, title=''): gtk.Dialog.__init__(self, title=title, parent=parent, @@ -361,13 +361,12 @@ class DialogDatabaseProperties(gtk.Dialog): self.connect('response', self.on_dialog_response) # setup widget - self.flags = flags - self.widgetDatabaseProperties = WidgetDatabaseProperties(databaseManager,database=database, flags=self.flags) + self.widgetDatabaseProperties = WidgetDatabaseProperties(databaseManager,database, mode=mode) self.vbox.pack_start(self.widgetDatabaseProperties, True, True) - self.widgetDatabaseProperties.show_all() + self.show_all() - def get_database(self): - return self.widgetDatabaseProperties.get_database() + def get_widget_database_properties(self): + return self.widgetDatabaseProperties def on_dialog_response(self, dlg, responseId): if responseId == gtk.RESPONSE_REJECT: @@ -375,21 +374,15 @@ class DialogDatabaseProperties(gtk.Dialog): elif responseId == gtk.RESPONSE_ACCEPT: pass - -#TODO: just boilerplate code -class DialogDatabase(gtk.Dialog): - def __init__(self, databaseManager, parent=None): - gtk.Dialog.__init__(self, - title="My dialog", - parent=parent, - flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - buttons=( - gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, - gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, - )) - #self.set_size_request(260, 250) + +#TODO: derrive from gtk.VBox? +# ..is there a way to derrive from gtk.Widget or similar? this would make parentWidget kw obsolete +class WidgetDatabaseManager(gtk.VBox): + def __init__(self, databaseManager, parentWidget=None): + gtk.VBox.__init__(self) self.databaseManager = databaseManager + self.parentWidget = parentWidget #TODO: dono how to make word wrap work as expected self.labelInfo = gtk.Label('database management') @@ -440,16 +433,14 @@ class DialogDatabase(gtk.Dialog): col.set_visible(False) self.treeDatabaseColumns = dict([(name, i) for (i, name) in enumerate(self.treeDatabaseColumns)]) - - self.treeDatabases.get_selection().connect('changed', self.on_tree_databases_selection_changed) # layout widgets - self.vbox.pack_start(self.labelInfo, False, False, 2) - self.vbox.pack_start(gtk.HSeparator(), False, False, 2) - + vbox = gtk.VBox(self) + vbox.pack_start(self.labelInfo, False, False, 2) + vbox.pack_start(gtk.HSeparator(), False, False, 2) hbox = gtk.HBox() - self.vbox.add(hbox) + self.add(hbox) hbox.set_homogeneous(False) vbox = gtk.VBox() hbox.pack_start(vbox, False, False, 2) @@ -470,25 +461,45 @@ class DialogDatabase(gtk.Dialog): for database in self.databaseManager: self.treeDatabases.get_model().append( (database.name, 'foo', database.Type, self.databaseManager.database_id(database)) ) - - #TODO: for some reason i have to click OK/Cancel twice to close the dialog def on_button_database_new_clicked(self, button): - dlg = DialogDatabaseProperties(self.databaseManager, parent=self, flags=WidgetDatabaseProperties.ModeNew) + databaseType = self.databaseManager.get_default_database_type() + if databaseType is None: + raise ValueError('no defult database type set') + dlg = DialogDatabaseProperties( + self.databaseManager, + databaseType(), + parent=self.parentWidget, + mode=WidgetDatabaseProperties.ModeNew, + title='[New database] - database properties' + ) if dlg.run() == gtk.RESPONSE_REJECT: pass if dlg.run() == gtk.RESPONSE_ACCEPT: - database = dlg.get_database() + database = dlg.get_widget_database_properties().get_database() + #TODO: sanity checks + init databse if necessary + self.databaseManager.add_database(database) self.treeDatabases.get_model().append( (database.name, 'foo', database.Type, self.databaseManager.database_id(database)) ) dlg.destroy() def on_button_database_add_clicked(self, button): - dlg = DialogDatabaseProperties(self.databaseManager, parent=self, flags=WidgetDatabaseProperties.ModeAdd) + databaseType = self.databaseManager.get_default_database_type() + if databaseType is None: + raise ValueError('no defult database type set') + dlg = DialogDatabaseProperties( + self.databaseManager, + databaseType(), + parent=self.parentWidget, + mode=WidgetDatabaseProperties.ModeAdd, + title='[Add database] - database properties' + ) if dlg.run() == gtk.RESPONSE_REJECT: pass if dlg.run() == gtk.RESPONSE_ACCEPT: - database = dlg.get_database() - print self.treeDatabases.get_model().append( (database.name, 'foo', database.Type, self.databaseManager.database_id(database)) ) + database = dlg.get_widget_database_properties().get_database() + #TODO: sanity checks + self.databaseManager.add_database(database) + self.treeDatabases.get_model().append( (database.name, 'foo', database.Type, self.databaseManager.database_id(database)) ) dlg.destroy() def on_button_database_edit_clicked(self, button): @@ -499,8 +510,13 @@ class DialogDatabase(gtk.Dialog): model, iter = selection.get_selected() idDatabase = model.get_value(iter, self.treeDatabaseColumns['_id']) database = self.databaseManager.database_from_id(idDatabase) - - dlg = DialogDatabaseProperties(self.databaseManager, database=database, parent=self, flags=WidgetDatabaseProperties.ModeEdit) + dlg = DialogDatabaseProperties( + self.databaseManager, + database=database, + parent=self.parentWidget, + mode=WidgetDatabaseProperties.ModeEdit, + title='[Edit database] - database properties' + ) if dlg.run() == gtk.RESPONSE_REJECT: pass if dlg.run() == gtk.RESPONSE_ACCEPT: @@ -519,9 +535,22 @@ class DialogDatabase(gtk.Dialog): self.buttonDatabaseRemove.set_sensitive(hasSelection) self.buttonDatabaseDelete.set_sensitive(hasSelection) - - +class DialogDatabaseManager(gtk.Dialog): + def __init__(self, databaseManager, parent=None): + gtk.Dialog.__init__(self, + title="My dialog", + parent=parent, + flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, + buttons=( + gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, + gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, + )) + #self.set_size_request(260, 250) + self.widgetDatabaseManager = WidgetDatabaseManager(databaseManager, parentWidget=self) + self.vbox.pack_start(self.widgetDatabaseManager, True, True) + self.show_all() + #************************************************************************************************** if __name__ == '__main__': databaseManager = DatabaseManager.from_fpdb('', defaultDatabaseType=DatabaseTypeSqLite) @@ -531,7 +560,7 @@ if __name__ == '__main__': #database=DatabaseTypePostgres(), # database=None, # ) - d = DialogDatabase(databaseManager) + d = DialogDatabaseManager(databaseManager) d.connect("destroy", gtk.main_quit) d.run() #gtk.main() From 604395b8687bba93f0ad107442ef22882faae711 Mon Sep 17 00:00:00 2001 From: fpdb-mme Date: Fri, 30 Oct 2009 16:48:51 +0100 Subject: [PATCH 16/17] bit of refactoring + sqlite needs database too Signed-off-by: fpdb-mme --- pyfpdb/DatabaseManager.py | 222 +++++++++++++++++++------------------- 1 file changed, 108 insertions(+), 114 deletions(-) diff --git a/pyfpdb/DatabaseManager.py b/pyfpdb/DatabaseManager.py index 55e37e20..ddaa6fcd 100644 --- a/pyfpdb/DatabaseManager.py +++ b/pyfpdb/DatabaseManager.py @@ -11,6 +11,7 @@ class DatabaseManager(object): @classmethod def from_fpdb(klass, data, defaultDatabaseType=None): #TODO: parse whatever data is + #TODO: sort out unsupported databases passed by user and log databases = ( DatabaseTypeSqLite(name='myDb'), DatabaseTypeSqLite(name='myDb2'), @@ -40,6 +41,8 @@ class DatabaseManager(object): self._databases.append(database) def remove_database(self, database): self._databases.remove(database) + def init_database(self, database): + pass class DatabaseTypeMeta(type): def __new__(klass, name, bases, kws): @@ -86,9 +89,10 @@ class DatabaseTypeSqLite(DatabaseTypeBase): @classmethod def display_name(klass): return 'SqLite' - def __init__(self, name='', host='', file=''): + def __init__(self, name='', host='', file='', database='fpdb'): self.name = name self.file = file + self.database = database #TODO: how do we want to handle unsupported database types? # ..uncomment to remove unsupported database types @@ -100,70 +104,60 @@ class DatabaseTypeSqLite(DatabaseTypeBase): #except ImportError: del DatabaseManager.DatabaseTypes['sqlite'] #*************************************************************************************************************************** -class MyFileChooserButton(gtk.HBox): - #NOTE: for some weird reason it is impossible to let the user choose a non exiting filename with gtk.FileChooserButton, so impl our own on the fly - def __init__(self): - gtk.HBox.__init__(self) - self.set_homogeneous(False) - - self.entry = gtk.Entry() - self.button = gtk.Button('...') - self.button.connect('clicked', self.on_button_clicked) - - # layout widgets - self.pack_start(self.entry, True, True) - self.pack_start(self.button, False, False) - - def get_filename(self): - return self.entry.get_text() - - def set_filename(self, name): - self.entry.set_text(name) - - def on_button_clicked(self, button): - dlg = gtk.FileChooserDialog( - title='Choose an exiting database file or type in name of a new one', - parent=None, - action=gtk.FILE_CHOOSER_ACTION_SAVE, - buttons=( - gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, - gtk.STOCK_OK, gtk.RESPONSE_OK, - ), - backend=None - ) - dlg.connect('confirm-overwrite', self.on_dialog_confirm_overwrite) - dlg.set_default_response(gtk.RESPONSE_OK) - dlg.set_do_overwrite_confirmation(True) - if dlg.run() == gtk.RESPONSE_OK: - self.set_filename(dlg.get_filename()) - dlg.destroy() - - #TODO: when the user selects a sqLite database file we got three possible actions - # 1. user types in a new filename. easy one, create the file - # 2. user selectes a file with the intention to overwrite it - # 3. user selects a file with the intention to plug an existing database file in - # - # if we create a new one, choice is create_new or overwrite - # if we add a database we may have to sanity check user picked one - def on_dialog_confirm_overwrite(self, dlg): - print dlg.get_filename() - - gtk.FILE_CHOOSER_CONFIRMATION_CONFIRM - #The file chooser will present its stock dialog to confirm overwriting an existing file. - - gtk.FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME - #The file chooser will terminate and accept the user's choice of a file name. - - gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN - # - - #TODO: derrive from gtk.VBox? class WidgetDatabaseProperties(gtk.VBox): ModeNew = 0 ModeEdit = 1 ModeAdd = 2 + + class SqLiteFileChooserButton(gtk.HBox): + #NOTE: for some weird reason it is impossible to let the user choose a non exiting filename with gtk.FileChooserButton, so impl our own on the fly + def __init__(self, widgetDatabaseProperties, parentWidget): + gtk.HBox.__init__(self) + self.set_homogeneous(False) + + self.parentWidget = parentWidget + self.widgetDatabaseProperties = widgetDatabaseProperties + self.entry = gtk.Entry() + self.button = gtk.Button('...') + self.button.connect('clicked', self.on_button_clicked) + + # layout widgets + self.pack_start(self.entry, True, True) + self.pack_start(self.button, False, False) + + def get_filename(self): + return self.entry.get_text() + + def set_filename(self, name): + self.entry.set_text(name) + + def on_button_clicked(self, button): + if self.widgetDatabaseProperties.mode == WidgetDatabaseProperties.ModeAdd: + action = gtk.FILE_CHOOSER_ACTION_OPEN + elif self.widgetDatabaseProperties.mode == WidgetDatabaseProperties.ModeNew: + action = gtk.FILE_CHOOSER_ACTION_SAVE + else: + raise ValueError('unsupported dialog mode') + dlg = gtk.FileChooserDialog( + title='Choose an exiting database file or type in name of a new one', + parent=self.parentWidget, + action=action, + buttons=( + gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, + gtk.STOCK_OK, gtk.RESPONSE_OK, + ), + backend=None + ) + dlg.set_default_response(gtk.RESPONSE_OK) + dlg.set_do_overwrite_confirmation(True) + if dlg.run() == gtk.RESPONSE_OK: + fileName = dlg.get_filename() + self.set_filename(fileName) + dlg.destroy() + + class FieldWidget(object): def __init__(self, text='', attrDatabase='', widget=None, attrGet=None, attrSet=None, defaultValue=None, canEdit=False, tooltip=''): @@ -200,13 +194,13 @@ class WidgetDatabaseProperties(gtk.VBox): def reset_value(self): getattr(self._widget, self._attrSet)(self._defaultValue) - def __init__(self, databaseManager, database, mode=ModeEdit): + def __init__(self, databaseManager, database, mode=ModeEdit, parentWidget=None): gtk.VBox.__init__(self) self.databaseManager = databaseManager self.database = database self.mode = mode - + self.parentWidget = parentWidget self.fieldWidgets = ( self.FieldWidget( text='Name:', @@ -216,57 +210,7 @@ class WidgetDatabaseProperties(gtk.VBox): attrGet='get_text', attrSet='set_text', canEdit=True, - tooltip='' - ), - self.FieldWidget( - text='File:', - attrDatabase='file', - widget=MyFileChooserButton(), - defaultValue='', - attrGet='get_filename', - attrSet='set_filename', - canEdit=False, - tooltip='' - ), - self.FieldWidget( - text='Host:', - attrDatabase='host', - widget=gtk.Entry(), - defaultValue='', - attrGet='get_text', - attrSet='set_text', - canEdit=False, - tooltip='' - ), - self.FieldWidget( - text='Port:', - attrDatabase='port', - widget=gtk.SpinButton(adjustment=gtk.Adjustment(value=0, lower=0, upper=999999, step_incr=1, page_incr=10) ), - defaultValue=0, - attrGet='get_value', - attrSet='set_value', - canEdit=False, - tooltip='' - ), - self.FieldWidget( - text='User:', - attrDatabase='user', - widget=gtk.Entry(), - defaultValue='', - attrGet='get_text', - attrSet='set_text', - canEdit=False, - tooltip='' - ), - self.FieldWidget( - text='Pwd:', - attrDatabase='password', - widget=gtk.Entry(), - defaultValue='', - attrGet='get_text', - attrSet='set_text', - canEdit=False, - tooltip='' + tooltip='Any name you like to name the database ' ), self.FieldWidget( text='Db:', @@ -276,7 +220,57 @@ class WidgetDatabaseProperties(gtk.VBox): attrGet='get_text', attrSet='set_text', canEdit=False, - tooltip='' + tooltip='Name of the database to create' + ), + self.FieldWidget( + text='File:', + attrDatabase='file', + widget=self.SqLiteFileChooserButton(self, self.parentWidget), + defaultValue='', + attrGet='get_filename', + attrSet='set_filename', + canEdit=False, + tooltip='Fully qualified path of the file to hold the database ' + ), + self.FieldWidget( + text='Host:', + attrDatabase='host', + widget=gtk.Entry(), + defaultValue='', + attrGet='get_text', + attrSet='set_text', + canEdit=False, + tooltip='Host the database is located at' + ), + self.FieldWidget( + text='Port:', + attrDatabase='port', + widget=gtk.SpinButton(adjustment=gtk.Adjustment(value=0, lower=0, upper=999999, step_incr=1, page_incr=10) ), + defaultValue=0, + attrGet='get_value', + attrSet='set_value', + canEdit=False, + tooltip='Port to use to connect to the host' + ), + self.FieldWidget( + text='User:', + attrDatabase='user', + widget=gtk.Entry(), + defaultValue='', + attrGet='get_text', + attrSet='set_text', + canEdit=False, + tooltip='User name used to login to the host' + ), + self.FieldWidget( + text='Pwd:', + attrDatabase='password', + widget=gtk.Entry(), + defaultValue='', + attrGet='get_text', + attrSet='set_text', + canEdit=False, + tooltip='Password used to login to the host' ), ) @@ -361,7 +355,7 @@ class DialogDatabaseProperties(gtk.Dialog): self.connect('response', self.on_dialog_response) # setup widget - self.widgetDatabaseProperties = WidgetDatabaseProperties(databaseManager,database, mode=mode) + self.widgetDatabaseProperties = WidgetDatabaseProperties(databaseManager,database, mode=mode, parentWidget=self) self.vbox.pack_start(self.widgetDatabaseProperties, True, True) self.show_all() From ce0e2c139bb7547339f12a403e1100dab3774c24 Mon Sep 17 00:00:00 2001 From: Worros Date: Sat, 31 Oct 2009 08:55:54 +0800 Subject: [PATCH 17/17] Revert "Print out actual exception when import fails" This reverts commit 626ff49eb41e5bb28d8db33277f3fe726d90aef5. Turns out this commit is python 2.6 syntax --- pyfpdb/GuiGraphViewer.py | 3 +-- pyfpdb/GuiSessionViewer.py | 7 ++++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyfpdb/GuiGraphViewer.py b/pyfpdb/GuiGraphViewer.py index 739a8f18..4011ba00 100644 --- a/pyfpdb/GuiGraphViewer.py +++ b/pyfpdb/GuiGraphViewer.py @@ -32,12 +32,11 @@ try: from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar from numpy import arange, cumsum from pylab import * -except ImportError as inst: +except ImportError: print """Failed to load libs for graphing, graphing will not function. Please in stall numpy and matplotlib if you want to use graphs.""" print """This is of no consequence for other parts of the program, e.g. import and HUD are NOT affected by this problem.""" - print "ImportError: %s" % inst.args import fpdb_import import Database diff --git a/pyfpdb/GuiSessionViewer.py b/pyfpdb/GuiSessionViewer.py index 5711cb89..7695cf73 100755 --- a/pyfpdb/GuiSessionViewer.py +++ b/pyfpdb/GuiSessionViewer.py @@ -37,10 +37,11 @@ try: # from matplotlib.dates import DateFormatter, WeekdayLocator, HourLocator, \ # DayLocator, MONDAY, timezone -except ImportError as inst: +except: + err = traceback.extract_tb(sys.exc_info()[2])[-1] + print "***Error: "+err[2]+"("+str(err[1])+"): "+str(sys.exc_info()[1]) print """Failed to load numpy in Session Viewer""" - print """This is of no consequence as the page is broken and only of interest to developers.""" - print "ImportError: %s" % inst.args + print """This is of no consequence as the module currently doesn't do anything.""" import Card import fpdb_import