Source code for gui.menus.Viewport

#
##
##  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/.
##
"""Viewport Menu.

This module defines the functions of the Viewport menu.
"""
import pyformex as pf
from pyformex.gui import guiscript as pg
from pyformex.gui import widgets
from pyformex.gui import views
from pyformex.opengl import canvas
from pyformex.gui.menus.Settings import updateSettings
_I = pg._I

def setTriade():
    try:
        pos = pf.canvas.triade.pos
        size = pf.canvas.triade.size
    except Exception:
        pos = 'lb'
        size = 50
    res = pg.askItems([
        _I('triade', True),
        _I('pos', pos, choices=[
            'lt', 'lc', 'lb', 'ct', 'cc', 'cb', 'rt', 'rc', 'rb']),
        _I('size', size),
    ])
    if res:
        pg.setTriade(res['triade'], res['pos'], res['size'])


def setGrid():
    canvasgrid_settings = dict(
        [(k, pf.cfg['canvasgrid/'+k]) for k in [
            'showgrid', 'spacing', 'size', 'linewidth', 'color', 'twocolor',
            'color2', 'ontop']])
    res = pg.askItems(store=canvasgrid_settings, save=False, items=[
        _I('showgrid', True, text='Show grid'),
        _I('spacing', text='Distance between grid lines'),
        _I('size', text='Total grid size (0=canvas size)'),
        _I('linewidth', text='Line width'),
        _I('color', itemtype='color', text='Line color'),
        _I('twocolor',),
        _I('color2', itemtype='color', text='Second line color'),
        _I('ontop', ),
        _I('_save_', False, text='Save as my defaults'),
    ], enablers=[
        ('twocolor', True, 'color2')
    ])
    if res:
        if res['twocolor']:
            color = (res['color'], res['color2'])
        else:
            color = res['color']
        s = res['size'] if res['size'] >= 1 else None
        if res['showgrid']:
            pg.setGrid(on=res['showgrid'], d=res['spacing'], s=s,
                       linewidth=res['linewidth'], ontop=res['ontop'],
                       color=color, lighting=False)
        save = res['_save_']
        del res['showgrid']
        del res['_save_']
        res = pf.utils.prefixDict(res, prefix='canvasgrid/')
        res['_save_'] = save
        print(res)
        updateSettings(res)


[docs]def setBgColor(): """Interactively set the viewport background colors.""" global bgcolor_dialog from pyformex.opengl.sanitize import saneColor bgmodes = pf.canvas.settings.bgcolor_modes mode = pf.canvas.settings.bgmode print(pf.canvas.settings.bgcolor) color = saneColor(pf.canvas.settings.bgcolor).reshape(-1, 3) if color.shape[0] != 4: color = pf.at.resizeAxis(color, 4, 0) cur = pf.Path(pf.canvas.settings.bgimage) showimage = cur.exists() if not showimage: cur = pf.cfg['gui/splash'] viewer = widgets.ImageView(cur, maxheight=200) def changeColor(field): if not bgcolor_dialog.validate(): return res = bgcolor_dialog.results if res: setBackground(**res) bgcolor_dialog = widgets.Dialog(items=[ _I('mode', mode, choices=bgmodes), _I('color1', color[0], itemtype='color', func=changeColor, text='Background color 1 (Bottom Left)'), _I('color2', color[1], itemtype='color', func=changeColor, text='Background color 2 (Bottom Right)'), _I('color3', color[2], itemtype='color', func=changeColor, text='Background color 3 (Top Right)'), _I('color4', color[3], itemtype='color', func=changeColor, text='Background color 4 (Top Left'), _I('showimage', showimage, text='Show background image'), _I('image', cur, text='Background image', itemtype='filename', filter='img', mode='exist', preview=viewer), viewer, _I('_save_', False, text='Save as default'), ], caption='Background settings', enablers=[ ('mode', 'vertical', 'color4'), ('mode', 'horizontal', 'color2'), ('mode', 'full', 'color2', 'color3', 'color4'), ('showimage', True, 'image'), # ('mode', 'solid', '_save_'), ]) res = bgcolor_dialog.getResults() if res: setBackground(**res)
def setBackground(mode, color1, color2, color3, color4, showimage, image, _save_): if mode == 'solid': color = color1 elif mode == 'vertical': color = [color1, color1, color4, color4] elif mode == 'horizontal': color = [color1, color2, color2, color1] else: color = [color1, color2, color3, color4] if not showimage: image = '' pf.canvas.setBackground(color=color, image=image) pf.canvas.update() if _save_: updateSettings({ 'canvas/bgmode': mode, 'canvas/bgcolor': color, 'canvas/bgimage': image, '_save_': _save_})
[docs]def setColors(): """Set the canvas colors""" def close(): nonlocal dia dia.close() def reset(): pass def setFgColor(fgcolor, save): if save: del pf.cfg['canvas/fgcolor'] pf.prefcfg['canvas/fgcolor'] = fgcolor else: pf.cfg['canvas/fgcolor'] = fgcolor pf.canvas.setFgColor(fgcolor) def setSlColor(slcolor, save): if save: del pf.cfg['canvas/slcolor'] pf.prefcfg['canvas/slcolor'] = slcolor else: pf.cfg['canvas/slcolor'] = slcolor pf.canvas.setSlColor(slcolor) def accept(save=False): nonlocal dia res = dia.results dia.close() setFgColor(res['fgcolor'], save) setSlColor(res['slcolor'], save) pf.canvas.update() def acceptAndSave(): accept(save=True) def on_color_change(field=None): nonlocal dia if not dia.validate(): return # res = dia.results accept() dia = widgets.Dialog([ _I('fgcolor', pf.canvas.settings.fgcolor, itemtype='color', text='Foreground color', func=on_color_change, tooltip='The default color used in drawing operations'), _I('slcolor', pf.canvas.settings.slcolor, itemtype='color', text='Highlight color', func=on_color_change, tooltip='The color used for selected items in picking operations'), ], actions=[ ('Cancel', close), ('Accept for this session', accept), ('Accept and Save', acceptAndSave), ]) dia.show()
[docs]def setLineWidth(): """Change the default line width.""" print(f"LINEWIDTH {pf.canvas.settings.linewidth}") res = pg.askItems([ _I('Line Width', pf.canvas.settings.linewidth) ], caption='Choose default line width') print(res) if res: pf.canvas.setLineWidth(res['Line Width'])
[docs]def setCanvasSize(): """Save the current viewport size""" res = pg.askItems([ _I('w', pf.canvas.width()), _I('h', pf.canvas.height()) ], caption='Canvas size') if res: pg.canvasSize(res['w'], res['h'])
[docs]def canvasSettings(): """Interactively change the canvas settings. Creates a dialog to change the canvasSettings of the current or any other viewport """ dia = None def close(): dia.close() def getVp(vp): """Return the vp corresponding with a vp choice string""" if vp == 'current': vp = pf.GUI.viewports.current elif vp == 'focus': vp = pf.canvas else: vp = pf.GUI.viewports.all[int(vp)] return vp def on_change(field): if not dia.validate(): return res = {field.key: dia.results[field.key]} apply_data(res) def apply_data(res): print(f"Changing Canvas settings: {res}") pf.canvas.settings.update(res, strict=False) pf.canvas.redrawAll() pf.canvas.update() def accept(save=False): if not dia.validate(): return res = dia.results vp = getVp(res['viewport']) if pf.debugon(pf.DEBUG.CANVAS): print("Changing Canvas settings for viewport " f"{pf.GUI.viewports.viewIndex(vp)} to:") print(res) pf.canvas.settings.update(res, strict=False) pf.canvas.redrawAll() pf.canvas.update() if save: res = pf.utils.prefixDict(res, 'canvas/') print(res) res['_save_'] = save del res['canvas/viewport'] updateSettings(res) def acceptAndSave(): accept(save=True) def changeViewport(item): vp = item.value() if vp == 'current': vp = pf.GUI.viewports.current elif vp == 'focus': vp = pf.canvas else: vp = pf.GUI.viewports.all[int(vp)] dia.updateData(vp.settings) canv = pf.canvas vp = pf.GUI.viewports if pf.debugon(pf.DEBUG.CANVAS): print(f"Focus canvas: {canv}; Current canvas: {vp}") # s = canv.settings vp_choices = ['focus', 'current'] + [ str(i) for i in range(len(pf.GUI.viewports.all))] dia = widgets.Dialog( caption='Canvas settings', store=canv.settings, save=False, items=[ _I('viewport', choices=vp_choices, func=changeViewport), _I('fgcolor', itemtype='color', func=on_change), _I('slcolor', itemtype='color', func=on_change), _I('pointsize', func=on_change), _I('linewidth', func=on_change), _I('linestipple',), _I('smooth'), _I('fill'), _I('lighting'), _I('culling'), _I('alphablend'), _I('transparency', min=0.0, max=1.0), _I('avgnormals',), ], enablers=[ ('alphablend', ('transparency')), ], actions=[ ('Close', close), ('Apply and Save', acceptAndSave), ('Apply', accept), ], ) dia.show()
[docs]def viewportLayout(): """Interactively set the viewport layout. Creates a dialog that allows the user to set the viewport layout. If the user accepts valid data, the viewport layout is changed accordingly. The contents of existing viewports remaining in the new layout is retained. """ nvps = len(pf.GUI.viewports.all) directions = ['rowwise', 'columnwise'] curdir = 0 if pf.GUI.viewports.rowwise else 1 ncols = pf.GUI.viewports.ncols nrows = (nvps+ncols-1) // ncols if curdir == 1: nrows, ncols = ncols, nrows res = pg.askItems(caption='Viewport configuration', items=[ _I('nvps', nvps, text='Number of viewports'), _I('dir', directions[curdir], text='Layout direction', choices=directions), _I('ncols', ncols, text='Number of columns'), _I('nrows', nrows, text='Number of rows'), ], enablers=[ ('dir', directions[0], 'ncols'), ('dir', directions[1], 'nrows') ]) if res: nvps = res['nvps'] rowwise = directions.index(res['dir']) == 0 if rowwise: ncols = res['ncols'] nrows = None else: ncols = None nrows = res['nrows'] pf.GUI.viewports.changeLayout(nvps, ncols, nrows)
[docs]def drawOptions(d={}): """Set the Drawing options. A dictionary may be specified to override the current defaults. """ pg.setDrawOptions(d) res = pg.askItems(items=[ _I('view', choices=['None']+views.viewNames(), tooltip="Camera viewing direction"), _I('bbox', choices=['auto', 'last'], tooltip="Automatically focus/zoom on the last drawn object(s)"), _I('clear_', tooltip="Clear the canvas on each drawing action"), _I('shrink', tooltip="Shrink all elements to make their borders better visible"), _I('shrink_factor', tooltip="Shrink factor to use when shrink mode is active"), _I('wait', tooltip="Activate the draw lock, guaranteeing the rendering" "will be visible for some time ('drawdelay')"), _I('silent', tooltip="Silently ignore non-drawable objects"), ], caption="Draw options", store=pf.canvas.drawoptions, save=False) if not res: return if res['view'] == 'None': res['view'] = None pg.setDrawOptions(res)
def cameraSettings(): from pyformex.plugins import cameratools cameratools.showCameraTool() # TODO: This does not work: repait if needed # def openglSettings(): # dia = None # def apply_(): # if dia.validateData(): # canvas.glSettings(dia.results) # def close(): # dia.close() # dia = widgets.Dialog(caption='OpenGL settings', items=[ # _I('Line Smoothing', 'Off', itemtype='radio', choices=['On', 'Off']), # _I('Polygon Mode', None, itemtype='radio', choices=['Fill', 'Line']), # _I('Polygon Fill', None, itemtype='radio', # choices=['Front and Back', 'Front', 'Back']), # _I('Culling', 'Off', itemtype='radio', choices=['On', 'Off']), # # These are currently set by the render mode # # ('Shading',None,'radio',{'choices':['Smooth','Flat']}), # # ('Lighting',None,'radio',{'choices':['On','Off']}), # ], actions=[('Done', close), ('Apply', apply_)]) # dia.show() def lineSmoothOn(): canvas.glLineSmooth(True) def lineSmoothOff(): canvas.glLineSmooth(False) def singleViewport(reset=True): pg.layout(1) if reset: pg.resetAll() def clearAll(): for vp in pf.GUI.viewports.all: vp.removeAll() vp.clearCanvas() vp.update() pf.app.processEvents()
[docs]def showObjectDialog(show=True): """Show the object dialog for the current scene. """ from pyformex.opengl import objectdialog dia = objectdialog.objectDialog(pf.canvas.actors) if dia: if show: dia.show() else: print("There are no editable attributes in the current actors") return dia
########## The menu ########## menu_items = [ ('Clear', pg.clear), ('Clear All Viewports', clearAll), ('Remove Highlight', pg.removeHighlight), ('Axes Triade', setTriade), ('Canvas Grid', setGrid), ('Background Color', setBgColor), ('Viewport Colors', setColors), ('LineWidth', setLineWidth), ('Canvas Size', setCanvasSize), ('Canvas Settings', canvasSettings), ('Draw Options', drawOptions), ('Camera Settings', cameraSettings), # ('OpenGL Settings', openglSettings), ('Redraw', pg.redraw), ('Object Dialog', showObjectDialog), ('Reset viewport', pg.reset), ('Reset layout', singleViewport), ('Change viewport layout', viewportLayout), ('Add new viewport', pg.addViewport), ('Remove last viewport', pg.removeViewport), ('Save', pg.saveCanvas), ('Load', pg.loadCanvas), ] # TODO: Fix problems # Viewport->Linewidth # Viewport->Foreground Color # Viewport->Highlight Color # Viewport->Canvas Settings: change focus # Viewport->Camera Settings: multiple failures # Viewport->OpenGL Settings: multiple failures # End