Source code for gui.toolbar
#
##
##  SPDX-FileCopyrightText: © 2007-2022 Benedict Verhegghe <bverheg@gmail.com>
##  SPDX-License-Identifier: GPL-3.0-or-later
##
##  This file is part of pyFormex 3.1  (Sat May 21 14:49:50 CEST 2022)
##  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/.
##
"""Toolbars for the pyFormex GUI.
This module defines the functions for creating the pyFormex window toolbars.
"""
import pyformex as pf
from pyformex import utils
from pyformex.gui import QtGui, QPixmap
from pyformex.gui import widgets
################### General Button Functions ###########
[docs]def addButton(toolbar, tooltip, icon, func, repeat=False, toggle=False, checked=False, icon0=None, enabled=True):
    """Add a button to a toolbar.
    - `toolbar`: the toolbar where the button will be added
    - `tooltip`: the text to appear as tooltip
    - `icon`: name of the icon to be displayed on the button,
    - `func`: function to be called when the button is pressed,
    - `repeat`: if True, the `func` will repeatedly be called if button is
      held down.
    - `toggle`: if True, the button is a toggle and stays in depressed state
      until pressed again.
    - `checked`: initial state for a toggle buton.
    - `icon1`: for a toggle button, icon to display when button is not checked.
    """
    iconset = QtGui.QIcon()
    icon_on = QPixmap(utils.findIcon(icon))
    iconset.addPixmap(icon_on, QtGui.QIcon.Normal, QtGui.QIcon.On)
    if toggle and icon0:
        icon_off = QPixmap(utils.findIcon(icon0))
        iconset.addPixmap(icon_off, QtGui.QIcon.Normal, QtGui.QIcon.Off)
    a = toolbar.addAction(iconset, tooltip, func)
    a.setEnabled(enabled)
    b = toolbar.widgetForAction(a)
    if repeat:
        b.setAutoRepeat(True)
        b.setAutoRepeatDelay(500)
        b.clicked.connect(a.trigger)
    if toggle:
        b.setCheckable(True)
        b.clicked.connect(b.toggle)
        b.setChecked(checked)
    b.setToolTip(tooltip)
    return b
[docs]def removeButton(toolbar, button):
    """Remove a button from a toolbar."""
    toolbar.removeAction(button)
################### Script action toolbar ###########
[docs]def addActionButtons(toolbar):
    """Add the script action buttons to the toolbar."""
    from pyformex.gui import draw
    from pyformex.gui.menus import File
    action = {}
    avail_buttons = [
        ("Play", "next", draw.play, False),
        ("ReRun", "rerun", draw.replay, False),
        ## ( "Step", "nextstop", draw.step, False ),
        ("Continue", "ff", draw.fforward, False),
        ("Stop", "stop", draw.raiseExit, False),
        ("Edit", "pencil", File.editApp, False),
        ("Info", "info", draw.showDoc, False),
        ]
    # Filter configured buttons
    show_buttons = pf.cfg['gui/actionbuttons']
    show_buttons = [b for b in avail_buttons if b[0].lower() in show_buttons]
    for name, icon, func, enabled in show_buttons:
        action[name] = addButton(toolbar, '', icon, func, enabled=enabled)
    return action
