How can I create a seamless backdrop with python in Blender? - python

I want to create a seamless backdrop for my blender project. It's done in a script and I want to add the backdrop to that script.
The problem is that I don't figure how to extrude a plane only in an edge so I can later bevel it and make it seamless. It's really easy to do with the GUI but I don't know how to do it in scripting.
I'm trying a few things, but for now I only have this code (which is obviously unfinished and not very well done):
bpy.ops.mesh.primitive_plane_add(radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0), layers=(True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
plane2 = bpy.data.objects['Plane']
dims = plane2.dimensions
plane2.dimensions = 100, 70, 35
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='EDGE')
bpy.context.scene.objects[0].data.edges[0]
me = bpy.context.object.data
"""
# Get a BMesh representation
bm = bmesh.new() # create an empty BMesh
bm.from_mesh(me) # fill it in from a Mesh
# Modify the BMesh, can do anything here...
for e in bm.edges:
e.co.x += 1.0
"""
bpy.context.tool_settings.mesh_select_mode = (False, True, False)
bpy.ops.mesh.extrude_region_move(MESH_OT_extrude_region={"mirror":False}, TRANSFORM_OT_translate={"value":(0, 0, 88.1553), "constraint_axis":(False, False, True), "constraint_orientation":'GLOBAL', "mirror":False, "proportional":'DISABLED', "proportional_edit_falloff":'SMOOTH', "proportional_size":1, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "gpencil_strokes":False, "texture_space":False, "remove_on_cancel":False, "release_confirm":False, "use_accurate":False})
bpy.ops.object.editmode_toggle()
I want the backdrop to look like this, in case it helps to understand better the goal:
https://www.youtube.com/watch?v=Ycz1wQY_7KI

As you only need two faces for the backdrop, I would make the mesh from a python list. Then add a bevel modifier to round out the back corner.
import bpy
from bpy_extras.object_utils import object_data_add
from mathutils import Vector
verts = [
Vector(( 50,-35, 0)),
Vector(( 50, 35, 0)),
Vector((-50, 35, 0)),
Vector((-50,-35, 0)),
Vector((-50, 35, 35)),
Vector(( 50, 35, 35)),
]
faces = [[2,3,0,1], [5,4,2,1]]
mesh = bpy.data.meshes.new(name="Backdrop")
mesh.from_pydata(verts, [], faces)
object_data_add(bpy.context, mesh)
backdrop = bpy.context.object
bpy.ops.object.shade_smooth()
bev_mod = backdrop.modifiers.new('bevel', 'BEVEL')
bev_mod.width = 12
bev_mod.segments = 5
mat = bpy.data.materials.new('back_mat')
if bpy.app.version[1] < 80:
mat.diffuse_color = [1,1,1] # white
else:
# 2.80 needs alpha value in colour
mat.diffuse_color = [1,1,1,1]
backdrop.active_material = mat

Related

blender 2.92 python overriding

I have seen quite a few questions regarding this issue, and I have not been able to wrap my head around it.
So here is a concrete example.
I create a cube and in edit mode I add loopcuts. No sweat!
I copy the code from the info window and I try it out in a script and I get the ominous RuntimeError: Operator bpy.ops.mesh.loopcut_slide.poll() expected a view3d region & editmesh error message.
I understand now that it is about first giving python the right context by overriding. And alas, I do not know how to do it!
Here is the code:
import bpy
import os
os.system("cls")
# remove the default cube...
objs = bpy.data.objects
for obj in objs:
if obj.name.find("Cube") == 0:
bpy.data.objects.remove(obj, do_unlink=True)
# add a cube!
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=True, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
# do something to it to be sure that we have it...
bpy.data.objects['Cube'].scale[0] = 10
# THE CODE BELOW GIVES THE RuntimeError: Operator bpy.ops.mesh.loopcut_slide.poll() expected a view3d region & editmesh error message.
# What is the override code I have to use to fix it????????
bpy.ops.mesh.loopcut_slide(MESH_OT_loopcut={"number_cuts":16, "smoothness":0, "falloff":'INVERSE_SQUARE', "object_index":0, "edge_index":4, "mesh_select_mode_init":(True, False, False)}, TRANSFORM_OT_edge_slide={"value":0, "single_side":False, "use_even":False, "flipped":False, "use_clamp":True, "mirror":True, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "correct_uv":True, "release_confirm":False, "use_accurate":False})
import bpy
import os
os.system("cls")
objs = bpy.data.objects
for obj in objs:
if obj.name.find("Cube") == 0:
bpy.data.objects.remove(obj, do_unlink=True)
# add a cube!
bpy.ops.mesh.primitive_cube_add(size=2, enter_editmode=True, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1))
# do something to it to be sure that we have it...
bpy.data.objects['Cube'].scale[0] = 10
for area in bpy.context.screen.areas:
if area.type == 'VIEW_3D':
for region in area.regions:
if region.type == 'WINDOW':
override = {'area': area, 'region': region, 'edit_object':bpy.context.edit_object}
bpy.ops.mesh.loopcut_slide(override,MESH_OT_loopcut={"number_cuts":16, "smoothness":0, "falloff":'INVERSE_SQUARE', "object_index":0, "edge_index":4, "mesh_select_mode_init":(True, False, False)}, TRANSFORM_OT_edge_slide={"value":0, "single_side":False, "use_even":False, "flipped":False, "use_clamp":True, "mirror":True, "snap":False, "snap_target":'CLOSEST', "snap_point":(0, 0, 0), "snap_align":False, "snap_normal":(0, 0, 0), "correct_uv":True, "release_confirm":False, "use_accurate":False})

