How to export a mesh's movement from Blender? - python

I have a simple UV Sphere in Blender which moves from point A to point B (I've set it up by pressing "I" button on location A, clicked on LocRot then clicked on LocRot on point B as well).
I'd like to export its movement.
For exporting a camera's movement I use
Blender Foundation\Blender\2.74\scripts\addons\io_anim_camera.py
(This is Cameras & Markers (.py) exporter.)
It's produces following output:
...
...
# new frame
scene.frame_set(1 + frame)
obj = cameras['Camera.003']
obj.location = -272.1265563964844, -155.54611206054688, -121.49121856689453 <-- here is the current coordinate of the camera, this is what I need for spheres
obj.scale = 0.9999998807907104, 0.9999998807907104, 0.9999999403953552
obj.rotation_euler = -1.6492990255355835, 0.00035389664117246866, 0.009288366883993149
obj.keyframe_insert('location')
obj.keyframe_insert('scale')
obj.keyframe_insert('rotation_euler')
data = obj.data
data.lens = 35.0
data.keyframe_insert('lens')
...
...
I'm looking for the same thing for meshes.
So here is the code of the basic exporter for cameras:
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
bl_info = {
"name": "Export Camera Animation",
"author": "Campbell Barton",
"version": (0, 1),
"blender": (2, 57, 0),
"location": "File > Export > Cameras & Markers (.py)",
"description": "Export Cameras & Markers (.py)",
"warning": "",
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
"Scripts/Import-Export/Camera_Animation",
"support": 'OFFICIAL',
"category": "Import-Export",
}
import bpy
def write_cameras(context, filepath, frame_start, frame_end, only_selected=False):
data_attrs = (
'lens',
'shift_x',
'shift_y',
'dof_distance',
'clip_start',
'clip_end',
'draw_size',
)
obj_attrs = (
'hide_render',
)
fw = open(filepath, 'w').write
scene = bpy.context.scene
cameras = []
for obj in scene.objects:
if only_selected and not obj.select:
continue
if obj.type != 'CAMERA':
continue
cameras.append((obj, obj.data))
frame_range = range(frame_start, frame_end + 1)
fw("import bpy\n"
"cameras = {}\n"
"scene = bpy.context.scene\n"
"frame = scene.frame_current - 1\n"
"\n")
for obj, obj_data in cameras:
fw("data = bpy.data.cameras.new(%r)\n" % obj.name)
for attr in data_attrs:
fw("data.%s = %s\n" % (attr, repr(getattr(obj_data, attr))))
fw("obj = bpy.data.objects.new(%r, data)\n" % obj.name)
for attr in obj_attrs:
fw("obj.%s = %s\n" % (attr, repr(getattr(obj, attr))))
fw("scene.objects.link(obj)\n")
fw("cameras[%r] = obj\n" % obj.name)
fw("\n")
for f in frame_range:
scene.frame_set(f)
fw("# new frame\n")
fw("scene.frame_set(%d + frame)\n" % f)
for obj, obj_data in cameras:
fw("obj = cameras['%s']\n" % obj.name)
matrix = obj.matrix_world.copy()
fw("obj.location = %r, %r, %r\n" % matrix.to_translation()[:])
fw("obj.scale = %r, %r, %r\n" % matrix.to_scale()[:])
fw("obj.rotation_euler = %r, %r, %r\n" % matrix.to_euler()[:])
fw("obj.keyframe_insert('location')\n")
fw("obj.keyframe_insert('scale')\n")
fw("obj.keyframe_insert('rotation_euler')\n")
# only key the angle
fw("data = obj.data\n")
fw("data.lens = %s\n" % obj_data.lens)
fw("data.keyframe_insert('lens')\n")
fw("\n")
# now markers
fw("# markers\n")
for marker in scene.timeline_markers:
fw("marker = scene.timeline_markers.new(%r)\n" % marker.name)
fw("marker.frame = %d + frame\n" % marker.frame)
# will fail if the cameras not selected
if marker.camera:
fw("marker.camera = cameras.get(%r)\n" % marker.camera.name)
fw("\n")
from bpy.props import StringProperty, IntProperty, BoolProperty
from bpy_extras.io_utils import ExportHelper
class CameraExporter(bpy.types.Operator, ExportHelper):
"""Save a python script which re-creates cameras and markers elsewhere"""
bl_idname = "export_animation.cameras"
bl_label = "Export Camera & Markers"
filename_ext = ".py"
filter_glob = StringProperty(default="*.py", options={'HIDDEN'})
frame_start = IntProperty(name="Start Frame",
description="Start frame for export",
default=1, min=1, max=300000)
frame_end = IntProperty(name="End Frame",
description="End frame for export",
default=250, min=1, max=300000)
only_selected = BoolProperty(name="Only Selected",
default=True)
def execute(self, context):
write_cameras(context, self.filepath, self.frame_start, self.frame_end, self.only_selected)
return {'FINISHED'}
def invoke(self, context, event):
self.frame_start = context.scene.frame_start
self.frame_end = context.scene.frame_end
wm = context.window_manager
wm.fileselect_add(self)
return {'RUNNING_MODAL'}
def menu_export(self, context):
import os
default_path = os.path.splitext(bpy.data.filepath)[0] + ".py"
self.layout.operator(CameraExporter.bl_idname, text="Cameras & Markers (.py)").filepath = default_path
def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_file_export.append(menu_export)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.INFO_MT_file_export.remove(menu_export)
if __name__ == "__main__":
register()
So is it possible? How to change this code to export the movement of a spheres?

Well, If you want to export the movement not as a Python script but in Text or in CSV format. Then look at the following code snippet.
CSV export can work for camera like this:
fw("# frame camera x y z qx qy qz qw\n")
for f in frame_range:
scene.frame_set(f)
for obj, obj_data in cameras:
matrix = obj.matrix_world.copy()
fw("%d," % f)
fw("%s," % obj.name)
fw("%r,%r,%r," % matrix.to_translation()[:])
fw("%r,%r,%r,%r" % matrix.to_quaternion()[:])
fw("\n")
from bpy.props import StringProperty, IntProperty, BoolProperty
from bpy_extras.io_utils import ExportHelper
class CameraExporter(bpy.types.Operator, ExportHelper):
"""Export camera trajectory to a file"""
bl_idname = "export_trajectory.cameras"
bl_label = "Export camera trajectory"
filename_ext = ".csv"
filter_glob = StringProperty(default="*.csv", options={'HIDDEN'})
In my opinion it should work similarly for any other objects in scene.
Not yet tried for other objects though!

Related

Making a 3D model in VTK solid instead of hollow inside

I'm trying to create 3D model of the skull using VTK [example]:(https://kitware.github.io/vtk-examples/site/Python/VisualizationAlgorithms/HeadBone/)
#!/usr/bin/env python
# noinspection PyUnresolvedReferences
import vtkmodules.vtkInteractionStyle
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import (
VTK_VERSION_NUMBER,
vtkVersion
)
from vtkmodules.vtkCommonDataModel import vtkMergePoints
from vtkmodules.vtkFiltersCore import (
vtkFlyingEdges3D,
vtkMarchingCubes
)
from vtkmodules.vtkFiltersModeling import vtkOutlineFilter
from vtkmodules.vtkIOImage import vtkMetaImageReader
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
def main():
# vtkFlyingEdges3D was introduced in VTK >= 8.2
use_flying_edges = vtk_version_ok(8, 2, 0)
file_name = get_program_parameters()
colors = vtkNamedColors()
# Create the RenderWindow, Renderer and Interactor.
ren = vtkRenderer()
ren_win = vtkRenderWindow()
ren_win.AddRenderer(ren)
iren = vtkRenderWindowInteractor()
iren.SetRenderWindow(ren_win)
# Create the pipeline.
reader = vtkMetaImageReader()
reader.SetFileName(file_name)
reader.Update()
locator = vtkMergePoints()
locator.SetDivisions(64, 64, 92)
locator.SetNumberOfPointsPerBucket(2)
locator.AutomaticOff()
if use_flying_edges:
try:
using_marching_cubes = False
iso = vtkFlyingEdges3D()
except AttributeError:
using_marching_cubes = True
iso = vtkMarchingCubes()
else:
using_marching_cubes = True
iso = vtkMarchingCubes()
iso.SetInputConnection(reader.GetOutputPort())
iso.ComputeGradientsOn()
iso.ComputeScalarsOff()
iso.SetValue(0, 1150)
if using_marching_cubes:
iso.SetLocator(locator)
iso_mapper = vtkPolyDataMapper()
iso_mapper.SetInputConnection(iso.GetOutputPort())
iso_mapper.ScalarVisibilityOff()
iso_actor = vtkActor()
iso_actor.SetMapper(iso_mapper)
iso_actor.GetProperty().SetColor(colors.GetColor3d('Ivory'))
outline = vtkOutlineFilter()
outline.SetInputConnection(reader.GetOutputPort())
outline_mapper = vtkPolyDataMapper()
outline_mapper.SetInputConnection(outline.GetOutputPort())
outline_actor = vtkActor()
outline_actor.SetMapper(outline_mapper)
# Add the actors to the renderer, set the background and size.
#
ren.AddActor(outline_actor)
ren.AddActor(iso_actor)
ren.SetBackground(colors.GetColor3d('SlateGray'))
ren.GetActiveCamera().SetFocalPoint(0, 0, 0)
ren.GetActiveCamera().SetPosition(0, -1, 0)
ren.GetActiveCamera().SetViewUp(0, 0, -1)
ren.ResetCamera()
ren.GetActiveCamera().Dolly(1.5)
ren.ResetCameraClippingRange()
ren_win.SetSize(640, 480)
ren_win.SetWindowName('HeadBone')
ren_win.Render()
iren.Start()
def get_program_parameters():
import argparse
description = 'Marching cubes surface of human bone.'
epilogue = '''
'''
parser = argparse.ArgumentParser(description=description, epilog=epilogue,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('filename', help='FullHead.mhd.')
args = parser.parse_args()
return args.filename
def vtk_version_ok(major, minor, build):
"""
Check the VTK version.
:param major: Major version.
:param minor: Minor version.
:param build: Build version.
:return: True if the requested VTK version is greater or equal to the actual VTK version.
"""
needed_version = 10000000000 * int(major) + 100000000 * int(minor) + int(build)
try:
vtk_version_number = VTK_VERSION_NUMBER
except AttributeError: # as error:
ver = vtkVersion()
vtk_version_number = 10000000000 * ver.GetVTKMajorVersion() + 100000000 * ver.GetVTKMinorVersion() \
+ ver.GetVTKBuildVersion()
if vtk_version_number >= needed_version:
return True
else:
return False
if __name__ == '__main__':
main()
It appears that model works just fine, but there is something that I want to know. The model of the skull works perfectly fine, but the model density is not solid (it hollow inside). It generates only the surface of the model.
I want to know how I can fill the gap in the surface to get the full solid model.
In this example, you are trying to extract the isosurface (hollow) from the data.
vtkFlyingEdges3D() and vtkMarchingCubes() algorithms will create contours based on the iso.SetValue(0, 1150) and extract the surface for you. If you want to keep it filled, remove the contouring functions, i'e, vtkFlyingEdges3D() and vtkMarchingCubes() from your script and use some image data mapper instead of vtkPolydataMapper, it will show the entire object.

I want to process a live stream from youtube with opencv

I am posting this while using a translation.
I would like to use a live stream on YouTube to action recognition.
Action recognition
https://github.com/felixchenfy/Realtime-Action-Recognition
Read Youtube live stream with opencv
How to read Youtube live stream using openCV python?
Youtube URL
https://www.youtube.com/watch?v=DjdUEyjx8GM
We have programmed it with reference to the above.The program runs normally for a few hours, but after 4 or 5 hours, I get an error saying that communication from the remote host has been lost.Checking the youtube video, the delivery does not seem to be interrupted.
A partial code for videocapture can be found here.
url = "https://www.youtube.com/watch?v=DjdUEyjx8GM"
video = pafy.new(url)
best = video.getbest(preftype="mp4")
class ReadFromVideo(object):
def __init__(self, video_path, sample_interval=1):
''' A video reader class for reading video frames from video.
Arguments:
video_path
sample_interval {int}: sample every kth image.
''' assert isinstance(sample_interval, int) and sample_interval >= 1
self.cnt_imgs = 0
self._is_stoped = False
self._video = cv2.VideoCapture(best.url)
ret, image = self._video.read()
ret, image2 = self._video2.read()
self._next_image = image
self._next_image2 = image2
self._sample_interval = sample_interval
self._fps = self.get_fps()
if not self._fps >= 0.0001:
import warnings
warnings.warn("Invalid fps of video: {}".format(video_path))
def has_image(self):
return self._next_image is not None
def get_curr_video_time(self):
return 1.0 / self._fps * self.cnt_imgs
def read_image(self):
image = self._next_image
image2 = self._next_image2
for i in range(self._sample_interval):
if self._video.isOpened():
ret, frame = self._video.read()
self._next_image = frame
ret, frame2 = self._video2.read()
self._next_image2 = frame2
else:
self._next_image = None
self._next_image2 = None
break
self.cnt_imgs += 1
return image, image2
def stop(self):
self._video.release()
self._video2.release()
self._is_stoped = True
def __del__(self):
if not self._is_stoped:
self.stop()
def get_fps(self):
# Find OpenCV version
(major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
# With webcam get(CV_CAP_PROP_FPS) does not work.
# Let's see for ourselves.
# Get video properties
if int(major_ver) < 3:
fps = self._video.get(cv2.cv.CV_CAP_PROP_FPS)
else:
fps = self._video.get(cv2.CAP_PROP_FPS)
return fps
#!/usr/bin/env python
# coding: utf-8
'''
Test action recognition on
(1) a video, (2) a folder of images, (3) or web camera.
Input:
model: model/trained_classifier.pickle
Output:
result video: output/${video_name}/video.avi
result skeleton: output/${video_name}/skeleton_res/XXXXX.txt
visualization by cv2.imshow() in img_displayer
'''
The entire source code is here
'''
Example of usage:
(1) Test on video file:
python src/s5_test.py \
--model_path model/trained_classifier.pickle \
--data_type video \
--data_path data_test/exercise.avi \
--output_folder output
(2) Test on a folder of images:
python src/s5_test.py \
--model_path model/trained_classifier.pickle \
--data_type folder \
--data_path data_test/apple/ \
--output_folder output
(3) Test on web camera:
python src/s5_test.py \
--model_path model/trained_classifier.pickle \
--data_type webcam \
--data_path 0 \
--output_folder output
if True: # Include project path
import sys
import os
ROOT = os.path.dirname(os.path.abspath(__file__))+"/../"
CURR_PATH = os.path.dirname(os.path.abspath(__file__))+"/"
sys.path.append(ROOT)
import utils.lib_images_io as lib_images_io
import utils.lib_plot as lib_plot
import utils.lib_commons as lib_commons
from utils.lib_openpose import SkeletonDetector
from utils.lib_tracker import Tracker
from utils.lib_tracker import Tracker
from utils.lib_classifier import ClassifierOnlineTest
from utils.lib_classifier import * # Import all sklearn related libraries
def par(path): # Pre-Append ROOT to the path if it's not absolute
return ROOT + path if (path and path[0] != "/") else path
# -- Command-line input
def get_command_line_arguments():
def parse_args():
parser = argparse.ArgumentParser(
description="Test action recognition on \n"
"(1) a video, (2) a folder of images, (3) or web camera.")
parser.add_argument("-m", "--model_path", required=False,
default='model/trained_classifier.pickle')
parser.add_argument("-t", "--data_type", required=False, default='webcam',
choices=["video", "folder", "webcam"])
parser.add_argument("-p", "--data_path", required=False, default="",
help="path to a video file, or images folder, or webcam. \n"
"For video and folder, the path should be "
"absolute or relative to this project's root. "
"For webcam, either input an index or device name. ")
parser.add_argument("-o", "--output_folder", required=False, default='output/',
help="Which folder to save result to.")
args = parser.parse_args()
return args
args = parse_args()
if args.data_type != "webcam" and args.data_path and args.data_path[0] != "/":
# If the path is not absolute, then its relative to the ROOT.
args.data_path = ROOT + args.data_path
return args
def get_dst_folder_name(src_data_type, src_data_path):
''' Compute a output folder name based on data_type and data_path.
The final output of this script looks like this:
DST_FOLDER/folder_name/vidoe.avi
DST_FOLDER/folder_name/skeletons/XXXXX.txt
'''
assert(src_data_type in ["video", "folder", "webcam"])
if src_data_type == "video": # /root/data/video.avi --> video
folder_name = str("video")
elif src_data_type == "folder": # /root/data/video/ --> video
folder_name = src_data_path.rstrip("/").split("/")[-1]
elif src_data_type == "webcam":
# month-day-hour-minute-seconds, e.g.: 02-26-15-51-12
folder_name = lib_commons.get_time_string()
return folder_name
args = get_command_line_arguments()
SRC_DATA_TYPE = args.data_type
SRC_DATA_PATH = args.data_path
SRC_MODEL_PATH = args.model_path
DST_FOLDER_NAME = get_dst_folder_name(SRC_DATA_TYPE, SRC_DATA_PATH)
# -- Settings
cfg_all = lib_commons.read_yaml(ROOT + "config/config.yaml")
cfg = cfg_all["s5_test.py"]
CLASSES = np.array(cfg_all["classes"])
SKELETON_FILENAME_FORMAT = cfg_all["skeleton_filename_format"]
# Action recognition: number of frames used to extract features.
WINDOW_SIZE = int(cfg_all["features"]["window_size"])
# Output folder
DST_FOLDER = args.output_folder + "/" + DST_FOLDER_NAME + "/"
DST_SKELETON_FOLDER_NAME = cfg["output"]["skeleton_folder_name"]
DST_VIDEO_NAME = cfg["output"]["video_name"]
# framerate of output video.avi
DST_VIDEO_FPS = float(cfg["output"]["video_fps"])
# writer
fmt = cv2.VideoWriter_fourcc(*'MP4V')
fps = 10.0
size = (800, 480)
writer = cv2.VideoWriter('outtest.mp4', fmt, fps, size)
backup_time = 3600 # sec
time = 0 # init
now = datetime.datetime.now()
s_now = now.strftime('%Y-%m-%d-%H-%M-%S')
back_up = cv2.VideoWriter(f'{s_now}.mp4',fmt, fps, size)
# Video setttings
# If data_type is webcam, set the max frame rate.
SRC_WEBCAM_MAX_FPS = float(cfg["settings"]["source"]
["webcam_max_framerate"])
# If data_type is video, set the sampling interval.
# For example, if it's 3, then the video will be read 3 times faster.
SRC_VIDEO_SAMPLE_INTERVAL = int(cfg["settings"]["source"]
["video_sample_interval"])
# Openpose settings
OPENPOSE_MODEL = cfg["settings"]["openpose"]["model"]
OPENPOSE_IMG_SIZE = cfg["settings"]["openpose"]["img_size"]
# Display settings
img_disp_desired_rows = int(cfg["settings"]["display"]["desired_rows"])
# -- Function
def select_images_loader(src_data_type, src_data_path):
if src_data_type == "video":
images_loader = lib_images_io.ReadFromVideo(
src_data_path,
sample_interval=SRC_VIDEO_SAMPLE_INTERVAL)
elif src_data_type == "folder":
images_loader = lib_images_io.ReadFromFolder(
folder_path=src_data_path)
elif src_data_type == "webcam":
if src_data_path == "":
webcam_idx = 0
elif src_data_path.isdigit():
webcam_idx = int(src_data_path)
else:
webcam_idx = src_data_path
images_loader = lib_images_io.ReadFromWebcam(
SRC_WEBCAM_MAX_FPS, webcam_idx)
return images_loader
class MultiPersonClassifier(object):
''' This is a wrapper around ClassifierOnlineTest
for recognizing actions of multiple people.
'''
def __init__(self, model_path, classes):
self.dict_id2clf = {} # human id -> classifier of this person
# Define a function for creating classifier for new people.
self._create_classifier = lambda human_id: ClassifierOnlineTest(
model_path, classes, WINDOW_SIZE, human_id)
def classify(self, dict_id2skeleton):
''' Classify the action type of each skeleton in dict_id2skeleton '''
# Clear people not in view
old_ids = set(self.dict_id2clf)
cur_ids = set(dict_id2skeleton)
humans_not_in_view = list(old_ids - cur_ids)
for human in humans_not_in_view:
del self.dict_id2clf[human]
# Predict each person's action
id2label = {}
for id, skeleton in dict_id2skeleton.items():
if id not in self.dict_id2clf: # add this new person
self.dict_id2clf[id] = self._create_classifier(id)
classifier = self.dict_id2clf[id]
id2label[id] = classifier.predict(skeleton) # predict label
# print("\n\nPredicting label for human{}".format(id))
# print(" skeleton: {}".format(skeleton))
# print(" label: {}".format(id2label[id]))
return id2label
def get_classifier(self, id):
''' Get the classifier based on the person id.
Arguments:
id {int or "min"}
'''
if len(self.dict_id2clf) == 0:
return None
if id == 'min':
id = min(self.dict_id2clf.keys())
return self.dict_id2clf[id]
def remove_skeletons_with_few_joints(skeletons):
''' Remove bad skeletons before sending to the tracker '''
good_skeletons = []
for skeleton in skeletons:
px = skeleton[2:2+13*2:2]
py = skeleton[3:2+13*2:2]
num_valid_joints = len([x for x in px if x != 0])
num_leg_joints = len([x for x in px[-6:] if x != 0])
total_size = max(py) - min(py)
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# IF JOINTS ARE MISSING, TRY CHANGING THESE VALUES:
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
if num_valid_joints >= 5 and total_size >= 0.1 and num_leg_joints >= 0:
# add this skeleton only when all requirements are satisfied
good_skeletons.append(skeleton)
return good_skeletons
def draw_result_img(img_disp, ith_img, humans, dict_id2skeleton,
skeleton_detector, multiperson_classifier):
''' Draw skeletons, labels, and prediction scores onto image for display '''
# Resize to a proper size for display
r, c = img_disp.shape[0:2]
desired_cols = int(1.0 * c * (img_disp_desired_rows / r))
img_disp = cv2.resize(img_disp,
dsize=(desired_cols, img_disp_desired_rows))
# Draw all people's skeleton
skeleton_detector.draw(img_disp, humans)
# Draw bounding box and label of each person
if len(dict_id2skeleton):
for id, label in dict_id2label.items():
skeleton = dict_id2skeleton[id]
# scale the y data back to original
skeleton[1::2] = skeleton[1::2] / scale_h
# print("Drawing skeleton: ", dict_id2skeleton[id], "with label:", label, ".")
lib_plot.draw_action_result(img_disp, id, skeleton, label)
# Add blank to the left for displaying prediction scores of each class
img_disp = lib_plot.add_white_region_to_left_of_image(img_disp)
cv2.putText(img_disp, "Frame:" + str(ith_img),
(20, 20), fontScale=1.5, fontFace=cv2.FONT_HERSHEY_PLAIN,
color=(0, 0, 0), thickness=2)
# Draw predicting score for only 1 person
if len(dict_id2skeleton):
classifier_of_a_person = multiperson_classifier.get_classifier(
id='min')
classifier_of_a_person.draw_scores_onto_image(img_disp)
return img_disp
# Time Display
cv2.putText(img2_disp, str(now_time.isoformat(timespec='seconds')),
(20, 20), fontScale=1.5, fontFace=cv2.FONT_HERSHEY_PLAIN,
color=(0, 0, 0), thickness=2)
# Draw predicting score for only 1 person
if len(dict2_id2skeleton):
classifier_of_a_person = multiperson_classifier.get_classifier(
id='min')
classifier_of_a_person.draw_scores_onto_image(img2_disp)
return img2_disp
def get_the_skeleton_data_to_save_to_disk(dict_id2skeleton):
'''
In each image, for each skeleton, save the:
human_id, label, and the skeleton positions of length 18*2.
So the total length per row is 2+36=38
'''
skels_to_save = []
for human_id in dict_id2skeleton.keys():
label = dict_id2label[human_id]
skeleton = dict_id2skeleton[human_id]
skels_to_save.append([[human_id, label] + skeleton.tolist()])
return skels_to_save
# -- Main
if __name__ == "__main__":
# -- Detector, tracker, classifier
skeleton_detector = SkeletonDetector(OPENPOSE_MODEL, OPENPOSE_IMG_SIZE)
multiperson_tracker = Tracker()
multiperson_classifier = MultiPersonClassifier(SRC_MODEL_PATH, CLASSES)
# -- Image reader and displayer
images_loader = select_images_loader(SRC_DATA_TYPE, SRC_DATA_PATH)
img_displayer = lib_images_io.ImageDisplayer()
# -- Init output
# output folder
os.makedirs(DST_FOLDER, exist_ok=True)
os.makedirs(DST_FOLDER + DST_SKELETON_FOLDER_NAME, exist_ok=True)
# -- Read images and process
try:
ith_img = -1
while images_loader.has_image():
# Timer
now = datetime.datetime.now()
now_time = now.time()
# -- Read image
#img = images_loader.read_image()
img = images_loader.read_image()
ith_img += 1
img_disp = img.copy()
# print(f"\nProcessing {ith_img}th image ...")
# -- Detect skeletons
humans = skeleton_detector.detect(img)
skeletons, scale_h = skeleton_detector.humans_to_skels_list(humans)
)
skeletons = remove_skeletons_with_few_joints(skeletons)
# -- Track people
dict_id2skeleton = multiperson_tracker.track(skeletons)
# -- Log & Recognize action of each person
log.append(str(now_time))
if len(dict_id2skeleton):
dict_id2label = multiperson_classifier.classify(
dict_id2skeleton)
# -- Draw
img_disp = draw_result_img(img_disp, ith_img, humans, dict_id2skeleton, skeleton_detector, multiperson_classifier)
# Print label of a person
if len(dict_id2skeleton):
min_id = min(dict_id2skeleton.keys())
# print("prediced label is :", dict_id2label[min_id])
# -- Display image, and write to video.avi
# video writer
if time >= backup_time*fps:
back_up.release()
now = datetime.datetime.now()
s_now = now.strftime('%Y-%m-%d-%H-%M-%S')
back_up = cv2.VideoWriter(f'{s_now}.mp4', fmt, fps, size)
time = 0
time += 1
writer.write(img_disp)
back_up.write(img_disp)
img_displayer.display(img_disp, wait_key_ms=1)
# -- Get skeleton data and save to file
skels_to_save = get_the_skeleton_data_to_save_to_disk(dict_id2skeleton)
lib_commons.save_listlist(DST_FOLDER + DST_SKELETON_FOLDER_NAME +SKELETON_FILENAME_FORMAT.format(ith_img),skels_to_save)
if cv2.waitKey(1) == 27:
break
finally:
writer.release()
back_up.release()
cv2.destroyAllWindows()
Thanks for your help.

"Could not convert string to float" when importing CSV to Blender

I have a script I'm trying to use (not mine) which is meant to import CSV data into Blender as a mesh.
I have two CSV files which are two parts of the same model, the script works fine for importing one of the CSV files but throws a "ValueError" when I try to import the second.
The error can be seen here:
As far as I know there's only one "-1" in the script, I've tried changing it, removing it, nothing works, I still get the same error.
I've also tried changing the encoding on the CVS files themselves but that didn't work either.
I don't know much about scripting and I'm at my wits end, any help would be greatly appreciated.
The script I'm using is here:
https://github.com/sbobovyc/GameTools/blob/master/Blender/import_pix.py
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
import bpy
import csv
import mathutils
from bpy_extras.io_utils import unpack_list, unpack_face_list, axis_conversion
from bpy.props import (BoolProperty,
FloatProperty,
StringProperty,
EnumProperty,
)
from collections import OrderedDict
bl_info = {
"name": "PIX CSV",
"author": "Stanislav Bobovych",
"version": (1, 0, 0),
"blender": (2, 7, 8),
"location": "File > Import-Export",
"description": "Import PIX csv dump of mesh. Import mesh, normals and UVs.",
"category": "Import"}
class PIX_CSV_Operator(bpy.types.Operator):
bl_idname = "object.pix_csv_importer"
bl_label = "Import PIX csv"
filepath = bpy.props.StringProperty(subtype="FILE_PATH")
filter_glob = StringProperty(default="*.csv", options={'HIDDEN'})
mirror_x = bpy.props.BoolProperty(name="Mirror X",
description="Mirror all the vertices across X axis",
default=True)
vertex_order = bpy.props.BoolProperty(name="Change vertex order",
description="Reorder vertices in counter-clockwise order",
default=True)
axis_forward = EnumProperty(
name="Forward",
items=(('X', "X Forward", ""),
('Y', "Y Forward", ""),
('Z', "Z Forward", ""),
('-X', "-X Forward", ""),
('-Y', "-Y Forward", ""),
('-Z', "-Z Forward", ""),
),
default='Z')
axis_up = EnumProperty(
name="Up",
items=(('X', "X Up", ""),
('Y', "Y Up", ""),
('Z', "Z Up", ""),
('-X', "-X Up", ""),
('-Y', "-Y Up", ""),
('-Z', "-Z Up", ""),
),
default='Y',
)
def execute(self, context):
keywords = self.as_keywords(ignore=("axis_forward",
"axis_up",
"filter_glob"))
global_matrix = axis_conversion(from_forward=self.axis_forward,
from_up=self.axis_up,
).to_4x4()
keywords["global_matrix"] = global_matrix
print(keywords)
importCSV(**keywords)
return {'FINISHED'}
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
def draw(self, context):
layout = self.layout
col = layout.column()
col.label(text="Import options")
row = col.row()
row.prop(self, "mirror_x")
row = col.row()
row.prop(self, "vertex_order")
layout.prop(self, "axis_forward")
layout.prop(self, "axis_up")
def make_mesh(verteces, faces, normals, uvs, global_matrix):
mesh = bpy.data.meshes.new('name')
mesh.vertices.add(len(verteces))
mesh.vertices.foreach_set("co", unpack_list(verteces))
mesh.tessfaces.add(len(faces))
mesh.tessfaces.foreach_set("vertices_raw", unpack_face_list(faces))
index = 0
for vertex in mesh.vertices:
vertex.normal = normals[index]
index += 1
uvtex = mesh.tessface_uv_textures.new()
uvtex.name = "UV"
for face, uv in enumerate(uvs):
data = uvtex.data[face]
data.uv1 = uv[0]
data.uv2 = uv[1]
data.uv3 = uv[2]
mesh.update(calc_tessface=False, calc_edges=False)
obj = bpy.data.objects.new('name', mesh)
# apply transformation matrix
obj.matrix_world = global_matrix
bpy.context.scene.objects.link(obj) # link object to scene
def importCSV(filepath=None, mirror_x=False, vertex_order=True, global_matrix=None):
if global_matrix is None:
global_matrix = mathutils.Matrix()
if filepath == None:
return
vertex_dict = {}
normal_dict = {}
vertices = []
faces = []
normals = []
uvs = []
with open(filepath) as f:
reader = csv.reader(f)
next(reader) # skip header
# face_count = sum(1 for row in reader) / 3
# print(face_count)
f.seek(0)
reader = csv.reader(f)
next(reader) # skip header
current_face = []
current_uv = []
i = 0
x_mod = 1
if mirror_x:
x_mod = -1
for row in reader:
vertex_index = int(row[0])
vertex_dict[vertex_index] = (x_mod*float(row[2]), float(row[3]), float(row[4]))
#TODO how are axis really ligned up?
# x, y, z = (vertex_dict[vertex_index][0], vertex_dict[vertex_index][1], vertex_dict[vertex_index][2])
# vertex_dict[vertex_index] = (x, z, y)
normal_dict[vertex_index] = (float(row[6]), float(row[7]), float(row[8]))
#TODO add support for changing the origin of UV coords
uv = (float(row[9]), 1.0 - float(row[10])) # modify V
if i < 2:
current_face.append(vertex_index)
current_uv.append(uv)
i += 1
else:
current_face.append(vertex_index)
#TODO add option to change order of marching vertices
if vertex_order:
faces.append((current_face[2], current_face[1], current_face[0]))
else:
faces.append(current_face)
current_uv.append(uv)
uvs.append(current_uv)
current_face = []
current_uv = []
i = 0
for i in range(len(vertex_dict)):
if i in vertex_dict:
pass
else:
# print("missing",i)
vertex_dict[i] = (0, 0, 0)
normal_dict[i] = (0, 0, 0)
# dictionary sorted by key
vertex_dict = OrderedDict(sorted(vertex_dict.items(), key=lambda t: t[0]))
normal_dict = OrderedDict(sorted(normal_dict.items(), key=lambda t: t[0]))
for key in vertex_dict:
vertices.append(list(vertex_dict[key]))
# print(key,vertex_dict[key])
for key in normal_dict:
normals.append(list(normal_dict[key]))
# print(vertices)
# print(faces)
# print(normals)
# print(uvs)
make_mesh(vertices, faces, normals, uvs, global_matrix)
def menu_func_import(self, context):
self.layout.operator(PIX_CSV_Operator.bl_idname, text="PIX CSV (.csv)")
def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_file_import.append(menu_func_import)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.INFO_MT_file_import.remove(menu_func_import)
if __name__ == "__main__":
# register()
# These run the script from "Run script" button
bpy.utils.register_class(PIX_CSV_Operator)
bpy.ops.object.pix_csv_importer('INVOKE_DEFAULT')
Can you search if the string -, occurs anywhere in the CSV file that doesn’t work? - mkrieger1

API Exporting Issue

I need someone's expertise on this exporting problem of mine.
How it works: Select a camera (animated or not is optional) >> File >> Export Selection >> File Type : .chan (need to load this script as a plugin)
Here's where the problem starts. It is able to create a .text file, however, it is not 'exporting' or writing out the contents into the text file and the file size is of zero bytes.
I am making use of the current API that it has been coded, modifying the code to add in some maya cmds
Can someone kindly help me out?
import math, sys, string, os
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
import maya.OpenMayaAnim as OpenMayaAnim
import maya.cmds as cmds
import maya.mel as mel
kPluginTranslatorTypeName = "chan Export/Import"
kVersionNumber = "0.5a"
camSel = []
win_name = "chan_window"
class CustomNodeTranslator(OpenMayaMPx.MPxFileTranslator):
def __init__(self):
OpenMayaMPx.MPxFileTranslator.__init__(self)
def haveWriteMethod(self):
return True
def haveReadMethod(self):
return True
def filter(self):
return " .chan"
def defaultExtension(self):
return "chan"
def writer( self, fileObject, optionString, accessMode ):
try:
fullName = fileObject.fullName()
fileHandle = open(fullName,"w")
selectList = OpenMaya.MSelectionList()
OpenMaya.MGlobal.getActiveSelectionList(selectList)
node = OpenMaya.MObject()
depFn = OpenMaya.MFnDependencyNode()
path = OpenMaya.MDagPath()
iterator = OpenMaya.MItSelectionList(selectList)
animationTime = OpenMayaAnim.MAnimControl()
maxTime = int(animationTime.maxTime().value())
minTime = int(animationTime.minTime().value())
while (iterator.isDone() == 0):
iterator.getDependNode(node)
depFn.setObject(node)
iterator.getDagPath(path, node)
cameraObject = OpenMaya.MFnCamera(path)
transform = OpenMaya.MFnTransform(path)
chanMe = fileExporter(transform, minTime, maxTime, cameraObject)
for all in chanMe():
fileHandle.write(all)
iterator.next()
fileHandle.close()
except:
sys.stderr.write( "Failed to write file information\n")
raise
def processLine( self, lineStr ):
self.importTheChan.writeFrameData(lineStr)
class fileExporter():
""" module for exporting chan files from application. arguments: object, startFrame, endFrame """
def __init__(self, transform, startAnimation, endAnimation, cameraObj):
self.fileExport = []
self.transform = transform
self.cameraObj = cameraObj
self.start = startAnimation
self.end = endAnimation
self.exportWin()
def exportWin(self):
self.expWindow = cmds.window(w=150, h=100, title = "Export Selection" )
cmds.columnLayout( adjustableColumn=True )
form = cmds.formLayout(numberOfDivisions=100)
cmds.radioCollection()
self.chk1 = cmds.radioButton( label='option1', onc = self.opt1On, ofc = self.opt1Off )
self.chk2 = cmds.radioButton( label='option2', onc = self.opt2On, ofc = self.opt2Off )
self.okayBtn = cmds.button(label='okay!', command=self.runSel, width=150, height=35)
cmds.formLayout(form, edit=True, attachForm=[\
(self.chk1, 'top', 15),\
(self.chk1, 'left', 15),\
(self.chk2, 'top', 30),\
(self.chk2, 'left', 15),\
(self.okayBtn, 'top', 50),\
(self.okayBtn, 'left', 15)])
cmds.showWindow( self.expWindow )
def opt1On(self, args):
print "User checked option1"
startAnimation = cmds.playbackOptions(query=True, minTime=True)
endAnimation = cmds.playbackOptions(query=True, maxTime=True)
self.start = startAnimation
self.end = endAnimation
def opt1Off(self, args):
print "User un-checked option1"
cmds.radioButton(self.chk2, edit = True, enable = True)
self.start = ""
self.end = ""
def opt2On(self, args):
print "User checked option2"
startAnimation = cmds.findKeyframe(which='first')
endAnimation = cmds.findKeyframe(which='last')
self.start = startAnimation
self.end = endAnimation
#self.start.append(int(startAnimation))
#self.end.append(int(endAnimation))
def opt2Off(self, args):
print "User un-checked option2"
self.start = ""
self.end = ""
def runSel(self, args):
chkVal1 = cmds.radioButton(self.chk1, query=True, sl=1)
chkVal2 = cmds.radioButton(self.chk2, query=True, sl=1)
if chkVal1 == 1:
print "opt1 Pressed!"
print self.start
print self.end
self.test()
self.closeWindow()
elif chkVal2 == 1:
print "opt2 Pressed!"
print self.start
print self.end
self.test()
self.closeWindow()
else:
cmds.warning("Check an option")
def closeWindow(self):
cmds.deleteUI(self.expWindow, window=True)
def test(self):
self.actualExp(self.transform, self.start, self.end, self.cameraObj)
def actualExp(self, transform, startAnimation, endAnimation, cameraObj):
mayaGlobal = OpenMaya.MGlobal()
mayaGlobal.viewFrame(OpenMaya.MTime(1))
# Converts the float arguement into integer
for i in range(int(startAnimation), int(endAnimation + 1)):
focalLength = cameraObj.focalLength()
vFilmApp = cameraObj.verticalFilmAperture()
focalOut = 2 math.degrees(math.atan(vFilmApp 25.4/ (2 focalLength)))
myEuler = OpenMaya.MEulerRotation()
spc = OpenMaya.MSpace.kWorld
trans = transform.getTranslation(spc)
rotation = transform.getRotation(myEuler)
rotVector = OpenMaya.MVector(myEuler.asVector())
self.fileExport.append((str(i) + '\t' + str(trans[0]) + "\t" + str(trans[1]) + "\t" + str(trans[2]) + "\t" + str(math.degrees(rotVector[0])) + "\t" + str(math.degrees(rotVector[1])) + "\t" + str(math.degrees(rotVector[2])) + "\t" + str(focalOut) + "\n"))
mayaGlobal.viewFrame(OpenMaya.MTime(i+1))
def __call__(self, args):
return self.fileExport
def radianToDegree(self, radians):
outDegrees = 0.0
outDegrees = (float(radians) / (math.pi)) 180
return outDegrees
# creator
def translatorCreator():
return OpenMayaMPx.asMPxPtr( CustomNodeTranslator() )
# initialize the script plug-in
def initializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.registerFileTranslator(kPluginTranslatorTypeName, None, translatorCreator)
except:
sys.stderr.write( "Failed to register translator: %s" % kPluginTranslatorTypeName )
raise
# uninitialize the script plug-in
def uninitializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.deregisterFileTranslator( kPluginTranslatorTypeName )
except:
sys.stderr.write( "Failed to deregister translator: %s" % kPluginTranslatorTypeName )
raise
the __call__ method is what's supposed to provide the contents of the file. It returns self.fileExport, which is an empty list that is not getting populated.
The problem here is the writer method of the plugin will not wait for your exportWin UI to return the user inputs when you call
chanMe = fileExporter(transform, minTime, maxTime, cameraObject)
By the time the user has entered the inputs, the statements that follow have already been executed:
for all in chanMe():
fileHandle.write(all)
iterator.next()
fileHandle.close()
That is why plugin-based file exporters like these have their options UI tucked away in the option box. These options will be passed prior to call to the plugin's writer().
You will need to export your options UI code (in a certain specific format) using MEL in another script file and specify the name of that file in the optionsScriptName param of the registerFileTranslator call. There is a communication protocol that needs to be followed for communication between this options UI and the writer plugin itself. RobTheBloke's awesome post illustrates this process.
Ideally, the writer() method should have all the details it needs for computing and exporting without having to wait for user input.
Alternatively, if you prefer to have your own UI window and more control over the flow of things, you could write the exporter not as a plugin, but as a simple MEL/Python module. You could still use the power of the API.
Hope this helped!

ReportLab variable nextPageTemplate's

I am in the process of converting the database from pfaf.org (plant for a future) into a pdf book.
I have hit a bit of a stumbling block regarding pageTemplates.
Each plant may begin on a left or a right aligned page; and may potentially be two or more pages.
I have two templates for the first plant page (left and right) and also two more templates for the potential subsequent pages.
Currently this is handled as follows (for instance):
for i, plant in enumerate(plants):
#the first plant is printed on a right-hand page
if i % 2 == 0:
template = 'left'
second = 'right'
else:
template = 'right'
second = 'left'
Story.append(nextPageTemplate(template,'*', 'secondary_'+template, 'secondary_'+second))
Story.append(PageBreak())
#append various paragraphs, jumping between frames, etc...
The code (as you can probably tell) works fine for single page plants.
It also works (semi) as expected for multi page plants.
However, as you can also probably see, a two (or four,etc) page plant will break the template arrangement, because the above code assumes page position based upon plant number, rather than page number.
I can't see a solution for this in the location of the code above (i.e during the Story.append cycle) - as at that point I can not tell whether the plant has used more than one page, and thus what page I am currently on, as such.
I hoped that I could perhaps tweak the nextPageTemplate structure from my custom docTemplate, but I can't figure out if this is possible.
Is it? Or is there another solution? Would really appreciate any help. Have been reading all over, but the best examples I can find don't quite cover this scenario.
Any questions, please ask.
Thank you
Thank you, Nitzle:
The trouble is that I don't know how many pages each plant will take up.
For instance - a new plant starts on an odd page so I give it a cycle of templates ('right', '*', 'secondaryLeft', 'secondaryRight'). [the secondary pages are just a single frame with appropriate margin.]
If that plant is one page long, no problem, the next plant will have the template cycle opposite to the above.
However, if the plant has, say, two pages, it will cause the following plant to fall again on an odd page again and thus the template cycle should not change... I hope this makes sense.
This is the circumstance I am having trouble solving... If I do as you say, it doesn't allow for multiple page plants. Most of my code is as follows; I have tried to slim it down a little though :) hopefully it still contains all relevant stuff and not too much unnecessary.
import os
import sys
import MySQLdb
from reportlab.platypus import Spacer, Image, Table, TableStyle, PageBreak, FrameBreak, paraparser
from reportlab.platypus.doctemplate import BaseDocTemplate, PageTemplate, NextPageTemplate, _doNothing
from reportlab.platypus.tableofcontents import TableOfContents
from reportlab.platypus.frames import Frame
from reportlab.platypus.flowables import KeepInFrame
from reportlab.platypus.paragraph import Paragraph
from reportlab.lib.units import mm, cm
from reportlab.lib.pagesizes import A4, A5
from reportlab.lib.enums import TA_JUSTIFY, TA_CENTER, TA_RIGHT
from reportlab.lib.styles import StyleSheet1, ParagraphStyle as PS
from reportlab.lib import colors
from reportlab.graphics.shapes import Drawing, Rect, String
from reportlab.pdfbase.pdfmetrics import registerFont, stringWidth
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.rl_config import warnOnMissingFontGlyphs
warnOnMissingFontGlyphs = 0
registerFont(TTFont('Museo_', '/home/wuwei/.fonts/Museo300-Regular.ttf'))
registerFont(TTFont('Museo_M', '/home/wuwei/.fonts/Museo500-Regular.ttf'))
registerFont(TTFont('Trebuchet', '/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS.ttf'))
registerFont(TTFont('Trebuchet_I', '/usr/share/fonts/truetype/msttcorefonts/Trebuchet_MS_Italic.ttf'))
## SOME VARIABLE DEFINITIONS ##
titleFont = "Museo_M"
subtitleFont = "Museo_"
stdFont = "Trebuchet"
stdItalic = "Trebuchet_I"
#stdSize = 14
"""CREATE GLOBALS"""
_w, _h = A4
_head_w = 17.5*cm
_head_pad = 0.2*cm
_main_w = 17.2*cm
_budge = 0.3*cm
_left_margin = 1.5*cm
_right_margin = 2.0*cm
_top_margin = 1.5*cm
_bottom_margin = 2.0*cm
_latinFontS = 18
#reset superFraction to style 'common name' placement
paraparser.superFraction = 0.15
paraparser.sizeDelta = 0
###########################################################################################################
######################################### ###############################################
######################################## DB FUNCTIONS #############################################
######################################### ###############################################
###########################################################################################################
def connectToDB():
try:
connection = MySQLdb.connect (host = "localhost",
user = "root",
passwd = "****************",
db = "pfaf")
except MySQLdb.Error, e:
print "I guess, either you don't have a local copy of the pfaf db"
print "or something is wrong with your connection details."
print "Error %d: %s" % (e.args[0], e.args[1])
sys.exit (1)
return connection
def close(item, exit=0):
#used to close both database cursors and connections
item.close()
if exit == 1:
sys.exit (0)
def runQuery(q, conn):
results = conn.cursor(MySQLdb.cursors.DictCursor)
results.execute (q)
return results
def Fetch(results, fetchAll=0):
if fetchAll:
print "fetchAll"
# FETCHALL option:
rows = results.fetchall()
#cursor.close()
#conn.close()
'''for row in rows:
print "%s, %s" % (row["Latin Name"], row["Hardyness"])
print "%d rows were returned" % results.rowcount'''
return rows
else:
# FETCHONE option:
##--- Print some debug info to command line ---##
print "Latin Name - Common Name - Hardyness"
while (1):
row = results.fetchone()
if row == None:
break
latin_name = row["Latin Name"]
common_name = row["Common name"]
hardyness = row["Hardyness"]
family = row["Family"]
synonyms = row["Synonyms"]
##--- Print some more useful debug info to command line ---##
print "%s - %s - %s" % (latin_name, common_name, hardyness)
print row
if results.rowcount != 1:
print "%d rows were returned" % results.rowcount
else:
print "%d row was returned" % results.rowcount
return row
###########################################################################################################
######################################### ###############################################
######################################## STORY PROCESSING #############################################
######################################### ###############################################
###########################################################################################################
def drawBorders(canv, side):
canv.saveState()
d = Drawing(0,0)
#header border#
r = Rect( side-_budge, _h-(2.4*cm), _head_w+(_budge*2), 1.2*cm, rx=5, ry=5 )
r.strokeColor = colors.black
r.fillColor = colors.white
r.strokeWidth = 1.5
d.add(r)
#hardyness border#
rad = 5
hWidth = 1.4*cm
if side == _left_margin:
hPos = -rad
else:
hPos = _w - hWidth + rad
r = Rect( hPos, _h-(3.8*cm), hWidth, 1.2*cm, rx=rad, ry=rad )
r.strokeColor = colors.black
r.fillColor = colors.white
r.strokeWidth = 1.5
d.add(r)
d.drawOn(canv, 0, 0)
canv.restoreState()
def drawFooter(canv, doc):
canv.saveState()
canv.setFont(stdFont,10)
canv.drawCentredString((_w/2.0), 1.5*cm, "%d - %s" % (doc.page, doc.latinName))
canv.restoreState()
class LeftPageTemplate(PageTemplate):
def __init__(self):
#allow a bigger margin on the right for binding
latinF = Frame(_left_margin, 27.5*cm, _head_w, 0.8*cm, id='latinL', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
hardyF = Frame(0.1*cm, 26.05*cm, cm, cm, id='hardL', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
synF = Frame(_left_margin, 26.65*cm, _main_w, 0.55*cm, id='synL', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
otherF = Frame(_left_margin, 22.1*cm, 12.4*cm, 4.5*cm, id='otherL', showBoundary=1)
calF = Frame(14.2*cm, 22.1*cm, 4.5*cm, 4.5*cm, id='calL', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
flowF = Frame(_left_margin, 2.0*cm, _main_w, 19.85*cm, id='flowL', showBoundary=1)
PageTemplate.__init__(self,
id='left',
frames=[latinF, hardyF, synF, otherF, calF, flowF],
pagesize=A4)
def beforeDrawPage(self, canv, doc):
drawBorders(canv, _left_margin)
def afterDrawPage(self, canv, doc):
drawFooter(canv, doc)
class RightPageTemplate(PageTemplate):
def __init__(self):
#allow a bigger margin on the left for binding
latinF = Frame(_right_margin, 27.5*cm, _head_w, 0.8*cm, id='latinR', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
hardyF = Frame(_w-1.1*cm, 26.05*cm, cm, cm, id='hardR', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
synF = Frame(_right_margin+_budge, 26.65*cm, _main_w, 0.55*cm, id='synR', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
calF = Frame(_right_margin+_budge, 22.1*cm, 4.5*cm, 4.5*cm, id='calR', showBoundary=0,
rightPadding=0, leftPadding=0, topPadding=0, bottomPadding=0)
otherF = Frame(_right_margin+5.1*cm, 22.1*cm, 12.4*cm, 4.5*cm, id='otherR', showBoundary=1)
flowF = Frame(_right_margin+_budge, 2.0*cm, _main_w, 19.85*cm, id='flowR', showBoundary=1)
PageTemplate.__init__(self,
id='right',
frames=[latinF, hardyF, synF, otherF, calF, flowF],
pagesize=A4)
def beforeDrawPage(self, canv, doc):
drawBorders(canv, _right_margin)
def afterDrawPage(self, canv, doc):
drawFooter(canv, doc)
class MyDocTemplate(BaseDocTemplate):
_invalidInitArgs = ('pageTemplates',)
def __init__(self, filename, **kw):
self.allowSplitting = 0
BaseDocTemplate.__init__(self, filename, **kw)
self.latinName = "(none initially)"
self.latinWidth = 0 #(none initially)
def afterInit(self):
self._calc() #in case we have changed margin sizes etc
self.leftMargin = _left_margin
self.rightMargin = _right_margin
self.topMargin = _top_margin
self.bottomMargin = _bottom_margin
self.width = _w - self.leftMargin - self.rightMargin
self.height = _h - self.topMargin - self.bottomMargin
frameStd = Frame(cm, self.bottomMargin, (_w - 2*cm), (_h - 3*cm), id='cvr', showBoundary=0)
frameToC = Frame(self.rightMargin, self.bottomMargin, self.width, self.height, id='tocFrame', showBoundary=0)
frameL = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='secLeftFrame', showBoundary=1)
frameR = Frame(self.leftMargin, self.bottomMargin, self.width, self.height, id='secRightFrame', showBoundary=1)
self.addPageTemplates( [PageTemplate(id='Cover', frames=frameStd, onPage=coverPage, pagesize=self.pagesize),
PageTemplate(id='ToC', frames=frameToC, onPage=tocPage, pagesize=self.pagesize),
PageTemplate(id='blank', frames=frameStd, onPage=_doNothing, pagesize=self.pagesize),
LeftPageTemplate(),
RightPageTemplate(),
PageTemplate(id='secondary_left', frames=frameL, onPage=_doNothing, pagesize=self.pagesize),
PageTemplate(id='secondary_right', frames=frameR, onPage=_doNothing, pagesize=self.pagesize)
] )
def afterFlowable(self, flowable):
"""Registers ToC entries - and captures latin name for footer"""
if isinstance(flowable, Paragraph):
style = flowable.style.name
key = None
firstWord = style.split('_',1)[0]
if (style == 'LatinName') or (style == 'LatinNameR') or (firstWord == 'LatinName'):
level = 0
key = 'latin-%s' % self.seq.nextf('LatinName')
self.canv.bookmarkPage(key)
wholeStr = flowable.getPlainText()
if self.page % 2 == 0: #even numbers are on left pages
latinOnly = wholeStr.split('\xc2\xa0\xc2\xa0')[0] #looks for '&nbsp&nbsp' as divider
else:
latinOnly = wholeStr.split('\xc2\xa0\xc2\xa0')[1]
self.latinName = latinOnly
E = [level, latinOnly, self.page]
if key is not None: E.append(key)
self.notify('TOCEntry', tuple(E))
'''elif (style == 'CommonName'):
self.commonName = flowable.getPlainText()
self.commonWidth = stringWidth(self.commonName, styles['common'].fontName, styles['common'].fontSize)'''
else:
return
""" coverPage and otherPages are intended for non-flowing (i.e standard) parts of the pages """
def coverPage(canvas, doc):
Title = "Plants for a Future"
pageinfo = "The full database collected as a printable book"
canvas.setTitle(Title + " : " + pageinfo)
print "creating cover page..."
canvas.saveState()
d = Drawing(0,0)
r = Rect( 0, 0, 12*cm, 4*cm, rx=5, ry=5 )
r.strokeColor = colors.black
r.fillColor = colors.white
r.strokeWidth = 3
d.add(r)
d.drawOn(canvas, (_w/2.0)-6*cm, _h-(6.2*cm))
canvas.setFont(stdFont, 30)
canvas.drawCentredString(_w/2.0, _h-108, Title)
canvas.setFont(stdItalic, 14)
canvas.drawCentredString(_w/2.0, _h-150, pageinfo)
canvas.restoreState()
def tocPage(canvas, doc):
canvas.saveState()
canvas.setFont(stdFont,10)
canvas.drawCentredString((_w/2.0), 1.5*cm, "Table of Contents")
canvas.restoreState()
def getMedicinal(plant):
p = plant
initial = p["Medicinal"]
return initial
""" Run after 'Story' has been fully populated """
def go():
doc = MyDocTemplate('result01.pdf')
passes = doc.multiBuild(Story)
########################################################################
"""Build StyleSheet"""
styles = buildStyle()
h1 = PS(name = 'HeadingOne',
fontName = stdFont,
fontSize = 14,
leading = 16)
h2 = PS(name = 'HeadingTwo',
fontName = stdFont,
fontSize = 12,
leading = 14,
leftIndent = 1*cm)
Story=[]
a = Story.append
a(NextPageTemplate('blank'))
a(PageBreak())
a(NextPageTemplate('ToC'))
a(PageBreak())
toc = TableOfContents()
toc.levelStyles = [ h1, h2 ]
a(toc)
a(NextPageTemplate('blank'))
a(PageBreak())
"""###LEFT PAGES SHOULD BE STYLED RIGHT-ALIGNED, AND RIGHT PAGES LEFT-ALIGNED###"""
#print type(plants)
for i, plant in enumerate(plants):
### THIS INITIAL CHECK BREAKS AS IT NEEDS TO BE BASED ON PAGE NUMBER, NOT PLANT NUMBER!!! ###
if i %2 == 0: #IF FIRST PLANT APPEARS ON A RIGHTSIDE PAGE, ELSE REVERSE THE R and L
page='R'
template = 'right'
second = 'left'
else:
page='L'
template ='left'
second = 'right'
#FIRST THINGS FIRST:
#Make sure the page templates flow nicely for each plant "chapter"
a(NextPageTemplate([template, '*', ('secondary_'+template), ('secondary_'+second) ]))
a(PageBreak())
'''CAPTURE PLANT INFO IN OBJECTS'''
p = plant
'''for info in plant:
print info, p[info]'''
'''Header'''
latin = p["Latin Name"]
common = p["Common name"]
family = p["Family"]
syn = p["Synonyms"]
"""X. congestum. (Lour.)Merrill. X. racemosum. Miq. Apactis japonica. Croton congestum.
Flacourtia japonica. Walp. Hisingera japonica. H. racemosa."""
hardy = str(p["Hardyness"])
'''Basic Info'''
author = p["Author"]
botanicalrefs = p["Botanical references"]
width = p["Width"]
height = p["Height"]
habit = p["Habit"]
planttype = clean("Deciduous/Evergreen", p)
plantrange = p["Range"]
habitat = p["Habitat"]
soil = clean("Soil", plant)
shade = p["Shade"]
moisture = p["Moisture"]
drained = p["Well-drained"]
nf = p["Nitrogen fixer"]
pH = p["pH"]
acid = p["Acid"]
alkaline = p["Alkaline"]
saline = p["Saline"]
wind = p["Wind"]
rate = clean("Growth rate", plant)
pollution = p["Pollution"]
poorsoil = p["Poor soil"]
drought = p["Drought"]
heavyclay = p["Heavy clay"]
tender = clean("FrostTender", plant)
inleaf = p["In leaf"]
flowering = p["Flowering time"]
seedripens = p["Seed ripens"]
flowertype = p["Flower Type"]
pollinators = p["Pollinators"]
selffertile = clean("Self-fertile", plant)
hazards = p["Known hazards"]
rating_edible = p["Rating"]
rating_med = p["Medicinal Rating"]
edibleuses = p["Edible uses"]
medicinaluses = getMedicinal(plant)
otheruses = p["Uses notes"]
#the following encoding allows for special characters such as degree symbol
cultivation = unicode(p["Cultivation details"], 'latin-1')#'ISO-8859-1')
propagation = p["Propagation 1"]
scented = p["Scented"] #boolean - requires further lookup in `ScentedPlants` table
string = '''%s is %s %s growing to %gm by %gm at a %s rate.<br/>
It's habitats are %s <br/><br/> Range: %s
<br/><br/>
Suitable for %s soils. <br/><br/>
Shade: %s, Moisture: %s <br/>
Well-drained: %d, Nitrogen fixer: %d <br/> ph: %s <br/>
Acid: %d, Alkaline: %d, Saline: %d <br/>
Wind: %s
<br/><br/>
Author: %s <br/> Botanical References: %s''' % (
latin, planttype, habit.lower(), width, height, rate,
habitat[0].lower()+habitat[1:], plantrange,
soil, shade, moisture, drained,
nf, pH, acid, alkaline, saline, wind, author, botanicalrefs )
string = unicode(string, 'latin-1')
latinW = stringWidth(latin, styles['latin'].fontName, styles['latin'].fontSize)
commonW = stringWidth(common, styles['common'].fontName, styles['common'].fontSize)
if (latinW + commonW + (_head_pad*3)) > _head_w:
styleName = "LatinName_" + str(i)
latinStyle = PS( name=styleName,
parent=styles['Normal'],
fontName=titleFont,
fontSize=_latinFontS,
leading=22,
spaceAfter=0)
j = 1
#should the latin string be too long, attempt to shrink until it fits
while (latinW + commonW + (_head_pad*3)) > _head_w:
#change the font size until ok...
latinStyle.fontSize = _latinFontS -j
latinW = stringWidth(latin, latinStyle.fontName, latinStyle.fontSize)
j += 0.2
else:
latinStyle = styles['LatinName']
if page == 'L':
headerText = '''<para align="left">
%s
<font face="%s" size="%d"> <super>%s</super></font>
</para>''' % (latin, subtitleFont, 12, common)
else:
headerText = '''<para align="right">
<font face="%s" size="%d"><super>%s</super> </font>
%s
</para>''' % (subtitleFont, 12, common, latin)
latinPara = Paragraph(headerText, latinStyle)
a(FrameBreak('latin'+page))
a(latinPara)
a(FrameBreak('syn'+page))
a(KeepInFrame(_main_w, 1.5*cm,
[Paragraph(syn, styles['syn'+page])],
mode="shrink")) #can be shrink, truncate or overflow
a(FrameBreak('hard'+page))
a(Paragraph(hardy, styles['hardy']))
a(FrameBreak('cal'+page))
#SHALL BE ULTIMATELY POPULATED VIA DATABASE#
greyOut = [ [0,0,1,1,1,1,1,0,0,0,0,0], [0,0,0,0,0,1,1,1,1,0,0,0], [0,0,0,0,0,0,0,0,1,1,1,0] ]
cal = drawCalendar(greyOut)
a(cal)
a(FrameBreak('flow'+page))
a(Paragraph(string, styles['Normal']))
a(Paragraph("Edible Uses", styles['title']))
a(Paragraph("Medicinal Uses", styles['title']))
a(Paragraph("Other Uses", styles['title']))
a(Paragraph("Cultivation", styles['title']))
a(Paragraph(cultivation, styles['Normal']))
a(Paragraph("Propagation", styles['title']))
a(Paragraph(propagation, styles['Normal']))
##ASSEMBLE PDF###
go()
If you're just switching between a "left" and a "right" template, you could try using the _handle_nextPageTemplate method of the BaseDocTemplate class. One way to keep track of page number would be to use the afterPage hook to increment a page number.
from reportlab.platypus import BaseDocTemplate
class MyDocTemplate(BaseDocTemplate):
def __init__(self, *args, **kwargs):
BaseDocTemplate.__init__(self, *args, **kwargs)
self.__pageNum = 1
def afterPage(self):
"""Called after all flowables have been drawn on a page"""
# Increment pageNum since the page has been completed
self.__pageNum += 1
# If the page number is even, force "left-side" template
if self.__pageNum % 2 == 0:
self._handle_nextPageTemplate('left_template')
else:
self._handle_nextPageTemplate('right_template')
I haven't tested the code above, but you may need to use beforePage instead depending on how it checks page template order.

Categories