Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    UI Tool Preview - The draw (DrawApi) module

    Scheduled Pinned Locked Moved PYTHON Development
    11 Posts 0 Posters 1.3k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H Offline
      Helper
      last edited by

      THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

      On 15/08/2011 at 12:13, xxxxxxxx wrote:

      draw (DrawApi) - Preview
      -----------------------

      NOTE: The module is now called draw, not DrawAPI any more. The example code of the examples shown here is from the old prototype. It is now even much more handy to use. I hope to finish it soon.

      Imagine you'd need to create a dynamic Graphical User Interface to show a list of items, like Cinema 4Ds Object Manager for example. It would be a pain for you to subclass the GeUserArea class and to all the drawing stuff, because it's so linear and intransparent.
      This is the place where DrawAPI comes in place. It's a module providing object-orientated handling of elements for a User Interface.

      The most important class is the View. A View defines a rectangular area on the User Area supporting a backgroundcolor, input events and even subviews.

      A views position and size is defined by a Frame, which can carry relative or absolute x, y, width and height values, customizeable for each.

      A relative position is always calculated to it's masters absolute position and size. This doesn't mean a View with relative Frame-values does need a superview with only absolute ones. The absolute values are calculated at rendertime allowing you to do any relative bahaviour you want.

      There is also a special subclass of a GeUserArea named DrawArea which does already implement the rendering of Views, so you don't even need to handle that.

      Let's just create a User Interface with a blue blackground and a View with another Subview on it, just like this hierarchy and properties.
      The little r's and a's before a number tells you that the value is either relative or absolute.

      DrawArea            Color blue  
      \+ - View            Position a10, a10   Size    r0.5, r0.5  Color red  
      \+ - + - View        Position r0,  r0    Size    a20,  r1    Color yellow
      

      The resulting Interface will look like this:

      We can see that the red area is moved about some pixles (exactly 10) from the blue areas origin (top left) in x and y direction.
      The red area is also only as half as wide and high as the blue one. (Remember that the view is really moved, so the red area overlaps about 10 pixels from the real middle of the blue area.)

      Next thing we see is the yellow area which has it's origin at the same position as the red view, although it's position is 0, 0. That's because it is a subview of the red one and it's position and size is relative to this one. This also leads to that the yellow area is as high as the red one,  but is only 20 pixels wide.

      And this is the simple code to achieve this.

      import  c4d  
      import  DrawAPI     as d  
        
      COLOR_BLUE  = c4d.Vector(.3, .4, .8)  
      COLOR_RED   = c4d.Vector(.8, .25, .15)  
      COLOR_YELLOW= c4d.Vector(.8, .7, .3)  
        
      def main() :  
        area        = d.DrawArea()  
        area.color  = COLOR_BLUE            # make the DrawArea's backgroundcolor blue  
        
        
        # create a View with our desired properties  
        view1       = d.View(10, 10, .5, .5, abs_x = True, abs_y = True)  
        view1.color = COLOR_RED  
        
        area.AddSubview(view1)  # make view1 a subview of the DrawArea  
        
        # create the second View with our desired properties  
        view2       = d.View(0, 0, 20, 1, abs_w = True)  
        view2.color = COLOR_YELLOW  
        
        view2.AddSubview(view2) # make view2 a subview of the first view  
        
        dlg         = d.TestDialog(area)    # a dialog just showing a GeUserArea  
        dlg.Open()  # by default c4d.DLG_TYPE_MODAL_RESIZEABLE  
        
      main()
      

      The code is actually for the Script manager. That is the best way to create and test interfaces for the DrawAPI, because you can get immediate feedback when clicking on execute.

      Now, what's with the example from above, the Interface with the list of items ? No problem. DrawAPI comes with a class for easy and efficient creating such, called the ListView.
      The ListView does actually nothing more than creating subviews that are the items in the list.
      But those Views must be a special subclass of (or provide the same interface as) ListViewRow. It is important that those Views provide a GetHeight() method, otherwise the ListView wouldn't know where to place the next row.
      Also, the ListView needs to know how many items to display, etc. This is why it also needs an instance of ListViewSource (or an object that provides the same interface).

      Let's just show you an example step-by-step. This will also introduce another class, the Label, which does nothing more than showing text.

      The resulting Interface will look like this:

      First thing we need to do is to decide if we subclass the ListViewRow to apply our desired behaviour or to just add a Label as subview. I decide to subclass it, as we need to override the ListViewRow.Update() method.

      This is actually very simple, because we only need to add a Label to the Row and override the Update method to be able to modify our row's label's text.

      import  c4d  
      import  DrawAPI     as d  
        
      COLOR_GREY1 = c4d.Vector(.21)  
      COLOR_GREY2 = c4d.Vector(.23)  
      COLOR_WHITE = c4d.Vector(.85)  
        
      class TextRow(d.ListViewRow) :  
        
        def OnInit(self) :  
            'Called on initialization to bypass overriding __init__.'  
            # create a label with (yet) no text and an absolute position of 5, 2  
            # (we don't want the label to change it's position when the userarea resizes)  
            # note that the Label-constructor does not take any argument defining the size.  
            # a label does only have a position.  
            self.label  = d.Label('', 5, 2, abs_x = True, abs_y = True) # text, x, y  
            self.label.textcolor    = COLOR_WHITE   # set the textcolor to white  
            self.AddSubview(self.label) # otherwise the label wouldn't be rendered  
        
            # we also want to have our view to be 17 pixels high  
            # note: you can override GetHeight, but the default implementation does already  
            # return the value of self.frame.size.y  
            self.frame.size.y   = 17  
        
        def Update(self, text, index) :  
            '''  
            This method is called in order to update some content within the row.  
            We can define the arguments in whatever way we want, because the arguments  
            passed to this method are defined in the ListViewSource.GetUpdateData() method.  
            We also pass the index of the row to this method, because we want to set the  
            background color according to this value, so the rows differ from each other.  
            '''  
            self.label.text = text  
        
            # set backgroundcolor  
            if index % 2:  
                self.color  = COLOR_GREY1  
            else:  
                self.color  = COLOR_GREY2
      

      Next thing is to define our source for the ListView.

      class TextSource(d.ListViewSource) :  
        
        def __init__(self, *strings) :  
            self._data  = strings  
        
        def GetCount(self) :  
            'Called in order to get the number of items in the list.'  
            return len(self._data)  
        
        def MakeRow(self, index) :  
            'Called in order to recieve a view that is placed in the list.'  
            # the default constructor of ListViewRow does not take any arguments.  
            return TextRow()  
        
        def GetUpdateData(self, index) :  
            '''  
            This method needs to return arguments and keyword-arguments passed  
            to ListViewRow.Update().  
            In our case, we need to return the value of the text-argument and  
            the row's index.  
            '''  
            return (self._data[index], index), {}     # a tuple with the string and the index and an empty dictionary
      

      Now, the only thing left to do is to create a ListView with our Source, a DrawArea and a Dialog.

      def main() :  
        area    = d.DrawArea()  
        
        source  = TextSource('I am the first row.', 'And I am the second one.', 'Could you please be quiet up there ?')  
        list_   = d.ListView(source, 0, 0, 1, 1)    # the source, x, y, width, height  
        list_.ReloadData()  # must be called, otherwise there won't be any rows in the ListView  
        
        area.AddSubview(list_)  
        
        dlg     = d.TestDialog(area)  
        dlg.Open()  
        
      main()
      

      Easy practice, isn't it ?
      Let's make this waaaay cooler ! What about the ability to click on one of the items causing the row the become bigger and revealing new content ?

      A View class does implement a MouseEvent and AnyEvent method. You can guess what method is called when.
      Overriding those will enable us to achieve the desired behaviour.

      class TextRow(d.ListViewRow) :  
        
        HEIGHT_DEFAULT  = 17  
        HEIGHT_EXPANDED = 40  
        
        # ....  
        
        def SetHeight(self, h) :  
            self.frame.size.y   = h  
        
        def MouseEvent(self, area, msg, mouse_global, mouse_local) :  
            'Called whenever the mouse clicks into this view.'  
            # we use ListViewRow.GetHeight() because it's convenient in this case and we  
            # don't need to write self.frame.size.y  
            if self.GetHeight() == self.HEIGHT_DEFAULT:  
                self.SetHeight(self.HEIGHT_EXPANDED)  
            else:  
                self.SetHeight(self.HEIGHT_DEFAULT)  
        
            # we need the listview to tell it there was something changed within it's rows  
            # using ListView.SendReloadMessage(with_update = False) we can do so.  
            # as we only need to refresh the position of the rows and not to call the rows'  
            # Update() method, we omit the call_update argument  
            # Note that we could also call ListView.ReloadData(False), but imagine more than one  
            # row would call this method, the rows would be unnecessarily updated multiple times,  
            self.list_view.SendReloadMessage()  
        
            # also, we need to tell the area to redraw after that input event.  
            # same as with ListView.SendReloadMessage(), we don't call UserArea.Redraw()  
            # directly, because it would lead to highly redundant redraw-traffic if  
            # more than one row would call it.  
            area.SendRedrawMessage()
      

      Now that we've implemented the mouseclick, we could add content to be shown on the new area revealed when clicking, but that's too much for this preview of the DrawAPI. But just to whet your appetite, we could add a subview that is within the hidden area of the row and set it's opaque-attribute to False, so it won't be rendered. When the user clicks and MouseEvent is called, we set it to True.

      The result when clicking on the first and last row, those are expanded to 40 pixels, while the middle row is still at 17 pixels.

      This was (hopefully) an introduction that made you curious about using the DrawAPI.
      Please leave some comments below and ask questions if you have any.

      The DrawAPI will be released soon as a stable release, ready for you to use. However, it will be extended by time and API might change. There also won't be a documentation, but methods are well commented. (Yes, the module will be Open Source).

      Thanks for reading !
      Niklas

      1 Reply Last reply Reply Quote 0
      • H Offline
        Helper
        last edited by

        THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

        On 16/08/2011 at 10:48, xxxxxxxx wrote:

        siiiiick!!!!! another incredibly useful tool from niklas. thanks duder!

        1 Reply Last reply Reply Quote 0
        • H Offline
          Helper
          last edited by

          THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

          On 22/08/2011 at 11:34, xxxxxxxx wrote:

          nice that you 'll find it well. 🙂
          Here's another Preview of the ListView class and how to use it.

          1 Reply Last reply Reply Quote 0
          • H Offline
            Helper
            last edited by

            THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

            On 30/08/2011 at 15:15, xxxxxxxx wrote:

            Please note that I have edited the first post. If you are interested in creating userinterfaces with userareas, I beg you to read it. It is very detailed and should give you a very good overview about what the DrawAPI is.

            Thank you !
            Niklas

            1 Reply Last reply Reply Quote 0
            • H Offline
              Helper
              last edited by

              THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

              On 18/11/2011 at 09:26, xxxxxxxx wrote:

              My newest achievement: Animations on a GeUserArea !
              Example Video

              Also note that the module will now be called draw , no more DrawApi.

              Cheers !

              1 Reply Last reply Reply Quote 0
              • H Offline
                Helper
                last edited by

                THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

                On 19/11/2011 at 04:29, xxxxxxxx wrote:

                Impressive work! The animation stuff is fantastic!

                1 Reply Last reply Reply Quote 0
                • H Offline
                  Helper
                  last edited by

                  THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

                  On 02/12/2011 at 14:05, xxxxxxxx wrote:

                  Thanks 990adjustments.

                  The release is in front of our doors, but not yet. 🙂 Here is the link to the package index on pypi, you can also find the so-far documentation there!

                  draw 0.0.71 - PyPi

                  1 Reply Last reply Reply Quote 0
                  • H Offline
                    Helper
                    last edited by

                    THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

                    On 17/02/2012 at 08:35, xxxxxxxx wrote:

                    hey Niklas,

                    VERY NICE STUFF

                    Is this implemented into C4D? I tried one of your code snippets and it gave an error trying to import the draw module.

                    I really like the direction you are going with this 🙂 As a newbie to python coding, I have found the grid-like nature of the dialogs to be frustrating. I can code HTML and CSS fairly well and my first bite into python for C4D made me feel like being confined to tables in HTML haha.

                    Maybe there is a way to do floating/self adjusting content in dialogs that I just dont know about yet because i am so new to all of this. I have my first plugin pretty much polished off, but there are some things I wish I could do with the interface...like having it automatically adjust the interface to fit where the user docks it (ie horizontally or vertically). I essentially have 25 icons acting as buttons and would LOVE it if they could be arranged more dynamically.

                    Anyway, congrats on this module...looks awesome!

                    1 Reply Last reply Reply Quote 0
                    • H Offline
                      Helper
                      last edited by

                      THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

                      On 17/02/2012 at 09:08, xxxxxxxx wrote:

                      Hei Marc,

                      I'm afraid it isn't implemented into Cinema 4D, it is a self-written Python module. It also isn't released yet, I've rewritten the whole package multiple times now because I didn't like the design of the code. Currently it is at a very stable status and I hope to find time and motivation to make it "finished", at least that it can be published.

                      Your post definitely motivates me, thank you for your interest! I'm really planning into moving it forward, but school and other projects keeps me on. I will post it when I've come to the point to be able to publish it. 🙂

                      Greetings,
                      -Niklas

                      1 Reply Last reply Reply Quote 0
                      • H Offline
                        Helper
                        last edited by

                        THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

                        On 17/02/2012 at 09:13, xxxxxxxx wrote:

                        Anytime Niklas!

                        Like I said, I am a total newbie to all of this. Coding is not my primary area of interest but I see the complete power it can give you over your work and workflow 🙂

                        There is much i need to learn! My new plugin...or at least the "cooler" new version will be out soon 🙂 Keep an eye out for it

                        1 Reply Last reply Reply Quote 0
                        • H Offline
                          Helper
                          last edited by

                          THE POST BELOW IS MORE THAN 5 YEARS OLD. RELATED SUPPORT INFORMATION MIGHT BE OUTDATED OR DEPRECATED

                          On 17/02/2012 at 09:14, xxxxxxxx wrote:

                          btw...did your friend get the RenderFrame script I put out yesterday? hopefully it worked well 🙂

                          1 Reply Last reply Reply Quote 0
                          • First post
                            Last post