Commit 2cec2a76 authored by bblanimation's avatar bblanimation

Merge branch 'errorHandling'

parents 7761cfad c8ce9c97
No preview for this file type
......@@ -2,3 +2,5 @@ __pycache__/
.DS_Store
servers.txt
to_host_server/blender_task.zip
testing/
error_log/
......@@ -31,27 +31,35 @@ Created by Christopher Gearhart
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
# system imports
# System imports
#None!!
# Blender imports
import bpy
from bpy.types import Operator
from bpy.props import *
# Render Farm imports
from .ui import *
from .buttons import *
from .functions.setupServers import *
# Used to store keymaps for addon
addon_keymaps = []
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')
# store keymaps here to access after registration
addon_keymaps = []
layout.operator("render_farm.render_frame_on_servers", text="Render Image on Servers", icon='RENDER_STILL')
layout.operator("render_farm.render_animation_on_servers", text="Render Animation on Servers", icon='RENDER_ANIMATION')
def register():
bpy.utils.register_module(__name__)
bpy.types.INFO_MT_render.append(more_menu_options)
bpy.props.render_farm_module_name = __name__
bpy.props.render_farm_version = str(bl_info["version"])[1:-1].replace(", ", ".")
bpy.types.Scene.showAdvanced = BoolProperty(
name="Show Advanced",
description="Display advanced remote server settings",
......@@ -116,8 +124,10 @@ def register():
min=100, max=9999,
default=1000)
bpy.types.Scene.renderType = []
bpy.types.Scene.renderStatus = {"animation":"None", "image":"None"}
bpy.types.Scene.imagePreviewAvailable = BoolProperty(default=False)
bpy.types.Scene.animPreviewAvailable = BoolProperty(default=False)
bpy.types.Scene.imageRenderStatus = StringProperty(name="Image Render Status", default="None")
bpy.types.Scene.animRenderStatus = StringProperty(name="Image Render Status", default="None")
# Initialize server and login variables
bpy.types.Scene.serverGroups = EnumProperty(
......@@ -126,62 +136,75 @@ def register():
description="Choose which hosts to use for render processes",
items=[("All Servers", "All Servers", "Render on all servers")],
default="All Servers")
bpy.props.lastServerGroup = "All Servers"
bpy.types.Scene.lastServerGroup = StringProperty(name="Last Server Group", default="All Servers")
bpy.props.serverPrefs = {"servers":None, "login":None, "path":None, "hostConnection":None}
bpy.types.Scene.availableServers = IntProperty(name="Available Servers", default=0)
bpy.types.Scene.offlineServers = IntProperty(name="Offline Servers", default=0)
bpy.props.lastRemotePath = None
bpy.props.needsUpdating = True
bpy.types.Scene.needsUpdating = BoolProperty(default=True)
bpy.props.nameAveragedImage = ""
bpy.props.imExtension = False
bpy.props.nameImOutputFiles = ""
bpy.props.animExtension = False
bpy.props.imFrame = -1
bpy.props.animFrameRange = []
bpy.types.Scene.nameAveragedImage = StringProperty(default="")
bpy.types.Scene.nameImOutputFiles = StringProperty(default="")
bpy.types.Scene.imExtension = StringProperty(default="")
bpy.types.Scene.animExtension = StringProperty(default="")
bpy.types.Scene.imFrame = IntProperty(default=-1)
bpy.props.animFrameRange = None
# handle the keymap
wm = bpy.context.window_manager
km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
kmi = km.keymap_items.new("scene.render_frame_on_servers", 'F12', 'PRESS', alt=True)
kmi = km.keymap_items.new("scene.render_animation_on_servers", 'F12', 'PRESS', alt=True, shift=True)
kmi = km.keymap_items.new("scene.open_rendered_image", 'O', 'PRESS', shift=True)
kmi = km.keymap_items.new("scene.open_rendered_animation", 'O', 'PRESS', alt=True, shift=True)
kmi = km.keymap_items.new("scene.list_frames", 'M', 'PRESS', shift=True)
kmi = km.keymap_items.new("scene.set_to_missing_frames", 'M', 'PRESS', alt=True, shift=True)
kmi = km.keymap_items.new("scene.refresh_num_available_servers", 'R', 'PRESS', ctrl=True)
kmi = km.keymap_items.new("scene.edit_servers_dict", 'E', 'PRESS', ctrl=True)
addon_keymaps.append(km)
# Note that in background mode (no GUI available), keyconfigs are not available either, so we have
# to check this to avoid nasty errors in background case.
kc = wm.keyconfigs.addon
if kc:
km = kc.keymaps.new(name='Object Mode', space_type='EMPTY')
kmi = km.keymap_items.new("render_farm.render_frame_on_servers", 'F12', 'PRESS', alt=True)
kmi = km.keymap_items.new("render_farm.render_animation_on_servers", 'F12', 'PRESS', alt=True, shift=True)
kmi = km.keymap_items.new("render_farm.open_rendered_image", 'O', 'PRESS', shift=True)
kmi = km.keymap_items.new("render_farm.open_rendered_animation", 'O', 'PRESS', alt=True, shift=True)
kmi = km.keymap_items.new("render_farm.list_frames", 'M', 'PRESS', shift=True)
kmi = km.keymap_items.new("render_farm.set_to_missing_frames", 'M', 'PRESS', alt=True, shift=True)
kmi = km.keymap_items.new("render_farm.refresh_num_available_servers", 'R', 'PRESS', ctrl=True)
kmi = km.keymap_items.new("render_farm.edit_servers_dict", 'E', 'PRESS', ctrl=True)
addon_keymaps.append(km)
def unregister():
bpy.utils.unregister_module(__name__)
bpy.types.INFO_MT_render.remove(more_menu_options)
del bpy.types.Scene.showAdvanced
del bpy.types.Scene.frameRanges
del bpy.types.Scene.tempLocalDir
del bpy.types.Scene.renderDumpLoc
del bpy.types.Scene.nameOutputFiles
del bpy.types.Scene.maxServerLoad
del bpy.types.Scene.timeout
del bpy.types.Scene.maxSamples
del bpy.types.Scene.renderType
del bpy.types.Scene.killPython
del bpy.types.Scene.renderStatus
del bpy.props.serverPrefs
del bpy.props.animFrameRange
del bpy.props.lastRemotePath
del bpy.props.nameAveragedImage
del bpy.props.imExtension
del bpy.props.animExtension
del bpy.props.needsUpdating
del bpy.types.Scene.serverGroups
# handle the keymap
wm = bpy.context.window_manager
for km in addon_keymaps:
wm.keyconfigs.addon.keymaps.remove(km)
# clear the list
addon_keymaps.clear()
Scn = bpy.types.Scene
del bpy.props.animFrameRange
del Scn.imFrame
del Scn.animExtension
del Scn.nameImOutputFiles
del Scn.imExtension
del Scn.nameAveragedImage
del bpy.props.serverPrefs
del Scn.offlineServers
del Scn.availableServers
del Scn.needsUpdating
del Scn.lastServerGroup
del Scn.serverGroups
del Scn.animRenderStatus
del Scn.imageRenderStatus
del Scn.animPreviewAvailable
del Scn.imagePreviewAvailable
del Scn.maxSamples
del Scn.samplesPerFrame
del Scn.timeout
del Scn.maxServerLoad
del Scn.nameOutputFiles
del Scn.renderDumpLoc
del Scn.tempLocalDir
del Scn.frameRanges
del Scn.compress
del Scn.killPython
del Scn.showAdvanced
bpy.utils.unregister_module(__name__)
bpy.types.INFO_MT_render.remove(more_menu_options)
if __name__ == "__main__":
register()
......@@ -19,4 +19,4 @@ Created by Christopher Gearhart
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
__all__ = ["editRemoteServersDict", "missingFramesActions", "openRenderInUI", "refreshNumAvailableServers", "sendAnimation", "sendFrame"]
__all__ = ["editRemoteServersDict", "missingFramesActions", "openRenderInUI", "refreshServers", "reportError", "sendAnimation", "sendFrame"]
......@@ -35,17 +35,18 @@ from ..functions.jobIsValid import *
class editRemoteServersDict(Operator):
"""Edit the remote servers dictionary in a text editor""" # blender will use this as a tooltip for menu items and buttons.
bl_idname = "scene.edit_servers_dict" # unique identifier for buttons and menu items to reference.
bl_idname = "render_farm.edit_servers_dict" # unique identifier for buttons and menu items to reference.
bl_label = "Edit Remote Servers" # display name in the interface.
bl_options = {"REGISTER", "UNDO"} # enable undo for the operator.
def execute(self, context):
scn = bpy.context.scene
changeContext(context, "TEXT_EDITOR")
try:
libraryServersPath = os.path.join(getLibraryPath(), "servers")
bpy.ops.text.open(filepath=os.path.join(libraryServersPath, "remoteServers.txt"))
self.report({"INFO"}, "Opened 'remoteServers.txt'")
bpy.props.needsUpdating = True
scn.needsUpdating = True
except:
self.report({"ERROR"}, "ERROR: Could not open 'remoteServers.txt'. If the problem persists, try reinstalling the add-on.")
return{"FINISHED"}
......@@ -35,40 +35,49 @@ from ..functions.jobIsValid import *
class listMissingFrames(Operator):
"""List the output files missing from the render dump folder""" # blender will use this as a tooltip for menu items and buttons.
bl_idname = "scene.list_frames" # unique identifier for buttons and menu items to reference.
bl_idname = "render_farm.list_frames" # unique identifier for buttons and menu items to reference.
bl_label = "List Missing Frames" # display name in the interface.
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"}
except:
handle_exception()
return{"CANCELLED"}
return{"FINISHED"}
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.
bl_idname = "scene.set_to_missing_frames" # unique identifier for buttons and menu items to reference.
bl_idname = "render_farm.set_to_missing_frames" # unique identifier for buttons and menu items to reference.
bl_label = "Set to Missing Frames" # display name in the interface.
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:
handle_exception()
return{"CANCELLED"}
......@@ -35,55 +35,66 @@ from ..functions.jobIsValid import *
class openRenderedImageInUI(Operator):
"""Open rendered image""" # blender will use this as a tooltip for menu items and buttons.
bl_idname = "scene.open_rendered_image" # unique identifier for buttons and menu items to reference.
bl_idname = "render_farm.open_rendered_image" # unique identifier for buttons and menu items to reference.
bl_label = "Open Rendered Image" # display name in the interface.
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")
scn = bpy.context.scene
try:
if bpy.data.images.find(scn.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[scn.nameAveragedImage]
elif scn.nameAveragedImage != "":
self.report({"ERROR"}, "Image could not be found: '{nameAveragedImage}'".format(nameAveragedImage=scn.nameAveragedImage))
return{"CANCELLED"}
else:
self.report({"WARNING"}, "No rendered images could be found")
return{"CANCELLED"}
return{"FINISHED"}
except:
handle_exception()
return{"CANCELLED"}
return{"FINISHED"}
class openRenderedAnimationInUI(Operator):
"""Open rendered animation""" # blender will use this as a tooltip for menu items and buttons.
bl_idname = "scene.open_rendered_animation" # unique identifier for buttons and menu items to reference.
bl_idname = "render_farm.open_rendered_animation" # unique identifier for buttons and menu items to reference.
bl_label = "Open Rendered Animation" # display name in the interface.
bl_options = {"REGISTER", "UNDO"} # enable undo for the 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"}
scn = bpy.context.scene
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=scn.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:
handle_exception()
return{"CANCELLED"}
......@@ -32,10 +32,11 @@ from bpy.props import *
from ..functions import *
from ..functions.averageFrames import *
from ..functions.jobIsValid import *
from ..functions.common import *
class refreshNumAvailableServers(Operator):
class refreshServers(Operator):
"""Attempt to connect to all servers through host server""" # blender will use this as a tooltip for menu items and buttons.
bl_idname = "scene.refresh_num_available_servers" # unique identifier for buttons and menu items to reference.
bl_idname = "render_farm.refresh_num_available_servers" # unique identifier for buttons and menu items to reference.
bl_label = "Refresh Available Servers" # display name in the interface.
bl_options = {"REGISTER", "UNDO"} # enable undo for the operator.
......@@ -46,13 +47,15 @@ class refreshNumAvailableServers(Operator):
return False
return True
def checkNumAvailServers(self):
@classmethod
def checkNumAvailServers(cls):
scn = bpy.context.scene
command = "ssh -T -oStrictHostKeyChecking=no -x {login} 'python {remotePath}blender_task -Hv --connection_timeout {timeout} --hosts_file {remotePath}servers.txt'".format(login=bpy.props.serverPrefs["login"], remotePath=bpy.props.serverPrefs["path"], timeout=scn.timeout)
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
return process
def updateAvailServerInfo(self):
@classmethod
def updateAvailServerInfo(cls, process):
scn = bpy.context.scene
available = None
......@@ -60,7 +63,7 @@ class refreshNumAvailableServers(Operator):
while available is None:
try:
rl = self.process.stdout.readline()
rl = process.stdout.readline()
line1 = rl.decode("ASCII").replace("\\n", "")
available = json.loads(line1.replace("'", "\""))
except:
......@@ -68,7 +71,7 @@ class refreshNumAvailableServers(Operator):
available = None
while offline is None:
try:
rl = self.process.stdout.readline()
rl = process.stdout.readline()
line2 = rl.decode("ASCII").replace("\\n", "")
offline = json.loads(line2.replace("'", "\""))
except:
......@@ -77,78 +80,108 @@ class refreshNumAvailableServers(Operator):
scn.availableServers = len(available)
scn.offlineServers = len(offline)
for a in bpy.context.screen.areas:
a.tag_redraw()
tag_redraw_areas()
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"}
def execute(self, context):
print("\nRunning 'checkNumAvailServers' function...")
scn = context.scene
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:
scn.needsUpdating = False
self.state += 1
self.process = self.checkNumAvailServers()
return{"PASS_THROUGH"}
elif self.state == 2:
self.updateAvailServerInfo(self.process)
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:
handle_exception()
return{"CANCELLED"}
# 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
@classmethod
def refreshServersBlock(cls, statusType=None):
scn = bpy.context.scene
if scn.needsUpdating or scn.lastServerGroup != scn.serverGroups:
scn.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)
return False
process = copyFiles()
while process.returncode == None:
process.poll()
if process.returncode != 0:
return False
process = cls.checkNumAvailServers()
while process.returncode == None:
process.poll()
if process.returncode != 0:
return False
self.report({"INFO"}, "Refreshing available servers...")
cls.updateAvailServerInfo(process)
scn.needsUpdating = False
return True
return{"RUNNING_MODAL"}
def execute(self, context):
try:
print("\nRunning 'checkNumAvailServers' function...")
scn = context.scene
# start initial process
self.state = 1 # initializes state for modal
if scn.needsUpdating or scn.lastServerGroup != scn.serverGroups:
scn.lastServerGroup = scn.serverGroups
updateStatus = updateServerPrefs()
if not updateStatus["valid"]:
self.report({"ERROR"}, updateStatus["errorMessage"])
return{"CANCELLED"}
self.process = copyFiles()
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:
handle_exception()
return{"CANCELLED"}
def cancel(self, context):
wm = context.window_manager
......
"""
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 time
import os
# Blender imports
import bpy
props = bpy.props
# Render Farm imports
from ..functions import *
class reportError(bpy.types.Operator):
"""Report a bug via an automatically generated issue ticket"""
bl_idname = "render_farm.report_error"
bl_label = "Report Error"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
scn = bpy.context.scene
try:
# set up file paths
libraryServersPath = os.path.join(getLibraryPath(), "error_log")
# write necessary debugging information to text file
writeErrorToFile(libraryServersPath, 'Render_Farm_log', bpy.props.render_farm_version)
# open error report in UI with text editor
changeContext(context, "TEXT_EDITOR")
try:
bpy.ops.text.open(filepath=os.path.join(libraryServersPath, "Render_Farm_error_report.txt"))
bpy.context.space_data.show_word_wrap = True
self.report({"INFO"}, "Opened 'Render_Farm_error_report.txt'")
scn.needsUpdating = True
except:
self.report({"ERROR"}, "ERROR: Could not open 'Render_Farm_error_report.txt'. If the problem persists, try reinstalling the add-on.")
except:
self.report({"ERROR"}, "ERROR: Could not generate error report. Please use the 'Report a Bug' button in the Render Farm Preferences (found in Add-On User Preferences)")
return{"FINISHED"}
class closeReportError(bpy.types.Operator):
"""Deletes error report from blender's memory (still exists in file system)"""
bl_idname = "render_farm.close_report_error"
bl_label = "Close Report Error"
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
def execute(self, context):
try:
txt = bpy.data.texts['Render_Farm_log']
bpy.data.texts.remove(txt, True)
except:
handle_exception()
return{"FINISHED"}
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 import *
try:
import httplib
except:
......@@ -43,6 +44,16 @@ def have_internet():
conn.close()
return False
def bashSafeName(string):