mmmrqs / bl_ui_widgets

UI Widgets for Blender 2.8 and newer versions
GNU General Public License v3.0
47 stars 5 forks source link

tooltips? #3

Closed SuddenDevelopment closed 1 year ago

SuddenDevelopment commented 1 year ago

I am having a great time with this library, looking forward to contributing. I have some nice dynamic image button groups going.

I havent been able to make any tooltips work tho. Is it supposed to work in the demo? I opened it and hovered on everything not sure how it's supposed to work.

thanks

mmmrqs commented 1 year ago

I am having a great time with this library, looking forward to contributing. I have some nice dynamic image button groups going.

I havent been able to make any tooltips work tho. Is it supposed to work in the demo? I opened it and hovered on everything not sure how it's supposed to work.

thanks

Hi SuddenDevelopment. I'm glad you are having a good time with my library/kit. :-)

The Patch object, like you've already noticed, does not offer a tooltip feature and that was intentional by design because that particular type is just meant to draw rectangular "patch" areas (thus its name because I could not come with a better name LOL). I may consider adding new features to the "patch" object in the future. In fact, I've got this very sentence noted in my to-do list, see: "tooltip: should it be available for patches? (perhaps only if images are present?) -- TBD..."

Right now I am completely buried with other projects and cannot put any quality time on this. Hopefully you've got some work around using the button objects.

PS: Just saw in your profile that you enjoy practicing Brazillian JiuJitsu. Well, that is not much of my thing, although I had practiced Kung-fu some 30 years ago, but the interesting point is that I am actually a Brazilian LOL, really.

Best wishes. Thanks.

SuddenDevelopment commented 1 year ago

I switched everything over to buttons which did help with other things, and tht tooltips still dont work for me, they also dont work in the included demo to install.

I'll see if I can figure out why.

mmmrqs commented 1 year ago

Hi. I looked into this here at my side and it is working as expected, the tooltips appear after a second if you hold mouse still over the button controls. There is one thing that came to my mind, though: Are you able to see the regular tooltips for other Blender controls? Because there is a checkbox under Preferences->Interface panel that turns the tooltips feature on/off and my library also looks for that option in order to determine whether the tooltips should display or not. Please verify that yours are check marked (see image below).

tooltip1

I verified this with Blender 3.3 and it works. I cannot say anything about version 3.4 or greater because I do not have those installed yet.

Thanks.

SuddenDevelopment commented 1 year ago

yes I can see regular tooltips the preference is checked. I am on 3.4.1 however.

SuddenDevelopment commented 1 year ago

in case you notice something, here's my code. I define it, and include it last in widgets


# --- ### Header
from .bl_ui_widgets.bl_ui_drag_panel import BL_UI_Drag_Panel
from .bl_ui_widgets.bl_ui_draw_op import BL_UI_OT_draw_operator
from .bl_ui_widgets.bl_ui_tooltip import BL_UI_Tooltip
from .bl_ui_widgets.bl_ui_button import BL_UI_Button
from .bl_ui_widgets.bl_ui_label import BL_UI_Label
import os
import time
import bpy

# --- ### Helper functions

def is_quadview_region(context):
    """ Identifies whether screen is in QuadView mode and if yes returns the corresponding area and region
    """
    for area in context.screen.areas:
        if area.type == 'VIEW_3D':
            if len(area.spaces.active.region_quadviews) > 0:
                region = [
                    region for region in area.regions if region.type == 'WINDOW'][3]
                return (True, area, region)
    return (False, None, None)

class KEY_OT_draw_operator(BL_UI_OT_draw_operator):  # in: bl_ui_draw_op.py ##
    bl_idname = "object.dp_ot_draw_operator"
    bl_label = "bl ui widgets custom operator"
    bl_description = "Operator for bl ui widgets"
    bl_options = {'REGISTER'}

    # --- methods
    @classmethod
    def poll(cls, context):
        # Show this panel in View_3D only
        if context.space_data.type != 'VIEW_3D':
            return False
        # Prevents multiple instances of panel
        try:
            if context.window_manager.KEY_UI.RemoVisible and int(time.time()) - context.window_manager.KEY_UI.btnRemoTime <= 1:
                return False
        except:
            return False
        return True

    def __init__(self):

        super().__init__()

        if __package__.find(".") != -1:
            package = __package__[0:__package__.find(".")]
        else:
            package = __package__

        # Note: Leave it empty, e.g. self.valid_modes = {}, for no restrictions to be applied.
        self.valid_modes = {}

        # -----------

        # This is for displaying the widgets tooltips. Only need one instance!
        self.tooltip = BL_UI_Tooltip()
# ==========
        objButtonDefaults = {
            "bg_color": (0, 0, 0, 0),
            "text": "",
            "outline_color": (1, 1, 1, 0.4),
            "roundness": 0.4,
            "corner_radius": 10,
            "has_shadow": True,
            "rounded_corners": (0, 0, 0, 0),
            "iconFile": 'add_asset',
            "imageSize": (44, 44),
            "imagePosition": (3, 3)
        }
        # group buttons and give overrides
        self.arrButtonGroups = [
            {
                "name": 'StopMotion Keys',
                "buttons": {
                    "insert_key": {"description": "Add a single keyframe to the right of the timeline indicators playhead."},
                    "remove_key": {"description": "Remove a single keyframe to the right of the timeline indicators playhead."},
                    "blank_key": {"description": "Insert a single blank keyframe to the right of the timeline indicators playhead."},
                },
            }, {
                "name": 'Duplicate',
                "buttons":
                    {
                        "clone_key": {"description": "Duplicate the current keyframe to the right of the current active keyframe/s."},
                        "clone_unique_key": {"description": "Duplicate the current keyframe to the right of the current active keyframe/s with a unique id."},
                        "clone_object": {"description": "Duplicate the object/s and the current keyframes with a unique id."},
                    },
            },  {
                "name": 'Frame Spacing',
                "buttons": {
                    "add_space": {
                        "description": "Adds a single extra space between selected keyframes.",
                        "text": "ADD",
                        "textwo": "SPACE"
                    },
                    "remove_space": {
                        "description": "Subtracts a single between selected keyframes. A cumulative behavior till there are no more spaces between keyframes.",
                        "text": "REMOVE",
                        "textwo": "SPACE"
                    },
                    "set_space": {
                        "description": "Removes all space between selected keyframes",
                        "text": "NO",
                        "textwo": "SPACE"
                    }
                }
            }, {
                "name": 'Compose Frames',
                "buttons":
                    {
                        "separate_objects": {"description": "Separates and converts the currently active selection in edit mode to a new object."},
                        "combine_objects": {"description": "Combines the selected object with the active merging keyframe data"}
                    },
            }, {
                "name": 'Geo',
                "buttons": {
                    "copy_data": {"description": "Copies object data."},
                },
            }, {
                "name": 'Assets',
                "buttons":
                    {
                        "add_asset": {"description": "Create assets out of what is selected. Object & Edit mode and Keyframe data is supported."},
                    }
            }
        ]
        # layout settings
        intGroupSpacing = 10
        intButtonSpacing = 5
        intButtonSize = 50

        # flywheel values
        intPositionX = 0
        intPositionY = 20
        for objButtonGroup in self.arrButtonGroups:
            intPositionX += intGroupSpacing
            intButtonCount = len(objButtonGroup['buttons'])
            # create the group label
            objLabel = BL_UI_Label(
                intPositionX+intGroupSpacing, 15,
                intButtonCount*(intButtonSpacing+intButtonSize), 18)
            objLabel.style = 'TITLE'
            objLabel.size = 14
            objLabel.text = objButtonGroup["name"]
            strGroupID = objButtonGroup["name"].replace(" ", "_")
            setattr(self, strGroupID, objLabel)
            for i, strButton in enumerate(objButtonGroup['buttons']):
                # combine the defaults and overrides into one object to apply
                objButtonSettings = objButtonDefaults.copy()
                if intButtonCount > 1 and i == 0:
                    # set beginning group style
                    objButtonSettings['rounded_corners'] = (1, 1, 0, 0)
                elif intButtonCount > 1 and i == intButtonCount-1:
                    # set ending group style
                    objButtonSettings['rounded_corners'] = (0, 0, 1, 1)
                elif intButtonCount == 1:
                    # set ending group style
                    objButtonSettings['rounded_corners'] = (1, 1, 1, 1)
                for strProp in objButtonGroup['buttons'][strButton]:
                    objButtonSettings[strProp] = objButtonGroup['buttons'][strButton][strProp]
                # get the position and size
                intPositionX += intButtonSpacing
                # set up the button with defaults
                objNewBtn = BL_UI_Button(
                    intPositionX, intPositionY, intButtonSize, intButtonSize)
                intPositionX += intButtonSize
                for strProp in objButtonSettings:
                    if strProp == 'iconFile':
                        directory = os.path.dirname(os.path.realpath(__file__))
                        objNewBtn.set_image(
                            f'{directory}\\icons\\{strButton}.png')
                    elif strProp == 'imageSize':
                        objNewBtn.set_image_size(objButtonSettings[strProp])
                    elif strProp == 'imagePosition':
                        objNewBtn.set_image_position(
                            objButtonSettings[strProp])
                    else:
                        setattr(objNewBtn, strProp, objButtonSettings[strProp])
                try:
                    # see if there is a matching click operator setup
                    objNewBtn.set_mouse_up(getattr(self, f'{strButton}_click'))
                except:
                    pass
                # add the btn to "self"
                setattr(self, strButton, objNewBtn)
        intPositionX += intGroupSpacing
        # (panX, panY, panW, panH)
        self.panel = BL_UI_Drag_Panel(
            intButtonSize, intButtonSize*2, intPositionX, 80)
        # Options are: {HEADER,PANEL,SUBPANEL,TOOLTIP,NONE}
        self.panel.style = 'PANEL'
        self.panel.bg_color = (0, 0, 0, 0.5)

    def on_invoke(self, context, event):
        # Add your widgets here (TODO: perhaps a better, more automated solution?)
        # --------------------------------------------------------------------------------------------------
        widgets_panel = [self.panel]
        widgets_items = []
        for objGroup in self.arrButtonGroups:
            widgets_items.append(
                getattr(self, objGroup["name"].replace(" ", "_")))
            for strButton in objGroup['buttons']:
                widgets_items.append(getattr(self, strButton))
        widgets_items.append(self.tooltip)
        # --------------------------------------------------------------------------------------------------

        widgets = widgets_panel + widgets_items

        self.init_widgets(context, widgets, self.valid_modes)

        self.panel.add_widgets(widgets_items)

        self.panel.quadview, _, region = is_quadview_region(context)

        if self.panel.quadview and region:
            # When in QuadView mode it has to be manually repositioned otherwise may get stuck out of region space
            if __package__.find(".") != -1:
                package = __package__[0:__package__.find(".")]
            else:
                package = __package__
            if bpy.context.preferences.addons[package].preferences.RC_UI_BIND:
                # From Preferences/Interface/"Display"
                ui_scale = bpy.context.preferences.view.ui_scale
            else:
                ui_scale = 1
            over_scale = bpy.context.preferences.addons[package].preferences.RC_SCALE
            panX = int(
                (region.width - (self.panel.width * ui_scale * over_scale)) / 2.0) + 1
            panY = self.panel.height + 10 - 1  # The '10' is just a spacing from region border
            self.panel.set_location(panX, panY)
        else:
            self.panel.set_location(self.panel.x, self.panel.y)

        # This statement is necessary so that the remote panel stays open when called by Blender's <F3> menu
        bpy.context.window_manager.KEY_UI.RemoVisible = True

    # -- Helper function
    def terminate_execution(self, area, region, event):
        if self.panel.quadview and area is None:
            bpy.context.window_manager.KEY_UI.RemoVisible = False
        else:
            if not area is None:
                if area.type == 'VIEW_3D':
                    # If user switches between regular and QuadView display modes, the panel is automatically closed
                    is_quadview = (
                        len(area.spaces.active.region_quadviews) > 0)
                    if self.panel.quadview != is_quadview:
                        bpy.context.window_manager.KEY_UI.RemoVisible = False

        if event.type == 'TIMER' and bpy.context.window_manager.KEY_UI.RemoVisible:
            # Update the remote panel "clock marker". This marker is used to keep track if the remote panel is
            # actually opened and active. In the case that bpy.context.window_manager.KEY_UI.RemoVisible state gets misleading
            # the panel will be automatically closed when this clock marker has not been updated for more than 1 sec
            bpy.context.window_manager.KEY_UI.btnRemoTime = int(time.time())

        return (not bpy.context.window_manager.KEY_UI.RemoVisible)
