Toggling toolTips on/off for a whole application

edited April 2016 in Ruby Scripting

I have an application that uses KLayout and its own GUI made with RBA.
I have had no luck turning tool tips on/off for my application. I can certainly
do it for a single widget, but not globally. My googling for the answer lead me
to suggestions of installing an eventFilter. However, in my version of Klayout,
0.24.2, there does not appear to be any way within Ruby to call the eventFilter on,
say, a QDialog. The eventFilter method exists in RBA but required arguments require
Qt forms not available in RBA.

Does anyone have a working Ruby solution for this?

Thanks so much for your consideration,

Dave

Comments

  • edited April 2016

    Hi Dave,

    tooltips should only pop up if you design your widgets to have some. I think I don't really understand your problem. Do you want to have tool tips in general, but sometimes you don't want to see them popping up?

    In general, event filtering is possible, but it's painful if applied to the application. You can basically create a global event filter this way:

    class KeyEventHandler < RBA::QObject
    
      def eventFilter(obj, ev)
        if ev.type == RBA::QEvent::KeyPress
          puts "Key event (text=" + ev.text.to_s + ") on object of class " + obj.class.to_s
        end
        return false
      end
    
    end
    
    # the global variable will make sure the event filter survives the GC
    $ef = KeyEventHandler::new
    RBA::Application::instance.installEventFilter($ef)
    

    But there are at least three cons:

    • This filter will definitely slow down your application.
    • Event object is passed through the filter and for certain objects there is no proper Qt binding, so some warnings will appear. They don't hurt but they are an indication that something heavy is going on in the background.
    • But worst of all: once you have installed an event filter, you won't be able to use the debugger. That's because when the event filter function is executed, the debugger will take control and this will itself creates some events (specifically paint events) which itself need to be filtered and the application enters infinite recursion.

    So I can't recommend this solution. Filtering events on widgets is possible however.

    Matthias

  • edited November -1

    Thanks Matthias.

    Yes, your assumption is correct. I have tool tips for nearly
    everything in the application. Once users are comfortable with
    the application, they normally click a checkbox to turn these
    tips off - at least that's how it worked under WxWidgets. The
    state of this checkbox is maintained between runs so a user
    need only turn off tips once or may turn them back on as needed.

    It seems this is a bit of a shortcoming with Qt.

    Have you seen anyone parse the whole app widget tree of objects and
    set/reset the tooltips from a string to an empty string? Surely it is brute force, but it may work without some of your listed cons.

    I have another use for your event filter advice, so a double 'thank you' is in order.

    Dave

  • edited November -1

    Hi Dave,

    So far I have not tried to educate users by giving too many tooltips :-)

    I personally favour links inside the labels showing some help text. That is not annoying and within the help text popup you can shown a more elaborate document.

    Coming back to your tool tips: I think you are using QFormBuilder to produce the user interface, are you? Maybe you could provide two versions of the .ui file - one for the beginner with the tooltips and another one for the expert (without tooltips). You could even generate the second one from the first one automatically - for example with a XSLT stylesheet like this:

    <?xml version="1.0" encoding="iso-8859-1"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
      <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
      <!-- copy everything ... -->
      <xsl:template match="node()|@*">
        <xsl:copy>
          <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
      </xsl:template>
      <!-- ... except for "property" elements where the "name" attribute is "toolTip" -->
      <xsl:template match="property[@name='toolTip']"/>
    </xsl:stylesheet>
    

    Matthias

  • edited June 2016

    Some code that I hope will reduce my debt to others. It handles the tooltip problem.
    Dave

    # == Synopsis
    # Set up a filter class to be used app-wide to catch Qt
    # tool tip, and interrupt key events.
    # Move this into a module for your app. 
    class EventFilter < RBA::QObject
        include RBA
    
        # The keys here are used as the *sym* argument to
        # register(sym, meth).  This has will store a method
        # assocated with *sym* that will be called when an
        # associated event is seen by the app.
        TO_BE_CALLED = { :do_escape_key => nil, :do_tooltip => nil }
    
        # Simple initialization.
        def initialize
            super
        end
    
        # Given a first arg *sym* that is a key from *TO_BE_CALLED*,
        # and a method from the calling context, *method*, this
        # assocates the method with an event of a Qt app.
        def register(sym, meth)
            if TO_BE_CALLED.include? sym
                TO_BE_CALLED[sym] = meth
            else
                fail "Missing #{sym} support"
            end
        end
    
        # Processes events we care about and passes others along to Qt
        # via *super*.  The name-form of the method is dictated by Qt.
        def eventFilter(object, event)
            @event  = event
            @object = object
            call_if_wanted || super
        end
    
        private
    
        # Holds event types we want to handle.  All others are passed
        # along to Qt for processing.
        WANTED_EVENTS = {
            QEvent_Type::KeyPress => :wanted_key, 
            QEvent_Type::ToolTip  => :wanted_tooltip
        }
    
        # This checks to see if an event is of a type we want to process.
        # It then calls a 'wanted*' method for additional culling and
        # ultimately, the running of a method associated with an event.
        # Events we care about are stored in *WANTED_EVENTS*.  Returns
        # false for events we don't care about.
        def call_if_wanted
            # Note the use of ===.  This is required rather than using
            # WANTED_EVENTS.include?().
            elem = WANTED_EVENTS.keys.detect do |elem| @event.type === elem; end
            val  = WANTED_EVENTS[elem]
            val ? method(val).call : false
        end
    
        # The 'wanted*' methods provide an additional culling of
        # events to process in addition to checking the event type.
        # They do not, however, have to do additional culling.
        def wanted_key
            @event.key == Qt::Key_Escape ?
                call_registered(:do_escape_key) : false
        end
    
        # See the *wanted_key* comment. This 'wanted*' method does no
        # additional culling.
        def wanted_tooltip
            call_registered(:do_tooltip)
        end
    
        # Call a method associated with the argument *sym*.  Uses
        # the TO_BE_CALLED hash, which stores the methods to be
        # called for each sym.  Returns false if no method is
        # registered for the sym.
        def call_registered(sym)
            meth = TO_BE_CALLED[sym]
            meth ? meth.call(@object, @event) : false
        end
    end
    
    def show_tips(_object, _event)
        # define this to return true if we handle the event and
        # false if Qt is to handle it.  Since I often want tooltips to be
        # off, I usually have this method return true and do nothing
        # else - essentially eating the tooltip event without doing
        # anything.
    end
    
    def got_interrupt(_object, _event)
        # Tell your app to handle the interrupt.  In my apps case, the
        # escape key. If your app always processes the interrupt key
        # then this should always return true.
    end
    
    Usage in the application:
        # Set up filter for key and tool tip events
        eventFilter = EventFilter.new
        eventFilter.register(:do_tooltip, method(:show_tips))
        eventFilter.register(:do_escape_key, method(:got_interrupt))
        RBA::Application.instance.installEventFilter(eventFilter)
    
Sign In or Register to comment.