################# Camera action toolbar ###############
[docs]def addCameraButtons(toolbar):
    """Add the camera buttons to a toolbar."""
    # The buttons have the following fields:
    #  0 : tooltip
    #  1 : icon
    #  2 : function
    # optional:
    #  3 : REPEAT  (default True)
    from pyformex.gui.menus import Camera
    buttons = [["Rotate left", "rotleft", Camera.rotLeft],
                ["Rotate right", "rotright", Camera.rotRight],
                ["Rotate up", "rotup", Camera.rotUp],
                ["Rotate down", "rotdown", Camera.rotDown],
                ["Twist left", "twistleft", Camera.twistLeft],
                ["Twist right", "twistright", Camera.twistRight],
                ["Translate left", "left", Camera.panLeft],
                ["Translate right", "right", Camera.panRight],
                ["Translate down", "down", Camera.panDown],
                ["Translate up", "up", Camera.panUp],
                ["Zoom Out", "zoomout", Camera.dollyOut],
                ["Zoom In", "zoomin", Camera.dollyIn],
                ["Zoom All", "zoomall", Camera.zoomAll, False],
                ["Zoom Rectangle", "zoomrect", Camera.zoomRectangle, False],
                ]
    for but in buttons:
        icon = widgets.pyformexIcon(but[1])
        a = toolbar.addAction(icon, but[0], but[2])
        b = toolbar.children()[-1]  # Get the QToolButton for the last action
        if len(but) < 4 or but[3]:
            b.setAutoRepeat(True)
            b.setAutoRepeatDelay(500)
            # This causes a double action on the button click
            # only use it if we can detach the button press from trigger
            #b.released.connect(a.trigger)
        if len(but) >= 5:
            # This is currently not used
            b.setCheckable(but[4])
            b.released.connect(a.toggle)
        b.setToolTip(but[0])
#######################################################################
# Viewport Toggle buttons #
###########################
class Toggle(object):
    def __init__(self, state=False):
        self.state = state
    def toggle(self, onoff=None):
        if onoff is None:
            onoff = not self.state
        self.state = onoff
class ViewportToggleButton(object):
    def __init__(self, toolbar, tooltip, icon, func, attr, checked=False, icon0=None):
        self.button = addButton(toolbar, tooltip, icon, func, toggle=True, icon0=icon0)
        self.attr = attr
    def updateButton(self):
        """Update the button to current viewport state."""
        #print "UPDATE TOGGLE %s" % attr
        vp = pf.GUI.viewports.current
        if vp == pf.canvas:
            self.setChecked(vp.settings[self.attr])
        pf.GUI.processEvents()
    def toggle(self, attr, state=None):
        """Update the corresponding viewport attribute.
        This does not update the button state.
        """
        vp = pf.GUI.viewports.current
        vp.setToggle(attr, state)
        vp.update()
        pf.GUI.processEvents()
[docs]def toggleButton(attr, state=None):
    """Update the corresponding viewport attribute.
    This does not update the button state.
    """
    vp = pf.GUI.viewports.current
    vp.setToggle(attr, state)
    vp.update()
    pf.GUI.processEvents()
[docs]def updateButton(button, attr):
    """Update the button to correct state."""
    if button:
        vp = pf.GUI.viewports.current
        button.setChecked(vp.settings[attr])
        pf.GUI.processEvents()
def updateViewportButtons(vp):
    ## if vp != pf.GUI.viewports.current:
    ##     print "viewport %s is not current" % pf.GUI.viewports.viewIndex(vp)
    if vp.focus:
        #print "viewport %s has focus" % pf.GUI.viewports.viewIndex(vp)
        updateWireButton()
        updateTransparencyButton()
        updateLightButton()
        updateNormalsButton()
#
# BEWARE: The toggle functions are currently not passed the state!
#         It should be got from
#
################# Wire Button ###############
wire_button = None  # the toggle wire button
def addWireButton(toolbar):
    global wire_button
    wire_button = addButton(toolbar, 'Toggle Wire Mode',
                            'wireall', toggleWire, icon0='wirenone',
                            toggle=True)
def toggleWire(state=None):
    vp = pf.GUI.viewports.current
    state = wire_button.isChecked()
    # TODO:
    # WELL, it looks like we get here BEFORE the button is updated,
    # so we need to change the state.
    # WE should really connect this to a signal that is raised
    # AFTER the button state is update
    state = not state
    vp.setWireMode(state)
    vp.update()
    pf.GUI.processEvents()
[docs]def updateWireButton():
    """Update the wire button to correct state."""
    if wire_button:
        vp = pf.GUI.viewports.current
        wire_button.setChecked(vp.settings['wiremode'] > 0)
        pf.GUI.processEvents()
