Commit ee42360f authored by Christopher Gearhart's avatar Christopher Gearhart

Working towards reliable error handling

parent 366400df
......@@ -43,7 +43,7 @@ def more_menu_options(self, context):
layout = self.layout
layout.separator()
layout.operator("scene.render_frame_on_servers", text="Render Image on Servers", icon='RENDER_STILL')
layout.operator("scene.render_animation_on_servers", text="Render Image on Servers", icon='RENDER_ANIMATION')
layout.operator("scene.render_animation_on_servers", text="Render Animation on Servers", icon='RENDER_ANIMATION')
# store keymaps here to access after registration
addon_keymaps = []
......
......@@ -40,20 +40,35 @@ class listMissingFrames(Operator):
bl_options = {"REGISTER", "UNDO"} # enable undo for the operator.
def execute(self, context):
scn = context.scene
try:
scn = context.scene
# initializes self.frameRangesDict (returns False if frame range invalid)
if not setFrameRangesDict(self):
return{"FINISHED"}
# initializes self.frameRangesDict (returns False if frame range invalid)
if not setFrameRangesDict(self):
return{"FINISHED"}
# list all missing files from start frame to end frame in render dump folder
missingFrames = listMissingFiles(getNameOutputFiles(), self.frameRangesDict["string"])
if len(missingFrames) > 0:
self.report({"INFO"}, "Missing frames: {missingFrames}".format(missingFrames=missingFrames))
else:
self.report({"INFO"}, "All frames accounted for!")
# list all missing files from start frame to end frame in render dump folder
missingFrames = listMissingFiles(getNameOutputFiles(), self.frameRangesDict["string"])
if len(missingFrames) > 0:
self.report({"INFO"}, "Missing frames: {missingFrames}".format(missingFrames=missingFrames))
else:
self.report({"INFO"}, "All frames accounted for!")
return{"FINISHED"}
return{"FINISHED"}
except:
self.handle_exception()
return{"CANCELLED"}
def handle_exception(self):
errormsg = print_exception('LEGOizer_log')
# if max number of exceptions occur within threshold of time, abort!
curtime = time.time()
print('\n'*5)
print('-'*100)
print("Something went wrong. Please start an error report with us so we can fix it! (press the 'Report a Bug' button under the 'Render on Servers' dropdown menu of the Render Farm Client)")
print('-'*100)
print('\n'*5)
showErrorMessage("Something went wrong. Please start an error report with us so we can fix it! (press the 'Report a Bug' button under the 'Render on Servers' dropdown menu of the Render Farm Client)", wrap=240)
class setToMissingFrames(Operator):
"""Set frame range to frames missing from the render dump folder""" # blender will use this as a tooltip for menu items and buttons.
......@@ -62,13 +77,28 @@ class setToMissingFrames(Operator):
bl_options = {"REGISTER", "UNDO"} # enable undo for the operator.
def execute(self, context):
scn = context.scene
try:
scn = context.scene
# initializes self.frameRangesDict (returns False if frame range invalid)
if not setFrameRangesDict(self):
return{"FINISHED"}
# initializes self.frameRangesDict (returns False if frame range invalid)
if not setFrameRangesDict(self):
return{"FINISHED"}
# list all missing files from start frame to end frame in render dump location
scn.frameRanges = listMissingFiles(getNameOutputFiles(), self.frameRangesDict["string"])
# list all missing files from start frame to end frame in render dump location
scn.frameRanges = listMissingFiles(getNameOutputFiles(), self.frameRangesDict["string"])
return{"FINISHED"}
return{"FINISHED"}
except:
self.handle_exception()
return{"CANCELLED"}
def handle_exception(self):
errormsg = print_exception('LEGOizer_log')
# if max number of exceptions occur within threshold of time, abort!
curtime = time.time()
print('\n'*5)
print('-'*100)
print("Something went wrong. Please start an error report with us so we can fix it! (press the 'Report a Bug' button under the 'Render on Servers' dropdown menu of the Render Farm Client)")
print('-'*100)
print('\n'*5)
showErrorMessage("Something went wrong. Please start an error report with us so we can fix it! (press the 'Report a Bug' button under the 'Render on Servers' dropdown menu of the Render Farm Client)", wrap=240)
......@@ -40,20 +40,36 @@ class openRenderedImageInUI(Operator):
bl_options = {"REGISTER", "UNDO"} # enable undo for the operator.
def execute(self, context):
if bpy.data.images.find(bpy.props.nameAveragedImage) >= 0:
# open rendered image in UV/Image_Editor
changeContext(context, "IMAGE_EDITOR")
for area in context.screen.areas:
if area.type == "IMAGE_EDITOR":
area.spaces.active.image = bpy.data.images[bpy.props.nameAveragedImage]
elif bpy.props.nameAveragedImage != "":
self.report({"ERROR"}, "Image could not be found: '{nameAveragedImage}'".format(nameAveragedImage=bpy.props.nameAveragedImage))
return{"CANCELLED"}
else:
self.report({"WARNING"}, "No rendered images could be found")
try:
if bpy.data.images.find(bpy.props.nameAveragedImage) >= 0:
# open rendered image in UV/Image_Editor
changeContext(context, "IMAGE_EDITOR")
for area in context.screen.areas:
if area.type == "IMAGE_EDITOR":
area.spaces.active.image = bpy.data.images[bpy.props.nameAveragedImage]
elif bpy.props.nameAveragedImage != "":
self.report({"ERROR"}, "Image could not be found: '{nameAveragedImage}'".format(nameAveragedImage=bpy.props.nameAveragedImage))
return{"CANCELLED"}
else:
self.report({"WARNING"}, "No rendered images could be found")
return{"CANCELLED"}
return{"FINISHED"}
except:
self.handle_exception()
return{"CANCELLED"}
return{"FINISHED"}
def handle_exception(self):
errormsg = print_exception('LEGOizer_log')
# if max number of exceptions occur within threshold of time, abort!
curtime = time.time()
print('\n'*5)
print('-'*100)
print("Something went wrong. Please start an error report with us so we can fix it! (press the 'Report a Bug' button under the 'Render on Servers' dropdown menu of the Render Farm Client)")
print('-'*100)
print('\n'*5)
showErrorMessage("Something went wrong. Please start an error report with us so we can fix it! (press the 'Report a Bug' button under the 'Render on Servers' dropdown menu of the Render Farm Client)", wrap=240)
class openRenderedAnimationInUI(Operator):
"""Open rendered animation""" # blender will use this as a tooltip for menu items and buttons.
......@@ -63,27 +79,42 @@ class openRenderedAnimationInUI(Operator):
def execute(self, context):
self.frameRangesDict = buildFrameRangesString(context.scene.frameRanges)
# change contexts
lastAreaType = changeContext(context, "CLIP_EDITOR")
# opens first frame of image sequence (blender imports full sequence)
openedFile = False
self.renderDumpFolder = getRenderDumpFolder()
image_sequence_filepath = "{dumpFolder}/".format(dumpFolder=self.renderDumpFolder)
for frame in bpy.props.animFrameRange:
image_filename = "{fileName}_{frame}{extension}".format(fileName=getNameOutputFiles(), frame=str(frame).zfill(4), extension=bpy.props.animExtension)
if os.path.isfile(os.path.join(image_sequence_filepath, image_filename)):
bpy.ops.clip.open(directory=image_sequence_filepath, files=[{"name":image_filename}])
openedFile = image_filename
openedFrame = frame
break
if openedFile:
bpy.ops.clip.reload()
bpy.data.movieclips[openedFile].frame_start = frame
else:
changeContext(context, lastAreaType)
self.report({"ERROR"}, "Could not open rendered animation. View files in file browser in the following folder: '{renderDumpFolder}'.".format(renderDumpFolder=self.renderDumpFolder))
return{"FINISHED"}
try:
self.frameRangesDict = buildFrameRangesString(context.scene.frameRanges)
# change contexts
lastAreaType = changeContext(context, "CLIP_EDITOR")
# opens first frame of image sequence (blender imports full sequence)
openedFile = False
self.renderDumpFolder = getRenderDumpFolder()
image_sequence_filepath = "{dumpFolder}/".format(dumpFolder=self.renderDumpFolder)
for frame in bpy.props.animFrameRange:
image_filename = "{fileName}_{frame}{extension}".format(fileName=getNameOutputFiles(), frame=str(frame).zfill(4), extension=bpy.props.animExtension)
if os.path.isfile(os.path.join(image_sequence_filepath, image_filename)):
bpy.ops.clip.open(directory=image_sequence_filepath, files=[{"name":image_filename}])
openedFile = image_filename
openedFrame = frame
break
if openedFile:
bpy.ops.clip.reload()
bpy.data.movieclips[openedFile].frame_start = frame
else:
changeContext(context, lastAreaType)
self.report({"ERROR"}, "Could not open rendered animation. View files in file browser in the following folder: '{renderDumpFolder}'.".format(renderDumpFolder=self.renderDumpFolder))
return{"FINISHED"}
except:
self.handle_exception()
return{"CANCELLED"}
def handle_exception(self):
errormsg = print_exception('LEGOizer_log')
# if max number of exceptions occur within threshold of time, abort!
curtime = time.time()
print('\n'*5)
print('-'*100)
print("Something went wrong. Please start an error report with us so we can fix it! (press the 'Report a Bug' button under the 'Render on Servers' dropdown menu of the Render Farm Client)")
print('-'*100)
print('\n'*5)
showErrorMessage("Something went wrong. Please start an error report with us so we can fix it! (press the 'Report a Bug' button under the 'Render on Servers' dropdown menu of the Render Farm Client)", wrap=240)
......@@ -81,74 +81,93 @@ class refreshNumAvailableServers(Operator):
a.tag_redraw()
def modal(self, context, event):
if event.type in {"ESC"}:
self.report({"INFO"}, "Refresh process cancelled")
self.cancel(context)
return{"CANCELLED"}
if event.type == "TIMER":
self.process.poll()
# if python not found on host server
if self.process.returncode == 127 and self.state == 2:
self.report({"ERROR"}, "python not installed on host server")
self.cancel(context)
return{"CANCELLED"}
# if process finished and unknown error thrown
if self.process.returncode != 0 and self.process.returncode != None:
handleError(self, "Process {curState}".format(curState=str(self.state-1)))
try:
if event.type in {"ESC"}:
self.report({"INFO"}, "Refresh process cancelled")
self.cancel(context)
return{"CANCELLED"}
# if process finished and no errors
if self.process.returncode != None:
# print("Process {curState} finished! (return code: {returnCode})".format(curState=str(self.state-1), returnCode=str(self.process.returncode)))
# check number of available servers via host server
if self.state == 1:
bpy.props.needsUpdating = False
self.state += 1
self.process = self.checkNumAvailServers()
return{"PASS_THROUGH"}
elif self.state == 2:
self.updateAvailServerInfo()
scn = context.scene
self.report({"INFO"}, "Refresh process completed ({num} servers available)".format(num=str(scn.availableServers)))
return{"FINISHED"}
else:
self.report({"ERROR"}, "ERROR: Current state not recognized.")
return{"FINISHED"}
return{"PASS_THROUGH"}
if event.type == "TIMER":
self.process.poll()
# if python not found on host server
if self.process.returncode == 127 and self.state == 2:
self.report({"ERROR"}, "python not installed on host server")
self.cancel(context)
return{"CANCELLED"}
# if process finished and unknown error thrown
if self.process.returncode != 0 and self.process.returncode != None:
handleError(self, "Process {curState}".format(curState=str(self.state-1)))
self.cancel(context)
return{"CANCELLED"}
# if process finished and no errors
if self.process.returncode != None:
# print("Process {curState} finished! (return code: {returnCode})".format(curState=str(self.state-1), returnCode=str(self.process.returncode)))
# check number of available servers via host server
if self.state == 1:
bpy.props.needsUpdating = False
self.state += 1
self.process = self.checkNumAvailServers()
return{"PASS_THROUGH"}
elif self.state == 2:
self.updateAvailServerInfo()
scn = context.scene
self.report({"INFO"}, "Refresh process completed ({num} servers available)".format(num=str(scn.availableServers)))
return{"FINISHED"}
else:
self.report({"ERROR"}, "ERROR: Current state not recognized.")
return{"FINISHED"}
return{"PASS_THROUGH"}
except:
self.handle_exception()
return{"CANCELLED"}
def execute(self, context):
print("\nRunning 'checkNumAvailServers' function...")
scn = context.scene
# start initial process
self.state = 1 # initializes state for modal
if bpy.props.needsUpdating or bpy.props.lastServerGroup != scn.serverGroups:
bpy.props.lastServerGroup = scn.serverGroups
updateStatus = updateServerPrefs()
if not updateStatus["valid"]:
self.report({"ERROR"}, updateStatus["errorMessage"])
return{"CANCELLED"}
self.process = copyFiles()
bpy.props.lastRemotePath = bpy.props.serverPrefs["path"]
else:
self.process = self.checkNumAvailServers()
self.state += 1
# create timer for modal
wm = context.window_manager
self._timer = wm.event_timer_add(0.1, context.window)
wm.modal_handler_add(self)
self.report({"INFO"}, "Refreshing available servers...")
try:
print("\nRunning 'checkNumAvailServers' function...")
scn = context.scene
# start initial process
self.state = 1 # initializes state for modal
if bpy.props.needsUpdating or bpy.props.lastServerGroup != scn.serverGroups:
bpy.props.lastServerGroup = scn.serverGroups
updateStatus = updateServerPrefs()
if not updateStatus["valid"]:
self.report({"ERROR"}, updateStatus["errorMessage"])
return{"CANCELLED"}
self.process = copyFiles()
bpy.props.lastRemotePath = bpy.props.serverPrefs["path"]
else:
self.process = self.checkNumAvailServers()
self.state += 1
# create timer for modal
wm = context.window_manager
self._timer = wm.event_timer_add(0.1, context.window)
wm.modal_handler_add(self)
self.report({"INFO"}, "Refreshing available servers...")
return{"RUNNING_MODAL"}
except:
self.handle_exception()
return{"CANCELLED"}
return{"RUNNING_MODAL"}
def handle_exception(self):
errormsg = print_exception('LEGOizer_log')
# if max number of exceptions occur within threshold of time, abort!
curtime = time.time()
print('\n'*5)
print('-'*100)
print("Something went wrong. Please start an error report with us so we can fix it! (press the 'Report a Bug' button under the 'Render on Servers' dropdown menu of the Render Farm Client)")
print('-'*100)
print('\n'*5)
showErrorMessage("Something went wrong. Please start an error report with us so we can fix it! (press the 'Report a Bug' button under the 'Render on Servers' dropdown menu of the Render Farm Client)", wrap=240)
def cancel(self, context):
wm = context.window_manager
......
This diff is collapsed.
This diff is collapsed.
No preview for this file type
......@@ -28,6 +28,7 @@ import os
import subprocess
import sys
from .setupServers import *
from .common_functions import *
try:
import httplib
except:
......
This diff is collapsed.
......@@ -30,27 +30,32 @@ scn = bpy.context.scene
@persistent
def handle_legoizer_animation(scene):
print("Adjusting frame")
groupsToAdjust = {}
for group in bpy.data.groups:
if group.name.startswith("LEGOizer_") and "_bricks_frame_" in group.name:
sourceName = group.name[9:(group.name.index("_bricks_frame_"))]
groupsToAdjust[sourceName].append(group)
if sourceName not in groupsToAdjust.keys():
groupsToAdjust[sourceName] = [group.name]
else:
groupsToAdjust[sourceName].append(group.name)
for sourceName in groupsToAdjust:
groupsToAdjust[sourceName].sort()
for i,group in enumerate(groupsToAdjust[sourceName]):
for i,gName in enumerate(groupsToAdjust[sourceName]):
group = bpy.data.groups.get(gName)
frame = int(group.name[(group.name.index("_bricks_frame_") + 14):])
onCurF = frame == scn.frame_current
beforeFirstF = (i == 0 and scn.frame_current < frame)
afterLastF = (i == (len(groupsToAdjust[sourceName]) - 1) and scn.frame_current > frame)
displayOnCurrentF = onCurF or beforeFirstF or afterLastF
displayOnCurF = onCurF or beforeFirstF or afterLastF
brick = group.objects[0]
if brick.hide == displayOnCurF:
brick.hide = not displayOnCurF
brick.hide_render = not displayOnCurF
handle_legoizer_animation(scn)
bpy.app.handlers.render_pre.append(handle_legoizer_animation)
bpy.app.handlers.frame_change_pre.append(handle_legoizer_animation)
handle_legoizer_animation(scn)
""" END SUPPORT FOR THE LEGOIZER """
......
"""
Copyright (C) 2017 Bricks Brought to Life
http://bblanimation.com/
chris@bblanimation.com
Created by Christopher Gearhart
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 3 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, see <http://www.gnu.org/licenses/>.
"""
# system imports
import bpy
import math
from bpy.types import Panel
from bpy.props import *
from ..functions import getRenderStatus, have_internet
from ..functions.setupServers import *
@persistent
def handle_saving_when_rendering(scene):
# TODO: Force end render process or something, instead of 'set_render_status_on_load' below
pass
bpy.app.handlers.save_pre.append(handle_saving_when_rendering)
@persistent
def set_render_status_on_load(scene):
scene.renderStatus["image"] = "None"
scene.renderStatus["animation"] = "None"
bpy.app.handlers.load_post.append(set_render_status_on_load)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment