mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
PUll from trunk
This commit is contained in:
commit
3210e8f584
@ -24,6 +24,7 @@ series_index_auto_increment = 'next'
|
||||
# invert: use "fn ln" -> "ln, fn" (the original algorithm)
|
||||
# copy : copy author to author_sort without modification
|
||||
# comma : use 'copy' if there is a ',' in the name, otherwise use 'invert'
|
||||
# nocomma : "fn ln" -> "ln fn" (without the comma)
|
||||
author_sort_copy_method = 'invert'
|
||||
|
||||
|
||||
@ -80,3 +81,11 @@ title_series_sorting = 'library_order'
|
||||
# strictly_alphabetic, it would remain "The Client".
|
||||
save_template_title_series_sorting = 'library_order'
|
||||
|
||||
# Specify a folder that calibre should connect to at startup using
|
||||
# connect_to_folder. This must be a full path to the folder. If the folder does
|
||||
# not exist when calibre starts, it is ignored. If there are '\' characters in
|
||||
# the path (such as in Windows paths), you must double them.
|
||||
# Examples:
|
||||
# auto_connect_to_folder = 'C:\\Users\\someone\\Desktop\\testlib'
|
||||
# auto_connect_to_folder = '/home/dropbox/My Dropbox/someone/library'
|
||||
auto_connect_to_folder = ''
|
BIN
resources/images/news/futurismic.png
Normal file
BIN
resources/images/news/futurismic.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 479 B |
38
resources/recipes/futurismic.recipe
Normal file
38
resources/recipes/futurismic.recipe
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>'
|
||||
'''
|
||||
futurismic.com
|
||||
'''
|
||||
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
|
||||
class Futurismic(BasicNewsRecipe):
|
||||
title = 'Futurismic'
|
||||
__author__ = 'Darko Miletic'
|
||||
description = 'Near-future science fiction and fact since 2001'
|
||||
oldest_article = 15
|
||||
max_articles_per_feed = 100
|
||||
language = 'en'
|
||||
encoding = 'utf-8'
|
||||
no_stylesheets = True
|
||||
use_embedded_content = False
|
||||
publication_type = 'blog'
|
||||
extra_css = ' body{font-family: Arial,Verdana,sans-serif} '
|
||||
|
||||
conversion_options = {
|
||||
'comment' : description
|
||||
, 'tags' : 'blog, sf'
|
||||
, 'publisher': 'Futurismic'
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
remove_attributes = ['width','height']
|
||||
keep_only_tags = [dict(attrs={'class':['post','commentlist']})]
|
||||
remove_tags = [dict(attrs={'class':['sociable','feedback','tagwords']})]
|
||||
feeds = [(u'Posts', u'http://feeds2.feedburner.com/futurismic_feed')]
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
return self.adeify_images(soup)
|
||||
|
||||
|
348
resources/recipes/go_comics.recipe
Normal file
348
resources/recipes/go_comics.recipe
Normal file
@ -0,0 +1,348 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = 'Copyright 2010 Starson17'
|
||||
'''
|
||||
www.gocomics.com
|
||||
'''
|
||||
from calibre.web.feeds.news import BasicNewsRecipe
|
||||
import mechanize
|
||||
|
||||
class GoComics(BasicNewsRecipe):
|
||||
title = 'GoComics'
|
||||
__author__ = 'Starson17'
|
||||
__version__ = '1.02'
|
||||
__date__ = '14 August 2010'
|
||||
description = u'200+ Comics - Customize for more days/comics: Defaults to 7 days, 25 comics - 20 general, 5 editorial.'
|
||||
category = 'news, comics'
|
||||
language = 'en'
|
||||
use_embedded_content= False
|
||||
no_stylesheets = True
|
||||
remove_javascript = True
|
||||
cover_url = 'http://paulbuckley14059.files.wordpress.com/2008/06/calvin-and-hobbes.jpg'
|
||||
|
||||
####### USER PREFERENCES - COMICS, IMAGE SIZE AND NUMBER OF COMICS TO RETRIEVE ########
|
||||
# num_comics_to_get - I've tried up to 99 on Calvin&Hobbes
|
||||
num_comics_to_get = 7
|
||||
# comic_size 300 is small, 600 is medium, 900 is large, 1500 is extra-large
|
||||
comic_size = 900
|
||||
# CHOOSE COMIC STRIPS BELOW - REMOVE COMMENT '# ' FROM IN FRONT OF DESIRED STRIPS
|
||||
# Please do not overload their servers by selecting all comics and 1000 strips from each!
|
||||
|
||||
conversion_options = {'linearize_tables' : True
|
||||
, 'comment' : description
|
||||
, 'tags' : category
|
||||
, 'language' : language
|
||||
}
|
||||
|
||||
keep_only_tags = [dict(name='div', attrs={'class':['feature','banner']}),
|
||||
]
|
||||
|
||||
remove_tags = [dict(name='a', attrs={'class':['beginning','prev','cal','next','newest']}),
|
||||
dict(name='div', attrs={'class':['tag-wrapper']}),
|
||||
dict(name='ul', attrs={'class':['share-nav','feature-nav']}),
|
||||
]
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser(self)
|
||||
cookies = mechanize.CookieJar()
|
||||
br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies))
|
||||
br.addheaders = [('Referer','http://www.gocomics.com/')]
|
||||
return br
|
||||
|
||||
def parse_index(self):
|
||||
feeds = []
|
||||
for title, url in [
|
||||
######## COMICS - GENERAL ########
|
||||
(u"2 Cows and a Chicken", u"http://www.gocomics.com/2cowsandachicken"),
|
||||
# (u"9 to 5", u"http://www.gocomics.com/9to5"),
|
||||
# (u"The Academia Waltz", u"http://www.gocomics.com/academiawaltz"),
|
||||
# (u"Adam@Home", u"http://www.gocomics.com/adamathome"),
|
||||
# (u"Agnes", u"http://www.gocomics.com/agnes"),
|
||||
# (u"Andy Capp", u"http://www.gocomics.com/andycapp"),
|
||||
# (u"Animal Crackers", u"http://www.gocomics.com/animalcrackers"),
|
||||
# (u"Annie", u"http://www.gocomics.com/annie"),
|
||||
(u"The Argyle Sweater", u"http://www.gocomics.com/theargylesweater"),
|
||||
# (u"Ask Shagg", u"http://www.gocomics.com/askshagg"),
|
||||
(u"B.C.", u"http://www.gocomics.com/bc"),
|
||||
# (u"Back in the Day", u"http://www.gocomics.com/backintheday"),
|
||||
# (u"Bad Reporter", u"http://www.gocomics.com/badreporter"),
|
||||
# (u"Baldo", u"http://www.gocomics.com/baldo"),
|
||||
# (u"Ballard Street", u"http://www.gocomics.com/ballardstreet"),
|
||||
# (u"Barkeater Lake", u"http://www.gocomics.com/barkeaterlake"),
|
||||
# (u"The Barn", u"http://www.gocomics.com/thebarn"),
|
||||
# (u"Basic Instructions", u"http://www.gocomics.com/basicinstructions"),
|
||||
# (u"Bewley", u"http://www.gocomics.com/bewley"),
|
||||
# (u"Big Top", u"http://www.gocomics.com/bigtop"),
|
||||
# (u"Biographic", u"http://www.gocomics.com/biographic"),
|
||||
(u"Birdbrains", u"http://www.gocomics.com/birdbrains"),
|
||||
# (u"Bleeker: The Rechargeable Dog", u"http://www.gocomics.com/bleeker"),
|
||||
# (u"Bliss", u"http://www.gocomics.com/bliss"),
|
||||
(u"Bloom County", u"http://www.gocomics.com/bloomcounty"),
|
||||
# (u"Bo Nanas", u"http://www.gocomics.com/bonanas"),
|
||||
# (u"Bob the Squirrel", u"http://www.gocomics.com/bobthesquirrel"),
|
||||
# (u"The Boiling Point", u"http://www.gocomics.com/theboilingpoint"),
|
||||
# (u"Boomerangs", u"http://www.gocomics.com/boomerangs"),
|
||||
# (u"The Boondocks", u"http://www.gocomics.com/boondocks"),
|
||||
# (u"Bottomliners", u"http://www.gocomics.com/bottomliners"),
|
||||
# (u"Bound and Gagged", u"http://www.gocomics.com/boundandgagged"),
|
||||
# (u"Brainwaves", u"http://www.gocomics.com/brainwaves"),
|
||||
# (u"Brenda Starr", u"http://www.gocomics.com/brendastarr"),
|
||||
# (u"Brewster Rockit", u"http://www.gocomics.com/brewsterrockit"),
|
||||
# (u"Broom Hilda", u"http://www.gocomics.com/broomhilda"),
|
||||
(u"Calvin and Hobbes", u"http://www.gocomics.com/calvinandhobbes"),
|
||||
# (u"Candorville", u"http://www.gocomics.com/candorville"),
|
||||
# (u"Cathy", u"http://www.gocomics.com/cathy"),
|
||||
# (u"C'est la Vie", u"http://www.gocomics.com/cestlavie"),
|
||||
# (u"Chuckle Bros", u"http://www.gocomics.com/chucklebros"),
|
||||
# (u"Citizen Dog", u"http://www.gocomics.com/citizendog"),
|
||||
# (u"The City", u"http://www.gocomics.com/thecity"),
|
||||
# (u"Cleats", u"http://www.gocomics.com/cleats"),
|
||||
# (u"Close to Home", u"http://www.gocomics.com/closetohome"),
|
||||
# (u"Compu-toon", u"http://www.gocomics.com/compu-toon"),
|
||||
# (u"Cornered", u"http://www.gocomics.com/cornered"),
|
||||
(u"Cul de Sac", u"http://www.gocomics.com/culdesac"),
|
||||
# (u"Daddy's Home", u"http://www.gocomics.com/daddyshome"),
|
||||
# (u"Deep Cover", u"http://www.gocomics.com/deepcover"),
|
||||
# (u"Dick Tracy", u"http://www.gocomics.com/dicktracy"),
|
||||
# (u"The Dinette Set", u"http://www.gocomics.com/dinetteset"),
|
||||
# (u"Dog Eat Doug", u"http://www.gocomics.com/dogeatdoug"),
|
||||
# (u"Domestic Abuse", u"http://www.gocomics.com/domesticabuse"),
|
||||
# (u"Doodles", u"http://www.gocomics.com/doodles"),
|
||||
(u"Doonesbury", u"http://www.gocomics.com/doonesbury"),
|
||||
# (u"The Doozies", u"http://www.gocomics.com/thedoozies"),
|
||||
# (u"The Duplex", u"http://www.gocomics.com/duplex"),
|
||||
# (u"Eek!", u"http://www.gocomics.com/eek"),
|
||||
# (u"The Elderberries", u"http://www.gocomics.com/theelderberries"),
|
||||
# (u"Flight Deck", u"http://www.gocomics.com/flightdeck"),
|
||||
# (u"Flo and Friends", u"http://www.gocomics.com/floandfriends"),
|
||||
# (u"The Flying McCoys", u"http://www.gocomics.com/theflyingmccoys"),
|
||||
(u"For Better or For Worse", u"http://www.gocomics.com/forbetterorforworse"),
|
||||
# (u"For Heaven's Sake", u"http://www.gocomics.com/forheavenssake"),
|
||||
# (u"Fort Knox", u"http://www.gocomics.com/fortknox"),
|
||||
# (u"FoxTrot", u"http://www.gocomics.com/foxtrot"),
|
||||
(u"FoxTrot Classics", u"http://www.gocomics.com/foxtrotclassics"),
|
||||
# (u"Frank & Ernest", u"http://www.gocomics.com/frankandernest"),
|
||||
# (u"Fred Basset", u"http://www.gocomics.com/fredbasset"),
|
||||
# (u"Free Range", u"http://www.gocomics.com/freerange"),
|
||||
# (u"Frog Applause", u"http://www.gocomics.com/frogapplause"),
|
||||
# (u"The Fusco Brothers", u"http://www.gocomics.com/thefuscobrothers"),
|
||||
(u"Garfield", u"http://www.gocomics.com/garfield"),
|
||||
# (u"Garfield Minus Garfield", u"http://www.gocomics.com/garfieldminusgarfield"),
|
||||
# (u"Gasoline Alley", u"http://www.gocomics.com/gasolinealley"),
|
||||
# (u"Gil Thorp", u"http://www.gocomics.com/gilthorp"),
|
||||
# (u"Ginger Meggs", u"http://www.gocomics.com/gingermeggs"),
|
||||
# (u"Girls & Sports", u"http://www.gocomics.com/girlsandsports"),
|
||||
# (u"Haiku Ewe", u"http://www.gocomics.com/haikuewe"),
|
||||
# (u"Heart of the City", u"http://www.gocomics.com/heartofthecity"),
|
||||
# (u"Heathcliff", u"http://www.gocomics.com/heathcliff"),
|
||||
# (u"Herb and Jamaal", u"http://www.gocomics.com/herbandjamaal"),
|
||||
# (u"Home and Away", u"http://www.gocomics.com/homeandaway"),
|
||||
# (u"Housebroken", u"http://www.gocomics.com/housebroken"),
|
||||
# (u"Hubert and Abby", u"http://www.gocomics.com/hubertandabby"),
|
||||
# (u"Imagine This", u"http://www.gocomics.com/imaginethis"),
|
||||
# (u"In the Bleachers", u"http://www.gocomics.com/inthebleachers"),
|
||||
# (u"In the Sticks", u"http://www.gocomics.com/inthesticks"),
|
||||
# (u"Ink Pen", u"http://www.gocomics.com/inkpen"),
|
||||
# (u"It's All About You", u"http://www.gocomics.com/itsallaboutyou"),
|
||||
# (u"Joe Vanilla", u"http://www.gocomics.com/joevanilla"),
|
||||
# (u"La Cucaracha", u"http://www.gocomics.com/lacucaracha"),
|
||||
# (u"Last Kiss", u"http://www.gocomics.com/lastkiss"),
|
||||
# (u"Legend of Bill", u"http://www.gocomics.com/legendofbill"),
|
||||
# (u"Liberty Meadows", u"http://www.gocomics.com/libertymeadows"),
|
||||
(u"Lio", u"http://www.gocomics.com/lio"),
|
||||
# (u"Little Dog Lost", u"http://www.gocomics.com/littledoglost"),
|
||||
# (u"Little Otto", u"http://www.gocomics.com/littleotto"),
|
||||
# (u"Loose Parts", u"http://www.gocomics.com/looseparts"),
|
||||
# (u"Love Is...", u"http://www.gocomics.com/loveis"),
|
||||
# (u"Maintaining", u"http://www.gocomics.com/maintaining"),
|
||||
# (u"The Meaning of Lila", u"http://www.gocomics.com/meaningoflila"),
|
||||
# (u"Middle-Aged White Guy", u"http://www.gocomics.com/middleagedwhiteguy"),
|
||||
# (u"The Middletons", u"http://www.gocomics.com/themiddletons"),
|
||||
# (u"Momma", u"http://www.gocomics.com/momma"),
|
||||
# (u"Mutt & Jeff", u"http://www.gocomics.com/muttandjeff"),
|
||||
# (u"Mythtickle", u"http://www.gocomics.com/mythtickle"),
|
||||
# (u"Nest Heads", u"http://www.gocomics.com/nestheads"),
|
||||
# (u"NEUROTICA", u"http://www.gocomics.com/neurotica"),
|
||||
(u"New Adventures of Queen Victoria", u"http://www.gocomics.com/thenewadventuresofqueenvictoria"),
|
||||
(u"Non Sequitur", u"http://www.gocomics.com/nonsequitur"),
|
||||
# (u"The Norm", u"http://www.gocomics.com/thenorm"),
|
||||
# (u"On A Claire Day", u"http://www.gocomics.com/onaclaireday"),
|
||||
# (u"One Big Happy", u"http://www.gocomics.com/onebighappy"),
|
||||
# (u"The Other Coast", u"http://www.gocomics.com/theothercoast"),
|
||||
# (u"Out of the Gene Pool Re-Runs", u"http://www.gocomics.com/outofthegenepool"),
|
||||
# (u"Overboard", u"http://www.gocomics.com/overboard"),
|
||||
# (u"Pibgorn", u"http://www.gocomics.com/pibgorn"),
|
||||
# (u"Pibgorn Sketches", u"http://www.gocomics.com/pibgornsketches"),
|
||||
(u"Pickles", u"http://www.gocomics.com/pickles"),
|
||||
# (u"Pinkerton", u"http://www.gocomics.com/pinkerton"),
|
||||
# (u"Pluggers", u"http://www.gocomics.com/pluggers"),
|
||||
(u"Pooch Cafe", u"http://www.gocomics.com/poochcafe"),
|
||||
# (u"PreTeena", u"http://www.gocomics.com/preteena"),
|
||||
# (u"The Quigmans", u"http://www.gocomics.com/thequigmans"),
|
||||
# (u"Rabbits Against Magic", u"http://www.gocomics.com/rabbitsagainstmagic"),
|
||||
(u"Real Life Adventures", u"http://www.gocomics.com/reallifeadventures"),
|
||||
# (u"Red and Rover", u"http://www.gocomics.com/redandrover"),
|
||||
# (u"Red Meat", u"http://www.gocomics.com/redmeat"),
|
||||
# (u"Reynolds Unwrapped", u"http://www.gocomics.com/reynoldsunwrapped"),
|
||||
# (u"Ronaldinho Gaucho", u"http://www.gocomics.com/ronaldinhogaucho"),
|
||||
# (u"Rubes", u"http://www.gocomics.com/rubes"),
|
||||
# (u"Scary Gary", u"http://www.gocomics.com/scarygary"),
|
||||
(u"Shoe", u"http://www.gocomics.com/shoe"),
|
||||
# (u"Shoecabbage", u"http://www.gocomics.com/shoecabbage"),
|
||||
# (u"Skin Horse", u"http://www.gocomics.com/skinhorse"),
|
||||
# (u"Slowpoke", u"http://www.gocomics.com/slowpoke"),
|
||||
# (u"Speed Bump", u"http://www.gocomics.com/speedbump"),
|
||||
# (u"State of the Union", u"http://www.gocomics.com/stateoftheunion"),
|
||||
(u"Stone Soup", u"http://www.gocomics.com/stonesoup"),
|
||||
# (u"Strange Brew", u"http://www.gocomics.com/strangebrew"),
|
||||
# (u"Sylvia", u"http://www.gocomics.com/sylvia"),
|
||||
# (u"Tank McNamara", u"http://www.gocomics.com/tankmcnamara"),
|
||||
# (u"Tiny Sepuku", u"http://www.gocomics.com/tinysepuku"),
|
||||
# (u"TOBY", u"http://www.gocomics.com/toby"),
|
||||
# (u"Tom the Dancing Bug", u"http://www.gocomics.com/tomthedancingbug"),
|
||||
# (u"Too Much Coffee Man", u"http://www.gocomics.com/toomuchcoffeeman"),
|
||||
# (u"W.T. Duck", u"http://www.gocomics.com/wtduck"),
|
||||
# (u"Watch Your Head", u"http://www.gocomics.com/watchyourhead"),
|
||||
# (u"Wee Pals", u"http://www.gocomics.com/weepals"),
|
||||
# (u"Winnie the Pooh", u"http://www.gocomics.com/winniethepooh"),
|
||||
(u"Wizard of Id", u"http://www.gocomics.com/wizardofid"),
|
||||
# (u"Working It Out", u"http://www.gocomics.com/workingitout"),
|
||||
# (u"Yenny", u"http://www.gocomics.com/yenny"),
|
||||
# (u"Zack Hill", u"http://www.gocomics.com/zackhill"),
|
||||
(u"Ziggy", u"http://www.gocomics.com/ziggy"),
|
||||
######## COMICS - EDITORIAL ########
|
||||
("Lalo Alcaraz","http://www.gocomics.com/laloalcaraz"),
|
||||
("Nick Anderson","http://www.gocomics.com/nickanderson"),
|
||||
("Chuck Asay","http://www.gocomics.com/chuckasay"),
|
||||
("Tony Auth","http://www.gocomics.com/tonyauth"),
|
||||
("Donna Barstow","http://www.gocomics.com/donnabarstow"),
|
||||
# ("Bruce Beattie","http://www.gocomics.com/brucebeattie"),
|
||||
# ("Clay Bennett","http://www.gocomics.com/claybennett"),
|
||||
# ("Lisa Benson","http://www.gocomics.com/lisabenson"),
|
||||
# ("Steve Benson","http://www.gocomics.com/stevebenson"),
|
||||
# ("Chip Bok","http://www.gocomics.com/chipbok"),
|
||||
# ("Steve Breen","http://www.gocomics.com/stevebreen"),
|
||||
# ("Chris Britt","http://www.gocomics.com/chrisbritt"),
|
||||
# ("Stuart Carlson","http://www.gocomics.com/stuartcarlson"),
|
||||
# ("Ken Catalino","http://www.gocomics.com/kencatalino"),
|
||||
# ("Paul Conrad","http://www.gocomics.com/paulconrad"),
|
||||
# ("Jeff Danziger","http://www.gocomics.com/jeffdanziger"),
|
||||
# ("Matt Davies","http://www.gocomics.com/mattdavies"),
|
||||
# ("John Deering","http://www.gocomics.com/johndeering"),
|
||||
# ("Bob Gorrell","http://www.gocomics.com/bobgorrell"),
|
||||
# ("Walt Handelsman","http://www.gocomics.com/walthandelsman"),
|
||||
# ("Clay Jones","http://www.gocomics.com/clayjones"),
|
||||
# ("Kevin Kallaugher","http://www.gocomics.com/kevinkallaugher"),
|
||||
# ("Steve Kelley","http://www.gocomics.com/stevekelley"),
|
||||
# ("Dick Locher","http://www.gocomics.com/dicklocher"),
|
||||
# ("Chan Lowe","http://www.gocomics.com/chanlowe"),
|
||||
# ("Mike Luckovich","http://www.gocomics.com/mikeluckovich"),
|
||||
# ("Gary Markstein","http://www.gocomics.com/garymarkstein"),
|
||||
# ("Glenn McCoy","http://www.gocomics.com/glennmccoy"),
|
||||
# ("Jim Morin","http://www.gocomics.com/jimmorin"),
|
||||
# ("Jack Ohman","http://www.gocomics.com/jackohman"),
|
||||
# ("Pat Oliphant","http://www.gocomics.com/patoliphant"),
|
||||
# ("Joel Pett","http://www.gocomics.com/joelpett"),
|
||||
# ("Ted Rall","http://www.gocomics.com/tedrall"),
|
||||
# ("Michael Ramirez","http://www.gocomics.com/michaelramirez"),
|
||||
# ("Marshall Ramsey","http://www.gocomics.com/marshallramsey"),
|
||||
# ("Steve Sack","http://www.gocomics.com/stevesack"),
|
||||
# ("Ben Sargent","http://www.gocomics.com/bensargent"),
|
||||
# ("Drew Sheneman","http://www.gocomics.com/drewsheneman"),
|
||||
# ("John Sherffius","http://www.gocomics.com/johnsherffius"),
|
||||
# ("Small World","http://www.gocomics.com/smallworld"),
|
||||
# ("Scott Stantis","http://www.gocomics.com/scottstantis"),
|
||||
# ("Wayne Stayskal","http://www.gocomics.com/waynestayskal"),
|
||||
# ("Dana Summers","http://www.gocomics.com/danasummers"),
|
||||
# ("Paul Szep","http://www.gocomics.com/paulszep"),
|
||||
# ("Mike Thompson","http://www.gocomics.com/mikethompson"),
|
||||
# ("Tom Toles","http://www.gocomics.com/tomtoles"),
|
||||
# ("Gary Varvel","http://www.gocomics.com/garyvarvel"),
|
||||
# ("ViewsAfrica","http://www.gocomics.com/viewsafrica"),
|
||||
# ("ViewsAmerica","http://www.gocomics.com/viewsamerica"),
|
||||
# ("ViewsAsia","http://www.gocomics.com/viewsasia"),
|
||||
# ("ViewsBusiness","http://www.gocomics.com/viewsbusiness"),
|
||||
# ("ViewsEurope","http://www.gocomics.com/viewseurope"),
|
||||
# ("ViewsLatinAmerica","http://www.gocomics.com/viewslatinamerica"),
|
||||
# ("ViewsMidEast","http://www.gocomics.com/viewsmideast"),
|
||||
# ("Views of the World","http://www.gocomics.com/viewsoftheworld"),
|
||||
# ("Kerry Waghorn","http://www.gocomics.com/facesinthenews"),
|
||||
# ("Dan Wasserman","http://www.gocomics.com/danwasserman"),
|
||||
# ("Signe Wilkinson","http://www.gocomics.com/signewilkinson"),
|
||||
# ("Wit of the World","http://www.gocomics.com/witoftheworld"),
|
||||
# ("Don Wright","http://www.gocomics.com/donwright"),
|
||||
]:
|
||||
articles = self.make_links(url)
|
||||
if articles:
|
||||
feeds.append((title, articles))
|
||||
return feeds
|
||||
|
||||
def make_links(self, url):
|
||||
title = 'Temp'
|
||||
current_articles = []
|
||||
pages = range(1, self.num_comics_to_get+1)
|
||||
for page in pages:
|
||||
page_soup = self.index_to_soup(url)
|
||||
if page_soup:
|
||||
try:
|
||||
strip_title = page_soup.h1.a.string
|
||||
except:
|
||||
strip_title = 'Error - no page_soup.h1.a.string'
|
||||
try:
|
||||
date_title = page_soup.find('ul', attrs={'class': 'feature-nav'}).li.string
|
||||
except:
|
||||
date_title = 'Error - no page_soup.h1.li.string'
|
||||
title = strip_title + ' - ' + date_title
|
||||
for i in range(2):
|
||||
try:
|
||||
strip_url_date = page_soup.h1.a['href']
|
||||
break #success - this is normal exit
|
||||
except:
|
||||
continue #try to get strip_url_date again
|
||||
continue # give up on this strip date
|
||||
for i in range(2):
|
||||
try:
|
||||
prev_strip_url_date = page_soup.find('a', attrs={'class': 'prev'})['href']
|
||||
break #success - this is normal exit
|
||||
except:
|
||||
continue #try to get prev_strip_url_date again
|
||||
continue # give up on this prev strip date
|
||||
if strip_url_date:
|
||||
page_url = 'http://www.gocomics.com' + strip_url_date
|
||||
else:
|
||||
continue
|
||||
if prev_strip_url_date:
|
||||
prev_page_url = 'http://www.gocomics.com' + prev_strip_url_date
|
||||
else:
|
||||
continue
|
||||
current_articles.append({'title': title, 'url': page_url, 'description':'', 'date':''})
|
||||
url = prev_page_url
|
||||
current_articles.reverse()
|
||||
return current_articles
|
||||
|
||||
def preprocess_html(self, soup):
|
||||
if soup.title:
|
||||
title_string = soup.title.string.strip()
|
||||
_cd = title_string.split(',',1)[1]
|
||||
comic_date = ' '.join(_cd.split(' ', 4)[0:-1])
|
||||
if soup.h1.span:
|
||||
artist = soup.h1.span.string
|
||||
soup.h1.span.string.replaceWith(comic_date + artist)
|
||||
feature_item = soup.find('p',attrs={'class':'feature_item'})
|
||||
if feature_item.a:
|
||||
a_tag = feature_item.a
|
||||
a_href = a_tag["href"]
|
||||
img_tag = a_tag.img
|
||||
img_tag["src"] = a_href
|
||||
img_tag["width"] = self.comic_size
|
||||
img_tag["height"] = None
|
||||
return self.adeify_images(soup)
|
||||
|
||||
extra_css = '''
|
||||
h1{font-family:Arial,Helvetica,sans-serif; font-weight:bold;font-size:large;}
|
||||
h2{font-family:Arial,Helvetica,sans-serif; font-weight:normal;font-size:small;}
|
||||
img {max-width:100%; min-width:100%;}
|
||||
p{font-family:Arial,Helvetica,sans-serif;font-size:small;}
|
||||
body{font-family:Helvetica,Arial,sans-serif;font-size:small;}
|
||||
'''
|
@ -22,7 +22,7 @@ class ScienceAAS(BasicNewsRecipe):
|
||||
timefmt = ' [%A, %d %B, %Y]'
|
||||
needs_subscription = True
|
||||
LOGIN = 'http://www.sciencemag.org/cgi/login?uri=%2Findex.dtl'
|
||||
|
||||
|
||||
def get_browser(self):
|
||||
br = BasicNewsRecipe.get_browser()
|
||||
if self.username is not None and self.password is not None:
|
||||
|
@ -1,244 +0,0 @@
|
||||
Changes from 4.0 to 4.0.1
|
||||
1) Added support for Python 2.6. On Windows a manifest file is now required
|
||||
because of the switch to using the new Microsoft C runtime.
|
||||
2) Ensure that hooks are run for builtin modules.
|
||||
|
||||
Changes from 4.0b1 to 4.0
|
||||
1) Added support for copying files to the target directory.
|
||||
2) Added support for a hook that runs when a module is missing.
|
||||
3) Added support for binary path includes as well as excludes; use sequences
|
||||
rather than dictionaries as a more convenient API; exclude the standard
|
||||
locations for 32-bit and 64-bit libaries in multi-architecture systems.
|
||||
4) Added support for searching zip files (egg files) for modules.
|
||||
5) Added support for handling system exit exceptions similarly to what Python
|
||||
does itself as requested by Sylvain.
|
||||
6) Added code to wait for threads to shut down like the normal Python
|
||||
interpreter does. Thanks to Mariano Disanzo for discovering this
|
||||
discrepancy.
|
||||
7) Hooks added or modified based on feedback from many people.
|
||||
8) Don't include the version name in the display name of the MSI.
|
||||
9) Use the OS dependent path normalization routines rather than simply use the
|
||||
lowercase value as on Unix case is important; thanks to Artie Eoff for
|
||||
pointing this out.
|
||||
10) Include a version attribute in the cx_Freeze package and display it in the
|
||||
output for the --version option to the script.
|
||||
11) Include build instructions as requested by Norbert Sebok.
|
||||
12) Add support for copying files when modules are included which require data
|
||||
files to operate properly; add support for copying the necessary files for
|
||||
the Tkinter and matplotlib modules.
|
||||
13) Handle deferred imports recursively as needed; ensure that from lists do
|
||||
not automatically indicate that they are part of the module or the deferred
|
||||
import processing doesn't actually work!
|
||||
14) Handle the situation where a module imports everything from a package and
|
||||
the __all__ variable has been defined but the package has not actually
|
||||
imported everything in the __all__ variable during initialization.
|
||||
15) Modified license text to more closely match the Python Software Foundation
|
||||
license as was intended.
|
||||
16) Added sample script for freezing an application using matplotlib.
|
||||
17) Renamed freeze to cxfreeze to avoid conflict with another package that uses
|
||||
that executable as requested by Siegfried Gevatter.
|
||||
|
||||
Changes from 3.0.3 to 4.0b1
|
||||
1) Added support for placing modules in library.zip or in a separate zip file
|
||||
for each executable that is produced.
|
||||
2) Added support for copying binary dependent files (DLLs and shared
|
||||
libraries)
|
||||
3) Added support for including all submodules in a package
|
||||
4) Added support for including icons in Windows executables
|
||||
5) Added support for constants module which can be used for determining
|
||||
certain build constants at runtime
|
||||
6) Added support for relative imports available in Python 2.5 and up
|
||||
7) Added support for building Windows installers (Python 2.5 and up) and
|
||||
RPM packages
|
||||
8) Added support for distutils configuration scripts
|
||||
9) Added support for hooks which can force inclusion or exclusion of modules
|
||||
when certain modules are included
|
||||
10) Added documentation and samples
|
||||
11) Added setup.py for building the cx_Freeze package instead of a script
|
||||
used to build only the frozen bases
|
||||
12) FreezePython renamed to a script called freeze in the Python distribution
|
||||
13) On Linux and other platforms that support it set LD_RUN_PATH to include
|
||||
the directory in which the executable is located
|
||||
|
||||
Changes from 3.0.2 to 3.0.3
|
||||
1) In Common.c, used MAXPATHLEN defined in the Python OS independent include
|
||||
file rather than the PATH_MAX define which is OS dependent and is not
|
||||
available on IRIX as noted by Andrew Jones.
|
||||
2) In the initscript ConsoleSetLibPath.py, added lines from initscript
|
||||
Console.py that should have been there since the only difference between
|
||||
that script and this one is the automatic re-execution of the executable.
|
||||
3) Added an explicit "import encodings" to the initscripts in order to handle
|
||||
Unicode encodings a little better. Thanks to Ralf Schmitt for pointing out
|
||||
the problem and its solution.
|
||||
4) Generated a meaningful name for the extension loader script so that it is
|
||||
clear which particular extension module is being loaded when an exception
|
||||
is being raised.
|
||||
5) In MakeFrozenBases.py, use distutils to figure out a few more
|
||||
platform-dependent linker flags as suggested by Ralf Schmitt.
|
||||
|
||||
Changes from 3.0.1 to 3.0.2
|
||||
1) Add support for compressing the byte code in the zip files that are
|
||||
produced.
|
||||
2) Add better support for the win32com package as requested by Barry Scott.
|
||||
3) Prevent deletion of target file if it happens to be identical to the
|
||||
source file.
|
||||
4) Include additional flags for local modifications to a Python build as
|
||||
suggested by Benjamin Rutt.
|
||||
5) Expanded instructions for building cx_Freeze from source based on a
|
||||
suggestion from Gregg Lind.
|
||||
6) Fix typo in help string.
|
||||
|
||||
Changes from 3.0 to 3.0.1
|
||||
1) Added option --default-path which is used to specify the path used when
|
||||
finding modules. This is particularly useful when performing cross
|
||||
compilations (such as for building a frozen executable for Windows CE).
|
||||
2) Added option --shared-lib-name which can be used to specify the name of
|
||||
the shared library (DLL) implementing the Python runtime that is required
|
||||
for the frozen executable to work. This option is also particularly useful
|
||||
when cross compiling since the normal method for determining this
|
||||
information cannot be used.
|
||||
3) Added option --zip-include which allows for additional files to be added
|
||||
to the zip file that contains the modules that implement the Python
|
||||
script. Thanks to Barray Warsaw for providing the initial patch.
|
||||
4) Added support for handling read-only files properly. Thanks to Peter
|
||||
Grayson for pointing out the problem and providing a solution.
|
||||
5) Added support for a frozen executable to be a symbolic link. Thanks to
|
||||
Robert Kiendl for providing the initial patch.
|
||||
6) Enhanced the support for running a frozen executable that uses an existing
|
||||
Python installation to locate modules it requires. This is primarily of
|
||||
use for embedding Python where the interface is C but the ability to run
|
||||
from source is still desired.
|
||||
7) Modified the documentation to indicate that building from source on
|
||||
Windows currently requires the mingw compiler (http://www.mingw.org).
|
||||
8) Workaround the problem in Python 2.3 (fixed in Python 2.4) which causes a
|
||||
broken module to be left in sys.modules if an ImportError takes place
|
||||
during the execution of the code in that module. Thanks to Roger Binns
|
||||
for pointing this out.
|
||||
|
||||
Changes from 3.0 beta3 to 3.0
|
||||
1) Ensure that ldd is only run on extension modules.
|
||||
2) Allow for using a compiler other than gcc for building the frozen base
|
||||
executables by setting the environment variable CC.
|
||||
3) Ensure that the import lock is not held while executing the main script;
|
||||
otherwise, attempts to import a module within a thread will hang that
|
||||
thread as noted by Roger Binns.
|
||||
4) Added support for replacing the paths in all frozen modules with something
|
||||
else (so that for example the path of the machine on which the freezing
|
||||
was done is not displayed in tracebacks)
|
||||
|
||||
Changes from 3.0 beta2 to 3.0 beta3
|
||||
1) Explicitly include the warnings module so that at runtime warnings are
|
||||
suppressed as when running Python normally.
|
||||
2) Improve the extension loader so that an ImportError is raised when the
|
||||
dynamic module is not located; otherwise an error about missing attributes
|
||||
is raised instead.
|
||||
3) Extension loaders are only created when copying dependencies since the
|
||||
normal module should be loadable in the situation where a Python
|
||||
installation is available.
|
||||
4) Added support for Python 2.4.
|
||||
5) Fixed the dependency checking for wxPython to be a little more
|
||||
intelligent.
|
||||
|
||||
Changes from 3.0 beta1 to 3.0 beta2
|
||||
1) Fix issues with locating the initscripts and bases relative to the
|
||||
directory in which the executable was started.
|
||||
2) Added new base executable ConsoleKeepPath which is used when an existing
|
||||
Python installation is required (such as for FreezePython itself).
|
||||
3) Forced the existence of a Python installation to be ignored when using the
|
||||
standard Console base executable.
|
||||
4) Remove the existing file when copying dependent files; otherwise, an error
|
||||
is raised when attempting to overwrite read-only files.
|
||||
5) Added option -O (or -OO) to FreezePython to set the optimization used when
|
||||
generating bytecode.
|
||||
|
||||
Changes from 2.2 to 3.0 beta1
|
||||
1) cx_Freeze now requires Python 2.3 or higher since it takes advantage of
|
||||
the ability of Python 2.3 and higher to import modules from zip files.
|
||||
This makes the freezing process considerably simpler and also allows for
|
||||
the execution of multiple frozen packages (such as found in COM servers or
|
||||
shared libraries) without requiring modification to the Python modules.
|
||||
2) All external dependencies have been removed. cx_Freeze now only requires
|
||||
a standard Python distribution to do its work.
|
||||
3) Added the ability to define the initialization scripts that cx_Freeze uses
|
||||
on startup of the frozen program. Previously, these scripts were written
|
||||
in C and could not easily be changed; now they are written in Python and
|
||||
can be found in the initscripts directory (and chosen with the
|
||||
new --init-script option to FreezePython).
|
||||
4) The base executable ConsoleSetLibPath has been removed and replaced with
|
||||
the initscript ConsoleSetLibPath.
|
||||
5) Removed base executables for Win32 services and Win32 COM servers. This
|
||||
functionality will be restored in the future but it is not currently in a
|
||||
state that is ready for release. If this functionality is required, please
|
||||
use py2exe or contact me for my work in progress.
|
||||
6) The attribute sys.frozen is now set so that more recent pywin32 modules
|
||||
work as expected when frozen.
|
||||
7) Added option --include-path to FreezePython to allow overriding of
|
||||
sys.path without modifying the environment variable PYTHONPATH.
|
||||
8) Added option --target-dir/--install-dir to specify the directory in which
|
||||
the frozen executable and its dependencies will be placed.
|
||||
9) Removed the option --shared-lib since it was used for building shared
|
||||
libraries and can be managed with the initscript SharedLib.py.
|
||||
10) MakeFrozenBases.py now checks the platform specific include directory as
|
||||
requested by Michael Partridge.
|
||||
|
||||
|
||||
Changes from 2.1 to 2.2
|
||||
1) Add option (--ext-list-file) to FreezePython to write the list of
|
||||
extensions copied to the installation directory to a file. This option is
|
||||
useful in cases where multiple builds are performed into the same
|
||||
installation directory.
|
||||
2) Pass the arguments on the command line through to Win32 GUI applications.
|
||||
Thanks to Michael Porter for pointing this out.
|
||||
3) Link directly against the python DLL when building the frozen bases on
|
||||
Windows, thus eliminating the need for building an import library.
|
||||
4) Force sys.path to include the directory in which the script to be frozen
|
||||
is found.
|
||||
5) Make sure that the installation directory exists before attempting to
|
||||
copy the target binary into it.
|
||||
6) The Win32GUI base has been modified to display fatal errors in message
|
||||
boxes, rather than printing errors to stderr, since on Windows the
|
||||
standard file IO handles are all closed.
|
||||
|
||||
Changes from 2.0 to 2.1
|
||||
1) Remove dependency on Python 2.2. Thanks to Paul Moore for not only
|
||||
pointing it out but providing patches.
|
||||
2) Set up the list of frozen modules in advance, rather than doing it after
|
||||
Python is initialized so that implicit imports done by Python can be
|
||||
satisfied. The bug in Python 2.3 that demonstrated this issue has been
|
||||
fixed in the first release candidate. Thanks to Thomas Heller for pointing
|
||||
out the obvious in this instance!
|
||||
3) Added additional base executable (ConsoleSetLibPath) to support setting
|
||||
the LD_LIBRARY_PATH variable on Unix platforms and restarting the
|
||||
executable to put the new setting into effect. This is primarily of use
|
||||
in distributing wxPython applications on Unix where the shared library
|
||||
has an embedded RPATH value which can cause problems.
|
||||
4) Small improvements of documentation based on feedback from several people.
|
||||
5) Print information about the files written or copied during the freezing
|
||||
process.
|
||||
6) Do not copy extensions when freezing if the path is being overridden since
|
||||
it is expected that a full Python installation is available to the target
|
||||
users of the frozen binary.
|
||||
7) Provide meaningful error message when the wxPython library cannot be
|
||||
found during the freezing process.
|
||||
|
||||
Changes from 1.1 to 2.0
|
||||
1) Added support for in process (DLL) COM servers using PythonCOM.
|
||||
2) Ensured that the frozen flag is set prior to determining the full path for
|
||||
the program in order to avoid warnings about Python not being found on
|
||||
some platforms.
|
||||
3) Added include file and resource file to the source tree to avoid the
|
||||
dependency on the Wine message compiler for Win32 builds.
|
||||
4) Dropped the option --copy-extensions; this now happens automatically since
|
||||
the resulting binary is useless without them.
|
||||
5) Added a sample for building a Win32 service.
|
||||
6) Make use of improved modules from Python 2.3 (which function under 2.2)
|
||||
|
||||
Changes from 1.0 to 1.1
|
||||
1) Fixed import error with C extensions in packages; thanks to Thomas Heller
|
||||
for pointing out the solution to this problem.
|
||||
2) Added options to FreezePython to allow for the inclusion of modules which
|
||||
will not be found by the module finder (--include-modules) and the
|
||||
exclusion of modules which will be found by the module finder but should
|
||||
not be included (--exclude-modules).
|
||||
3) Fixed typo in README.txt.
|
||||
|
@ -1,53 +0,0 @@
|
||||
Copyright © 2007-2008, Colt Engineering, Edmonton, Alberta, Canada.
|
||||
Copyright © 2001-2006, Computronix (Canada) Ltd., Edmonton, Alberta, Canada.
|
||||
All rights reserved.
|
||||
|
||||
NOTE: this license is derived from the Python Software Foundation License
|
||||
which can be found at http://www.python.org/psf/license
|
||||
|
||||
License for cx_Freeze 4.0.1
|
||||
---------------------------
|
||||
|
||||
1. This LICENSE AGREEMENT is between the copyright holders and the Individual
|
||||
or Organization ("Licensee") accessing and otherwise using cx_Freeze
|
||||
software in source or binary form and its associated documentation.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, the
|
||||
copyright holders hereby grant Licensee a nonexclusive, royalty-free,
|
||||
world-wide license to reproduce, analyze, test, perform and/or display
|
||||
publicly, prepare derivative works, distribute, and otherwise use cx_Freeze
|
||||
alone or in any derivative version, provided, however, that this License
|
||||
Agreement and this notice of copyright are retained in cx_Freeze alone or in
|
||||
any derivative version prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on or
|
||||
incorporates cx_Freeze or any part thereof, and wants to make the derivative
|
||||
work available to others as provided herein, then Licensee hereby agrees to
|
||||
include in any such work a brief summary of the changes made to cx_Freeze.
|
||||
|
||||
4. The copyright holders are making cx_Freeze available to Licensee on an
|
||||
"AS IS" basis. THE COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES,
|
||||
EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, THE COPYRIGHT
|
||||
HOLDERS MAKE NO AND DISCLAIM ANY REPRESENTATION OR WARRANTY OF
|
||||
MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF
|
||||
CX_FREEZE WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
5. THE COPYRIGHT HOLDERS SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF
|
||||
CX_FREEZE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
|
||||
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING CX_FREEZE, OR ANY
|
||||
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
6. This License Agreement will automatically terminate upon a material breach
|
||||
of its terms and conditions.
|
||||
|
||||
7. Nothing in this License Agreement shall be deemed to create any relationship
|
||||
of agency, partnership, or joint venture between the copyright holders and
|
||||
Licensee. This License Agreement does not grant permission to use
|
||||
copyright holder's trademarks or trade name in a trademark sense to endorse
|
||||
or promote products or services of Licensee, or any third party.
|
||||
|
||||
8. By copying, installing or otherwise using cx_Freeze, Licensee agrees to be
|
||||
bound by the terms and conditions of this License Agreement.
|
||||
|
||||
Computronix® is a registered trademark of Computronix (Canada) Ltd.
|
||||
|
@ -1,6 +0,0 @@
|
||||
include MANIFEST.in
|
||||
include *.txt
|
||||
recursive-include doc *.html
|
||||
recursive-include initscripts *.py
|
||||
recursive-include samples *.py
|
||||
recursive-include source *.c *.rc
|
@ -1,22 +0,0 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: cx_Freeze
|
||||
Version: 4.0.1
|
||||
Summary: create standalone executables from Python scripts
|
||||
Home-page: http://cx-freeze.sourceforge.net
|
||||
Author: Anthony Tuininga
|
||||
Author-email: anthony.tuininga@gmail.com
|
||||
License: Python Software Foundation License
|
||||
Description: create standalone executables from Python scripts
|
||||
Keywords: freeze
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: Python Software Foundation License
|
||||
Classifier: Natural Language :: English
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: C
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Topic :: Software Development :: Build Tools
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: System :: Software Distribution
|
||||
Classifier: Topic :: Utilities
|
@ -1,12 +0,0 @@
|
||||
Please see cx_Freeze.html for documentation on how to use cx_Freeze.
|
||||
|
||||
To build:
|
||||
|
||||
python setup.py build
|
||||
python setup.py install
|
||||
|
||||
On Windows I have used the MinGW compiler (http://www.mingw.org)
|
||||
|
||||
python setup.py build --compiler=mingw32
|
||||
python setup.py build --compiler=mingw32 install
|
||||
|
@ -1,14 +0,0 @@
|
||||
version = "4.0.1"
|
||||
|
||||
import sys
|
||||
from dist import *
|
||||
if sys.platform == "win32" and sys.version_info[:2] >= (2, 5):
|
||||
from windist import *
|
||||
from finder import *
|
||||
from freezer import *
|
||||
from main import *
|
||||
|
||||
del dist
|
||||
del finder
|
||||
del freezer
|
||||
|
@ -1,279 +0,0 @@
|
||||
import distutils.command.bdist_rpm
|
||||
import distutils.command.build
|
||||
import distutils.command.install
|
||||
import distutils.core
|
||||
import distutils.dir_util
|
||||
import distutils.dist
|
||||
import distutils.util
|
||||
import distutils.version
|
||||
import os
|
||||
import sys
|
||||
|
||||
import cx_Freeze
|
||||
|
||||
__all__ = [ "bdist_rpm", "build", "build_exe", "install", "install_exe",
|
||||
"setup" ]
|
||||
|
||||
class Distribution(distutils.dist.Distribution):
|
||||
|
||||
def __init__(self, attrs):
|
||||
self.executables = []
|
||||
distutils.dist.Distribution.__init__(self, attrs)
|
||||
|
||||
|
||||
class bdist_rpm(distutils.command.bdist_rpm.bdist_rpm):
|
||||
|
||||
def finalize_options(self):
|
||||
distutils.command.bdist_rpm.bdist_rpm.finalize_options(self)
|
||||
self.use_rpm_opt_flags = 1
|
||||
|
||||
def _make_spec_file(self):
|
||||
contents = distutils.command.bdist_rpm.bdist_rpm._make_spec_file(self)
|
||||
return [c for c in contents if c != 'BuildArch: noarch']
|
||||
|
||||
|
||||
class build(distutils.command.build.build):
|
||||
user_options = distutils.command.build.build.user_options + [
|
||||
('build-exe=', None, 'build directory for executables')
|
||||
]
|
||||
|
||||
def get_sub_commands(self):
|
||||
subCommands = distutils.command.build.build.get_sub_commands(self)
|
||||
if self.distribution.executables:
|
||||
subCommands.append("build_exe")
|
||||
return subCommands
|
||||
|
||||
def initialize_options(self):
|
||||
distutils.command.build.build.initialize_options(self)
|
||||
self.build_exe = None
|
||||
|
||||
def finalize_options(self):
|
||||
distutils.command.build.build.finalize_options(self)
|
||||
if self.build_exe is None:
|
||||
dirName = "exe.%s-%s" % \
|
||||
(distutils.util.get_platform(), sys.version[0:3])
|
||||
self.build_exe = os.path.join(self.build_base, dirName)
|
||||
|
||||
|
||||
class build_exe(distutils.core.Command):
|
||||
description = "build executables from Python scripts"
|
||||
user_options = [
|
||||
('build-exe=', 'b',
|
||||
'directory for built executables'),
|
||||
('optimize=', 'O',
|
||||
'optimization level: -O1 for "python -O", '
|
||||
'-O2 for "python -OO" and -O0 to disable [default: -O0]'),
|
||||
('excludes=', 'e',
|
||||
'comma-separated list of modules to exclude'),
|
||||
('includes=', 'i',
|
||||
'comma-separated list of modules to include'),
|
||||
('packages=', 'p',
|
||||
'comma-separated list of packages to include'),
|
||||
('replace-paths=', None,
|
||||
'comma-separated list of paths to replace in included modules'),
|
||||
('path=', None,
|
||||
'comma-separated list of paths to search'),
|
||||
('init-script=', 'i',
|
||||
'name of script to use during initialization'),
|
||||
('base=', None,
|
||||
'name of base executable to use'),
|
||||
('compressed', 'c',
|
||||
'create a compressed zipfile'),
|
||||
('copy-dependent-files', None,
|
||||
'copy all dependent files'),
|
||||
('create-shared-zip', None,
|
||||
'create a shared zip file containing shared modules'),
|
||||
('append-script-to-exe', None,
|
||||
'append the script module to the exe'),
|
||||
('include-in-shared-zip', None,
|
||||
'include the script module in the shared zip file'),
|
||||
('icon', None,
|
||||
'include the icon along with the frozen executable(s)'),
|
||||
('constants=', None,
|
||||
'comma-separated list of constants to include'),
|
||||
('include-files=', 'f',
|
||||
'list of tuples of additional files to include in distribution'),
|
||||
('bin-includes', None,
|
||||
'list of names of files to include when determining dependencies'),
|
||||
('bin-excludes', None,
|
||||
'list of names of files to exclude when determining dependencies')
|
||||
]
|
||||
boolean_options = ["compressed", "copy_dependent_files",
|
||||
"create_shared_zip", "append_script_to_exe",
|
||||
"include_in_shared_zip"]
|
||||
|
||||
def _normalize(self, attrName):
|
||||
value = getattr(self, attrName)
|
||||
if value is None:
|
||||
normalizedValue = []
|
||||
elif isinstance(value, basestring):
|
||||
normalizedValue = value.split()
|
||||
else:
|
||||
normalizedValue = list(value)
|
||||
setattr(self, attrName, normalizedValue)
|
||||
|
||||
def initialize_options(self):
|
||||
self.optimize = 0
|
||||
self.build_exe = None
|
||||
self.excludes = []
|
||||
self.includes = []
|
||||
self.packages = []
|
||||
self.replace_paths = []
|
||||
self.compressed = None
|
||||
self.copy_dependent_files = None
|
||||
self.init_script = None
|
||||
self.base = None
|
||||
self.path = None
|
||||
self.create_shared_zip = None
|
||||
self.append_script_to_exe = None
|
||||
self.include_in_shared_zip = None
|
||||
self.icon = None
|
||||
self.constants = []
|
||||
self.include_files = []
|
||||
self.bin_excludes = []
|
||||
self.bin_includes = []
|
||||
|
||||
def finalize_options(self):
|
||||
self.set_undefined_options('build', ('build_exe', 'build_exe'))
|
||||
self.optimize = int(self.optimize)
|
||||
self._normalize("excludes")
|
||||
self._normalize("includes")
|
||||
self._normalize("packages")
|
||||
self._normalize("constants")
|
||||
|
||||
def run(self):
|
||||
metadata = self.distribution.metadata
|
||||
constantsModule = cx_Freeze.ConstantsModule(metadata.version)
|
||||
for constant in self.constants:
|
||||
parts = constant.split("=")
|
||||
if len(parts) == 1:
|
||||
name = constant
|
||||
value = None
|
||||
else:
|
||||
name, stringValue = parts
|
||||
value = eval(stringValue)
|
||||
constantsModule.values[name] = value
|
||||
freezer = cx_Freeze.Freezer(self.distribution.executables,
|
||||
[constantsModule], self.includes, self.excludes, self.packages,
|
||||
self.replace_paths, self.compressed, self.optimize,
|
||||
self.copy_dependent_files, self.init_script, self.base,
|
||||
self.path, self.create_shared_zip, self.append_script_to_exe,
|
||||
self.include_in_shared_zip, self.build_exe, icon = self.icon,
|
||||
includeFiles = self.include_files,
|
||||
binIncludes = self.bin_includes,
|
||||
binExcludes = self.bin_excludes)
|
||||
freezer.Freeze()
|
||||
|
||||
|
||||
class install(distutils.command.install.install):
|
||||
user_options = distutils.command.install.install.user_options + [
|
||||
('install-exe=', None,
|
||||
'installation directory for executables')
|
||||
]
|
||||
|
||||
def expand_dirs(self):
|
||||
distutils.command.install.install.expand_dirs(self)
|
||||
self._expand_attrs(['install_exe'])
|
||||
|
||||
def get_sub_commands(self):
|
||||
subCommands = distutils.command.install.install.get_sub_commands(self)
|
||||
if self.distribution.executables:
|
||||
subCommands.append("install_exe")
|
||||
return [s for s in subCommands if s != "install_egg_info"]
|
||||
|
||||
def initialize_options(self):
|
||||
distutils.command.install.install.initialize_options(self)
|
||||
self.install_exe = None
|
||||
|
||||
def finalize_options(self):
|
||||
if self.prefix is None and sys.platform == "win32":
|
||||
import _winreg
|
||||
key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
|
||||
r"Software\Microsoft\Windows\CurrentVersion")
|
||||
prefix = str(_winreg.QueryValueEx(key, "ProgramFilesDir")[0])
|
||||
metadata = self.distribution.metadata
|
||||
dirName = "%s-%s" % (metadata.name, metadata.version)
|
||||
self.prefix = "%s/%s" % (prefix, dirName)
|
||||
distutils.command.install.install.finalize_options(self)
|
||||
self.convert_paths('exe')
|
||||
if self.root is not None:
|
||||
self.change_roots('exe')
|
||||
|
||||
def select_scheme(self, name):
|
||||
distutils.command.install.install.select_scheme(self, name)
|
||||
if self.install_exe is None:
|
||||
if sys.platform == "win32":
|
||||
self.install_exe = '$base'
|
||||
else:
|
||||
metadata = self.distribution.metadata
|
||||
dirName = "%s-%s" % (metadata.name, metadata.version)
|
||||
self.install_exe = '$base/lib/%s' % dirName
|
||||
|
||||
|
||||
class install_exe(distutils.core.Command):
|
||||
description = "install executables built from Python scripts"
|
||||
user_options = [
|
||||
('install-dir=', 'd', 'directory to install executables to'),
|
||||
('build-dir=', 'b', 'build directory (where to install from)'),
|
||||
('force', 'f', 'force installation (overwrite existing files)'),
|
||||
('skip-build', None, 'skip the build steps')
|
||||
]
|
||||
|
||||
def initialize_options(self):
|
||||
self.install_dir = None
|
||||
self.force = 0
|
||||
self.build_dir = None
|
||||
self.skip_build = None
|
||||
|
||||
def finalize_options(self):
|
||||
self.set_undefined_options('build', ('build_exe', 'build_dir'))
|
||||
self.set_undefined_options('install',
|
||||
('install_exe', 'install_dir'),
|
||||
('force', 'force'),
|
||||
('skip_build', 'skip_build'))
|
||||
|
||||
def run(self):
|
||||
if not self.skip_build:
|
||||
self.run_command('build_exe')
|
||||
self.outfiles = self.copy_tree(self.build_dir, self.install_dir)
|
||||
if sys.platform != "win32":
|
||||
baseDir = os.path.dirname(os.path.dirname(self.install_dir))
|
||||
binDir = os.path.join(baseDir, "bin")
|
||||
if not os.path.exists(binDir):
|
||||
os.makedirs(binDir)
|
||||
sourceDir = os.path.join("..", self.install_dir[len(baseDir) + 1:])
|
||||
for executable in self.distribution.executables:
|
||||
name = os.path.basename(executable.targetName)
|
||||
source = os.path.join(sourceDir, name)
|
||||
target = os.path.join(binDir, name)
|
||||
if os.path.exists(target):
|
||||
os.unlink(target)
|
||||
os.symlink(source, target)
|
||||
self.outfiles.append(target)
|
||||
|
||||
def get_inputs(self):
|
||||
return self.distribution.executables or []
|
||||
|
||||
def get_outputs(self):
|
||||
return self.outfiles or []
|
||||
|
||||
|
||||
def _AddCommandClass(commandClasses, name, cls):
|
||||
if name not in commandClasses:
|
||||
commandClasses[name] = cls
|
||||
|
||||
|
||||
def setup(**attrs):
|
||||
attrs["distclass"] = Distribution
|
||||
commandClasses = attrs.setdefault("cmdclass", {})
|
||||
if sys.platform == "win32":
|
||||
if sys.version_info[:2] >= (2, 5):
|
||||
_AddCommandClass(commandClasses, "bdist_msi", cx_Freeze.bdist_msi)
|
||||
else:
|
||||
_AddCommandClass(commandClasses, "bdist_rpm", cx_Freeze.bdist_rpm)
|
||||
_AddCommandClass(commandClasses, "build", build)
|
||||
_AddCommandClass(commandClasses, "build_exe", build_exe)
|
||||
_AddCommandClass(commandClasses, "install", install)
|
||||
_AddCommandClass(commandClasses, "install_exe", install_exe)
|
||||
distutils.core.setup(**attrs)
|
||||
|
@ -1,455 +0,0 @@
|
||||
"""
|
||||
Base class for finding modules.
|
||||
"""
|
||||
|
||||
import dis
|
||||
import imp
|
||||
import marshal
|
||||
import new
|
||||
import opcode
|
||||
import os
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
import cx_Freeze.hooks
|
||||
|
||||
BUILD_LIST = opcode.opmap["BUILD_LIST"]
|
||||
INPLACE_ADD = opcode.opmap["INPLACE_ADD"]
|
||||
LOAD_CONST = opcode.opmap["LOAD_CONST"]
|
||||
IMPORT_NAME = opcode.opmap["IMPORT_NAME"]
|
||||
IMPORT_FROM = opcode.opmap["IMPORT_FROM"]
|
||||
STORE_NAME = opcode.opmap["STORE_NAME"]
|
||||
STORE_GLOBAL = opcode.opmap["STORE_GLOBAL"]
|
||||
STORE_OPS = (STORE_NAME, STORE_GLOBAL)
|
||||
|
||||
__all__ = [ "Module", "ModuleFinder" ]
|
||||
|
||||
class ModuleFinder(object):
|
||||
|
||||
def __init__(self, includeFiles, excludes, path, replacePaths):
|
||||
self.includeFiles = includeFiles
|
||||
self.excludes = dict.fromkeys(excludes)
|
||||
self.replacePaths = replacePaths
|
||||
self.path = path or sys.path
|
||||
self.modules = []
|
||||
self.aliases = {}
|
||||
self._modules = dict.fromkeys(excludes)
|
||||
self._builtinModules = dict.fromkeys(sys.builtin_module_names)
|
||||
self._badModules = {}
|
||||
self._zipFileEntries = {}
|
||||
self._zipFiles = {}
|
||||
cx_Freeze.hooks.initialize(self)
|
||||
|
||||
def _AddModule(self, name):
|
||||
"""Add a module to the list of modules but if one is already found,
|
||||
then return it instead; this is done so that packages can be
|
||||
handled properly."""
|
||||
module = self._modules.get(name)
|
||||
if module is None:
|
||||
module = self._modules[name] = Module(name)
|
||||
self.modules.append(module)
|
||||
if name in self._badModules:
|
||||
del self._badModules[name]
|
||||
return module
|
||||
|
||||
def _DetermineParent(self, caller):
|
||||
"""Determine the parent to use when searching packages."""
|
||||
if caller is not None:
|
||||
if caller.path is not None:
|
||||
return caller
|
||||
return self._GetParentByName(caller.name)
|
||||
|
||||
def _EnsureFromList(self, caller, packageModule, fromList,
|
||||
deferredImports):
|
||||
"""Ensure that the from list is satisfied. This is only necessary for
|
||||
package modules. If the caller is the package itself, actually
|
||||
attempt to import right then since it must be a submodule; otherwise
|
||||
defer until after all global names are defined in order to avoid
|
||||
spurious complaints about missing modules."""
|
||||
if caller is not packageModule:
|
||||
deferredImports.append((packageModule, fromList))
|
||||
else:
|
||||
if fromList == ("*",):
|
||||
fromList = packageModule.allNames
|
||||
for name in fromList:
|
||||
if name in packageModule.globalNames:
|
||||
continue
|
||||
subModuleName = "%s.%s" % (packageModule.name, name)
|
||||
self._ImportModule(subModuleName, deferredImports, caller)
|
||||
|
||||
def _FindModule(self, name, path):
|
||||
try:
|
||||
return imp.find_module(name, path)
|
||||
except ImportError:
|
||||
if not path:
|
||||
path = []
|
||||
for location in path:
|
||||
if name in self._zipFileEntries:
|
||||
break
|
||||
if location in self._zipFiles:
|
||||
continue
|
||||
if os.path.isdir(location) or not zipfile.is_zipfile(location):
|
||||
self._zipFiles[location] = None
|
||||
continue
|
||||
zip = zipfile.ZipFile(location)
|
||||
for archiveName in zip.namelist():
|
||||
baseName, ext = os.path.splitext(archiveName)
|
||||
if ext not in ('.pyc', '.pyo'):
|
||||
continue
|
||||
moduleName = ".".join(baseName.split("/"))
|
||||
if moduleName in self._zipFileEntries:
|
||||
continue
|
||||
self._zipFileEntries[moduleName] = (zip, archiveName)
|
||||
self._zipFiles[location] = None
|
||||
info = self._zipFileEntries.get(name)
|
||||
if info is not None:
|
||||
zip, archiveName = info
|
||||
fp = zip.read(archiveName)
|
||||
info = (".pyc", "rb", imp.PY_COMPILED)
|
||||
return fp, os.path.join(zip.filename, archiveName), info
|
||||
raise
|
||||
|
||||
def _GetParentByName(self, name):
|
||||
"""Return the parent module given the name of a module."""
|
||||
pos = name.rfind(".")
|
||||
if pos > 0:
|
||||
parentName = name[:pos]
|
||||
return self._modules[parentName]
|
||||
|
||||
def _ImportAllSubModules(self, module, deferredImports, recursive = True):
|
||||
"""Import all sub modules to the given package."""
|
||||
suffixes = dict.fromkeys([s[0] for s in imp.get_suffixes()])
|
||||
for dir in module.path:
|
||||
try:
|
||||
fileNames = os.listdir(dir)
|
||||
except os.error:
|
||||
continue
|
||||
for fileName in fileNames:
|
||||
name, ext = os.path.splitext(fileName)
|
||||
if ext not in suffixes:
|
||||
continue
|
||||
if name == "__init__":
|
||||
continue
|
||||
subModuleName = "%s.%s" % (module.name, name)
|
||||
subModule, returnError = \
|
||||
self._InternalImportModule(subModuleName,
|
||||
deferredImports)
|
||||
if returnError and subModule is None:
|
||||
raise ImportError, "No module named %s" % subModuleName
|
||||
module.globalNames[name] = None
|
||||
if subModule.path and recursive:
|
||||
self._ImportAllSubModules(subModule, deferredImports,
|
||||
recursive)
|
||||
|
||||
def _ImportDeferredImports(self, deferredImports):
|
||||
"""Import any sub modules that were deferred, if applicable."""
|
||||
while deferredImports:
|
||||
newDeferredImports = []
|
||||
for packageModule, subModuleNames in deferredImports:
|
||||
self._EnsureFromList(packageModule, packageModule,
|
||||
subModuleNames, newDeferredImports)
|
||||
deferredImports = newDeferredImports
|
||||
|
||||
def _ImportModule(self, name, deferredImports, caller = None,
|
||||
relativeImportIndex = 0):
|
||||
"""Attempt to find the named module and return it or None if no module
|
||||
by that name could be found."""
|
||||
|
||||
# absolute import (available in Python 2.5 and up)
|
||||
# the name given is the only name that will be searched
|
||||
if relativeImportIndex == 0:
|
||||
module, returnError = self._InternalImportModule(name,
|
||||
deferredImports)
|
||||
|
||||
# old style relative import (only possibility in Python 2.4 and prior)
|
||||
# the name given is tried in all parents until a match is found and if
|
||||
# no match is found, the global namespace is searched
|
||||
elif relativeImportIndex < 0:
|
||||
parent = self._DetermineParent(caller)
|
||||
while parent is not None:
|
||||
fullName = "%s.%s" % (parent.name, name)
|
||||
module, returnError = self._InternalImportModule(fullName,
|
||||
deferredImports)
|
||||
if module is not None:
|
||||
parent.globalNames[name] = None
|
||||
return module
|
||||
parent = self._GetParentByName(parent.name)
|
||||
module, returnError = self._InternalImportModule(name,
|
||||
deferredImports)
|
||||
|
||||
# new style relative import (available in Python 2.5 and up)
|
||||
# the index indicates how many levels to traverse and only that level
|
||||
# is searched for the named module
|
||||
elif relativeImportIndex > 0:
|
||||
parent = caller
|
||||
if parent.path is not None:
|
||||
relativeImportIndex -= 1
|
||||
while parent is not None and relativeImportIndex > 0:
|
||||
parent = self._GetParentByName(parent.name)
|
||||
relativeImportIndex -= 1
|
||||
if parent is None:
|
||||
module = None
|
||||
returnError = True
|
||||
elif not name:
|
||||
module = parent
|
||||
else:
|
||||
name = "%s.%s" % (parent.name, name)
|
||||
module, returnError = self._InternalImportModule(name,
|
||||
deferredImports)
|
||||
|
||||
# if module not found, track that fact
|
||||
if module is None:
|
||||
if caller is None:
|
||||
raise ImportError, "No module named %s" % name
|
||||
self._RunHook("missing", name, caller)
|
||||
if returnError and name not in caller.ignoreNames:
|
||||
callers = self._badModules.setdefault(name, {})
|
||||
callers[caller.name] = None
|
||||
|
||||
return module
|
||||
|
||||
def _InternalImportModule(self, name, deferredImports):
|
||||
"""Internal method used for importing a module which assumes that the
|
||||
name given is an absolute name. None is returned if the module
|
||||
cannot be found."""
|
||||
try:
|
||||
return self._modules[name], False
|
||||
except KeyError:
|
||||
pass
|
||||
if name in self._builtinModules:
|
||||
module = self._AddModule(name)
|
||||
self._RunHook("load", module.name, module)
|
||||
return module, False
|
||||
pos = name.rfind(".")
|
||||
if pos < 0:
|
||||
path = self.path
|
||||
searchName = name
|
||||
parentModule = None
|
||||
else:
|
||||
parentName = name[:pos]
|
||||
parentModule, returnError = \
|
||||
self._InternalImportModule(parentName, deferredImports)
|
||||
if parentModule is None:
|
||||
return None, returnError
|
||||
path = parentModule.path
|
||||
searchName = name[pos + 1:]
|
||||
if name in self.aliases:
|
||||
actualName = self.aliases[name]
|
||||
module, returnError = \
|
||||
self._InternalImportModule(actualName, deferredImports)
|
||||
self._modules[name] = module
|
||||
return module, returnError
|
||||
try:
|
||||
fp, path, info = self._FindModule(searchName, path)
|
||||
except ImportError:
|
||||
self._modules[name] = None
|
||||
return None, True
|
||||
module = self._LoadModule(name, fp, path, info, deferredImports,
|
||||
parentModule)
|
||||
return module, False
|
||||
|
||||
def _LoadModule(self, name, fp, path, info, deferredImports,
|
||||
parent = None):
|
||||
"""Load the module, given the information acquired by the finder."""
|
||||
suffix, mode, type = info
|
||||
if type == imp.PKG_DIRECTORY:
|
||||
return self._LoadPackage(name, path, parent, deferredImports)
|
||||
module = self._AddModule(name)
|
||||
module.file = path
|
||||
module.parent = parent
|
||||
if type == imp.PY_SOURCE:
|
||||
module.code = compile(fp.read() + "\n", path, "exec")
|
||||
elif type == imp.PY_COMPILED:
|
||||
if isinstance(fp, str):
|
||||
magic = fp[:4]
|
||||
else:
|
||||
magic = fp.read(4)
|
||||
if magic != imp.get_magic():
|
||||
raise ImportError, "Bad magic number in %s" % path
|
||||
if isinstance(fp, str):
|
||||
module.code = marshal.loads(fp[8:])
|
||||
module.inZipFile = True
|
||||
else:
|
||||
fp.read(4)
|
||||
module.code = marshal.load(fp)
|
||||
self._RunHook("load", module.name, module)
|
||||
if module.code is not None:
|
||||
if self.replacePaths:
|
||||
topLevelModule = module
|
||||
while topLevelModule.parent is not None:
|
||||
topLevelModule = topLevelModule.parent
|
||||
module.code = self._ReplacePathsInCode(topLevelModule,
|
||||
module.code)
|
||||
self._ScanCode(module.code, module, deferredImports)
|
||||
return module
|
||||
|
||||
def _LoadPackage(self, name, path, parent, deferredImports):
|
||||
"""Load the package, given its name and path."""
|
||||
module = self._AddModule(name)
|
||||
module.path = [path]
|
||||
fp, path, info = imp.find_module("__init__", module.path)
|
||||
self._LoadModule(name, fp, path, info, deferredImports, parent)
|
||||
return module
|
||||
|
||||
def _ReplacePathsInCode(self, topLevelModule, co):
|
||||
"""Replace paths in the code as directed, returning a new code object
|
||||
with the modified paths in place."""
|
||||
origFileName = newFileName = os.path.normpath(co.co_filename)
|
||||
for searchValue, replaceValue in self.replacePaths:
|
||||
if searchValue == "*":
|
||||
searchValue = os.path.dirname(topLevelModule.file)
|
||||
if topLevelModule.path:
|
||||
searchValue = os.path.dirname(searchValue)
|
||||
if searchValue:
|
||||
searchValue = searchValue + os.pathsep
|
||||
elif not origFileName.startswith(searchValue):
|
||||
continue
|
||||
newFileName = replaceValue + origFileName[len(searchValue):]
|
||||
break
|
||||
constants = list(co.co_consts)
|
||||
for i, value in enumerate(constants):
|
||||
if isinstance(value, type(co)):
|
||||
constants[i] = self._ReplacePathsInCode(topLevelModule, value)
|
||||
return new.code(co.co_argcount, co.co_nlocals, co.co_stacksize,
|
||||
co.co_flags, co.co_code, tuple(constants), co.co_names,
|
||||
co.co_varnames, newFileName, co.co_name, co.co_firstlineno,
|
||||
co.co_lnotab, co.co_freevars, co.co_cellvars)
|
||||
|
||||
def _RunHook(self, hookName, moduleName, *args):
|
||||
"""Run hook for the given module if one is present."""
|
||||
name = "%s_%s" % (hookName, moduleName.replace(".", "_"))
|
||||
method = getattr(cx_Freeze.hooks, name, None)
|
||||
if method is not None:
|
||||
method(self, *args)
|
||||
|
||||
def _ScanCode(self, co, module, deferredImports):
|
||||
"""Scan code, looking for imported modules and keeping track of the
|
||||
constants that have been created in order to better tell which
|
||||
modules are truly missing."""
|
||||
opIndex = 0
|
||||
arguments = []
|
||||
code = co.co_code
|
||||
numOps = len(code)
|
||||
while opIndex < numOps:
|
||||
op = ord(code[opIndex])
|
||||
opIndex += 1
|
||||
if op >= dis.HAVE_ARGUMENT:
|
||||
opArg = ord(code[opIndex]) + ord(code[opIndex + 1]) * 256
|
||||
opIndex += 2
|
||||
if op == LOAD_CONST:
|
||||
arguments.append(co.co_consts[opArg])
|
||||
elif op == IMPORT_NAME:
|
||||
name = co.co_names[opArg]
|
||||
if len(arguments) == 2:
|
||||
relativeImportIndex, fromList = arguments
|
||||
else:
|
||||
relativeImportIndex = -1
|
||||
fromList, = arguments
|
||||
if name not in module.excludeNames:
|
||||
subModule = self._ImportModule(name, deferredImports,
|
||||
module, relativeImportIndex)
|
||||
if subModule is not None:
|
||||
module.globalNames.update(subModule.globalNames)
|
||||
if fromList and subModule.path is not None:
|
||||
self._EnsureFromList(module, subModule, fromList,
|
||||
deferredImports)
|
||||
elif op == IMPORT_FROM:
|
||||
opIndex += 3
|
||||
elif op not in (BUILD_LIST, INPLACE_ADD):
|
||||
if op in STORE_OPS:
|
||||
name = co.co_names[opArg]
|
||||
if name == "__all__":
|
||||
module.allNames.extend(arguments)
|
||||
module.globalNames[name] = None
|
||||
arguments = []
|
||||
for constant in co.co_consts:
|
||||
if isinstance(constant, type(co)):
|
||||
self._ScanCode(constant, module, deferredImports)
|
||||
|
||||
def AddAlias(self, name, aliasFor):
|
||||
"""Add an alias for a particular module; when an attempt is made to
|
||||
import a module using the alias name, import the actual name
|
||||
instead."""
|
||||
self.aliases[name] = aliasFor
|
||||
|
||||
def ExcludeModule(self, name):
|
||||
"""Exclude the named module from the resulting frozen executable."""
|
||||
self.excludes[name] = None
|
||||
self._modules[name] = None
|
||||
|
||||
def IncludeFile(self, path, moduleName = None):
|
||||
"""Include the named file as a module in the frozen executable."""
|
||||
name, ext = os.path.splitext(os.path.basename(path))
|
||||
if moduleName is None:
|
||||
moduleName = name
|
||||
info = (ext, "r", imp.PY_SOURCE)
|
||||
deferredImports = []
|
||||
module = self._LoadModule(moduleName, file(path, "U"), path, info,
|
||||
deferredImports)
|
||||
self._ImportDeferredImports(deferredImports)
|
||||
return module
|
||||
|
||||
def IncludeFiles(self, sourcePath, targetPath):
|
||||
"""Include the files in the given directory in the target build."""
|
||||
self.includeFiles.append((sourcePath, targetPath))
|
||||
|
||||
def IncludeModule(self, name):
|
||||
"""Include the named module in the frozen executable."""
|
||||
deferredImports = []
|
||||
module = self._ImportModule(name, deferredImports)
|
||||
self._ImportDeferredImports(deferredImports)
|
||||
return module
|
||||
|
||||
def IncludePackage(self, name):
|
||||
"""Include the named package and any submodules in the frozen
|
||||
executable."""
|
||||
deferredImports = []
|
||||
module = self._ImportModule(name, deferredImports)
|
||||
if module.path:
|
||||
self._ImportAllSubModules(module, deferredImports)
|
||||
self._ImportDeferredImports(deferredImports)
|
||||
return module
|
||||
|
||||
def ReportMissingModules(self):
|
||||
if self._badModules:
|
||||
print "Missing modules:"
|
||||
names = self._badModules.keys()
|
||||
names.sort()
|
||||
for name in names:
|
||||
callers = self._badModules[name].keys()
|
||||
callers.sort()
|
||||
print "?", name, "imported from", ", ".join(callers)
|
||||
print
|
||||
|
||||
|
||||
class Module(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.file = None
|
||||
self.path = None
|
||||
self.code = None
|
||||
self.parent = None
|
||||
self.globalNames = {}
|
||||
self.excludeNames = {}
|
||||
self.ignoreNames = {}
|
||||
self.allNames = []
|
||||
self.inZipFile = False
|
||||
|
||||
def __repr__(self):
|
||||
parts = ["name=%s" % repr(self.name)]
|
||||
if self.file is not None:
|
||||
parts.append("file=%s" % repr(self.file))
|
||||
if self.path is not None:
|
||||
parts.append("path=%s" % repr(self.path))
|
||||
return "<Module %s>" % ", ".join(parts)
|
||||
|
||||
def AddGlobalName(self, name):
|
||||
self.globalNames[name] = None
|
||||
|
||||
def ExcludeName(self, name):
|
||||
self.excludeNames[name] = None
|
||||
|
||||
def IgnoreName(self, name):
|
||||
self.ignoreNames[name] = None
|
||||
|
@ -1,550 +0,0 @@
|
||||
"""
|
||||
Base class for freezing scripts into executables.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import distutils.sysconfig
|
||||
import imp
|
||||
import marshal
|
||||
import os
|
||||
import shutil
|
||||
import socket
|
||||
import stat
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
import cx_Freeze
|
||||
import cx_Freeze.util
|
||||
|
||||
__all__ = [ "ConfigError", "ConstantsModule", "Executable", "Freezer" ]
|
||||
|
||||
if sys.platform == "win32":
|
||||
pythonDll = "python%s%s.dll" % sys.version_info[:2]
|
||||
GLOBAL_BIN_PATH_EXCLUDES = [cx_Freeze.util.GetSystemDir()]
|
||||
GLOBAL_BIN_INCLUDES = [
|
||||
pythonDll,
|
||||
"gdiplus.dll",
|
||||
"mfc71.dll",
|
||||
"msvcp71.dll",
|
||||
"msvcr71.dll"
|
||||
]
|
||||
GLOBAL_BIN_EXCLUDES = [
|
||||
"comctl32.dll",
|
||||
"oci.dll",
|
||||
"cx_Logging.pyd"
|
||||
]
|
||||
else:
|
||||
extension = distutils.sysconfig.get_config_var("SO")
|
||||
pythonSharedLib = "libpython%s.%s%s" % \
|
||||
(sys.version_info[:2] + (extension,))
|
||||
GLOBAL_BIN_INCLUDES = [pythonSharedLib]
|
||||
GLOBAL_BIN_EXCLUDES = [
|
||||
"libclntsh.so",
|
||||
"libwtc9.so"
|
||||
]
|
||||
GLOBAL_BIN_PATH_EXCLUDES = ["/lib", "/lib32", "/lib64", "/usr/lib",
|
||||
"/usr/lib32", "/usr/lib64"]
|
||||
|
||||
|
||||
# NOTE: the try: except: block in this code is not necessary under Python 2.4
|
||||
# and higher and can be removed once support for Python 2.3 is no longer needed
|
||||
EXTENSION_LOADER_SOURCE = \
|
||||
"""
|
||||
import imp, os, sys
|
||||
|
||||
found = False
|
||||
for p in sys.path:
|
||||
if not os.path.isdir(p):
|
||||
continue
|
||||
f = os.path.join(p, "%s")
|
||||
if not os.path.exists(f):
|
||||
continue
|
||||
try:
|
||||
m = imp.load_dynamic(__name__, f)
|
||||
except ImportError:
|
||||
del sys.modules[__name__]
|
||||
raise
|
||||
sys.modules[__name__] = m
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
del sys.modules[__name__]
|
||||
raise ImportError, "No module named %%s" %% __name__
|
||||
"""
|
||||
|
||||
|
||||
class Freezer(object):
|
||||
|
||||
def __init__(self, executables, constantsModules = [], includes = [],
|
||||
excludes = [], packages = [], replacePaths = [], compress = None,
|
||||
optimizeFlag = 0, copyDependentFiles = None, initScript = None,
|
||||
base = None, path = None, createLibraryZip = None,
|
||||
appendScriptToExe = None, appendScriptToLibrary = None,
|
||||
targetDir = None, binIncludes = [], binExcludes = [],
|
||||
binPathIncludes = [], binPathExcludes = [], icon = None,
|
||||
includeFiles = []):
|
||||
self.executables = executables
|
||||
self.constantsModules = constantsModules
|
||||
self.includes = includes
|
||||
self.excludes = excludes
|
||||
self.packages = packages
|
||||
self.replacePaths = replacePaths
|
||||
self.compress = compress
|
||||
self.optimizeFlag = optimizeFlag
|
||||
self.copyDependentFiles = copyDependentFiles
|
||||
self.initScript = initScript
|
||||
self.base = base
|
||||
self.path = path
|
||||
self.createLibraryZip = createLibraryZip
|
||||
self.appendScriptToExe = appendScriptToExe
|
||||
self.appendScriptToLibrary = appendScriptToLibrary
|
||||
self.targetDir = targetDir
|
||||
self.binIncludes = [os.path.normcase(n) \
|
||||
for n in GLOBAL_BIN_INCLUDES + binIncludes]
|
||||
self.binExcludes = [os.path.normcase(n) \
|
||||
for n in GLOBAL_BIN_EXCLUDES + binExcludes]
|
||||
self.binPathIncludes = [os.path.normcase(n) for n in binPathIncludes]
|
||||
self.binPathExcludes = [os.path.normcase(n) \
|
||||
for n in GLOBAL_BIN_PATH_EXCLUDES + binPathExcludes]
|
||||
self.icon = icon
|
||||
self.includeFiles = includeFiles
|
||||
self._VerifyConfiguration()
|
||||
|
||||
def _CopyFile(self, source, target, copyDependentFiles,
|
||||
includeMode = False):
|
||||
normalizedSource = os.path.normcase(os.path.normpath(source))
|
||||
normalizedTarget = os.path.normcase(os.path.normpath(target))
|
||||
if normalizedTarget in self.filesCopied:
|
||||
return
|
||||
if normalizedSource == normalizedTarget:
|
||||
return
|
||||
self._RemoveFile(target)
|
||||
targetDir = os.path.dirname(target)
|
||||
self._CreateDirectory(targetDir)
|
||||
print "copying", source, "->", target
|
||||
shutil.copyfile(source, target)
|
||||
if includeMode:
|
||||
shutil.copymode(source, target)
|
||||
self.filesCopied[normalizedTarget] = None
|
||||
if copyDependentFiles:
|
||||
for source in self._GetDependentFiles(source):
|
||||
target = os.path.join(targetDir, os.path.basename(source))
|
||||
self._CopyFile(source, target, copyDependentFiles)
|
||||
|
||||
def _CreateDirectory(self, path):
|
||||
if not os.path.isdir(path):
|
||||
print "creating directory", path
|
||||
os.makedirs(path)
|
||||
|
||||
def _FreezeExecutable(self, exe):
|
||||
if self.createLibraryZip:
|
||||
finder = self.finder
|
||||
else:
|
||||
finder = self._GetModuleFinder(exe)
|
||||
if exe.script is None:
|
||||
scriptModule = None
|
||||
else:
|
||||
scriptModule = finder.IncludeFile(exe.script, exe.moduleName)
|
||||
self._CopyFile(exe.base, exe.targetName, exe.copyDependentFiles,
|
||||
includeMode = True)
|
||||
if exe.icon is not None:
|
||||
if sys.platform == "win32":
|
||||
cx_Freeze.util.AddIcon(exe.targetName, exe.icon)
|
||||
else:
|
||||
targetName = os.path.join(os.path.dirname(exe.targetName),
|
||||
os.path.basename(exe.icon))
|
||||
self._CopyFile(exe.icon, targetName,
|
||||
copyDependentFiles = False)
|
||||
if not os.access(exe.targetName, os.W_OK):
|
||||
mode = os.stat(exe.targetName).st_mode
|
||||
os.chmod(exe.targetName, mode | stat.S_IWUSR)
|
||||
if not exe.appendScriptToLibrary:
|
||||
if exe.appendScriptToExe:
|
||||
fileName = exe.targetName
|
||||
else:
|
||||
baseFileName, ext = os.path.splitext(exe.targetName)
|
||||
fileName = baseFileName + ".zip"
|
||||
self._RemoveFile(fileName)
|
||||
if not self.createLibraryZip and exe.copyDependentFiles:
|
||||
scriptModule = None
|
||||
self._WriteModules(fileName, exe.initScript, finder, exe.compress,
|
||||
exe.copyDependentFiles, scriptModule)
|
||||
|
||||
def _GetBaseFileName(self, argsSource = None):
|
||||
if argsSource is None:
|
||||
argsSource = self
|
||||
name = argsSource.base
|
||||
if name is None:
|
||||
if argsSource.copyDependentFiles:
|
||||
name = "Console"
|
||||
else:
|
||||
name = "ConsoleKeepPath"
|
||||
argsSource.base = self._GetFileName("bases", name)
|
||||
if argsSource.base is None:
|
||||
raise ConfigError("no base named %s", name)
|
||||
|
||||
def _GetDependentFiles(self, path):
|
||||
dependentFiles = self.dependentFiles.get(path)
|
||||
if dependentFiles is None:
|
||||
if sys.platform == "win32":
|
||||
origPath = os.environ["PATH"]
|
||||
os.environ["PATH"] = origPath + os.pathsep + \
|
||||
os.pathsep.join(sys.path)
|
||||
dependentFiles = cx_Freeze.util.GetDependentFiles(path)
|
||||
os.environ["PATH"] = origPath
|
||||
else:
|
||||
dependentFiles = []
|
||||
for line in os.popen('ldd "%s"' % path):
|
||||
parts = line.strip().split(" => ")
|
||||
if len(parts) != 2:
|
||||
continue
|
||||
dependentFile = parts[1]
|
||||
if dependentFile == "not found":
|
||||
print "WARNING: cannot find", parts[0]
|
||||
continue
|
||||
pos = dependentFile.find(" (")
|
||||
if pos >= 0:
|
||||
dependentFile = dependentFile[:pos].strip()
|
||||
if dependentFile:
|
||||
dependentFiles.append(dependentFile)
|
||||
dependentFiles = self.dependentFiles[path] = \
|
||||
[f for f in dependentFiles if self._ShouldCopyFile(f)]
|
||||
return dependentFiles
|
||||
|
||||
def _GetFileName(self, dir, name):
|
||||
if os.path.isabs(name):
|
||||
return name
|
||||
name = os.path.normcase(name)
|
||||
fullDir = os.path.join(os.path.dirname(cx_Freeze.__file__), dir)
|
||||
if os.path.isdir(fullDir):
|
||||
for fileName in os.listdir(fullDir):
|
||||
if name == os.path.splitext(os.path.normcase(fileName))[0]:
|
||||
return os.path.join(fullDir, fileName)
|
||||
|
||||
def _GetInitScriptFileName(self, argsSource = None):
|
||||
if argsSource is None:
|
||||
argsSource = self
|
||||
name = argsSource.initScript
|
||||
if name is None:
|
||||
if argsSource.copyDependentFiles:
|
||||
name = "Console"
|
||||
else:
|
||||
name = "ConsoleKeepPath"
|
||||
argsSource.initScript = self._GetFileName("initscripts", name)
|
||||
if argsSource.initScript is None:
|
||||
raise ConfigError("no initscript named %s", name)
|
||||
|
||||
def _GetModuleFinder(self, argsSource = None):
|
||||
if argsSource is None:
|
||||
argsSource = self
|
||||
finder = cx_Freeze.ModuleFinder(self.includeFiles, argsSource.excludes,
|
||||
argsSource.path, argsSource.replacePaths)
|
||||
if argsSource.copyDependentFiles:
|
||||
finder.IncludeModule("imp")
|
||||
finder.IncludeModule("os")
|
||||
finder.IncludeModule("sys")
|
||||
if argsSource.compress:
|
||||
finder.IncludeModule("zlib")
|
||||
for name in argsSource.includes:
|
||||
finder.IncludeModule(name)
|
||||
for name in argsSource.packages:
|
||||
finder.IncludePackage(name)
|
||||
return finder
|
||||
|
||||
def _PrintReport(self, fileName, modules):
|
||||
print "writing zip file", fileName
|
||||
print
|
||||
print " %-25s %s" % ("Name", "File")
|
||||
print " %-25s %s" % ("----", "----")
|
||||
for module in modules:
|
||||
if module.path:
|
||||
print "P",
|
||||
else:
|
||||
print "m",
|
||||
print "%-25s" % module.name, module.file or ""
|
||||
print
|
||||
|
||||
def _RemoveFile(self, path):
|
||||
if os.path.exists(path):
|
||||
os.chmod(path, 0777)
|
||||
os.remove(path)
|
||||
|
||||
def _ShouldCopyFile(self, path):
|
||||
dir, name = os.path.split(os.path.normcase(path))
|
||||
parts = name.split(".")
|
||||
tweaked = False
|
||||
while True:
|
||||
if not parts[-1].isdigit():
|
||||
break
|
||||
parts.pop(-1)
|
||||
tweaked = True
|
||||
if tweaked:
|
||||
name = ".".join(parts)
|
||||
if name in self.binIncludes:
|
||||
return True
|
||||
if name in self.binExcludes:
|
||||
return False
|
||||
for path in self.binPathIncludes:
|
||||
if dir.startswith(path):
|
||||
return True
|
||||
for path in self.binPathExcludes:
|
||||
if dir.startswith(path):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _VerifyCanAppendToLibrary(self):
|
||||
if not self.createLibraryZip:
|
||||
raise ConfigError("script cannot be appended to library zip if "
|
||||
"one is not being created")
|
||||
|
||||
def _VerifyConfiguration(self):
|
||||
if self.compress is None:
|
||||
self.compress = True
|
||||
if self.copyDependentFiles is None:
|
||||
self.copyDependentFiles = True
|
||||
if self.createLibraryZip is None:
|
||||
self.createLibraryZip = True
|
||||
if self.appendScriptToExe is None:
|
||||
self.appendScriptToExe = False
|
||||
if self.appendScriptToLibrary is None:
|
||||
self.appendScriptToLibrary = \
|
||||
self.createLibraryZip and not self.appendScriptToExe
|
||||
if self.targetDir is None:
|
||||
self.targetDir = os.path.abspath("dist")
|
||||
self._GetInitScriptFileName()
|
||||
self._GetBaseFileName()
|
||||
if self.path is None:
|
||||
self.path = sys.path
|
||||
if self.appendScriptToLibrary:
|
||||
self._VerifyCanAppendToLibrary()
|
||||
for sourceFileName, targetFileName in self.includeFiles:
|
||||
if not os.path.exists(sourceFileName):
|
||||
raise ConfigError("cannot find file/directory named %s",
|
||||
sourceFileName)
|
||||
if os.path.isabs(targetFileName):
|
||||
raise ConfigError("target file/directory cannot be absolute")
|
||||
for executable in self.executables:
|
||||
executable._VerifyConfiguration(self)
|
||||
|
||||
def _WriteModules(self, fileName, initScript, finder, compress,
|
||||
copyDependentFiles, scriptModule = None):
|
||||
initModule = finder.IncludeFile(initScript, "cx_Freeze__init__")
|
||||
if scriptModule is None:
|
||||
for module in self.constantsModules:
|
||||
module.Create(finder)
|
||||
modules = [m for m in finder.modules \
|
||||
if m.name not in self.excludeModules]
|
||||
else:
|
||||
modules = [initModule, scriptModule]
|
||||
self.excludeModules[initModule.name] = None
|
||||
self.excludeModules[scriptModule.name] = None
|
||||
itemsToSort = [(m.name, m) for m in modules]
|
||||
itemsToSort.sort()
|
||||
modules = [m for n, m in itemsToSort]
|
||||
self._PrintReport(fileName, modules)
|
||||
if scriptModule is None:
|
||||
finder.ReportMissingModules()
|
||||
targetDir = os.path.dirname(fileName)
|
||||
self._CreateDirectory(targetDir)
|
||||
filesToCopy = []
|
||||
if os.path.exists(fileName):
|
||||
mode = "a"
|
||||
else:
|
||||
mode = "w"
|
||||
outFile = zipfile.PyZipFile(fileName, mode, zipfile.ZIP_DEFLATED)
|
||||
for module in modules:
|
||||
if module.code is None and module.file is not None:
|
||||
fileName = os.path.basename(module.file)
|
||||
baseFileName, ext = os.path.splitext(fileName)
|
||||
if baseFileName != module.name and module.name != "zlib":
|
||||
if "." in module.name:
|
||||
fileName = module.name + ext
|
||||
generatedFileName = "ExtensionLoader_%s.py" % \
|
||||
module.name.replace(".", "_")
|
||||
module.code = compile(EXTENSION_LOADER_SOURCE % fileName,
|
||||
generatedFileName, "exec")
|
||||
target = os.path.join(targetDir, fileName)
|
||||
filesToCopy.append((module, target))
|
||||
if module.code is None:
|
||||
continue
|
||||
fileName = "/".join(module.name.split("."))
|
||||
if module.path:
|
||||
fileName += "/__init__"
|
||||
if module.file is not None and os.path.exists(module.file):
|
||||
mtime = os.stat(module.file).st_mtime
|
||||
else:
|
||||
mtime = time.time()
|
||||
zipTime = time.localtime(mtime)[:6]
|
||||
data = imp.get_magic() + struct.pack("<i", mtime) + \
|
||||
marshal.dumps(module.code)
|
||||
zinfo = zipfile.ZipInfo(fileName + ".pyc", zipTime)
|
||||
if compress:
|
||||
zinfo.compress_type = zipfile.ZIP_DEFLATED
|
||||
outFile.writestr(zinfo, data)
|
||||
origPath = os.environ["PATH"]
|
||||
for module, target in filesToCopy:
|
||||
try:
|
||||
if module.parent is not None:
|
||||
path = os.pathsep.join([origPath] + module.parent.path)
|
||||
os.environ["PATH"] = path
|
||||
self._CopyFile(module.file, target, copyDependentFiles)
|
||||
finally:
|
||||
os.environ["PATH"] = origPath
|
||||
|
||||
def Freeze(self):
|
||||
self.finder = None
|
||||
self.excludeModules = {}
|
||||
self.dependentFiles = {}
|
||||
self.filesCopied = {}
|
||||
cx_Freeze.util.SetOptimizeFlag(self.optimizeFlag)
|
||||
if self.createLibraryZip:
|
||||
self.finder = self._GetModuleFinder()
|
||||
for executable in self.executables:
|
||||
self._FreezeExecutable(executable)
|
||||
if self.createLibraryZip:
|
||||
fileName = os.path.join(self.targetDir, "library.zip")
|
||||
self._RemoveFile(fileName)
|
||||
self._WriteModules(fileName, self.initScript, self.finder,
|
||||
self.compress, self.copyDependentFiles)
|
||||
for sourceFileName, targetFileName in self.includeFiles:
|
||||
fullName = os.path.join(self.targetDir, targetFileName)
|
||||
if os.path.isdir(sourceFileName):
|
||||
for path, dirNames, fileNames in os.walk(sourceFileName):
|
||||
shortPath = path[len(sourceFileName) + 1:]
|
||||
if ".svn" in dirNames:
|
||||
dirNames.remove(".svn")
|
||||
if "CVS" in dirNames:
|
||||
dirNames.remove("CVS")
|
||||
for fileName in fileNames:
|
||||
fullSourceName = os.path.join(path, fileName)
|
||||
fullTargetName = os.path.join(self.targetDir,
|
||||
targetFileName, shortPath, fileName)
|
||||
self._CopyFile(fullSourceName, fullTargetName,
|
||||
copyDependentFiles = False)
|
||||
else:
|
||||
self._CopyFile(sourceFileName, fullName,
|
||||
copyDependentFiles = False)
|
||||
|
||||
|
||||
class ConfigError(Exception):
|
||||
|
||||
def __init__(self, format, *args):
|
||||
self.what = format % args
|
||||
|
||||
def __str__(self):
|
||||
return self.what
|
||||
|
||||
|
||||
class Executable(object):
|
||||
|
||||
def __init__(self, script, initScript = None, base = None, path = None,
|
||||
targetDir = None, targetName = None, includes = None,
|
||||
excludes = None, packages = None, replacePaths = None,
|
||||
compress = None, copyDependentFiles = None,
|
||||
appendScriptToExe = None, appendScriptToLibrary = None,
|
||||
icon = None):
|
||||
self.script = script
|
||||
self.initScript = initScript
|
||||
self.base = base
|
||||
self.path = path
|
||||
self.targetDir = targetDir
|
||||
self.targetName = targetName
|
||||
self.includes = includes
|
||||
self.excludes = excludes
|
||||
self.packages = packages
|
||||
self.replacePaths = replacePaths
|
||||
self.compress = compress
|
||||
self.copyDependentFiles = copyDependentFiles
|
||||
self.appendScriptToExe = appendScriptToExe
|
||||
self.appendScriptToLibrary = appendScriptToLibrary
|
||||
self.icon = icon
|
||||
|
||||
def __repr__(self):
|
||||
return "<Executable script=%s>" % self.script
|
||||
|
||||
def _VerifyConfiguration(self, freezer):
|
||||
if self.path is None:
|
||||
self.path = freezer.path
|
||||
if self.targetDir is None:
|
||||
self.targetDir = freezer.targetDir
|
||||
if self.includes is None:
|
||||
self.includes = freezer.includes
|
||||
if self.excludes is None:
|
||||
self.excludes = freezer.excludes
|
||||
if self.packages is None:
|
||||
self.packages = freezer.packages
|
||||
if self.replacePaths is None:
|
||||
self.replacePaths = freezer.replacePaths
|
||||
if self.compress is None:
|
||||
self.compress = freezer.compress
|
||||
if self.copyDependentFiles is None:
|
||||
self.copyDependentFiles = freezer.copyDependentFiles
|
||||
if self.appendScriptToExe is None:
|
||||
self.appendScriptToExe = freezer.appendScriptToExe
|
||||
if self.appendScriptToLibrary is None:
|
||||
self.appendScriptToLibrary = freezer.appendScriptToLibrary
|
||||
if self.initScript is None:
|
||||
self.initScript = freezer.initScript
|
||||
else:
|
||||
freezer._GetInitScriptFileName(self)
|
||||
if self.base is None:
|
||||
self.base = freezer.base
|
||||
else:
|
||||
freezer._GetBaseFileName(self)
|
||||
if self.appendScriptToLibrary:
|
||||
freezer._VerifyCanAppendToLibrary()
|
||||
if self.icon is None:
|
||||
self.icon = freezer.icon
|
||||
if self.script is not None:
|
||||
name, ext = os.path.splitext(os.path.basename(self.script))
|
||||
if self.appendScriptToLibrary:
|
||||
self.moduleName = "%s__main__" % os.path.normcase(name)
|
||||
else:
|
||||
self.moduleName = "__main__"
|
||||
if self.targetName is None:
|
||||
baseName, ext = os.path.splitext(self.base)
|
||||
self.targetName = name + ext
|
||||
self.targetName = os.path.join(self.targetDir, self.targetName)
|
||||
|
||||
|
||||
class ConstantsModule(object):
|
||||
|
||||
def __init__(self, releaseString = None, copyright = None,
|
||||
moduleName = "BUILD_CONSTANTS", timeFormat = "%B %d, %Y %H:%M:%S"):
|
||||
self.moduleName = moduleName
|
||||
self.timeFormat = timeFormat
|
||||
self.values = {}
|
||||
self.values["BUILD_RELEASE_STRING"] = releaseString
|
||||
self.values["BUILD_COPYRIGHT"] = copyright
|
||||
|
||||
def Create(self, finder):
|
||||
"""Create the module which consists of declaration statements for each
|
||||
of the values."""
|
||||
today = datetime.datetime.today()
|
||||
sourceTimestamp = 0
|
||||
for module in finder.modules:
|
||||
if module.file is None:
|
||||
continue
|
||||
if module.inZipFile:
|
||||
continue
|
||||
if not os.path.exists(module.file):
|
||||
raise ConfigError("no file named %s", module.file)
|
||||
timestamp = os.stat(module.file).st_mtime
|
||||
sourceTimestamp = max(sourceTimestamp, timestamp)
|
||||
sourceTimestamp = datetime.datetime.fromtimestamp(sourceTimestamp)
|
||||
self.values["BUILD_TIMESTAMP"] = today.strftime(self.timeFormat)
|
||||
self.values["BUILD_HOST"] = socket.gethostname().split(".")[0]
|
||||
self.values["SOURCE_TIMESTAMP"] = \
|
||||
sourceTimestamp.strftime(self.timeFormat)
|
||||
module = finder._AddModule(self.moduleName)
|
||||
sourceParts = []
|
||||
names = self.values.keys()
|
||||
names.sort()
|
||||
for name in names:
|
||||
value = self.values[name]
|
||||
sourceParts.append("%s = %r" % (name, value))
|
||||
source = "\n".join(sourceParts)
|
||||
module.code = compile(source, "%s.py" % self.moduleName, "exec")
|
||||
|
@ -1,281 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
def initialize(finder):
|
||||
"""upon initialization of the finder, this routine is called to set up some
|
||||
automatic exclusions for various platforms."""
|
||||
finder.ExcludeModule("FCNTL")
|
||||
finder.ExcludeModule("os.path")
|
||||
if os.name == "nt":
|
||||
finder.ExcludeModule("fcntl")
|
||||
finder.ExcludeModule("grp")
|
||||
finder.ExcludeModule("pwd")
|
||||
finder.ExcludeModule("termios")
|
||||
else:
|
||||
finder.ExcludeModule("_winreg")
|
||||
finder.ExcludeModule("msilib")
|
||||
finder.ExcludeModule("msvcrt")
|
||||
finder.ExcludeModule("nt")
|
||||
if os.name not in ("os2", "ce"):
|
||||
finder.ExcludeModule("ntpath")
|
||||
finder.ExcludeModule("nturl2path")
|
||||
finder.ExcludeModule("pythoncom")
|
||||
finder.ExcludeModule("pywintypes")
|
||||
finder.ExcludeModule("winerror")
|
||||
finder.ExcludeModule("winsound")
|
||||
finder.ExcludeModule("win32api")
|
||||
finder.ExcludeModule("win32con")
|
||||
finder.ExcludeModule("win32event")
|
||||
finder.ExcludeModule("win32file")
|
||||
finder.ExcludeModule("win32pdh")
|
||||
finder.ExcludeModule("win32pipe")
|
||||
finder.ExcludeModule("win32process")
|
||||
finder.ExcludeModule("win32security")
|
||||
finder.ExcludeModule("win32service")
|
||||
finder.ExcludeModule("wx.activex")
|
||||
if os.name != "posix":
|
||||
finder.ExcludeModule("posix")
|
||||
if os.name != "mac":
|
||||
finder.ExcludeModule("Carbon")
|
||||
finder.ExcludeModule("gestalt")
|
||||
finder.ExcludeModule("ic")
|
||||
finder.ExcludeModule("mac")
|
||||
finder.ExcludeModule("MacOS")
|
||||
finder.ExcludeModule("macpath")
|
||||
finder.ExcludeModule("macurl2path")
|
||||
if os.name != "nt":
|
||||
finder.ExcludeModule("EasyDialogs")
|
||||
if os.name != "os2":
|
||||
finder.ExcludeModule("os2")
|
||||
finder.ExcludeModule("os2emxpath")
|
||||
finder.ExcludeModule("_emx_link")
|
||||
if os.name != "ce":
|
||||
finder.ExcludeModule("ce")
|
||||
if os.name != "riscos":
|
||||
finder.ExcludeModule("riscos")
|
||||
finder.ExcludeModule("riscosenviron")
|
||||
finder.ExcludeModule("riscospath")
|
||||
finder.ExcludeModule("rourl2path")
|
||||
if sys.platform[:4] != "java":
|
||||
finder.ExcludeModule("java.lang")
|
||||
finder.ExcludeModule("org.python.core")
|
||||
|
||||
|
||||
def load_cElementTree(finder, module):
|
||||
"""the cElementTree module implicitly loads the elementtree.ElementTree
|
||||
module; make sure this happens."""
|
||||
finder.IncludeModule("elementtree.ElementTree")
|
||||
|
||||
|
||||
def load_ceODBC(finder, module):
|
||||
"""the ceODBC module implicitly imports both datetime and decimal; make
|
||||
sure this happens."""
|
||||
finder.IncludeModule("datetime")
|
||||
finder.IncludeModule("decimal")
|
||||
|
||||
|
||||
def load_cx_Oracle(finder, module):
|
||||
"""the cx_Oracle module implicitly imports datetime; make sure this
|
||||
happens."""
|
||||
finder.IncludeModule("datetime")
|
||||
|
||||
|
||||
def load_docutils_frontend(finder, module):
|
||||
"""The optik module is the old name for the optparse module; ignore the
|
||||
module if it cannot be found."""
|
||||
module.IgnoreName("optik")
|
||||
|
||||
|
||||
def load_dummy_threading(finder, module):
|
||||
"""the dummy_threading module plays games with the name of the threading
|
||||
module for its own purposes; ignore that here"""
|
||||
finder.ExcludeModule("_dummy_threading")
|
||||
|
||||
|
||||
def load_email(finder, module):
|
||||
"""the email package has a bunch of aliases as the submodule names were
|
||||
all changed to lowercase in Python 2.5; mimic that here."""
|
||||
if sys.version_info[:2] >= (2, 5):
|
||||
for name in ("Charset", "Encoders", "Errors", "FeedParser",
|
||||
"Generator", "Header", "Iterators", "Message", "Parser",
|
||||
"Utils", "base64MIME", "quopriMIME"):
|
||||
finder.AddAlias("email.%s" % name, "email.%s" % name.lower())
|
||||
|
||||
|
||||
def load_ftplib(finder, module):
|
||||
"""the ftplib module attempts to import the SOCKS module; ignore this
|
||||
module if it cannot be found"""
|
||||
module.IgnoreName("SOCKS")
|
||||
|
||||
|
||||
def load_matplotlib(finder, module):
|
||||
"""the matplotlib module requires data to be found in mpl-data in the
|
||||
same directory as the frozen executable so oblige it"""
|
||||
dir = os.path.join(module.path[0], "mpl-data")
|
||||
finder.IncludeFiles(dir, "mpl-data")
|
||||
|
||||
|
||||
def load_matplotlib_numerix(finder, module):
|
||||
"""the numpy.numerix module loads a number of modules dynamically"""
|
||||
for name in ("ma", "fft", "linear_algebra", "random_array", "mlab"):
|
||||
finder.IncludeModule("%s.%s" % (module.name, name))
|
||||
|
||||
|
||||
def load_numpy_linalg(finder, module):
|
||||
"""the numpy.linalg module implicitly loads the lapack_lite module; make
|
||||
sure this happens"""
|
||||
finder.IncludeModule("numpy.linalg.lapack_lite")
|
||||
|
||||
|
||||
def load_pty(finder, module):
|
||||
"""The sgi module is not needed for this module to function."""
|
||||
module.IgnoreName("sgi")
|
||||
|
||||
|
||||
def load_pythoncom(finder, module):
|
||||
"""the pythoncom module is actually contained in a DLL but since those
|
||||
cannot be loaded directly in Python 2.5 and higher a special module is
|
||||
used to perform that task; simply use that technique directly to
|
||||
determine the name of the DLL and ensure it is included as a normal
|
||||
extension; also load the pywintypes module which is implicitly
|
||||
loaded."""
|
||||
import pythoncom
|
||||
module.file = pythoncom.__file__
|
||||
module.code = None
|
||||
finder.IncludeModule("pywintypes")
|
||||
|
||||
|
||||
def load_pywintypes(finder, module):
|
||||
"""the pywintypes module is actually contained in a DLL but since those
|
||||
cannot be loaded directly in Python 2.5 and higher a special module is
|
||||
used to perform that task; simply use that technique directly to
|
||||
determine the name of the DLL and ensure it is included as a normal
|
||||
extension."""
|
||||
import pywintypes
|
||||
module.file = pywintypes.__file__
|
||||
module.code = None
|
||||
|
||||
|
||||
def load_PyQt4_Qt(finder, module):
|
||||
"""the PyQt4.Qt module is an extension module which imports a number of
|
||||
other modules and injects their namespace into its own. It seems a
|
||||
foolish way of doing things but perhaps there is some hidden advantage
|
||||
to this technique over pure Python; ignore the absence of some of
|
||||
the modules since not every installation includes all of them."""
|
||||
finder.IncludeModule("PyQt4.QtCore")
|
||||
finder.IncludeModule("PyQt4.QtGui")
|
||||
finder.IncludeModule("sip")
|
||||
for name in ("PyQt4.QtSvg", "PyQt4.Qsci", "PyQt4.QtAssistant",
|
||||
"PyQt4.QtNetwork", "PyQt4.QtOpenGL", "PyQt4.QtScript", "PyQt4._qt",
|
||||
"PyQt4.QtSql", "PyQt4.QtSvg", "PyQt4.QtTest", "PyQt4.QtXml"):
|
||||
try:
|
||||
finder.IncludeModule(name)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def load_Tkinter(finder, module):
|
||||
"""the Tkinter module has data files that are required to be loaded so
|
||||
ensure that they are copied into the directory that is expected at
|
||||
runtime."""
|
||||
import Tkinter
|
||||
import _tkinter
|
||||
tk = _tkinter.create()
|
||||
tclDir = os.path.dirname(tk.call("info", "library"))
|
||||
tclSourceDir = os.path.join(tclDir, "tcl%s" % _tkinter.TCL_VERSION)
|
||||
tkSourceDir = os.path.join(tclDir, "tk%s" % _tkinter.TK_VERSION)
|
||||
finder.IncludeFiles(tclSourceDir, "tcl")
|
||||
finder.IncludeFiles(tkSourceDir, "tk")
|
||||
|
||||
|
||||
def load_tempfile(finder, module):
|
||||
"""the tempfile module attempts to load the fcntl and thread modules but
|
||||
continues if these modules cannot be found; ignore these modules if they
|
||||
cannot be found."""
|
||||
module.IgnoreName("fcntl")
|
||||
module.IgnoreName("thread")
|
||||
|
||||
|
||||
def load_time(finder, module):
|
||||
"""the time module implicitly loads _strptime; make sure this happens."""
|
||||
finder.IncludeModule("_strptime")
|
||||
|
||||
|
||||
def load_win32api(finder, module):
|
||||
"""the win32api module implicitly loads the pywintypes module; make sure
|
||||
this happens."""
|
||||
finder.IncludeModule("pywintypes")
|
||||
|
||||
|
||||
def load_win32com(finder, module):
|
||||
"""the win32com package manipulates its search path at runtime to include
|
||||
the sibling directory called win32comext; simulate that by changing the
|
||||
search path in a similar fashion here."""
|
||||
baseDir = os.path.dirname(os.path.dirname(module.file))
|
||||
module.path.append(os.path.join(baseDir, "win32comext"))
|
||||
|
||||
|
||||
def load_win32file(finder, module):
|
||||
"""the win32api module implicitly loads the pywintypes module; make sure
|
||||
this happens."""
|
||||
finder.IncludeModule("pywintypes")
|
||||
|
||||
|
||||
def load_xml(finder, module):
|
||||
"""the builtin xml package attempts to load the _xmlplus module to see if
|
||||
that module should take its role instead; ignore the failure to find
|
||||
this module, though."""
|
||||
module.IgnoreName("_xmlplus")
|
||||
|
||||
|
||||
def load_xml_etree_cElementTree(finder, module):
|
||||
"""the xml.etree.cElementTree module implicitly loads the
|
||||
xml.etree.ElementTree module; make sure this happens."""
|
||||
finder.IncludeModule("xml.etree.ElementTree")
|
||||
|
||||
def load_IPython(finder, module):
|
||||
ipy = os.path.join(os.path.dirname(module.file), 'Extensions')
|
||||
extensions = set([])
|
||||
for m in os.listdir(ipy):
|
||||
extensions.add(os.path.splitext(m)[0])
|
||||
extensions.remove('__init__')
|
||||
for m in extensions:
|
||||
finder.IncludeModule('IPython.Extensions.'+m)
|
||||
|
||||
def load_lxml(finder, module):
|
||||
finder.IncludeModule('lxml._elementpath')
|
||||
|
||||
def load_cherrypy(finder, module):
|
||||
finder.IncludeModule('cherrypy.lib.encoding')
|
||||
|
||||
def missing_cElementTree(finder, caller):
|
||||
"""the cElementTree has been incorporated into the standard library in
|
||||
Python 2.5 so ignore its absence if it cannot found."""
|
||||
if sys.version_info[:2] >= (2, 5):
|
||||
caller.IgnoreName("cElementTree")
|
||||
|
||||
|
||||
def missing_EasyDialogs(finder, caller):
|
||||
"""the EasyDialogs module is not normally present on Windows but it also
|
||||
may be so instead of excluding it completely, ignore it if it can't be
|
||||
found"""
|
||||
if sys.platform == "win32":
|
||||
caller.IgnoreName("EasyDialogs")
|
||||
|
||||
|
||||
def missing_readline(finder, caller):
|
||||
"""the readline module is not normally present on Windows but it also may
|
||||
be so instead of excluding it completely, ignore it if it can't be
|
||||
found"""
|
||||
if sys.platform == "win32":
|
||||
caller.IgnoreName("readline")
|
||||
|
||||
|
||||
def missing_xml_etree(finder, caller):
|
||||
"""the xml.etree package is new for Python 2.5 but it is common practice
|
||||
to use a try..except.. block in order to support versions earlier than
|
||||
Python 2.5 transparently; ignore the absence of the package in this
|
||||
situation."""
|
||||
if sys.version_info[:2] < (2, 5):
|
||||
caller.IgnoreName("xml.etree")
|
||||
|
@ -1,171 +0,0 @@
|
||||
import optparse
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import sys
|
||||
|
||||
import cx_Freeze
|
||||
|
||||
__all__ = ["main"]
|
||||
|
||||
USAGE = \
|
||||
"""
|
||||
%prog [options] [SCRIPT]
|
||||
|
||||
Freeze a Python script and all of its referenced modules to a base
|
||||
executable which can then be distributed without requiring a Python
|
||||
installation."""
|
||||
|
||||
VERSION = \
|
||||
"""
|
||||
%%prog %s
|
||||
Copyright (c) 2007-2008 Colt Engineering. All rights reserved.
|
||||
Copyright (c) 2001-2006 Computronix Corporation. All rights reserved.""" % \
|
||||
cx_Freeze.version
|
||||
|
||||
|
||||
def ParseCommandLine():
|
||||
parser = optparse.OptionParser(version = VERSION.strip(),
|
||||
usage = USAGE.strip())
|
||||
parser.add_option("-O",
|
||||
action = "count",
|
||||
default = 0,
|
||||
dest = "optimized",
|
||||
help = "optimize generated bytecode as per PYTHONOPTIMIZE; "
|
||||
"use -OO in order to remove doc strings")
|
||||
parser.add_option("-c", "--compress",
|
||||
action = "store_true",
|
||||
dest = "compress",
|
||||
help = "compress byte code in zip files")
|
||||
parser.add_option("--base-name",
|
||||
dest = "baseName",
|
||||
metavar = "NAME",
|
||||
help = "file on which to base the target file; if the name of the "
|
||||
"file is not an absolute file name, the subdirectory bases "
|
||||
"(rooted in the directory in which the freezer is found) "
|
||||
"will be searched for a file matching the name")
|
||||
parser.add_option("--init-script",
|
||||
dest = "initScript",
|
||||
metavar = "NAME",
|
||||
help = "script which will be executed upon startup; if the name "
|
||||
"of the file is not an absolute file name, the "
|
||||
"subdirectory initscripts (rooted in the directory in "
|
||||
"which the cx_Freeze package is found) will be searched "
|
||||
"for a file matching the name")
|
||||
parser.add_option("--target-dir", "--install-dir",
|
||||
dest = "targetDir",
|
||||
metavar = "DIR",
|
||||
help = "the directory in which to place the target file and "
|
||||
"any dependent files")
|
||||
parser.add_option("--target-name",
|
||||
dest = "targetName",
|
||||
metavar = "NAME",
|
||||
help = "the name of the file to create instead of the base name "
|
||||
"of the script and the extension of the base binary")
|
||||
parser.add_option("--no-copy-deps",
|
||||
dest = "copyDeps",
|
||||
default = True,
|
||||
action = "store_false",
|
||||
help = "do not copy the dependent files (extensions, shared "
|
||||
"libraries, etc.) to the target directory; this also "
|
||||
"modifies the default init script to ConsoleKeepPath.py "
|
||||
"and means that the target executable requires a Python "
|
||||
"installation to execute properly")
|
||||
parser.add_option("--default-path",
|
||||
action = "append",
|
||||
dest = "defaultPath",
|
||||
metavar = "DIRS",
|
||||
help = "list of paths separated by the standard path separator "
|
||||
"for the platform which will be used to initialize "
|
||||
"sys.path prior to running the module finder")
|
||||
parser.add_option("--include-path",
|
||||
action = "append",
|
||||
dest = "includePath",
|
||||
metavar = "DIRS",
|
||||
help = "list of paths separated by the standard path separator "
|
||||
"for the platform which will be used to modify sys.path "
|
||||
"prior to running the module finder")
|
||||
parser.add_option("--replace-paths",
|
||||
dest = "replacePaths",
|
||||
metavar = "DIRECTIVES",
|
||||
help = "replace all the paths in modules found in the given paths "
|
||||
"with the given replacement string; multiple values are "
|
||||
"separated by the standard path separator and each value "
|
||||
"is of the form path=replacement_string; path can be * "
|
||||
"which means all paths not already specified")
|
||||
parser.add_option("--include-modules",
|
||||
dest = "includeModules",
|
||||
metavar = "NAMES",
|
||||
help = "comma separated list of modules to include")
|
||||
parser.add_option("--exclude-modules",
|
||||
dest = "excludeModules",
|
||||
metavar = "NAMES",
|
||||
help = "comma separated list of modules to exclude")
|
||||
parser.add_option("--ext-list-file",
|
||||
dest = "extListFile",
|
||||
metavar = "NAME",
|
||||
help = "name of file in which to place the list of dependent files "
|
||||
"which were copied into the target directory")
|
||||
parser.add_option("-z", "--zip-include",
|
||||
dest = "zipIncludes",
|
||||
action = "append",
|
||||
default = [],
|
||||
metavar = "SPEC",
|
||||
help = "name of file to add to the zip file or a specification of "
|
||||
"the form name=arcname which will specify the archive name "
|
||||
"to use; multiple --zip-include arguments can be used")
|
||||
options, args = parser.parse_args()
|
||||
if len(args) == 0:
|
||||
options.script = None
|
||||
elif len(args) == 1:
|
||||
options.script, = args
|
||||
else:
|
||||
parser.error("only one script can be specified")
|
||||
if not args and options.includeModules is None and options.copyDeps:
|
||||
parser.error("script or a list of modules must be specified")
|
||||
if not args and options.targetName is None:
|
||||
parser.error("script or a target name must be specified")
|
||||
if options.excludeModules:
|
||||
options.excludeModules = options.excludeModules.split(",")
|
||||
else:
|
||||
options.excludeModules = []
|
||||
if options.includeModules:
|
||||
options.includeModules = options.includeModules.split(",")
|
||||
else:
|
||||
options.includeModules = []
|
||||
replacePaths = []
|
||||
if options.replacePaths:
|
||||
for directive in options.replacePaths.split(os.pathsep):
|
||||
fromPath, replacement = directive.split("=")
|
||||
replacePaths.append((fromPath, replacement))
|
||||
options.replacePaths = replacePaths
|
||||
if options.defaultPath is not None:
|
||||
sys.path = [p for mp in options.defaultPath \
|
||||
for p in mp.split(os.pathsep)]
|
||||
if options.includePath is not None:
|
||||
paths = [p for mp in options.includePath for p in mp.split(os.pathsep)]
|
||||
sys.path = paths + sys.path
|
||||
if options.script is not None:
|
||||
sys.path.insert(0, os.path.dirname(options.script))
|
||||
return options
|
||||
|
||||
|
||||
def main():
|
||||
options = ParseCommandLine()
|
||||
executables = [cx_Freeze.Executable(options.script,
|
||||
targetName = options.targetName)]
|
||||
freezer = cx_Freeze.Freezer(executables,
|
||||
includes = options.includeModules,
|
||||
excludes = options.excludeModules,
|
||||
replacePaths = options.replacePaths,
|
||||
compress = options.compress,
|
||||
optimizeFlag = options.optimized,
|
||||
copyDependentFiles = options.copyDeps,
|
||||
initScript = options.initScript,
|
||||
base = options.baseName,
|
||||
path = None,
|
||||
createLibraryZip = False,
|
||||
appendScriptToExe = True,
|
||||
targetDir = options.targetDir)
|
||||
freezer.Freeze()
|
||||
|
@ -1,337 +0,0 @@
|
||||
import distutils.command.bdist_msi
|
||||
import msilib
|
||||
import os
|
||||
|
||||
__all__ = [ "bdist_msi" ]
|
||||
|
||||
# force the remove existing products action to happen first since Windows
|
||||
# installer appears to be braindead and doesn't handle files shared between
|
||||
# different "products" very well
|
||||
sequence = msilib.sequence.InstallExecuteSequence
|
||||
for index, info in enumerate(sequence):
|
||||
if info[0] == u'RemoveExistingProducts':
|
||||
sequence[index] = (info[0], info[1], 1450)
|
||||
|
||||
|
||||
class bdist_msi(distutils.command.bdist_msi.bdist_msi):
|
||||
user_options = distutils.command.bdist_msi.bdist_msi.user_options + [
|
||||
('add-to-path=', None, 'add target dir to PATH environment variable'),
|
||||
('upgrade-code=', None, 'upgrade code to use')
|
||||
]
|
||||
x = y = 50
|
||||
width = 370
|
||||
height = 300
|
||||
title = "[ProductName] Setup"
|
||||
modeless = 1
|
||||
modal = 3
|
||||
|
||||
def add_config(self, fullname):
|
||||
initialTargetDir = self.get_initial_target_dir(fullname)
|
||||
if self.add_to_path is None:
|
||||
self.add_to_path = False
|
||||
for executable in self.distribution.executables:
|
||||
if os.path.basename(executable.base).startswith("Console"):
|
||||
self.add_to_path = True
|
||||
break
|
||||
if self.add_to_path:
|
||||
msilib.add_data(self.db, 'Environment',
|
||||
[("E_PATH", "Path", r"[~];[TARGETDIR]", "TARGETDIR")])
|
||||
msilib.add_data(self.db, 'CustomAction',
|
||||
[("InitialTargetDir", 256 + 51, "TARGETDIR", initialTargetDir)
|
||||
])
|
||||
msilib.add_data(self.db, 'InstallExecuteSequence',
|
||||
[("InitialTargetDir", 'TARGETDIR=""', 401)])
|
||||
msilib.add_data(self.db, 'InstallUISequence',
|
||||
[("PrepareDlg", None, 140),
|
||||
("InitialTargetDir", 'TARGETDIR=""', 401),
|
||||
("SelectDirectoryDlg", "not Installed", 1230),
|
||||
("MaintenanceTypeDlg",
|
||||
"Installed and not Resume and not Preselected", 1250),
|
||||
("ProgressDlg", None, 1280)
|
||||
])
|
||||
|
||||
def add_cancel_dialog(self):
|
||||
dialog = msilib.Dialog(self.db, "CancelDlg", 50, 10, 260, 85, 3,
|
||||
self.title, "No", "No", "No")
|
||||
dialog.text("Text", 48, 15, 194, 30, 3,
|
||||
"Are you sure you want to cancel [ProductName] installation?")
|
||||
button = dialog.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No")
|
||||
button.event("EndDialog", "Exit")
|
||||
button = dialog.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes")
|
||||
button.event("EndDialog", "Return")
|
||||
|
||||
def add_error_dialog(self):
|
||||
dialog = msilib.Dialog(self.db, "ErrorDlg", 50, 10, 330, 101, 65543,
|
||||
self.title, "ErrorText", None, None)
|
||||
dialog.text("ErrorText", 50, 9, 280, 48, 3, "")
|
||||
for text, x in [("No", 120), ("Yes", 240), ("Abort", 0),
|
||||
("Cancel", 42), ("Ignore", 81), ("Ok", 159), ("Retry", 198)]:
|
||||
button = dialog.pushbutton(text[0], x, 72, 81, 21, 3, text, None)
|
||||
button.event("EndDialog", "Error%s" % text)
|
||||
|
||||
def add_exit_dialog(self):
|
||||
dialog = distutils.command.bdist_msi.PyDialog(self.db, "ExitDialog",
|
||||
self.x, self.y, self.width, self.height, self.modal,
|
||||
self.title, "Finish", "Finish", "Finish")
|
||||
dialog.title("Completing the [ProductName] installer")
|
||||
dialog.back("< Back", "Finish", active = False)
|
||||
dialog.cancel("Cancel", "Back", active = False)
|
||||
dialog.text("Description", 15, 235, 320, 20, 0x30003,
|
||||
"Click the Finish button to exit the installer.")
|
||||
button = dialog.next("Finish", "Cancel", name = "Finish")
|
||||
button.event("EndDialog", "Return")
|
||||
|
||||
def add_fatal_error_dialog(self):
|
||||
dialog = distutils.command.bdist_msi.PyDialog(self.db, "FatalError",
|
||||
self.x, self.y, self.width, self.height, self.modal,
|
||||
self.title, "Finish", "Finish", "Finish")
|
||||
dialog.title("[ProductName] installer ended prematurely")
|
||||
dialog.back("< Back", "Finish", active = False)
|
||||
dialog.cancel("Cancel", "Back", active = False)
|
||||
dialog.text("Description1", 15, 70, 320, 80, 0x30003,
|
||||
"[ProductName] setup ended prematurely because of an error. "
|
||||
"Your system has not been modified. To install this program "
|
||||
"at a later time, please run the installation again.")
|
||||
dialog.text("Description2", 15, 155, 320, 20, 0x30003,
|
||||
"Click the Finish button to exit the installer.")
|
||||
button = dialog.next("Finish", "Cancel", name = "Finish")
|
||||
button.event("EndDialog", "Exit")
|
||||
|
||||
def add_files_in_use_dialog(self):
|
||||
dialog = distutils.command.bdist_msi.PyDialog(self.db, "FilesInUse",
|
||||
self.x, self.y, self.width, self.height, 19, self.title,
|
||||
"Retry", "Retry", "Retry", bitmap = False)
|
||||
dialog.text("Title", 15, 6, 200, 15, 0x30003,
|
||||
r"{\DlgFontBold8}Files in Use")
|
||||
dialog.text("Description", 20, 23, 280, 20, 0x30003,
|
||||
"Some files that need to be updated are currently in use.")
|
||||
dialog.text("Text", 20, 55, 330, 50, 3,
|
||||
"The following applications are using files that need to be "
|
||||
"updated by this setup. Close these applications and then "
|
||||
"click Retry to continue the installation or Cancel to exit "
|
||||
"it.")
|
||||
dialog.control("List", "ListBox", 20, 107, 330, 130, 7,
|
||||
"FileInUseProcess", None, None, None)
|
||||
button = dialog.back("Exit", "Ignore", name = "Exit")
|
||||
button.event("EndDialog", "Exit")
|
||||
button = dialog.next("Ignore", "Retry", name = "Ignore")
|
||||
button.event("EndDialog", "Ignore")
|
||||
button = dialog.cancel("Retry", "Exit", name = "Retry")
|
||||
button.event("EndDialog", "Retry")
|
||||
|
||||
def add_maintenance_type_dialog(self):
|
||||
dialog = distutils.command.bdist_msi.PyDialog(self.db,
|
||||
"MaintenanceTypeDlg", self.x, self.y, self.width, self.height,
|
||||
self.modal, self.title, "Next", "Next", "Cancel")
|
||||
dialog.title("Welcome to the [ProductName] Setup Wizard")
|
||||
dialog.text("BodyText", 15, 63, 330, 42, 3,
|
||||
"Select whether you want to repair or remove [ProductName].")
|
||||
group = dialog.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3,
|
||||
"MaintenanceForm_Action", "", "Next")
|
||||
group.add("Repair", 0, 18, 300, 17, "&Repair [ProductName]")
|
||||
group.add("Remove", 0, 36, 300, 17, "Re&move [ProductName]")
|
||||
dialog.back("< Back", None, active = False)
|
||||
button = dialog.next("Finish", "Cancel")
|
||||
button.event("[REINSTALL]", "ALL",
|
||||
'MaintenanceForm_Action="Repair"', 5)
|
||||
button.event("[Progress1]", "Repairing",
|
||||
'MaintenanceForm_Action="Repair"', 6)
|
||||
button.event("[Progress2]", "repairs",
|
||||
'MaintenanceForm_Action="Repair"', 7)
|
||||
button.event("Reinstall", "ALL",
|
||||
'MaintenanceForm_Action="Repair"', 8)
|
||||
button.event("[REMOVE]", "ALL",
|
||||
'MaintenanceForm_Action="Remove"', 11)
|
||||
button.event("[Progress1]", "Removing",
|
||||
'MaintenanceForm_Action="Remove"', 12)
|
||||
button.event("[Progress2]", "removes",
|
||||
'MaintenanceForm_Action="Remove"', 13)
|
||||
button.event("Remove", "ALL",
|
||||
'MaintenanceForm_Action="Remove"', 14)
|
||||
button.event("EndDialog", "Return",
|
||||
'MaintenanceForm_Action<>"Change"', 20)
|
||||
button = dialog.cancel("Cancel", "RepairRadioGroup")
|
||||
button.event("SpawnDialog", "CancelDlg")
|
||||
|
||||
def add_prepare_dialog(self):
|
||||
dialog = distutils.command.bdist_msi.PyDialog(self.db, "PrepareDlg",
|
||||
self.x, self.y, self.width, self.height, self.modeless,
|
||||
self.title, "Cancel", "Cancel", "Cancel")
|
||||
dialog.text("Description", 15, 70, 320, 40, 0x30003,
|
||||
"Please wait while the installer prepares to guide you through"
|
||||
"the installation.")
|
||||
dialog.title("Welcome to the [ProductName] installer")
|
||||
text = dialog.text("ActionText", 15, 110, 320, 20, 0x30003,
|
||||
"Pondering...")
|
||||
text.mapping("ActionText", "Text")
|
||||
text = dialog.text("ActionData", 15, 135, 320, 30, 0x30003, None)
|
||||
text.mapping("ActionData", "Text")
|
||||
dialog.back("Back", None, active = False)
|
||||
dialog.next("Next", None, active = False)
|
||||
button = dialog.cancel("Cancel", None)
|
||||
button.event("SpawnDialog", "CancelDlg")
|
||||
|
||||
def add_progress_dialog(self):
|
||||
dialog = distutils.command.bdist_msi.PyDialog(self.db, "ProgressDlg",
|
||||
self.x, self.y, self.width, self.height, self.modeless,
|
||||
self.title, "Cancel", "Cancel", "Cancel", bitmap = False)
|
||||
dialog.text("Title", 20, 15, 200, 15, 0x30003,
|
||||
r"{\DlgFontBold8}[Progress1] [ProductName]")
|
||||
dialog.text("Text", 35, 65, 300, 30, 3,
|
||||
"Please wait while the installer [Progress2] [ProductName].")
|
||||
dialog.text("StatusLabel", 35, 100 ,35, 20, 3, "Status:")
|
||||
text = dialog.text("ActionText", 70, 100, self.width - 70, 20, 3,
|
||||
"Pondering...")
|
||||
text.mapping("ActionText", "Text")
|
||||
control = dialog.control("ProgressBar", "ProgressBar", 35, 120, 300,
|
||||
10, 65537, None, "Progress done", None, None)
|
||||
control.mapping("SetProgress", "Progress")
|
||||
dialog.back("< Back", "Next", active = False)
|
||||
dialog.next("Next >", "Cancel", active = False)
|
||||
button = dialog.cancel("Cancel", "Back")
|
||||
button.event("SpawnDialog", "CancelDlg")
|
||||
|
||||
def add_properties(self):
|
||||
metadata = self.distribution.metadata
|
||||
props = [
|
||||
('DistVersion', metadata.get_version()),
|
||||
('DefaultUIFont', 'DlgFont8'),
|
||||
('ErrorDialog', 'ErrorDlg'),
|
||||
('Progress1', 'Install'),
|
||||
('Progress2', 'installs'),
|
||||
('MaintenanceForm_Action', 'Repair')
|
||||
]
|
||||
email = metadata.author_email or metadata.maintainer_email
|
||||
if email:
|
||||
props.append(("ARPCONTACT", email))
|
||||
if metadata.url:
|
||||
props.append(("ARPURLINFOABOUT", metadata.url))
|
||||
if self.upgrade_code is not None:
|
||||
props.append(("UpgradeCode", self.upgrade_code))
|
||||
msilib.add_data(self.db, 'Property', props)
|
||||
|
||||
def add_select_directory_dialog(self):
|
||||
dialog = distutils.command.bdist_msi.PyDialog(self.db,
|
||||
"SelectDirectoryDlg", self.x, self.y, self.width, self.height,
|
||||
self.modal, self.title, "Next", "Next", "Cancel")
|
||||
dialog.title("Select destination directory")
|
||||
dialog.back("< Back", None, active = False)
|
||||
button = dialog.next("Next >", "Cancel")
|
||||
button.event("SetTargetPath", "TARGETDIR", ordering = 1)
|
||||
button.event("SpawnWaitDialog", "WaitForCostingDlg", ordering = 2)
|
||||
button.event("EndDialog", "Return", ordering = 3)
|
||||
button = dialog.cancel("Cancel", "DirectoryCombo")
|
||||
button.event("SpawnDialog", "CancelDlg")
|
||||
dialog.control("DirectoryCombo", "DirectoryCombo", 15, 70, 272, 80,
|
||||
393219, "TARGETDIR", None, "DirectoryList", None)
|
||||
dialog.control("DirectoryList", "DirectoryList", 15, 90, 308, 136, 3,
|
||||
"TARGETDIR", None, "PathEdit", None)
|
||||
dialog.control("PathEdit", "PathEdit", 15, 230, 306, 16, 3,
|
||||
"TARGETDIR", None, "Next", None)
|
||||
button = dialog.pushbutton("Up", 306, 70, 18, 18, 3, "Up", None)
|
||||
button.event("DirectoryListUp", "0")
|
||||
button = dialog.pushbutton("NewDir", 324, 70, 30, 18, 3, "New", None)
|
||||
button.event("DirectoryListNew", "0")
|
||||
|
||||
def add_text_styles(self):
|
||||
msilib.add_data(self.db, 'TextStyle',
|
||||
[("DlgFont8", "Tahoma", 9, None, 0),
|
||||
("DlgFontBold8", "Tahoma", 8, None, 1),
|
||||
("VerdanaBold10", "Verdana", 10, None, 1),
|
||||
("VerdanaRed9", "Verdana", 9, 255, 0)
|
||||
])
|
||||
|
||||
def add_ui(self):
|
||||
self.add_text_styles()
|
||||
self.add_error_dialog()
|
||||
self.add_fatal_error_dialog()
|
||||
self.add_cancel_dialog()
|
||||
self.add_exit_dialog()
|
||||
self.add_user_exit_dialog()
|
||||
self.add_files_in_use_dialog()
|
||||
self.add_wait_for_costing_dialog()
|
||||
self.add_prepare_dialog()
|
||||
self.add_select_directory_dialog()
|
||||
self.add_progress_dialog()
|
||||
self.add_maintenance_type_dialog()
|
||||
|
||||
def add_upgrade_config(self, sversion):
|
||||
if self.upgrade_code is not None:
|
||||
msilib.add_data(self.db, 'Upgrade',
|
||||
[(self.upgrade_code, None, sversion, None, 513, None,
|
||||
"REMOVEOLDVERSION"),
|
||||
(self.upgrade_code, sversion, None, None, 257, None,
|
||||
"REMOVENEWVERSION")
|
||||
])
|
||||
|
||||
def add_user_exit_dialog(self):
|
||||
dialog = distutils.command.bdist_msi.PyDialog(self.db, "UserExit",
|
||||
self.x, self.y, self.width, self.height, self.modal,
|
||||
self.title, "Finish", "Finish", "Finish")
|
||||
dialog.title("[ProductName] installer was interrupted")
|
||||
dialog.back("< Back", "Finish", active = False)
|
||||
dialog.cancel("Cancel", "Back", active = False)
|
||||
dialog.text("Description1", 15, 70, 320, 80, 0x30003,
|
||||
"[ProductName] setup was interrupted. Your system has not "
|
||||
"been modified. To install this program at a later time, "
|
||||
"please run the installation again.")
|
||||
dialog.text("Description2", 15, 155, 320, 20, 0x30003,
|
||||
"Click the Finish button to exit the installer.")
|
||||
button = dialog.next("Finish", "Cancel", name = "Finish")
|
||||
button.event("EndDialog", "Exit")
|
||||
|
||||
def add_wait_for_costing_dialog(self):
|
||||
dialog = msilib.Dialog(self.db, "WaitForCostingDlg", 50, 10, 260, 85,
|
||||
self.modal, self.title, "Return", "Return", "Return")
|
||||
dialog.text("Text", 48, 15, 194, 30, 3,
|
||||
"Please wait while the installer finishes determining your "
|
||||
"disk space requirements.")
|
||||
button = dialog.pushbutton("Return", 102, 57, 56, 17, 3, "Return",
|
||||
None)
|
||||
button.event("EndDialog", "Exit")
|
||||
|
||||
def get_initial_target_dir(self, fullname):
|
||||
return r"[ProgramFilesFolder]\%s" % fullname
|
||||
|
||||
def get_installer_filename(self, fullname):
|
||||
return os.path.join(self.dist_dir, "%s.msi" % fullname)
|
||||
|
||||
def initialize_options(self):
|
||||
distutils.command.bdist_msi.bdist_msi.initialize_options(self)
|
||||
self.upgrade_code = None
|
||||
self.add_to_path = None
|
||||
|
||||
def run(self):
|
||||
if not self.skip_build:
|
||||
self.run_command('build')
|
||||
install = self.reinitialize_command('install', reinit_subcommands = 1)
|
||||
install.prefix = self.bdist_dir
|
||||
install.skip_build = self.skip_build
|
||||
install.warn_dir = 0
|
||||
distutils.log.info("installing to %s", self.bdist_dir)
|
||||
install.ensure_finalized()
|
||||
install.run()
|
||||
self.mkpath(self.dist_dir)
|
||||
fullname = self.distribution.get_fullname()
|
||||
filename = os.path.abspath(self.get_installer_filename(fullname))
|
||||
if os.path.exists(filename):
|
||||
os.unlink(filename)
|
||||
metadata = self.distribution.metadata
|
||||
author = metadata.author or metadata.maintainer or "UNKNOWN"
|
||||
version = metadata.get_version()
|
||||
sversion = "%d.%d.%d" % \
|
||||
distutils.version.StrictVersion(version).version
|
||||
self.db = msilib.init_database(filename, msilib.schema,
|
||||
self.distribution.metadata.name, msilib.gen_uuid(), sversion,
|
||||
author)
|
||||
msilib.add_tables(self.db, msilib.sequence)
|
||||
self.add_properties()
|
||||
self.add_config(fullname)
|
||||
self.add_upgrade_config(sversion)
|
||||
self.add_ui()
|
||||
self.add_files()
|
||||
self.db.Commit()
|
||||
if not self.keep_temp:
|
||||
distutils.dir_util.remove_tree(self.bdist_dir,
|
||||
dry_run = self.dry_run)
|
||||
|
@ -1,6 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
from cx_Freeze import main
|
||||
|
||||
main()
|
||||
|
@ -1,35 +0,0 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# Console.py
|
||||
# Initialization script for cx_Freeze which manipulates the path so that the
|
||||
# directory in which the executable is found is searched for extensions but
|
||||
# no other directory is searched. It also sets the attribute sys.frozen so that
|
||||
# the Win32 extensions behave as expected.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import encodings
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
import zipimport
|
||||
|
||||
sys.frozen = True
|
||||
sys.path = sys.path[:4]
|
||||
|
||||
os.environ["TCL_LIBRARY"] = os.path.join(DIR_NAME, "tcl")
|
||||
os.environ["TK_LIBRARY"] = os.path.join(DIR_NAME, "tk")
|
||||
|
||||
m = __import__("__main__")
|
||||
importer = zipimport.zipimporter(INITSCRIPT_ZIP_FILE_NAME)
|
||||
if INITSCRIPT_ZIP_FILE_NAME != SHARED_ZIP_FILE_NAME:
|
||||
moduleName = m.__name__
|
||||
else:
|
||||
name, ext = os.path.splitext(os.path.basename(os.path.normcase(FILE_NAME)))
|
||||
moduleName = "%s__main__" % name
|
||||
code = importer.get_code(moduleName)
|
||||
exec code in m.__dict__
|
||||
|
||||
if sys.version_info[:2] >= (2, 5):
|
||||
module = sys.modules.get("threading")
|
||||
if module is not None:
|
||||
module._shutdown()
|
||||
|
@ -1,19 +0,0 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# ConsoleKeepPath.py
|
||||
# Initialization script for cx_Freeze which leaves the path alone and does
|
||||
# not set the sys.frozen attribute.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import sys
|
||||
import zipimport
|
||||
|
||||
m = __import__("__main__")
|
||||
importer = zipimport.zipimporter(INITSCRIPT_ZIP_FILE_NAME)
|
||||
code = importer.get_code(m.__name__)
|
||||
exec code in m.__dict__
|
||||
|
||||
if sys.version_info[:2] >= (2, 5):
|
||||
module = sys.modules.get("threading")
|
||||
if module is not None:
|
||||
module._shutdown()
|
||||
|
@ -1,38 +0,0 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# ConsoleSetLibPath.py
|
||||
# Initialization script for cx_Freeze which manipulates the path so that the
|
||||
# directory in which the executable is found is searched for extensions but
|
||||
# no other directory is searched. The environment variable LD_LIBRARY_PATH is
|
||||
# manipulated first, however, to ensure that shared libraries found in the
|
||||
# target directory are found. This requires a restart of the executable because
|
||||
# the environment variable LD_LIBRARY_PATH is only checked at startup.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import encodings
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
import zipimport
|
||||
|
||||
paths = os.environ.get("LD_LIBRARY_PATH", "").split(os.pathsep)
|
||||
if DIR_NAME not in paths:
|
||||
paths.insert(0, DIR_NAME)
|
||||
os.environ["LD_LIBRARY_PATH"] = os.pathsep.join(paths)
|
||||
os.execv(sys.executable, sys.argv)
|
||||
|
||||
sys.frozen = True
|
||||
sys.path = sys.path[:4]
|
||||
|
||||
os.environ["TCL_LIBRARY"] = os.path.join(DIR_NAME, "tcl")
|
||||
os.environ["TK_LIBRARY"] = os.path.join(DIR_NAME, "tk")
|
||||
|
||||
m = __import__("__main__")
|
||||
importer = zipimport.zipimporter(INITSCRIPT_ZIP_FILE_NAME)
|
||||
code = importer.get_code(m.__name__)
|
||||
exec code in m.__dict__
|
||||
|
||||
if sys.version_info[:2] >= (2, 5):
|
||||
module = sys.modules.get("threading")
|
||||
if module is not None:
|
||||
module._shutdown()
|
||||
|
@ -1,20 +0,0 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# SharedLib.py
|
||||
# Initialization script for cx_Freeze which behaves similarly to the one for
|
||||
# console based applications but must handle the case where Python has already
|
||||
# been initialized and another DLL of this kind has been loaded. As such it
|
||||
# does not block the path unless sys.frozen is not already set.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import encodings
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
if not hasattr(sys, "frozen"):
|
||||
sys.frozen = True
|
||||
sys.path = sys.path[:4]
|
||||
|
||||
os.environ["TCL_LIBRARY"] = os.path.join(DIR_NAME, "tcl")
|
||||
os.environ["TK_LIBRARY"] = os.path.join(DIR_NAME, "tk")
|
||||
|
@ -1,23 +0,0 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# SharedLibSource.py
|
||||
# Initialization script for cx_Freeze which imports the site module (as per
|
||||
# normal processing of a Python script) and then searches for a file with the
|
||||
# same name as the shared library but with the extension .pth. The entries in
|
||||
# this file are used to modify the path to use for subsequent imports.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
# the site module must be imported for normal behavior to take place; it is
|
||||
# done dynamically so that cx_Freeze will not add all modules referenced by
|
||||
# the site module to the frozen executable
|
||||
__import__("site")
|
||||
|
||||
# now locate the pth file to modify the path appropriately
|
||||
baseName, ext = os.path.splitext(FILE_NAME)
|
||||
pathFileName = baseName + ".pth"
|
||||
sys.path = [s.strip() for s in file(pathFileName).read().splitlines()] + \
|
||||
sys.path
|
||||
|
@ -1,7 +0,0 @@
|
||||
import sys
|
||||
|
||||
print "Hello from cx_Freeze Advanced #1"
|
||||
print
|
||||
|
||||
module = __import__("testfreeze_1")
|
||||
|
@ -1,7 +0,0 @@
|
||||
import sys
|
||||
|
||||
print "Hello from cx_Freeze Advanced #2"
|
||||
print
|
||||
|
||||
module = __import__("testfreeze_2")
|
||||
|
@ -1 +0,0 @@
|
||||
print "Test freeze module #1"
|
@ -1 +0,0 @@
|
||||
print "Test freeze module #2"
|
@ -1,31 +0,0 @@
|
||||
# An advanced setup script to create multiple executables and demonstrate a few
|
||||
# of the features available to setup scripts
|
||||
#
|
||||
# hello.py is a very simple "Hello, world" type script which also displays the
|
||||
# environment in which the script runs
|
||||
#
|
||||
# Run the build process by running the command 'python setup.py build'
|
||||
#
|
||||
# If everything works well you should find a subdirectory in the build
|
||||
# subdirectory that contains the files needed to run the script without Python
|
||||
|
||||
import sys
|
||||
from cx_Freeze import setup, Executable
|
||||
|
||||
executables = [
|
||||
Executable("advanced_1.py"),
|
||||
Executable("advanced_2.py")
|
||||
]
|
||||
|
||||
buildOptions = dict(
|
||||
compressed = True,
|
||||
includes = ["testfreeze_1", "testfreeze_2"],
|
||||
path = sys.path + ["modules"])
|
||||
|
||||
setup(
|
||||
name = "advanced_cx_Freeze_sample",
|
||||
version = "0.1",
|
||||
description = "Advanced sample cx_Freeze script",
|
||||
options = dict(build_exe = buildOptions),
|
||||
executables = executables)
|
||||
|
@ -1,27 +0,0 @@
|
||||
# A simple setup script to create an executable using matplotlib.
|
||||
#
|
||||
# test_matplotlib.py is a very simple matplotlib application that demonstrates
|
||||
# its use.
|
||||
#
|
||||
# Run the build process by running the command 'python setup.py build'
|
||||
#
|
||||
# If everything works well you should find a subdirectory in the build
|
||||
# subdirectory that contains the files needed to run the application
|
||||
|
||||
import cx_Freeze
|
||||
import sys
|
||||
|
||||
base = None
|
||||
if sys.platform == "win32":
|
||||
base = "Win32GUI"
|
||||
|
||||
executables = [
|
||||
cx_Freeze.Executable("test_matplotlib.py", base = base)
|
||||
]
|
||||
|
||||
cx_Freeze.setup(
|
||||
name = "test_matplotlib",
|
||||
version = "0.1",
|
||||
description = "Sample matplotlib script",
|
||||
executables = executables)
|
||||
|
@ -1,48 +0,0 @@
|
||||
from numpy import arange, sin, pi
|
||||
import matplotlib
|
||||
matplotlib.use('WXAgg')
|
||||
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
|
||||
from matplotlib.backends.backend_wx import NavigationToolbar2Wx
|
||||
from matplotlib.figure import Figure
|
||||
from wx import *
|
||||
|
||||
class CanvasFrame(Frame):
|
||||
def __init__(self):
|
||||
Frame.__init__(self,None,-1, 'CanvasFrame',size=(550,350))
|
||||
self.SetBackgroundColour(NamedColor("WHITE"))
|
||||
self.figure = Figure()
|
||||
self.axes = self.figure.add_subplot(111)
|
||||
t = arange(0.0,3.0,0.01)
|
||||
s = sin(2*pi*t)
|
||||
self.axes.plot(t,s)
|
||||
self.canvas = FigureCanvas(self, -1, self.figure)
|
||||
self.sizer = BoxSizer(VERTICAL)
|
||||
self.sizer.Add(self.canvas, 1, LEFT | TOP | GROW)
|
||||
self.SetSizerAndFit(self.sizer)
|
||||
self.add_toolbar()
|
||||
|
||||
def add_toolbar(self):
|
||||
self.toolbar = NavigationToolbar2Wx(self.canvas)
|
||||
self.toolbar.Realize()
|
||||
if Platform == '__WXMAC__':
|
||||
self.SetToolBar(self.toolbar)
|
||||
else:
|
||||
tw, th = self.toolbar.GetSizeTuple()
|
||||
fw, fh = self.canvas.GetSizeTuple()
|
||||
self.toolbar.SetSize(Size(fw, th))
|
||||
self.sizer.Add(self.toolbar, 0, LEFT | EXPAND)
|
||||
self.toolbar.update()
|
||||
|
||||
def OnPaint(self, event):
|
||||
self.canvas.draw()
|
||||
|
||||
class App(App):
|
||||
def OnInit(self):
|
||||
'Create the main window and insert the custom frame'
|
||||
frame = CanvasFrame()
|
||||
frame.Show(True)
|
||||
return True
|
||||
|
||||
app = App(0)
|
||||
app.MainLoop()
|
||||
|
@ -1,3 +0,0 @@
|
||||
print "importing pkg1"
|
||||
from . import sub1
|
||||
from . import pkg2
|
@ -1,3 +0,0 @@
|
||||
print "importing pkg1.pkg2"
|
||||
from . import sub3
|
||||
from .. import sub4
|
@ -1,3 +0,0 @@
|
||||
print "importing pkg1.pkg2.sub3"
|
||||
from . import sub5
|
||||
from .. import sub6
|
@ -1 +0,0 @@
|
||||
print "importing pkg1.pkg2.sub5"
|
@ -1,2 +0,0 @@
|
||||
print "importing pkg1.sub1"
|
||||
from . import sub2
|
@ -1 +0,0 @@
|
||||
print "importing pkg1.sub2"
|
@ -1 +0,0 @@
|
||||
print 'importing pkg1.sub4'
|
@ -1 +0,0 @@
|
||||
print "importing pkg1.sub6"
|
@ -1 +0,0 @@
|
||||
import pkg1
|
@ -1,16 +0,0 @@
|
||||
# relimport.py is a very simple script that tests importing using relative
|
||||
# imports (available in Python 2.5 and up)
|
||||
#
|
||||
# Run the build process by running the command 'python setup.py build'
|
||||
#
|
||||
# If everything works well you should find a subdirectory in the build
|
||||
# subdirectory that contains the files needed to run the script without Python
|
||||
|
||||
from cx_Freeze import setup, Executable
|
||||
|
||||
setup(
|
||||
name = "relimport",
|
||||
version = "0.1",
|
||||
description = "Sample cx_Freeze script for relative imports",
|
||||
executables = [Executable("relimport.py")])
|
||||
|
@ -1,19 +0,0 @@
|
||||
import sys
|
||||
|
||||
print "Hello from cx_Freeze"
|
||||
print
|
||||
|
||||
print "sys.executable", sys.executable
|
||||
print "sys.prefix", sys.prefix
|
||||
print
|
||||
|
||||
print "ARGUMENTS:"
|
||||
for a in sys.argv:
|
||||
print a
|
||||
print
|
||||
|
||||
print "PATH:"
|
||||
for p in sys.path:
|
||||
print p
|
||||
print
|
||||
|
@ -1,18 +0,0 @@
|
||||
# A very simple setup script to create a single executable
|
||||
#
|
||||
# hello.py is a very simple "Hello, world" type script which also displays the
|
||||
# environment in which the script runs
|
||||
#
|
||||
# Run the build process by running the command 'python setup.py build'
|
||||
#
|
||||
# If everything works well you should find a subdirectory in the build
|
||||
# subdirectory that contains the files needed to run the script without Python
|
||||
|
||||
from cx_Freeze import setup, Executable
|
||||
|
||||
setup(
|
||||
name = "hello",
|
||||
version = "0.1",
|
||||
description = "Sample cx_Freeze script",
|
||||
executables = [Executable("hello.py")])
|
||||
|
@ -1,25 +0,0 @@
|
||||
# A simple setup script to create an executable running wxPython. This also
|
||||
# demonstrates the method for creating a Windows executable that does not have
|
||||
# an associated console.
|
||||
#
|
||||
# wxapp.py is a very simple "Hello, world" type wxPython application
|
||||
#
|
||||
# Run the build process by running the command 'python setup.py build'
|
||||
#
|
||||
# If everything works well you should find a subdirectory in the build
|
||||
# subdirectory that contains the files needed to run the application
|
||||
|
||||
import sys
|
||||
|
||||
from cx_Freeze import setup, Executable
|
||||
|
||||
base = None
|
||||
if sys.platform == "win32":
|
||||
base = "Win32GUI"
|
||||
|
||||
setup(
|
||||
name = "hello",
|
||||
version = "0.1",
|
||||
description = "Sample cx_Freeze wxPython script",
|
||||
executables = [Executable("wxapp.py", base = base)])
|
||||
|
@ -1,42 +0,0 @@
|
||||
import wx
|
||||
|
||||
class Frame(wx.Frame):
|
||||
|
||||
def __init__(self):
|
||||
wx.Frame.__init__(self, parent = None, title = "Hello from cx_Freeze")
|
||||
panel = wx.Panel(self)
|
||||
closeMeButton = wx.Button(panel, -1, "Close Me")
|
||||
wx.EVT_BUTTON(self, closeMeButton.GetId(), self.OnCloseMe)
|
||||
wx.EVT_CLOSE(self, self.OnCloseWindow)
|
||||
pushMeButton = wx.Button(panel, -1, "Push Me")
|
||||
wx.EVT_BUTTON(self, pushMeButton.GetId(), self.OnPushMe)
|
||||
sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
sizer.Add(closeMeButton, flag = wx.ALL, border = 20)
|
||||
sizer.Add(pushMeButton, flag = wx.ALL, border = 20)
|
||||
panel.SetSizer(sizer)
|
||||
topSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
topSizer.Add(panel, flag = wx.ALL | wx.EXPAND)
|
||||
topSizer.Fit(self)
|
||||
|
||||
def OnCloseMe(self, event):
|
||||
self.Close(True)
|
||||
|
||||
def OnPushMe(self, event):
|
||||
1 / 0
|
||||
|
||||
def OnCloseWindow(self, event):
|
||||
self.Destroy()
|
||||
|
||||
|
||||
class App(wx.App):
|
||||
|
||||
def OnInit(self):
|
||||
frame = Frame()
|
||||
frame.Show(True)
|
||||
self.SetTopWindow(frame)
|
||||
return True
|
||||
|
||||
|
||||
app = App(1)
|
||||
app.MainLoop()
|
||||
|
@ -1,197 +0,0 @@
|
||||
"""
|
||||
Distutils script for cx_Freeze.
|
||||
"""
|
||||
|
||||
import distutils.command.bdist_rpm
|
||||
import distutils.command.build_ext
|
||||
import distutils.command.build_scripts
|
||||
import distutils.command.install
|
||||
import distutils.command.install_data
|
||||
import distutils.sysconfig
|
||||
import os
|
||||
import sys
|
||||
|
||||
from distutils.core import setup
|
||||
from distutils.extension import Extension
|
||||
|
||||
class bdist_rpm(distutils.command.bdist_rpm.bdist_rpm):
|
||||
|
||||
# rpm automatically byte compiles all Python files in a package but we
|
||||
# don't want that to happen for initscripts and samples so we tell it to
|
||||
# ignore those files
|
||||
def _make_spec_file(self):
|
||||
specFile = distutils.command.bdist_rpm.bdist_rpm._make_spec_file(self)
|
||||
specFile.insert(0, "%define _unpackaged_files_terminate_build 0%{nil}")
|
||||
return specFile
|
||||
|
||||
def run(self):
|
||||
distutils.command.bdist_rpm.bdist_rpm.run(self)
|
||||
specFile = os.path.join(self.rpm_base, "SPECS",
|
||||
"%s.spec" % self.distribution.get_name())
|
||||
queryFormat = "%{name}-%{version}-%{release}.%{arch}.rpm"
|
||||
command = "rpm -q --qf '%s' --specfile %s" % (queryFormat, specFile)
|
||||
origFileName = os.popen(command).read()
|
||||
parts = origFileName.split("-")
|
||||
parts.insert(2, "py%s%s" % sys.version_info[:2])
|
||||
newFileName = "-".join(parts)
|
||||
self.move_file(os.path.join("dist", origFileName),
|
||||
os.path.join("dist", newFileName))
|
||||
|
||||
|
||||
class build_ext(distutils.command.build_ext.build_ext):
|
||||
|
||||
def build_extension(self, ext):
|
||||
if ext.name.find("bases") < 0:
|
||||
distutils.command.build_ext.build_ext.build_extension(self, ext)
|
||||
return
|
||||
os.environ["LD_RUN_PATH"] = "${ORIGIN}:${ORIGIN}/../lib"
|
||||
objects = self.compiler.compile(ext.sources,
|
||||
output_dir = self.build_temp,
|
||||
include_dirs = ext.include_dirs,
|
||||
debug = self.debug,
|
||||
depends = ext.depends)
|
||||
fileName = os.path.splitext(self.get_ext_filename(ext.name))[0]
|
||||
fullName = os.path.join(self.build_lib, fileName)
|
||||
libraryDirs = ext.library_dirs or []
|
||||
libraries = self.get_libraries(ext)
|
||||
extraArgs = ext.extra_link_args or []
|
||||
if sys.platform != "win32":
|
||||
vars = distutils.sysconfig.get_config_vars()
|
||||
libraryDirs.append(vars["LIBPL"])
|
||||
libraries.append("python%s.%s" % sys.version_info[:2])
|
||||
if vars["LINKFORSHARED"]:
|
||||
extraArgs.extend(vars["LINKFORSHARED"].split())
|
||||
if vars["LIBS"]:
|
||||
extraArgs.extend(vars["LIBS"].split())
|
||||
if vars["LIBM"]:
|
||||
extraArgs.append(vars["LIBM"])
|
||||
if vars["BASEMODLIBS"]:
|
||||
extraArgs.extend(vars["BASEMODLIBS"].split())
|
||||
if vars["LOCALMODLIBS"]:
|
||||
extraArgs.extend(vars["LOCALMODLIBS"].split())
|
||||
extraArgs.append("-s")
|
||||
self.compiler.link_executable(objects, fullName,
|
||||
libraries = libraries,
|
||||
library_dirs = libraryDirs,
|
||||
runtime_library_dirs = ext.runtime_library_dirs,
|
||||
extra_postargs = extraArgs,
|
||||
debug = self.debug)
|
||||
|
||||
def get_ext_filename(self, name):
|
||||
fileName = distutils.command.build_ext.build_ext.get_ext_filename(self,
|
||||
name)
|
||||
if name.find("bases") < 0:
|
||||
return fileName
|
||||
ext = self.compiler.exe_extension or ""
|
||||
return os.path.splitext(fileName)[0] + ext
|
||||
|
||||
|
||||
class build_scripts(distutils.command.build_scripts.build_scripts):
|
||||
|
||||
def copy_scripts(self):
|
||||
distutils.command.build_scripts.build_scripts.copy_scripts(self)
|
||||
if sys.platform == "win32":
|
||||
for script in self.scripts:
|
||||
batFileName = os.path.join(self.build_dir, script + ".bat")
|
||||
fullScriptName = r"%s\Scripts\%s" % \
|
||||
(os.path.dirname(sys.executable), script)
|
||||
command = "%s %s %%1 %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9" % \
|
||||
(sys.executable, fullScriptName)
|
||||
file(batFileName, "w").write("@echo off\n\n%s" % command)
|
||||
|
||||
|
||||
class install(distutils.command.install.install):
|
||||
|
||||
def get_sub_commands(self):
|
||||
subCommands = distutils.command.install.install.get_sub_commands(self)
|
||||
subCommands.append("install_packagedata")
|
||||
return subCommands
|
||||
|
||||
|
||||
class install_packagedata(distutils.command.install_data.install_data):
|
||||
|
||||
def run(self):
|
||||
installCommand = self.get_finalized_command("install")
|
||||
installDir = getattr(installCommand, "install_lib")
|
||||
sourceDirs = ["samples", "initscripts"]
|
||||
while sourceDirs:
|
||||
sourceDir = sourceDirs.pop(0)
|
||||
targetDir = os.path.join(installDir, "cx_Freeze", sourceDir)
|
||||
self.mkpath(targetDir)
|
||||
for name in os.listdir(sourceDir):
|
||||
if name == "build" or name.startswith("."):
|
||||
continue
|
||||
fullSourceName = os.path.join(sourceDir, name)
|
||||
if os.path.isdir(fullSourceName):
|
||||
sourceDirs.append(fullSourceName)
|
||||
else:
|
||||
fullTargetName = os.path.join(targetDir, name)
|
||||
self.copy_file(fullSourceName, fullTargetName)
|
||||
self.outfiles.append(fullTargetName)
|
||||
|
||||
|
||||
commandClasses = dict(
|
||||
build_ext = build_ext,
|
||||
build_scripts = build_scripts,
|
||||
bdist_rpm = bdist_rpm,
|
||||
install = install,
|
||||
install_packagedata = install_packagedata)
|
||||
|
||||
if sys.platform == "win32":
|
||||
libraries = ["imagehlp"]
|
||||
else:
|
||||
libraries = []
|
||||
utilModule = Extension("cx_Freeze.util", ["source/util.c"],
|
||||
libraries = libraries)
|
||||
depends = ["source/bases/Common.c"]
|
||||
if sys.platform == "win32":
|
||||
if sys.version_info[:2] >= (2, 6):
|
||||
extraSources = ["source/bases/manifest.rc"]
|
||||
else:
|
||||
extraSources = ["source/bases/dummy.rc"]
|
||||
else:
|
||||
extraSources = []
|
||||
console = Extension("cx_Freeze.bases.Console",
|
||||
["source/bases/Console.c"] + extraSources, depends = depends)
|
||||
consoleKeepPath = Extension("cx_Freeze.bases.ConsoleKeepPath",
|
||||
["source/bases/ConsoleKeepPath.c"] + extraSources, depends = depends)
|
||||
extensions = [utilModule, console, consoleKeepPath]
|
||||
if sys.platform == "win32":
|
||||
gui = Extension("cx_Freeze.bases.Win32GUI",
|
||||
["source/bases/Win32GUI.c"] + extraSources,
|
||||
depends = depends, extra_link_args = ["-mwindows"])
|
||||
extensions.append(gui)
|
||||
|
||||
docFiles = "LICENSE.txt README.txt HISTORY.txt doc/cx_Freeze.html"
|
||||
|
||||
classifiers = [
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: Python Software Foundation License",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: C",
|
||||
"Programming Language :: Python",
|
||||
"Topic :: Software Development :: Build Tools",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Topic :: System :: Software Distribution",
|
||||
"Topic :: Utilities"
|
||||
]
|
||||
|
||||
setup(name = "cx_Freeze",
|
||||
description = "create standalone executables from Python scripts",
|
||||
long_description = "create standalone executables from Python scripts",
|
||||
version = "4.0.1",
|
||||
cmdclass = commandClasses,
|
||||
options = dict(bdist_rpm = dict(doc_files = docFiles),
|
||||
install = dict(optimize = 1)),
|
||||
ext_modules = extensions,
|
||||
packages = ['cx_Freeze'],
|
||||
maintainer="Anthony Tuininga",
|
||||
maintainer_email="anthony.tuininga@gmail.com",
|
||||
url = "http://cx-freeze.sourceforge.net",
|
||||
scripts = ["cxfreeze"],
|
||||
classifiers = classifiers,
|
||||
keywords = "freeze",
|
||||
license = "Python Software Foundation License")
|
||||
|
@ -1,262 +0,0 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Common.c
|
||||
// Routines which are common to running frozen executables.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <compile.h>
|
||||
#include <eval.h>
|
||||
#include <osdefs.h>
|
||||
|
||||
// global variables (used for simplicity)
|
||||
static PyObject *g_FileName = NULL;
|
||||
static PyObject *g_DirName = NULL;
|
||||
static PyObject *g_ExclusiveZipFileName = NULL;
|
||||
static PyObject *g_SharedZipFileName = NULL;
|
||||
static PyObject *g_InitScriptZipFileName = NULL;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GetDirName()
|
||||
// Return the directory name of the given path.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int GetDirName(
|
||||
const char *path, // path to calculate dir name for
|
||||
PyObject **dirName) // directory name (OUT)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = strlen(path); i > 0 && path[i] != SEP; --i);
|
||||
*dirName = PyString_FromStringAndSize(path, i);
|
||||
if (!*dirName)
|
||||
return FatalError("cannot create string for directory name");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SetExecutableName()
|
||||
// Set the script to execute and calculate the directory in which the
|
||||
// executable is found as well as the exclusive (only for this executable) and
|
||||
// shared zip file names.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int SetExecutableName(
|
||||
const char *fileName) // script to execute
|
||||
{
|
||||
char temp[MAXPATHLEN + 12], *ptr;
|
||||
#ifndef WIN32
|
||||
char linkData[MAXPATHLEN + 1];
|
||||
struct stat statData;
|
||||
size_t linkSize, i;
|
||||
PyObject *dirName;
|
||||
#endif
|
||||
|
||||
// store file name
|
||||
g_FileName = PyString_FromString(fileName);
|
||||
if (!g_FileName)
|
||||
return FatalError("cannot create string for file name");
|
||||
|
||||
#ifndef WIN32
|
||||
for (i = 0; i < 25; i++) {
|
||||
if (lstat(fileName, &statData) < 0) {
|
||||
PyErr_SetFromErrnoWithFilename(PyExc_OSError, (char*) fileName);
|
||||
return FatalError("unable to stat file");
|
||||
}
|
||||
if (!S_ISLNK(statData.st_mode))
|
||||
break;
|
||||
linkSize = readlink(fileName, linkData, sizeof(linkData));
|
||||
if (linkSize < 0) {
|
||||
PyErr_SetFromErrnoWithFilename(PyExc_OSError, (char*) fileName);
|
||||
return FatalError("unable to stat file");
|
||||
}
|
||||
if (linkData[0] == '/') {
|
||||
Py_DECREF(g_FileName);
|
||||
g_FileName = PyString_FromStringAndSize(linkData, linkSize);
|
||||
} else {
|
||||
if (GetDirName(PyString_AS_STRING(g_FileName), &dirName) < 0)
|
||||
return -1;
|
||||
if (PyString_GET_SIZE(dirName) + linkSize + 1 > MAXPATHLEN) {
|
||||
Py_DECREF(dirName);
|
||||
return FatalError("cannot dereference link, path too large");
|
||||
}
|
||||
strcpy(temp, PyString_AS_STRING(dirName));
|
||||
strcat(temp, "/");
|
||||
strcat(temp, linkData);
|
||||
Py_DECREF(g_FileName);
|
||||
g_FileName = PyString_FromString(temp);
|
||||
}
|
||||
if (!g_FileName)
|
||||
return FatalError("cannot create string for linked file name");
|
||||
fileName = PyString_AS_STRING(g_FileName);
|
||||
}
|
||||
#endif
|
||||
|
||||
// calculate and store directory name
|
||||
if (GetDirName(fileName, &g_DirName) < 0)
|
||||
return -1;
|
||||
|
||||
// calculate and store exclusive zip file name
|
||||
strcpy(temp, fileName);
|
||||
ptr = temp + strlen(temp) - 1;
|
||||
while (ptr > temp && *ptr != SEP && *ptr != '.')
|
||||
ptr--;
|
||||
if (*ptr == '.')
|
||||
*ptr = '\0';
|
||||
strcat(temp, ".zip");
|
||||
g_ExclusiveZipFileName = PyString_FromString(temp);
|
||||
if (!g_ExclusiveZipFileName)
|
||||
return FatalError("cannot create string for exclusive zip file name");
|
||||
|
||||
// calculate and store shared zip file name
|
||||
strcpy(temp, PyString_AS_STRING(g_DirName));
|
||||
ptr = temp + strlen(temp);
|
||||
*ptr++ = SEP;
|
||||
strcpy(ptr, "library.zip");
|
||||
g_SharedZipFileName = PyString_FromString(temp);
|
||||
if (!g_SharedZipFileName)
|
||||
return FatalError("cannot create string for shared zip file name");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SetPathToSearch()
|
||||
// Set the path to search. This includes the file (for those situations where
|
||||
// a zip file is attached to the executable itself), the directory where the
|
||||
// executable is found (to search for extensions), the exclusive zip file
|
||||
// name and the shared zip file name.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int SetPathToSearch(void)
|
||||
{
|
||||
PyObject *pathList;
|
||||
|
||||
pathList = PySys_GetObject("path");
|
||||
if (!pathList)
|
||||
return FatalError("cannot acquire sys.path");
|
||||
if (PyList_Insert(pathList, 0, g_FileName) < 0)
|
||||
return FatalError("cannot insert file name into sys.path");
|
||||
if (PyList_Insert(pathList, 1, g_DirName) < 0)
|
||||
return FatalError("cannot insert directory name into sys.path");
|
||||
if (PyList_Insert(pathList, 2, g_ExclusiveZipFileName) < 0)
|
||||
return FatalError("cannot insert exclusive zip name into sys.path");
|
||||
if (PyList_Insert(pathList, 3, g_SharedZipFileName) < 0)
|
||||
return FatalError("cannot insert shared zip name into sys.path");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GetImporterHelper()
|
||||
// Helper which is used to locate the importer for the initscript.
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *GetImporterHelper(
|
||||
PyObject *module, // zipimport module
|
||||
PyObject *fileName) // name of file to search
|
||||
{
|
||||
PyObject *importer;
|
||||
|
||||
importer = PyObject_CallMethod(module, "zipimporter", "O", fileName);
|
||||
if (importer)
|
||||
g_InitScriptZipFileName = fileName;
|
||||
else
|
||||
PyErr_Clear();
|
||||
return importer;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GetImporter()
|
||||
// Return the importer which will be used for importing the initialization
|
||||
// script. The executable itself is searched first, followed by the exclusive
|
||||
// zip file and finally by the shared zip file.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int GetImporter(
|
||||
PyObject **importer) // importer (OUT)
|
||||
{
|
||||
PyObject *module;
|
||||
|
||||
module = PyImport_ImportModule("zipimport");
|
||||
if (!module)
|
||||
return FatalError("cannot import zipimport module");
|
||||
*importer = GetImporterHelper(module, g_FileName);
|
||||
if (!*importer) {
|
||||
*importer = GetImporterHelper(module, g_ExclusiveZipFileName);
|
||||
if (!*importer)
|
||||
*importer = GetImporterHelper(module, g_SharedZipFileName);
|
||||
}
|
||||
Py_DECREF(module);
|
||||
if (!*importer)
|
||||
return FatalError("cannot get zipimporter instance");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// PopulateInitScriptDict()
|
||||
// Return the dictionary used by the initialization script.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int PopulateInitScriptDict(
|
||||
PyObject *dict) // dictionary to populate
|
||||
{
|
||||
if (!dict)
|
||||
return FatalError("unable to create temporary dictionary");
|
||||
if (PyDict_SetItemString(dict, "__builtins__", PyEval_GetBuiltins()) < 0)
|
||||
return FatalError("unable to set __builtins__");
|
||||
if (PyDict_SetItemString(dict, "FILE_NAME", g_FileName) < 0)
|
||||
return FatalError("unable to set FILE_NAME");
|
||||
if (PyDict_SetItemString(dict, "DIR_NAME", g_DirName) < 0)
|
||||
return FatalError("unable to set DIR_NAME");
|
||||
if (PyDict_SetItemString(dict, "EXCLUSIVE_ZIP_FILE_NAME",
|
||||
g_ExclusiveZipFileName) < 0)
|
||||
return FatalError("unable to set EXCLUSIVE_ZIP_FILE_NAME");
|
||||
if (PyDict_SetItemString(dict, "SHARED_ZIP_FILE_NAME",
|
||||
g_SharedZipFileName) < 0)
|
||||
return FatalError("unable to set SHARED_ZIP_FILE_NAME");
|
||||
if (PyDict_SetItemString(dict, "INITSCRIPT_ZIP_FILE_NAME",
|
||||
g_InitScriptZipFileName) < 0)
|
||||
return FatalError("unable to set INITSCRIPT_ZIP_FILE_NAME");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ExecuteScript()
|
||||
// Execute the script found within the file.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int ExecuteScript(
|
||||
const char *fileName) // name of file containing Python code
|
||||
{
|
||||
PyObject *importer, *dict, *code, *temp;
|
||||
|
||||
if (SetExecutableName(fileName) < 0)
|
||||
return -1;
|
||||
if (SetPathToSearch() < 0)
|
||||
return -1;
|
||||
importer = NULL;
|
||||
if (GetImporter(&importer) < 0)
|
||||
return -1;
|
||||
|
||||
// create and populate dictionary for initscript module
|
||||
dict = PyDict_New();
|
||||
if (PopulateInitScriptDict(dict) < 0) {
|
||||
Py_XDECREF(dict);
|
||||
Py_DECREF(importer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// locate and execute script
|
||||
code = PyObject_CallMethod(importer, "get_code", "s", "cx_Freeze__init__");
|
||||
Py_DECREF(importer);
|
||||
if (!code)
|
||||
return FatalError("unable to locate initialization module");
|
||||
temp = PyEval_EvalCode( (PyCodeObject*) code, dict, dict);
|
||||
Py_DECREF(code);
|
||||
Py_DECREF(dict);
|
||||
if (!temp)
|
||||
return FatalScriptError();
|
||||
Py_DECREF(temp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,72 +0,0 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Console.c
|
||||
// Main routine for frozen programs which run in a console.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <Python.h>
|
||||
#ifdef __WIN32__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FatalError()
|
||||
// Prints a fatal error.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int FatalError(
|
||||
const char *message) // message to print
|
||||
{
|
||||
PyErr_Print();
|
||||
Py_FatalError(message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FatalScriptError()
|
||||
// Prints a fatal error in the initialization script.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int FatalScriptError(void)
|
||||
{
|
||||
PyErr_Print();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#include "Common.c"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// main()
|
||||
// Main routine for frozen programs.
|
||||
//-----------------------------------------------------------------------------
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *fileName;
|
||||
char *encoding;
|
||||
|
||||
// initialize Python
|
||||
Py_NoSiteFlag = 1;
|
||||
Py_FrozenFlag = 1;
|
||||
Py_IgnoreEnvironmentFlag = 1;
|
||||
|
||||
encoding = getenv("PYTHONIOENCODING");
|
||||
if (encoding != NULL) {
|
||||
Py_FileSystemDefaultEncoding = strndup(encoding, 100);
|
||||
}
|
||||
|
||||
Py_SetPythonHome("");
|
||||
Py_SetProgramName(argv[0]);
|
||||
fileName = Py_GetProgramFullPath();
|
||||
|
||||
Py_Initialize();
|
||||
PySys_SetArgv(argc, argv);
|
||||
|
||||
|
||||
// do the work
|
||||
if (ExecuteScript(fileName) < 0)
|
||||
return 1;
|
||||
|
||||
Py_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// ConsoleKeepPath.c
|
||||
// Main routine for frozen programs which need a Python installation to do
|
||||
// their work.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <Python.h>
|
||||
#ifdef __WIN32__
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FatalError()
|
||||
// Prints a fatal error.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int FatalError(
|
||||
const char *message) // message to print
|
||||
{
|
||||
PyErr_Print();
|
||||
Py_FatalError(message);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FatalScriptError()
|
||||
// Prints a fatal error in the initialization script.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int FatalScriptError(void)
|
||||
{
|
||||
PyErr_Print();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
#include "Common.c"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// main()
|
||||
// Main routine for frozen programs.
|
||||
//-----------------------------------------------------------------------------
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *fileName;
|
||||
|
||||
// initialize Python
|
||||
Py_SetProgramName(argv[0]);
|
||||
fileName = Py_GetProgramFullPath();
|
||||
Py_Initialize();
|
||||
PySys_SetArgv(argc, argv);
|
||||
|
||||
// do the work
|
||||
if (ExecuteScript(fileName) < 0)
|
||||
return 1;
|
||||
|
||||
Py_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,242 +0,0 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Win32GUI.c
|
||||
// Main routine for frozen programs written for the Win32 GUI subsystem.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <Python.h>
|
||||
#include <windows.h>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FatalError()
|
||||
// Handle a fatal error.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int FatalError(
|
||||
char *a_Message) // message to display
|
||||
{
|
||||
MessageBox(NULL, a_Message, "cx_Freeze Fatal Error", MB_ICONERROR);
|
||||
Py_Finalize();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// StringifyObject()
|
||||
// Stringify a Python object.
|
||||
//-----------------------------------------------------------------------------
|
||||
static char *StringifyObject(
|
||||
PyObject *object, // object to stringify
|
||||
PyObject **stringRep) // string representation
|
||||
{
|
||||
if (object) {
|
||||
*stringRep = PyObject_Str(object);
|
||||
if (*stringRep)
|
||||
return PyString_AS_STRING(*stringRep);
|
||||
return "Unable to stringify";
|
||||
}
|
||||
|
||||
// object is NULL
|
||||
*stringRep = NULL;
|
||||
return "None";
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FatalPythonErrorNoTraceback()
|
||||
// Handle a fatal Python error without traceback.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int FatalPythonErrorNoTraceback(
|
||||
PyObject *origType, // exception type
|
||||
PyObject *origValue, // exception value
|
||||
char *message) // message to display
|
||||
{
|
||||
PyObject *typeStrRep, *valueStrRep, *origTypeStrRep, *origValueStrRep;
|
||||
char *totalMessage, *typeStr, *valueStr, *origTypeStr, *origValueStr;
|
||||
PyObject *type, *value, *traceback;
|
||||
int totalMessageLength;
|
||||
char *messageFormat;
|
||||
|
||||
// fetch error and string representations of the error
|
||||
PyErr_Fetch(&type, &value, &traceback);
|
||||
origTypeStr = StringifyObject(origType, &origTypeStrRep);
|
||||
origValueStr = StringifyObject(origValue, &origValueStrRep);
|
||||
typeStr = StringifyObject(type, &typeStrRep);
|
||||
valueStr = StringifyObject(value, &valueStrRep);
|
||||
|
||||
// fill out the message to be displayed
|
||||
messageFormat = "Type: %s\nValue: %s\nOther Type: %s\nOtherValue: %s\n%s";
|
||||
totalMessageLength = strlen(origTypeStr) + strlen(origValueStr) +
|
||||
strlen(typeStr) + strlen(valueStr) + strlen(message) +
|
||||
strlen(messageFormat) + 1;
|
||||
totalMessage = malloc(totalMessageLength);
|
||||
if (!totalMessage)
|
||||
return FatalError("Out of memory!");
|
||||
sprintf(totalMessage, messageFormat, typeStr, valueStr, origTypeStr,
|
||||
origValueStr, message);
|
||||
|
||||
// display the message
|
||||
MessageBox(NULL, totalMessage,
|
||||
"cx_Freeze: Python error in main script (traceback unavailable)",
|
||||
MB_ICONERROR);
|
||||
free(totalMessage);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ArgumentValue()
|
||||
// Return a suitable argument value by replacing NULL with Py_None.
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *ArgumentValue(
|
||||
PyObject *object) // argument to massage
|
||||
{
|
||||
if (object) {
|
||||
Py_INCREF(object);
|
||||
return object;
|
||||
}
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HandleSystemExitException()
|
||||
// Handles a system exit exception differently. If an integer value is passed
|
||||
// through then that becomes the exit value; otherwise the string value of the
|
||||
// value passed through is displayed in a message box.
|
||||
//-----------------------------------------------------------------------------
|
||||
static void HandleSystemExitException()
|
||||
{
|
||||
PyObject *type, *value, *traceback, *valueStr;
|
||||
int exitCode = 0;
|
||||
char *message;
|
||||
|
||||
PyErr_Fetch(&type, &value, &traceback);
|
||||
if (PyInstance_Check(value)) {
|
||||
PyObject *code = PyObject_GetAttrString(value, "code");
|
||||
if (code) {
|
||||
Py_DECREF(value);
|
||||
value = code;
|
||||
if (value == Py_None)
|
||||
Py_Exit(0);
|
||||
}
|
||||
}
|
||||
if (PyInt_Check(value))
|
||||
exitCode = PyInt_AsLong(value);
|
||||
else {
|
||||
message = StringifyObject(value, &valueStr);
|
||||
MessageBox(NULL, message, "cx_Freeze: Application Terminated",
|
||||
MB_ICONERROR);
|
||||
Py_XDECREF(valueStr);
|
||||
exitCode = 1;
|
||||
}
|
||||
Py_Exit(exitCode);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FatalScriptError()
|
||||
// Handle a fatal Python error with traceback.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int FatalScriptError()
|
||||
{
|
||||
PyObject *type, *value, *traceback, *argsTuple, *module, *method, *result;
|
||||
int tracebackLength, i;
|
||||
char *tracebackStr;
|
||||
|
||||
// if a system exception, handle it specially
|
||||
if (PyErr_ExceptionMatches(PyExc_SystemExit))
|
||||
HandleSystemExitException();
|
||||
|
||||
// get the exception details
|
||||
PyErr_Fetch(&type, &value, &traceback);
|
||||
|
||||
// import the traceback module
|
||||
module = PyImport_ImportModule("traceback");
|
||||
if (!module)
|
||||
return FatalPythonErrorNoTraceback(type, value,
|
||||
"Cannot import traceback module.");
|
||||
|
||||
// get the format_exception method
|
||||
method = PyObject_GetAttrString(module, "format_exception");
|
||||
Py_DECREF(module);
|
||||
if (!method)
|
||||
return FatalPythonErrorNoTraceback(type, value,
|
||||
"Cannot get format_exception method.");
|
||||
|
||||
// create a tuple for the arguments
|
||||
argsTuple = PyTuple_New(3);
|
||||
if (!argsTuple) {
|
||||
Py_DECREF(method);
|
||||
return FatalPythonErrorNoTraceback(type, value,
|
||||
"Cannot create arguments tuple for traceback.");
|
||||
}
|
||||
PyTuple_SET_ITEM(argsTuple, 0, ArgumentValue(type));
|
||||
PyTuple_SET_ITEM(argsTuple, 1, ArgumentValue(value));
|
||||
PyTuple_SET_ITEM(argsTuple, 2, ArgumentValue(traceback));
|
||||
|
||||
// call the format_exception method
|
||||
result = PyObject_CallObject(method, argsTuple);
|
||||
Py_DECREF(method);
|
||||
Py_DECREF(argsTuple);
|
||||
if (!result)
|
||||
return FatalPythonErrorNoTraceback(type, value,
|
||||
"Failed calling format_exception method.");
|
||||
|
||||
// determine length of string representation of formatted traceback
|
||||
tracebackLength = 1;
|
||||
for (i = 0; i < PyList_GET_SIZE(result); i++)
|
||||
tracebackLength += PyString_GET_SIZE(PyList_GET_ITEM(result, i));
|
||||
|
||||
// create a string representation of the formatted traceback
|
||||
tracebackStr = malloc(tracebackLength);
|
||||
if (!tracebackStr) {
|
||||
Py_DECREF(result);
|
||||
return FatalError("Out of memory!");
|
||||
}
|
||||
tracebackStr[0] = '\0';
|
||||
for (i = 0; i < PyList_GET_SIZE(result); i++)
|
||||
strcat(tracebackStr, PyString_AS_STRING(PyList_GET_ITEM(result, i)));
|
||||
Py_DECREF(result);
|
||||
|
||||
// bring up the error
|
||||
MessageBox(NULL, tracebackStr, "cx_Freeze: Python error in main script",
|
||||
MB_ICONERROR);
|
||||
Py_Finalize();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#include "Common.c"
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// WinMain()
|
||||
// Main routine for the executable in Windows.
|
||||
//-----------------------------------------------------------------------------
|
||||
int WINAPI WinMain(
|
||||
HINSTANCE instance, // handle to application
|
||||
HINSTANCE prevInstance, // previous handle to application
|
||||
LPSTR commandLine, // command line
|
||||
int showFlag) // show flag
|
||||
{
|
||||
const char *fileName;
|
||||
|
||||
// initialize Python
|
||||
Py_NoSiteFlag = 1;
|
||||
Py_FrozenFlag = 1;
|
||||
Py_IgnoreEnvironmentFlag = 1;
|
||||
Py_SetPythonHome("");
|
||||
Py_SetProgramName(__argv[0]);
|
||||
fileName = Py_GetProgramFullPath();
|
||||
Py_Initialize();
|
||||
PySys_SetArgv(__argc, __argv);
|
||||
|
||||
// do the work
|
||||
if (ExecuteScript(fileName) < 0)
|
||||
return 1;
|
||||
|
||||
// terminate Python
|
||||
Py_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
STRINGTABLE
|
||||
{
|
||||
1, "Just to ensure that buggy EndUpdateResource doesn't fall over."
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
#include "dummy.rc"
|
||||
|
||||
1 24 source/bases/manifest.txt
|
@ -1,418 +0,0 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// util.c
|
||||
// Shared library for use by cx_Freeze.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <imagehlp.h>
|
||||
|
||||
#pragma pack(2)
|
||||
|
||||
typedef struct {
|
||||
BYTE bWidth; // Width, in pixels, of the image
|
||||
BYTE bHeight; // Height, in pixels, of the image
|
||||
BYTE bColorCount; // Number of colors in image
|
||||
BYTE bReserved; // Reserved ( must be 0)
|
||||
WORD wPlanes; // Color Planes
|
||||
WORD wBitCount; // Bits per pixel
|
||||
DWORD dwBytesInRes; // How many bytes in this resource?
|
||||
DWORD dwImageOffset; // Where in the file is this image?
|
||||
} ICONDIRENTRY;
|
||||
|
||||
typedef struct {
|
||||
WORD idReserved; // Reserved (must be 0)
|
||||
WORD idType; // Resource Type (1 for icons)
|
||||
WORD idCount; // How many images?
|
||||
ICONDIRENTRY idEntries[0]; // An entry for each image
|
||||
} ICONDIR;
|
||||
|
||||
typedef struct {
|
||||
BYTE bWidth; // Width, in pixels, of the image
|
||||
BYTE bHeight; // Height, in pixels, of the image
|
||||
BYTE bColorCount; // Number of colors in image
|
||||
BYTE bReserved; // Reserved ( must be 0)
|
||||
WORD wPlanes; // Color Planes
|
||||
WORD wBitCount; // Bits per pixel
|
||||
DWORD dwBytesInRes; // How many bytes in this resource?
|
||||
WORD nID; // resource ID
|
||||
} GRPICONDIRENTRY;
|
||||
|
||||
typedef struct {
|
||||
WORD idReserved; // Reserved (must be 0)
|
||||
WORD idType; // Resource Type (1 for icons)
|
||||
WORD idCount; // How many images?
|
||||
GRPICONDIRENTRY idEntries[0]; // An entry for each image
|
||||
} GRPICONDIR;
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Globals
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifdef WIN32
|
||||
static PyObject *g_BindErrorException = NULL;
|
||||
static PyObject *g_ImageNames = NULL;
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
//-----------------------------------------------------------------------------
|
||||
// BindStatusRoutine()
|
||||
// Called by BindImageEx() at various points. This is used to determine the
|
||||
// dependency tree which is later examined by cx_Freeze.
|
||||
//-----------------------------------------------------------------------------
|
||||
static BOOL __stdcall BindStatusRoutine(
|
||||
IMAGEHLP_STATUS_REASON reason, // reason called
|
||||
PSTR imageName, // name of image being examined
|
||||
PSTR dllName, // name of DLL
|
||||
ULONG virtualAddress, // computed virtual address
|
||||
ULONG parameter) // parameter (value depends on reason)
|
||||
{
|
||||
char fileName[MAX_PATH + 1];
|
||||
|
||||
switch (reason) {
|
||||
case BindImportModule:
|
||||
if (!SearchPath(NULL, dllName, NULL, sizeof(fileName), fileName,
|
||||
NULL))
|
||||
return FALSE;
|
||||
Py_INCREF(Py_None);
|
||||
if (PyDict_SetItemString(g_ImageNames, fileName, Py_None) < 0)
|
||||
return FALSE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GetFileData()
|
||||
// Return the data for the given file.
|
||||
//-----------------------------------------------------------------------------
|
||||
static int GetFileData(
|
||||
const char *fileName, // name of file to read
|
||||
char **data) // pointer to data (OUT)
|
||||
{
|
||||
DWORD numberOfBytesRead, dataSize;
|
||||
HANDLE file;
|
||||
|
||||
file = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (file == INVALID_HANDLE_VALUE)
|
||||
return -1;
|
||||
dataSize = GetFileSize(file, NULL);
|
||||
if (dataSize == INVALID_FILE_SIZE) {
|
||||
CloseHandle(file);
|
||||
return -1;
|
||||
}
|
||||
*data = PyMem_Malloc(dataSize);
|
||||
if (!*data) {
|
||||
CloseHandle(file);
|
||||
return -1;
|
||||
}
|
||||
if (!ReadFile(file, *data, dataSize, &numberOfBytesRead, NULL)) {
|
||||
CloseHandle(file);
|
||||
return -1;
|
||||
}
|
||||
CloseHandle(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// CreateGroupIconResource()
|
||||
// Return the group icon resource given the icon file data.
|
||||
//-----------------------------------------------------------------------------
|
||||
static GRPICONDIR *CreateGroupIconResource(
|
||||
ICONDIR *iconDir, // icon information
|
||||
DWORD *resourceSize) // size of resource (OUT)
|
||||
{
|
||||
GRPICONDIR *groupIconDir;
|
||||
int i;
|
||||
|
||||
*resourceSize = sizeof(GRPICONDIR) +
|
||||
sizeof(GRPICONDIRENTRY) * iconDir->idCount;
|
||||
groupIconDir = PyMem_Malloc(*resourceSize);
|
||||
if (!groupIconDir)
|
||||
return NULL;
|
||||
groupIconDir->idReserved = iconDir->idReserved;
|
||||
groupIconDir->idType = iconDir->idType;
|
||||
groupIconDir->idCount = iconDir->idCount;
|
||||
for (i = 0; i < iconDir->idCount; i++) {
|
||||
groupIconDir->idEntries[i].bWidth = iconDir->idEntries[i].bWidth;
|
||||
groupIconDir->idEntries[i].bHeight = iconDir->idEntries[i].bHeight;
|
||||
groupIconDir->idEntries[i].bColorCount =
|
||||
iconDir->idEntries[i].bColorCount;
|
||||
groupIconDir->idEntries[i].bReserved = iconDir->idEntries[i].bReserved;
|
||||
groupIconDir->idEntries[i].wPlanes = iconDir->idEntries[i].wPlanes;
|
||||
groupIconDir->idEntries[i].wBitCount = iconDir->idEntries[i].wBitCount;
|
||||
groupIconDir->idEntries[i].dwBytesInRes =
|
||||
iconDir->idEntries[i].dwBytesInRes;
|
||||
groupIconDir->idEntries[i].nID = i + 1;
|
||||
}
|
||||
|
||||
return groupIconDir;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ExtAddIcon()
|
||||
// Add the icon as a resource to the specified file.
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *ExtAddIcon(
|
||||
PyObject *self, // passthrough argument
|
||||
PyObject *args) // arguments
|
||||
{
|
||||
char *executableName, *iconName, *data, *iconData;
|
||||
GRPICONDIR *groupIconDir;
|
||||
DWORD resourceSize;
|
||||
ICONDIR *iconDir;
|
||||
BOOL succeeded;
|
||||
HANDLE handle;
|
||||
int i;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ss", &executableName, &iconName))
|
||||
return NULL;
|
||||
|
||||
// begin updating the executable
|
||||
handle = BeginUpdateResource(executableName, FALSE);
|
||||
if (!handle) {
|
||||
PyErr_SetExcFromWindowsErrWithFilename(PyExc_WindowsError,
|
||||
GetLastError(), executableName);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// first attempt to get the data from the icon file
|
||||
data = NULL;
|
||||
succeeded = TRUE;
|
||||
groupIconDir = NULL;
|
||||
if (GetFileData(iconName, &data) < 0)
|
||||
succeeded = FALSE;
|
||||
iconDir = (ICONDIR*) data;
|
||||
|
||||
// next, attempt to add a group icon resource
|
||||
if (succeeded) {
|
||||
groupIconDir = CreateGroupIconResource(iconDir, &resourceSize);
|
||||
if (groupIconDir)
|
||||
succeeded = UpdateResource(handle, RT_GROUP_ICON,
|
||||
MAKEINTRESOURCE(1),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
|
||||
groupIconDir, resourceSize);
|
||||
else succeeded = FALSE;
|
||||
}
|
||||
|
||||
// next, add each icon as a resource
|
||||
if (succeeded) {
|
||||
for (i = 0; i < iconDir->idCount; i++) {
|
||||
iconData = &data[iconDir->idEntries[i].dwImageOffset];
|
||||
resourceSize = iconDir->idEntries[i].dwBytesInRes;
|
||||
succeeded = UpdateResource(handle, RT_ICON, MAKEINTRESOURCE(i + 1),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), iconData,
|
||||
resourceSize);
|
||||
if (!succeeded)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// finish writing the resource (or discarding the changes upon an error)
|
||||
if (!EndUpdateResource(handle, !succeeded)) {
|
||||
if (succeeded) {
|
||||
succeeded = FALSE;
|
||||
PyErr_SetExcFromWindowsErrWithFilename(PyExc_WindowsError,
|
||||
GetLastError(), executableName);
|
||||
}
|
||||
}
|
||||
|
||||
// clean up
|
||||
if (groupIconDir)
|
||||
PyMem_Free(groupIconDir);
|
||||
if (data)
|
||||
PyMem_Free(data);
|
||||
if (!succeeded)
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ExtBeginUpdateResource()
|
||||
// Wrapper for BeginUpdateResource().
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *ExtBeginUpdateResource(
|
||||
PyObject *self, // passthrough argument
|
||||
PyObject *args) // arguments
|
||||
{
|
||||
BOOL deleteExistingResources;
|
||||
char *fileName;
|
||||
HANDLE handle;
|
||||
|
||||
deleteExistingResources = TRUE;
|
||||
if (!PyArg_ParseTuple(args, "s|i", &fileName, &deleteExistingResources))
|
||||
return NULL;
|
||||
handle = BeginUpdateResource(fileName, deleteExistingResources);
|
||||
if (!handle) {
|
||||
PyErr_SetExcFromWindowsErrWithFilename(PyExc_WindowsError,
|
||||
GetLastError(), fileName);
|
||||
return NULL;
|
||||
}
|
||||
return PyInt_FromLong((long) handle);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ExtUpdateResource()
|
||||
// Wrapper for UpdateResource().
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *ExtUpdateResource(
|
||||
PyObject *self, // passthrough argument
|
||||
PyObject *args) // arguments
|
||||
{
|
||||
int resourceType, resourceId, resourceDataSize;
|
||||
char *resourceData;
|
||||
HANDLE handle;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "iiis#", &handle, &resourceType, &resourceId,
|
||||
&resourceData, &resourceDataSize))
|
||||
return NULL;
|
||||
if (!UpdateResource(handle, MAKEINTRESOURCE(resourceType),
|
||||
MAKEINTRESOURCE(resourceId),
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), resourceData,
|
||||
resourceDataSize)) {
|
||||
PyErr_SetExcFromWindowsErr(PyExc_WindowsError, GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ExtEndUpdateResource()
|
||||
// Wrapper for EndUpdateResource().
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *ExtEndUpdateResource(
|
||||
PyObject *self, // passthrough argument
|
||||
PyObject *args) // arguments
|
||||
{
|
||||
BOOL discardChanges;
|
||||
HANDLE handle;
|
||||
|
||||
discardChanges = FALSE;
|
||||
if (!PyArg_ParseTuple(args, "i|i", &handle, &discardChanges))
|
||||
return NULL;
|
||||
if (!EndUpdateResource(handle, discardChanges)) {
|
||||
PyErr_SetExcFromWindowsErr(PyExc_WindowsError, GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ExtGetDependentFiles()
|
||||
// Return a list of files that this file depends on.
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *ExtGetDependentFiles(
|
||||
PyObject *self, // passthrough argument
|
||||
PyObject *args) // arguments
|
||||
{
|
||||
PyObject *results;
|
||||
char *imageName;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &imageName))
|
||||
return NULL;
|
||||
g_ImageNames = PyDict_New();
|
||||
if (!g_ImageNames)
|
||||
return NULL;
|
||||
if (!BindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES,
|
||||
imageName, NULL, NULL, BindStatusRoutine)) {
|
||||
Py_DECREF(g_ImageNames);
|
||||
PyErr_SetExcFromWindowsErrWithFilename(g_BindErrorException,
|
||||
GetLastError(), imageName);
|
||||
return NULL;
|
||||
}
|
||||
results = PyDict_Keys(g_ImageNames);
|
||||
Py_DECREF(g_ImageNames);
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ExtGetSystemDir()
|
||||
// Return the Windows directory (C:\Windows for example).
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *ExtGetSystemDir(
|
||||
PyObject *self, // passthrough argument
|
||||
PyObject *args) // arguments (ignored)
|
||||
{
|
||||
char dir[MAX_PATH + 1];
|
||||
|
||||
if (GetSystemDirectory(dir, sizeof(dir)))
|
||||
return PyString_FromString(dir);
|
||||
PyErr_SetExcFromWindowsErr(PyExc_RuntimeError, GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ExtSetOptimizeFlag()
|
||||
// Set the optimize flag as needed.
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyObject *ExtSetOptimizeFlag(
|
||||
PyObject *self, // passthrough argument
|
||||
PyObject *args) // arguments
|
||||
{
|
||||
if (!PyArg_ParseTuple(args, "i", &Py_OptimizeFlag))
|
||||
return NULL;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Methods
|
||||
//-----------------------------------------------------------------------------
|
||||
static PyMethodDef g_ModuleMethods[] = {
|
||||
{ "SetOptimizeFlag", ExtSetOptimizeFlag, METH_VARARGS },
|
||||
#ifdef WIN32
|
||||
{ "BeginUpdateResource", ExtBeginUpdateResource, METH_VARARGS },
|
||||
{ "UpdateResource", ExtUpdateResource, METH_VARARGS },
|
||||
{ "EndUpdateResource", ExtEndUpdateResource, METH_VARARGS },
|
||||
{ "AddIcon", ExtAddIcon, METH_VARARGS },
|
||||
{ "GetDependentFiles", ExtGetDependentFiles, METH_VARARGS },
|
||||
{ "GetSystemDir", ExtGetSystemDir, METH_NOARGS },
|
||||
#endif
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// initutil()
|
||||
// Initialization routine for the shared libary.
|
||||
//-----------------------------------------------------------------------------
|
||||
void initutil(void)
|
||||
{
|
||||
PyObject *module;
|
||||
|
||||
module = Py_InitModule("cx_Freeze.util", g_ModuleMethods);
|
||||
if (!module)
|
||||
return;
|
||||
#ifdef WIN32
|
||||
g_BindErrorException = PyErr_NewException("cx_Freeze.util.BindError",
|
||||
NULL, NULL);
|
||||
if (!g_BindErrorException)
|
||||
return;
|
||||
if (PyModule_AddObject(module, "BindError", g_BindErrorException) < 0)
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
@ -237,28 +237,10 @@ cp build/podofo/build/src/Release/podofo.exp lib/
|
||||
cp build/podofo/build/podofo_config.h include/podofo/
|
||||
cp -r build/podofo/src/* include/podofo/
|
||||
|
||||
The following patch (against 0.8.1) was required to get it to compile:
|
||||
You have to use >0.8.1 (>= revision 1269)
|
||||
|
||||
The following patch (against -r1269) was required to get it to compile:
|
||||
|
||||
Index: src/PdfImage.cpp
|
||||
===================================================================
|
||||
--- src/PdfImage.cpp (revision 1261)
|
||||
+++ src/PdfImage.cpp (working copy)
|
||||
@@ -627,7 +627,7 @@
|
||||
|
||||
long lLen = static_cast<long>(pInfo->rowbytes * height);
|
||||
char* pBuffer = static_cast<char*>(malloc(sizeof(char) * lLen));
|
||||
- png_bytep pRows[height];
|
||||
+ png_bytepp pRows = static_cast<png_bytepp>(malloc(sizeof(png_bytep)*height));
|
||||
for(int y=0; y<height; y++)
|
||||
{
|
||||
pRows[y] = reinterpret_cast<png_bytep>(pBuffer + (y * pInfo->rowbytes));
|
||||
@@ -672,6 +672,7 @@
|
||||
this->SetImageData( width, height, pInfo->bit_depth, &stream );
|
||||
|
||||
free(pBuffer);
|
||||
+ free(pRows);
|
||||
}
|
||||
#endif // PODOFO_HAVE_PNG_LIB
|
||||
|
||||
Index: src/PdfFiltersPrivate.cpp
|
||||
===================================================================
|
||||
|
@ -3,7 +3,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import sys, os, re, logging, time, mimetypes, \
|
||||
import uuid, sys, os, re, logging, time, mimetypes, \
|
||||
__builtin__, warnings, multiprocessing
|
||||
from urllib import getproxies
|
||||
__builtin__.__dict__['dynamic_property'] = lambda(func): func(None)
|
||||
@ -23,6 +23,8 @@ from calibre.startup import winutil, winutilerror
|
||||
|
||||
import mechanize
|
||||
|
||||
uuid.uuid4() # Imported before PyQt4 to workaround PyQt4 util-linux conflict on gentoo
|
||||
|
||||
if False:
|
||||
winutil, winutilerror, __appname__, islinux, __version__
|
||||
fcntl, win32event, isfrozen, __author__, terminal_controller
|
||||
|
@ -438,7 +438,7 @@ from calibre.ebooks.txt.output import TXTOutput
|
||||
from calibre.customize.profiles import input_profiles, output_profiles
|
||||
|
||||
from calibre.devices.apple.driver import ITUNES
|
||||
from calibre.devices.hanlin.driver import HANLINV3, HANLINV5, BOOX
|
||||
from calibre.devices.hanlin.driver import HANLINV3, HANLINV5, BOOX, SPECTRA
|
||||
from calibre.devices.blackberry.driver import BLACKBERRY
|
||||
from calibre.devices.cybook.driver import CYBOOK
|
||||
from calibre.devices.eb600.driver import EB600, COOL_ER, SHINEBOOK, \
|
||||
@ -567,6 +567,7 @@ plugins += [
|
||||
MENTOR,
|
||||
SWEEX,
|
||||
PDNOVEL,
|
||||
SPECTRA,
|
||||
ITUNES,
|
||||
]
|
||||
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \
|
||||
@ -657,9 +658,14 @@ class ActionEditCollections(InterfaceActionBase):
|
||||
name = 'Edit Collections'
|
||||
actual_plugin = 'calibre.gui2.actions.edit_collections:EditCollectionsAction'
|
||||
|
||||
class ActionCopyToLibrary(InterfaceActionBase):
|
||||
name = 'Copy To Library'
|
||||
actual_plugin = 'calibre.gui2.actions.copy_to_library:CopyToLibraryAction'
|
||||
|
||||
plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog,
|
||||
ActionConvert, ActionDelete, ActionEditMetadata, ActionView,
|
||||
ActionFetchNews, ActionSaveToDisk, ActionShowBookDetails,
|
||||
ActionRestart, ActionOpenFolder, ActionConnectShare,
|
||||
ActionSendToDevice, ActionHelp, ActionPreferences, ActionSimilarBooks,
|
||||
ActionAddToLibrary, ActionEditCollections, ActionChooseLibrary]
|
||||
ActionAddToLibrary, ActionEditCollections, ActionChooseLibrary,
|
||||
ActionCopyToLibrary]
|
||||
|
@ -112,6 +112,10 @@ class InputFormatPlugin(Plugin):
|
||||
#: convenience method, :meth:`get_image_collection`.
|
||||
is_image_collection = False
|
||||
|
||||
#: Number of CPU cores used by this plugin
|
||||
#: A value of -1 means that it uses all available cores
|
||||
core_usage = 1
|
||||
|
||||
#: If set to True, the input plugin will perform special processing
|
||||
#: to make its output suitable for viewing
|
||||
for_viewer = False
|
||||
|
@ -46,7 +46,7 @@ class ANDROID(USBMS):
|
||||
# Eken?
|
||||
0x040d : { 0x0851 : [0x0001]},
|
||||
}
|
||||
EBOOK_DIR_MAIN = ['wordplayer/calibretransfer', 'eBooks/import', 'Books']
|
||||
EBOOK_DIR_MAIN = ['eBooks/import', 'wordplayer/calibretransfer', 'Books']
|
||||
EXTRA_CUSTOMIZATION_MESSAGE = _('Comma separated list of directories to '
|
||||
'send e-books to on the device. The first one that exists will '
|
||||
'be used')
|
||||
@ -55,9 +55,9 @@ class ANDROID(USBMS):
|
||||
VENDOR_NAME = ['HTC', 'MOTOROLA', 'GOOGLE_', 'ANDROID', 'ACER',
|
||||
'GT-I5700', 'SAMSUNG', 'DELL', 'LINUX']
|
||||
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
|
||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD',
|
||||
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
|
||||
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID']
|
||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD',
|
||||
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
|
||||
'FILE-STOR_GADGET', 'SGH-T959', 'SAMSUNG_ANDROID']
|
||||
|
||||
OSX_MAIN_MEM = 'HTC Android Phone Media'
|
||||
|
@ -80,6 +80,15 @@ class HANLINV3(USBMS):
|
||||
drives['carda'] = main
|
||||
return drives
|
||||
|
||||
class SPECTRA(HANLINV3):
|
||||
|
||||
name = 'Spectra'
|
||||
gui_name = 'Spectra'
|
||||
PRODUCT_ID = [0xa4a5]
|
||||
|
||||
FORMATS = ['epub', 'mobi', 'fb2', 'lit', 'prc', 'djvu', 'pdf', 'rtf', 'txt']
|
||||
|
||||
SUPPORTS_SUB_DIRS = True
|
||||
|
||||
class HANLINV5(HANLINV3):
|
||||
name = 'Hanlin V5 driver'
|
||||
|
@ -181,7 +181,9 @@ class CollectionsBookList(BookList):
|
||||
if lpath not in collections_lpaths[category]:
|
||||
collections_lpaths[category].add(lpath)
|
||||
collections[category].append(book)
|
||||
if attr == 'series':
|
||||
if attr == 'series' or \
|
||||
('series' in collection_attributes and
|
||||
getattr(book, 'series', None) == category):
|
||||
series_categories.add(category)
|
||||
# Sort collections
|
||||
for category, books in collections.items():
|
||||
|
@ -163,8 +163,12 @@ class PageProcessor(list):
|
||||
wand.quantize(self.opts.colors)
|
||||
dest = '%d_%d.%s'%(self.num, i, self.opts.output_format)
|
||||
dest = os.path.join(self.dest, dest)
|
||||
wand.save(dest+'8')
|
||||
os.rename(dest+'8', dest)
|
||||
if dest.lower().endswith('.png'):
|
||||
dest += '8'
|
||||
wand.save(dest)
|
||||
if dest.endswith('8'):
|
||||
dest = dest[:-1]
|
||||
os.rename(dest+'8', dest)
|
||||
self.append(dest)
|
||||
|
||||
def render_pages(tasks, dest, opts, notification=lambda x, y: x):
|
||||
@ -247,6 +251,7 @@ class ComicInput(InputFormatPlugin):
|
||||
description = 'Optimize comic files (.cbz, .cbr, .cbc) for viewing on portable devices'
|
||||
file_types = set(['cbz', 'cbr', 'cbc'])
|
||||
is_image_collection = True
|
||||
core_usage = -1
|
||||
|
||||
options = set([
|
||||
OptionRecommendation(name='colors', recommended_value=256,
|
||||
|
@ -38,7 +38,7 @@ def author_to_author_sort(author):
|
||||
author = _bracket_pat.sub('', author).strip()
|
||||
tokens = author.split()
|
||||
tokens = tokens[-1:] + tokens[:-1]
|
||||
if len(tokens) > 1:
|
||||
if len(tokens) > 1 and method != 'nocomma':
|
||||
tokens[0] += ','
|
||||
return ' '.join(tokens)
|
||||
|
||||
|
@ -26,8 +26,9 @@ class InterfaceAction(QObject):
|
||||
If two :class:`InterfaceAction` objects have the same name, the one with higher
|
||||
priority takes precedence.
|
||||
|
||||
Sub-classes should implement the :meth:`genesis` and
|
||||
:meth:`location_selected` methods.
|
||||
Sub-classes should implement the :meth:`genesis`, :meth:`library_moved`,
|
||||
:meth:`location_selected` :meth:`shutting_down`
|
||||
and :meth:`initialization_complete` methods.
|
||||
|
||||
Once initialized, this plugin has access to the main calibre GUI via the
|
||||
:attr:`gui` member. You can access other plugins by name, for example::
|
||||
@ -108,3 +109,28 @@ class InterfaceAction(QObject):
|
||||
'''
|
||||
pass
|
||||
|
||||
def library_changed(self, db):
|
||||
'''
|
||||
Called whenever the current library is changed.
|
||||
|
||||
:param db: The LibraryDatabase corresponding to the current library.
|
||||
'''
|
||||
pass
|
||||
|
||||
def initialization_complete(self):
|
||||
'''
|
||||
Called once per action when the initialization of the main GUI is
|
||||
completed.
|
||||
'''
|
||||
pass
|
||||
|
||||
def shutting_down(self):
|
||||
'''
|
||||
Called once per plugin when the main GUI is in the process of shutting
|
||||
down. Release any used resources, but try not to block the shutdown for
|
||||
long periods of time.
|
||||
|
||||
:return: False to halt the shutdown. You are responsible for telling
|
||||
the user why the shutdown was halted.
|
||||
'''
|
||||
return True
|
||||
|
@ -5,8 +5,68 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os
|
||||
from functools import partial
|
||||
|
||||
from PyQt4.Qt import QMenu
|
||||
|
||||
from calibre import isbytestring
|
||||
from calibre.constants import filesystem_encoding
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.gui2 import gprefs, warning_dialog
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
|
||||
class LibraryUsageStats(object):
|
||||
|
||||
def __init__(self):
|
||||
self.stats = {}
|
||||
self.read_stats()
|
||||
|
||||
def read_stats(self):
|
||||
stats = gprefs.get('library_usage_stats', {})
|
||||
self.stats = stats
|
||||
|
||||
def write_stats(self):
|
||||
locs = list(self.stats.keys())
|
||||
locs.sort(cmp=lambda x, y: cmp(self.stats[x], self.stats[y]),
|
||||
reverse=True)
|
||||
for key in locs[15:]:
|
||||
self.stats.pop(key)
|
||||
gprefs.set('library_usage_stats', self.stats)
|
||||
|
||||
def remove(self, location):
|
||||
self.stats.pop(location, None)
|
||||
self.write_stats()
|
||||
|
||||
def canonicalize_path(self, lpath):
|
||||
if isbytestring(lpath):
|
||||
lpath = lpath.decode(filesystem_encoding)
|
||||
lpath = lpath.replace(os.sep, '/')
|
||||
return lpath
|
||||
|
||||
def library_used(self, db):
|
||||
lpath = self.canonicalize_path(db.library_path)
|
||||
if lpath not in self.stats:
|
||||
self.stats[lpath] = 0
|
||||
self.stats[lpath] += 1
|
||||
self.write_stats()
|
||||
|
||||
def locations(self, db):
|
||||
lpath = self.canonicalize_path(db.library_path)
|
||||
locs = list(self.stats.keys())
|
||||
if lpath in locs:
|
||||
locs.remove(lpath)
|
||||
locs.sort(cmp=lambda x, y: cmp(self.stats[x], self.stats[y]),
|
||||
reverse=True)
|
||||
for loc in locs:
|
||||
yield self.pretty(loc), loc
|
||||
|
||||
def pretty(self, loc):
|
||||
if loc.endswith('/'):
|
||||
loc = loc[:-1]
|
||||
return loc.split('/')[-1]
|
||||
|
||||
|
||||
class ChooseLibraryAction(InterfaceAction):
|
||||
|
||||
name = 'Choose Library'
|
||||
@ -17,10 +77,85 @@ class ChooseLibraryAction(InterfaceAction):
|
||||
self.count_changed(0)
|
||||
self.qaction.triggered.connect(self.choose_library)
|
||||
|
||||
self.stats = LibraryUsageStats()
|
||||
self.create_action(spec=(_('Switch to library...'), 'lt.png', None,
|
||||
None), attr='action_choose')
|
||||
self.action_choose.triggered.connect(self.choose_library)
|
||||
self.choose_menu = QMenu(self.gui)
|
||||
self.choose_menu.addAction(self.action_choose)
|
||||
self.qaction.setMenu(self.choose_menu)
|
||||
|
||||
self.quick_menu = QMenu(_('Quick switch'))
|
||||
self.quick_menu_action = self.choose_menu.addMenu(self.quick_menu)
|
||||
self.qs_separator = self.choose_menu.addSeparator()
|
||||
self.switch_actions = []
|
||||
for i in range(5):
|
||||
ac = self.create_action(spec=('', None, None, None),
|
||||
attr='switch_action%d'%i)
|
||||
self.switch_actions.append(ac)
|
||||
ac.setVisible(False)
|
||||
ac.triggered.connect(partial(self.qs_requested, i))
|
||||
self.choose_menu.addAction(ac)
|
||||
|
||||
def library_name(self):
|
||||
db = self.gui.library_view.model().db
|
||||
path = db.library_path
|
||||
if isbytestring(path):
|
||||
path = path.decode(filesystem_encoding)
|
||||
path = path.replace(os.sep, '/')
|
||||
return self.stats.pretty(path)
|
||||
|
||||
def library_changed(self, db):
|
||||
self.stats.library_used(db)
|
||||
self.build_menus()
|
||||
|
||||
def initialization_complete(self):
|
||||
self.library_changed(self.gui.library_view.model().db)
|
||||
|
||||
def build_menus(self):
|
||||
db = self.gui.library_view.model().db
|
||||
locations = list(self.stats.locations(db))
|
||||
for ac in self.switch_actions:
|
||||
ac.setVisible(False)
|
||||
self.quick_menu.clear()
|
||||
self.qs_locations = [i[1] for i in locations]
|
||||
for name, loc in locations:
|
||||
self.quick_menu.addAction(name, partial(self.switch_requested,
|
||||
loc))
|
||||
|
||||
for i, x in enumerate(locations[:len(self.switch_actions)]):
|
||||
name, loc = x
|
||||
ac = self.switch_actions[i]
|
||||
ac.setText(name)
|
||||
ac.setVisible(True)
|
||||
|
||||
self.quick_menu_action.setVisible(bool(locations))
|
||||
|
||||
|
||||
def location_selected(self, loc):
|
||||
enabled = loc == 'library'
|
||||
self.qaction.setEnabled(enabled)
|
||||
|
||||
def switch_requested(self, location):
|
||||
if not self.change_library_allowed():
|
||||
return
|
||||
loc = location.replace('/', os.sep)
|
||||
exists = self.gui.library_view.model().db.exists_at(loc)
|
||||
if not exists:
|
||||
warning_dialog(self.gui, _('No library found'),
|
||||
_('No existing calibre library was found at %s.'
|
||||
' It will be removed from the list of known'
|
||||
' libraries.')%loc, show=True)
|
||||
self.stats.remove(location)
|
||||
self.build_menus()
|
||||
return
|
||||
|
||||
prefs['library_path'] = loc
|
||||
self.gui.library_moved(loc)
|
||||
|
||||
def qs_requested(self, idx, *args):
|
||||
self.switch_requested(self.qs_locations[idx])
|
||||
|
||||
def count_changed(self, new_count):
|
||||
text = self.action_spec[0]%new_count
|
||||
a = self.qaction
|
||||
@ -31,9 +166,23 @@ class ChooseLibraryAction(InterfaceAction):
|
||||
a.setWhatsThis(tooltip)
|
||||
|
||||
def choose_library(self, *args):
|
||||
if not self.change_library_allowed():
|
||||
return
|
||||
from calibre.gui2.dialogs.choose_library import ChooseLibrary
|
||||
db = self.gui.library_view.model().db
|
||||
c = ChooseLibrary(db, self.gui.library_moved, self.gui)
|
||||
c.exec_()
|
||||
|
||||
def change_library_allowed(self):
|
||||
if self.gui.device_connected:
|
||||
warning_dialog(self.gui, _('Not allowed'),
|
||||
_('You cannot change libraries when a device is'
|
||||
' connected.'), show=True)
|
||||
return False
|
||||
if self.gui.job_manager.has_jobs():
|
||||
warning_dialog(self.gui, _('Not allowed'),
|
||||
_('You cannot change libraries while jobs'
|
||||
' are running.'), show=True)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -14,6 +14,7 @@ from calibre.gui2 import error_dialog, Dispatcher
|
||||
from calibre.gui2.tools import convert_single_ebook, convert_bulk_ebook
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
from calibre.customize.ui import plugin_for_input_format
|
||||
|
||||
class ConvertAction(InterfaceAction):
|
||||
|
||||
@ -115,9 +116,19 @@ class ConvertAction(InterfaceAction):
|
||||
def queue_convert_jobs(self, jobs, changed, bad, rows, previous,
|
||||
converted_func, extra_job_args=[]):
|
||||
for func, args, desc, fmt, id, temp_files in jobs:
|
||||
input_file = args[0]
|
||||
input_fmt = os.path.splitext(input_file)[1]
|
||||
core_usage = 1
|
||||
if input_fmt:
|
||||
input_fmt = input_fmt[1:]
|
||||
plugin = plugin_for_input_format(input_fmt)
|
||||
if plugin is not None:
|
||||
core_usage = plugin.core_usage
|
||||
|
||||
if id not in bad:
|
||||
job = self.gui.job_manager.run_job(Dispatcher(converted_func),
|
||||
func, args=args, description=desc)
|
||||
func, args=args, description=desc,
|
||||
core_usage=core_usage)
|
||||
args = [temp_files, fmt, id]+extra_job_args
|
||||
self.conversion_jobs[job] = tuple(args)
|
||||
|
||||
|
21
src/calibre/gui2/actions/copy_to_library.py
Normal file
21
src/calibre/gui2/actions/copy_to_library.py
Normal file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from PyQt4.Qt import QMenu
|
||||
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
|
||||
class CopyToLibraryAction(InterfaceAction):
|
||||
|
||||
name = 'Copy To Library'
|
||||
action_spec = (_('Copy to library'), 'lt.png',
|
||||
_('Copy selected books to the specified library'), None)
|
||||
|
||||
def genesis(self):
|
||||
self.menu = QMenu(self.gui)
|
||||
|
||||
|
@ -16,6 +16,7 @@ from calibre.gui2.dialogs.metadata_bulk import MetadataBulkDialog
|
||||
from calibre.gui2.dialogs.confirm_delete import confirm
|
||||
from calibre.gui2.dialogs.tag_list_editor import TagListEditor
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
from calibre.gui2.dialogs.progress import BlockingBusy
|
||||
|
||||
class EditMetadataAction(InterfaceAction):
|
||||
|
||||
@ -95,18 +96,19 @@ class EditMetadataAction(InterfaceAction):
|
||||
x = _('social metadata')
|
||||
else:
|
||||
x = _('covers') if covers and not set_metadata else _('metadata')
|
||||
self.gui.progress_indicator.start(
|
||||
_('Downloading %s for %d book(s)')%(x, len(ids)))
|
||||
self._book_metadata_download_check = QTimer(self.gui)
|
||||
self._book_metadata_download_check.timeout.connect(self.book_metadata_download_check,
|
||||
type=Qt.QueuedConnection)
|
||||
self._book_metadata_download_check.start(100)
|
||||
self._bb_dialog = BlockingBusy(_('Downloading %s for %d book(s)')%(x,
|
||||
len(ids)), parent=self.gui)
|
||||
self._bb_dialog.exec_()
|
||||
|
||||
def book_metadata_download_check(self):
|
||||
if self._download_book_metadata.is_alive():
|
||||
return
|
||||
self._book_metadata_download_check.stop()
|
||||
self.gui.progress_indicator.stop()
|
||||
self._bb_dialog.accept()
|
||||
cr = self.gui.library_view.currentIndex().row()
|
||||
x = self._download_book_metadata
|
||||
self._download_book_metadata = None
|
||||
@ -174,8 +176,14 @@ class EditMetadataAction(InterfaceAction):
|
||||
_('No books selected'))
|
||||
d.exec_()
|
||||
return
|
||||
if MetadataBulkDialog(self.gui, rows,
|
||||
self.gui.library_view.model().db).changed:
|
||||
# Prevent the TagView from updating due to signals from the database
|
||||
self.gui.tags_view.blockSignals(True)
|
||||
try:
|
||||
changed = MetadataBulkDialog(self.gui, rows,
|
||||
self.gui.library_view.model().db).changed
|
||||
finally:
|
||||
self.gui.tags_view.blockSignals(False)
|
||||
if changed:
|
||||
self.gui.library_view.model().resort(reset=False)
|
||||
self.gui.library_view.model().research()
|
||||
self.gui.tags_view.recount()
|
||||
|
@ -31,7 +31,12 @@ class FetchNewsAction(InterfaceAction):
|
||||
self.qaction.setMenu(self.scheduler.news_menu)
|
||||
self.qaction.triggered.connect(
|
||||
self.scheduler.show_dialog)
|
||||
self.database_changed = self.scheduler.database_changed
|
||||
|
||||
def library_changed(self, db):
|
||||
self.scheduler.database_changed(db)
|
||||
|
||||
def initialization_complete(self):
|
||||
self.connect_scheduler()
|
||||
|
||||
def connect_scheduler(self):
|
||||
self.scheduler.delete_old_news.connect(
|
||||
|
@ -16,7 +16,7 @@ class PluginWidget(QWidget,Ui_Form):
|
||||
|
||||
TITLE = _('E-book options')
|
||||
HELP = _('Options specific to')+' EPUB/MOBI '+_('output')
|
||||
OPTION_FIELDS = [('exclude_genre','\[[\w ]*\]'),
|
||||
OPTION_FIELDS = [('exclude_genre','\[.+\]'),
|
||||
('exclude_tags','~,'+_('Catalog')),
|
||||
('generate_titles', True),
|
||||
('generate_recently_added', True),
|
||||
|
@ -80,7 +80,7 @@
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>Regex tips:
|
||||
- The default regex - \[[\w ]*\] - excludes genre tags of the form [tag], e.g., [Amazon Freebie]
|
||||
- The default regex - \[.+\] - excludes genre tags of the form [tag], e.g., [Amazon Freebie]
|
||||
- A regex pattern of a single dot excludes all genre tags, generating no Genre Section</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
|
@ -16,18 +16,39 @@ from calibre.ebooks.conversion.config import load_defaults, \
|
||||
save_defaults as save_defaults_, \
|
||||
load_specifics, GuiRecommendations
|
||||
from calibre import prepare_string_for_xml
|
||||
from calibre.customize.ui import plugin_for_input_format
|
||||
|
||||
def config_widget_for_input_plugin(plugin):
|
||||
name = plugin.name.lower().replace(' ', '_')
|
||||
try:
|
||||
return __import__('calibre.gui2.convert.'+name,
|
||||
fromlist=[1]).PluginWidget
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
def bulk_defaults_for_input_format(fmt):
|
||||
plugin = plugin_for_input_format(fmt)
|
||||
if plugin is not None:
|
||||
w = config_widget_for_input_plugin(plugin)
|
||||
if w is not None:
|
||||
return load_defaults(w.COMMIT_NAME)
|
||||
return {}
|
||||
|
||||
|
||||
|
||||
class Widget(QWidget):
|
||||
|
||||
TITLE = _('Unknown')
|
||||
ICON = I('config.svg')
|
||||
HELP = ''
|
||||
COMMIT_NAME = None
|
||||
|
||||
def __init__(self, parent, name, options):
|
||||
def __init__(self, parent, options):
|
||||
QWidget.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
self._options = options
|
||||
self._name = name
|
||||
self._name = self.commit_name = self.COMMIT_NAME
|
||||
assert self._name is not None
|
||||
self._icon = QIcon(self.ICON)
|
||||
for name in self._options:
|
||||
if not hasattr(self, 'opt_'+name):
|
||||
@ -58,7 +79,7 @@ class Widget(QWidget):
|
||||
def commit_options(self, save_defaults=False):
|
||||
recs = self.create_recommendations()
|
||||
if save_defaults:
|
||||
save_defaults_(self._name, recs)
|
||||
save_defaults_(self.commit_name, recs)
|
||||
return recs
|
||||
|
||||
def create_recommendations(self):
|
||||
|
@ -14,9 +14,10 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('Comic Input')
|
||||
HELP = _('Options specific to')+' comic '+_('input')
|
||||
COMMIT_NAME = 'comic_input'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'comic_input',
|
||||
Widget.__init__(self, parent,
|
||||
['colors', 'dont_normalize', 'keep_aspect_ratio', 'right2left',
|
||||
'despeckle', 'no_sort', 'no_process', 'landscape',
|
||||
'dont_sharpen', 'disable_trim', 'wide', 'output_format',
|
||||
|
@ -19,9 +19,10 @@ class DebugWidget(Widget, Ui_Form):
|
||||
TITLE = _('Debug')
|
||||
ICON = I('debug.svg')
|
||||
HELP = _('Debug the conversion process.')
|
||||
COMMIT_NAME = 'debug'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'debug',
|
||||
Widget.__init__(self, parent,
|
||||
['debug_pipeline']
|
||||
)
|
||||
self.db, self.book_id = db, book_id
|
||||
|
@ -14,9 +14,10 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('EPUB Output')
|
||||
HELP = _('Options specific to')+' EPUB '+_('output')
|
||||
COMMIT_NAME = 'epub_output'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'epub_output',
|
||||
Widget.__init__(self, parent,
|
||||
['dont_split_on_page_breaks', 'flow_size',
|
||||
'no_default_epub_cover', 'no_svg_cover',
|
||||
'preserve_cover_aspect_ratio',]
|
||||
|
@ -11,9 +11,10 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('FB2 Input')
|
||||
HELP = _('Options specific to')+' FB2 '+_('input')
|
||||
COMMIT_NAME = 'fb2_input'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'fb2_input',
|
||||
Widget.__init__(self, parent,
|
||||
['no_inline_fb2_toc'])
|
||||
self.db, self.book_id = db, book_id
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
@ -13,8 +13,9 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('FB2 Output')
|
||||
HELP = _('Options specific to')+' FB2 '+_('output')
|
||||
COMMIT_NAME = 'fb2_output'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'fb2_output', ['inline_toc'])
|
||||
Widget.__init__(self, parent, ['inline_toc'])
|
||||
self.db, self.book_id = db, book_id
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
@ -16,9 +16,10 @@ class LookAndFeelWidget(Widget, Ui_Form):
|
||||
TITLE = _('Look & Feel')
|
||||
ICON = I('lookfeel.svg')
|
||||
HELP = _('Control the look and feel of the output')
|
||||
COMMIT_NAME = 'look_and_feel'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'look_and_feel',
|
||||
Widget.__init__(self, parent,
|
||||
['change_justification', 'extra_css', 'base_font_size',
|
||||
'font_size_mapping', 'line_height',
|
||||
'linearize_tables',
|
||||
|
@ -18,9 +18,10 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('LRF Output')
|
||||
HELP = _('Options specific to')+' LRF '+_('output')
|
||||
COMMIT_NAME = 'lrf_output'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'lrf_output',
|
||||
Widget.__init__(self, parent,
|
||||
['wordspace', 'header', 'header_format',
|
||||
'minimum_indent', 'serif_family',
|
||||
'render_tables_as_images', 'sans_family', 'mono_family',
|
||||
|
@ -42,9 +42,10 @@ class MetadataWidget(Widget, Ui_Form):
|
||||
ICON = I('dialog_information.svg')
|
||||
HELP = _('Set the metadata. The output file will contain as much of this '
|
||||
'metadata as possible.')
|
||||
COMMIT_NAME = 'metadata'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'metadata', ['prefer_metadata_cover'])
|
||||
Widget.__init__(self, parent, ['prefer_metadata_cover'])
|
||||
self.db, self.book_id = db, book_id
|
||||
self.cover_changed = False
|
||||
self.cover_data = None
|
||||
|
@ -19,10 +19,10 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('MOBI Output')
|
||||
HELP = _('Options specific to')+' MOBI '+_('output')
|
||||
|
||||
COMMIT_NAME = 'mobi_output'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'mobi_output',
|
||||
Widget.__init__(self, parent,
|
||||
['prefer_author_sort', 'rescale_images', 'toc_title',
|
||||
'dont_compress', 'no_inline_toc', 'masthead_font','personal_doc']
|
||||
)
|
||||
|
@ -33,9 +33,10 @@ class ProfileModel(QAbstractListModel):
|
||||
class PageSetupWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('Page Setup')
|
||||
COMMIT_NAME = 'page_setup'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'page_setup',
|
||||
Widget.__init__(self, parent,
|
||||
['margin_top', 'margin_left', 'margin_right', 'margin_bottom',
|
||||
'input_profile', 'output_profile']
|
||||
)
|
||||
|
@ -11,9 +11,10 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('PDB Input')
|
||||
HELP = _('Options specific to')+' PDB '+_('input')
|
||||
COMMIT_NAME = 'pdb_input'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'pdb_input',
|
||||
Widget.__init__(self, parent,
|
||||
['single_line_paras', 'print_formatted_paras'])
|
||||
self.db, self.book_id = db, book_id
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
@ -15,9 +15,10 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('PDB Output')
|
||||
HELP = _('Options specific to')+' PDB '+_('output')
|
||||
COMMIT_NAME = 'pdb_output'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'pdb_output', ['format', 'inline_toc'])
|
||||
Widget.__init__(self, parent, ['format', 'inline_toc'])
|
||||
self.db, self.book_id = db, book_id
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
||||
|
@ -11,9 +11,10 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('PDF Input')
|
||||
HELP = _('Options specific to')+' PDF '+_('input')
|
||||
COMMIT_NAME = 'pdf_input'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'pdf_input',
|
||||
Widget.__init__(self, parent,
|
||||
['no_images', 'unwrap_factor'])
|
||||
self.db, self.book_id = db, book_id
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
@ -16,9 +16,10 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('PDF Output')
|
||||
HELP = _('Options specific to')+' PDF '+_('output')
|
||||
COMMIT_NAME = 'pdf_output'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'pdf_output', ['paper_size',
|
||||
Widget.__init__(self, parent, ['paper_size',
|
||||
'orientation', 'preserve_cover_aspect_ratio'])
|
||||
self.db, self.book_id = db, book_id
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
@ -13,8 +13,9 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('RB Output')
|
||||
HELP = _('Options specific to')+' RB '+_('output')
|
||||
COMMIT_NAME = 'rb_output'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'rb_output', ['inline_toc'])
|
||||
Widget.__init__(self, parent, ['inline_toc'])
|
||||
self.db, self.book_id = db, book_id
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
@ -18,9 +18,10 @@ class StructureDetectionWidget(Widget, Ui_Form):
|
||||
ICON = I('chapters.svg')
|
||||
HELP = _('Fine tune the detection of chapter headings and '
|
||||
'other document structure.')
|
||||
COMMIT_NAME = 'structure_detection'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'structure_detection',
|
||||
Widget.__init__(self, parent,
|
||||
['chapter', 'chapter_mark',
|
||||
'remove_first_image',
|
||||
'insert_metadata', 'page_breaks_before',
|
||||
|
@ -16,9 +16,10 @@ class TOCWidget(Widget, Ui_Form):
|
||||
TITLE = _('Table of\nContents')
|
||||
ICON = I('series.svg')
|
||||
HELP = _('Control the creation/conversion of the Table of Contents.')
|
||||
COMMIT_NAME = 'toc'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'toc',
|
||||
Widget.__init__(self, parent,
|
||||
['level1_toc', 'level2_toc', 'level3_toc',
|
||||
'toc_threshold', 'max_toc_links', 'no_chapters_in_toc',
|
||||
'use_auto_toc', 'toc_filter',
|
||||
|
@ -11,9 +11,10 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('TXT Input')
|
||||
HELP = _('Options specific to')+' TXT '+_('input')
|
||||
COMMIT_NAME = 'txt_input'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'txt_input',
|
||||
Widget.__init__(self, parent,
|
||||
['single_line_paras', 'print_formatted_paras', 'markdown',
|
||||
'markdown_disable_toc', 'preserve_spaces'])
|
||||
self.db, self.book_id = db, book_id
|
||||
|
@ -15,9 +15,10 @@ class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('TXT Output')
|
||||
HELP = _('Options specific to')+' TXT '+_('output')
|
||||
COMMIT_NAME = 'txt_output'
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'txt_output',
|
||||
Widget.__init__(self, parent,
|
||||
['newline', 'max_line_length', 'force_max_line_length',
|
||||
'inline_toc'])
|
||||
self.db, self.book_id = db, book_id
|
||||
|
@ -196,6 +196,7 @@ class CoverFlowMixin(object):
|
||||
|
||||
def show_cover_browser(self):
|
||||
d = CBDialog(self, self.cover_flow)
|
||||
d.addAction(self.cb_splitter.action_toggle)
|
||||
self.cover_flow.setVisible(True)
|
||||
self.cover_flow.setFocus(Qt.OtherFocusReason)
|
||||
d.show()
|
||||
|
@ -32,11 +32,17 @@ class Base(object):
|
||||
val = self.normalize_db_val(val)
|
||||
self.setter(val)
|
||||
|
||||
@property
|
||||
def gui_val(self):
|
||||
return self.getter()
|
||||
|
||||
|
||||
def commit(self, book_id, notify=False):
|
||||
val = self.getter()
|
||||
val = self.gui_val
|
||||
val = self.normalize_ui_val(val)
|
||||
if val != self.initial_val:
|
||||
self.db.set_custom(book_id, val, num=self.col_id, notify=notify)
|
||||
self.db.set_custom(book_id, val, num=self.col_id, notify=notify,
|
||||
commit=False)
|
||||
|
||||
def normalize_db_val(self, val):
|
||||
return val
|
||||
@ -284,10 +290,14 @@ class Series(Base):
|
||||
if idx is not None:
|
||||
self.widgets[1].setCurrentIndex(idx)
|
||||
|
||||
def getter(self):
|
||||
n = unicode(self.name_widget.currentText()).strip()
|
||||
i = self.idx_widget.value()
|
||||
return n, i
|
||||
|
||||
def commit(self, book_id, notify=False):
|
||||
val = unicode(self.name_widget.currentText()).strip()
|
||||
val, s_index = self.gui_val
|
||||
val = self.normalize_ui_val(val)
|
||||
s_index = self.idx_widget.value()
|
||||
if val != self.initial_val or s_index != self.initial_index:
|
||||
if s_index == 0.0:
|
||||
if tweaks['series_index_auto_increment'] == 'next':
|
||||
@ -296,7 +306,7 @@ class Series(Base):
|
||||
else:
|
||||
s_index = None
|
||||
self.db.set_custom(book_id, val, extra=s_index,
|
||||
num=self.col_id, notify=notify)
|
||||
num=self.col_id, notify=notify, commit=False)
|
||||
|
||||
widgets = {
|
||||
'bool' : Bool,
|
||||
@ -378,6 +388,13 @@ def populate_metadata_page(layout, db, book_id, bulk=False, two_column=False, pa
|
||||
|
||||
class BulkBase(Base):
|
||||
|
||||
@property
|
||||
def gui_val(self):
|
||||
if not hasattr(self, '_cached_gui_val_'):
|
||||
self._cached_gui_val_ = self.getter()
|
||||
return self._cached_gui_val_
|
||||
|
||||
|
||||
def get_initial_value(self, book_ids):
|
||||
values = set([])
|
||||
for book_id in book_ids:
|
||||
@ -394,26 +411,16 @@ class BulkBase(Base):
|
||||
ans = list(ans)
|
||||
return ans
|
||||
|
||||
def process_each_book(self):
|
||||
return False
|
||||
|
||||
def initialize(self, book_ids):
|
||||
if not self.process_each_book():
|
||||
self.initial_val = val = self.get_initial_value(book_ids)
|
||||
val = self.normalize_db_val(val)
|
||||
self.setter(val)
|
||||
self.initial_val = val = self.get_initial_value(book_ids)
|
||||
val = self.normalize_db_val(val)
|
||||
self.setter(val)
|
||||
|
||||
def commit(self, book_ids, notify=False):
|
||||
if self.process_each_book():
|
||||
for book_id in book_ids:
|
||||
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
|
||||
self.db.set_custom(book_id, self.getter(val), num=self.col_id, notify=notify)
|
||||
else:
|
||||
val = self.getter()
|
||||
val = self.normalize_ui_val(val)
|
||||
if val != self.initial_val:
|
||||
for book_id in book_ids:
|
||||
self.db.set_custom(book_id, val, num=self.col_id, notify=notify)
|
||||
val = self.gui_val
|
||||
val = self.normalize_ui_val(val)
|
||||
if val != self.initial_val:
|
||||
self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify)
|
||||
|
||||
class BulkBool(BulkBase, Bool):
|
||||
pass
|
||||
@ -431,6 +438,7 @@ class BulkDateTime(BulkBase, DateTime):
|
||||
pass
|
||||
|
||||
class BulkSeries(BulkBase):
|
||||
|
||||
def setup_ui(self, parent):
|
||||
values = self.all_values = list(self.db.all_custom(num=self.col_id))
|
||||
values.sort(cmp = lambda x,y: cmp(x.lower(), y.lower()))
|
||||
@ -450,27 +458,31 @@ class BulkSeries(BulkBase):
|
||||
self.name_widget.addItem(c)
|
||||
self.name_widget.setEditText('')
|
||||
|
||||
def getter(self):
|
||||
n = unicode(self.name_widget.currentText()).strip()
|
||||
i = self.idx_widget.checkState()
|
||||
return n, i
|
||||
|
||||
def commit(self, book_ids, notify=False):
|
||||
val = unicode(self.name_widget.currentText()).strip()
|
||||
val, update_indices = self.gui_val
|
||||
val = self.normalize_ui_val(val)
|
||||
update_indices = self.idx_widget.checkState()
|
||||
if val != '':
|
||||
extras = []
|
||||
next_index = self.db.get_next_cc_series_num_for(val, num=self.col_id)
|
||||
for book_id in book_ids:
|
||||
if update_indices:
|
||||
if tweaks['series_index_auto_increment'] == 'next':
|
||||
s_index = self.db.get_next_cc_series_num_for\
|
||||
(val, num=self.col_id)
|
||||
s_index = next_index
|
||||
next_index += 1
|
||||
else:
|
||||
s_index = 1.0
|
||||
else:
|
||||
s_index = self.db.get_custom_extra(book_id, num=self.col_id,
|
||||
index_is_id=True)
|
||||
self.db.set_custom(book_id, val, extra=s_index,
|
||||
extras.append(s_index)
|
||||
self.db.set_custom_bulk(book_ids, val, extras=extras,
|
||||
num=self.col_id, notify=notify)
|
||||
|
||||
def process_each_book(self):
|
||||
return True
|
||||
|
||||
class RemoveTags(QWidget):
|
||||
|
||||
def __init__(self, parent, values):
|
||||
@ -533,20 +545,37 @@ class BulkText(BulkBase):
|
||||
if idx is not None:
|
||||
self.widgets[1].setCurrentIndex(idx)
|
||||
|
||||
def process_each_book(self):
|
||||
return self.col_metadata['is_multiple']
|
||||
|
||||
def getter(self, original_value = None):
|
||||
def commit(self, book_ids, notify=False):
|
||||
if self.col_metadata['is_multiple']:
|
||||
if self.removing_widget.checkbox.isChecked():
|
||||
ans = set()
|
||||
remove_all, adding, rtext = self.gui_val
|
||||
remove = set()
|
||||
if remove_all:
|
||||
for book_id in book_ids:
|
||||
remove |= set(self.db.get_custom(book_id, num=self.col_id,
|
||||
index_is_id=True))
|
||||
else:
|
||||
ans = set(original_value)
|
||||
ans -= set([v.strip() for v in
|
||||
unicode(self.removing_widget.tags_box.text()).split(',')])
|
||||
ans |= set([v.strip() for v in
|
||||
unicode(self.adding_widget.text()).split(',')])
|
||||
return ans # returning a set instead of a list works, for now at least.
|
||||
txt = rtext
|
||||
if txt:
|
||||
remove = set([v.strip() for v in txt.split(',')])
|
||||
txt = adding
|
||||
if txt:
|
||||
add = set([v.strip() for v in txt.split(',')])
|
||||
else:
|
||||
add = set()
|
||||
self.db.set_custom_bulk_multiple(book_ids, add=add, remove=remove,
|
||||
num=self.col_id)
|
||||
else:
|
||||
val = self.gui_val
|
||||
val = self.normalize_ui_val(val)
|
||||
if val != self.initial_val:
|
||||
self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify)
|
||||
|
||||
def getter(self):
|
||||
if self.col_metadata['is_multiple']:
|
||||
return self.removing_widget.checkbox.isChecked(), \
|
||||
unicode(self.adding_widget.text()), \
|
||||
unicode(self.removing_widget.tags_box.text())
|
||||
|
||||
val = unicode(self.widgets[1].currentText()).strip()
|
||||
if not val:
|
||||
val = None
|
||||
|
@ -33,7 +33,7 @@ from calibre.devices.apple.driver import ITUNES_ASYNC
|
||||
from calibre.devices.folder_device.driver import FOLDER_DEVICE
|
||||
from calibre.ebooks.metadata.meta import set_metadata
|
||||
from calibre.constants import DEBUG
|
||||
from calibre.utils.config import prefs
|
||||
from calibre.utils.config import prefs, tweaks
|
||||
|
||||
# }}}
|
||||
|
||||
@ -613,6 +613,8 @@ class DeviceMixin(object): # {{{
|
||||
self.device_manager = DeviceManager(Dispatcher(self.device_detected),
|
||||
self.job_manager, Dispatcher(self.status_bar.show_message))
|
||||
self.device_manager.start()
|
||||
if tweaks['auto_connect_to_folder']:
|
||||
self.connect_to_folder_named(tweaks['auto_connect_to_folder'])
|
||||
|
||||
def set_default_thumbnail(self, height):
|
||||
r = QSvgRenderer(I('book.svg'))
|
||||
@ -624,6 +626,11 @@ class DeviceMixin(object): # {{{
|
||||
self.default_thumbnail = (pixmap.width(), pixmap.height(),
|
||||
pixmap_to_data(pixmap))
|
||||
|
||||
def connect_to_folder_named(self, folder):
|
||||
if os.path.exists(folder) and os.path.isdir(folder):
|
||||
self.device_manager.mount_device(kls=FOLDER_DEVICE, kind='folder',
|
||||
path=folder)
|
||||
|
||||
def connect_to_folder(self):
|
||||
dir = choose_dir(self, 'Select Device Folder',
|
||||
_('Select folder to open as device'))
|
||||
|
@ -48,11 +48,11 @@ class BookInfo(QDialog, Ui_BookInfo):
|
||||
self.refresh(row)
|
||||
|
||||
def open_book_path(self, path):
|
||||
if os.sep in unicode(path):
|
||||
path = unicode(path)
|
||||
if os.sep in path:
|
||||
open_local_file(path)
|
||||
else:
|
||||
format = unicode(path)
|
||||
path = self.view.model().db.format_abspath(self.current_row, format)
|
||||
path = self.view.model().db.format_abspath(self.current_row, path)
|
||||
if path is not None:
|
||||
open_local_file(path)
|
||||
|
||||
|
@ -57,7 +57,7 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="fit_cover">
|
||||
<property name="text">
|
||||
<string>Fit &cover to view</string>
|
||||
<string>Fit &cover within view</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -29,12 +29,15 @@ from calibre.customize.ui import initialized_plugins, is_disabled, enable_plugin
|
||||
input_format_plugins, \
|
||||
output_format_plugins, available_output_formats
|
||||
from calibre.utils.smtp import config as smtp_prefs
|
||||
from calibre.gui2.convert import config_widget_for_input_plugin
|
||||
from calibre.gui2.convert.look_and_feel import LookAndFeelWidget
|
||||
from calibre.gui2.convert.page_setup import PageSetupWidget
|
||||
from calibre.gui2.convert.structure_detection import StructureDetectionWidget
|
||||
from calibre.ebooks.conversion.plumber import Plumber
|
||||
from calibre.utils.logging import Log
|
||||
from calibre.gui2.convert.toc import TOCWidget
|
||||
from calibre.utils.search_query_parser import saved_searches
|
||||
|
||||
|
||||
class ConfigTabs(QTabWidget):
|
||||
|
||||
@ -58,15 +61,10 @@ class ConfigTabs(QTabWidget):
|
||||
self.widgets = [lf, ps, sd, toc]
|
||||
|
||||
for plugin in input_format_plugins():
|
||||
name = plugin.name.lower().replace(' ', '_')
|
||||
try:
|
||||
input_widget = __import__('calibre.gui2.convert.'+name,
|
||||
fromlist=[1])
|
||||
pw = input_widget.PluginWidget
|
||||
pw = config_widget_for_input_plugin(plugin)
|
||||
if pw is not None:
|
||||
pw.ICON = I('forward.svg')
|
||||
self.widgets.append(widget_factory(pw))
|
||||
except ImportError:
|
||||
continue
|
||||
|
||||
for plugin in output_format_plugins():
|
||||
name = plugin.name.lower().replace(' ', '_')
|
||||
@ -496,6 +494,14 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
||||
if x == config['gui_layout']:
|
||||
li = i
|
||||
self.opt_gui_layout.setCurrentIndex(li)
|
||||
restrictions = sorted(saved_searches().names(),
|
||||
cmp=lambda x,y: cmp(x.lower(), y.lower()))
|
||||
restrictions.insert(0, '')
|
||||
for x in ('gui', 'cs'):
|
||||
w = getattr(self, 'opt_%s_restriction'%x)
|
||||
w.addItems(restrictions)
|
||||
idx = w.findText(self.db.prefs.get(x+'_restriction', ''))
|
||||
w.setCurrentIndex(0 if idx < 0 else idx)
|
||||
self.opt_disable_animations.setChecked(config['disable_animations'])
|
||||
self.opt_show_donate_button.setChecked(config['show_donate_button'])
|
||||
idx = 0
|
||||
@ -930,6 +936,9 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
|
||||
config['internally_viewed_formats'] = fmts
|
||||
val = self.opt_gui_layout.itemData(self.opt_gui_layout.currentIndex()).toString()
|
||||
config['gui_layout'] = unicode(val)
|
||||
for x in ('gui', 'cs'):
|
||||
w = getattr(self, 'opt_%s_restriction'%x)
|
||||
self.db.prefs.set(x+'_restriction', unicode(w.currentText()))
|
||||
|
||||
if must_restart:
|
||||
warning_dialog(self, _('Must restart'),
|
||||
|
@ -7,7 +7,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1000</width>
|
||||
<width>1001</width>
|
||||
<height>730</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -89,8 +89,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>720</width>
|
||||
<height>679</height>
|
||||
<width>725</width>
|
||||
<height>683</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_7">
|
||||
@ -295,7 +295,7 @@
|
||||
</widget>
|
||||
<widget class="QWidget" name="page">
|
||||
<layout class="QGridLayout" name="gridLayout_8">
|
||||
<item row="1" column="0">
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="roman_numerals">
|
||||
<property name="text">
|
||||
<string>Use &Roman numerals for series number</string>
|
||||
@ -305,35 +305,35 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="systray_icon">
|
||||
<property name="text">
|
||||
<string>Enable system &tray icon (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QCheckBox" name="systray_notifications">
|
||||
<property name="text">
|
||||
<string>Show &notifications in system tray</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="show_splash_screen">
|
||||
<property name="text">
|
||||
<string>Show &splash screen at startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="separate_cover_flow">
|
||||
<property name="text">
|
||||
<string>Show cover &browser in a separate window (needs restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="show_avg_rating">
|
||||
<property name="text">
|
||||
<string>Show &average ratings in the tags browser</string>
|
||||
@ -343,7 +343,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="search_as_you_type">
|
||||
<property name="text">
|
||||
<string>Search as you type</string>
|
||||
@ -353,21 +353,21 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<item row="9" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="sync_news">
|
||||
<property name="text">
|
||||
<string>Automatically send downloaded &news to ebook reader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="2">
|
||||
<item row="10" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="delete_news">
|
||||
<property name="text">
|
||||
<string>&Delete news from library when it is automatically sent to reader</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0" colspan="2">
|
||||
<item row="11" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_6">
|
||||
@ -384,7 +384,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="11" column="0" colspan="2">
|
||||
<item row="12" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_7">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
@ -570,7 +570,36 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_170">
|
||||
<property name="text">
|
||||
<string>Restriction to apply when the current library is opened:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_gui_restriction</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="opt_gui_restriction">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>250</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Apply this restriction on calibre startup if the current library is being used. Also applied when switching to this library. Note that this setting is per library. </string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
|
||||
</property>
|
||||
<property name="minimumContentsLength">
|
||||
<number>20</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QCheckBox" name="opt_disable_animations">
|
||||
<property name="toolTip">
|
||||
<string>Disable all animations. Useful if you have a slow/old computer.</string>
|
||||
@ -580,14 +609,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="QCheckBox" name="opt_show_donate_button">
|
||||
<property name="text">
|
||||
<string>Show &donate button (restart)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>&Toolbar</string>
|
||||
@ -1040,6 +1069,26 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_164">
|
||||
<property name="text">
|
||||
<string>Restriction (saved search) to apply:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QComboBox" name="opt_cs_restriction">
|
||||
<property name="toolTip">
|
||||
<string>This restriction (based on a saved search) will restrict the books the content server makes available to those matching the search. This setting is per library (i.e. you can have a different restriction per library).</string>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy">
|
||||
<enum>QComboBox::AdjustToMinimumContentsLengthWithIcon</enum>
|
||||
</property>
|
||||
<property name="minimumContentsLength">
|
||||
<number>20</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -3,14 +3,101 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
'''Dialog to edit metadata in bulk'''
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, QObject
|
||||
from PyQt4.QtGui import QDialog, QGridLayout
|
||||
from threading import Thread
|
||||
|
||||
from PyQt4.Qt import QDialog, QGridLayout
|
||||
|
||||
from calibre.gui2.dialogs.metadata_bulk_ui import Ui_MetadataBulkDialog
|
||||
from calibre.gui2.dialogs.tag_editor import TagEditor
|
||||
from calibre.ebooks.metadata import string_to_authors, \
|
||||
authors_to_string
|
||||
from calibre.gui2.custom_column_widgets import populate_metadata_page
|
||||
from calibre.gui2.dialogs.progress import BlockingBusy
|
||||
from calibre.gui2 import error_dialog, Dispatcher
|
||||
|
||||
class Worker(Thread):
|
||||
|
||||
def __init__(self, args, db, ids, cc_widgets, callback):
|
||||
Thread.__init__(self)
|
||||
self.args = args
|
||||
self.db = db
|
||||
self.ids = ids
|
||||
self.error = None
|
||||
self.callback = callback
|
||||
self.cc_widgets = cc_widgets
|
||||
|
||||
def doit(self):
|
||||
remove, add, au, aus, do_aus, rating, pub, do_series, \
|
||||
do_autonumber, do_remove_format, remove_format, do_swap_ta, \
|
||||
do_remove_conv, do_auto_author, series = self.args
|
||||
|
||||
# first loop: do author and title. These will commit at the end of each
|
||||
# operation, because each operation modifies the file system. We want to
|
||||
# try hard to keep the DB and the file system in sync, even in the face
|
||||
# of exceptions or forced exits.
|
||||
for id in self.ids:
|
||||
if do_swap_ta:
|
||||
title = self.db.title(id, index_is_id=True)
|
||||
aum = self.db.authors(id, index_is_id=True)
|
||||
if aum:
|
||||
aum = [a.strip().replace('|', ',') for a in aum.split(',')]
|
||||
new_title = authors_to_string(aum)
|
||||
self.db.set_title(id, new_title, notify=False)
|
||||
if title:
|
||||
new_authors = string_to_authors(title)
|
||||
self.db.set_authors(id, new_authors, notify=False)
|
||||
|
||||
if au:
|
||||
self.db.set_authors(id, string_to_authors(au), notify=False)
|
||||
|
||||
# All of these just affect the DB, so we can tolerate a total rollback
|
||||
for id in self.ids:
|
||||
if do_auto_author:
|
||||
x = self.db.author_sort_from_book(id, index_is_id=True)
|
||||
if x:
|
||||
self.db.set_author_sort(id, x, notify=False, commit=False)
|
||||
|
||||
if aus and do_aus:
|
||||
self.db.set_author_sort(id, aus, notify=False, commit=False)
|
||||
|
||||
if rating != -1:
|
||||
self.db.set_rating(id, 2*rating, notify=False, commit=False)
|
||||
|
||||
if pub:
|
||||
self.db.set_publisher(id, pub, notify=False, commit=False)
|
||||
|
||||
if do_series:
|
||||
next = self.db.get_next_series_num_for(series)
|
||||
self.db.set_series(id, series, notify=False, commit=False)
|
||||
num = next if do_autonumber and series else 1.0
|
||||
self.db.set_series_index(id, num, notify=False, commit=False)
|
||||
|
||||
if do_remove_format:
|
||||
self.db.remove_format(id, remove_format, index_is_id=True, notify=False, commit=False)
|
||||
|
||||
if do_remove_conv:
|
||||
self.db.delete_conversion_options(id, 'PIPE', commit=False)
|
||||
self.db.commit()
|
||||
|
||||
for w in self.cc_widgets:
|
||||
w.commit(self.ids)
|
||||
self.db.bulk_modify_tags(self.ids, add=add, remove=remove,
|
||||
notify=False)
|
||||
self.db.clean()
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.doit()
|
||||
except Exception, err:
|
||||
import traceback
|
||||
try:
|
||||
err = unicode(err)
|
||||
except:
|
||||
err = repr(err)
|
||||
self.error = (err, traceback.format_exc())
|
||||
|
||||
self.callback()
|
||||
|
||||
|
||||
class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
|
||||
@ -25,10 +112,10 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
len(rows))
|
||||
self.write_series = False
|
||||
self.changed = False
|
||||
QObject.connect(self.button_box, SIGNAL("accepted()"), self.sync)
|
||||
|
||||
self.tags.update_tags_cache(self.db.all_tags())
|
||||
self.remove_tags.update_tags_cache(self.db.all_tags())
|
||||
all_tags = self.db.all_tags()
|
||||
self.tags.update_tags_cache(all_tags)
|
||||
self.remove_tags.update_tags_cache(all_tags)
|
||||
|
||||
self.initialize_combos()
|
||||
|
||||
@ -37,9 +124,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
|
||||
self.remove_format.setCurrentIndex(-1)
|
||||
|
||||
QObject.connect(self.series, SIGNAL('currentIndexChanged(int)'), self.series_changed)
|
||||
QObject.connect(self.series, SIGNAL('editTextChanged(QString)'), self.series_changed)
|
||||
QObject.connect(self.tag_editor_button, SIGNAL('clicked()'), self.tag_editor)
|
||||
self.series.currentIndexChanged[int].connect(self.series_changed)
|
||||
self.series.editTextChanged.connect(self.series_changed)
|
||||
self.tag_editor_button.clicked.connect(self.tag_editor)
|
||||
if len(db.custom_column_label_map) == 0:
|
||||
self.central_widget.tabBar().setVisible(False)
|
||||
else:
|
||||
@ -93,7 +180,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
self.publisher.addItem(name)
|
||||
self.publisher.setEditText('')
|
||||
|
||||
def tag_editor(self):
|
||||
def tag_editor(self, *args):
|
||||
d = TagEditor(self, self.db, None)
|
||||
d.exec_()
|
||||
if d.result() == QDialog.Accepted:
|
||||
@ -102,61 +189,51 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
self.tags.update_tags_cache(self.db.all_tags())
|
||||
self.remove_tags.update_tags_cache(self.db.all_tags())
|
||||
|
||||
def sync(self):
|
||||
for id in self.ids:
|
||||
au = unicode(self.authors.text())
|
||||
if au:
|
||||
au = string_to_authors(au)
|
||||
self.db.set_authors(id, au, notify=False)
|
||||
if self.auto_author_sort.isChecked():
|
||||
x = self.db.author_sort_from_book(id, index_is_id=True)
|
||||
if x:
|
||||
self.db.set_author_sort(id, x, notify=False)
|
||||
aus = unicode(self.author_sort.text())
|
||||
if aus and self.author_sort.isEnabled():
|
||||
self.db.set_author_sort(id, aus, notify=False)
|
||||
if self.rating.value() != -1:
|
||||
self.db.set_rating(id, 2*self.rating.value(), notify=False)
|
||||
pub = unicode(self.publisher.text())
|
||||
if pub:
|
||||
self.db.set_publisher(id, pub, notify=False)
|
||||
remove_tags = unicode(self.remove_tags.text()).strip()
|
||||
if remove_tags:
|
||||
remove_tags = [i.strip() for i in remove_tags.split(',')]
|
||||
self.db.unapply_tags(id, remove_tags, notify=False)
|
||||
tags = unicode(self.tags.text()).strip()
|
||||
if tags:
|
||||
tags = map(lambda x: x.strip(), tags.split(','))
|
||||
self.db.set_tags(id, tags, append=True, notify=False)
|
||||
if self.write_series:
|
||||
series = unicode(self.series.currentText()).strip()
|
||||
next = self.db.get_next_series_num_for(series)
|
||||
self.db.set_series(id, series, notify=False)
|
||||
num = next if self.autonumber_series.isChecked() and series else 1.0
|
||||
self.db.set_series_index(id, num, notify=False)
|
||||
def accept(self):
|
||||
if len(self.ids) < 1:
|
||||
return QDialog.accept(self)
|
||||
|
||||
if self.remove_format.currentIndex() > -1:
|
||||
self.db.remove_format(id, unicode(self.remove_format.currentText()), index_is_id=True, notify=False)
|
||||
|
||||
if self.swap_title_and_author.isChecked():
|
||||
title = self.db.title(id, index_is_id=True)
|
||||
aum = self.db.authors(id, index_is_id=True)
|
||||
if aum:
|
||||
aum = [a.strip().replace('|', ',') for a in aum.split(',')]
|
||||
new_title = authors_to_string(aum)
|
||||
self.db.set_title(id, new_title, notify=False)
|
||||
if title:
|
||||
new_authors = string_to_authors(title)
|
||||
self.db.set_authors(id, new_authors, notify=False)
|
||||
|
||||
if self.remove_conversion_settings.isChecked():
|
||||
self.db.delete_conversion_options(id, 'PIPE')
|
||||
|
||||
self.changed = True
|
||||
self.changed = bool(self.ids)
|
||||
# Cache values from GUI so that Qt widgets are not used in
|
||||
# non GUI thread
|
||||
for w in getattr(self, 'custom_column_widgets', []):
|
||||
w.commit(self.ids)
|
||||
w.gui_val
|
||||
|
||||
remove = unicode(self.remove_tags.text()).strip().split(',')
|
||||
add = unicode(self.tags.text()).strip().split(',')
|
||||
au = unicode(self.authors.text())
|
||||
aus = unicode(self.author_sort.text())
|
||||
do_aus = self.author_sort.isEnabled()
|
||||
rating = self.rating.value()
|
||||
pub = unicode(self.publisher.text())
|
||||
do_series = self.write_series
|
||||
series = unicode(self.series.currentText()).strip()
|
||||
do_autonumber = self.autonumber_series.isChecked()
|
||||
do_remove_format = self.remove_format.currentIndex() > -1
|
||||
remove_format = unicode(self.remove_format.currentText())
|
||||
do_swap_ta = self.swap_title_and_author.isChecked()
|
||||
do_remove_conv = self.remove_conversion_settings.isChecked()
|
||||
do_auto_author = self.auto_author_sort.isChecked()
|
||||
|
||||
args = (remove, add, au, aus, do_aus, rating, pub, do_series,
|
||||
do_autonumber, do_remove_format, remove_format, do_swap_ta,
|
||||
do_remove_conv, do_auto_author, series)
|
||||
|
||||
bb = BlockingBusy(_('Applying changes to %d books. This may take a while.')
|
||||
%len(self.ids), parent=self)
|
||||
self.worker = Worker(args, self.db, self.ids,
|
||||
getattr(self, 'custom_column_widgets', []),
|
||||
Dispatcher(bb.accept, parent=bb))
|
||||
self.worker.start()
|
||||
bb.exec_()
|
||||
|
||||
if self.worker.error is not None:
|
||||
return error_dialog(self, _('Failed'),
|
||||
self.worker.error[0], det_msg=self.worker.error[1],
|
||||
show=True)
|
||||
return QDialog.accept(self)
|
||||
|
||||
|
||||
def series_changed(self):
|
||||
def series_changed(self, *args):
|
||||
self.write_series = True
|
||||
|
||||
|
@ -668,9 +668,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
self.tags.setText(', '.join(book.tags))
|
||||
if book.series is not None:
|
||||
if self.series.text() is None or self.series.text() == '':
|
||||
self.series.setText(book.series)
|
||||
if book.series_index is not None:
|
||||
self.series_index.setValue(book.series_index)
|
||||
self.series.setText(book.series)
|
||||
if book.series_index is not None:
|
||||
self.series_index.setValue(book.series_index)
|
||||
else:
|
||||
error_dialog(self, _('Cannot fetch metadata'),
|
||||
_('You must specify at least one of ISBN, Title, '
|
||||
@ -719,24 +719,31 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
self.db.set_authors(self.id, string_to_authors(au), notify=False)
|
||||
aus = unicode(self.author_sort.text())
|
||||
if aus:
|
||||
self.db.set_author_sort(self.id, aus, notify=False)
|
||||
self.db.set_author_sort(self.id, aus, notify=False, commit=False)
|
||||
self.db.set_isbn(self.id,
|
||||
re.sub(r'[^0-9a-zA-Z]', '', unicode(self.isbn.text())), notify=False)
|
||||
self.db.set_rating(self.id, 2*self.rating.value(), notify=False)
|
||||
self.db.set_publisher(self.id, unicode(self.publisher.currentText()), notify=False)
|
||||
re.sub(r'[^0-9a-zA-Z]', '', unicode(self.isbn.text())),
|
||||
notify=False, commit=False)
|
||||
self.db.set_rating(self.id, 2*self.rating.value(), notify=False,
|
||||
commit=False)
|
||||
self.db.set_publisher(self.id, unicode(self.publisher.currentText()),
|
||||
notify=False, commit=False)
|
||||
self.db.set_tags(self.id, [x.strip() for x in
|
||||
unicode(self.tags.text()).split(',')], notify=False)
|
||||
unicode(self.tags.text()).split(',')], notify=False, commit=False)
|
||||
self.db.set_series(self.id,
|
||||
unicode(self.series.currentText()).strip(), notify=False)
|
||||
self.db.set_series_index(self.id, self.series_index.value(), notify=False)
|
||||
self.db.set_comment(self.id, unicode(self.comments.toPlainText()), notify=False)
|
||||
unicode(self.series.currentText()).strip(), notify=False,
|
||||
commit=False)
|
||||
self.db.set_series_index(self.id, self.series_index.value(),
|
||||
notify=False, commit=False)
|
||||
self.db.set_comment(self.id, unicode(self.comments.toPlainText()),
|
||||
notify=False, commit=False)
|
||||
d = self.pubdate.date()
|
||||
d = qt_to_dt(d)
|
||||
self.db.set_pubdate(self.id, d, notify=False)
|
||||
self.db.set_pubdate(self.id, d, notify=False, commit=False)
|
||||
d = self.date.date()
|
||||
d = qt_to_dt(d)
|
||||
if d.date() != self.orig_timestamp.date():
|
||||
self.db.set_timestamp(self.id, d, notify=False)
|
||||
self.db.set_timestamp(self.id, d, notify=False, commit=False)
|
||||
self.db.commit()
|
||||
|
||||
if self.cover_changed:
|
||||
if self.cover_data is not None:
|
||||
@ -745,6 +752,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
self.db.remove_cover(self.id)
|
||||
for w in getattr(self, 'custom_column_widgets', []):
|
||||
w.commit(self.id)
|
||||
self.db.commit()
|
||||
except IOError, err:
|
||||
if err.errno == 13: # Permission denied
|
||||
fname = err.filename if err.filename else 'file'
|
||||
@ -769,9 +777,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog):
|
||||
wg = dynamic.get('metasingle_window_geometry', None)
|
||||
ss = dynamic.get('metasingle_splitter_state', None)
|
||||
if wg is not None:
|
||||
self.restoreGeometry(wg)
|
||||
self.restoreGeometry(wg)
|
||||
if ss is not None:
|
||||
self.splitter.restoreState(ss)
|
||||
self.splitter.restoreState(ss)
|
||||
|
||||
def save_state(self):
|
||||
dynamic.set('metasingle_window_geometry', bytes(self.saveGeometry()))
|
||||
|
@ -5,15 +5,17 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
''''''
|
||||
|
||||
from PyQt4.Qt import QDialog, pyqtSignal, Qt
|
||||
from PyQt4.Qt import QDialog, pyqtSignal, Qt, QVBoxLayout, QLabel, QFont
|
||||
|
||||
from calibre.gui2.dialogs.progress_ui import Ui_Dialog
|
||||
from calibre.gui2.progress_indicator import ProgressIndicator
|
||||
|
||||
class ProgressDialog(QDialog, Ui_Dialog):
|
||||
|
||||
canceled_signal = pyqtSignal()
|
||||
|
||||
def __init__(self, title, msg='', min=0, max=99, parent=None):
|
||||
def __init__(self, title, msg='', min=0, max=99, parent=None,
|
||||
cancelable=True):
|
||||
QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
self.setWindowTitle(title)
|
||||
@ -26,6 +28,9 @@ class ProgressDialog(QDialog, Ui_Dialog):
|
||||
self.canceled = False
|
||||
|
||||
self.button_box.rejected.connect(self._canceled)
|
||||
if not cancelable:
|
||||
self.button_box.setVisible(False)
|
||||
self.cancelable = cancelable
|
||||
|
||||
def set_msg(self, msg=''):
|
||||
self.message.setText(msg)
|
||||
@ -54,8 +59,48 @@ class ProgressDialog(QDialog, Ui_Dialog):
|
||||
self.title.setText(_('Aborting...'))
|
||||
self.canceled_signal.emit()
|
||||
|
||||
def reject(self):
|
||||
if not self.cancelable:
|
||||
return
|
||||
QDialog.reject(self)
|
||||
|
||||
def keyPressEvent(self, ev):
|
||||
if ev.key() == Qt.Key_Escape:
|
||||
self._canceled()
|
||||
if self.cancelable:
|
||||
self._canceled()
|
||||
else:
|
||||
QDialog.keyPressEvent(self, ev)
|
||||
|
||||
class BlockingBusy(QDialog):
|
||||
|
||||
def __init__(self, msg, parent=None, window_title=_('Working')):
|
||||
QDialog.__init__(self, parent)
|
||||
|
||||
self._layout = QVBoxLayout()
|
||||
self.setLayout(self._layout)
|
||||
self.msg = QLabel(msg)
|
||||
#self.msg.setWordWrap(True)
|
||||
self.font = QFont()
|
||||
self.font.setPointSize(self.font.pointSize() + 8)
|
||||
self.msg.setFont(self.font)
|
||||
self.pi = ProgressIndicator(self)
|
||||
self.pi.setDisplaySize(100)
|
||||
self._layout.addWidget(self.pi, 0, Qt.AlignHCenter)
|
||||
self._layout.addSpacing(15)
|
||||
self._layout.addWidget(self.msg, 0, Qt.AlignHCenter)
|
||||
self.start()
|
||||
self.setWindowTitle(window_title)
|
||||
self.resize(self.sizeHint())
|
||||
|
||||
def start(self):
|
||||
self.pi.startAnimation()
|
||||
|
||||
def stop(self):
|
||||
self.pi.stopAnimation()
|
||||
|
||||
def accept(self):
|
||||
self.stop()
|
||||
return QDialog.accept(self)
|
||||
|
||||
def reject(self):
|
||||
pass # Cannot cancel this dialog
|
||||
|
@ -14,7 +14,8 @@ from Queue import Empty, Queue
|
||||
from PyQt4.Qt import QAbstractTableModel, QVariant, QModelIndex, Qt, \
|
||||
QTimer, pyqtSignal, QIcon, QDialog, QAbstractItemDelegate, QApplication, \
|
||||
QSize, QStyleOptionProgressBarV2, QString, QStyle, QToolTip, QFrame, \
|
||||
QHBoxLayout, QVBoxLayout, QSizePolicy, QLabel, QCoreApplication
|
||||
QHBoxLayout, QVBoxLayout, QSizePolicy, QLabel, QCoreApplication, QAction, \
|
||||
QByteArray
|
||||
|
||||
from calibre.utils.ipc.server import Server
|
||||
from calibre.utils.ipc.job import ParallelJob
|
||||
@ -175,6 +176,7 @@ class JobManager(QAbstractTableModel):
|
||||
self.jobs.append(job)
|
||||
self.jobs.sort()
|
||||
self.job_added.emit(len(self.unfinished_jobs()))
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def done_jobs(self):
|
||||
return [j for j in self.jobs if j.is_finished]
|
||||
@ -198,8 +200,9 @@ class JobManager(QAbstractTableModel):
|
||||
return False
|
||||
|
||||
def run_job(self, done, name, args=[], kwargs={},
|
||||
description=''):
|
||||
description='', core_usage=1):
|
||||
job = ParallelJob(name, description, done, args=args, kwargs=kwargs)
|
||||
job.core_usage = core_usage
|
||||
self.add_job(job)
|
||||
self.server.add_job(job)
|
||||
return job
|
||||
@ -279,6 +282,7 @@ class JobsButton(QFrame):
|
||||
self.pi = ProgressIndicator(self, size)
|
||||
self._jobs = QLabel('<b>'+_('Jobs:')+' 0')
|
||||
self._jobs.mouseReleaseEvent = self.mouseReleaseEvent
|
||||
self.shortcut = _('Shift+Alt+J')
|
||||
|
||||
if horizontal:
|
||||
self.setLayout(QHBoxLayout())
|
||||
@ -295,15 +299,24 @@ class JobsButton(QFrame):
|
||||
self.layout().setMargin(0)
|
||||
self._jobs.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
|
||||
self.setCursor(Qt.PointingHandCursor)
|
||||
self.setToolTip(_('Click to see list of active jobs.'))
|
||||
b = _('Click to see list of jobs')
|
||||
self.setToolTip(b + u' (%s)'%self.shortcut)
|
||||
self.action_toggle = QAction(b, parent)
|
||||
parent.addAction(self.action_toggle)
|
||||
self.action_toggle.setShortcut(self.shortcut)
|
||||
self.action_toggle.triggered.connect(self.toggle)
|
||||
|
||||
def initialize(self, jobs_dialog, job_manager):
|
||||
self.jobs_dialog = jobs_dialog
|
||||
job_manager.job_added.connect(self.job_added)
|
||||
job_manager.job_done.connect(self.job_done)
|
||||
self.jobs_dialog.addAction(self.action_toggle)
|
||||
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
self.toggle()
|
||||
|
||||
def toggle(self, *args):
|
||||
if self.jobs_dialog.isVisible():
|
||||
self.jobs_dialog.hide()
|
||||
else:
|
||||
@ -363,12 +376,26 @@ class JobsDialog(QDialog, Ui_JobsDialog):
|
||||
self.jobs_view.setItemDelegateForColumn(2, self.pb_delegate)
|
||||
self.jobs_view.doubleClicked.connect(self.show_job_details)
|
||||
self.jobs_view.horizontalHeader().setMovable(True)
|
||||
state = gprefs.get('jobs view column layout', None)
|
||||
if state is not None:
|
||||
try:
|
||||
self.jobs_view.horizontalHeader().restoreState(bytes(state))
|
||||
except:
|
||||
pass
|
||||
self.restore_state()
|
||||
|
||||
def restore_state(self):
|
||||
try:
|
||||
geom = gprefs.get('jobs_dialog_geometry', bytearray(''))
|
||||
self.restoreGeometry(QByteArray(geom))
|
||||
state = gprefs.get('jobs view column layout', bytearray(''))
|
||||
self.jobs_view.horizontalHeader().restoreState(QByteArray(state))
|
||||
except:
|
||||
pass
|
||||
|
||||
def save_state(self):
|
||||
try:
|
||||
state = bytearray(self.jobs_view.horizontalHeader().saveState())
|
||||
gprefs['jobs view column layout'] = state
|
||||
geom = bytearray(self.saveGeometry())
|
||||
gprefs['jobs_dialog_geometry'] = geom
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def show_job_details(self, index):
|
||||
row = index.row()
|
||||
@ -392,9 +419,13 @@ class JobsDialog(QDialog, Ui_JobsDialog):
|
||||
self.model.kill_all_jobs()
|
||||
|
||||
def closeEvent(self, e):
|
||||
try:
|
||||
state = bytearray(self.jobs_view.horizontalHeader().saveState())
|
||||
gprefs['jobs view column layout'] = state
|
||||
except:
|
||||
pass
|
||||
e.accept()
|
||||
self.save_state()
|
||||
return QDialog.closeEvent(self, e)
|
||||
|
||||
def show(self, *args):
|
||||
self.restore_state()
|
||||
return QDialog.show(self, *args)
|
||||
|
||||
def hide(self, *args):
|
||||
self.save_state()
|
||||
return QDialog.hide(self, *args)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user