add DatabaseManager
This commit is contained in:
		
							parent
							
								
									0b0312d1ee
								
							
						
					
					
						commit
						1efdd7fc68
					
				
							
								
								
									
										390
									
								
								pyfpdb/DatabaseManager.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										390
									
								
								pyfpdb/DatabaseManager.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,390 @@ | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
|  | 	@classmethod | ||||||
|  | 	def display_name(klass): | ||||||
|  | 		raise NotImplementedError() | ||||||
|  | 	 | ||||||
|  | class DatabaseTypePostgres(DatabaseTypeBase): | ||||||
|  | 	Type = 'postgres' | ||||||
|  | 	Flags = DatabaseTypeBase.DBFlagsServer | ||||||
|  | 	@classmethod | ||||||
|  | 	def display_name(klass): | ||||||
|  | 		return 'Postgres' | ||||||
|  | 	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 | ||||||
|  | 	@classmethod | ||||||
|  | 	def display_name(klass): | ||||||
|  | 		return 'MySql' | ||||||
|  | 	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 = 'sqlie' | ||||||
|  | 	Flags = DatabaseTypeBase.DBFlagsFileSystem | ||||||
|  | 	@classmethod | ||||||
|  | 	def display_name(klass): | ||||||
|  | 		return 'SqLite' | ||||||
|  | 	def __init__(self, host='', file='/home/me2/winetricks', 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) | ||||||
|  | 		 | ||||||
|  | 	#TODO: we got three possible actions here | ||||||
|  | 	#    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_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() | ||||||
|  | 		 | ||||||
|  | 	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 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:  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.display_name()) 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): | ||||||
|  | 		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 | ||||||
|  | 			 | ||||||
|  | 		label = gtk.Label('database stuff') | ||||||
|  | 		label.set_line_wrap(True) | ||||||
|  | 		label.set_selectable(True) | ||||||
|  | 		label.set_single_line_mode(False) | ||||||
|  | 		label.set_alignment(0, 0) | ||||||
|  | 		self.vbox.pack_start(label, False, False, 2) | ||||||
|  | 		self.vbox.pack_start(gtk.HSeparator(), False, False, 2) | ||||||
|  | 				 | ||||||
|  | 		hbox = gtk.HBox() | ||||||
|  | 		self.vbox.add(hbox) | ||||||
|  | 		hbox.set_homogeneous(False) | ||||||
|  | 			 | ||||||
|  | 		 | ||||||
|  | 		# database management buttons | ||||||
|  | 		vbox = gtk.VBox() | ||||||
|  | 		hbox.pack_start(vbox, False, False, 2) | ||||||
|  | 		self.buttonDatabaseNew = gtk.Button("New...") | ||||||
|  | 		self.buttonDatabaseNew.connect('clicked', self.onButtonDatabaseNewClicked) | ||||||
|  | 		vbox.pack_start(self.buttonDatabaseNew, False, False, 2) | ||||||
|  | 		self.buttonDatabaseEdit = gtk.Button("Edit...") | ||||||
|  | 		vbox.pack_start(self.buttonDatabaseEdit, False, False, 2) | ||||||
|  | 		self.buttonDatabaseDelete = gtk.Button("Delete") | ||||||
|  | 		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) | ||||||
|  | 			 | ||||||
|  | 		# database tree		 | ||||||
|  | 		self.treeDatabases = gtk.TreeView() | ||||||
|  | 		hbox.pack_end(self.treeDatabases, True, True, 2) | ||||||
|  | 			 | ||||||
|  | 		self.show_all() | ||||||
|  | 		 | ||||||
|  | 		# fill database tree | ||||||
|  | 		store = gtk.ListStore(str, str) | ||||||
|  | 		self.treeDatabases.set_model(store) | ||||||
|  | 		columns = ('Name', 'Status', 'Type') | ||||||
|  | 		for column in columns: | ||||||
|  | 			col = gtk.TreeViewColumn(column) | ||||||
|  | 			self.treeDatabases.append_column(col) | ||||||
|  | 			 | ||||||
|  | 			 | ||||||
|  | 	def onButtonDatabaseNewClicked(self, button): | ||||||
|  | 		dlg = DialogDatabaseProperties(self.databaseManager, parent=self) | ||||||
|  | 		if dlg.run() == gtk.RESPONSE_REJECT: | ||||||
|  | 			pass | ||||||
|  | 		if dlg.run() == gtk.RESPONSE_ACCEPT: | ||||||
|  | 			pass | ||||||
|  | 		 | ||||||
|  | 		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() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user