Jump to content
  • Advertisement

Python Mock for "cube_add" Blender API

8Observer8

1914 views

Mock-objects are useful if you use external editor like VSCode or IDE like PyCharm, Eclipse and so on, because you can use DI (Dependency Injection) and you can debug your code logic with breakpoints.

For example, we want to add a cube on a scene:

bpy.ops.mesh.primitive_cube_add()

You can read about this API function in the documentation: primitive_cube_add

  • Create a work folder with the name: mock-object-for-primitive_cube_add-api
  • Open Blender and safe project in the work folder
  • Open "Scripting" tab in Blender from the top menu
  • Open your favourite Python editor. I use VSCode. You can read about how to use VSCode with Python here Python in Visual Studio Code
  • Create a file with the name "main.py" in you favourite Python editor. This file must be placed in the "mock-object-for-primitive_cube_add-api" folder
  • Write in the "main.py":
print("hello from blender")

You can run this code from command line terminal or from VSCode internal terminal. Press in VSCode "Ctrl+`" and enter command:
 

python main.py

You will see in the console terminal this message:

Quote

"hello from blender"

If you opened "Scripting" tab in Blender you will see an ability to open Python script in Blender.

  • Click on the "Open" Button in Blender Script editor inside Blender
  • Choose the "main.py" file and click the "Open Text Block" button
  • Open the Blender console terminal. For this you need to select in the main menu of Blender "Window" and select "Toggle System Console"
  • Run the "main.py" script from Blender. For this you need to place your mouse pointer on text area and press "Alt+P" button
  • You will see this message in the Blender console terminal:
Quote

"hello from blender"


If you will change a code in an external editor like VSCode you need to reload in the Blender text editor. For this you need to press the "Alt+R+R" button

You need to add only one file: "main.py" to the Blender text editor. Another files you need place in the work directory: "mock-object-for-primitive_cube_add-api"

  • Copy this code to the "main.py" file:

main.py

import bpy
import sys
import os
 
# Get a path to the directory with .blend file
# There are the scripts in this directory
dir = os.path.dirname(bpy.data.filepath)
 
# Is the directory in the list with included
# directories? If no, include the directory
if not dir in sys.path:
    sys.path.append(dir)
 
import object3d_service
 
# Reload module. It is necessary if you use
# external editor like VSCode
# For reloading module you need to press in
# Blender: Alt + R + R
import importlib
importlib.reload(object3d_service)
 
# Note. You do not need to open all scripts in Blender,
# you need only this script
 
from object3d_service import Object3DService
 
def main():
    objectService = Object3DService()
    objectService.create_cube()
 
if __name__ == "__main__":
    main()

This is another files that you need to copy to the work directory:

test_blender_service.py

import unittest
from unittest.mock import MagicMock
 
from object3d_service import Object3DService
 
class BlenderServiceTest(unittest.TestCase):
 
    def test_myTest(self):
        # Arrange
        object3DService = Object3DService()
        object3DService.blender_api.create_cube = MagicMock("create_cube")
        
        # Act
        object3DService.create_cube()
 
        # Assert
        object3DService.blender_api.create_cube.assert_called_once()

object3d_service.py

from blender_api import BlenderAPI
 
class Object3DService:
 
    def __init__(self):
        self.blender_api = BlenderAPI()
 
    def create_cube(self):
        self.blender_api.create_cube()

blender_api.py

import bpy
 
class BlenderAPI:
 
    def create_cube(self):
        bpy.ops.mesh.primitive_cube_add()

Delete a default cube from the scene. Now you can reload Blender code editor ("Alt+R+R") and run the code ("Alt + P"). You will see that a new code will be created:

mock-object-primitive_cube_add.png.da9a7661d432d254d8cafce2764f9476.png

You can set breakpoints in "main.py" because there are mock-object for Blender API. And you can run unit tests using this command:
 

python -m unittest


You will see that unit test are passed.



3 Comments


Recommended Comments

Excellent blog topic choice. I enjoyed your WebGL experiments and will certainly be watching this. My interest lies in the data export side instead of runtime modeler tools. I get as far as geometry (verts, uvs, normals, indices) but when moving to get animation data out (weights, deform bones, keyframes), it gets a bit more complicated. 

As for this entry, pretty cool setup.

Edited by GoliathForge

Share this comment


Link to comment
2 hours ago, Anthony Constantinou - CEO - CWM FX said:

Good topic. Though I am not a python dev, but can say that PyCharm is based on Intellij which is very slow in heart.

