Skip to content

Getting Started

carefree0910 edited this page Apr 15, 2023 · 27 revisions

🚧🚧 WIP 🚧🚧. If you encountered any problems, feel free to open an issue/a discussion!

The best way to get started is probably going for the examples, especially the Stable Diffusion example. However, if you want a step-by-step guidance, this is the right place to go!

Installation

carefree-drawboard 🎨 requires the following to get started:

pip install carefree-drawboard
npm install --global yarn

Your first carefree-drawboard 🎨 App

Create a folder (e.g., my_fancy_app) wherever you like, get into it, and run

cfdraw init

When you run this command for the first time, we will use yarn to install the JavaScript dependencies for you, which may be quite slow!

This command will write two files to your folder (my_fancy_app). After which you can run the app in development mode:

cfdraw run

And you should see your app running at http://localhost:5123. Now you can play with the generated app.py file and see warm reload (yeah, not hot enough because we rely on the reload provided by uvicorn 🀣).

Notice that the generated template implements a Gaussian Blur plugin, which requires an image to pop up. You can upload an image either by dropping on directly to the drawboard 🎨, or by clicking the Plus button at the top right corner and select Upload Image.

20230415-131404.mp4

Explanations

In this section we'll break down the codes in app.py to show you what's going on under the hood.

class Plugin(IHttpFieldsPlugin):
    ...

This line shows that we are utilizing the IHttpFieldsPlugin binding to define our plugin. An IHttpFieldsPlugin is a convenient interface that allows you to create a plugin with multiple input fields (e.g., text inputs, number inputs) in a declarative way.

See PythonHttpFieldsPlugin for more details.

Every IHttpFieldsPlugin only needs to define two stuffs:

  • The settings property, which is used for defining Styles.
  • The process method, which is used to define logics (algorithms).

settings

Let's see the settings property first:

@property
def settings(self) -> IPluginSettings:
    return IPluginSettings(
        w=300,
        h=180,
        nodeConstraint=NodeConstraints.IMAGE,
        pivot=PivotType.RT,
        follow=True,
        pluginInfo=IHttpFieldsPluginInfo(
            definitions=dict(
                size=INumberField(
                    default=3,
                    min=1,
                    max=10,
                    step=1,
                    isInt=True,
                    label="Size",
                )
            ),
        ),
    )
  • w, h: the width and height of the expanded panel.

If we need to specify the button's wh, we can use iconW and iconH.

  • nodeConstraint: control whether this plugin will be displayed. By specifying NodeConstraints.IMAGE, it means that this plugin will only occur when an ImageNode is selected.

See Node and Plugin Positioning for more details.

  • pivot, follow: positioning settings of this plugin.

See Pivot and Plugin Positioning for more details.

  • pluginInfo: specify some behaviors of the expanded panel.

See IHttpFieldsPluginInfo for more details.

Apart from these, the easiest and (maybe) also the most important thing we need to do is assigning a nice-looking icon to our plugin. We can achieve this by specifying an src argument to the returned IPluginSettings instance:

@property
def settings(self) -> IPluginSettings:
    return IPluginSettings(
        src="...",
        w=300,
        h=180,
        ...

Here, the src should be a url of the desired image. If you don't have one in hand, you may try use this one.

After saving the modifications, you should be able to see the icon of your plugin changes, cool!

You can play with other fields in IPluginSettings to see what will happen!

process

def process(self, data: IPluginRequest) -> Image.Image:
    image = self.load_image(data.nodeData.src)
    return image.filter(ImageFilter.GaussianBlur(data.extraData["size"]))
  • The incoming argument, data, contains all necessary information from the drawboard 🎨. There are two common properties that you may need:
    • nodeData, it is an INodeData instance.
      • If no nodes are selected, this field will be empty.
      • If multiple nodes are selected, this field will be empty and please use nodeDataList instead.
    • nodeDataList, it is a list of INodeData instance.
      • If no nodes are selected, this field will be empty.
      • Please use this field if multiple nodes are selected.
      • If only one node is selected, this field will be empty and please use nodeData instead.
    • extraData, it is a dict that aligns to the definitions field defined in the IHttpFieldsPluginInfo.
  • The load_image is a build-in method that can load a PIL.Image for further processing.

We will add more build-in methods in the future based on your needs!

  • The process method can directly return an str, or a PIL.Image, or a list of them. This is because some Middleware in carefree-drawboard 🎨 will convert them to the data structure we actually need.

See Middleware for more details.

Clone this wiki locally