Creating a Template Plugin - Investigamer/Proxyshop GitHub Wiki

Proxyshop comes with a plugin loader that makes it simple to integrate your own Photoshop templates and release them as a plugin! Let's explore the basic steps.

The Photoshop Template

If you are setting out to create a Photoshop template (PSD file) or have one of your own you'd like to use, its important to structure your layers as similarly to existing Proxyshop templates as possible, this will make integrating your template much easier. Let's take a look at how layers are usually structured:

Layer Example

Please note your template doesn't need to include features like Nyx borders, Companion elements, or even some basic elements like Legendary Crowns. For example, if you look at the Kaldheim template included with Proxyshop there are no Legendary Crown layers. Just make sure to name and structure your layers as similarly to this format as possible, we'll explain how to omit things like Legendary Crowns later. Another thing to keep in mind is Art Frame and Full Art Frame is the reference area that Proxyshop uses to size and frame the art for your card. Make sure these match the bounds of where your image should be placed. Under Text and Icons, you will also see "Textbox Reference", this is the references used to size and position the cards rule text. Make sure this fits inside the bounds of your rules text area. You also have "Expansion Reference", this is used to size and position the Expansion Symbol.

Your Template Code

Let's create the code for your template! In your text editor of choice, create a new file called templates.py. Copy the following code and paste it in:

"""
<YOUR NAME> TEMPLATES
"""
# Standard Library Imports
from functools import cached_property

# Proxyshop Imports
import src.text_layers as text_classes
from src.templates import NormalTemplate
from src.constants import con
from src.settings import cfg
import src.helpers as psd


class <TemplateName> (NormalTemplate):
    """
     * Notes about my template here.
     * Created by <YourName>
    """
    template_suffix = "<Suffix>"

    # OPTIONAL
    @property
    def property_name(self):
        # Overwrite a property with a new value
        return some_value

    # OPTIONAL
    @cached_property
    def cached_property_name(self):
        # Cached properties are saved to memory the first time they are accessed
        # Can improve execution time for properties like layer objects
        return psd.getLayer('Some layer name')
    
    # OPTIONAL
    def __init__ (self, layout):
        # DO STUFF
        super().__init__(layout)
    
    # OPTIONAL
    def enable_frame_layers (self):
        # DO STUFF
        super().enable_frame_layers()

    # OPTIONAL
    def basic_text_layers (self):
        # DO STUFF
        super().basic_text_layers()

    # OPTIONAL
    def rules_text_and_pt_layers (self):
        # DO STUFF
        super().rules_text_and_pt_layers()
  • <YourName> - Replace this with your creator name
  • <TemplateName> - The name identifying your template, example: MysticalArchiveTemplate
  • <Suffix> - The suffix that will be placed inside parenthesis at the end of cards rendered with this template, example: Mystical Tutor (Mystical Archive).png Lets talk about the structure of your code:
  • The code at the top of the file are your imports. If you are an advanced user and want to build in your own module such as your own "helpers.py" script in your plugin folder, you can add your own imports here as well. In a separate guide I'll talk about some of the useful imports included with Proxyshop that can help you supercharge custom templates.
  • class TemplateName (SuperTemplate): — This is your templates class name, ex: MysticalArchiveTemplate. In parenthesis is the super template. Your template can adopt the features of another template, in most cases this will be NormalTemplate. If your PSD is structured identically to the NormalTemplate (normal.psd) you won't have to write any special code at all! If you are making a highly customized template with a lot of special code, you might instead use BaseTemplate which only provides the most basic functionality, or StarterTemplate which provides a little bit more functionality. There's also NormalVectorTemplate which allows you to build templates around vector shape layers with auto-blending mechanics, and DynamicVectorTemplate which adds multi-modal capabilities like a template that can handle normal, MDFC, and Transform cards all with the same PSD file!
  • OPTIONAL: @property and @cached_property — This is the lifeblood of custom templates. Because of improvements to Proxyshop in version 1.2.0 most of the important customization can now be handled by simply changing a few properties. For example you can change a layer property if you want to use unconventional layer names, or you can change toggle properties like is_legendary to disable legendary crown functionality.
  • OPTIONAL: __init__ — This method will execute before anything else in your template code (when the class initializes). You probably won't need this method, but a common use case is for overwriting user settings. For example if your template has a small text box and you want to always remove reminder text, you could add the line cfg.remove_reminder = Trueto this method before super is called. You could also overwrite global constants in the con object (more advanced).
  • OPTIONAL: enable_frame_layers — This method enables the layers that contain the imagery of the card frame, such as the pinlines, the twins (name and title boxes), and the background. If you don't include this function, it will just enable the layers according to your super template (NormalTemplate in this case). You only need this if your PSD is structured differently than your extended template and requires specialized code.
  • OPTIONAL: basic_text_layers — This method governs basic text layers such as name, type line, mana cost. You likely won't need to touch this unless you're an advanced user.
  • OPTIONAL: rules_text_and_pt_layers — This method governs rules text and PT text. Again, you likely won't need to touch this unless you're an advanced user.

Lets take a look at one of my custom template classes as an example:

class KaldheimTemplate (NormalTemplate):
    """Kaldheim legendary showcase."""
    template_suffix = "Kaldheim"

    @property
    def is_nyx(self) -> bool:
        return False

    @property
    def is_legendary(self) -> bool:
        return False

    @property
    def twins_layer(self) -> Optional[ArtLayer]:
        return

    @property
    def background_layer(self) -> Optional[ArtLayer]:
        return

    @cached_property
    def pt_layer(self) -> Optional[ArtLayer]:
        if "Vehicle" in self.layout.type_line:
            return psd.getLayer("Vehicle", con.layers.PT_BOX)
        return psd.getLayer(self.twins, con.layers.PT_BOX)

    @cached_property
    def pinlines_layer(self) -> Optional[ArtLayer]:
        if self.is_land:
            return psd.getLayer(self.pinlines, con.layers.LAND_PINLINES_TEXTBOX)
        if "Vehicle" in self.layout.type_line:
            return psd.getLayer("Vehicle", con.layers.PINLINES_TEXTBOX)
        return psd.getLayer(self.pinlines, con.layers.PINLINES_TEXTBOX)

My Kaldheim template doesn't have legendary crowns, so is_legendary is disabled, it doesn't support Nyx backgrounds so is_nyx is disabled. The background_layer and twins_layer properties return none because the Kaldheim template doesn't have seperate background or title/type bars. I've made adjustments to the pt_layer and pinlines_layer properties to support layers for vehicle cards. Most of the modifications I made were doable by just changing properties of the template! How easy is that?

Your Plugin Manifest

The last thing you need is a JSON file inside your plugin folder that tells Proxyshop how to load in your templates. This is the easy part, in your plugin folder create a file called manifest.json, open that file in your favorite text editor and paste the following text, then fill it with the correct information:

{
    "normal": {
        "Template Name": {
            "class": "TemplateClass"
            "file": "template-file-name.psd"
            "id": "efjoi5mkj6DSRGeeGOOGLE-ID"
        },
        "Another Template Name": {
            "class": "AnotherTemplateClass"
            "file": "another-template-file-name.psd"
            "id": "another_efjoi5mkj6DSRGeeGOOGLE-ID"
        },
    }
}
  • normal — This is a section that represents a card type, and items in this block is a template that supports this type. Some cards have unique card types that Proxyshop needs to do extra lifting for, this first section maps these templates to the normal type which is what most people like to make templates for as it covers the most cards possible. Other types include saga, planeswalker, mdfc-front, mdfc-back, transform-front, transform-back, leveler, mutate, adventure, etc. You can add another block after the last comma, and structure it the same way but replace this with a different card type and build in templates for that type.
  • Template Name — The name of your template that will be displayed in the GUI, example: Mystical Archive
  • class — The name of this template's class in your templates.py module, ex: MysticalArchiveTemplate
  • file — The name of this template's PSD file in your plugin's templates folder, ex: mystical-archive.psd
  • id — The file ID of the PSD file hosted on Google Drive. This is optional, but required if you want other users to be able to update your template using the built in Updater. Upload your PSD file to Google Drive, then to grab the ID right click the file on Google Drive, hit share, copy the long ID string in the middle of the link and paste it in the "id" field in your manifest.

Adding Preview Images

You may notice in the Proxyshop GUI there is a panel to the right that displays a preview image for each template you click on, to include preview images for your plugin's templates, follow these steps:

  • Render a card image using your template.
  • Open the image in Photoshop, go to Edit > Resize Image, set the width to 600 px, it will automatically adjust the height proportionally.
  • Once the image is resized, save the image according to the name of the TEMPLATE CLASS in your templates.py file. So if the image was rendered using the class MyCoolTemplate, name it MyCoolTemplate.jpg
  • Make sure to save it as a JPEG with the extension jpg. It MUST be this extension.
  • Repeat the above steps for all of your template classes.
  • Create a folder in your plugin named img and put these preview images in there. All done!

Packaging Your Plugin

Now that your plugin is complete, you can package your files in a zip and share it with others! Here's how to structure your zip file:

# Main folder of your Plugin
~/Proxyshop/plugins/CreatorName/
┠───── templates.py
┠───── manifest.json
┗───── README.txt [OPTIONAL]

# Folder for image previews
~/Proxyshop/plugins/CreatorName/img/
┠───── MyCoolTemplate.jpg
┗───── AnotherCoolTemplate.jpg

# Folder for your PSD templates
~/Proxyshop/plugins/CreatorName/templates/
┠───── template-1.psd
┠───── template-2.psd
┗───── template-3.psd

Alternatively, I recommend creating a Github repository that represents your plugin folder, have all the files in it EXCEPT the PSD files. Feel free to include an empty templates folder in that repository, but store the PSD files on your Google Drive so users can download your plugin and the updater will be able to grab the PSD files from Google Drive. This is preferred as Github does not like storing large files like PSD files. Feel free to include a readme explaining your templates, or any additional functionality you might build in! That's about it, look out for my Advanced User guide for building more advanced functionality :) Happy crafting! Make sure to join our discord if you have any questions.

⚠️ **GitHub.com Fallback** ⚠️