I am asking a duplicate of this question, except that the answer submitted does not work for me. I would like to toggle the data_labels' "Wrap text in shape" button from the powerpoint UI via python-pptx. The linked answer ends up removing the data labels altogether instead. I am using the latest python-pptx version (0.6.18).
Here is a simple example to replicate:
from pptx import Presentation
from pptx.chart.data import ChartData
from pptx.enum.chart import XL_CHART_TYPE
from pptx.util import Cm
from pptx.text.text import TextFrame
# create presentation with 1 slide ------
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[5])
x = ['one','two','three', 'four']
y = [['diff',
[1,
2,
3,
4,
]],
]
specs = {
'height': Cm(7.82),
'width': Cm(14.8),
'left': Cm(2.53),
'top': Cm(5.72)}
data = ChartData()
data.categories = x
data.add_series('diff', [j for j in y[0][1]])
frame = slide.shapes.add_chart(
XL_CHART_TYPE.BAR_CLUSTERED, specs['left'], specs['top'],
specs['width'], specs['height'], data
)
plot = frame.chart.plots[0]
plot.has_data_labels = True
data_labels = plot.series[0].data_labels
dLbls = data_labels._element
# ---use its <c:txPr> child to create TextFrame object---
text_frame = TextFrame(dLbls.get_or_add_txPr(), None)
# ---turn off word-wrap in the usual way---
text_frame.wrap = False
prs.save('chart-01.pptx')
I believe the second to last line should be text_frame.word_wrap = False, not .wrap; that's my mistake on the earlier answer (now fixed).
Also change this line:
data_labels = plot.series[0].data_labels
to:
data_labels = plot.data_labels
And I think you'll get what you're looking for.
Related
I am using BalancedColumns to generate multiple column layout.
I am not sure, how to resolve an issue of the split happening of the Flowables across the column frames.
I have a heading and it's content. I don't want BalancedColumns to split the flowables in such a way that heading is part of one column and its content is part of another.
The content of the paragraph can split.
The basic python code:
from reportlab.platypus.flowables import BalancedColumns, CondPageBreak
from reportlab.platypus import BaseDocTemplate, Paragraph, Spacer, Frame, PageTemplate, PageBreak
from reportlab.lib.units import mm
from reportlab.lib.pagesizes import A4, LETTER
import os
from reportlab.lib.colors import HexColor
framewidth = A4[0] - 10*mm
frameheight = A4[1] - 20*mm
portrait_frame = Frame(5*mm, 10*mm, framewidth, frameheight,
leftPadding=0,
bottomPadding=0,
rightPadding=0,
topPadding=0,
id=0,
showBoundary=False )
pTemplate = PageTemplate(id=0,frames=[portrait_frame])
templates = [pTemplate]
pdfPath = os.path.abspath(os.path.join(os.path.dirname(__file__), 'balancedColTest.pdf'))
doc = BaseDocTemplate(pdfPath, pagesize=A4, rightMargin=2*mm, leftMargin=2*mm,topMargin=5*mm,bottomMargin=5*mm,showBoundary=0)
doc.addPageTemplates(templates)
# generate stories for balanced columns
story = []
minPadding = 2
for i in range(3):
fs = []
numOfFlowables = random.choice([2, 5, 6, 1, 3])
padding = minPadding + 5*i
for ii in range(numOfFlowables):
text = '<b> <font color="#77D179"> Heading </font></b> <br/>'
heading = Paragraph(text)
heading.keepWithNext = True
fs.append(heading)
fs.append(Paragraph("This is another text in new Para flowable."))
# numCols = 2 if i%2 == 0 else 3
numCols = 2
bCols = BalancedColumns(fs, nCols=numCols, spaceAfter=5*mm, vLinesStrokeColor=HexColor('#77D179'), vLinesStrokeWidth=0.5)
story.append(bCols)
doc.build(story)
Even if I try to use keepWithNext=True the flowables are not part of same frame.
If I use KeepTogether, the BalanceColumns takes the entire space of the page.
heading = Paragraph(text)
content = Paragraph("This is another text in the paragraph")
f= KeepTogether([heading, content])
fs.append(f)
Can anyone suggest a solution so that the heading and its underlying paragraph can remain in the same frame?
I already referred these posts here here, here and here. Please don't mark it as a duplicate.
I have a chart embedded inside the ppt like below
I wish to replace the axis headers from FY2021 HC to FY1918 HC. Similarly, FY2122 HC should be replaced with FY1718 HC.
How can I do this using python pptx? This chart is coming from embedded Excel though. Is there anyway to change it in ppt?
When I tried the below, it doesn't get the axis headers
text_runs = []
for shape in slide.shapes:
if not shape.has_text_frame:
continue
for paragraph in shape.text_frame.paragraphs:
for run in paragraph.runs:
text_runs.append(run.text)
when I did the below, I find the list of shape types from the specific slide. I wish to change only the chart headers. So, the screenshot shows only two charts that I have in my slide.
for slide in ip_ppt.slides:
for shape in slide.shapes:
print("id: %s, type: %s" % (shape.shape_id, shape.shape_type))
id: 24, type: TEXT_BOX (17)
id: 10242, type: TEXT_BOX (17)
id: 11306, type: TEXT_BOX (17)
id: 11, type: AUTO_SHAPE (1)
id: 5, type: TABLE (19)
id: 7, type: TABLE (19)
id: 19, type: AUTO_SHAPE (1)
id: 13, type: CHART (3)
id: 14, type: CHART (3)
When I try to access the shape using id, I am unable to as well
ip_ppt.slides[5].shapes[13].Chart
I also tried the code below
from pptx import chart
from pptx.chart.data import CategoryChartData
chart_data = CategoryChartData()
chart.datalabel = ['FY1918 HC', 'FY1718 HC']
Am new to python and pptx. Any solution on how to edit the embedded charts headers would really be useful. Help please
You can get to the category labels the following way:
from pptx import Presentation
from pptx.shapes.graphfrm import GraphicFrame
prs = Presentation('chart-01.pptx')
for slide in prs.slides:
for shape in slide.shapes:
print("slide: %s, id: %s, index: %s, type: %s" % (slide.slide_id, shape.shape_id, slide.shapes.index(shape), shape.shape_type))
if isinstance(shape, GraphicFrame) and shape.has_chart:
plotIndex = 0
for plot in shape.chart.plots:
catIndex = 0
for cat in plot.categories:
print(" plot %s, category %s, category label: %s" % (plotIndex, catIndex, cat.label))
catIndex += 1
plotIndex += 1
which will put out something like that:
slide: 256, id: 2, index: 0, type: PLACEHOLDER (14)
slide: 256, id: 3, index: 1, type: CHART (3)
plot 0, category 0, category label: East
plot 0, category 1, category label: West
plot 0, category 2, category label: Midwest
Unfortunately you can not change the category label, because it is stored in the embedded Excel. The only way to change those is to replace the chart data by using the chart.replace_data() method.
Recreating the ChartData object you need for the call to replace_data based on the existing chart is a bit more involved, but here is my go at it based on a chart that I created with the following code:
from pptx import Presentation
from pptx.chart.data import CategoryChartData
from pptx.enum.chart import XL_CHART_TYPE,XL_LABEL_POSITION
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
# create presentation with 1 slide ------
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[5])
# define chart data ---------------------
chart_data = CategoryChartData()
chart_data.categories = ['FY2021 HC', 'FY2122 HC']
chart_data.add_series('blue', (34.5, 31.5))
chart_data.add_series('orange', (74.1, 77.8))
chart_data.add_series('grey', (56.3, 57.3))
# add chart to slide --------------------
x, y, cx, cy = Inches(2), Inches(2), Inches(6), Inches(4.5)
gframe = slide.shapes.add_chart(
XL_CHART_TYPE.COLUMN_STACKED, x, y, cx, cy, chart_data
)
chart = gframe.chart
plot = chart.plots[0]
plot.has_data_labels = True
data_labels = plot.data_labels
data_labels.font.size = Pt(13)
data_labels.font.color.rgb = RGBColor(0x0A, 0x42, 0x80)
data_labels.position = XL_LABEL_POSITION.INSIDE_END
prs.save('chart-01.pptx')
and that looks almost identical to your picture in the question:
The following code will change the category labels in that chart:
from pptx import Presentation
from pptx.chart.data import CategoryChartData
from pptx.shapes.graphfrm import GraphicFrame
from pptx.enum.chart import XL_CHART_TYPE
from pptx.util import Inches
# read presentation from file
prs = Presentation('chart-01.pptx')
# find the first chart object in the presentation
slideIdx = 0
for slide in prs.slides:
for shape in slide.shapes:
if shape.has_chart:
chart = shape.chart
print("Chart of type %s found in slide[%s, id=%s] shape[%s, id=%s, type=%s]"
% (chart.chart_type, slideIdx, slide.slide_id,
slide.shapes.index(shape), shape.shape_id, shape.shape_type ))
break
slideIdx += 1
# create list with changed category names
categorie_map = { 'FY2021 HC': 'FY1918 HC', 'FY2122 HC': 'FY1718 HC' }
new_categories = list(categorie_map[c] for c in chart.plots[0].categories)
# build new chart data with new category names and old data values
new_chart_data = CategoryChartData()
new_chart_data.categories = new_categories
for series in chart.series:
new_chart_data.add_series(series.name,series.values)
# write the new chart data to the chart
chart.replace_data(new_chart_data)
# save everything in a new file
prs.save('chart-02.pptx')
The comments should explain what is going on and if you open chart-02.pptx with PowerPoint, this is what you will see:
Hope that solves your problem!
I am currently trying to write a program that will switch between two sets of data when different options are chosen from the select widget. I am trying to make this program as autonomous as possible so in the future when people update the data they don't have to modify the code at all and the updates will happen automatically.
Currently, my issue is that when I select 'White' I want the plot to update but nothing is happening.
The two data sets are currently a dict of lists, one labeled 'White_dict' and the other labeled 'black_dict' solely to represent the color of the material for the data (I know its kinda ironic).
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, Legend
from bokeh.models import Select
from bokeh.layouts import column
import pandas as pd
from plot_tools import add_hover
import itertools
from collections import defaultdict
bokeh_doc = curdoc()
material_types = pd.read_csv('data/material-information.csv')
df = pd.read_csv('data/Black_Materials_total_reflecatance.csv')
black_df = pd.read_csv('data/Black_Materials_total_reflecatance.csv')
white_df = pd.read_csv('data/SPIE18_white_all.csv')
names = []
w_names = []
black_dict = defaultdict(list)
white_dict = defaultdict(list)
for name, w_name in zip(df, white_df):
names.append(name)
w_names.append(w_name)
data = pd.read_csv('data/Black_Materials_total_reflecatance.csv', usecols = names)
w_data = pd.read_csv('data/SPIE18_white_all.csv', usecols = w_names)
for name, w_name in zip(names, w_names):
for i in range(0, 2250):
black_dict[name].append(data[name][i])
white_dict[w_name].append(w_data[w_name][i])
mySource = ColumnDataSource(data = black_dict)
#create total reflectance figure
total_fig = figure(plot_width = 650, plot_height = 350,
title = 'Total Reflectance',
x_axis_label = 'Wavelength(nm)', y_axis_label = 'Total Reflectance',
x_range = (250, 2500), y_range = (0,10),
title_location = 'above', sizing_mode = "scale_both",
toolbar_location = "below",
tools = "box_zoom, pan, wheel_zoom, save")
select = Select(title="Material Type", options=['Black', 'White'])
def update_plot(attr, old, new):
if new == 'White':
mySource.data = white_dict
else:
mySource.data = black_dict
for name, color in zip(mySource.data, Turbo256):
if name != 'nm':
total_fig.line('nm', name, line_width = .7, source = mySource, color = color)
select.on_change('value', update_plot)
bokeh_doc.add_root(total_fig)
bokeh_doc.add_root(select)
I'm currently using bokeh serve bokehWork.py to launch the server. If anyone has any idea on what I should fix it would be much appreciated! Thanks!
EDIT:
Adding data for Black_materials_total_reflectance.csv
Black Reflectance Data sample
Adding data for White_all.csv
White Reflectance Data sample
There are two main issues with your code:
You read the same files multiple times and you do a lot of work that Pandas and Bokeh can already do for you
(the main one) You do not take into account the fact that different CSV files have different column names
Here's a fixed version. Notice also the usage of the palette. With just Turbo256 you were getting almost the same color for all lines.
import pandas as pd
from bokeh.models import ColumnDataSource, Select
from bokeh.palettes import turbo
from bokeh.plotting import figure, curdoc
black_ds = ColumnDataSource(pd.read_csv('/home/p-himik/Downloads/Black_material_data - Sheet1.csv').set_index('nm'))
white_ds = ColumnDataSource(pd.read_csv('/home/p-himik/Downloads/White Materials Sample - Sheet1.csv').set_index('nm'))
total_fig = figure(plot_width=650, plot_height=350,
title='Total Reflectance',
x_axis_label='Wavelength(nm)', y_axis_label='Total Reflectance',
title_location='above', sizing_mode="scale_both",
toolbar_location="below",
tools="box_zoom, pan, wheel_zoom, save")
total_fig.x_range.range_padding = 0
total_fig.x_range.only_visible = True
total_fig.y_range.only_visible = True
palette = turbo(len(black_ds.data) + len(white_ds.data))
def plot_lines(ds, color_offset, visible):
renderers = []
for name, color in zip(ds.data, palette[color_offset:]):
if name != 'nm':
r = total_fig.line('nm', name, line_width=.7, color=color,
source=ds, visible=visible)
renderers.append(r)
return renderers
black_renderers = plot_lines(black_ds, 0, True)
white_renderers = plot_lines(white_ds, len(black_ds.data), False)
select = Select(title="Material Type", options=['Black', 'White'], value='Black')
def update_plot(attr, old, new):
wv = new == 'White'
for r in white_renderers:
r.visible = wv
for r in black_renderers:
r.visible = not wv
select.on_change('value', update_plot)
bokeh_doc = curdoc()
bokeh_doc.add_root(total_fig)
bokeh_doc.add_root(select)
Currently I am using the following code to define and replace
Placeholder (Text data) in existing Powerpoint presentations.
current_dir = os.path.dirname(os.path.realpath(__file__))
prs = Presentation(current_dir + '/test2.pptx')
slides = prs.slides
title_slide_layout = prs.slide_layouts[0]
slide = slides[0]
for shape in slide.placeholders:
print('%d %s' % (shape.placeholder_format.idx, shape.name))
title = slide.shapes.title
subtitle1 = slide.shapes.placeholders[0]
subtitle2 = slide.shapes.placeholders[10]
subtitle10 = slide.shapes.placeholders[11]
subtitle11 = slide.shapes.placeholders[12]
subtitle1.text = "1"
subtitle2.text = "2"
subtitle10.text = "3"
subtitle11.text = "4"
slide2 = slides[1]
for shape in slide2.placeholders:
print('%d %s' % (shape.placeholder_format.idx, shape.name))
subtitle3 = slide2.shapes.placeholders[10]
subtitle4 = slide2.shapes.placeholders[11]
subtitle5 = slide2.shapes.placeholders[12]
subtitle6 = slide2.shapes.placeholders[13]
subtitle12 = slide2.shapes.placeholders[16]
companydate = slide2.shapes.placeholders[14]
subtitle3.text = "1"
subtitle4.text = "2"
subtitle5.text = "3"
subtitle6.text = "4"
subtitle12.text = "40%"
companydate.text = "Insert company"
slide3 = slides[2]
for shape in slide3.placeholders:
print('%d %s' % (shape.placeholder_format.idx, shape.name))
subtitle7 = slide3.shapes.placeholders[10]
subtitle8 = slide3.shapes.placeholders[11]
subtitle9 = slide3.shapes.placeholders[12]
subtitle13 = slide3.shapes.placeholders[16]
companydate2 = slide3.shapes.placeholders[14]
subtitle7.text = "1"
subtitle8.text = "2"
subtitle9.text = "3"
subtitle13.text = "5x"
companydate2.text = "Insert Company"
slide4 = slides[3]
# for shape in slide4.placeholders:
#print('%d %s' % (shape.placeholder_format.idx, shape.name))
companydate3 = slide4.shapes.placeholders[14]
companydate3.text = "Insert Company"
"'Adapting Charts'"
from pptx.chart.data import ChartData
from pptx.enum.chart import XL_CHART_TYPE
from pptx.util import Pt
"Adapting Chart 1"
prs1 = Presentation(current_dir + '/output4.pptx')
slides1 = prs1.slides
chart1 = prs1.slides[0].chart
However, I am also running analytics in the background and I was wondering if it is possible to recognize (define) charts in the same presentation along with extracting and replacing the data in those charts. These chards are not embedded in the template.
As plotting charts with plotly or mathplotlib does not render a compliant image I am not able to use these , unless fully modified into the following format:Graph budget Click Correl
If yes, would it be possible to give concrete coding examples?
Thanks in advance!
Yes, it's possible to do that. The documentation will be your best source.
This will find the chart shapes:
for shape in slide.shapes:
if shape.has_chart:
chart = shape.chart
print('found a chart')
Data is extracted from the chart series(es):
for series in chart.series:
for value in series.values:
print(value)
Data is replaced by creating a new ChartData object and calling .replace_data() on the chart using that chart data object:
chart_data = ChartData(...)
... # add categories, series with values, etc.
chart.replace_data(chart_data)
http://python-pptx.readthedocs.io/en/latest/api/chart.html#pptx.chart.chart.Chart.replace_data
Adding to the answer by #scanny above, this worked for me:
if shape.name == 'Chart1':
chart = shape.chart
print(shape.name)
for series in chart.plots:
print(list(series.categories))
cat = list(series.categories)
for series in chart.series:
ser = series.values
print(series.values)
try:
# ---define new chart data---
chart_data = CategoryChartData()
chart_data.categories = cat
chart_data.add_series('category', df['column'])
# ---replace chart data---
chart.replace_data(chart_data)
except KeyError:
continue
Using the code above, you can print the categories and the series values, then replace them with your new values (while keeping category the same).
I added the KeyError exception because without it, you get a "rId3" error. From the forums it seems like there is some XML writing issue in writing to PPTX.
I'm trying to display further images (ct-scan) using numpy/vtk as describe in this sample code (http://www.vtk.org/Wiki/VTK/Examples/Python/vtkWithNumpy) but I don't get it and don't know why.
If someone could help me it would be kind.
Here's my code :
import vtk
import numpy as np
import os
import cv, cv2
import matplotlib.pyplot as plt
import PIL
import Image
DEBUG =True
directory="splitted_mri/"
w = 226
h = 186
d = 27
stack = np.zeros((w,d,h))
k=-1 #add the next picture in a differente level of depth/z-positions
for file in os.listdir(directory):
k+=1
img = directory + file
im = Image.open(img)
temp = np.asarray(im, dtype=int)
stack[:,k,:]= temp
print stack.shape
#~ plt.imshow(test)
#~ plt.show()
print type(stack[10,10,15])
res = np.amax(stack)
res1 = np.amin(stack)
print res, type(res)
print res1, type(res1)
#~ for (x,y,z), value in np.ndenumerate(stack):
#~ stack[x,y,z]=np.require(stack[x,y,z],dtype=np.int16)
#~ print type(stack[x,y,z])
stack = np.require(stack,dtype=np.uint16)
print stack.dtype
if DEBUG : print stack.shape
dataImporter = vtk.vtkImageImport()
data_string = stack.tostring()
dataImporter.CopyImportVoidPointer(data_string, len(data_string))
dataImporter.SetDataScalarTypeToUnsignedChar()
dataImporter.SetNumberOfScalarComponents(1)
dataImporter.SetDataExtent(0, w-1, 0, 1, 0, h-1)
dataImporter.SetWholeExtent(0, w-1, 0, 1, 0, h-1)
essai = raw_input()
alphaChannelFunc = vtk.vtkPiecewiseFunction()
colorFunc = vtk.vtkColorTransferFunction()
for i in range (0,255):
alphaChannelFunc.AddPoint(i, 0.9)
colorFunc.AddRGBPoint(i,i,i,i)
volumeProperty = vtk.vtkVolumeProperty()
volumeProperty.SetColor(colorFunc)
#volumeProperty.ShadeOn()
volumeProperty.SetScalarOpacity(alphaChannelFunc)
# This class describes how the volume is rendered (through ray tracing).
compositeFunction = vtk.vtkVolumeRayCastCompositeFunction()
# We can finally create our volume. We also have to specify the data for it, as well as how the data will be rendered.
volumeMapper = vtk.vtkVolumeRayCastMapper()
volumeMapper.SetVolumeRayCastFunction(compositeFunction)
volumeMapper.SetInputConnection(dataImporter.GetOutputPort())
# The class vtkVolume is used to pair the preaviusly declared volume as well as the properties to be used when rendering that volume.
volume = vtk.vtkVolume()
volume.SetMapper(volumeMapper)
volume.SetProperty(volumeProperty)
# With almost everything else ready, its time to initialize the renderer and window, as well as creating a method for exiting the application
renderer = vtk.vtkRenderer()
renderWin = vtk.vtkRenderWindow()
renderWin.AddRenderer(renderer)
renderInteractor = vtk.vtkRenderWindowInteractor()
renderInteractor.SetRenderWindow(renderWin)
# We add the volume to the renderer ...
renderer.AddVolume(volume)
# ... set background color to white ...
renderer.SetBackground(1, 1, 1)
# ... and set window size.
renderWin.SetSize(400, 400)
# A simple function to be called when the user decides to quit the application.
def exitCheck(obj, event):
if obj.GetEventPending() != 0:
obj.SetAbortRender(1)
# Tell the application to use the function as an exit check.
renderWin.AddObserver("AbortCheckEvent", exitCheck)
#to quit, press q
renderInteractor.Initialize()
# Because nothing will be rendered without any input, we order the first render manually before control is handed over to the main-loop.
renderWin.Render()
renderInteractor.Start()
I finally find out what was wrong
here's my new code
import vtk
import numpy as np
import os
import matplotlib.pyplot as plt
import PIL
import Image
DEBUG =False
directory="splitted_mri/"
l = []
k=0 #add the next picture in a differente level of depth/z-positions
for file in os.listdir(directory):
img = directory + file
if DEBUG : print img
l.append(img)
# the os.listdir function do not give the files in the right order
#so we need to sort them
l=sorted(l)
temp = Image.open(l[0])
h, w = temp.size
d = len(l)*5 #with our sample each images will be displayed 5times to get a better view
if DEBUG : print 'width, height, depth : ',w,h,d
stack = np.zeros((w,d,h),dtype=np.uint8)
for i in l:
im = Image.open(i)
temp = np.asarray(im, dtype=int)
for i in range(5):
stack[:,k+i,:]= temp
k+=5
#~ stack[:,k,:]= temp
#~ k+=1
if DEBUG :
res = np.amax(stack)
print 'max value',res
res1 = np.amin(stack)
print 'min value',res1
#convert the stack in the right dtype
stack = np.require(stack,dtype=np.uint8)
if DEBUG :#check if the image have not been modified
test = stack [:,0,:]
plt.imshow(test,cmap='gray')
plt.show()
if DEBUG : print 'stack shape & dtype' ,stack.shape,',',stack.dtype
dataImporter = vtk.vtkImageImport()
data_string = stack.tostring()
dataImporter.CopyImportVoidPointer(data_string, len(data_string))
dataImporter.SetDataScalarTypeToUnsignedChar()
dataImporter.SetNumberOfScalarComponents(1)
#vtk uses an array in the order : height, depth, width which is
#different of numpy (w,h,d)
w, d, h = stack.shape
dataImporter.SetDataExtent(0, h-1, 0, d-1, 0, w-1)
dataImporter.SetWholeExtent(0, h-1, 0, d-1, 0, w-1)
alphaChannelFunc = vtk.vtkPiecewiseFunction()
colorFunc = vtk.vtkColorTransferFunction()
for i in range(256):
alphaChannelFunc.AddPoint(i, 0.2)
colorFunc.AddRGBPoint(i,i/255.0,i/255.0,i/255.0)
# for our test sample, we set the black opacity to 0 (transparent) so as
#to see the sample
alphaChannelFunc.AddPoint(0, 0.0)
colorFunc.AddRGBPoint(0,0,0,0)
volumeProperty = vtk.vtkVolumeProperty()
volumeProperty.SetColor(colorFunc)
#volumeProperty.ShadeOn()
volumeProperty.SetScalarOpacity(alphaChannelFunc)
# This class describes how the volume is rendered (through ray tracing).
compositeFunction = vtk.vtkVolumeRayCastCompositeFunction()
# We can finally create our volume. We also have to specify the data for
# it, as well as how the data will be rendered.
volumeMapper = vtk.vtkVolumeRayCastMapper()
# function to reduce the spacing between each image
volumeMapper.SetMaximumImageSampleDistance(0.01)
volumeMapper.SetVolumeRayCastFunction(compositeFunction)
volumeMapper.SetInputConnection(dataImporter.GetOutputPort())
# The class vtkVolume is used to pair the preaviusly declared volume as
#well as the properties to be used when rendering that volume.
volume = vtk.vtkVolume()
volume.SetMapper(volumeMapper)
volume.SetProperty(volumeProperty)
# With almost everything else ready, its time to initialize the renderer and window,
# as well as creating a method for exiting the application
renderer = vtk.vtkRenderer()
renderWin = vtk.vtkRenderWindow()
renderWin.AddRenderer(renderer)
renderInteractor = vtk.vtkRenderWindowInteractor()
renderInteractor.SetRenderWindow(renderWin)
# We add the volume to the renderer ...
renderer.AddVolume(volume)
# ... set background color to white ...
renderer.SetBackground(1, 1, 1)
# ... and set window size.
renderWin.SetSize(550, 550)
renderWin.SetMultiSamples(4)
# A simple function to be called when the user decides to quit the application.
def exitCheck(obj, event):
if obj.GetEventPending() != 0:
obj.SetAbortRender(1)
# Tell the application to use the function as an exit check.
renderWin.AddObserver("AbortCheckEvent", exitCheck)
#to auit, press q
renderInteractor.Initialize()
# Because nothing will be rendered without any input, we order the first
# render manually before control is handed over to the main-loop.
renderWin.Render()
renderInteractor.Start()
If you are ok with a solution not using VTK, you could use Matplotlib imshow and interactive navigation with keys.
This tutorial shows how:
https://www.datacamp.com/community/tutorials/matplotlib-3d-volumetric-data
https://github.com/jni/mpl-volume-viewer
and here an implementation for viewing RTdose files:
https://github.com/pydicom/contrib-pydicom/pull/19
See also:
https://github.com/napari/napari