1 #! /usr/bin/env python3
2 # -*- coding: iso-8859-1 -*-
3 # Copyright (C) 2007-2023 CEA, EDF, OPEN CASCADE
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
26 # Options of this script
27 def profileQuickStartParser() :
29 parser = argparse.ArgumentParser( usage = "usage: python app-quickstart.py [options]" )
31 parser.add_argument('-p',
33 metavar="</Path/to/the/sources/of/application>",
37 help="Where the application's sources will be generated. [Default : '.']")
39 parser.add_argument('-m',
41 metavar="<module1,module2,...>",
45 help="List of the application's modules. [Default : KERNEL,GUI]")
47 parser.add_argument('-n',
51 help="Name of the application")
53 parser.add_argument('-v',
58 help="Version of the application. [Default : 1.0]")
60 parser.add_argument('-s',
65 help="Slogan of the application.")
67 parser.add_argument('-f',
71 help="Overwrites existing sources")
77 #Create the splash screen
78 def profileGenerateSplash( resources_dir, appname, version, subtext ):
83 if isinstance(appname, bytes):
84 uname = str(appname, 'UTF-8')
87 if isinstance(version, bytes):
88 uversion = str(version, 'UTF-8')
93 fontbig = ImageFont.truetype( os.path.join( resources_dir, 'Anita semi square.ttf' ), 64)
94 fontsmall = ImageFont.truetype( os.path.join( resources_dir, 'Anita semi square.ttf' ), 20)
95 textColor = "rgb(255, 250, 250)"
96 shadowColor = "rgb(0, 0, 0)"
102 width = min( width*nbcar//12, 1024) #a little more
109 # load background image
110 f0 = os.path.join( resources_dir, "background.png" )
112 im = im.resize( ( width, height ) )
113 draw = ImageDraw.Draw(im)
115 # add the name of the application
116 iw, ih = draw.textsize(uname, font=fontbig)
117 x = (width - iw) / 2.0 # horizontal center
118 y = (height - ih) / 2.0 # vertical center
119 draw.text((x+shadowX, y+shadowY), uname, font=fontbig, fill=shadowColor)
120 draw.text((x, y), uname, font=fontbig, fill=textColor)
124 iw, ih = draw.textsize(subtext, font=fontsmall)
125 draw.text((borderX+shadowX, height+shadowY-borderY-ih),
126 subtext, font=fontsmall, fill=shadowColor)
127 draw.text((borderX, height-borderY-ih),
128 subtext, font=fontsmall, fill=textColor)
130 # add the version if any
132 iw, ih = draw.textsize(uversion, font=fontsmall)
133 draw.text((width+shadowX-borderX-iw, height+shadowY-borderY-ih),
134 uversion, font=fontsmall, fill=shadowColor)
135 draw.text((width-borderX-iw, height-borderY-ih),
136 uversion, font=fontsmall, fill=textColor)
142 #Create the application logo
143 def profileGenerateLogo( appname, font ):
147 if isinstance(appname, bytes):
148 uname = str(appname, 'UTF-8')
152 # evaluate size before deleting draw
153 im = Image.new( "RGBA", (1, 1), (0, 0, 0, 0) )
154 draw = ImageDraw.Draw( im )
156 im = Image.new( "RGBA", draw.textsize( uname, font=font ), (0, 0, 0, 0) )
157 draw = ImageDraw.Draw(im)
158 draw.text( (0+1, 0), uname, font=font, fill="rgb(0, 0, 0)" )
159 draw.text( (0, -1), uname, font=font, fill="rgb(191, 191, 191)" )
165 # Check if filename is a binary file
166 def is_binary(filename):
167 """ returns True if filename is a binary file
168 (from https://stackoverflow.com/a/7392391/2531279)
170 textchars = bytearray({7,8,9,10,12,13,27} | set(range(0x20, 0x100)) - {0x7f})
171 with open(filename, 'rb') as f:
173 return bool(s.translate(None, textchars))
176 #Replace strings in the template
177 def profileReplaceStrings( src, dst, args) :
179 shutil.copyfile(src, dst)
181 with open( dst, "w") as fout, \
182 open( src, "r") as fin:
184 if args.modules == '_NO_' and '[LIST_OF_MODULES]' in line:
186 l = line.replace( '[LIST_OF_MODULES]', args.modules )
187 l = l.replace( '[VERSION]', args.version )
188 l = l.replace( '[SLOGAN]', args.slogan )
189 l = l.replace( '[NAME_OF_APPLICATION]', args.name.upper() )
190 l = l.replace( '[Name_of_Application]', args.name )
191 l = l.replace( '(name_of_application)', args.name.lower() )
195 #Generation of a template profile sources
196 def profileGenerateSources( args ) :
198 #Set name of several directories
199 app_dir = args.prefix
200 app_resources_dir = os.path.join( app_dir, "resources" )
201 kernel_root_dir = os.environ["KERNEL_ROOT_DIR"]
202 bin_salome_dir = os.path.join( kernel_root_dir, "bin", "salome" )
203 kernel_resources_dir = os.path.join( kernel_root_dir, "share", "salome", "resources", "kernel" )
204 template_dir = os.path.join( kernel_resources_dir, "app-template" )
206 #Check if the directory of the sources already exists and delete it
207 if os.path.exists( app_dir ) :
209 print("Directory %s already exists." %app_dir)
210 print("Use option --force to overwrite it.")
213 shutil.rmtree( app_dir )
215 #Copy template directory
217 for root, dirs, files in os.walk( template_dir ) :
218 dst_dir = root.replace( template_dir, app_dir )
220 os.mkdir( os.path.join( dst_dir, d ) )
222 profileReplaceStrings( os.path.join( root, f ), os.path.join( dst_dir, f ), args)
224 #Complete source directory
225 contextFiles = [ "salomeContext.py", "salomeContextUtils.py", "parseConfigFile.py" ]
226 for f in contextFiles :
227 shutil.copy( os.path.join( bin_salome_dir, f ), os.path.join( app_dir, "src" ) )
229 #Search for python modules Image, ImageDraw and ImageFont
232 imp.find_module('Image')
233 imp.find_module('ImageDraw')
234 imp.find_module('ImageFont')
239 #Generate logo and splash
240 logo_destination = os.path.join( app_resources_dir, 'app_logo.png')
241 splash_destination = os.path.join( app_resources_dir, 'splash.png')
242 about_destination = os.path.join( app_resources_dir, 'about.png')
245 font = ImageFont.truetype( os.path.join( kernel_resources_dir, "Anita semi square.ttf" ) , 18 )
247 #Generate and save logo
248 app_logo = profileGenerateLogo( args.name, font )
249 app_logo.save( logo_destination, "PNG" )
251 #Generate and splash screen and about image
253 subtext = args.slogan
255 subtext = "Powered by SALOME"
256 im = profileGenerateSplash( kernel_resources_dir, args.name, args.version, subtext )
257 im.save( splash_destination, "PNG" )
258 im.save( about_destination, "PNG" )
260 gui_resources_dir = os.path.join( os.environ["GUI_ROOT_DIR"], "share", "salome", "resources", "gui" )
261 logo_name = os.path.join( gui_resources_dir, "icon_applogo.png" )
262 if os.path.exists( logo_name ) :
263 shutil.copy( logo_name, logo_destination )
264 about_name = os.path.join( gui_resources_dir, "icon_about.png" )
265 if os.path.exists( about_name ) :
266 shutil.copy( about_name, about_destination )
267 shutil.copy( about_name, splash_destination )
270 print("Sources of %s were generated in %s." %( args.name, app_dir ))
273 # -----------------------------------------------------------------------------
275 if __name__ == '__main__':
276 #Get optional and positional args
277 args = profileQuickStartParser().parse_args()
279 #Check name of the application
281 raise RuntimeError( "A name must be given to the application. Please use option --name." )
283 #Check if the prefix's parent is a directory
284 if not os.path.isdir( os.path.dirname( args.prefix ) ) :
285 raise RuntimeError( "%s is not a directory." % os.path.dirname( args.prefix ) )
287 #Generate sources of the profile
288 profileGenerateSources( args )