Change maya global preferences via Pymel

I'm trying to make a simple button that wiil set global animation preferences in maya.
The script is supposed to change the animation preferences of DEFAULT IN TANGENT , DEFAULT OUT TANGENT and DEFAULT TANGENT WEIGHT.
so far this is what I got:
import pymel.core as pm
def setBlockingPrefs():
pm.keyTangent(edit = True, g = True, itt = 'clamped')
pm.keyTangent(edit = True, g = True, ott = 'step')
pm.keyTangent(edit = True, g = True, weightedTangents = 1)
def setSpliningPrefs():
pm.keyTangent(edit = True, g = True, itt = 'auto')
pm.keyTangent(edit = True, g = True, ott = 'auto')
pm.keyTangent(edit = True, g = True, weightedTangents = 1)
I get no error with this code, but then, i get nothing at all, the preferences are not changed.
What am I missing?
Thank you

How do I make a rectangle change color when a ball hits it the first and second time and then make it disappear on the third hit?

I am making a brick breaker game and right now the ball hits a brick (a rectangle) and it breaks on the first hit. I want it so that the first time the ball hits the brick, it changes to light red and the second to red and then on the third hit, it breaks.
#brick list
rectInfo = [rX, rY]
allRectInfo = [rectInfo[:] for i in range(numRects)]
allRectInfo[0] = [45, 20, True]
allRectInfo[1] = [45, 60, True]
allRectInfo[2] = [45, 100, True]
allRectInfo[3] = [45, 140, True]
allRectInfo[4] = [195, 20, True]
allRectInfo[5] = [195, 60, True]
allRectInfo[6] = [195, 100, True]
allRectInfo[7] = [195, 140, True]
allRectInfo[8] = [345, 20, True]
allRectInfo[9] = [345, 100, True]
allRectInfo[10] = [345, 60, True]
allRectInfo[11] = [345, 140, True]
allRectInfo[12] = [495, 140, True]
allRectInfo[13] = [495, 100, True]
allRectInfo[14] = [495, 60, True]
allRectInfo[15] = [495, 20, True]
allRectInfo[16] = [645, 20, True]
allRectInfo[17] = [645, 60, True]
allRectInfo[18] = [645, 100, True]
allRectInfo[19] = [645, 140, True]
#when the ball hits the bricks
for i in range (numRects):
if allRectInfo[i][2] == True:
rect (allRectInfo[i][0], allRectInfo[i][1], rectW, rectH)
if allRectInfo[i][1]-25 <= y <= allRectInfo[i][1]+74 and
allRectInfo[i][0]-5.5 <= x <= allRectInfo[i][0]+150:
incry = incry * (-1)
allRectInfo[i][2] = allRectInfo[i][2] - 1
score = score + 1
hits = hits + 1
Instead of an boolean attribute, the 3rd element of the allRectInfo[i] tuple, has to be the number of hits, initialized by 0:
allRectInfo[0] = [45, 20, 0]
allRectInfo[1] = [45, 60, 0]
allRectInfo[2] = [45, 100, 0]
# [...]
Increment the hit counter when a hit is identified:
for i in range (numRects):
if # [...]
# [...]
allRectInfo[i][2] += 1
Define an array of colors and use the color dependent on the brick:
brickcolor1 = # [...]
brickcolor2 = (255, 128, 128)
brickcolor3 = (255, 0, 0)
brickcolors = [brickcolor1, brickcolor2, brickcolor3]
currentcolor = brickcolors[allRectInfo[i][2]]
The brick breaks if a brick has reached the hit limit. Skip the brick in this case:
for i in range (numRects):
if allRectInfo[i][2] < 3:
# [...]
All together that may look as follows:
for i in range (numRects):
if allRectInfo[i][2] < 3:
currentcolor = brickcolors[allRectInfo[i][2]]
fill(*currentcolor)
rect(allRectInfo[i][0], allRectInfo[i][1], rectW, rectH)
if allRectInfo[i][1]-25 <= y <= allRectInfo[i][1]+74 and allRectInfo[i][0]-5.5 <= x <= allRectInfo[i][0]+150:
incry = incry * (-1)
score = score + 1
allRectInfo[i][2] += 1