Thank you! But I work on a laptop and I do not have an enough free space. I have the VSCode lightweight editor for Python, HTML/CSS/TypeScript. And I use VS IDE for C# development for Unity, OpenTK/OpenGL3/WinForms/WPF.

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By buffalo974
      Hi,
      Hi, i saw examples for pytmx with pygame but nothing for pyglet on python 3. except those two lines:
      Load with pyglet images (experimental):
      from pytmx.util_pyglet import pyglet_image_loader
      tmx_data = load_pygame('map.tmx')
      somebody can give me a little bit more ? do you have better tools to work on tmx files ?
    • By Onny98
      Hello, im trying to learn new techniques to implement in my Unity3D projects and i have been looking for a particular answer about the way Hollow Knight's creators made to give background sprites that feeling of depth. I was thinking about just blurring them out in ps but someone suggested me increasing saturation, white degrading and changing opacity. Im unsure what is exactly the way to recreate this and i would like to know aswell if there is a tool in Unity3D that can make this automatically. Thank you so much I will leave a image showing what i mean exactly.

    • By JohnElliott
      Greetings all.
      I'm a researcher from Portugal, with the multimedia department of CIEBA, and I'm currently writing my PhD thesis on videogame composers' expansion of knowledge into areas of sound design, implementation, programming, and soft skills.
      I come to ask for bibliography, books and articles, on the process of creating videogames, particularly about differences between indie and AAA production (I am focusing on indie).
      So far I've used O'Donnell's "Developer's Dilemma", a great read I suggest to all, as well as my own experiences, but I am in need of more sources.
      Thanks in advance!
    • By ChocolatePancakes
      I have this weird glitch: whenever I am pressing the D-pad on the 360 controller, the A-button also lights up. The A-button returns a 1 for positive and 0 for negative. It returns a 1 whenever I am pressing the 4 directions of the D-pad. Does anyone know how to solve this? By the way, I am using Linux Mint, Python 3.6.8, and Pygame 1.9.6
      The source code for the class is below:
      # Xbox 360 Controller class class xpad: def __init__(self, pygameJoystick, deadzone): self.ID = pygameJoystick.get_id() pygameJoystick.init() self.deadzone = deadzone # Xbox 360 Controller Name self.name = pygameJoystick.get_name() # Thumbsticks self.left_thumb = ( self.dead( pygameJoystick.get_axis(0) ), self.dead( pygameJoystick.get_axis(1) ) ) self.right_thumb = ( self.dead( pygameJoystick.get_axis(3) ), self.dead( pygameJoystick.get_axis(4) ) ) # Triggers self.left_trig = pygameJoystick.get_axis(2) self.right_trig = pygameJoystick.get_axis(5) # Buttons self.A = pygameJoystick.get_button(0) self.B = pygameJoystick.get_button(1) self.X = pygameJoystick.get_button(2) self.Y = pygameJoystick.get_button(3) self.LB = pygameJoystick.get_button(4) self.RB = pygameJoystick.get_button(5) self.Back = pygameJoystick.get_button(6) self.Start = pygameJoystick.get_button(7) # The center button self.Guide = pygameJoystick.get_button(8) # When you click the thumbsticks self.left_stick = pygameJoystick.get_button(9) self.right_stick = pygameJoystick.get_button(10) # The directional pad on the 360 controller self.dpad = pygameJoystick.get_hat(0) # Limits a particular axis # by returning its value if it is # within the set deadzone. Returns a # zero otherwise. def dead(self, axis): if ( abs(axis) >= self.deadzone ): return axis return 0 # Returns a string that can be sent over the network # and be easily interpreted by an Arduino. def serialized(self): packet = ("<%i,%s,%s,%s,%s>")%( self.ID, hex( int(256*( (self.left_thumb[0]+1)/2 )) ), hex( int(256*( (self.left_thumb[1]+1)/2 )) ), hex( int(256*( (self.right_thumb[0]+1)/2 )) ), hex( int(256*( (self.right_thumb[1]+1)/2 )) ) ) return packet Here is how you can visualize the problem:
      import pygame import xinput # Define some colors. BLACK = pygame.Color('black') WHITE = pygame.Color('white') # This is a simple class that will help us print to the screen. # It has nothing to do with the joysticks, just outputting the # information. class TextPrint(object): def __init__(self): self.reset() self.font = pygame.font.Font(None, 20) def tprint(self, screen, textString): textBitmap = self.font.render(textString, True, BLACK) screen.blit(textBitmap, (self.x, self.y)) self.y += self.line_height def reset(self): self.x = 10 self.y = 10 self.line_height = 15 def indent(self): self.x += 10 def unindent(self): self.x -= 10 pygame.init() # Set the width and height of the screen (width, height). screen = pygame.display.set_mode((500, 700)) pygame.display.set_caption("ECU Robotics Xbox 360 Controller Layout") # Loop until the user clicks the close button. done = False # Used to manage how fast the screen updates. clock = pygame.time.Clock() # Initialize the joysticks. pygame.joystick.init() # Get ready to print. textPrint = TextPrint() # -------- Main Program Loop ----------- while not done: # # EVENT PROCESSING STEP # # Possible joystick actions: JOYAXISMOTION, JOYBALLMOTION, JOYBUTTONDOWN, # JOYBUTTONUP, JOYHATMOTION for event in pygame.event.get(): # User did something. if event.type == pygame.QUIT: # If user clicked close. done = True # Flag that we are done so we exit this loop. elif event.type == pygame.JOYBUTTONDOWN: print("Joystick button pressed.") elif event.type == pygame.JOYBUTTONUP: print("Joystick button released.") # # DRAWING STEP # # First, clear the screen to white. Don't put other drawing commands # above this, or they will be erased with this command. screen.fill(WHITE) textPrint.reset() # Initialize the connected Xbox controller xpad = xinput.xpad(pygame.joystick.Joystick(0), 0.25) # Print the controller's name textPrint.tprint( screen, xpad.name ) textPrint.tprint(screen, '') # Begin main body... textPrint.indent() # Thumbsticks... textPrint.tprint( screen, ("Left Thumbstick (x, y): %.3f, %.3f")%( xpad.left_thumb[0], xpad.left_thumb[1] ) ) textPrint.tprint( screen, ("Right Thumbstick (x, y): %.3f, %.3f")%( xpad.right_thumb[0], xpad.right_thumb[1] ) ) textPrint.tprint(screen, '') # Triggers... textPrint.tprint( screen, ("Left Trigger: %.3f")%( xpad.left_trig ) ) textPrint.tprint( screen, ("Right Trigger: %.3f")%( xpad.right_trig ) ) textPrint.tprint(screen, '') # Buttons... textPrint.tprint(screen, ("A Button: %i")%(xpad.A)) textPrint.tprint(screen, ("B Button: %i")%(xpad.B)) textPrint.tprint(screen, ("X Button: %i")%(xpad.X)) textPrint.tprint(screen, ("Y Button: %i")%(xpad.Y)) textPrint.tprint(screen, ("Left Bumper: %i")%(xpad.LB)) textPrint.tprint(screen, ("Right Bumper: %i")%(xpad.RB)) textPrint.tprint(screen, ("Start Button: %i")%(xpad.Start)) textPrint.tprint(screen, ("Back Button: %i")%(xpad.Back)) textPrint.tprint(screen, ("Guide Button: %i")%(xpad.Guide)) textPrint.tprint(screen, ("Left Stick Click: %i")%(xpad.left_stick)) textPrint.tprint(screen, ("Right Stick Click: %i")%(xpad.right_stick)) textPrint.tprint(screen, ("D-Pad: %i, %i")%(xpad.dpad[0], xpad.dpad[1])) # Serialized packet... textPrint.tprint(screen, '') textPrint.tprint(screen, 'Format: <ID, J1.x, J1.y, J2.x, J2.y>') textPrint.tprint(screen, ("%s")%(xpad.serialized())) # # ALL CODE TO DRAW SHOULD GO ABOVE THIS COMMENT # # Go ahead and update the screen with what we've drawn. pygame.display.flip() # Limit to 20 frames per second. clock.tick(60) # Close the window and quit. # If you forget this line, the program will 'hang' # on exit if running from IDLE. pygame.quit()  
    • By leePerak
      Hi guys,
       Check this new free Android game I just released today in Play Store - Grazomvity.  Specially designed to be played one handed while standing in train, bus etc.   
       https://play.google.com/store/apps/details?id=com.adriasys.grazomvity
       There are no make-believed, over-hyped, fabricated comments and ratings from friends or family. YOU be the FIRST to rate and comment.
       As a bonus, the winner gets free temporary upgrade and eventually can see the entire game source code.
      Your feedback is much appreciated.
      Thanks guys,
      Lee



  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!