Info display panel

edited June 2015 in Ruby Scripting

I would find it useful to see the 'state' of certain settings at a glance without opening any additional dialog/window.

For example, see if 'select top level objects' is selected.

So I wrote this code. It mostly works (displays correctly, works correctly when you click the checkbox). But the last bit where I try to link that to a shortcut key doesn't work. (Scroll to "DOESN'T WORK")

I think that the a=Action.new... functionality was probably originally intended to create Action items to place inside menus, and I have successfully used it in that way before.. but am I correct in concluding that this .shortcut method doesn't work if the Action is not inside a menu?

include RBA
inst = Application.instance

# Set up the panel

dock = QDockWidget::new("Settings", Application.instance.main_window)
dock.setAllowedAreas(Qt_DockWidgetArea::AllDockWidgetAreas) # Prior to 0.24 you had to use: dock.setAllowedAreas(0xF)
lw = QListWidget.new(dock)
dock.setWidget(lw)

sel_top_t = "Select top level (T)"
sel_top = QListWidgetItem.new(sel_top_t,lw)
snap_obj_t = "Snap to objects (J)"
snap_obj = QListWidgetItem.new(snap_obj_t,lw)
lw.addItem(sel_top)
lw.addItem(snap_obj)

c  = Qt_CheckState::Checked
nc = Qt_CheckState::Unchecked

# Figure out if it should start out checked or not

s  = "edit-top-level-selection"
inst.get_config(s)=="true" ? sel_top.setCheckState(c)  : sel_top.setCheckState(nc)
s = "edit-snap-to-objects"
inst.get_config(s)=="true" ? snap_obj.setCheckState(c) : snap_obj.setCheckState(nc)

# Whenever it's clicked with the mouse, toggle the actual setting state

lw.itemChanged() { |qlwi|
  case qlwi.text
  when sel_top_t
    s = "edit-top-level-selection"
  when snap_obj_t
    s = "edit-snap-to-objects"
  end
  inst.get_config(s)=="true" ? inst.set_config(s, "false") : inst.set_config(s, "true") # Toggle it
}

# Or, whenever you press the relevant shortcut key, it toggles the setting state and the checked state
# DOESN'T WORK

a = Action.new
a.shortcut = "T"
a.on_triggered {
  s = "edit-top-level-selection"
  if inst.get_config(s)=="true"
    sel_top.setCheckState(c) 
    inst.set_config(s, "false")
  else
    sel_top.setCheckState(nc)
    inst.set_config(s, "true")
  end
}

a = Action.new
a.shortcut = "J"
a.on_triggered {
  s = "edit-snap-to-objects"
  if inst.get_config(s)=="true" 
    snap_obj.setCheckState(c)
    inst.set_config(s, "false")
  else
    snap_obj.setCheckState(nc)
    inst.set_config(s, "true")
  end
}

inst.main_window.addDockWidget(Qt_DockWidgetArea::RightDockWidgetArea, dock)

Thanks,
David