PyQt5 QWebview Refresh Issue

I use a QWebEngineView and set it's HTML. When I display it, the HTML is always smaller than the total area I gave the QWebEngineView. Right clicking the page and clicking "Reload" always fixes the problem, but I don't want end users to click "Reload" every time. I've already searched online for a way to reload, and found:
my_qwebengineview.page().action(QWebEnginePage.Reload).trigger()
This isn't resizing it the same way a right click does, though.
If it helps with reading the code, it's displaying a Plotly plot as an HTML page (long story).
I create the widget it sits on with the code below:
grid_layout = QGridLayout()
logo = QLabel()
logo_pixmap = QPixmap('logo.jpg')
logo.setPixmap(logo_pixmap)
grid_layout.addWidget(logo, 0, 0, 2, 6)
grid_layout.addItem(QSpacerItem(500, 50, QSizePolicy.Minimum, QSizePolicy.Expanding), 0, 6)
graph_label = QLabel('Graph')
graph_names_combobox = QComboBox()
grid_layout.addWidget(graph_label, 0, 7, 1, 1)
grid_layout.addWidget(self.graph_names_combobox, 0, 8, 1, 4)
self.graph_widget = QStackedWidget()
grid_layout.addWidget(self.graph_widget, 2, 1, 10, 12)
self.graph_window_widget = QWidget()
self.graph_window_widget.setLayout(grid_layout)
Later on I use:
temp_webpage = QWebEngineView()
temp_webpage.setHTML(*graph html goes here*)
self.graph_widget.addWidget(temp_webpage)
# In the course of the full program, a lot of graphs are added, so reloading
# them all here.
for child in self.graph_widget.children()
if type(child) == QWebEngineView:
child.page().action(QWebEnginePage.Reload).trigger()
I've tried using the reload code right after I set it the stacked widget to the selected graph, but like I said, it's having the same result.
Is there a way to make the page do the same thing as right click -> reload?
Edit
In response to a comment, the code I use to generate the html comes from this (data is a pandas DataFrame that I load somewhere else from SQL, but I've included a toy DataFrame here):
path = QDir.current().filePath('plotly-latest.min.js')
local = QUrl.fromLocalFile(path).toString()
data_frame = DataFrame(index=(range(2)),columns=('Date', 'SKUs', 'Lines', 'Orders', 'Eaches'))
data_frame.loc[0:1,'Date'] = (datetime.date(1999, 1, 1), datetime.date(1999, 1, 2))
data_frame.loc[0:1,'SKUs'] = (12, 13)
data_frame.loc[0:1,'Lines'] = (14, 15)
data_frame.loc[0:1,'Orders'] = (16, 17)
data_frame.loc[0:1,'Eaches'] = (18, 19)
trace_lines = Bar(x=data_frame['Date'], y=data_frame['Lines'], name='Lines', visible=True)
trace_orders = Bar(x=data_frame['Date'], y=data_frame['Orders'], name='Orders', visible=False)
trace_eaches = Bar(x=data_frame['Date'], y=data_frame['Eaches'], name='Eaches', visible=False)
trace_SKUs = Bar(x=data_frame['Date'], y=data_frame['SKUs'], name='SKUs', visible=False)
data = [trace_lines, trace_orders, trace_eaches, trace_SKUs]
lines_title = "%s Lines" % name
orders_title = "%s Orders" % name
eaches_title = "%s Eaches" % name
skus_title = "%s SKUs" % name
update_menus = list([dict(active=0,
buttons=list([dict(label='Lines',
method='update',
args=[{'visible': [True, False, False, False]},
{'title': lines_title}]),
dict(label='Orders',
method='update',
args=[{'visible': [False, True, False, False]},
{'title': orders_title}]),
dict(label='Eaches',
method='update',
args=[{'visible': [False, False, True, False]},
{'title': eaches_title}]),
dict(label='SKUs',
method='update',
args=[{'visible': [False, False, False, True]},
{'title': skus_title}])]))])
layout = dict(title=lines_title, showlegend=False, updatemenus=update_menus, xaxis=dict(
rangeselector=dict(buttons=list([dict(count=1, label='1m', step='month', stepmode='backward'),
dict(count=6, label='6m', step='month', stepmode='backward'),
dict(step='all')])),
rangeslider=dict(),
type='date'
))
fig = dict(data=data, layout=layout)
raw_html = '<html><head><meta charset="utf-8" /><script src="{}"></script></head>'.format(local)
raw_html += '<body>'
raw_html += plot(fig, include_plotlyjs=False, output_type='div')
raw_html += '</body></html>'
return raw_html
A possible solution is to reload the page for the second time as shown below:
self.isFirst = True
temp_webpage.loadFinished.connect(self.onLoadFinished)
def onLoadFinished(self, ok):
if self.isFirst:
# self.sender().page().action(QWebEnginePage.Reload).trigger()
self.sender().reload()
self.isFirst = False

"Blinking" animation in Python with libavg

I'm working on a project using libavg and a series of RectNodes. What I'm trying to do is play an animation that makes each node light up white for 2,5 seconds, and then fades back. Every time you click one of the nodes, the same animation should happen for that specific node.
I'm using the AVGApp class, and a list with RectNode id and how many times they are supposed to light up, like (id1, 2)
def playAnim(self, animarr):
for i in range(0, len(animarr)):
i, count = animarr[i]
sid = "r" + str(i)
node = g_player.getElementByID(sid)
while count > 0:
self.blink(node)
count -= 1
return
and my code for blink:
def blink(self, node):
pos = node.pos
size = node.size
covernode = avg.RectNode(pos=pos, size=size, fillopacity=0,
parent = self._parentNode, fillcolor="ffffff",
color="000000", strokewidth=2)
self.animObj = LinearAnim(covernode, 'fillopacity', 1000, 0, 1)
self.animObj.start()
self.animObj = LinearAnim(covernode, 'fillopacity', 1000, 1, 0)
self.animObj.start()
covernode.unlink(True)
return
I'm calling it with:
def _enter(self):
(some other stuff here)
print "Let's get started!"
self.playAnim(self.animArr)
print "Your turn!"
Any help is greatly appreciated, the libavg Reference isn't helping me much.
The problem is that anim.start() is non-blocking.
Instead of only returning after the animation is finished, it will return immediately and the animation will be executed concurrently. This means at the same time your function starts two animations and unlinks the node that should be animated.
So instead you should use the end callbacks that can be given to the animations to trigger one step after another. The the blink function could then look like this:
def blink(self, node):
pos = node.pos
size = node.size
covernode = avg.RectNode(pos=pos, size=size, fillopacity=0,
parent = self._parentNode, fillcolor="ffffff",
color="000000", strokewidth=2)
fadeOutAnim = LinearAnim(covernode, 'fillopacity', 1000, 1, 0, False,
None, covernode.unlink)
fadInAnim= LinearAnim(covernode, 'fillopacity', 1000, 0, 1, False,
None, fadeOutAnim.start)
fadInAnim.start()
If you want the node to stay white for a certain amount of time you have to insert another step using either a WaitAnim or player.setTimeout().
(https://www.libavg.de/reference/current/player.html#libavg.avg.Player)
def blink(self, node):
pos = node.pos
size = node.size
covernode = avg.RectNode(pos=pos, size=size, fillopacity=0,
parent = self._parentNode, fillcolor="ffffff",
color="000000", strokewidth=2)
fadeOutAnim = LinearAnim(covernode, 'fillopacity', 1000, 1, 0, False,
None, covernode.unlink
)
fadInAnimObj = LinearAnim(covernode, 'fillopacity', 1000, 0, 1, False,
None, lambda:self.wait(500, fadeOutAnim.start)
)
fadInAnimObj.start()
def wait(self, time, end_cb):
avg.Player.get().setTimeout(time, end_cb)

Categories