Timo wrote:
Hello, I would like to have a gtk.Calendar to popup under a gtk.Entry as soon as the user clicks on the entry. So I use the focus-in-event to capture when the user clicks in the entry. But I can't seem to find a way to popup a calendar.

Timo
_______________________________________________
pygtk mailing list   [email protected]
http://www.daa.com.au/mailman/listinfo/pygtk
Read the PyGTK FAQ: http://faq.pygtk.org/

Here is a custom widget I use.  You can default the format of the date
to anything you like.   When you right click in the widget it pops up
a calendar dialog.

You can also type the date into the widget in a wide set of formats.

When the date changes it also sends a 'date_changed' signal.


Hope this helps.



import gtk
from gtk import gdk
import gobject
import time

class InvalidDate(Exception):
	def __init__(self,date):
		super(InvalidDate, self).__init__()
		self.message = "Invalid date \"%s\"." % (date)
	def __str__(self):
		return self.message

class DateEntry(gtk.Entry) :
	#date = None
	calendar_dialog = False
	timestamp = None
	format = '%e-%b-%Y'
	widget = None
	window = None
	__gsignals__ = dict(date_changed=(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()))
	
	def __init__(self, window, title = None) :
		super(DateEntry, self).__init__()
		self.connect('focus_out_event',self.on_focus_out_event)
		self.connect('button_press_event',self.on_button_press_event)
		self.connect('activate', lambda widget: widget.get_toplevel().child_focus(gtk.DIR_TAB_FORWARD))
		if not window :
			raise ValueError('Parent window needed')
		self.window = window
		self.title = title
		self.set_width_chars(11)
		#self.set_max_length(11)

	def __str__(self) :
		if self.timestamp == None :
			date = 'Unknown'
		else :
			date = time.strftime(self.format, self.timestamp)
		return date
	
	def on_button_press_event (self,widget,event) :
		#print "date_widget:on_button_press_event()"
		if event.button == 3 and event.type == gtk.gdk.BUTTON_PRESS :
			text = super(DateEntry, self).get_text()
			if text == None or len(text.strip()) == 0 : 
				self.timestamp = None	#we don't want to emit a signal as the popup will do so when needed
			self.popup_calendar()
			return True
		else :
			return False
	
	def popup_calendar(self) :
		self.calendar_dialog = True
		#print "date_widget:popup_calendar() -- ",self.timestamp
		dialog = gtk.Dialog (self.title, self.window,
				gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
				(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK))
		#self.dialog.set_position(gtk.WIN_POS_MOUSE)
		calendar = gtk.Calendar()
		dialog.vbox.pack_start (calendar, True, True, 0)
		calendar.connect('day_selected_double_click', self.calendar_check, dialog)
		timestamp = self.timestamp
		if (timestamp == None) :
			timestamp = time.localtime()
		if (timestamp) :
			calendar.select_month(timestamp[1] - 1,timestamp[0])
			calendar.select_day(timestamp[2])
		dialog.show_all()
		result = dialog.run()
		if result == gtk.RESPONSE_OK :
			self.calendar_check(calendar, dialog)
		else :
			dialog.destroy()
		self.calendar_dialog = False

	def calendar_check(self,widget, dialog) :
		(year,month,day) = widget.get_date()
		current = time.strptime("%d-%d-%d" % (year,month + 1,day),'%Y-%m-%d')
		#print self,"calendar_check",current
		super(DateEntry, self).set_text(time.strftime(self.format, current))
		self.check_for_signal(current)
		dialog.destroy()
	
	def set_format(self,format) :
		#print "date_widget:set_format"
		if format :
			self.format = format
	
	def set_today(self) :
		# round the current time into a date
		timestamp = time.strptime(time.strftime('%d-%b-%Y',time.localtime()),'%d-%b-%Y')
		self.check_for_signal(timestamp)
		super(DateEntry, self).set_text(self.get_date())

	def set_date(self,date,format = None) :
		#print "date_widget:set_date()"
		if date == None or len(date.strip()) == 0 :
			self.check_for_signal(None)
			super(DateEntry, self).set_text('')
			return
		if format == None : 
			format = self.check_formats(date)
		else :
			self.timestamp = time.strptime(date,format)
			super(DateEntry, self).set_text(self.get_date())
		if format == None :
			raise ValueError,'Unknown date format - %s' % (date)
	
	def set_pg_date (self, field) :
		return self.set_date(str(field.strftime('%d-%m-%Y')))

	def get_date(self,format = None) :
		#print "date_widget:get_date()"
		# check if the widget has the focus
		if self.is_focus() :
			# the widget has the focus
			# we need to check if the current text is OK
			text = super(DateEntry, self).get_text()
			if len(text) > 0 :
				timestamp = self.check_formats(text) 
				if not timestamp :
					raise InvalidDate(text)
			else :
				return None
		if self.timestamp == None : return None
		if format == None : format = self.format
		return time.strftime(format, self.timestamp)
	
	def check_for_signal(self,current) :
		#print "timestamp = ",self.timestamp,"    current ",current
		if (current != self.timestamp) :
			#print self,"emit date_changed",current
			self.timestamp = current
			self.emit('date_changed')
	
	def on_focus_out_event(self,widget,event) :
		#print "date_widget:on_focus_out()"
		#text = self.get_text()		# get the text entered
		text = super(DateEntry, self).get_text()
		if text == None or len(text.strip()) == 0 :
			#print "no text in widget"
			# no text clear the date
			self.check_for_signal(None)
			return
		timestamp = self.check_formats(text) 
		if timestamp == None and not self.calendar_dialog :
			#raise InvalidDate(text)
			self.dialog = gtk.MessageDialog(self.window,
								gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
								gtk.MESSAGE_ERROR,
								gtk.BUTTONS_CANCEL,
								"Unknown date format\n%s" % (text))
			self.dialog.connect("response", self.on_dialog_response)
			#self.dialog.set_position(gtk.WIN_POS_MOUSE)
			self.dialog.show()

	def check_formats (self, text) :
		# try multple formats for converting to a timestamp
		try :
			timestamp = time.strptime(text,self.format)
		except :
			pass
		else :
			super(DateEntry, self).set_text(time.strftime(self.format, timestamp))
			self.check_for_signal(timestamp)
			return timestamp
		for format in ['%d-%m-%Y','%d-%b-%Y','%d-%B-%Y','%d-%m-%y','%d-%b-%y','%d-%B-%y','%Y-%m-%d','%Y-%b-%d','%Y-%B-%d',
					'%d/%m/%Y','%d/%b/%Y','%d/%B/%Y','%d/%m/%y','%d/%b/%y','%d/%B/%y','%Y/%m/%d','%Y/%b/%d','%Y/%B/%d'] :
			try :
				timestamp = time.strptime(text, format)
			except :
				pass
			else :
				super(DateEntry, self).set_text(time.strftime(self.format, timestamp))
				self.check_for_signal(timestamp)
				return timestamp
		return None
		
	def on_dialog_response(self,dialog,response) :
		#print "date_widget:on_dialog_response()"
		#raise (AttributeError, 'Invalid date')
		gobject.timeout_add(100, self.grab_focus)
		self.dialog.destroy()
	
	def set_text(self, text) :
		raise (AttributeError, 'Use set_date()')

	def get_text(self) :
		return self.get_date()


