Back to index

scribus-ng  1.3.4.dfsg+svn20071115
CalendarWizard.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 
00003 """ This is a simple 'Calendar creation wizard' for Scribus. It's a fully
00004 rewritten Calender.py from Scribus examples. Enjoy.
00005 
00006 DESCRIPTION & USAGE:
00007 This script needs Tkinter. It will create a GUI with available options
00008 for easy calendar page creation. You'll get new pages with calendar
00009 tables into a new document you are asked for. Position of the
00010 objects in page is calculated with the "golden-ratio" aspect from the
00011 page margins.
00012 
00013 Steps to create:
00014     1) Fill requested values in the Calendar dialog
00015     2) You will be prompted for new document
00016     3) You will be prompted for new paragraph style which will be used
00017        in calendar text frames. It could be changed later.
00018 
00019 There are 2 types of calender supported:
00020     1) Classic calendar with one month matrix per page. I suggest
00021        here PORTRAIT orientation.
00022     2) Event calendar with one week per page with huge place for
00023        human inputs. There should be LANDSCAPE imho.
00024 
00025 But everything works with both orientations well of course too.
00026 
00027 AUTHOR:
00028     Petr Vanek <petr@scribus.info>
00029 
00030 LICENSE:
00031 This program is free software; you can redistribute it and/or modify
00032 it under the terms of the GNU General Public License as published by
00033 the Free Software Foundation; either version 2 of the License, or
00034 (at your option) any later version.
00035 
00036 This program is distributed in the hope that it will be useful,
00037 but WITHOUT ANY WARRANTY; without even the implied warranty of
00038 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00039 GNU General Public License for more details.
00040 
00041 You should have received a copy of the GNU General Public License
00042 along with this program; if not, write to the Free Software
00043 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00044 
00045 """
00046 
00047 import sys
00048 import calendar
00049 import datetime
00050 
00051 try:
00052     from scribus import *
00053 except ImportError:
00054     print "This Python script is written for the Scribus scripting interface."
00055     print "It can only be run from within Scribus."
00056     sys.exit(1)
00057 
00058 try:
00059     # I wish PyQt installed everywhere :-/
00060     from Tkinter import *
00061     from tkFont import Font
00062 except ImportError:
00063     print "This script requires Python's Tkinter properly installed."
00064     messageBox('Script failed',
00065                'This script requires Python\'s Tkinter properly installed.',
00066                ICON_CRITICAL)
00067     sys.exit(1)
00068 
00069 
00070 localization = {
00071 'Czech' :
00072     [['Leden', 'Únor', 'Březen', 'Duben', 'Květen',
00073         'Červen', 'Červenec', 'Srpen', 'Září',
00074         'Říjen', 'Listopad', 'Prosinec'],
00075      ['Pondělí','Úterý','Středa','Čtvrtek','Pátek','Sobota', 'Neděle']],
00076 'Czech-short' :
00077     [['Leden', 'Únor', 'Březen', 'Duben', 'Květen',
00078         'Červen', 'Červenec', 'Srpen', 'Září',
00079         'Říjen', 'Listopad', 'Prosinec'],
00080      ['Po', 'Út', 'St', 'Čt', 'Pá', 'So', 'Ne']],
00081 'Dutch' :
00082     [['Januari', 'Februari', 'Maart', 'April',
00083       'Mei', 'Juni', 'Juli', 'Augustus', 'September',
00084       'Oktober', 'November', 'December'],
00085      ['Maandag','Dinsdag','Woensdag','Donderdag','Vrijdag','Zaterdag', 'Zondag']],
00086 # Dutch by "Christoph Schäfer" <christoph-schaefer@gmx.de>
00087 'English' :
00088     [['January', 'February', 'March', 'April',
00089       'May', 'June', 'July', 'August', 'September',
00090       'October', 'November', 'December'],
00091      ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday', 'Sunday']],
00092 'English-short' :
00093     [['January', 'February', 'March', 'April', 'May',
00094       'June', 'July', 'August', 'September', 'October',
00095       'November', 'December'],
00096      ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']],
00097 'Finnish' :
00098     [['Tammikuu', 'Helmikuu', 'Maaliskuu', 'Huhtikuu',
00099       'Toukokuu', 'Kesäkuu', 'Heinäkuu', 'Elokuu', 'Syyskuu',
00100       'Lokakuu', 'Marraskuu', 'Joulukuu'],
00101      ['ma','ti','ke','to','pe','la', 'su']],
00102 'French':
00103     [['Janvier', u'F\xe9vrier', 'Mars', 'Avril',
00104       'Mai', 'Juin', 'Juillet', u'Ao\xfbt', 'Septembre',
00105       'Octobre', 'Novembre', u'D\xe9cembre'],
00106      ['Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi','Dimanche']],
00107 'German' :
00108     [['Januar', 'Februar', u'M\xe4rz', 'April',
00109       'Mai', 'Juni', 'Juli', 'August', 'September',
00110       'Oktober', 'November', 'Dezember'],
00111      ['Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag','Sonntag']],
00112 'Italian' :
00113     [['Gennaio', 'Febbraio', 'Marzo', 'Aprile',
00114        'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre',
00115        'Ottobre', 'Novembre', 'Dicembre'],
00116     [u'Luned\xec', u'Marted\xec', u'Mercoled\xec', u'Gioved\exc', u'Venerd\xec', 'Sabato', 'Domenica']],
00117 # Polish by "Łukasz [DeeJay1] Jernaś" <deejay1@nsj.srem.pl>
00118 'Polish' :
00119     [['Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj',
00120       'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień',
00121       'Październik', 'Listopad', 'Grudzień'],
00122      ['Poniedziałek', 'Wtorek', 'Środa', 'Czwartek', 'Piątek', 'Sobota', 'Niedziela']],
00123 'Portuguese' :
00124     [['Janeiro', 'Fevereiro', u'Mar\xe7o', 'Abril',
00125       'Pode', 'Junho', 'Julho', 'Agosto', 'Setembro',
00126       'Outubro', 'Novembro', 'Dezembro'],
00127      ['Segunda-feira', u'Ter\xe7a-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', u'S\xe1bado', 'Domingo']],
00128 'Russian' :
00129     [['Январь', 'Февраль', 'Март', 'Апрель',
00130       'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь',
00131       'Октябрь', 'Ноябрь', 'Декабрь'],
00132      ['Понедельник','Вторник','Среда','Четверг','Пятница','Суббота', 'Воскресенье']],
00133 'Slovak' :
00134     [['Január', 'Február', 'Marec', 'Apríl',
00135       'Máj', 'Jún', 'Júl', 'August', 'September',
00136       'Október', 'November', 'December'],
00137       ['Pondelok','Utorok','Streda','Štvrtok','Piatok','Sobota', 'Nedeľa']],
00138 'Slovak-short' :
00139     [['Január', 'Február', 'Marec', 'Apríl',
00140       'Máj', 'Jún', 'Júl', 'August', 'September',
00141       'Október', 'November', 'December'],
00142       ['Po','Ut','St','Št','Pi','So', 'Ne']],
00143 'Spanish' :
00144     [['Enero', 'Febrero', 'Marco', 'Abril', 'Mayo',
00145       'Junio', 'Julio', 'Agosto', 'Septiembre',
00146       'Octubre', 'Noviembre', 'Diciembre'],
00147      ['Lunes', 'Martes', u'Mi\xe9rcoles', 'Jueves', 'Viernes', u'S\xe1bado', 'Domingo']],
00148 'Swedish' :
00149     [['Januari', 'Februari','Mars', 'April','Maj', 'Juni','Juli', 'Augusti','September', 'Oktober', 'November', 'December'],
00150      ['Måndag', 'Tisdag','Onsdag', 'Torsdag','Fredag', 'Lördag','Söndag']]
00151 }
00152 
00153 
00154 from math import sqrt
00155 
00156 class ScCalendar:
00157     """ Parent class for all calendar types """
00158 
00159     def __init__(self, year, months={}, dayOrder=[], firstDay=calendar.SUNDAY, drawSauce=True):
00160         """ Setup base things """
00161         # params
00162         self.drawSauce = drawSauce # draw supplementary image?
00163         self.year = year
00164         self.months = months
00165         self.dayOrder = dayOrder
00166         self.layerImg = 'Calendar image'
00167         self.layerCal = 'Calendar'
00168         self.pStyle = None # paragraph style
00169         # settings
00170         self.firstPage = True # create only 2nd 3rd ... pages. No 1st one.
00171         calendar.setfirstweekday(firstDay)
00172         progressTotal(len(months))
00173 
00174     def setupDocVariables(self):
00175         """ Compute base metrics here. Page layout is bordered by margins and
00176         virtually divided by golden mean 'cut' in the bottom. The calendar is
00177         in the bottom part - top is occupied with empty image frame. """
00178         page = getPageSize()
00179         self.pagex = page[0]
00180         self.pagey = page[1]
00181         marg = getPageMargins()
00182         self.marginl = marg[0]
00183         self.marginr = marg[1]
00184         self.margint = marg[2]
00185         self.marginb = marg[3]
00186         self.width = self.pagex - self.marginl - self.marginr
00187         self.height = self.pagey - self.margint - self.marginb
00188 
00189     def goldenMean(self, aSize):
00190         """ Taken from samples/golden-mean.py."""
00191         return aSize * ((sqrt(5) - 1)/2)
00192 
00193     def applyTextToFrame(self, aText, aFrame):
00194         """ Insert the text with style. """
00195         setText(aText, aFrame)
00196         setStyle(self.pStyle, aFrame)
00197 
00198     def createCalendar(self):
00199         """ Walk throudh months dict and calls monthly sheet """
00200         if not newDocDialog():
00201             return 'Create a new document first, please'
00202         self.pStyle = newStyleDialog()
00203         if  self.pStyle == None:
00204             closeDoc()
00205             return 'Create a default paragraph style, please'
00206         originalUnit = getUnit()
00207         setUnit(UNIT_POINTS)
00208         self.setupDocVariables()
00209         if self.drawSauce:
00210             createLayer(self.layerImg)
00211         createLayer(self.layerCal)
00212         run = 0
00213         for i in self.months:
00214             run += 1
00215             progressSet(run)
00216             self.createMonthCalendar(i)
00217         setUnit(originalUnit)
00218         return None
00219 
00220     def createLayout(self):
00221         """ Create the page and optional bells and whistless around """
00222         self.createPage()
00223         if self.drawSauce:
00224             setActiveLayer(self.layerImg)
00225             self.createImage()
00226         setActiveLayer(self.layerCal)
00227 
00228     def createPage(self):
00229         """ Wrapper to the new page with layers """
00230         if self.firstPage:
00231             self.firstPage = False
00232             gotoPage(1)
00233             return
00234         newPage(-1)
00235 
00236 
00237 class ScEventCalendar(ScCalendar):
00238     """ One day = one row calendar. I suggest LANDSCAPE orientation.\
00239         One week per page."""
00240 
00241     def __init__(self, year, months = {}, dayOrder = [], firstDay = calendar.SUNDAY, drawSauce=True):
00242         ScCalendar.__init__(self, year, months, dayOrder, firstDay, drawSauce)
00243 
00244     def setupDocVariables(self):
00245         """ Compute base metrics here. Page layout is bordered by margins and
00246         virtually divided by golden mean 'cut' in the bottom. The calendar is
00247         in the bottom part - top is occupied with empty image frame. """
00248         # gloden mean
00249         ScCalendar.setupDocVariables(self)
00250         self.gmean = self.width - self.goldenMean(self.width) + self.marginl
00251         # calendar size = gmean
00252         # rows and cols
00253         self.rowSize = self.height / 8
00254 
00255     def createMonthCalendar(self, month):
00256         cal = calendar.monthcalendar(self.year, month + 1)
00257         for i in cal:
00258             self.createLayout()
00259             self.createHeader(self.months[month])
00260             rowCnt = 1
00261             for j in i: # days
00262                 cel = createText(self.gmean + self.marginl,
00263                                  self.margint + rowCnt * self.rowSize,
00264                                  self.width - self.gmean, self.rowSize)
00265                 rowCnt += 1
00266                 if j != 0:
00267                     self.applyTextToFrame(str(j), cel)
00268 
00269     def createHeader(self, monthName):
00270         cel = createText(self.gmean + self.marginl, self.margint,
00271                             self.width - self.gmean, self.rowSize)
00272         self.applyTextToFrame(monthName, cel)
00273 
00274     def createImage(self):
00275         """ Wrapper for everytime-the-same image frame. """
00276         if self.drawSauce:
00277             createImage(self.marginl, self.margint, self.gmean, self.height)
00278 
00279 class ScClassicCalendar(ScCalendar):
00280     """ Calendar matrix creator itself. I suggest PORTRAIT orientation.
00281         One month per page."""
00282 
00283     def __init__(self, year, months = {}, dayOrder = [], firstDay = calendar.SUNDAY, drawSauce=True):
00284         ScCalendar.__init__(self, year, months, dayOrder, firstDay, drawSauce)
00285 
00286     def setupDocVariables(self):
00287         """ Compute base metrics here. Page layout is bordered by margins and
00288         virtually divided by golden mean 'cut' in the bottom. The calendar is
00289         in the bottom part - top is occupied with empty image frame. """
00290         # gloden mean
00291         ScCalendar.setupDocVariables(self)
00292         self.gmean = self.height - self.goldenMean(self.height) + self.margint
00293         # calendar size
00294         self.calHeight = self.height - self.gmean + self.margint
00295         # rows and cols
00296         self.rowSize = self.gmean / 8
00297         self.colSize = self.width / 7
00298 
00299     def createMonthCalendar(self, month):
00300         """ Draw one month calendar per page """
00301         cal = calendar.monthcalendar(self.year, month + 1)
00302         self.createLayout()
00303         self.createHeader(self.months[month])
00304         # weeks
00305         rowCnt = 2
00306         for i in cal:
00307             colCnt = 0
00308             for j in i: # days
00309                 cel = createText(self.marginl + colCnt * self.colSize,
00310                                  self.calHeight + rowCnt * self.rowSize,
00311                                  self.colSize, self.rowSize)
00312                 colCnt += 1
00313                 if j != 0:
00314                     self.applyTextToFrame(str(j), cel)
00315             rowCnt += 1
00316 
00317     def createHeader(self, monthName):
00318         """ Draw calendar header. Month name and days of the week """
00319         header = createText(self.marginl, self.calHeight, self.width, self.rowSize)
00320         self.applyTextToFrame(monthName, header)
00321         colCnt = 0
00322         for i in self.dayOrder:
00323             cel = createText(self.marginl + colCnt * self.colSize,
00324                              self.calHeight + self.rowSize,
00325                              self.colSize, self.rowSize)
00326             self.applyTextToFrame(i, cel)
00327             colCnt += 1
00328 
00329     def createImage(self):
00330         """ Wrapper for everytime-the-same image frame. """
00331         if self.drawSauce:
00332             createImage(self.marginl, self.margint,
00333                         self.width, self.calHeight - self.margint)
00334 
00335 
00336 class TkCalendar(Frame):
00337     """ GUI interface for Scribus calendar wizard.
00338         It's ugly and very simple. I can say I hate Tkinter :-/"""
00339 
00340     def __init__(self, master=None):
00341         """ Setup the dialog """
00342         # refernce to the localization dictionary
00343         self.key = 'English'
00344         Frame.__init__(self, master)
00345         self.grid()
00346         self.master.resizable(0, 0)
00347         self.master.title('Scribus Calendar Wizard')
00348         #define widgets
00349         self.statusVar = StringVar()
00350         self.statusLabel = Label(self, textvariable=self.statusVar)
00351         self.statusVar.set('Select Options and Values')
00352         # langs
00353        # change the height = to match number of langs.
00354         self.langLabel = Label(self, text='Select language:')
00355         self.langListbox = Listbox(self, selectmode=SINGLE, height=16)
00356         keys = localization.keys()
00357         keys.sort()
00358         for i in keys:
00359             self.langListbox.insert(END, i)
00360         self.langButton = Button(self, text='Change language', command=self.languageChange)
00361         # calendar type
00362         self.typeLabel = Label(self, text='Calendar type')
00363         self.typeVar = IntVar()
00364         self.typeClRadio = Radiobutton(self, text='Classic', variable=self.typeVar, value=0)
00365         self.typeEvRadio = Radiobutton(self, text='Event', variable=self.typeVar, value=1)
00366         # start of week
00367         self.weekStartsLabel = Label(self, text='Week begins with:')
00368         self.weekVar = IntVar()
00369         self.weekMondayRadio = Radiobutton(self, text='Mon', variable=self.weekVar, value=calendar.MONDAY)
00370         self.weekSundayRadio = Radiobutton(self, text='Sun', variable=self.weekVar, value=calendar.SUNDAY)
00371         # year
00372         self.yearLabel = Label(self, text='Year:')
00373         self.yearVar = StringVar()
00374         self.yearEntry = Entry(self, textvariable=self.yearVar, width=4)
00375         self.wholeYearLabel = Label(self, text='Whole year:')
00376         self.wholeYear = IntVar()
00377         self.wholeYearCheck = Checkbutton(self, command=self.setWholeYear, variable=self.wholeYear)
00378         # months
00379         self.monthLabel = Label(self, text='Months:')
00380         self.monthListbox = Listbox(self, selectmode=MULTIPLE, height=12)
00381         # layout stuff
00382         self.imageLabel = Label(self, text='Draw Image Frame:')
00383         self.imageVar = IntVar()
00384         self.imageCheck = Checkbutton(self, variable=self.imageVar)
00385         # closing/running
00386         self.okButton = Button(self, text="OK", width=6, command=self.okButonn_pressed)
00387         self.cancelButton = Button(self, text="Cancel", command=self.quit)
00388         # setup values
00389         self.weekMondayRadio.select()
00390         self.typeClRadio.select()
00391         self.yearVar.set(str(datetime.date(1, 1, 1).today().year))
00392         self.imageCheck.select()
00393         # make layout
00394         self.columnconfigure(0, pad=6)
00395         currRow = 0
00396         self.statusLabel.grid(column=0, row=currRow, columnspan=4)
00397         currRow += 1
00398         self.langLabel.grid(column=0, row=currRow, sticky=W)
00399         self.monthLabel.grid(column=3, row=currRow, sticky=W)
00400         currRow += 1
00401         self.langListbox.grid(column=0, row=currRow, rowspan=6, sticky=N)
00402         self.typeLabel.grid(column=1, row=currRow, sticky=E)
00403         self.typeClRadio.grid(column=2, row=currRow, sticky=W)
00404         self.monthListbox.grid(column=3, row=currRow, rowspan=8)
00405         currRow += 1
00406         self.typeEvRadio.grid(column=2, row=currRow, sticky=W)
00407         currRow += 1
00408         self.weekStartsLabel.grid(column=1, row=currRow, sticky=N+E)
00409         self.weekMondayRadio.grid(column=2, row=currRow, sticky=N+W)
00410         currRow += 1
00411         self.weekSundayRadio.grid(column=2, row=currRow, sticky=N+W)
00412         currRow += 1
00413         self.yearLabel.grid(column=1, row=currRow, sticky=N+E)
00414         self.yearEntry.grid(column=2, row=currRow, sticky=N+W)
00415         currRow += 1
00416         self.wholeYearLabel.grid(column=1, row=currRow, sticky=N+E)
00417         self.wholeYearCheck.grid(column=2, row=currRow, sticky=N+W)
00418         currRow += 1
00419         self.imageLabel.grid(column=1, row=currRow, sticky=N+E)
00420         self.imageCheck.grid(column=2, row=currRow, sticky=N+W)
00421         self.langButton.grid(column=0, row=currRow)
00422         currRow += 3
00423         self.rowconfigure(currRow, pad=6)
00424         self.okButton.grid(column=1, row=currRow, sticky=E)
00425         self.cancelButton.grid(column=2, row=currRow, sticky=W)
00426         # fill the values
00427         self.realLangChange()
00428 
00429     def languageChange(self, lang='English'):
00430         """ Called by Change button. Get language list value and
00431             call real re-filling. """
00432         ix = self.langListbox.curselection()
00433         if len(ix)==0:
00434             self.statusVar.set('Select a language, please')
00435             return
00436         self.realLangChange(lang=self.langListbox.get(ix[0]))
00437 
00438     def realLangChange(self, lang='English'):
00439         """ Real widget setup. Ot takes values from localization dictionary.
00440         [0] = months, [1] Days """
00441         self.key = lang
00442         self.monthListbox.delete(0, END)
00443         self.wholeYear.set(0)
00444         for i in localization[lang][0]:
00445             self.monthListbox.insert(END, i)
00446 
00447     def setWholeYear(self):
00448         """ All/none months selection. It's called after "Whole year" check button
00449         click. """
00450         if self.wholeYear.get() == 1:
00451             self.monthListbox.selection_set(0, END)
00452         else:
00453             self.monthListbox.selection_clear(0, END)
00454 
00455     def okButonn_pressed(self):
00456         """ User variables testing and preparing """
00457         # year
00458         try:
00459             year = self.yearVar.get().strip()
00460             if len(year) != 4:
00461                 raise ValueError
00462             year = int(year, 10)
00463         except ValueError:
00464             self.statusVar.set('Year must be in the "YYYY" format e.g. 2005.')
00465             return
00466         # months
00467         selMonths = self.monthListbox.curselection()
00468         if len(selMonths) == 0:
00469             self.statusVar.set('At least one month must be selected.')
00470             return
00471         months = {}
00472         for i in selMonths:
00473             months[int(i)] = self.monthListbox.get(i)
00474         # day order
00475         dayList = localization[self.key][1]
00476         if self.weekVar.get() == calendar.SUNDAY:
00477             dl = dayList[:6]
00478             dl.insert(0, dayList[6])
00479             dayList = dl
00480         # draw images etc.
00481         if self.imageVar.get() == 0:
00482             draw = False
00483         else:
00484             draw = True
00485         # create calendar (finally)
00486         if self.typeVar.get() == 0:
00487             cal = ScClassicCalendar(year, months, dayList, self.weekVar.get(), draw)
00488         else:
00489             cal = ScEventCalendar(year, months, dayList, self.weekVar.get(), draw)
00490         self.master.withdraw()
00491         err = cal.createCalendar()
00492         if err != None:
00493             self.master.deiconify()
00494             self.statusVar.set(err)
00495         else:
00496             self.quit()
00497 
00498     def quit(self):
00499         self.master.destroy()
00500 
00501 
00502 def main():
00503     """ Application/Dialog loop with Scribus sauce around """
00504     try:
00505         statusMessage('Running script...')
00506         progressReset()
00507         root = Tk()
00508         app = TkCalendar(root)
00509         root.mainloop()
00510     finally:
00511         if haveDoc():
00512             redrawAll()
00513         statusMessage('Done.')
00514         progressReset()
00515 
00516 if __name__ == '__main__':
00517     main()