#
##
## SPDX-FileCopyrightText: © 2007-2024 Benedict Verhegghe <bverheg@gmail.com>
## SPDX-License-Identifier: GPL-3.0-or-later
##
## This file is part of pyFormex 3.5 (Thu Feb 8 19:11:13 CET 2024)
## pyFormex is a tool for generating, manipulating and transforming 3D
## geometrical models by sequences of mathematical operations.
## Home page: https://pyformex.org
## Project page: https://savannah.nongnu.org/projects/pyformex/
## Development: https://gitlab.com/bverheg/pyformex
## Distributed under the GNU General Public License version 3 or later.
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see http://www.gnu.org/licenses/.
##
"""Functions from the File menu.
"""
import pyformex as pf
from pyformex.gui import guiscript as pg
from pyformex.gui import dialogs
from pyformex.gui import image
from pyformex.gui.widgets import _I, _G
from pyformex.gui.menus.Settings import updateSettings
##################### handle script files ##########################
[docs]def openScript(fn=None, exist=False, create=False):
"""Open a pyFormex script and set it as the current script.
If no filename is specified, a file selection dialog is started to select
an existing script, or allow to create a new file if exist is False.
If the file exists and is a pyFormex script, it is set ready to execute.
If the file is empty or create is True, a default pyFormex script
template will be written to the file, overwriting the contents if the
file existed. Then, the script is loaded into the editor.
"""
if fn is not None:
fn = pf.Path(fn)
else:
mode = 'exist' if exist else 'file'
fn = pg.askFilename(pf.cfg['curfile'], 'pyformex', mode=mode)
if fn:
if create and fn.exists() and fn.size > 0:
if not pg.ack(f"The file {fn} exists and is not empty.\n "
f"Are you sure you want to overwrite it?"):
return None
create = create or not fn.exists() or fn.size == 0
if create:
template = pf.cfg['scripttemplate']
if template.exists():
template.copy(fn)
updateSettings({'workdir': fn.parent}, save=True)
pf.chdir(fn)
pf.GUI.setcurfile(fn)
pf.GUI.scripthistory.add(fn)
if create:
pg.editFile(fn)
return fn
[docs]def editApp(appname=None):
"""Edit an application source file.
If no appname is specified, the current application is used.
This loads the application module, gets its file name, and if
it is a source file or the the corresponding source file exists,
that file is loaded into the editor.
"""
if appname is None:
appname = pf.cfg['curfile']
if pf.utils.is_script(appname):
# this is a script, not an app
fn = pf.Path(appname)
else:
from pyformex import apps
app = apps.load(appname)
if app is None:
fn = apps.findAppSource(appname)
else:
fn = apps.findAppSource(app)
if not fn.exists():
pg.showWarning(f"The file '{fn}' does not exist")
return
pg.editFile(fn)
##################### other functions ##########################
def showImage(filename=None):
pf.clear()
dia = pf.ImageLoadDialog(filename, store='showImage_data', extra=[
_I('size', (0, 0), itemtype='ivector', fields=('w', 'h'),
text='Resize (if negative or zero: unchanged)'),
_I('pos', (-1, -1), itemtype='ivector', fields=('x', 'y'),
text='Insertion position (if negative: centered'),
])
res = dia.getResults()
if res:
pf.drawImage(res.pop('filename'), **res)
def showImage3D(filename=None):
pf.clear()
dia = pf.ImageLoadDialog(filename, store='showImage3D_data', extra=[
_I('pixel cell', choices=['dot', 'quad'])
])
res = dia.getResults()
if res:
pf.drawImage3D(res['filename'], pixel=res['pixel cell'])
[docs]def saveImage(multi=False):
"""Save an image to file.
Show the Save Image dialog, with the multisave mode checked if
multi = True. Then, depending on the user's selection, either:
- save the current Canvas/Window to file
- start the multisave/autosave mode
- do nothing
"""
dia = dialogs.ImageSaveDialog(multi=multi)
res = dia.getResults()
if res:
if pf.verbosity(3):
print(res)
del res['set_size']
if pf.debugon(pf.DEBUG.IMAGE):
print(res)
# updateSettings({'workdir': pf.Path(opt.fn).parent}, save=True)
image.saveImage(**res)
[docs]def startMultiSave():
"""Start/change multisave mode"""
saveImage(True)
[docs]def stopMultiSave():
"""Stop multisave mode"""
image.saveImage()
[docs]def saveIcon():
"""Save an image as icon.
This will show the Save Image dialog, with the multisave mode off and
asking for an icon file name. Then save the current rendering to that file.
"""
## We should create a specialized input dialog, asking also for the size
fn = pg.askFilename(filter='icon')
if fn:
image.saveIcon(fn, size=32)
# viewer = None
# def showImage():
# """Display an image file"""
# from pyformex.gui.imageViewer import ImageViewer
# global viewer
# fn = pg.askFilename(filter='img')
# if fn:
# viewer = ImageViewer(pf.app, fn)
# viewer.show()
[docs]def createMovieInteractive():
"""Create a movie from a saved sequence of images.
"""
if not image.multisave:
pf.warning('You need to start multisave mode first!')
return
names = image.multisave['filename']
glob = names.glob()
res = pg.askItems([
_I('files', glob),
_I('encoder', choices=['mencoder', 'convert', 'ffmpeg']),
_G('Mencoder', [
_I('fps', 10),
_I('vbirate', 800),
]),
_G('Convert', [
_I('delay', 1),
_I('colors', 256),
]),
], enablers=[
('encoder', 'mencoder', 'Mencoder'),
('encoder', 'convert', 'Convert'),
])
if not res:
return
with pg.busyCursor():
image.createMovie(**res)
#
# TODO: this no longer works with the new webgl; should redo
#
# def exportPGF():
# """Export the current scene to PGF"""
# from pyformex.plugins.webgl import WebGL
# fn = pg.askFilename(pf.cfg['workdir'], ['pgf', 'all'])
# if fn:
# with pg.busyCursor():
# print("Exporting current scene to", fn)
# fn = fn.with_suffix('.pgf')
# W = WebGL(fn.stem)
# W.addScene()
# res = W.exportPGF(fn, sep='')
# print("Contents:", res)
last_exported_webgl = None
[docs]def exportWebGL():
"""Export the current scene to WebGL
The user is asked for a file name to store the exported model,
and after export, whether to load the model in the browser.
"""
global last_exported_webgl
last_exported_webgl = None
fn = pg.askFilename(pf.cfg['workdir'], 'html')
if fn:
last_exported_webgl = pg.exportWebGL(fn)
[docs]def multiWebGL():
"""Export the current scene as a model in a multiscene WebGL
The user is asked for a file name to store the exported model,
and after export, whether to load the model in the browser.
"""
global last_exported_webgl
last_exported_webgl = None
if pg.the_multiWebGL is None:
print("NO CURRENT EXPORT")
else:
print("CURRENTLY EXPORTED:", pg.the_multiWebGL.scenes)
if pg.the_multiWebGL is None:
fn = pg.askFilename(pf.cfg['workdir'], 'html')
if fn:
print("Exporting multiscene WebGL to", fn)
pg.multiWebGL(fn=fn)
print(pg.the_multiWebGL)
if pg.the_multiWebGL is not None:
res = pg.askItems([
_I('name', pg.the_multiWebGL.name, text='Name of the scene',
tooltip='An empty name will skip the export of the current scene'),
_I('finish', False, text='Finish the export'),
])
if res['name']:
pg.multiWebGL(res['name'])
if res['finish']:
last_exported_webgl = pg.multiWebGL()
[docs]def showWebGL():
"""Show the last WebGL model.
"""
if last_exported_webgl and last_exported_webgl.exists():
pg.showHtml(last_exported_webgl)
[docs]def recordSession():
"""Record the current pyFormex session"""
if pf.debugon(pf.DEBUG.IMAGE):
print(f"RECORDING with {pf.options.dri=}")
pf.External.require('ffmpeg')
dia = dialogs.RecordSessionDialog(pf.cfg['workdir'])
res = dia.getResults()
if res:
image.recordSession(**res)
########## The menu ##########
menu_items = [
('Exit', pg.closeGui),
('---', None),
('Change workdir', pg.askDirname),
('Open pyFormex script', openScript),
('Edit current script/app', editApp),
('Run current script/app', pg.play),
('Reload and run current app', pg.replay),
('Show Text File', pg.showFile),
('Edit Text File', pg.editFile),
('---', None),
('Save Image', saveImage),
('Start MultiSave', startMultiSave),
('Save Next Image', image.saveNext),
('Create Movie', createMovieInteractive),
('Stop MultiSave', stopMultiSave),
('Save as Icon', saveIcon),
('Show Image', [
('on canvas', showImage),
('on 3d grid', showImage3D),
]),
('Export as WebGL', exportWebGL),
('Export as multiscene WebGL', multiWebGL),
('Show exported WebGL', showWebGL),
('Show HTML', pg.showHtml),
('Record Session', recordSession),
]
# End