Erbt man von der Klasse ``AutoSizeSave``, dann wird automatisch jede Größenänderung des Fensters in eine INI-Datei geschrieben. Die Größe des Fensters wird in der INI nur dann geändert, wenn das Fenster nicht maximiert ist. Ist das Fenster maximiert, dann wird diese Information natürlich auch in die INI-Datei geschrieben.
Erbt man von der Klasse ``AutoPositionSave``, dann wir automatisch jede Positionsänderung des Fensters in eine INI-Datei geschrieben. Existiert die INI-Datei noch nicht, dann wird das Fenster automatisch zentriert.
Um die Ressourcen zu schonen, wird nur alle 1,3 Sekunden geprüft, ob sich etwas geändert hat. Nur bei Änderung der Position oder der Größe wird die INI-Datei neu geschrieben.
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
"""
Automatisches Speichern der Position und Größe von wxPython-Fenstern
Created: 2007-02-24 by Gerold -- http://gerold.bcom.at/
Requirements: Python: http://www.python.org/
wxPython: http://wxpython.org/
-----------------------------------------------------
Die Verwendung von AutoSizeSave and AutoPositionSave
-----------------------------------------------------
Wenn man von beiden Klassen erben möchte, dann muss man darauf achten,
dass zuerst die AutoSizeSave-Klasse initialisiert wird und dann erst
die AutoPositionSave-Klasse.
Es ist ebenfalls wichtig, dass der Anwendung ein "eindeutiger" Name
gegeben wird, da dieser Name bestimmt, in welchem Ordner die
Einstellungen gespeichert werden. Ein nicht eindeutiger Name
würde dazu führen, dass sich die Anwendungen die Einstellungen
gegenseitig überschreiben.
::
import wx
wx.SetDefaultPyEncoding("iso-8859-1")
...
class MainFrame(wx.Frame, AutoSizeSave, AutoPositionSave):
def __init__(self, parent = None, id = -1, title = "My Application"):
wx.Frame.__init__(self, parent, id, title)
AutoSizeSave.__init__(self, autosave = True)
AutoPositionSave.__init__(self, autosave = True)
...
...
app = wx.PySimpleApp()
app.SetAppName("MySaveDemo")
f = MainFrame()
f.Show()
app.MainLoop()
"""
__all__ = ["AutoSizeSave", "AutoPositionSave"]
import os
import sys
import wx
import ConfigParser
class AutoSizeSave(object):
"""
Speichert die Fenstergröße automatisch, wenn diese Klasse beerbt wird.
"""
# Diese Werte werden beim Speichern neu gesetzt
_ass_old_w = None
_ass_old_h = None
_ass_old_maximized = None
AUTOSAVE_MILLISECONDS = 1300
def __init__(self, appname = None, containername = None, autosave = True):
"""
Init
:param appname: Name der Anwendung. Dieser ist wichtig, um den richtigen
Pfad zu ermitteln.
:param containername: Name des Fensters. Dieser ist wichtig, um den
richtigen Pfad zu ermitteln.
:param autosave: Wenn True, dann wird versucht, an ``self`` einen Timer
anzuhängen, der alle paar Sekunden die aktuelle Größe speichert, falls
sich diese inzwischen geändert hat.
"""
assert(isinstance(self, wx.TopLevelWindow))
# Standard-Pfade
standard_paths = wx.StandardPaths.Get()
# App-Name
if appname is None:
app = wx.GetApp()
appname = app.GetAppName()
# Container-Name
if containername is None:
containername = self.__class__.__name__
# UserLocalDataDir
if not sys.platform.startswith("win"):
appname = "." + appname.rstrip(".")
user_local_data_dir = os.path.join(
standard_paths.GetUserConfigDir(), appname
)
# Einstellungsordner erstellen, falls dieser noch nicht existiert
if not os.path.isdir(user_local_data_dir):
os.makedirs(user_local_data_dir)
# INI
filename = "saved_size_%s.ini" % containername.replace(" ", "_")
self._ass_ini_filename = os.path.join(user_local_data_dir, filename)
self._ass_ini = ConfigParser.SafeConfigParser()
self._ass_ini.read(self._ass_ini_filename)
if not self._ass_ini.has_section("SIZE"):
self._ass_ini.add_section("SIZE")
# Alte Größe wieder herstellen
if (
self._ass_ini.has_option("SIZE", "W") and
self._ass_ini.has_option("SIZE", "H") and
self._ass_ini.has_option("SIZE", "MAXIMIZED")
):
w = self._ass_ini.getint("SIZE", "W")
h = self._ass_ini.getint("SIZE", "H")
maximized = self._ass_ini.getboolean("SIZE", "MAXIMIZED")
self._ass_old_w = w
self._ass_old_h = h
self._ass_old_maximized = maximized
self.SetSize(wx.Size(w, h))
wx.YieldIfNeeded()
if maximized:
# WICHTIG! Mit CallAfter aufrufen!
wx.CallAfter(self.Maximize, (True))
# Events binden
self.Bind(wx.EVT_SIZE, self._ass_on_size)
# Timer an "self" anhängen
if autosave:
self._ass_timer = wx.Timer()
self._ass_timer.Bind(wx.EVT_TIMER, self.save_size)
self._ass_timer.Start(self.AUTOSAVE_MILLISECONDS) # alle x sec.
def _ass_on_size(self, event):
event.Skip()
size = self.GetSize()
maximized = self.IsMaximized()
self._ass_ini.set("SIZE", "MAXIMIZED", maximized and "TRUE" or "FALSE")
if not maximized:
self._ass_ini.set("SIZE", "W", str(size[0]))
self._ass_ini.set("SIZE", "H", str(size[1]))
def save_size(self, event = None, force = False):
"""
Speichert die Größe in die INI-Datei
"""
# Aktuelle Werte einlesen und evt. neu in INI setzen
try:
maximized = self._ass_ini.getboolean("SIZE", "MAXIMIZED")
except ConfigParser.NoOptionError:
maximized = self.IsMaximized()
self._ass_ini.set("SIZE", "MAXIMIZED", maximized and "TRUE" or "FALSE")
try:
w = self._ass_ini.getint("SIZE", "W")
except ConfigParser.NoOptionError:
if not maximized:
size = self.GetSize()
w = size[0]
else:
w = "-1"
self._ass_ini.set("SIZE", "W", str(w))
try:
h = self._ass_ini.getint("SIZE", "H")
except ConfigParser.NoOptionError:
if not maximized:
size = self.GetSize()
h = size[1]
else:
h = "-1"
self._ass_ini.set("SIZE", "H", str(h))
# Prüfen ob gespeichert werden muss
if not force:
if (
(w == self._ass_old_w) and
(h == self._ass_old_h) and
(maximized == self._ass_old_maximized)
):
# Es muss nichts gespeichert werden --> Abbruch
return
# Speichern
f = file(self._ass_ini_filename, "w")
self._ass_ini.write(f)
f.close()
# Alte Werte erneuern
self._ass_old_w = self._ass_ini.getint("SIZE", "W")
self._ass_old_h = self._ass_ini.getint("SIZE", "H")
self._ass_old_maximized = self._ass_ini.getboolean("SIZE", "MAXIMIZED")
class AutoPositionSave(object):
"""
Speichert die Fensterposition automatisch, wenn diese Klasse beerbt wird.
"""
# Diese Werte werden beim Speichern neu gesetzt
_aps_old_x = None
_aps_old_y = None
AUTOSAVE_MILLISECONDS = 1300
def __init__(self, appname = None, containername = None, autosave = True):
"""
Init
:param appname: Name der Anwendung. Dieser ist wichtig, um den richtigen
Pfad zu ermitteln.
:param containername: Name des Fensters. Dieser ist wichtig, um den
richtigen Pfad zu ermitteln.
:param autosave: Wenn True, dann wird versucht, an ``self`` einen Timer
anzuhängen, der alle paar Sekunden die aktuelle Position speichert, falls
sich diese inzwischen geändert hat.
"""
assert isinstance(self, wx.TopLevelWindow)
# Standard-Pfade
standard_paths = wx.StandardPaths.Get()
# App-Name
if not appname:
app = wx.GetApp()
appname = app.GetAppName()
# Container-Name
if containername is None:
containername = self.__class__.__name__
# UserLocalDataDir
if not sys.platform.startswith("win"):
appname = "." + appname.rstrip(".")
user_local_data_dir = os.path.join(
standard_paths.GetUserConfigDir(), appname
)
# Einstellungsordner erstellen, falls dieser noch nicht existiert
if not os.path.isdir(user_local_data_dir):
os.makedirs(user_local_data_dir)
# INI
filename = "saved_position_%s.ini" % containername.replace(" ", "_")
self._aps_ini_filename = os.path.join(user_local_data_dir, filename)
self._aps_ini = ConfigParser.SafeConfigParser()
self._aps_ini.read(self._aps_ini_filename)
if not self._aps_ini.has_section("POS"):
self._aps_ini.add_section("POS")
# Alte Position wieder herstellen
if (
self._aps_ini.has_option("POS", "X") and
self._aps_ini.has_option("POS", "Y")
):
x = self._aps_ini.getint("POS", "X")
y = self._aps_ini.getint("POS", "Y")
self._aps_old_x = x
self._aps_old_y = y
self.SetPosition(wx.Point(x, y))
else:
# Zentrieren, da nichts in der INI steht
if not self.IsMaximized():
self.Center()
# Event(s) binden
self.Bind(wx.EVT_MOVE, self._aps_on_move)
# Timer an "self" anhängen, der alle paar Sekunden ein
# ``save_position`` auslöst.
if autosave:
self._aps_timer = wx.Timer()
self._aps_timer.Bind(wx.EVT_TIMER, self.save_position)
self._aps_timer.Start(self.AUTOSAVE_MILLISECONDS) # alle x sec.
def _aps_on_move(self, event):
event.Skip()
pos = self.GetPosition()
maximized = self.IsMaximized()
if not maximized:
self._aps_ini.set("POS", "X", str(pos[0]))
self._aps_ini.set("POS", "Y", str(pos[1]))
def save_position(self, event = None, force = False):
"""
Speichert die Positionseinstellungen in die INI-Datei
"""
# Aktuelle Werte einlesen und evt. neu in INI setzen
try:
x = self._aps_ini.getint("POS", "X")
except ConfigParser.NoOptionError:
pos = self.GetPosition()
x = pos[0]
self._aps_ini.set("POS", "X", str(pos[0]))
try:
y = self._aps_ini.getint("POS", "Y")
except ConfigParser.NoOptionError:
pos = self.GetPosition()
y = pos[1]
self._aps_ini.set("POS", "Y", str(pos[1]))
# Prüfen ob gespeichert werden muss
if not force:
if (
(x == self._aps_old_x) and
(y == self._aps_old_y)
):
# Es muss nichts gespeichert werden --> Abbruch
return
# Speichern
f = file(self._aps_ini_filename, "w")
self._aps_ini.write(f)
f.close()
# Alte Werte erneuern
self._aps_old_x = self._aps_ini.getint("POS", "X")
self._aps_old_y = self._aps_ini.getint("POS", "Y")
class TestFrame(wx.Frame, AutoSizeSave, AutoPositionSave):
def __init__(self, parent = None, id = -1, title = "Testfenster"):
wx.Frame.__init__(self, parent, id, title)
AutoSizeSave.__init__(self)
AutoPositionSave.__init__(self)
def main():
"""
Hauptprozedur
"""
app = wx.PySimpleApp()
app.SetAppName("Python AutoSave-Example")
f = TestFrame()
f.Show()
app.MainLoop()
if __name__ == "__main__":
main()
Gerold
Edit: Code ein wenig verbessert.
Edit: AUTOSAVE_MILLISECONDS auf 1300 gesetzt.
Edit: Auf NICHT-Windows-Systemen wird ein Punkt vor dem Anwendungsnamen gesetzt.