Change Icon based on State in CommandData



  • On 26/09/2013 at 07:52, xxxxxxxx wrote:

    Hello,

    I am wondering if it's possible to change the icon of CommandData plugin based on a its state.

    I searched on the forums here and cgtalk but it looks like it can't be done (but not sure), so I wan't to ask to be clear on this.

    Thanks for any info.



  • On 26/09/2013 at 08:29, xxxxxxxx wrote:

    Technically yes, practically no / you shouldn't. Invoking BaseList2D.GetIcon() will return a reference 
    to the BaseBitmap used by c4d to draw the interface. Any modifications made here will also show 
    up in the gui, but we are actually meant to treat that bitmap as readonly, so handle with care ;)

    import c4d, random
    from c4d import bitmaps
      
    def main() :
        if isinstance(op, c4d.BaseList2D) :
            data = op.GetIcon()
            bmp = data['bmp']
            sx, sy = data['x'], data['y']
            w, h = data['w'], data['h']
            for px in xrange(sx, sx + w) :
                for py in xrange(sy, sy + h) :
                    r = random.randint(0,255)
                    g = random.randint(0,255)
                    b = random.randint(0,255)
                    bmp.SetPixel(px,py,r,g,b)
            bitmaps.ShowBitmap(bmp)
        c4d.EventAdd()
        
    if __name__=='__main__':
        main()
    

    That code chunk will change the color information of the selected objects icon to some random 
    color noise. For anything useful you would have to write some alpha informations too.

    FYI: For a command plugin you would have to grab the BasePlugin instance of your plugin.

    happy rendering,
    ferdinand



  • On 26/09/2013 at 23:56, xxxxxxxx wrote:

    thanks alot for that, but how do I get an instance of my plugin, searching the forums I found

    myPlugin = c4d.BaseList2D(c4d.plugins.BasePlugin(PLUGIN_ID))

    but this gives "this cannot be instantiated" error

    which is also mentioned in python documentation, so is this a dead end?



  • On 27/09/2013 at 00:32, xxxxxxxx wrote:

    The BasePlugin can be found via c4d.plugins.FindPlugin(PLUGIN_ID)

    You can use c4d.gui.GetIcon() instead, too.

    -Niklas



  • On 27/09/2013 at 01:39, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    myPlugin = c4d.BaseList2D(c4d.plugins.BasePlugin(PLUGIN_ID))

    I can not really believe that you have found that code fragment somewhere in the c4d python
    documentation, as it does not make any sense on a very basic level. There is only one constructor
    for BaseList2D and that does require you to pass an integer as an argument. You are trying to 
    pass a BasePlugin instance, which does not work because the BasePlugin class cannot be 
    instantiated, as the interpreter already did tell you and also is not the correct argument type for 
    the BaseList2D constructor.

    Also trying to instantiate your plugin does not really make sense in that case, as it is one of the
    plugin types (CommandData, MessageData etc.)  which cannot be instantiated, or more precisely 
    are only instantiated once after the start up by c4d. It is also quite important to understand the 
    difference of your plugin class derived from BaseData, a BasePlugin and an actual node  repre-
    senting a plugin object.



  • On 27/09/2013 at 01:41, xxxxxxxx wrote:

    awesome, thanks for all the help!



  • On 27/09/2013 at 01:55, xxxxxxxx wrote:

    Yeah thats not a direct copy, most of the code I simply copy from here and there.

    I don't have any deep understanding of the sdk, but I get along with trial and error, examples and forums =)

    Thanks for that explanation.



  • On 27/09/2013 at 08:27, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    I am wondering if it's possible to change the icon of CommandData plugin based on a its state.

    It's not possible to dynamically change the icon images for CommandData plugins in the menu.
    That image is set during the registration process when C4D starts up. And cannot be changed dynamically.

    The CommandData plugin does have a checkbox option for it which might look like an icon image. But it's not. It's done with a flag. And doesn't use the bitmap image where the icons are stored.
    It's an internal thing hard coded by Maxon.

    It would be very nice to be able swap our own icon images for the on/off state of these plugins. Instead of being stuck with that checkmark option. Lots of people want to do that.
    But it's not possible until Maxon adds that ability to the API.

    -ScottA



  • On 27/09/2013 at 10:17, xxxxxxxx wrote:

    It is possible. You can easily change the icon of the Cube plugin with this script.
    >
    >
    >
    >
    > import c4d
    >
    > def set_icon(pluginid, bmp, overwrite_alpha=True) :
    > icon = c4d.gui.GetIcon(pluginid)
    > if not icon:
    > return c4d.gui.RegisterIcon(pluginid, bmp)
    >
    > ref = icon['bmp']
    > w, h = icon['w'], icon['h']
    >
    > temp = c4d.bitmaps.BaseBitmap()
    > if temp.Init(w, h, bmp.GetBt()) != c4d.IMAGERESULT_OK:
    > return False
    >
    > bmp.ScaleIt(temp, 256, True, True)
    >
    > a1 = a2 = None
    > if overwrite_alpha:
    > a1 = ref.GetInternalChannel()
    > a2 = temp.GetInternalChannel()
    > for x in xrange(w) :
    > rx = x + icon['x']
    > for y in xrange(h) :
    > ry = y + icon['y']
    > ref[rx, ry] = temp[x, y]
    > if a1:
    > if a2:
    > alpha = temp.GetAlphaPixel(a2, x, y)
    > else:
    > alpha = 255
    > ref.SetAlphaPixel(a1, rx, ry, alpha)
    >
    > return True
    >
    > def main() :
    > fn = c4d.storage.LoadDialog()
    > if not fn: return
    >
    > bmp = c4d.bitmaps.BaseBitmap()
    > res = bmp.InitWith(fn)[0]
    > if res != c4d.IMAGERESULT_OK:
    > print "Invalid image:", res
    > return
    >
    > set_icon(c4d.Ocube, bmp)
    > c4d.gui.GeUpdateUI()
    > c4d.EventAdd()
    >
    > main()

    It is not possible to change the icon of an individual node, but that is also not a necessity when
    working with a CommandData plugin.

    Best,
    -Niklas



  • On 27/09/2013 at 10:21, xxxxxxxx wrote:

    Well actually it is Scott, my example above shows how to ;). But it is kind of black magic, as we
    are meant to treat the BaseBitmap as readonly.



  • On 27/09/2013 at 10:46, xxxxxxxx wrote:

    I think we're talking about different things.
    Your code shows how to edit the read only bitmap file found in: resource\icons\interface_icons.tif
    But a CommandData plugin does not use this file.

    The CommandData plugin uses a flag in the GetState() method to toggle a check mark on/off

        if(menustate) return CMD_ENABLED|CMD_VALUE;  //Enable the menu checkmark  
      else return CMD_ENABLED;                     //Turn off the menu checkmark
    

    AFAIK. It's not possible toggle between two images instead of this checkmark.
    There's nothing in the SDK that allows us to do this.
    Many people have tried. No one has succeeded.

    -ScottA



  • On 27/09/2013 at 11:48, xxxxxxxx wrote:

    Not sure what you mean, for me it is just working fine. It actually does not matter if the
    bitmap is located in the main icon file or somewhere else. Took a command data plugin
    of mine and did a quick test.

    import c4d, random
    from c4d import bitmaps, gui
      
    CMD_DATA_ID = 1030901
      
    def main() :
        data = gui.GetIcon(CMD_DATA_ID)
        bmp = data['bmp']
        sx, sy = data['x'], data['y']
        w, h = data['w'], data['h']
        for px in xrange(sx, sx + w) :
            for py in xrange(sy, sy + h) :
                bmp.SetPixel(px,py,255,0,0)
        gui.GeUpdateUI()
        c4d.EventAdd()
        
    if __name__=='__main__':
        main()
    



  • On 27/09/2013 at 12:11, xxxxxxxx wrote:

    Scott, the GetState() method has nothing to do with the icon of the command. As littledevil
    already pointed out, it doesn't matter whether you modify an icon which originates from
    Cinema's own icon-table bitmap. You are only modifying the copy in the RAM, not the one
    on the HD.

    Download the full plugin
     
    >
    > _<_t_>_
    >
    >
    > 1
    > 2
    > 3
    > 4
    > 5
    > 6
    > 7
    > 8
    > 9
    > 10
    > 11
    > 12
    > 13
    > 14
    > 15
    > 16
    > 17
    > 18
    > 19
    > 20
    > 21
    > 22
    > 23
    > 24
    > 25
    > 26
    > 27
    > 28
    > 29
    > 30
    > 31
    > 32
    > 33
    > 34
    > 35
    > 36
    > 37
    > 38
    > 39
    > 40
    > 41
    > 42
    > 43
    > 44
    > 45
    > 46
    > 47
    > 48
    > 49
    > 50
    > 51
    > 52
    > 53
    > 54
    > 55
    > 56
    > 57
    > 58
    > 59
    > 60
    > 61
    > 62
    > 63
    > 64
    > 65
    > 66
    > 67
    > 68
    > 69
    > 70
    > 71
    > 72
    > 73
    >
    > |
    >
    >
    > import os
    > import c4d
    >
    > def get_res_bitmap(filename) :
    > bmp = c4d.bitmaps.BaseBitmap()
    > result = bmp.InitWith(os.path.join(os.path.dirname(__file__), 'res', filename))
    > if not result or result[0] != c4d.IMAGERESULT_OK:
    > return None
    >
    > return bmp
    >
    > def set_icon(pluginid, bmp, overwrite_alpha=True) :
    > icon = c4d.gui.GetIcon(pluginid)
    > if not icon and bmp:
    > return c4d.gui.RegisterIcon(pluginid, bmp)
    > elif icon and not bmp:
    > return c4d.gui.UnregisterIcon(pluginid, bmp)
    > elif not icon:
    > return
    >
    > ref = icon['bmp']
    > w, h = icon['w'], icon['h']
    >
    > temp = c4d.bitmaps.BaseBitmap()
    > if temp.Init(w, h, bmp.GetBt()) != c4d.IMAGERESULT_OK:
    > return False
    >
    > bmp.ScaleIt(temp, 256, True, True)
    >
    > a1 = a2 = None
    > if overwrite_alpha:
    > a1 = ref.GetInternalChannel()
    > a2 = temp.GetInternalChannel()
    > for x in xrange(w) :
    > rx = x + icon['x']
    > for y in xrange(h) :
    > ry = y + icon['y']
    > ref[rx, ry] = temp[x, y]
    > if a1:
    > if a2:
    > alpha = temp.GetAlphaPixel(a2, x, y)
    > else:
    > alpha = 255
    > ref.SetAlphaPixel(a1, rx, ry, alpha)
    >
    > return True
    >
    > class MyCommand(c4d.plugins.CommandData) :
    >
    > pluginid = 1000010 # TEST ID ONLY
    > state = False
    > bmp_a = get_res_bitmap('state-a.png')
    > bmp_b = get_res_bitmap('state-b.png')
    >
    > def Register(self) :
    > c4d.plugins.RegisterCommandPlugin(self.pluginid, "Icon Toggler", 0,
    > None, "Execute to switch the icon of the command.", self)
    > self.UpdateIcon()
    >
    > def UpdateIcon(self) :
    > if not self.state:
    > bmp = self.bmp_a
    > else:
    > bmp = self.bmp_b
    > set_icon(self.pluginid, bmp)
    >
    > def Execute(self, doc) :
    > self.state = not self.state
    > self.UpdateIcon()
    > return True
    >
    > if __name__ == "__main__":
    > MyCommand().Register()
    >
    > <_<_t_>_

    Best,
    -Niklas



  • On 27/09/2013 at 13:01, xxxxxxxx wrote:

    Thanks for the code Nik.

    It doesn't solve the problem I was talking about.
    But storing the icon in RAM is an interesting way to get around the problem.

    -ScottA



  • On 27/09/2013 at 13:08, xxxxxxxx wrote:

    Originally posted by xxxxxxxx

    Thanks for the code Nik.

    It doesn't solve the problem I was talking about.
    But storing the icon in RAM is an interesting way to get around the problem.

    -ScottA

    I am not sure what problem you are talking about, could you pinpoint it for me please?

    Best,
    -Niklas



  • On 27/09/2013 at 14:44, xxxxxxxx wrote:

    Well. It was a long time ago. And now I'm wondering if I'm remembering something else we were trying to do and having problems.
    I can't remember if we couldn't update the CommandData icons in the menu. Or if it was the icons for a plugin that were docked in the UI. Or something like that.
    The original poster commented about reading this problem on CGTalk. I'm guessing he was probably reading our old posts.

    We were trying to swap images that were stored on the HD using COFFEE. But could not figure it out.
    It was a long time ago.

    -ScottA


Log in to reply