if __name__ == "__main__":
	class window:
		def __init__(self) :
			window = gtk.Window()
			window.connect("destroy", gtk.main_quit)

			vbox = gtk.VBox()
			window.add(vbox)
		
			# a date widget for testing
			date = DateEntry(window)
			vbox.pack_start(date)

			# button for testing the get_date()
			button = gtk.Button('get date')
			vbox.pack_start(button, False, False)
			button.connect('clicked', self.on_button_clicked, date)

			date.connect("date_changed", self.date_changed_cb)
			window.show_all()
	
			#date.set_date('10-jan-2007','%d-%b-%Y')
			date.set_date(None,'%d-%b-%Y')

		def date_changed_cb(self, widget):
			print self,"date-changed \"%s\"" % (widget.get_date())
	
		def setup_date(self, widget, event, date) :
			if widget.changed :
				date.set_date(widget.get_text())
			widget.changed = False

		def setup_changed(self,widget) :
			widget.changed = True

		def main(self):
			gtk.main()

		def on_button_clicked(self, button, widget) :
			try :
				date = widget.get_date()
			except datewidget.InvalidDate,msg :
				print "oh! \"%s\"" % (msg)
			else :
				print "Date = %s" % (date)

	window = window()
	window.main()



_______________________________________________
pygtk mailing list   [email protected]
http://www.daa.com.au/mailman/listinfo/pygtk
Read the PyGTK FAQ: http://faq.pygtk.org/

Reply via email to