# -------------------DEFINE OPERATORS----------------------

# removed my operator connections to save space

# -----------------------------------------
# -Register/unregister processes

def register():
    bpy.utils.register_class(KEY_OT_draw_operator)

def unregister():
    bpy.utils.unregister_class(KEY_OT_draw_operator)

if __name__ == '__main__':
    register()
mmmrqs commented 1 year ago

Hi @SuddenDevelopment, I installed version 3.4.1 and the tooltips in the demo panel are working as expected, as you can see on the screen-print below. That tooltip displayed at the bottom right corner appeared once I hovered the mouse pointer over the Slider controls. Next I will take a look at your code and see if I can run it in my Blender setup to uncover the issue. By the way, in what OS are you using it? Mine is Windows 10 and I do not know how it would behave in other OS.

tooltip2

mmmrqs commented 1 year ago

Hi again. I tried your code in my Blender installation (Windows 10) and the tooltips have worked alright. I made no changes to your code, other than renaming the main class back to DP_OT_draw_operator and pointing RemoVisible/btnRemoTime back to scene.var because I wanted to use my existing Operator to launch your floating panel and it would take more time for me to readjust my pieces to your coding, but that was all I did, and I do not believe that it had anything to do with the issue you are experiencing.

tooltip3

You meant that even with the demo panel that goes within the library kit you cannot see the tooltips, is that correct? If that is the case then there might be something else going on with your setup (perhaps some other addon conflicting with my library and capturing the events when hovering over the bl_ui_widgets panel, I really dunno).

One thing you may want to try is to test it using a clean parallel installation of Blender, with no other addons installed. Then you may be able to figure out what is impacting your panel. One other option would be to run it thru a "Debugger" and see what happens with the events that are not triggering the tooltip.

Please let me know when you have it figured out so that I can be of help to other people if they bounce on the same issue.

Thanks.