Skip to content

Getting Started

carefree0910 edited this page Apr 18, 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 StableDiffusion examples (see this, this, this and this). 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:

To be exact, we need ^14.13.1 || ^16 || >=18 because of the tsconfck@2.1.1 package.

pip install carefree-drawboard
npm install --global yarn

Although we'll always try to help you install the frontend packages, it is recommended to install them beforehands because you can receive much more verbose:

cfdraw install

If you are interested in the latest features, you may use pip to install from source as well:

git clone https://github.com/carefree0910/carefree-drawboard.git
cd carefree-drawboard
pip install -e .

Your first carefree-drawboard 🎨 App

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

cfdraw init

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

cfdraw run

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

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 GaussianBlur 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

async def process(self, data: IPluginRequest) -> Image.Image:
    image = await 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 async method that can load a PIL.Image from src 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