################# Transparency Button ###############
transparency_button = None  # the toggle transparency button
def addTransparencyButton(toolbar):
    global transparency_button
    transparency_button = addButton(toolbar, 'Toggle Transparent Mode',
                                    'transparent', toggleTransparency,
                                    toggle=True)
def toggleTransparency(state=None):
    toggleButton('alphablend', state)
[docs]def updateTransparencyButton():
    """Update the transparency button to correct state."""
    updateButton(transparency_button, 'alphablend')
################# Lights Button ###############
light_button = None
def addLightButton(toolbar):
    global light_button
    light_button = addButton(toolbar, 'Toggle Lights',
                             'lamp-on', toggleLight, icon0='lamp',
                             toggle=True, checked=False)
def toggleLight(state=None):
    toggleButton('lighting', state)
[docs]def updateLightButton():
    """Update the light button to correct state."""
    updateButton(light_button, 'lighting')
################# Normals Button ###############
normals_button = None
def addNormalsButton(toolbar):
    global normals_button
    normals_button = addButton(toolbar, 'Toggle Normals',
                               'normals-avg', toggleNormals, icon0='normals-ind',
                               toggle=True, checked=False)
def toggleNormals(state=None):
    toggleButton('avgnormals', state)
[docs]def updateNormalsButton(state=True):
    """Update the normals button to correct state."""
    updateButton(normals_button, 'avgnormals')
################# Perspective Button ###############
perspective_button = None  # the toggle perspective button
def togglePerspective(mode=None):  # Called by the button, not by user
    vp = pf.GUI.viewports.current
    if mode is None:
        mode = not vp.camera.perspective
    vp.camera.setPerspective(mode)
    vp.display()
    vp.update()
    pf.GUI.processEvents()
def addPerspectiveButton(toolbar):
    global perspective_button
    perspective_button = addButton(toolbar, 'Toggle Perspective/Projective Mode',
                                   'perspect', togglePerspective,
                                   toggle=True, icon0='project', checked=True)
[docs]def updatePerspectiveButton():
    """Update the normals button to correct state."""
    #updateButton(perspective_button,'avgnormals')
    vp = pf.GUI.viewports.current
    if vp == pf.canvas:
        perspective_button.setChecked(vp.camera.perspective)
    pf.GUI.processEvents()
def setPerspective():
    togglePerspective(True)
    updatePerspectiveButton()
def setProjection():
    togglePerspective(False)
    updatePerspectiveButton()
################# Timeout Button ###############
timeout_button = None  # the timeout toggle button
def toggleTimeout(onoff=None):
    if onoff is None:
        onoff = widgets.input_timeout < 0
    if onoff:
        timeout = pf.cfg['gui/timeoutvalue']
    else:
        timeout = -1
    widgets.setInputTimeout(timeout)
    onoff = widgets.input_timeout > 0
    if onoff:
        # THIS SUSPENDS ALL WAITING! WE SHOULD IMPLEMENT A TIMEOUT!
        # BY FORCING ALL INDEFINITE PAUSES TO A WAIT TIME EQUAL TO
        # WIDGET INPUT TIMEOUT
        pf.debug("FREEING the draw lock")
        pf.GUI.drawlock.free()
    else:
        pf.debug("ALLOWING the draw lock")
        pf.GUI.drawlock.allow()
    return onoff
[docs]def addTimeoutButton(toolbar):
    """Add or remove the timeout button,depending on cfg."""
    global timeout_button
    if pf.cfg['gui/timeoutbutton']:
        if timeout_button is None:
            timeout_button = addButton(toolbar, 'Toggle Timeout', 'clock', toggleTimeout, toggle=True, checked=False)
    else:
        if timeout_button is not None:
            removeButton(toolbar, timeout_button)
            timeout_button = None
[docs]def timeout(onoff=None):
    """Programmatically toggle the timeout button"""
    if timeout_button is not None:
        timeout_button.setChecked(toggleTimeout(onoff))
# End
 
  