I have a workaround to connect the transparency of a Metrial to the keyframe bar in Maya. I create a new Material press "s" for a keyframe got to the keyframe 10, set the transparency to 0 and press again "s".
So you are able to fade the transparency beteween the 1 and 10 keyframe.
I want to script this in python and I have no idea how to do this.
here is what I've made. I tested it with basic Maya materials, but it should work for any material wich has a transparency attribute (note that it won't work for surface shaders as the transparency attribute is called outTransparency, but you could fix it by changing the function so you could also pass it the attribute name).
I got the getShaderFrom (obj) function in this topic and just converted it in Python (it's in MEL).
I made it as simple and detailled as possible but it could probably be optimized. Anyway, hope this answered your question!
from maya import cmds
def getShaderFrom (obj):
'''
List all materials for the given object
'''
# List the shapes of the given object
shapes = cmds.ls (obj, objectsOnly = 1, dagObjects = 1, shapes = 1)
# List the shading engines connected to the shapes
shadingEngines = cmds.listConnections (shapes, type = "shadingEngine")
# List the materails connected to the shading engines
rawMaterials = cmds.ls ((cmds.listConnections (shadingEngines)), materials = 1)
# Remove duplicated occurences of materials
materials = []
for mat in rawMaterials:
if mat not in materials:
materials.append(mat)
return materials
def keyTransparency (startFrame, endFrame, material):
'''
Will key the transparency of
a given materials between
selected frames
'''
# Set transparency value at 1
cmds.setAttr (material + ".transparency", 1,1,1, type = "double3")
# Add a keyframe at last frame
cmds.setKeyframe (material, attribute = "transparency", time = endFrame, inTangentType = "linear", outTangentType = "linear")
# Add a keyframe at first frame
cmds.setKeyframe (material, attribute = "transparency", time = startFrame, inTangentType = "linear", outTangentType = "linear")
# Set transparency value at 0
cmds.setAttr (material + ".transparency", 0,0,0, type = "double3")
def doKeyTransparencyForMaterials (startTime, endTime):
'''
Main function
Call functions: 'getShaderFrom', 'keyTransparency'
'''
if ((not isinstance(startTime, int)) or (not isinstance(endTime, int))):
cmds.error ("You must provide start and end frame numbers (int).")
# List selected objects
selection = cmds.ls (selection = 1)
if len(selection) == 0:
cmds.error ("Nothing is selected.")
# List all materials
for obj in selection:
materials = getShaderFrom (obj)
# Key transparency
for mat in materials:
keyTransparency (startTime, endTime, mat)
print "Transparency has been successfully keyed for: " + (", ".join(materials)) + "\n",
doKeyTransparencyForMaterials (1, 10)
This is what I did.
import maya.cmds as cmds
######### Delete all existing Shader ##############
cmds.delete (cmds.ls(type='shadingDependNode'))
cmds.delete (cmds.ls(type='shadingEngine'))
######### Delete all existing Shader ##############
######### Delete all Keyframes between 0 and 20 ##############
cmds.setKeyframe( 'lambert1', t='1', at='transparency' )
cmds.cutKey ( t=(0,20) )
######### Delete all Keyframes ##############
#create Blau shader
Blau = cmds.shadingNode('phong', asShader=True, n='Blau', )
cmds.setAttr(Blau + '.color', 0.163,0.284,0.5)
#create KeyFrame Transparency ###Blau####
cmds.setKeyframe( 'Blau', t='1', at='transparency' )
cmds.setAttr ( 'Blau.transparency', 1,1,1, type = "double3")
cmds.setKeyframe( 'Blau', t='15', at='transparency' )
######### update Time to 2 and back to 0 ##############
cmds.currentTime ( 2, update=True )
cmds.currentTime ( 0, update=True )
######### update Time to 2 and back to 0 ##############
The setKeyFrame for lambert1 line is just to make the script running if no Key exists before.
I adjust the current time because your material is shown transparent if you apply it the first time.
PS: Blau is Blue in German
Related
In python I want to select one object and change repeatUV .
The code work for all objects and not for one specific object
import maya.cmds as cmds
import pymel.core as pmc
def UVTILECHANGE():
selected = cmds.ls(sl=True)
textField_obj_name = cmds.textField( 'textField_action_name',query=True,text=True )
textField_obj = cmds.textField( 'textField_action',query=True,text=True )
#print textField_obj
textField_obj2 = cmds.textField( 'textField_action2',query=True,text=True )
if textField_obj_name==selected[0]:
array = cmds.ls(type="place2dTexture")
#SELECT ALL PLACE2DTECTURE
#I WANT JUST FOR selected[0]
#print len(array)
i=0
while (i <= len(array)):
print
cmds.setAttr(str(array[i])+ ".repeatU", float(textField_obj))
cmds.setAttr(str(array[i])+ ".repeatV", float(textField_obj2))
i=i+1
def MakeWindow():
if (pmc.window('flattenVertwindow', exists=True)):
pmc.deleteUI('flattenVertwindow')
pmc.window('flattenVertwindow', title="TEST",widthHeight=(200,100))
pmc.columnLayout(adjustableColumn=True)
#pmc.text(label='select axis')
cmds.text("Choisir nom de l'objet")
cmds.separator(h=10)
textField00 = cmds.textField( 'textField_action_name' )
cmds.separator(h=10)
cmds.text("Taille des Tiles")
cmds.separator(h=10)
textField01 = cmds.textField( 'textField_action' )
textField02 = cmds.textField( 'textField_action2' )
red= pmc.button(label='Change UV TILE ', command= 'UVTILECHANGE()')
pmc.showWindow('flattenVertwindow')
MakeWindow()
Im not sure what doesn't work, Ive improved your code with comments.
I hope it will help, it works here :
import maya.cmds as cmds
import pymel.core as pmc
# if you dont know classes, you can create a dict to store all main controls
UI_DIC = {}
# for function used in ui, you need *args because maya will try to parse a default True
# argument as last argument
def picker(*args):
sel = cmds.ls(sl=True)[0]
cmds.textField(UI_DIC['textField00'], edit=True, text=sel )
# use lower case for functions, you can use camel casing like maya : uvTilesChange
# but keep the first letter lower case as uppercase for the first is used for Class
def uv_tile_change(*args):
# we parse fields with the value captured in the dictionnary
textField_obj_name = cmds.textField(UI_DIC['textField00'], query=True, text=True )
textField_obj = cmds.textField(UI_DIC['textField01'], query=True, text=True )
textField_obj2 = cmds.textField(UI_DIC['textField02'], query=True, text=True )
# if there is nothing in the ui, dont execute anything
if not textField_obj_name:
return
# we are using few funcitons to find the place2dTexture of an object
shape_mesh = cmds.ls(textField_obj_name, dag=True, type='shape', ni=True)
if not shape_mesh:
# if the object doesnt exists, stop here
return
# we look for the shading engine of the object
hist = cmds.listHistory(shape_mesh, future=True)
sgs = cmds.ls(hist, type="shadingEngine")
# from the sgs, we look for the place2dTexture
place2dTextures = [i for i in cmds.listHistory(sgs) if cmds.ls(i, type='place2dTexture')]
# instead of a while, just use a python for loop
for p2t in place2dTextures:
cmds.setAttr(p2t + ".repeatU", float(textField_obj))
cmds.setAttr(p2t + ".repeatV", float(textField_obj2))
def make_window():
width = 180
height = 150
if (pmc.window('flattenVertwindow', exists=True)):
pmc.deleteUI('flattenVertwindow')
# sizeable works for me
pmc.window('flattenVertwindow', title="TEST",widthHeight=(width,height), sizeable=False)
main_layout = pmc.columnLayout(adjustableColumn=True)
# use the flag parent, it is more clear
pmc.text("Choisir nom de l'objet", parent=main_layout)
# choose either cmds or pymel, do not use both
pmc.separator(h=10, parent=main_layout)
# here is an example on how parent flag is useful
# we create a sub layout and parent it to the main one
# we parent all the controls to the sub layout
picker_layout = pmc.rowLayout(nc=3, parent=main_layout, width=width, ad1=True)
# we capture the name of the control on creation inside the dict
UI_DIC['textField00'] = pmc.textField(parent=picker_layout, w=width)
# take the habit to not put string in the command flag
pmc.button(label='<', command= picker, parent=picker_layout)
pmc.separator(h=10, parent=main_layout)
pmc.text("Taille des Tiles", parent=main_layout)
pmc.separator(h=10)
UI_DIC['textField01'] = pmc.textField(parent=main_layout)
UI_DIC['textField02'] = pmc.textField(parent=main_layout)
red= pmc.button(label='Change UV TILE ', command= uv_tile_change, parent=main_layout)
pmc.showWindow('flattenVertwindow')
make_window()
I'm writing a tool that will help make me make custom uvSets on a model that will be linked to ramps(no interpolation) that will be sent to vray_extraTex render elements. these will become mattes and used in the same way as vRay multi mattes. however, I am not able to link the ramp that is my texture for the vray_extraTex to the custom uvSet using pymel.
I can do all this manually in Maya but for some reason, I am missing something for pymel to link the UVs to the ramp. I am testing in a Maya scene with a pSphere that has two uvSets and the second set is active. This code has been stripped down a bit:
def main():
inclusiveSet = None
renderElements =[]
ramps = []
newChannels = ['TestA','TestB','TestC','TestD']
for i, channel in enumerate(newChannels):
modIndex = i % 3 # 0:Red, 1:Green, 2:Blue
shapeNode=pm.PyNode('pSphereShape1')
transformNode=shapeNode.getTransform()
if modIndex == 0: # the first channel in the new Render Element
# make an etex render element
eTexElement = pm.PyNode(pm.mel.eval('vrayAddRenderElement("ExtraTexElement")'))
eTexElement.vray_name_extratex.set('')
eTexElement.vray_explicit_name_extratex.set('empty_empty_empy')
renderElements.append(eTexElement)
# make a ramp
ramp = pm.shadingNode('ramp', asTexture=True, name='eTex_{}_ramp'.format(transformNode.name()))
ramps.append(ramp)
ramp.outColor.connect(eTexElement.vray_texture_extratex)
# make a place2dtexture
place2d = pm.shadingNode('place2dTexture', asUtility=True)
place2d.outUV.connect(ramp.uv)
place2d.translateFrameU.set(len(renderElements) - 1)
# link UVs to ramp
# NOT WORKING
indices = pm.polyUVSet(shapeNode.name(), query=True, allUVSetsIndices=True)
currentUVSet = pm.polyUVSet(shapeNode, query=True, currentUVSet=True )
for i in indices:
if currentUVSet == pm.getAttr("{}.uvSet[{}].uvSetName".format(shapeNode.name(), i)):
pm.uvLink(uvSet='{}.uvSet[{}].uvSetName'.format(shapeNode.name(), i) , texture=ramp)
explicit_name = eTexElement.vray_explicit_name_extratex.get()
nameTokens = explicit_name.split('_')
nameTokens[modIndex] = channel
explicit_name = '_'.join(nameTokens)
eTexElement.vray_explicit_name_extratex.set(explicit_name)
main()
I get no errors but when I check the UV Linking the ramps are still set to map1 uvSet and not the second set that was active.
I expected to see the ramps connected to the uvChooser node and linked to the second uvSet.
I realized while writing this post that maybe I need to attach the ramps to the shader that is assigned to the geo before I can uvlink them with python. I'll try and test that next
so after some more testing, I was able to get it to work after assigning the ramps to a new attr on the objects' shader and with a couple of minor fixes to the code I posted before. fixed:
def connectTexture(layeredTex, shapeNode):
shadingGrps = pm.listConnections(shapeNode,type='shadingEngine')
shaders = pm.ls(pm.listConnections(shadingGrps),materials=1)
shader = shaders[0]
pm.addAttr(shader, longName='eTexLayeredTex', usedAsColor=True, attributeType='float3' )
pm.addAttr(shader, longName='eTexLayeredTexR', attributeType='float', parent='eTexLayeredTex' )
pm.addAttr(shader, longName='eTexLayeredTexG', attributeType='float', parent='eTexLayeredTex' )
pm.addAttr(shader, longName='eTexLayeredTexB', attributeType='float', parent='eTexLayeredTex' )
layeredTex.outColor.connect(shader.eTexLayeredTex)
def main():
inclusiveSet=None
renderElements=[]
ramps=[]
newChannels = ['TestA','TestB','TestC','TestD']
shapeNode=pm.PyNode('pSphereShape1')
transformNode=shapeNode.getTransform()
# make a layeredTexture
layeredTexture = pm.shadingNode('layeredTexture', asTexture=True, name='eTex_{}_layeredTexture'.format(transformNode.name()))
layeredTexture.attr('inputs')[0].color.set(0,0,0)
layeredTexture.attr('inputs')[0].alpha.set(1)
layeredTexture.attr('inputs')[0].blendMode.set(1)
connectTexture(layeredTexture, shapeNode)
for i, channel in enumerate(newChannels):
modIndex = i % 3 # 0:Red, 1:Green, 2:Blue
if modIndex == 0: # the first channel in the new Render Element
# make an etex render element
eTexElement = pm.PyNode(pm.mel.eval('vrayAddRenderElement("ExtraTexElement")'))
eTexElement.vray_name_extratex.set('')
eTexElement.vray_explicit_name_extratex.set('empty_empty_empy')
renderElements.append(eTexElement)
# make a ramp
ramp = pm.shadingNode('ramp', asTexture=True, name='eTex_{}_ramp'.format(transformNode.name()))
ramps.append(ramp)
ramp.interpolation.set(0)
ramp.colorEntryList[0].position.set(0.0)
ramp.colorEntryList[1].position.set((1.0 / 3.0))
ramp.colorEntryList[2].position.set((2.0 / 3.0))
ramp.colorEntryList[0].color.set(1,0,0)
ramp.colorEntryList[1].color.set(0,1,0)
ramp.colorEntryList[2].color.set(0,0,1)
ramp.defaultColor.set(0,0,0)
ramp.outColor.connect(eTexElement.vray_texture_extratex)
ramp.outColor.connect( layeredTexture.attr( 'inputs[{}].color'.format( 1 + i // 3)))
# make a place2dtexture
place2d = pm.shadingNode('place2dTexture', asUtility=True)
place2d.outUV.connect(ramp.uv)
place2d.outUvFilterSize.connect(ramp.uvFilterSize)
place2d.wrapU.set(0)
place2d.wrapV.set(0)
place2d.translateFrameU.set(len(renderElements) - 1)
# link UVs to ramp
indices = pm.polyUVSet(shapeNode.name(), query=True, allUVSetsIndices=True)
currentUVSet = pm.polyUVSet(shapeNode, query=True, currentUVSet=True )[0]
for index in indices:
if currentUVSet == pm.getAttr('{}.uvSet[{}].uvSetName'.format(shapeNode.name(), index)):
pm.uvLink(uvSet='{}.uvSet[{}].uvSetName'.format(shapeNode.name(), index) , texture=ramp)
explicit_name = eTexElement.vray_explicit_name_extratex.get()
nameTokens = explicit_name.split('_')
nameTokens[modIndex] = channel
explicit_name = '_'.join(nameTokens)
eTexElement.vray_explicit_name_extratex.set(explicit_name)
main()
they key was connecting the ramps to a material assigned to the object. Strangely after the uvLinks are made you can delete the connection between the ramps and the material and the uvLinks still work correctly. I posted this in case someone comes across a similar problem
For the class below, I'm getting this exception:
AttributeError: LineIntensityProfileLogic instance has no attribute 'probeVolume'
How can I solve this issue? Thanks
class LineIntensityProfileLogic(ScriptedLoadableModuleLogic):
"""This class should implement all the actual
computation done by your module. The interface
should be such that other python code can import
this class and make use of the functionality without
requiring an instance of the Widget.
Uses ScriptedLoadableModuleLogic base class, available at:
https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/ScriptedLoadableModule.py
"""
def hasImageData(self,volumeNode):
"""This is a dummy logic method that
returns true if the passed in volume
node has valid image data
"""
if not volumeNode:
print('no volume node')
return False
if volumeNode.GetImageData() == None:
print('no image data')
return False
return True
def takeScreenshot(self,name,description,type=-1):
# show the message even if not taking a screen shot
self.delayDisplay(description)
if self.enableScreenshots == 0:
return
lm = slicer.app.layoutManager()
# switch on the type to get the requested window
widget = 0
if type == slicer.qMRMLScreenShotDialog.FullLayout:
# full layout
widget = lm.viewport()
elif type == slicer.qMRMLScreenShotDialog.ThreeD:
# just the 3D window
widget = lm.threeDWidget(0).threeDView()
elif type == slicer.qMRMLScreenShotDialog.Red:
# red slice window
widget = lm.sliceWidget("Red")
elif type == slicer.qMRMLScreenShotDialog.Yellow:
# yellow slice window
widget = lm.sliceWidget("Yellow")
elif type == slicer.qMRMLScreenShotDialog.Green:
# green slice window
widget = lm.sliceWidget("Green")
else:
# default to using the full window
widget = slicer.util.mainWindow()
# reset the type so that the node is set correctly
type = slicer.qMRMLScreenShotDialog.FullLayout
# grab and convert to vtk image data
qpixMap = qt.QPixmap().grabWidget(widget)
qimage = qpixMap.toImage()
imageData = vtk.vtkImageData()
slicer.qMRMLUtils().qImageToVtkImageData(qimage,imageData)
annotationLogic = slicer.modules.annotations.logic()
annotationLogic.CreateSnapShot(name, description, type, self.screenshotScaleFactor, imageData)
def run(self,volumeNode1,volumeNode2,rulerNode,enableScreenshots=0,screenshotScaleFactor=1):
"""
Run the actual algorithm
"""
print('LineIntensityProfileLogic run() called')
"""
1. get the list(s) of intensity samples along the ruler
2. set up quantitative layout
3. use the chart view to plot the intensity sampless
"""
"""
1. get the list of samples
"""
if not rulerNode or (not volumeNode1 and not volumeNode2):
print('Inputs are not initialized')
return
volumeSamples1 = None
volumeSamples2 = None
if volumeNode1:
volumeSamples1 = self.probeVolume(volumeNode1, rulerNode)
if volumeNode2:
volumeSamples2 = self.probeVolume(volumeNode2, rulerNode)
print('volumeSamples1 = '+str(volumeSamples1))
print('volumeSamples2 = '+str(volumeSamples2))
imageSamples = [volumeSamples1, volumeSamples2]
legendNames = [volumeNode1.GetName()+' - '+rulerNode.GetName(), volumeNode2.GetName+' - '+rulerNode.GetName()]
self.showChart(imageSamples, legendNames)
self.delayDisplay('Running the aglorithm')
self.enableScreenshots = enableScreenshots
self.screenshotScaleFactor = screenshotScaleFactor
self.takeScreenshot('LineIntensityProfile-Start','Start',-1)
return True
def probeVolume(self,volumeNode,rulerNode):
# get ruler endpoints coordinates in RAS
p0ras = rulerNode.GetPolyData().GetPoint(0)+(1,)
p1ras = rulerNode.GetPolyData().GetPoint(1)+(1,)
# Convert RAS to IJK coordinates of the vtkImageData
ras2ijk = vtk.vtkMatrix4x4()
volumeNode.GetRASToIJKMatrix(ras2ijk)
p0ijk = [int(round(c)) for c in ras2ijk.MultiplyPoint(p0ras)[:3]]
p1ijk = [int(round(c)) for c in ras2ijk.MultiplyPoint(p1ras)[:3]]
# Create VTK line that will be used for sampling
line = vtk.vtkLineSource()
line.SetResolution(100)
line.SetPoint1(p0ijk[0],p0ijk[1],p0ijk[2])
line.SetPoint2(p1ijk[0],p1ijk[1],p1ijk[2])
# Create VTK probe filter and sample the image
probe = vtk.vtkProbeFilter()
probe.SetInputConnection(line.GetOutputPort())
probe.SetSourceData(volumeNode.GetImageData())
probe.Update()
# return VTK array
return probe.GetOutput().GetPointData().GetArray('ImageScalars')
def showChart(self, samples, names):
print("Logic showing chart")
# Switch to a layout containing a chart viewer
lm = slicer.app.layoutManager()
lm.setLayout(slicer.vtkMRMLLayoutNode.SlicerLayoutFourUpQuantitativeView)
# Initialize double array MRML node for each sample list since this is ,
# what chart view MRML node needs
doubleArrays = []
for sample in samples:
arrayNode = slicer.mrmlScene.AddNode(slicer.vtkMRMLDoubleArrayNode())
array = arrayNode.GetArray()
nDataPoints = sample.GetNumberOfTuples()
array.SetNumberOfTuples(nDataPoints)
array.SetNumberOfComponents(3)
for i in range(nDataPoints):
array.SetComponent(i, 0, i)
array.SetComponent(i, 1, sample.GetTuple(i))
array.SetComponent(i, 2, 0)
doubleArrays.append(arrayNode)
# Get the chart view MRML node
cvNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLChartViewNode')
cvNodes.SetReferenceCount(cvNodes.GetReferenceCount()-1)
cvNodes.InitTraversal()
cvNode = cvNodes.GetNextItemAsObject()
# Create a new chart node
chartNode = slicer.mrmlScene.AddNode(slicer.vtkMRMLChartNode())
for pairs in zip(names, doubleArrays):
chartNode.AddArray(pairs[0], pairs[1], GetID())
cvNode.SetChartNodeID(chartNode.GetID())
return
You seem to be using 2 spaces for indentation.
But for the function - probeVolume , it is indented 4 spaces , which caused it to be inside function run , are you sure that indentation is correct?
Not just probeVolume , function - showChart is also indented 4 spaces,
I'm having some trouble rescaling video output of GStreamer to the dimension of the window the video is displayed in (retaining aspect ratio of the video). The problem is that I first need to preroll the video to be able to determine its dimensions by retrieving the negotiated caps, and then calculate the dimensions it needs to be displayed in to fit the window. Once I have prerolled the video and got the dimension caps, I cannot change the video's dimension anymore. Setting the new caps still results in the video being output in its original size. What must I do to solve this?
Just to be complete. In the current implementation I cannot render to an OpenGL texture which would have easily solved this problem because you could simply render output to the texture and scale it to fit the screen. I have to draw the output on a pygame surface, which needs to have the correct dimensions. pygame does offer functionality to scale its surfaces, but I think such an implementation (as I have now) is slower than retrieving the frames in their correct size directly from GStreamer (am I right?)
This is my code for loading and displaying the video (I omitted the main loop stuff):
def calcScaledRes(self, screen_res, image_res):
"""Calculate image size so it fits the screen
Args
screen_res (tuple) - Display window size/Resolution
image_res (tuple) - Image width and height
Returns
tuple - width and height of image scaled to window/screen
"""
rs = screen_res[0]/float(screen_res[1])
ri = image_res[0]/float(image_res[1])
if rs > ri:
return (int(image_res[0] * screen_res[1]/image_res[1]), screen_res[1])
else:
return (screen_res[0], int(image_res[1]*screen_res[0]/image_res[0]))
def load(self, vfile):
"""
Loads a videofile and makes it ready for playback
Arguments:
vfile -- the uri to the file to be played
"""
# Info required for color space conversion (YUV->RGB)
# masks are necessary for correct display on unix systems
_VIDEO_CAPS = ','.join([
'video/x-raw-rgb',
'red_mask=(int)0xff0000',
'green_mask=(int)0x00ff00',
'blue_mask=(int)0x0000ff'
])
self.caps = gst.Caps(_VIDEO_CAPS)
# Create videoplayer and load URI
self.player = gst.element_factory_make("playbin2", "player")
self.player.set_property("uri", vfile)
# Enable deinterlacing of video if necessary
self.player.props.flags |= (1 << 9)
# Reroute frame output to Python
self._videosink = gst.element_factory_make('appsink', 'videosink')
self._videosink.set_property('caps', self.caps)
self._videosink.set_property('async', True)
self._videosink.set_property('drop', True)
self._videosink.set_property('emit-signals', True)
self._videosink.connect('new-buffer', self.__handle_videoframe)
self.player.set_property('video-sink', self._videosink)
# Preroll movie to get dimension data
self.player.set_state(gst.STATE_PAUSED)
# If movie is loaded correctly, info about the clip should be available
if self.player.get_state(gst.CLOCK_TIME_NONE)[0] == gst.STATE_CHANGE_SUCCESS:
pads = self._videosink.pads()
for pad in pads:
caps = pad.get_negotiated_caps()[0]
self.vidsize = caps['width'], caps['height']
else:
raise exceptions.runtime_error("Failed to retrieve video size")
# Calculate size of video when fit to screen
self.scaledVideoSize = self.calcScaledRes((self.screen_width,self.screen_height), self.vidsize)
# Calculate the top left corner of the video (to later center it vertically on screen)
self.vidPos = ((self.screen_width - self.scaledVideoSize [0]) / 2, (self.screen_height - self.scaledVideoSize [1]) / 2)
# Add width and height info to video caps and reload caps
_VIDEO_CAPS += ", width={0}, heigh={1}".format(self.scaledVideoSize[0], self.scaledVideoSize[1])
self.caps = gst.Caps(_VIDEO_CAPS)
self._videosink.set_property('caps', self.caps) #??? not working, video still displayed in original size
def __handle_videoframe(self, appsink):
"""
Callback method for handling a video frame
Arguments:
appsink -- the sink to which gst supplies the frame (not used)
"""
buffer = self._videosink.emit('pull-buffer')
img = pygame.image.frombuffer(buffer.data, self.vidsize, "RGB")
# Upscale image to new surfuace if presented fullscreen
# Create the surface if it doesn't exist yet and keep rendering to this surface
# for future frames (should be faster)
if not hasattr(self,"destSurf"):
self.destSurf = pygame.transform.scale(img, self.destsize)
else:
pygame.transform.scale(img, self.destsize, self.destSurf)
self.screen.blit(self.destSurf, self.vidPos)
# Swap the buffers
pygame.display.flip()
# Increase frame counter
self.frameNo += 1
I'm pretty sure that your issue was (has it is very long time since you asked this question) that you never hooked up the bus to watch for messages that were emitted.
The code for this is usually something like this:
def some_function(self):
#code defining Gplayer (the pipeline)
#
# here
Gplayer.set_property('flags', self.GST_VIDEO|self.GST_AUDIO|self.GST_TEXT|self.GST_SOFT_VOLUME|self.GST_DEINTERLACE)
# more code
#
# finally
# Create the bus to listen for messages
bus = Gplayer.get_bus()
bus.add_signal_watch()
bus.enable_sync_message_emission()
bus.connect('message', self.OnBusMessage)
bus.connect('sync-message::element', self.OnSyncMessage)
# Listen for gstreamer bus messages
def OnBusMessage(self, bus, message):
t = message.type
if t == Gst.MessageType.ERROR:
pass
elif t == Gst.MessageType.EOS:
print ("End of Audio")
return True
def OnSyncMessage(self, bus, msg):
if msg.get_structure() is None:
return True
if message_name == 'prepare-window-handle':
imagesink = msg.src
imagesink.set_property('force-aspect-ratio', True)
imagesink.set_window_handle(self.panel1.GetHandle())
The key bit for your issue is setting up a call back for the sync-message and in that call-back, setting the property force-aspect-ratio to True.
This property ensures that the video fits the window it is being displayed in at all times.
Note the self.panel1.GetHandle() function names the panel in which you are displaying the video.
I appreciate that you will have moved on but hopefully this will help someone else trawling through the archives.
Well, while I don't consider myself an experienced programmer, especially when it comes to multimedia applications, I had to do this image viewer sort of a program that displays images fullscreen, and the images change when the users press <- or -> arrows.
The general idea was to make a listbox where all the images contained in a certain folder (imgs) are shown, and when someone does double click or hits RETURN a new, fullscreen frame is generated containing, at first, the image selected in the listbox but after the user hits the arrows the images change in conecutive order, just like in a regular image viewer.
At the beginning, when the first 15 - 20 images are generated, everything goes well, after that, although the program still works an intermediary, very unpleasant and highly unwanted, effect appears between image generations, where basically some images that got generated previously are displayed quickly on the screen and after this the right, consecutive image appears. On the first apparitions the effect is barelly noticeble, but after a while it takes longer and longer between generations.
Here's the code that runs when someone does double click on a listbox entry:
def lbclick(self, eve):
frm = wx.Frame(None, -1, '')
frm.ShowFullScreen(True)
self.sel = self.lstb.GetSelection() # getting the selection from the listbox
def pressk(eve):
keys = eve.GetKeyCode()
if keys == wx.WXK_LEFT:
self.sel = self.sel - 1
if self.sel < 0:
self.sel = len(self.chk) - 1
imgs() # invocking the function made for displaying fullscreen images when left arrow key is pressed
elif keys == wx.WXK_RIGHT:
self.sel = self.sel + 1
if self.sel > len(self.chk) - 1:
self.sel = 0
imgs() # doing the same for the right arrow
elif keys == wx.WXK_ESCAPE:
frm.Destroy()
eve.Skip()
frm.Bind(wx.EVT_CHAR_HOOK, pressk)
def imgs(): # building the function
imgsl = self.chk[self.sel]
itm = wx.Image(str('imgs/{0}'.format(imgsl)), wx.BITMAP_TYPE_JPEG).ConvertToBitmap() # obtaining the name of the image stored in the list self.chk
mar = itm.Size # Because not all images are landscaped I had to figure a method to rescale them after height dimension, which is common to all images
frsz = frm.GetSize()
marx = float(mar[0])
mary = float(mar[1])
val = frsz[1] / mary
vsize = int(mary * val)
hsize = int(marx * val)
panl = wx.Panel(frm, -1, size = (hsize, vsize), pos = (frsz[0] / 2 - hsize / 2, 0)) # making a panel container
panl.SetBackgroundColour('#000000')
imag = wx.Image(str('imgs/{0}'.format(imgsl)), wx.BITMAP_TYPE_JPEG).Scale(hsize, vsize, quality = wx.IMAGE_QUALITY_NORMAL).ConvertToBitmap()
def destr(eve): # unprofessionaly trying to destroy the panel container when a new image is generated hopeing the unvanted effect, with previous generated images will disappear. But it doesn't.
keycd = eve.GetKeyCode()
if keycd == wx.WXK_LEFT or keycd == wx.WXK_RIGHT:
try:
panl.Destroy()
except:
pass
eve.Skip()
panl.Bind(wx.EVT_CHAR_HOOK, destr) # the end of my futile tries
if vsize > hsize: # if the image is portrait instead of landscaped I have to put a black image as a container, otherwise in the background the previous image will remain, even if I use Refresh() on the container (the black bitmap named fundal)
intermed = wx.Image('./res/null.jpg', wx.BITMAP_TYPE_JPEG).Scale(frsz[0], frsz[1]).ConvertToBitmap()
fundal = wx.StaticBitmap(frm, 101, intermed)
stimag = wx.StaticBitmap(fundal, -1, imag, size = (hsize, vsize), pos = (frsz[0] / 2 - hsize / 2, 0))
fundal.Refresh()
stimag.SetToolTip(wx.ToolTip('Esc = exits fullscreen\n<- -> arrows = quick navigation'))
def destr(eve): # the same lame attempt to destroy the container in the portarit images situation
keycd = eve.GetKeyCode()
if keycd == wx.WXK_LEFT or keycd == wx.WXK_RIGHT:
try:
fundal.Destroy()
except:
pass
eve.Skip()
frm.Bind(wx.EVT_CHAR_HOOK, destr)
else: # the case when the images are landscape
stimag = wx.StaticBitmap(panl, -1, imag)
stimag.Refresh()
stimag.SetToolTip(wx.ToolTip('Esc = exits fullscreen\n<- -> arrows = quick navigation'))
imgs() # invocking the function imgs for the situation when someone does double click
frm.Center()
frm.Show(True)
Thanks for any advice in advance.
Added later:
The catch is I'm trying to do an autorun presentation for a DVD with lots of images on it. Anyway it's not necessarely to make the above piece of code work properly if there are any other options. I've already tried to use windows image viewer, but strangely enough it doesn't recognizes relative paths and when I do this
path = os.getcwd() # getting the path of the current working directory
sel = listbox.GetSelection() # geting the value of the current selection from the list box
imgname = memlist[sel] # retrieving the name of the images stored in a list, using the listbox selection, that uses as choices the same list
os.popen(str('rundll32.exe C:\WINDOWS\System32\shimgvw.dll,ImageView_Fullscreen {0}/imgsdir/{1}'.format(path, imgname))) # oepning the images up with bloody windows image viewer
it opens the images only when my program is on the hard disk, if it's on a CD / image drive it doesn't do anything.
There's an image viewer included with the wxPython demo package. I also wrote a really simple one here. Either one should help you in your journey. I suspect that you're not reusing your panels/frames and instead you're seeing old ones that were never properly destroyed.