initial commit

This commit is contained in:
Thomas Vogl 2022-12-17 00:35:20 +01:00
commit ad5773abfc
10 changed files with 1275 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
**/*.pyc

616
appdirs.py Normal file
View File

@ -0,0 +1,616 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2005-2010 ActiveState Software Inc.
# Copyright (c) 2013 Eddy Petrișor
"""Utilities for determining application-specific dirs.
See <https://github.com/ActiveState/appdirs> for details and usage.
"""
# Dev Notes:
# - MSDN on where to store app data files:
# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120
# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html
# - XDG spec for Un*x: https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
__version__ = "1.4.4"
__version_info__ = tuple(int(segment) for segment in __version__.split("."))
import sys
import os
PY3 = sys.version_info[0] == 3
if PY3:
unicode = str
if sys.platform.startswith('java'):
import platform
os_name = platform.java_ver()[3][0]
if os_name.startswith('Windows'): # "Windows XP", "Windows 7", etc.
system = 'win32'
elif os_name.startswith('Mac'): # "Mac OS X", etc.
system = 'darwin'
else: # "Linux", "SunOS", "FreeBSD", etc.
# Setting this to "linux2" is not ideal, but only Windows or Mac
# are actually checked for and the rest of the module expects
# *sys.platform* style strings.
system = 'linux2'
else:
system = sys.platform
def user_data_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user data directories are:
Mac OS X: ~/Library/Application Support/<AppName>
Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined
Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName>
Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>
Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>
Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName>
For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
That means, by default "~/.local/share/<AppName>".
"""
if system == "win32":
if appauthor is None:
appauthor = appname
const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA"
path = os.path.normpath(_get_win_folder(const))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
elif system == 'darwin':
path = os.path.expanduser('~/Library/Application Support/')
if appname:
path = os.path.join(path, appname)
else:
path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def site_data_dir(appname=None, appauthor=None, version=None, multipath=False):
r"""Return full path to the user-shared data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"multipath" is an optional parameter only applicable to *nix
which indicates that the entire list of data dirs should be
returned. By default, the first item from XDG_DATA_DIRS is
returned, or '/usr/local/share/<AppName>',
if XDG_DATA_DIRS is not set
Typical site data directories are:
Mac OS X: /Library/Application Support/<AppName>
Unix: /usr/local/share/<AppName> or /usr/share/<AppName>
Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName>
Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7.
For Unix, this is using the $XDG_DATA_DIRS[0] default.
WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
"""
if system == "win32":
if appauthor is None:
appauthor = appname
path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA"))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
elif system == 'darwin':
path = os.path.expanduser('/Library/Application Support')
if appname:
path = os.path.join(path, appname)
else:
# XDG default for $XDG_DATA_DIRS
# only first, if multipath is False
path = os.getenv('XDG_DATA_DIRS',
os.pathsep.join(['/usr/local/share', '/usr/share']))
pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)]
if appname:
if version:
appname = os.path.join(appname, version)
pathlist = [os.sep.join([x, appname]) for x in pathlist]
if multipath:
path = os.pathsep.join(pathlist)
else:
path = pathlist[0]
return path
if appname and version:
path = os.path.join(path, version)
return path
def user_config_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific config dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user config directories are:
Mac OS X: ~/Library/Preferences/<AppName>
Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined
Win *: same as user_data_dir
For Unix, we follow the XDG spec and support $XDG_CONFIG_HOME.
That means, by default "~/.config/<AppName>".
"""
if system == "win32":
path = user_data_dir(appname, appauthor, None, roaming)
elif system == 'darwin':
path = os.path.expanduser('~/Library/Preferences/')
if appname:
path = os.path.join(path, appname)
else:
path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def site_config_dir(appname=None, appauthor=None, version=None, multipath=False):
r"""Return full path to the user-shared data dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"multipath" is an optional parameter only applicable to *nix
which indicates that the entire list of config dirs should be
returned. By default, the first item from XDG_CONFIG_DIRS is
returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set
Typical site config directories are:
Mac OS X: same as site_data_dir
Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in
$XDG_CONFIG_DIRS
Win *: same as site_data_dir
Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False
WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
"""
if system == 'win32':
path = site_data_dir(appname, appauthor)
if appname and version:
path = os.path.join(path, version)
elif system == 'darwin':
path = os.path.expanduser('/Library/Preferences')
if appname:
path = os.path.join(path, appname)
else:
# XDG default for $XDG_CONFIG_DIRS
# only first, if multipath is False
path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg')
pathlist = [os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep)]
if appname:
if version:
appname = os.path.join(appname, version)
pathlist = [os.sep.join([x, appname]) for x in pathlist]
if multipath:
path = os.pathsep.join(pathlist)
else:
path = pathlist[0]
return path
def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True):
r"""Return full path to the user-specific cache dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"opinion" (boolean) can be False to disable the appending of
"Cache" to the base app data dir for Windows. See
discussion below.
Typical user cache directories are:
Mac OS X: ~/Library/Caches/<AppName>
Unix: ~/.cache/<AppName> (XDG default)
Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
On Windows the only suggestion in the MSDN docs is that local settings go in
the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming
app data dir (the default returned by `user_data_dir` above). Apps typically
put cache data somewhere *under* the given dir here. Some examples:
...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
...\Acme\SuperApp\Cache\1.0
OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
This can be disabled with the `opinion=False` option.
"""
if system == "win32":
if appauthor is None:
appauthor = appname
path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA"))
if appname:
if appauthor is not False:
path = os.path.join(path, appauthor, appname)
else:
path = os.path.join(path, appname)
if opinion:
path = os.path.join(path, "Cache")
elif system == 'darwin':
path = os.path.expanduser('~/Library/Caches')
if appname:
path = os.path.join(path, appname)
else:
path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def user_state_dir(appname=None, appauthor=None, version=None, roaming=False):
r"""Return full path to the user-specific state dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"roaming" (boolean, default False) can be set True to use the Windows
roaming appdata directory. That means that for users on a Windows
network setup for roaming profiles, this user data will be
sync'd on login. See
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
for a discussion of issues.
Typical user state directories are:
Mac OS X: same as user_data_dir
Unix: ~/.local/state/<AppName> # or in $XDG_STATE_HOME, if defined
Win *: same as user_data_dir
For Unix, we follow this Debian proposal <https://wiki.debian.org/XDGBaseDirectorySpecification#state>
to extend the XDG spec and support $XDG_STATE_HOME.
That means, by default "~/.local/state/<AppName>".
"""
if system in ["win32", "darwin"]:
path = user_data_dir(appname, appauthor, None, roaming)
else:
path = os.getenv('XDG_STATE_HOME', os.path.expanduser("~/.local/state"))
if appname:
path = os.path.join(path, appname)
if appname and version:
path = os.path.join(path, version)
return path
def user_log_dir(appname=None, appauthor=None, version=None, opinion=True):
r"""Return full path to the user-specific log dir for this application.
"appname" is the name of application.
If None, just the system directory is returned.
"appauthor" (only used on Windows) is the name of the
appauthor or distributing body for this application. Typically
it is the owning company name. This falls back to appname. You may
pass False to disable it.
"version" is an optional version path element to append to the
path. You might want to use this if you want multiple versions
of your app to be able to run independently. If used, this
would typically be "<major>.<minor>".
Only applied when appname is present.
"opinion" (boolean) can be False to disable the appending of
"Logs" to the base app data dir for Windows, and "log" to the
base cache dir for Unix. See discussion below.
Typical user log directories are:
Mac OS X: ~/Library/Logs/<AppName>
Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined
Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
On Windows the only suggestion in the MSDN docs is that local settings
go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in
examples of what some windows apps use for a logs dir.)
OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA`
value for Windows and appends "log" to the user cache dir for Unix.
This can be disabled with the `opinion=False` option.
"""
if system == "darwin":
path = os.path.join(
os.path.expanduser('~/Library/Logs'),
appname)
elif system == "win32":
path = user_data_dir(appname, appauthor, version)
version = False
if opinion:
path = os.path.join(path, "Logs")
else:
path = user_cache_dir(appname, appauthor, version)
version = False
if opinion:
path = os.path.join(path, "log")
if appname and version:
path = os.path.join(path, version)
return path
class AppDirs(object):
"""Convenience wrapper for getting application dirs."""
def __init__(self, appname=None, appauthor=None, version=None,
roaming=False, multipath=False):
self.appname = appname
self.appauthor = appauthor
self.version = version
self.roaming = roaming
self.multipath = multipath
@property
def user_data_dir(self):
return user_data_dir(self.appname, self.appauthor,
version=self.version, roaming=self.roaming)
@property
def site_data_dir(self):
return site_data_dir(self.appname, self.appauthor,
version=self.version, multipath=self.multipath)
@property
def user_config_dir(self):
return user_config_dir(self.appname, self.appauthor,
version=self.version, roaming=self.roaming)
@property
def site_config_dir(self):
return site_config_dir(self.appname, self.appauthor,
version=self.version, multipath=self.multipath)
@property
def user_cache_dir(self):
return user_cache_dir(self.appname, self.appauthor,
version=self.version)
@property
def user_state_dir(self):
return user_state_dir(self.appname, self.appauthor,
version=self.version)
@property
def user_log_dir(self):
return user_log_dir(self.appname, self.appauthor,
version=self.version)
#---- internal support stuff
def _get_win_folder_from_registry(csidl_name):
"""This is a fallback technique at best. I'm not sure if using the
registry for this guarantees us the correct answer for all CSIDL_*
names.
"""
if PY3:
import winreg as _winreg
else:
import _winreg
shell_folder_name = {
"CSIDL_APPDATA": "AppData",
"CSIDL_COMMON_APPDATA": "Common AppData",
"CSIDL_LOCAL_APPDATA": "Local AppData",
}[csidl_name]
key = _winreg.OpenKey(
_winreg.HKEY_CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
)
dir, type = _winreg.QueryValueEx(key, shell_folder_name)
return dir
def _get_win_folder_with_pywin32(csidl_name):
from win32com.shell import shellcon, shell
dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0)
# Try to make this a unicode path because SHGetFolderPath does
# not return unicode strings when there is unicode data in the
# path.
try:
dir = unicode(dir)
# Downgrade to short path name if have highbit chars. See
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
has_high_char = False
for c in dir:
if ord(c) > 255:
has_high_char = True
break
if has_high_char:
try:
import win32api
dir = win32api.GetShortPathName(dir)
except ImportError:
pass
except UnicodeError:
pass
return dir
def _get_win_folder_with_ctypes(csidl_name):
import ctypes
csidl_const = {
"CSIDL_APPDATA": 26,
"CSIDL_COMMON_APPDATA": 35,
"CSIDL_LOCAL_APPDATA": 28,
}[csidl_name]
buf = ctypes.create_unicode_buffer(1024)
ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
# Downgrade to short path name if have highbit chars. See
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
has_high_char = False
for c in buf:
if ord(c) > 255:
has_high_char = True
break
if has_high_char:
buf2 = ctypes.create_unicode_buffer(1024)
if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
buf = buf2
return buf.value
def _get_win_folder_with_jna(csidl_name):
import array
from com.sun import jna
from com.sun.jna.platform import win32
buf_size = win32.WinDef.MAX_PATH * 2
buf = array.zeros('c', buf_size)
shell = win32.Shell32.INSTANCE
shell.SHGetFolderPath(None, getattr(win32.ShlObj, csidl_name), None, win32.ShlObj.SHGFP_TYPE_CURRENT, buf)
dir = jna.Native.toString(buf.tostring()).rstrip("\0")
# Downgrade to short path name if have highbit chars. See
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
has_high_char = False
for c in dir:
if ord(c) > 255:
has_high_char = True
break
if has_high_char:
buf = array.zeros('c', buf_size)
kernel = win32.Kernel32.INSTANCE
if kernel.GetShortPathName(dir, buf, buf_size):
dir = jna.Native.toString(buf.tostring()).rstrip("\0")
return dir
if system == "win32":
try:
import win32com.shell
_get_win_folder = _get_win_folder_with_pywin32
except ImportError:
try:
from ctypes import windll
_get_win_folder = _get_win_folder_with_ctypes
except ImportError:
try:
import com.sun.jna
_get_win_folder = _get_win_folder_with_jna
except ImportError:
_get_win_folder = _get_win_folder_from_registry
#---- self test code
if __name__ == "__main__":
appname = "MyApp"
appauthor = "MyCompany"
props = ("user_data_dir",
"user_config_dir",
"user_cache_dir",
"user_state_dir",
"user_log_dir",
"site_data_dir",
"site_config_dir")
print("-- app dirs %s --" % __version__)
print("-- app dirs (with optional 'version')")
dirs = AppDirs(appname, appauthor, version="1.0")
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (without optional 'version')")
dirs = AppDirs(appname, appauthor)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (without optional 'appauthor')")
dirs = AppDirs(appname)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))
print("\n-- app dirs (with disabled 'appauthor')")
dirs = AppDirs(appname, appauthor=False)
for prop in props:
print("%s: %s" % (prop, getattr(dirs, prop)))

78
calendar_main.py Normal file
View File

@ -0,0 +1,78 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import os
try:
import scribus
except ImportError:
print("This Python script is written for the Scribus scripting interface.")
print("It can only be run from within Scribus.")
sys.exit(1)
from scribus_calendar.config_parser import ConfigParser
from scribus_calendar.calendars import MonthlyCalendar
from scribus_calendar.monthly_objects import *
def class_from_name(className):
return globals()[className]
def main(argv):
objects = []
#compute config file path
thisDir = os.path.dirname(argv[0])
defaultConfigFile = os.path.join(thisDir, "test.json")
configParser = ConfigParser(defaultConfigFile)
#create objects from config file
strListModuleObjects = configParser.modules.keys()
for module in strListModuleObjects:
obj = class_from_name(module)()
obj.configure(configParser)
objects.append(obj)
#create calendar object from defined class in config
calendar = class_from_name(configParser.settings["calendarClass"])(objects, configParser.settings["year"])
calendar.createStyles()
calendar.createLayers()
if calendar.plotStubs():
scribus.messageBox('created objects','created objects. Please adjust its position and re-run script.' ,scribus.ICON_NONE, scribus.BUTTON_OK)
return
calendar.readFromStubs()
calendar.plotObjects()
def main_wrapper(argv):
"""The main_wrapper() function disables redrawing, sets a sensible generic
status bar message, and optionally sets up the progress bar. It then runs
the main() function. Once everything finishes it cleans up after the main()
function, making sure everything is sane before the script terminates."""
try:
scribus.statusMessage("Running script...")
scribus.progressReset()
main(argv)
finally:
# Exit neatly even if the script terminated with an exception,
# so we leave the progress bar and status bar blank and make sure
# drawing is enabled.
if scribus.haveDoc():
scribus.setRedraw(True)
scribus.statusMessage("")
scribus.progressReset()
# This code detects if the script is being run as a script, or imported as a module.
# It only runs main() if being run as a script. This permits you to import your script
# and control it manually for debugging.
if __name__ == '__main__':
main_wrapper(sys.argv)

View File

View File

@ -0,0 +1,167 @@
import scribus
import datetime
import calendar
class ScribusCalendar(object):
def __init__(self, objects, year):
self.objects = objects
self.year = year
def createStyles(self):
existing_styles = scribus.getAllStyles()
for obj in self.objects:
obj.createStyles(existing_styles)
def createLayers(self):
existingLayers = scribus.getLayers()
for obj in self.objects:
obj.createLayer(existingLayers)
def plotStubs(self):
created = False
for obj in self.objects:
created = obj.plotStub() or created
return created
def plotObjects(self):
pass
def readFromStubs(self):
for obj in self.objects:
obj.readFromStub()
class CalendarObject(object):
def __init__(self, name):
self.name = name
self.position = (0,0)
self.size = (100,100)
self.paragraphStyle = "DefaultCalenderParagraphStyle"
self.charStyle = "DefaultCalenderCharStyle"
self.layer = "CalendarLayer"
self.date = datetime.date.today()
def setDate(self, date):
self.date = date
def setDimension(self, pos, size):
self.position = pos
self.size = size
def createLayer(self, existingLayers):
if self.layer not in existingLayers:
scribus.createLayer(self.layer)
existingLayers.append(self.layer)
def createStyles(self, existingStyles):
if self.charStyle not in existingStyles:
scribus.createCharStyle(self.charStyle, "DejaVu Sans", 8)
existingStyles.append(self.charStyle)
if self.paragraphStyle not in existingStyles:
scribus.createParagraphStyle(self.paragraphStyle ,1,0,0,0,0,0,0,0,0,0,0,self.charStyle)
existingStyles.append(self.paragraphStyle)
def plotStub(self):
if not scribus.objectExists(self.name):
scribus.createText(self.position[0],self.position[1], self.size[0],self.size[1], self.name)
scribus.insertText(self.name, 0, self.name)
scribus.sentToLayer(self.layer, self.name)
return True
return False
def readFromStub(self):
p = scribus.getPosition(self.name)
s = scribus.getSize(self.name)
self.setDimension(p, s)
def plotObject(self):
pass
def configure(self, configParser):
className = self.__class__.__name__
settings = dict()
try:
settings = configParser.modules[className]
except KeyError:
return
for key, val in settings.items():
setattr(self,key, val)
class WeekDayNameObject(CalendarObject):
def __init__(self, name):
CalendarObject.__init__(self,name)
self.weekday = datetime.date.today().weekday()
def getName(self):
return calendar.day_name[self.date.weekday()]
def getNameAbbrev(self):
return calendar.day_abbr[self.date.weekday()]
class WeekNameObject(CalendarObject):
def __init__(self, name):
CalendarObject.__init__(self,name)
self.weeknumber = 1
def setDate(self, date):
CalendarObject.setDate(self, date)
self.weeknumber = self.date.isocalendar()[1]
def getName(self):
return str(self.weeknumber)
def getNameAbbrev(self):
return self.getName()
class DailyCalendarObject(CalendarObject):
def __init__(self, name):
CalendarObject.__init__(self,name)
self.isActive = True
def getName(self):
return str(self.date.day)
def setActive(self, state=True):
self.isActive = state
class MonthlyCalendarObject(CalendarObject):
def __init__(self, name):
CalendarObject.__init__(self,name)
def setDate(self, date):
CalendarObject.setDate(self, date)
self.month = self.date.month
def getName(self):
return calendar.month_name[self.month]
def getNameAbbrev(self):
return calendar.month_abbr[self.month]

View File

@ -0,0 +1,51 @@
import datetime
import calendar
class MonthCalendarMatrix(object):
def __init__(self, year, month, weeksPerRow, fixedRows=True):
self.weeksPerRow = weeksPerRow
self.firstDayInMonth = datetime.date(year,month,1)
maxDay = calendar.monthrange(self.firstDayInMonth.year, self.firstDayInMonth.month)[1]
self.lastDayInMonth = datetime.date(self.firstDayInMonth.year, self.firstDayInMonth.month, maxDay)
self.firstDayInMonth = self.firstDayInMonth - datetime.timedelta(days=self.firstDayInMonth.weekday())
self.lastDayInMonth = self.lastDayInMonth + datetime.timedelta(days=(6-self.lastDayInMonth.weekday()))
self.numDays = (self.lastDayInMonth - self.firstDayInMonth + datetime.timedelta(days=1)).days
self.numRows = int(self.numDays / ( 7 * self.weeksPerRow ))
if fixedRows:
if ( self.numRows < ( 6 / self.weeksPerRow) ):
self.firstDayInMonth = self.firstDayInMonth - datetime.timedelta(days=7)
self.numDays = (self.lastDayInMonth - self.firstDayInMonth + datetime.timedelta(days=1)).days
self.numRows = int(self.numDays / ( 7 * self.weeksPerRow ))
if ( self.numRows < ( 6 / self.weeksPerRow) ):
self.lastDayInMonth = self.lastDayInMonth + datetime.timedelta(days=7)
self.numDays = (self.lastDayInMonth - self.firstDayInMonth + datetime.timedelta(days=1)).days
self.numRows = int(self.numDays / ( 7 * self.weeksPerRow ))
def get(self):
out = []
tmp = []
num_rows = 0
for i in range(0, self.numDays):
tmp.append(self.firstDayInMonth + datetime.timedelta(days=i))
if i % ( 7 * self.weeksPerRow ) == ( 7 * self.weeksPerRow ) - 1:
out.append(tmp)
tmp = []
return out
if __name__ == "__main__":
x = MonthCalendarMatrix(2020,1,1,True)
for i in x.get():
print(len(i))

View File

@ -0,0 +1,23 @@
import scribus
import datetime
from .base_objects import ScribusCalendar
class MonthlyCalendar(ScribusCalendar):
def __init__(self, objects, year):
ScribusCalendar.__init__(self, objects, year)
def plotObjects(self):
numPages = scribus.pageCount()
if numPages > 1:
for i in range(1, numPages):
scribus.deletePage(i)
for i in range(1,2):
scribus.newPage(-1)
for obj in self.objects:
obj.setDate(datetime.date(self.year, i, 1))
obj.plotObject()

View File

@ -0,0 +1,82 @@
import json
import datetime
class ConfigParser(object):
def __init__(self, filename):
with open(filename, "r") as f:
self.jsonContent = json.load(f)
self.events = dict()
self.settings = dict()
self.__parseSettings()
self.__parseEvents()
self.__parseModuleSettings()
def __parseModuleSettings(self):
self.modules = self.jsonContent["modules"]
def __parseSettings(self):
self.settings = self.jsonContent["settings"]
def __parseDateRange(self, val ):
_from = self.__fromisoformat(val["from"])
_to = self.__fromisoformat(val["to"])
val["from"] = _from
val["to"] = _to
def __fromisoformat(self, val):
d = val.split("-")
if len(d) == 3:
return datetime.date(year=int(d[0]), month=int(d[1]), day=int(d[2]))
elif len(d) == 2:
return datetime.date(year=self.settings["year"], month=int(d[0]), day=int(d[1]))
def __parseEvents(self):
self.events = self.jsonContent["events"]
for key, val in self.events.items():
dates = val["dates"]
for ev in dates:
self.__parseDateRange(ev)
def printEvents(self):
for key, val in self.events.items():
dates = val["dates"]
print("%s\n================" % (val["name"]) )
for ev in dates:
print("from: %s until: %s ---> %s" % (ev["from"], ev["to"], ev["name"]) )
def getEvent(self, date, eventName=None):
for key,val in self.events.items():
if eventName:
if key == eventName:
for d in val["dates"]:
_from = d["from"]
_to = d["to"]
if date >= _from and date <= _to:
return (key, d)
else:
for d in val["dates"]:
_from = d["from"]
_to = d["to"]
if date >= _from and date <= _to:
return (key, d)
return None
if __name__ == "__main__":
c = ConfigParser("test.json")
startDate = datetime.date(2020,1,1)
for i in range(0,366):
d = startDate + datetime.timedelta(days=i)
ev = c.getEvent(d)
if ev: print(d, ev[0], ev[1]["name"])

View File

@ -0,0 +1,172 @@
from .base_objects import MonthlyCalendarObject, WeekDayNameObject, DailyCalendarObject, WeekNameObject
from .calendar_helpers import MonthCalendarMatrix
import calendar
import math
import copy
import datetime
import scribus
import json
def class_from_name(className):
return globals()[className]
class SimpleWeekDayNameObject(WeekDayNameObject):
def __init__(self):
WeekDayNameObject.__init__(self, "SimpleWeekDayNameObject")
def plotObject(self):
textBox = scribus.createText(self.position[0],self.position[1], self.size[0],self.size[1])
scribus.insertText(self.getNameAbbrev(), 0, textBox )
scribus.sentToLayer(self.layer, textBox)
class SimpleWeekNameObject(WeekNameObject):
def __init__(self):
WeekNameObject.__init__(self, "SimpleWeekNameObject")
def plotObject(self):
textBox = scribus.createText(self.position[0],self.position[1], self.size[0],self.size[1])
scribus.insertText(self.getNameAbbrev(), 0, textBox )
scribus.sentToLayer(self.layer, textBox)
class FormattedDayObject(DailyCalendarObject):
def __init__(self):
DailyCalendarObject.__init__(self,"FormattedDayObject")
self.paragraphStyle = "S"
def plotObject(self):
if self.isActive:
textBox = scribus.createText(self.position[0],self.position[1], self.size[0],self.size[1])
scribus.insertText(self.getName(), 0, textBox )
scribus.sentToLayer(self.layer, textBox)
class DayGridObject(DailyCalendarObject):
def __init__(self):
DailyCalendarObject.__init__(self,"DayGridObject")
def plotObject(self):
if self.isActive:
textBox = scribus.createText(self.position[0],self.position[1], self.size[0],self.size[1])
scribus.insertText(self.getName(), 0, textBox )
scribus.sentToLayer(self.layer, textBox)
class MonthlyGrid(MonthlyCalendarObject):
def __init__(self):
MonthlyCalendarObject.__init__(self, "MonthlyGrid")
self.numberOfWeeksPerLine = 1
self.oneLineOnly = False
self.heightColWeekNumber = 0.3
self.widthRowWeekName = 0.3
self.sizeCellDay = (1.0, 1.0)
self.scaleFactor = [1,1]
self.calendarWeekClass = None
self.weekDayClass = None
self.dayClass = None
self.createdObjects = []
self.setDate(datetime.date.today())
def setDimension(self, pos, size):
MonthlyCalendarObject.setDimension(self,pos,size)
self.computeScaleFactor()
def computeScaleFactor(self):
self.numCols = len(self.calendarMatrix)
unitsX = 1 * self.widthRowWeekName + len(self.calendarMatrix[0]) * self.sizeCellDay[0]
unitsY = ( 1 * self.heightColWeekNumber) + ( self.numCols * self.sizeCellDay[1] )
self.scaleFactor[0] = self.size[0] / unitsX
self.scaleFactor[1] = self.size[1] / unitsY
def setDate(self, date):
MonthlyCalendarObject.setDate(self, date)
if self.oneLineOnly:
self.calendarMatrix = MonthCalendarMatrix(self.date.year, self.date.month, 1, False).get()
t = [[]]
for i in self.calendarMatrix:
t[0] += i
self.calendarMatrix = t
self.numberOfWeeksPerLine = 1
else:
self.calendarMatrix = MonthCalendarMatrix(self.date.year, self.date.month, self.numberOfWeeksPerLine, True).get()
self.computeScaleFactor()
def plotObject(self):
self.createObjects()
for obj in self.createdObjects:
obj.plotObject()
def createObjects(self):
if type(self.weekDayClass) == unicode :
self.weekDayClass = class_from_name(self.weekDayClass)
if type(self.dayClass) == unicode:
self.dayClass = class_from_name(self.dayClass)
if type(self.calendarWeekClass) == unicode:
self.calendarWeekClass = class_from_name(self.calendarWeekClass)
self.createdObjects = []
w = self.position[0]
h = self.position[1]
for i in range(0, len(self.calendarMatrix[0])):
if self.calendarMatrix[0][i].weekday() == 0:
x = self.widthRowWeekName * self.scaleFactor[0]
w += x
weekDayObj = self.weekDayClass()
weekDayObj.setDate(self.calendarMatrix[0][i])
self.createdObjects.append(weekDayObj)
x = self.sizeCellDay[0] * self.scaleFactor[0]
y = self.heightColWeekNumber * self.scaleFactor[1]
weekDayObj.setDimension((w,h), (x,y))
w += x
w = self.position[0]
h += self.heightColWeekNumber * self.scaleFactor[1]
for i in self.calendarMatrix:
for j in range(0,len(i)):
if i[j].weekday() == 0:
calendarWeekObj = self.calendarWeekClass()
calendarWeekObj.setDate(i[j])
x = self.widthRowWeekName * self.scaleFactor[0]
y = self.sizeCellDay[1] * self.scaleFactor[1]
calendarWeekObj.setDimension((w,h), (x, y) )
w += x
self.createdObjects.append(calendarWeekObj)
dailyObj = self.dayClass()
dailyObj.setDate(i[j])
dailyObj.setActive(i[j].month == self.date.month)
x = self.sizeCellDay[0] * self.scaleFactor[0]
y = self.sizeCellDay[1] * self.scaleFactor[1]
dailyObj.setDimension((w, h), (x, y))
w += x
self.createdObjects.append(dailyObj)
h += self.sizeCellDay[1] * self.scaleFactor[1]
w = self.position[0]

85
test.json Normal file
View File

@ -0,0 +1,85 @@
{
"settings": {
"year": 2020,
"calendarClass": "MonthlyCalendar"
},
"modules": {
"MonthlyGrid": {
"numberOfWeeksPerLine": 1,
"oneLineOnly": true,
"heightColWeekNumber": 1.0,
"widthRowWeekName": 1.0,
"sizeCellDay": [1.0, 1.0],
"calendarWeekClass": "SimpleWeekNameObject",
"weekDayClass": "SimpleWeekDayNameObject",
"dayClass": "DayGridObject"
}
},
"events": {
"bankHolidays": {
"name": "Feiertage (Bayern)",
"format": {
"type": "style",
"charStyle": "FeiertageCharStyle",
"paragraphStyle": "FeiertageParagraphStyle"
},
"dates": [
{
"from": "01-01",
"to": "01-01",
"name": "Neujahr"
},
{
"from": "10-03",
"to": "10-03",
"name": "Tag der deutschen Einheit"
},
{
"from": "12-24",
"to": "12-24",
"name": "Heilig Abend"
},
{
"from": "12-25",
"to": "12-25",
"name": "1. Weihnachtsfeiertag"
},
{
"from": "12-26",
"to": "12-26",
"name": "2. Weihnachtsfeiertag"
}
]
},
"birthdays": {
"name": "Geburtstage",
"highlight": {
"type": "marker",
"charStyle": "FeiertageCharStyle",
"paragraphStyle": "FeiertageParagraphStyle"
},
"dates": [
{
"from": "2020-12-30",
"to": "2020-12-30",
"name": "Geburtstag Papa"
},
{
"from": "2020-11-08",
"to": "2020-11-08",
"name": "Geburtstag Mama"
}
]
},
"schoolHolidays": {
"name": "Schulferien",
"dates": [
{
"name": "Weihnachtsferien",
"from": "2020-12-21",
"to": "2021-01-07"
}
]
}
}
}