Comments

  • edited November -1

    I realized a second problem. If you change the setting in the usual manner (eg. F3 > Select top level objects) then the toggling function of the above code gets out of sync.

    Basically it lacks a way to respond to the user setting the "true" or "false" setting by another method (F3).

    Is this not possible with the existing API?

  • edited June 2015

    Hi David,

    well, you'd need a way to watch the configuration settings. That's more or less the database holding the configuration parameters where various sources (including scripts) send their configuration requests to. Later these requests will be distributed to the modules.

    Right now there is no direct way to receive configuration events, but there is an indirect one: By registering a plugin you basically create another module which will receive configuration events. This solution exploits an undocumented feature: a plugin factory will receive configuration events even for keys not registered for that specific plugin.

    Please don't tell anybody :-)

    class PluginTestFactory < RBA::PluginFactory
    
      # Constructor
      def initialize
        # Note: -1000 will make the plugin register itself before other plugins
        register(-1000, "my_plugin_test", "My plugin test")
      end
    
      # Will be called whenever a configuration parameter changes:
      def configure(name, value)
        puts("configuration changed: #{name}: #{value}")
        # Makes the system propagate the event further:
        return false
      end
    
    end
    
    PluginTestFactory.new
    

    Matthias

  • edited June 2015

    Thanks Matthias,

    Yes your secret is safe with me. :)

    Well this is a nice feature! But it seems to be messing with either other plugins or configuration settings. When I run it, various things get disabled, for example

    • The background I usually have is to show axes as lines and grid as dots. These disappear.
    • The scale bar at bottom left disappears.
    • I have several rulers defined. While these still work as rulers, they now show up as the default ruler (e.g. no angle constraint, no arrows, no special formatting, ...)

    Hopefully the above symptoms might give you some idea what is going on? I took a look at PluginFactory class but can't see any hints..

    I understand this is not standard territory into which we are venturing, and KL is still 'young', so please don't view my comments as any kind of disrespect for what is a GREAT tool and for the thought you have put in to it.

    David

    P.S. For future readers: A second minor point is that it installs a toolbar entry "My plugin test" which wouldn't be required for such 'stealth' (run-in-background) plugins. I find I can delete it with RBA::Application.instance.main_window.menu.delete_item("@toolbar.my_plugin_test"); deleting it doesn't appear to affect the functioning of the plugin.

  • edited June 2015

    Huh, never mind, it seems to be solved by just making it "Run early on startup" -- i.e. run before any layout views are created.

    If I get a working final "settings display" script, I'll post it here.

    Thanks Matthias

    David

  • edited June 2015

    Hi David,

    The symptoms you describe look like the return value of the "configure" method is not "false" - if it's true, the configuration events will basically be blocked with similar effects than you describe.

    To prevent the toolbar entry, you can set the "has_tool_entry" attribute to false in the initialization. Here is my complete code which works for me even if started without "run early":

    class PluginTestFactory < RBA::PluginFactory
    
      # Constructor
      def initialize
        self.has_tool_entry = false
        register(-1000, "my_plugin_test", "My plugin test")
      end
    
      def configure(name, value)
        puts("configuration changed: #{name}=#{value}")
        return false
      end
    
    end
    
    PluginTestFactory.new
    

    Best regards,

    Matthias

  • edited November -1

    Thanks Matthias for the tip about has_tool_entry.

    Thanks also for your other comments. I do still see the problems I mention if I run the plugin after a layoutview already exists. If I run it before a layoutview exists, it is fine. In that sense then it doesn't need to "run on startup" necessarily, it just needs to run before a layoutview exists. But it's fine, the fix for me is just to run on startup. ("early" is not required, contrary to my previous post.)

    Here is the final code, which works quite nicely I think! Thanks so much for your help.

    David

    # Quick view panel plugin.lym
    # By davidnhutch
    # 
    # You must choose "run on startup" or else run this somehow before
    # the LayoutView exists.
    # 
    # Installs a new panel on the right. Displays and lets you change 
    # settings for various parameters. All of these are accessible 
    # elsewhere (most in File > Setup or Edit > Options), but this 
    # panel gives you visual feedback at a glance.
    
    include RBA
    
    class QVPPluginFactory < PluginFactory
    
      def initialize(qlwi_arr)
        @qlwi_arr = qlwi_arr # Array of QListWidgetItems
        self.has_tool_entry = false # Don't add a toolbar item
        register(-1000, "quick_view_panel", "Quick view panel") # The -1000 will make the plugin register itself before other plugins
      end
    
      def configure(slug, value) # Will be called whenever a configuration parameter changes
    
        # Find which QListWidgetItem that was changed
        qlwi = @qlwi_arr.find { |item| "#{item.slug}" == slug }
    
        # If it's not nil then it was found (i.e. it's a param we care about), so change its value in the object itself.
        qlwi.value = value if !qlwi.nil?
    
        # Make the system propagate the event further
        return false 
      end
    
    end
    
    class QVPItem < QListWidgetItem
    
      attr_reader :name,:slug,:has_checkbox,:app,:value # These are readonly. Except for value, which has a writer method defined separately below
    
      def initialize(name,slug,has_checkbox,value,app) # Except for app, all are strings, e.g. "true" -- because that is how they are stored in the config file
        @name = name
        @slug = slug
        @has_checkbox = has_checkbox
        @value = value
        @app = app
    
        super(name)
    
        # By setting .checkState to true or false initially, it knows there should be a checkbox and therefore shows one.
        if @has_checkbox == "true"
          if @value == "true" || @value == "diagonal"
            self.checkState = Qt_CheckState::Checked
          elsif @value == "false" || @value != "diagonal"
            self.checkState = Qt_CheckState::Unchecked
          else
            raise "Check state not recognized"
          end
        end
    
      end
    
      def value=(val)
        @value = val
        if @has_checkbox == "true"
          if @value == "true" || @value == "diagonal"
            self.checkState = Qt_CheckState::Checked
          elsif @value == "false" || @value != "diagonal"
            self.checkState = Qt_CheckState::Unchecked
          else
            raise "Check state not recognized"
          end
        else # Otherwise, it will evaluate to something we feed in to the display text
          self.text = "Grid: #{val} um" # But right now our only option is the 'Grid' text, so we feed that.
        end
      end
    
      def toggle_value!
        # Set this object's internal tracking parameter
        case @value
        when "true"
          @value = "false"
        when "false"
          @value = "true"
        when "diagonal" # I don't use manhattan. I just go back and forth between diagonal and any.
          @value = "any"
        when "any"
          @value = "diagonal"
        end
    
        # Actually set the setting
        @app.set_config(@slug,@value)
    
        # Return the new value
        @value
    
      end
    end
    
    # Set up the panel
    app = Application.instance
    mw = app.main_window
    dock = QDockWidget.new("Quick view panel", mw)
    dock.setAllowedAreas(Qt_DockWidgetArea::AllDockWidgetAreas) # Prior to 0.24 you had to use: dock.setAllowedAreas(0xF)
    lw = QListWidget.new(dock)
    dock.setWidget(lw)
    
    # Set up the panel's items
    items = []
    
    slug = "grid-micron"
    value = app.get_config(slug)
    name = "Grid: #{value} um"
    has_checkbox = "false"
    items << QVPItem.new(name,slug,has_checkbox,value,app)
    
    slug = "edit-top-level-selection"
    value = app.get_config(slug)
    name = "Select top level"
    has_checkbox = "true"
    items << QVPItem.new(name,slug,has_checkbox,value,app)
    
    slug = "edit-snap-to-objects"
    value = app.get_config(slug)
    name = "Snap to objects"
    has_checkbox = "true"
    items << QVPItem.new(name,slug,has_checkbox,value,app)
    
    slug = "ruler-grid-snap"
    value = app.get_config(slug)
    name = "Ruler-grid snap"
    has_checkbox = "true"
    items << QVPItem.new(name,slug,has_checkbox,value,app)
    
    slug = "no-stipple"
    value = app.get_config(slug)
    name = "Layers without fill"
    has_checkbox = "true"
    items << QVPItem.new(name,slug,has_checkbox,value,app)
    
    slug = "edit-connect-angle-mode"
    value = app.get_config(slug)
    name = "Diag conn"
    has_checkbox = "true"
    items << QVPItem.new(name,slug,has_checkbox,value,app)
    
    slug = "edit-move-angle-mode"
    value = app.get_config(slug)
    name = "Diag mvmts"
    has_checkbox = "true"
    items << QVPItem.new(name,slug,has_checkbox,value,app)
    
    slug = "guiding-shape-visible"
    value = app.get_config(slug)
    name = "Show guid shapes"
    has_checkbox = "true"
    items << QVPItem.new(name,slug,has_checkbox,value,app)
    
    items.each { |item| lw.addItem(item) }
    
    # Whenever any toggle-able items are clicked with the mouse, toggle 
    # the actual setting state. Does nothing for non-toggle-able items.
    lw.itemChanged() { |lwi| lwi.toggle_value! }
    
    # Show the panel
    mw.addDockWidget(Qt_DockWidgetArea::RightDockWidgetArea, dock)
    
    # Instantiate the plugin
    QVPPluginFactory.new(items)
    
  • edited November -1

    Hi David,

    thanks for providing the script.

    I can confirm the issue with the missing background after running the script directly. I'd like to understand the reason, but on the other hand there seems to be a simple solution.

    Thanks,

    Matthias

Sign In or Register to comment.