Hi, I have been using xpostit and icewm for a while (no gnome). I have always hated the window border around the notes that icewm adds. So I just took your program and added a line like this:
window.set_decorated(False) in function _make_widgets right after the creation of the window. And it works great, I got rid of that annoying border. Thanks for the program!! Carlos On 25/08/05 15:46, Johan Dahlin wrote: > > > -------- Original Message -------- > Subject: stix: a sticky note with pygtk 2.4 > Date: Fri, 26 Aug 2005 02:36:19 +0800 > From: Ping Yeh <[EMAIL PROTECTED]> > To: [EMAIL PROTECTED] > > Hello, > > First of all, I'd like to thank all of you who created pygtk. > It really makes GUI program fun and easy! :) > > I'm new to pygtk although I'm been programming in python for > 7 years. I did not find a handy sticky-note application in GNOME. > Maybe I was not looking in the right place. Anyway, I decided > to write one by myself in pygtk. And, yes, it is not hard. > The result is the "stix" program attached in this mail, just one > python script. I'm releasing it in GPL v2. I don't have a web > page for it yet. > > The main features are: > > * Multiple notes are supported. > * Each note can have its own foreground font and background color. > * When pressing ctrl-Q to quit the application, all contents are > automatically saved. > * When starting it again, all previously open notes are opened > in the same state (position,size,font,color,content) as it were. > * And, it sticks on the desktop! > > It is written on my linux box, which comes with python 2.2.3 > and a very old pygtk. I spent some time to install python 2.4.1 > and also pygtk 2.4.1. So the first line of the program says > #!/usr/bin/env python2.4. What should I do to make it run on a > larger python install base? > > If you find it interesting (or boring), either way, I'd appreciate > any advices/comments. Thanks a lot in advance. > > cheers, > Ping > > > -- > Johan Dahlin <[EMAIL PROTECTED]> > Async Open Source > > > #!/usr/bin/env python2.4 > # -*- coding: utf8 -*- > # > # This is free software. It is released under the GNU GENERAL PUBLIC LICENSE > # Version 2, June 1991. > # > # This is just a small sticky-note program that really "sticks". :) > # > # Copyright (c) Ping Yeh <[EMAIL PROTECTED]> > # 2005/08/13 - 2005/08/26 > > # a pygtk2 tutorial can be found in http://www.pygtk.org/pygtk2tutorial > # a pygtk2 reference manual is at > http://www.moeraki.com/pygtkreference/pygtk2reference/ > > __app__ = 'stix' > __version__ = '0.1' > > import pygtk > pygtk.require('2.0') > import gtk, pango > > _ = lambda x: x > > import os, re, types, sys, time, glob, tempfile > > class StixConfig: > '''Configurations of the Stix application''' > def __init__(self, filename=''): > # default values > today = '%d-%d-%d' % time.localtime()[0:3] > self.rcdir = os.path.expanduser('~/.' + __app__) > self.defaults = { 'font': 'courier', > 'background': 'yellow', > 'foreground': 'black', > 'sticky': 'yes', > 'open': 'no', > 'lastopen': today} > # everything else is read from the file if available > self._note_conf = {} # dict of all notes (url: note) > self._open_notes = [] # list of URL of open notes > if filename: self.read(filename) > def read(self, filename): > '''read the Stix application configuration from a file''' > import xml.dom.minidom > doc = xml.dom.minidom.parse(filename) > # read the default settings > nodes = doc.getElementsByTagName('defaults') > if nodes: # use application defaults > for nd in filter(lambda x: x.nodeType == > x.ELEMENT_NODE, nodes[0].childNodes): > self.defaults[nd.nodeName] = self._gettext(nd) > # read the per note settings > nodes = doc.getElementsByTagName('note') > #if not nodes: > # url = 'note://1.note' > # self.note[url] = StixNoteConfig(url, self.defaults) > # self.opennote.append(url) > # return > for fino in nodes: > url = fino.getAttribute('href') > isopen = fino.getAttribute('open') > noteconf = StixNoteConfig(url, self.defaults) > noteconf['open'] = isopen > self._note_conf[url] = noteconf > if isopen in ['yes', 'true']: > self._open_notes.append(url) > #else: > # self.closednotes[url] = noteconf > # note-specific settings > for nd in filter(lambda x: x.nodeType == > x.ELEMENT_NODE, fino.childNodes): > noteconf[nd.nodeName] = self._gettext(nd) > def write(self, filename): > '''write the configuration to specified XML file''' > f = open(filename, 'w') > f.write('<?xml version="1.0"?>\n<stix>\n<defaults>\n') > for k, v in self.defaults.iteritems(): > f.write(' <%s>%s</%s>\n' % (k,v,k)) > f.write('</defaults>\n') > for url, note in self._note_conf.iteritems(): > f.write(note.toxml()) > f.write('</stix>\n') > f.close() > def get(self, url, name): > try: value = self._note_conf[url][name] > except: return None # no such url or name > if value == StixNoteConfig.Default: # use defaults > return self.defaults[name] > else: return value > > def new_noteconf(self, url=''): > if not url: > fd, name = tempfile.mkstemp(suffix='.note', > prefix='stix_', dir=self.rcdir, text=True) > os.close(fd) # now there is an empty file > url = name.replace(self.rcdir+'/', 'note://') > new_conf = StixNoteConfig(url, self.defaults) > self._note_conf[url] = new_conf > return new_conf > #def add_noteconf(self, noteconf): > # self._note_conf[noteconf['url']] = noteconf > def get_noteconf(self, url): > '''get the note configuration instance or None''' > return self._note_conf.get(url, None) > def has_noteconf(self, url): > '''Does the note with specified URL exist in the conf?''' > return self._note_conf.has_key(url) > def get_noteconf_urls(self): > '''return the list of note URLs''' > return self._note_conf.keys() > def _gettext(self, elem): > '''used by read() only''' > return ''.join([x.data for x in filter(lambda x: x.nodeType == > x.TEXT_NODE, elem.childNodes)]) > > class StixNoteConfig(dict): > '''The configuration of a note''' > Default = None > def __init__(self, url, defaults): > dict.__init__(self) > self['url'] = url > self._defaults = defaults > # create keys with default values > for k in defaults.keys(): self[k] = self.Default > def __getitem__(self, key): > if not self.has_key(key): return None > value = dict.__getitem__(self, key) > if value is self.Default: > return self._defaults[key] > else: return value > def toxml(self): > s = '<note href="%s" open="%s">\n' % (self['url'], self['open']) > for k,v in self.iteritems(): > if v == None or k in ['url', 'open']: continue > s += ' <%s>%s</%s>\n' % (k,v,k) > s += '</note>\n' > return s > > > class StixApp: > '''The sticky GUI application class, Borg pattern''' > # This Borg object has no visible window > instance = None > def __init__(self): > if StixApp.instance != None: > self.__dict__ = StixApp.instance.__dict__ > return > else: > StixApp.instance = self > # initial data members > self.rcdir = os.path.expanduser('~/.' + __app__) > self.conf_filename = self.rcdir + '/' + __app__ + '.conf' > self._load_config() > self._init_notes() > > def new_note(self): > #unnamed = self._get_noname_notes() > #self._add_note('note://%d.note' % (len(unnamed)+1)) > new_conf = self._conf.new_noteconf() > self._add_note(new_conf['url']) > > def open_note(self, url): > '''open an existing note''' > self._add_note(url) > #self.opennote.append(StixNote(self, url)) > > def close_note(self, note): > '''mark the note as closed''' > #print 'before removing:', self.opennote, 'vs', note > if len(self._notes) == 1 and self._notes[0] == note: > self.quit() # finish stix when the last note is closed > else: > note._conf['open'] = 'no' > self._notes.remove(note) > > def _load_config(self): > '''Load the configuration file. Create one if non-existent.''' > # make sure self.rcdir points to a directory > if not os.path.exists(self.rcdir): os.mkdir(self.rcdir) > elif not os.path.isdir(self.rcdir): > raise IOError('%s should be a directory.' % self.rcdir) > # read the configuration file if exists, otherwise create one > if not os.path.exists(self.conf_filename): # write defaults > self._conf = StixConfig() > self._conf.write(self.conf_filename) > else: > self._conf = StixConfig(self.conf_filename) > > def _save_config(self): > '''write configuration to self.conf_filename''' > if not os.path.exists(self.rcdir): os.mkdir(self.rcdir) > self._conf.write(self.conf_filename) > > def _init_notes(self): > '''initialize all open notes''' > self._notes = [] > url_list = self._conf.get_noteconf_urls() > print '_init_notes: url_list =', url_list > if url_list == []: # no notes in the conf > new_conf = self._conf.new_noteconf('note://1.note') > new_conf['open'] = 'yes' > self._notes.append(StixNote(self, new_conf)) > return > for url in url_list: > #if url == 'null://noname': > # self.opennote.append(StixNote(self)) > if not url.startswith('file://') and not > url.startswith('note://'): > continue # only support file:// for now > note_conf = self._conf.get_noteconf(url) > if note_conf['open'] in ['yes', 'true']: > self._notes.append(StixNote(self, note_conf)) > if self._notes == []: # no open notes?!!! > new_conf = self._conf.new_noteconf() > new_conf['open'] = 'yes' > self._notes.append(StixNote(self, new_conf)) > return > > def _add_note(self, url): > '''add a note with specified url into the application''' > # first work on the configuration of the note > noteconf = self._conf.get_noteconf(url) > if not noteconf: > noteconf = self._conf.new_noteconf(url) > noteconf['open'] = 'yes' > note = StixNote(self, noteconf) > self._notes.append(note) > > def quit(self): > '''terminate the application''' > #self.conf.notes = [x.filename for x in self.opennote] > self._save_config() > for note in self._notes: > note._save() > gtk.main_quit() > sys.exit(0) > > > class StixNote: > '''An open note and its associated GUI''' > # All cb_* functions are callbacks. > geomre = re.compile('(\d+)x(\d+)\+(\d+)\+(\d+)') > def __init__(self, app, conf): > self._app = app # link to the application instance > #app.conf.newopennote(url) > self._conf = conf > self._url = conf['url'] > self._read() > self._make_widgets() > > def _close(self): > '''close the note''' > x, y = self._window.get_position() > width, height = self._window.get_size() > self._conf['geometry'] = '%dx%d+%d+%d' % (width, height, x, y) > self._window.destroy() > self._app.close_note(self) > > def _read(self): > '''read the note content''' > self._content = self._filename = '' > fname = self._url2filename(self._url) > if not fname: return > if self._url.startswith('file://'): self._filename = fname > print '_read: %s | %s | %s |' % (self._url,fname,self._filename) > try: > self._content = open(fname).read() > except IOError: > pass > > def _url2filename(self, url): > '''convert url to local filename if possible''' > if url.startswith('file://'): return url[7:] > elif url.startswith('note://'): return url.replace('note://', > self._app.rcdir+'/') > else: return None > > def _save(self): > '''save the content to file''' > start = self._buffer.get_start_iter() > end = self._buffer.get_end_iter() > self._content = content = self._buffer.get_text(start, end, > include_hidden_chars=True) > fname = self._url2filename(self._url) > print 'writing to %s...' % fname > open(fname, 'w').write(content) > > > def _set_title(self): > title = __app__ + ' ' + __version__ > if self._filename: > home = os.environ['HOME'] > name = self._filename.replace(home, '~') > title = '%s (%s)' % (name, title) > print 'setting title to', title > self._window.set_title(title) > > def _make_widgets(self): > '''create graphical user interface components''' > self._window = window = gtk.Window(gtk.WINDOW_TOPLEVEL) > self._set_title() > window.connect('delete_event', self.cb_close) > #window.connect('leave_notify_event', self.cb_quicksave) > window.connect('configure_event', self.cb_change_window) > #window.set_border_width(3) > if self._conf['sticky'] == 'yes': window.stick() > geom = self._conf['geometry'] > if geom: > mat = self.geomre.match(geom) > if mat: > width, height, x, y = map(int, > [mat.group(i) for i in [1,2,3,4]]) > self._window.resize(width, height) > self._window.move(x,y) > > # the main window > mainbox = gtk.VBox() > window.add(mainbox) > > # menubar in > menubox = gtk.HBox() > mainbox.pack_start(menubox, expand=False) > > menu_items = ( > (_('/_File/_New'), '<control>N', self.cb_new, 0, None), > (_('/_File/_Open'), '<control>O', self.cb_open, 0, None), > (_('/File/sep1'), None, None, 0, '<Separator>' ), > (_('/_File/_Save'), '<control>S', self.cb_save, 0, None), > (_('/_File/Save _As'), '<shift><control>S', self.cb_saveas, 0, > None), > (_('/File/sep2'), None, None, 0, '<Separator>' ), > (_('/_File/_Close'), '<control>W', self.cb_close, 0, None), > (_('/_File/_Quit'), '<control>Q', self.cb_quit, 0, None), > (_('/_Option/_Font'), None, self.cb_choose_font, 0, None), > (_('/_Option/_Background'), None, self.cb_choose_bg, 0, None), > (_('/_Help'), None, None, 0, '<LastBranch>'), > (_('/_Help/_Content'), 'F1', self.cb_show_help, 0, None), > (_('/_Help/_About'), None, self.cb_show_about, 0, None), > ) > accel_group = gtk.AccelGroup() > item_factory = gtk.ItemFactory(gtk.MenuBar, > '<main>', accel_group) > item_factory.create_items(menu_items) > window.add_accel_group(accel_group) > self._menubar = menubar = item_factory.get_widget('<main>') > menubox.pack_start(menubar, True, True, 0) > #menubar.modify_bg(gtk.STATE_NORMAL, color) > menubar.show() > menubox.show() > > #--- the note display: TextView in ScrolledWindow in Window > swin = gtk.ScrolledWindow() > swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) > mainbox.pack_start(swin, expand=True) > > self._textview = textview = gtk.TextView() > swin.add_with_viewport(textview) > self._set_font(self._conf['font']) > textview.show() > > textview.set_editable(True) > textview.set_wrap_mode(gtk.WRAP_WORD) > self._buffer = buffer = textview.get_buffer() > buffer.set_text(self._content) > swin.show() > > mainbox.show() > > # finalize the GUI > window.set_default_size(300,400) > > color = gtk.gdk.color_parse(self._conf['background']) > print '_make_widget: color = #%04X%04X%04X' %(color.red, > color.green, color.blue) > self._set_bg(color) > > window.show() > > def _set_font(self, fontname): > pangoFont = pango.FontDescription(fontname) > self._textview.modify_font(pangoFont) > self._conf['font'] = fontname > > def _set_bg(self, color): > self._textview.modify_base(gtk.STATE_NORMAL, color) > self._menubar.modify_bg(gtk.STATE_NORMAL, color) > self._conf['background'] = '#%04X%04X%04X' % (color.red, > color.green, color.blue) > > def cb_choose_font(self, *args): > '''let the user choose a font''' > da = gtk.FontSelectionDialog(_('Select a font')) > response = da.run() > fontname = da.get_font_name() > print 'you chose', fontname, 'response =', response > da.destroy() > if response == gtk.RESPONSE_CANCEL: > return True > elif response == gtk.RESPONSE_OK: > self._set_font(fontname) > #elif response == gtk.RESPONSE_ACCEPT: return filename > else: print 'unknown response', response > > def cb_choose_bg(self, *args): > '''let the user choose a background color''' > da = gtk.ColorSelectionDialog(_('Select background color')) > response = da.run() > color = da.colorsel.get_current_color() > print 'you chose', color, 'response =', response > da.destroy() > if response == gtk.RESPONSE_CANCEL: > return True > elif response == gtk.RESPONSE_OK: > self._set_bg(color) > else: print 'unknown response', response > > def cb_new(self, *args): > self._app.new_note() > > def cb_open(self, *args): > '''callback: File/Open''' > filename = AskFilename(self._window) > url = 'file://' + filename > self._app.open_note(url) > > def cb_editoptions(self, *args): > pass > > def cb_change_window(self, *args): > event = args[1] > #print 'cb_change_window: (w,h,x,y) =', event.width, > event.height, event.x, event.y > self._conf['geometry'] = '%dx%d+%d+%d' % (event.width, > event.height, event.x, event.y) > return False > > def cb_quicksave(self, *args): > '''save the current note contents when filename is known''' > self._save() > > def cb_save(self, *args): > '''save the current note contents, ask for filename if needed''' > #if not self.filename: # no name yet, can't save > # self.filename = AskFilename(self.window) > # if not self.filename: # user did not give a filename > # return True > # self._set_title() > self._save() > > def cb_saveas(self, *args): > '''save the current note contents, always ask for filename''' > self._filename = AskFilename(self._window) > if not self._filename: # user did not give a filename > return True > self._save() > self._set_title() > > def cb_close(self,*args): > '''close the note window''' > ## give user a chance to regret > #if not self._filename: > # wanttosave = AskSaveOrNot(self._window) > # if wanttosave == 1: # yes! > # self._filename = AskFilename(self._window) > # #self.filename = raw_input('filename: ') > # if not self._filename: return True > # self._save() > # elif wanttosave == 0: # no! go ahead delete this > # pass > # elif wanttosave == -1: # cancel > # return True # don't pass this event on > #else: > self._save() > #print 'destroying the window...' > self._close() > > def cb_show_help(self, *args): > '''Show help messages''' > hw = HelpWindow(self._window) > hw.show() > > def cb_show_about(self, *args): > '''Show help/about messages''' > da = AboutDialog(title=_('About Stix'), parent = self._window) > da.show() > > def cb_quit(self,*args): > '''terminate the application''' > self._save() > #app = StixApp() > self._app.quit() > > class HelpWindow(gtk.Dialog): > '''The window that shows help messages''' > def __init__(self, parent): > help_text = '... not written yet ...' > gtk.Dialog.__init__(self, > title='Help Message of %s %s' % (__app__, __version__), > parent=parent, buttons = ( _('OK'), gtk.RESPONSE_OK) ) > self.set_default_response(gtk.RESPONSE_OK) > # the top part: message... > swin = gtk.ScrolledWindow() > swin.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) > self.vbox.pack_start(swin, expand=True) > textview = gtk.TextView() > textview.set_editable(False) > swin.add_with_viewport(textview) > textview.show() > swin.show() > buffer = textview.get_buffer() > buffer.set_text(help_text) > self.connect('response', lambda x,y: self.destroy()) > self.resize(220, 280) > > class AboutDialog(gtk.Dialog): > '''The dialog to display Help/About stuff''' > def __init__(self, title='', parent=None): > gtk.Dialog.__init__(self,title, parent, flags=0, > buttons = ( _('Credits'), gtk.RESPONSE_HELP, > _('OK'), gtk.RESPONSE_OK) ) > self.set_default_response(gtk.RESPONSE_OK) > # the top part: icon + name + version > hbox = gtk.HBox() > self.vbox.pack_start(hbox, expand=False) > icon = gtk.Image() > icon.set_from_file('stix_icon.png') > hbox.pack_start(icon, expand=False) > icon.show() > label = gtk.Label(_('Stix '+__version__)) > hbox.pack_start(label, expand=True) > label.show() > hbox.show() > label = gtk.Label(_('Copyright (c) 2005 Ping Yeh')) > self.vbox.pack_start(label, expand=False) > label.show() > self.connect('response', self.cb_response) > def cb_response(self, *args): > #print 'cb_response:', args > rid = args[1] > if rid == gtk.RESPONSE_OK: > self.destroy() > elif rid == gtk.RESPONSE_HELP: > print 'showing credits...' > da = CreditsDialog(title='credits towards %s %s' % > (__app__, __version__), parent=self) > da.show() > > class CreditsDialog(gtk.Dialog): > '''The dialog to show credits''' > def __init__(self, title='', parent=None): > gtk.Dialog.__init__(self, title, parent, flags=0, > buttons = ( _('OK'), gtk.RESPONSE_OK) ) > credit_text = _('''Author: Ping Yeh > Translator: None so far > Icon Design: Ping Yeh''') > label = gtk.Label(credit_text) > self.vbox.pack_start(label, expand=True) > label.show() > self.connect('response', lambda x,y: self.destroy()) > > def AskFilename(parent): > da = gtk.FileChooserDialog(parent=parent, > action=gtk.FILE_CHOOSER_ACTION_SAVE, > buttons = ( _('Save'), gtk.RESPONSE_ACCEPT, > _('Cancel'), gtk.RESPONSE_CANCEL) ) > da.set_default_response(gtk.RESPONSE_ACCEPT) > #gtk.FileSelection() > response = da.run() > filename = da.get_filename() > print 'AskFilename: response =', response, 'filename =', filename > da.destroy() > if response == gtk.RESPONSE_CANCEL: # pressing cancel means cancel > return None # even if a name is typed > elif response == gtk.RESPONSE_ACCEPT: return filename > else: print 'unknown response', response > > def AskSaveOrNot(parent): > da = gtk.MessageDialog(parent, message_format=_('Save contents?')) > da.add_buttons( _('Save'), gtk.RESPONSE_YES, > _("Don't Save"), gtk.RESPONSE_NO, > _('Cancel'), gtk.RESPONSE_CANCEL ) > response = da.run() > da.destroy() > if response == gtk.RESPONSE_YES: > return 1 > elif response == gtk.RESPONSE_NO: > return 0 > elif response == gtk.RESPONSE_CANCEL: > return -1 > > def start(): > app = StixApp() > gtk.main() > > if __name__ == '__main__': > start() > #a = StixConfig('/home/pyeh/.stix/stix.conf') > #a.write('test.conf') > > > begin:vcard > fn:Johan Dahlin > n:Dahlin;Johan > org:Async Open Source;Development > adr;quoted-printable:Jardim Macarengo;;Rua Orlando Damiano, 2212;S=C3=A3o > Carlos;SP;13560-450;Brazil > email;internet:[EMAIL PROTECTED] > title:Software developer > tel;work:+55 16 3376 0125 > tel;fax:+55 16 3501 5394 > tel;home:+55 16 3501 5332 > tel;cell:+55 16 9112 6219 > x-mozilla-html:FALSE > url:http://www.async.com.br > version:2.1 > end:vcard > > _______________________________________________ > pygtk mailing list [email protected] > http://www.daa.com.au/mailman/listinfo/pygtk > Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/ -- La instrucción en 0x0054f580 hace referencia a la memoria en 0x00000000. La memoria no se puede "read". Haga clic en aceptar para finalizar este programa. --- V I S U A L T O O L S Carlos Rivera Cordero R&D Department / Departamento de I+D C/Isla Graciosa, 1. 28034 Madrid - Spain Telephone: +34 91 72948 44 Fax: +34 91 358 52 36 [EMAIL PROTECTED]
signature.asc
Description: Digital signature
_______________________________________________ pygtk mailing list [email protected] http://www.daa.com.au/mailman/listinfo/pygtk Read the PyGTK FAQ: http://www.async.com.br/faq/pygtk/
