Template
Adding project files to project template
This commit is contained in:
Generated
+14
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<content url="file://$MODULE_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/bpy/2.79/scripts">
|
||||
<sourceFolder url="file://$MODULE_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/bpy/2.79/scripts" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.6 (PushBlendPull)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
<option name="PROJECT_TEST_RUNNER" value="Unittests" />
|
||||
</component>
|
||||
</module>
|
||||
Generated
+5
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
Generated
+70
@@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownProjectSettings">
|
||||
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="false" showSelectionInPreview="true">
|
||||
<PanelProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
|
||||
</PanelProvider>
|
||||
</PreviewSettings>
|
||||
<ParserSettings>
|
||||
<PegdownExtensions>
|
||||
<option name="ABBREVIATIONS" value="false" />
|
||||
<option name="ANCHORLINKS" value="true" />
|
||||
<option name="ASIDE" value="false" />
|
||||
<option name="ATXHEADERSPACE" value="true" />
|
||||
<option name="AUTOLINKS" value="true" />
|
||||
<option name="DEFINITIONS" value="false" />
|
||||
<option name="FENCED_CODE_BLOCKS" value="true" />
|
||||
<option name="FOOTNOTES" value="false" />
|
||||
<option name="HARDWRAPS" value="false" />
|
||||
<option name="INSERTED" value="false" />
|
||||
<option name="QUOTES" value="false" />
|
||||
<option name="RELAXEDHRULES" value="true" />
|
||||
<option name="SMARTS" value="false" />
|
||||
<option name="STRIKETHROUGH" value="true" />
|
||||
<option name="SUBSCRIPT" value="false" />
|
||||
<option name="SUPERSCRIPT" value="false" />
|
||||
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
|
||||
<option name="SUPPRESS_INLINE_HTML" value="false" />
|
||||
<option name="TABLES" value="true" />
|
||||
<option name="TASKLISTITEMS" value="true" />
|
||||
<option name="TOC" value="false" />
|
||||
<option name="WIKILINKS" value="true" />
|
||||
</PegdownExtensions>
|
||||
<ParserOptions>
|
||||
<option name="COMMONMARK_LISTS" value="false" />
|
||||
<option name="DUMMY" value="false" />
|
||||
<option name="EMOJI_SHORTCUTS" value="true" />
|
||||
<option name="FLEXMARK_FRONT_MATTER" value="false" />
|
||||
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="true" />
|
||||
<option name="GFM_TABLE_RENDERING" value="true" />
|
||||
<option name="GITBOOK_URL_ENCODING" value="false" />
|
||||
<option name="GITHUB_EMOJI_URL" value="false" />
|
||||
<option name="GITHUB_LISTS" value="true" />
|
||||
<option name="GITHUB_WIKI_LINKS" value="true" />
|
||||
<option name="JEKYLL_FRONT_MATTER" value="false" />
|
||||
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
|
||||
</ParserOptions>
|
||||
</ParserSettings>
|
||||
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true">
|
||||
<GeneratorProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
|
||||
</GeneratorProvider>
|
||||
<headerTop />
|
||||
<headerBottom />
|
||||
<bodyTop />
|
||||
<bodyBottom />
|
||||
</HtmlSettings>
|
||||
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssTextEnabled="false" isDynamicPageWidth="true">
|
||||
<StylesheetProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
|
||||
</StylesheetProvider>
|
||||
<ScriptProviders />
|
||||
<cssText />
|
||||
</CssSettings>
|
||||
<HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" />
|
||||
<LinkMapSettings>
|
||||
<textMaps />
|
||||
</LinkMapSettings>
|
||||
</component>
|
||||
</project>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<component name="MarkdownNavigator.ProfileManager">
|
||||
<settings default="" pdf-export="" />
|
||||
</component>
|
||||
Generated
+4
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6 (PushBlendPull)" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/PushBlendPull.iml" filepath="$PROJECT_DIR$/.idea/PushBlendPull.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+940
@@ -0,0 +1,940 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="2dd0ebfd-c64a-47fb-842f-d234330d06b7" name="Default" comment="" />
|
||||
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
|
||||
<option name="TRACKING_ENABLED" value="true" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileEditorManager">
|
||||
<splitter split-orientation="horizontal" split-proportion="0.4599529">
|
||||
<split-first>
|
||||
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
|
||||
<file leaf-file-name="dispatcher.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/dispatcher.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="170">
|
||||
<caret line="10" column="19" lean-forward="false" selection-start-line="10" selection-start-column="19" selection-end-line="10" selection-end-column="19" />
|
||||
<folding>
|
||||
<element signature="e#482#718#0" expanded="false" />
|
||||
<element signature="e#2807#2905#0" expanded="false" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="client.py" pinned="false" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/client.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="27">
|
||||
<caret line="65" column="28" lean-forward="true" selection-start-line="65" selection-start-column="28" selection-end-line="65" selection-end-column="28" />
|
||||
<folding>
|
||||
<element signature="e#0#10#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="epollreactor.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/twisted/internet/epollreactor.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="3689">
|
||||
<caret line="227" column="0" lean-forward="false" selection-start-line="227" selection-start-column="0" selection-end-line="227" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="threading.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file:///usr/lib/python3.6/threading.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="8160">
|
||||
<caret line="486" column="6" lean-forward="false" selection-start-line="486" selection-start-column="6" selection-end-line="486" selection-end-column="6" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="_pydev_execfile.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$APPLICATION_HOME_DIR$/helpers/pydev/_pydev_imps/_pydev_execfile.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="289">
|
||||
<caret line="17" column="0" lean-forward="false" selection-start-line="17" selection-start-column="0" selection-end-line="17" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="resource.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/twisted/web/resource.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="6919">
|
||||
<caret line="407" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="407" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="http.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/twisted/web/http.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="14042">
|
||||
<caret line="826" column="32" lean-forward="false" selection-start-line="826" selection-start-column="32" selection-end-line="826" selection-end-column="32" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="test_dispatcher.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/tests/test_dispatcher.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="1139">
|
||||
<caret line="78" column="43" lean-forward="false" selection-start-line="78" selection-start-column="43" selection-end-line="78" selection-end-column="43" />
|
||||
<folding>
|
||||
<element signature="e#0#9#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="README.md" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/README.md">
|
||||
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
||||
<state split_layout="SPLIT">
|
||||
<first_editor relative-caret-position="238">
|
||||
<caret line="14" column="27" lean-forward="false" selection-start-line="14" selection-start-column="27" selection-end-line="14" selection-end-column="27" />
|
||||
<folding />
|
||||
</first_editor>
|
||||
<second_editor>
|
||||
<js_state />
|
||||
</second_editor>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="worker.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/worker.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="8092">
|
||||
<caret line="476" column="0" lean-forward="false" selection-start-line="476" selection-start-column="0" selection-end-line="476" selection-end-column="0" />
|
||||
<folding>
|
||||
<element signature="e#1#10#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
</leaf>
|
||||
</split-first>
|
||||
<split-second>
|
||||
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
|
||||
<file leaf-file-name="worker.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/worker.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="3077">
|
||||
<caret line="283" column="104" lean-forward="false" selection-start-line="283" selection-start-column="104" selection-end-line="283" selection-end-column="104" />
|
||||
<folding>
|
||||
<element signature="e#1#10#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="test.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/test.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="2910">
|
||||
<caret line="228" column="8" lean-forward="false" selection-start-line="228" selection-start-column="8" selection-end-line="229" selection-end-column="62" />
|
||||
<folding>
|
||||
<element signature="e#0#10#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="test_dispatcher.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/tests/test_dispatcher.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="1938">
|
||||
<caret line="114" column="17" lean-forward="false" selection-start-line="114" selection-start-column="17" selection-end-line="114" selection-end-column="17" />
|
||||
<folding>
|
||||
<element signature="e#0#9#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="dispatcher.py" pinned="false" current-in-tab="true">
|
||||
<entry file="file://$PROJECT_DIR$/dispatcher.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="-3186">
|
||||
<caret line="262" column="29" lean-forward="false" selection-start-line="262" selection-start-column="29" selection-end-line="262" selection-end-column="29" />
|
||||
<folding>
|
||||
<element signature="e#482#718#0" expanded="false" />
|
||||
<element signature="e#2807#2905#0" expanded="false" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
<file leaf-file-name="client.py" pinned="false" current-in-tab="false">
|
||||
<entry file="file://$PROJECT_DIR$/client.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="187">
|
||||
<caret line="50" column="72" lean-forward="false" selection-start-line="50" selection-start-column="72" selection-end-line="50" selection-end-column="72" />
|
||||
<folding>
|
||||
<element signature="e#0#10#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</file>
|
||||
</leaf>
|
||||
</split-second>
|
||||
</splitter>
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Python Script" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="FindInProjectRecents">
|
||||
<findStrings>
|
||||
<find>queue</find>
|
||||
<find>remo</find>
|
||||
<find>htt</find>
|
||||
<find>dec</find>
|
||||
<find>canceled</find>
|
||||
<find>http</find>
|
||||
<find>class</find>
|
||||
<find>cgi</find>
|
||||
<find>render</find>
|
||||
<find>Job</find>
|
||||
<find>curre</find>
|
||||
<find>rege</find>
|
||||
<find>get_worker</find>
|
||||
<find>don</find>
|
||||
<find>time</find>
|
||||
<find>worker</find>
|
||||
<find>process_joblist</find>
|
||||
<find>onli</find>
|
||||
<find>loc</find>
|
||||
<find>str(</find>
|
||||
<find>proc</find>
|
||||
<find>synch</find>
|
||||
<find>debu</find>
|
||||
<find>pol</find>
|
||||
<find>done</find>
|
||||
<find>Pop</find>
|
||||
<find>RLock</find>
|
||||
<find>time.</find>
|
||||
<find>col</find>
|
||||
<find>layou</find>
|
||||
</findStrings>
|
||||
<replaceStrings>
|
||||
<replace>action_queue</replace>
|
||||
</replaceStrings>
|
||||
</component>
|
||||
<component name="IdeDocumentHistory">
|
||||
<option name="CHANGED_PATHS">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/server.py" />
|
||||
<option value="$PROJECT_DIR$/tests/test_dispatcher.py" />
|
||||
<option value="$PROJECT_DIR$/dispatcher.py" />
|
||||
<option value="$PROJECT_DIR$/worker.py" />
|
||||
<option value="$PROJECT_DIR$/test.py" />
|
||||
<option value="$PROJECT_DIR$/README.md" />
|
||||
<option value="$PROJECT_DIR$/client.py" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectFrameBounds" extendedState="6">
|
||||
<option name="x" value="1920" />
|
||||
<option name="width" value="1920" />
|
||||
<option name="height" value="1050" />
|
||||
</component>
|
||||
<component name="ProjectView">
|
||||
<navigator currentView="ProjectPane" proportions="" version="1">
|
||||
<flattenPackages />
|
||||
<showMembers />
|
||||
<showModules />
|
||||
<showLibraryContents />
|
||||
<hideEmptyPackages />
|
||||
<abbreviatePackageNames />
|
||||
<autoscrollToSource />
|
||||
<autoscrollFromSource />
|
||||
<sortByType />
|
||||
<manualOrder />
|
||||
<foldersAlwaysOnTop value="true" />
|
||||
</navigator>
|
||||
<panes>
|
||||
<pane id="ProjectPane">
|
||||
<subPane>
|
||||
<expand>
|
||||
<path>
|
||||
<item name="PushBlendPull" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="PushBlendPull" type="47feb1d3:ProjectViewModuleNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="PushBlendPull" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="PushBlendPull" type="47feb1d3:ProjectViewModuleNode" />
|
||||
<item name="PushBlendPull" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="PushBlendPull" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="PushBlendPull" type="47feb1d3:ProjectViewModuleNode" />
|
||||
<item name="PushBlendPull" type="462c0819:PsiDirectoryNode" />
|
||||
<item name="tests" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="PushBlendPull" type="b2602c69:ProjectViewProjectNode" />
|
||||
<item name="PushBlendPull" type="47feb1d3:ProjectViewModuleNode" />
|
||||
<item name="scripts" type="462c0819:PsiDirectoryNode" />
|
||||
</path>
|
||||
</expand>
|
||||
<select />
|
||||
</subPane>
|
||||
</pane>
|
||||
<pane id="Scratches" />
|
||||
<pane id="Scope" />
|
||||
</panes>
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/../scripts" />
|
||||
<property name="settings.editor.selected.configurable" value="preferences.sourceCode.Python" />
|
||||
</component>
|
||||
<component name="PyConsoleOptionsProvider">
|
||||
<option name="myPythonConsoleState">
|
||||
<console-settings module-name="PushBlendPull" is-module-sdk="true">
|
||||
<option name="myUseModuleSdk" value="true" />
|
||||
<option name="myModuleName" value="PushBlendPull" />
|
||||
</console-settings>
|
||||
</option>
|
||||
</component>
|
||||
<component name="RunDashboard">
|
||||
<option name="ruleStates">
|
||||
<list>
|
||||
<RuleState>
|
||||
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
|
||||
</RuleState>
|
||||
<RuleState>
|
||||
<option name="name" value="StatusDashboardGroupingRule" />
|
||||
</RuleState>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="RunManager" selected="Python.dispatcher">
|
||||
<configuration name="dispatcher" type="PythonConfigurationType" factoryName="Python">
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="$PROJECT_DIR$/../../python/Envs/PushBlendPull/bin/python" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="IS_MODULE_SDK" value="false" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<module name="PushBlendPull" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/dispatcher.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
</configuration>
|
||||
<configuration name="test_dispatcher" type="PythonConfigurationType" factoryName="Python" temporary="true">
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/tests" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<module name="PushBlendPull" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/tests/test_dispatcher.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
</configuration>
|
||||
<configuration name="worker" type="PythonConfigurationType" factoryName="Python" temporary="true">
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="PARENT_ENVS" value="true" />
|
||||
<envs>
|
||||
<env name="PYTHONUNBUFFERED" value="1" />
|
||||
</envs>
|
||||
<option name="SDK_HOME" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||
<option name="IS_MODULE_SDK" value="true" />
|
||||
<option name="ADD_CONTENT_ROOTS" value="true" />
|
||||
<option name="ADD_SOURCE_ROOTS" value="true" />
|
||||
<module name="PushBlendPull" />
|
||||
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/worker.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" value="false" />
|
||||
<option name="EMULATE_TERMINAL" value="false" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
</configuration>
|
||||
<list size="3">
|
||||
<item index="0" class="java.lang.String" itemvalue="Python.dispatcher" />
|
||||
<item index="1" class="java.lang.String" itemvalue="Python.worker" />
|
||||
<item index="2" class="java.lang.String" itemvalue="Python.test_dispatcher" />
|
||||
</list>
|
||||
<recent_temporary>
|
||||
<list size="2">
|
||||
<item index="0" class="java.lang.String" itemvalue="Python.test_dispatcher" />
|
||||
<item index="1" class="java.lang.String" itemvalue="Python.worker" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
<component name="ShelveChangesManager" show_recycled="false">
|
||||
<option name="remove_strategy" value="false" />
|
||||
</component>
|
||||
<component name="SvnConfiguration">
|
||||
<configuration />
|
||||
</component>
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="2dd0ebfd-c64a-47fb-842f-d234330d06b7" name="Default" comment="" />
|
||||
<created>1514719771907</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1514719771907</updated>
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="ToolWindowManager">
|
||||
<frame x="1920" y="0" width="1920" height="1050" extended-state="6" />
|
||||
<editor active="true" />
|
||||
<layout>
|
||||
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.104902476" sideWeight="0.5" order="0" side_tool="false" content_ui="combo" />
|
||||
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.3991323" sideWeight="0.44860306" order="7" side_tool="true" content_ui="tabs" />
|
||||
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Python Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3394794" sideWeight="0.85714287" order="7" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3286334" sideWeight="0.4391144" order="2" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32910052" sideWeight="0.49791667" order="7" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" />
|
||||
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3991323" sideWeight="0.55139697" order="3" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" />
|
||||
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
||||
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
|
||||
</layout>
|
||||
</component>
|
||||
<component name="VcsContentAnnotationSettings">
|
||||
<option name="myLimit" value="2678400000" />
|
||||
</component>
|
||||
<component name="XDebuggerManager">
|
||||
<breakpoint-manager>
|
||||
<breakpoints>
|
||||
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
|
||||
<url>file:///usr/lib/python3.6/socketserver.py</url>
|
||||
<line>318</line>
|
||||
<option name="timeStamp" value="37" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
|
||||
<url>file://$PROJECT_DIR$/worker.py</url>
|
||||
<line>311</line>
|
||||
<option name="timeStamp" value="107" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
|
||||
<url>file://$PROJECT_DIR$/worker.py</url>
|
||||
<line>133</line>
|
||||
<option name="timeStamp" value="114" />
|
||||
</line-breakpoint>
|
||||
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
|
||||
<url>file://$PROJECT_DIR$/dispatcher.py</url>
|
||||
<line>241</line>
|
||||
<option name="timeStamp" value="169" />
|
||||
</line-breakpoint>
|
||||
</breakpoints>
|
||||
<breakpoints-dialog>
|
||||
<breakpoints-dialog />
|
||||
</breakpoints-dialog>
|
||||
<option name="time" value="171" />
|
||||
</breakpoint-manager>
|
||||
<watches-manager />
|
||||
</component>
|
||||
<component name="debuggerHistoryManager">
|
||||
<expressions id="evaluateExpression">
|
||||
<expression>
|
||||
<expression-string>event_data</expression-string>
|
||||
<language-id>Python</language-id>
|
||||
<evaluation-mode>EXPRESSION</evaluation-mode>
|
||||
</expression>
|
||||
<expression>
|
||||
<expression-string>self._download_idle</expression-string>
|
||||
<language-id>Python</language-id>
|
||||
<evaluation-mode>EXPRESSION</evaluation-mode>
|
||||
</expression>
|
||||
<expression>
|
||||
<expression-string>reactor.getThreadPool()</expression-string>
|
||||
<language-id>Python</language-id>
|
||||
<evaluation-mode>EXPRESSION</evaluation-mode>
|
||||
</expression>
|
||||
<expression>
|
||||
<expression-string>reactor.getThreadPool().dumpStats()</expression-string>
|
||||
<language-id>Python</language-id>
|
||||
<evaluation-mode>EXPRESSION</evaluation-mode>
|
||||
</expression>
|
||||
<expression>
|
||||
<expression-string>reactor.getThreadPoll()</expression-string>
|
||||
<language-id>Python</language-id>
|
||||
<evaluation-mode>EXPRESSION</evaluation-mode>
|
||||
</expression>
|
||||
<expression>
|
||||
<expression-string>p.stdout.readline()</expression-string>
|
||||
<language-id>Python</language-id>
|
||||
<evaluation-mode>EXPRESSION</evaluation-mode>
|
||||
</expression>
|
||||
<expression>
|
||||
<expression-string>self.get_http_address(self.path_status)</expression-string>
|
||||
<language-id>Python</language-id>
|
||||
<evaluation-mode>EXPRESSION</evaluation-mode>
|
||||
</expression>
|
||||
<expression>
|
||||
<expression-string>'>'.join(job)</expression-string>
|
||||
<language-id>Python</language-id>
|
||||
<evaluation-mode>EXPRESSION</evaluation-mode>
|
||||
</expression>
|
||||
<expression>
|
||||
<expression-string>job.join('>')</expression-string>
|
||||
<language-id>Python</language-id>
|
||||
<evaluation-mode>EXPRESSION</evaluation-mode>
|
||||
</expression>
|
||||
</expressions>
|
||||
</component>
|
||||
<component name="editorHistoryManager">
|
||||
<entry file="file://$PROJECT_DIR$/test.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="0">
|
||||
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$USER_HOME$/Téléchargements/pydev-blender/doc/python_api/pypredef/aud.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="0">
|
||||
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/client.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="0">
|
||||
<caret line="0" column="10" lean-forward="false" selection-start-line="0" selection-start-column="10" selection-end-line="0" selection-end-column="10" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/twisted/web/resource.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="4233">
|
||||
<caret line="249" column="0" lean-forward="false" selection-start-line="249" selection-start-column="0" selection-end-line="249" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/worker.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="2771">
|
||||
<caret line="163" column="41" lean-forward="false" selection-start-line="159" selection-start-column="8" selection-end-line="163" selection-end-column="41" />
|
||||
<folding>
|
||||
<element signature="e#1#10#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/twisted/web/server.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="4403">
|
||||
<caret line="262" column="67" lean-forward="false" selection-start-line="262" selection-start-column="67" selection-end-line="262" selection-end-column="67" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/http/server.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="2397">
|
||||
<caret line="141" column="57" lean-forward="false" selection-start-line="141" selection-start-column="57" selection-end-line="141" selection-end-column="57" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/README.md">
|
||||
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
||||
<state split_layout="SPLIT">
|
||||
<first_editor relative-caret-position="153">
|
||||
<caret line="9" column="0" lean-forward="false" selection-start-line="9" selection-start-column="0" selection-end-line="9" selection-end-column="0" />
|
||||
<folding />
|
||||
</first_editor>
|
||||
<second_editor>
|
||||
<js_state />
|
||||
</second_editor>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/socketserver.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="13107">
|
||||
<caret line="775" column="35" lean-forward="false" selection-start-line="775" selection-start-column="35" selection-end-line="775" selection-end-column="35" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/threading.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="14569">
|
||||
<caret line="863" column="0" lean-forward="false" selection-start-line="863" selection-start-column="0" selection-end-line="863" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/test.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="2907">
|
||||
<caret line="188" column="36" lean-forward="false" selection-start-line="188" selection-start-column="36" selection-end-line="188" selection-end-column="36" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/http/server.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="2397">
|
||||
<caret line="141" column="57" lean-forward="false" selection-start-line="141" selection-start-column="57" selection-end-line="141" selection-end-column="57" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/worker.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="3604">
|
||||
<caret line="224" column="0" lean-forward="false" selection-start-line="224" selection-start-column="0" selection-end-line="224" selection-end-column="0" />
|
||||
<folding>
|
||||
<element signature="e#1#10#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/socketserver.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="8143">
|
||||
<caret line="483" column="0" lean-forward="false" selection-start-line="483" selection-start-column="0" selection-end-line="483" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/threading.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="14671">
|
||||
<caret line="863" column="0" lean-forward="false" selection-start-line="863" selection-start-column="0" selection-end-line="863" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/README.md">
|
||||
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
||||
<state split_layout="SPLIT">
|
||||
<first_editor relative-caret-position="0">
|
||||
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||
<folding />
|
||||
</first_editor>
|
||||
<second_editor>
|
||||
<js_state />
|
||||
</second_editor>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/test.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="3077">
|
||||
<caret line="198" column="35" lean-forward="false" selection-start-line="198" selection-start-column="35" selection-end-line="198" selection-end-column="35" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/socketserver.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="12716">
|
||||
<caret line="752" column="0" lean-forward="false" selection-start-line="752" selection-start-column="0" selection-end-line="752" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/http/server.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="2108">
|
||||
<caret line="141" column="57" lean-forward="false" selection-start-line="141" selection-start-column="57" selection-end-line="141" selection-end-column="57" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/threading.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="14569">
|
||||
<caret line="863" column="0" lean-forward="false" selection-start-line="863" selection-start-column="0" selection-end-line="863" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/worker.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="3009">
|
||||
<caret line="189" column="50" lean-forward="false" selection-start-line="189" selection-start-column="49" selection-end-line="189" selection-end-column="50" />
|
||||
<folding>
|
||||
<element signature="e#1#10#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/socketserver.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="13107">
|
||||
<caret line="775" column="35" lean-forward="false" selection-start-line="775" selection-start-column="35" selection-end-line="775" selection-end-column="35" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$USER_HOME$/Téléchargements/pydev-blender/doc/python_api/pypredef/aud.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="0">
|
||||
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/http/server.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="17">
|
||||
<caret line="141" column="57" lean-forward="false" selection-start-line="141" selection-start-column="57" selection-end-line="141" selection-end-column="57" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$USER_HOME$/Téléchargements/pydev-blender/doc/python_api/pypredef/bpy.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="0">
|
||||
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/bpy/2.79/scripts/modules/bpy/__init__.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="0">
|
||||
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/bpy/2.79/scripts/modules/bpy_types.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="0">
|
||||
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/bpy/2.79/scripts/modules/bpy_extras/__init__.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="0">
|
||||
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/bpy/2.79/scripts/modules/bpy/ops.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="3842">
|
||||
<caret line="226" column="0" lean-forward="false" selection-start-line="226" selection-start-column="0" selection-end-line="226" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/blendfile/__init__.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="179">
|
||||
<caret line="740" column="0" lean-forward="false" selection-start-line="740" selection-start-column="0" selection-end-line="740" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$USER_HOME$/.PyCharmCE2017.3/system/python_stubs/-1694100972/builtins.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="94639">
|
||||
<caret line="5567" column="6" lean-forward="false" selection-start-line="5567" selection-start-column="6" selection-end-line="5567" selection-end-column="6" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/subprocess.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="21726">
|
||||
<caret line="1278" column="0" lean-forward="false" selection-start-line="1278" selection-start-column="0" selection-end-line="1278" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/twisted/internet/defer.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="179">
|
||||
<caret line="652" column="0" lean-forward="false" selection-start-line="652" selection-start-column="0" selection-end-line="652" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/twisted/web/_responses.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="179">
|
||||
<caret line="34" column="0" lean-forward="false" selection-start-line="34" selection-start-column="0" selection-end-line="34" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/sseclient/__init__.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="-1258">
|
||||
<caret line="25" column="8" lean-forward="false" selection-start-line="25" selection-start-column="8" selection-end-line="25" selection-end-column="8" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/requests/adapters.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="1551">
|
||||
<caret line="507" column="0" lean-forward="false" selection-start-line="507" selection-start-column="0" selection-end-line="507" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/cgi.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="68">
|
||||
<caret line="4" column="26" lean-forward="false" selection-start-line="4" selection-start-column="26" selection-end-line="4" selection-end-column="26" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/queue.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="64">
|
||||
<caret line="203" column="0" lean-forward="true" selection-start-line="203" selection-start-column="0" selection-end-line="203" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$USER_HOME$/.PyCharmCE2017.3/system/python_stubs/-1694100972/_collections.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="71">
|
||||
<caret line="169" column="0" lean-forward="true" selection-start-line="169" selection-start-column="0" selection-end-line="169" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/twisted/web/server.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="3247">
|
||||
<caret line="194" column="0" lean-forward="false" selection-start-line="194" selection-start-column="0" selection-end-line="194" selection-end-column="0" />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/twisted/web/http.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="14042">
|
||||
<caret line="826" column="32" lean-forward="false" selection-start-line="826" selection-start-column="32" selection-end-line="826" selection-end-column="32" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$APPLICATION_HOME_DIR$/helpers/pydev/_pydev_imps/_pydev_execfile.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="289">
|
||||
<caret line="17" column="0" lean-forward="false" selection-start-line="17" selection-start-column="0" selection-end-line="17" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/twisted/web/resource.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="6919">
|
||||
<caret line="407" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="407" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/../../python/Envs/PushBlendPull/lib/python3.6/site-packages/twisted/internet/epollreactor.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="3689">
|
||||
<caret line="227" column="0" lean-forward="false" selection-start-line="227" selection-start-column="0" selection-end-line="227" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file:///usr/lib/python3.6/threading.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="8160">
|
||||
<caret line="486" column="6" lean-forward="false" selection-start-line="486" selection-start-column="6" selection-end-line="486" selection-end-column="6" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/tests/test_dispatcher.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="1938">
|
||||
<caret line="114" column="17" lean-forward="false" selection-start-line="114" selection-start-column="17" selection-end-line="114" selection-end-column="17" />
|
||||
<folding>
|
||||
<element signature="e#0#9#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/README.md">
|
||||
<provider selected="true" editor-type-id="split-provider[text-editor;MarkdownPreviewEditor]">
|
||||
<state split_layout="SPLIT">
|
||||
<first_editor relative-caret-position="238">
|
||||
<caret line="14" column="27" lean-forward="false" selection-start-line="14" selection-start-column="27" selection-end-line="14" selection-end-column="27" />
|
||||
<folding />
|
||||
</first_editor>
|
||||
<second_editor>
|
||||
<js_state />
|
||||
</second_editor>
|
||||
</state>
|
||||
</provider>
|
||||
<provider editor-type-id="text-editor">
|
||||
<state relative-caret-position="0">
|
||||
<caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
|
||||
<folding />
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/worker.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="3077">
|
||||
<caret line="283" column="104" lean-forward="false" selection-start-line="283" selection-start-column="104" selection-end-line="283" selection-end-column="104" />
|
||||
<folding>
|
||||
<element signature="e#1#10#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/test.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="2910">
|
||||
<caret line="228" column="8" lean-forward="false" selection-start-line="228" selection-start-column="8" selection-end-line="229" selection-end-column="62" />
|
||||
<folding>
|
||||
<element signature="e#0#10#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/client.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="187">
|
||||
<caret line="50" column="72" lean-forward="false" selection-start-line="50" selection-start-column="72" selection-end-line="50" selection-end-column="72" />
|
||||
<folding>
|
||||
<element signature="e#0#10#0" expanded="true" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
<entry file="file://$PROJECT_DIR$/dispatcher.py">
|
||||
<provider selected="true" editor-type-id="text-editor">
|
||||
<state relative-caret-position="-3186">
|
||||
<caret line="262" column="29" lean-forward="false" selection-start-line="262" selection-start-column="29" selection-end-line="262" selection-end-column="29" />
|
||||
<folding>
|
||||
<element signature="e#482#718#0" expanded="false" />
|
||||
<element signature="e#2807#2905#0" expanded="false" />
|
||||
</folding>
|
||||
</state>
|
||||
</provider>
|
||||
</entry>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,3 +1,26 @@
|
||||
# PushBlendPull
|
||||
|
||||
Blender plugin that allows you to deport the actual blending of your project on another computer.
|
||||
|
||||
## Requirements:
|
||||
1. Worker:
|
||||
* twisted (requires python header)
|
||||
|
||||
## Bugs:
|
||||
|
||||
|
||||
## Improvements:
|
||||
1. Worker
|
||||
* Clean after cancel
|
||||
2. Dispatcher
|
||||
* Accept workload parameter
|
||||
* Cancel Job/Farm(redispactch)/All
|
||||
* Only sync/ only blend
|
||||
* Exchange only needed files
|
||||
* P2P file exchange
|
||||
* Slave mode
|
||||
3. Plugin
|
||||
* Push/Pull to remote dispatcher
|
||||
* Allow farm configuration/file exchange
|
||||
* Track rendered/canceld scenes/blendfiles
|
||||
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
import bpy
|
||||
|
||||
from bpy.types import Panel, Operator, PropertyGroup, UIList
|
||||
from bpy.props import (StringProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
EnumProperty,
|
||||
PointerProperty,
|
||||
CollectionProperty
|
||||
)
|
||||
|
||||
|
||||
class PBPBlendFileProperty(PropertyGroup):
|
||||
path = StringProperty(subtype="FILE_PATH")
|
||||
filename = StringProperty(subtype="FILE_NAME")
|
||||
blending_enabled = BoolProperty(
|
||||
name="Enable or Disable",
|
||||
description="Enable blend file for blending",
|
||||
default=False)
|
||||
status = StringProperty(
|
||||
name="Status",
|
||||
description="Status of current file",
|
||||
default=""
|
||||
)
|
||||
|
||||
|
||||
class PBPWorkerProperty(PropertyGroup):
|
||||
hostname = StringProperty()
|
||||
port = StringProperty()
|
||||
remote_project_path = StringProperty()
|
||||
blending_enabled = BoolProperty(
|
||||
name="Enable or Disable",
|
||||
description="Enable farm for blending",
|
||||
default=False)
|
||||
status = StringProperty(
|
||||
name="Status",
|
||||
description="Status of current file",
|
||||
default=""
|
||||
)
|
||||
|
||||
|
||||
class PBPProperties(PropertyGroup):
|
||||
project_folder = StringProperty(
|
||||
name="Project folder",
|
||||
description="Path to project folder",
|
||||
default="//",
|
||||
subtype="DIR_PATH"
|
||||
)
|
||||
worker_list = CollectionProperty(type=PBPWorkerProperty)
|
||||
worker_list_index = IntProperty(name="Index for my_list", default=0)
|
||||
|
||||
blend_file_list = CollectionProperty(type=PBPBlendFileProperty)
|
||||
upload_status = StringProperty(
|
||||
name="Upload status",
|
||||
description="Status of current upload",
|
||||
default=""
|
||||
)
|
||||
|
||||
|
||||
class RENDER_UL_workers(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
layout.label(item.hostname)
|
||||
|
||||
|
||||
class PBPProjectPanel(Panel):
|
||||
bl_idname = "RENDER_PT_PBP_project_panel"
|
||||
bl_label = "PBP Workers Settings"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "render"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
# layout.operator(ProjectManagerStartBlend.bl_idname)
|
||||
configuration = context.window_manager.push_blend_pull
|
||||
layout.label(text="Workers")
|
||||
|
||||
row = layout.row()
|
||||
col = row.column()
|
||||
col.template_list("RENDER_UL_workers", "", configuration, "worker_list", configuration, "worker_list_index", rows=2)
|
||||
|
||||
col = row.column()
|
||||
sub = col.column(align=True)
|
||||
sub.operator("render.pbp_add_worker", icon='ZOOMIN', text="")
|
||||
sub.operator("render.pbp_del_worker", icon='ZOOMOUT', text="")
|
||||
|
||||
|
||||
class PBPWorkersPanel(Panel):
|
||||
bl_idname = "RENDER_PT_PBP_workers_panel"
|
||||
bl_label = "PBP Workers Settings"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = "render"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
# layout.operator(ProjectManagerStartBlend.bl_idname)
|
||||
configuration = context.window_manager.push_blend_pull
|
||||
layout.label(text="Workers")
|
||||
|
||||
row = layout.row()
|
||||
col = row.column()
|
||||
col.template_list("RENDER_UL_workers", "", configuration, "worker_list", configuration, "worker_list_index", rows=2)
|
||||
|
||||
col = row.column()
|
||||
sub = col.column(align=True)
|
||||
sub.operator("render.pbp_add_worker", icon='ZOOMIN', text="")
|
||||
sub.operator("render.pbp_del_worker", icon='ZOOMOUT', text="")
|
||||
|
||||
|
||||
class PBPAddWorkerOperator(Operator):
|
||||
bl_idname = "render.pbp_add_worker"
|
||||
bl_label = "Add a worker"
|
||||
|
||||
def execute(self, context):
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
wm = context.window_manager
|
||||
return wm.invoke_props_dialog(self, width=600)
|
||||
|
||||
def draw(self, context):
|
||||
configuration = context.window_manager.push_blend_pull
|
||||
new_worker = configuration.worker_list.add()
|
||||
layout = self.layout
|
||||
row = layout.row(align=True)
|
||||
row.prop(new_worker, "hostname")
|
||||
row.prop(new_worker, "port")
|
||||
row.prop(new_worker, "remote_project_path")
|
||||
row.prop(new_worker, "blending_enabled")
|
||||
|
||||
|
||||
class PBPDelWorkerOperator(Operator):
|
||||
bl_idname = "render.pbp_del_worker"
|
||||
bl_label = "Remove a worker"
|
||||
|
||||
def execute(self, context):
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
def register():
|
||||
|
||||
bpy.utils.register_class(RENDER_UL_workers)
|
||||
bpy.utils.register_module(__name__)
|
||||
bpy.types.WindowManager.push_blend_pull = PointerProperty(type=PBPProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
||||
del bpy.types.WindowManager.push_blend_pull
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
+574
@@ -0,0 +1,574 @@
|
||||
import os
|
||||
import re
|
||||
import math
|
||||
import json
|
||||
import logging
|
||||
|
||||
from threading import Thread, Event, RLock
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
|
||||
import requests
|
||||
from requests.exceptions import ConnectionError
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.web import server, resource, http
|
||||
|
||||
import sseclient
|
||||
import blendfile as bf
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
PROJECT_PATH = "/home/ewandor/projects/dev/blender/PushBlendPull"
|
||||
|
||||
class Index(resource.Resource):
|
||||
isLeaf = False
|
||||
|
||||
def getChild(self, path, request):
|
||||
if path == b'':
|
||||
return self
|
||||
return resource.Resource.getChild(self, path, request)
|
||||
|
||||
def render_GET(self, request):
|
||||
return get_index_content()
|
||||
|
||||
|
||||
class WorkpileResource(resource.Resource):
|
||||
isLeaf = True
|
||||
PARAM_WORKERS = b'workers'
|
||||
PARAM_WORKPILE = b'workpile'
|
||||
PARAM_PROJECT_PATH = b'project_path'
|
||||
|
||||
@classmethod
|
||||
def get_param_workers(cls, request):
|
||||
if cls.PARAM_WORKERS not in request.args:
|
||||
raise ValueError
|
||||
|
||||
regex_worker = "^(?P<server>[^:]+(:\d+)?[^:]*):(?P<remote_path>.+)$"
|
||||
workers = []
|
||||
for worker in request.args[cls.PARAM_WORKERS]:
|
||||
match = re.search(regex_worker, worker.decode("utf-8"))
|
||||
if match is None:
|
||||
raise ValueError
|
||||
workers.append((match.group('server'), match.group('remote_path'),))
|
||||
|
||||
return workers
|
||||
|
||||
@classmethod
|
||||
def get_param_workpile(cls, request):
|
||||
if cls.PARAM_WORKPILE not in request.args:
|
||||
raise ValueError
|
||||
|
||||
workpile = []
|
||||
for work in request.args[cls.PARAM_WORKPILE]:
|
||||
result = work.decode("utf-8").split('>')
|
||||
if len(result) != 2:
|
||||
raise ValueError
|
||||
workpile.append(tuple(result))
|
||||
|
||||
return workpile
|
||||
|
||||
@classmethod
|
||||
def get_param_project_path(cls, request):
|
||||
if cls.PARAM_PROJECT_PATH not in request.args:
|
||||
raise ValueError
|
||||
|
||||
project_path = request.args[cls.PARAM_PROJECT_PATH][0].decode("utf-8")
|
||||
if not os.path.isdir(project_path):
|
||||
raise FileNotFoundError
|
||||
|
||||
return project_path
|
||||
|
||||
def render_POST(self, request):
|
||||
try:
|
||||
logging.info('Request Handler: Received a new work pile')
|
||||
stats = JobDispatcher.process_workpile(
|
||||
self.get_param_workers(request),
|
||||
self.get_param_project_path(request),
|
||||
self.get_param_workpile(request)
|
||||
)
|
||||
return b'OK'
|
||||
except ValueError:
|
||||
return resource.ErrorPage(http.BAD_REQUEST, "Bad request", "Error in the request's format")
|
||||
except FileNotFoundError:
|
||||
return resource.NoResource("Blend file not found")
|
||||
pass
|
||||
|
||||
|
||||
class StatusResource(resource.Resource):
|
||||
isLeaf = True
|
||||
|
||||
def render_GET(self, request):
|
||||
return json.dumps(get_status()).encode()
|
||||
|
||||
|
||||
class Job:
|
||||
STATUS_QUEUED = 'queued'
|
||||
STATUS_DONE = 'done'
|
||||
STATUS_CANCELED = 'canceled'
|
||||
|
||||
def __init__(self, blend_file, scene, start_frame, end_frame, local_project_path, remote_project_path):
|
||||
self.blend_file = blend_file
|
||||
self.scene = scene
|
||||
self.start_frame = start_frame
|
||||
self.end_frame = end_frame
|
||||
|
||||
self.local_project_path = local_project_path
|
||||
self.remote_project_path = remote_project_path
|
||||
|
||||
self.current_frame = 0
|
||||
self.status = self.STATUS_QUEUED
|
||||
|
||||
def __str__(self):
|
||||
return "{}>{} {}:{}".format(
|
||||
self.blend_file,
|
||||
self.scene if self.scene is not None else "default_scene",
|
||||
self.start_frame if self.start_frame is not None else "start",
|
||||
self.end_frame if self.end_frame is not None else "end"
|
||||
)
|
||||
|
||||
def get_http_payload(self):
|
||||
result = {'blend_file': self.blend_file}
|
||||
if self.scene is not None:
|
||||
result['scene'] = self.scene
|
||||
if self.start_frame is not None:
|
||||
result['start_frame'] = self.start_frame
|
||||
if self.end_frame is not None:
|
||||
result['end_frame'] = self.end_frame
|
||||
|
||||
return result
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, str):
|
||||
return other == str(self)
|
||||
|
||||
return other == self.blend_file and other.scene == self.scene \
|
||||
and other.start_frame == self.start_frame \
|
||||
and other.end_frame == self.end_frame
|
||||
|
||||
|
||||
class Worker:
|
||||
path_status = '/status'
|
||||
path_job = '/job'
|
||||
path_cancel = '/cancel'
|
||||
worker_pool = []
|
||||
finished_jobs = []
|
||||
|
||||
@classmethod
|
||||
def get_worker(cls, authority):
|
||||
try:
|
||||
worker = cls.worker_pool[cls.worker_pool.index(authority)]
|
||||
except ValueError:
|
||||
worker = Worker(authority)
|
||||
|
||||
return worker
|
||||
|
||||
def __init__(self, authority):
|
||||
self.authority = authority
|
||||
self.job_queue = []
|
||||
self.idle = True
|
||||
|
||||
self._upload_lock = RLock()
|
||||
self._download_lock = RLock()
|
||||
self.upload_status = None
|
||||
self.download_status = None
|
||||
|
||||
self.worker_pool.append(self)
|
||||
|
||||
def get_http_address(self, path):
|
||||
return 'http://' + self.authority + path
|
||||
|
||||
def check_online(self):
|
||||
try:
|
||||
requests.get(self.get_http_address(self.path_status))
|
||||
except ConnectionError:
|
||||
return False
|
||||
return True
|
||||
|
||||
def process_joblist(self, project_path, remote_path, joblist):
|
||||
reactor.callInThread(self._do_process_joblist, project_path, remote_path, joblist)
|
||||
#Thread(target=self._do_process_joblist, args=(project_path, remote_path, joblist,)).start()
|
||||
|
||||
def _do_process_joblist(self, project_path, remote_path, joblist):
|
||||
logging.info('Worker<{}>: Synchronizing worker'.format(self))
|
||||
self.push_to(project_path, remote_path)
|
||||
for job in joblist:
|
||||
self.enqueue_job(job)
|
||||
|
||||
def push_to(self, project_path, remote_path):
|
||||
self._upload_lock.acquire()
|
||||
for upload_status in RemoteSynchronizer.push_to_worker(self, project_path, remote_path):
|
||||
self.upload_status = upload_status
|
||||
|
||||
self.upload_status = None
|
||||
self._upload_lock.release()
|
||||
|
||||
def pull_from(self, project_path, remote_path):
|
||||
self._download_lock.acquire()
|
||||
for download_status in RemoteSynchronizer.pull_from_worker(self, project_path, remote_path):
|
||||
self.download_status = download_status
|
||||
|
||||
self.download_status = None
|
||||
self._download_lock.release()
|
||||
|
||||
def enqueue_job(self, job):
|
||||
self.job_queue.append(job)
|
||||
if self.idle:
|
||||
ready_event = Event()
|
||||
Thread(target=self.start_listening, args=(ready_event,)).start()
|
||||
ready_event.wait()
|
||||
logging.info('Worker<{}>: Sending {}'.format(self, job))
|
||||
requests.post(self.get_http_address(self.path_job), data=job.get_http_payload())
|
||||
|
||||
def start_listening(self, ready_event):
|
||||
response = requests.get(
|
||||
self.get_http_address(self.path_status),
|
||||
stream=True,
|
||||
headers={'accept': 'text/event-stream'}
|
||||
)
|
||||
status_output = sseclient.SSEClient(response)
|
||||
stop = False
|
||||
self.idle = False
|
||||
ready_event.set()
|
||||
for event in status_output.events():
|
||||
event_data = json.loads(event.data)
|
||||
if event_data[1] == Job.STATUS_CANCELED:
|
||||
if event_data[0] in self.job_queue:
|
||||
job = self.job_queue.pop(self.job_queue.index(event_data[0]))
|
||||
job.status = Job.STATUS_CANCELED
|
||||
self.finished_jobs.append(job)
|
||||
|
||||
elif event_data[0] == self.job_queue[0]:
|
||||
if event_data[1] == Job.STATUS_DONE:
|
||||
job = self.job_queue.pop(0)
|
||||
job.status = Job.STATUS_DONE
|
||||
self.finished_jobs.append(job)
|
||||
logging.info('Worker<{}>: Job done {}, starting syncing local files'.format(self, job))
|
||||
Thread(target=self.pull_from, args=(job.local_project_path, job.remote_project_path)).start()
|
||||
if len(self.job_queue) == 0:
|
||||
stop = True
|
||||
elif event_data[1][:4] == 'Fra:':
|
||||
self.job_queue[0].current_frame = int(event_data[1][4:])
|
||||
if stop:
|
||||
status_output.close()
|
||||
self.idle = True
|
||||
return
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, str):
|
||||
return other == self.authority
|
||||
|
||||
return other.authority == self.authority
|
||||
|
||||
def __str__(self):
|
||||
return self.authority
|
||||
|
||||
def __hash__(self):
|
||||
return self.authority.__hash__()
|
||||
|
||||
|
||||
class RemoteSynchronizer:
|
||||
@classmethod
|
||||
def push_to_worker(cls, worker, local_path, remote_path):
|
||||
address = worker.authority.split(':')[0]
|
||||
|
||||
destination = "{}:{}".format(address, remote_path)
|
||||
return cls._do_sync(local_path, destination, ['*.blend1', 'README.md'])
|
||||
|
||||
@classmethod
|
||||
def pull_from_worker(cls, worker, local_path, remote_path):
|
||||
address = worker.authority.split(':')[0]
|
||||
|
||||
source = "{}:{}".format(address, remote_path)
|
||||
return cls._do_sync(source, local_path, ['*.blend'])
|
||||
|
||||
@classmethod
|
||||
def _do_sync(cls, source, destination, exclude_list):
|
||||
args = ['rsync', '-azP', '--info=progress2']
|
||||
for exclude_patern in exclude_list:
|
||||
args.append('--exclude')
|
||||
args.append(exclude_patern)
|
||||
|
||||
args.append(source)
|
||||
args.append(destination)
|
||||
|
||||
p = Popen(args, stdout=PIPE, stderr=STDOUT, universal_newlines=True, bufsize=1)
|
||||
last_line = ''
|
||||
while True:
|
||||
line = p.stdout.readline()
|
||||
if line == '':
|
||||
if p.poll() is not None:
|
||||
break
|
||||
elif line != last_line:
|
||||
logging.debug('Synch: stdout: {}'.format(line))
|
||||
last_line = line
|
||||
|
||||
status = cls.handleLine(line)
|
||||
if status is not None:
|
||||
yield status
|
||||
|
||||
|
||||
REGEX_PUSHPULL_STATUS = "\d+%"
|
||||
@classmethod
|
||||
def handleLine(cls, line, current_file=None):
|
||||
if line == 'end':
|
||||
return line
|
||||
t = re.search(cls.REGEX_PUSHPULL_STATUS, line)
|
||||
if t is not None:
|
||||
return t[0]
|
||||
|
||||
|
||||
def get_status():
|
||||
workers_status = {}
|
||||
subjobs_status = {}
|
||||
for worker in Worker.worker_pool:
|
||||
for subjob in worker.finished_jobs:
|
||||
jobkey = (subjob.blend_file, subjob.scene)
|
||||
if jobkey not in subjobs_status:
|
||||
subjobs_status[jobkey] = []
|
||||
|
||||
subjobs_status[jobkey].append(subjob)
|
||||
|
||||
job_queue_status = []
|
||||
for subjob in worker.job_queue:
|
||||
job_queue_status.append(get_subjob_stat(subjob))
|
||||
jobkey = (subjob.blend_file, subjob.scene)
|
||||
if jobkey not in subjobs_status:
|
||||
subjobs_status[jobkey] = []
|
||||
|
||||
subjobs_status[jobkey].append(subjob)
|
||||
workers_status[worker.authority] = {
|
||||
'upload_status': worker.upload_status,
|
||||
'download_status':worker.download_status,
|
||||
'job_queue': job_queue_status
|
||||
}
|
||||
jobs_status = {}
|
||||
for job, subjobs in subjobs_status.items():
|
||||
total_frame = 0
|
||||
frame_done = 0
|
||||
for subjob in subjobs:
|
||||
total_frame += subjob.end_frame - subjob.start_frame + 1
|
||||
frame_done += subjob.current_frame - subjob.start_frame + 1
|
||||
jobs_status['>'.join(job)] = {'frame_done': frame_done, 'total_frame': total_frame}
|
||||
|
||||
return {
|
||||
'project_path': PROJECT_PATH,
|
||||
'workers': workers_status,
|
||||
'jobs': jobs_status
|
||||
}
|
||||
|
||||
|
||||
def get_subjob_stat(subjob):
|
||||
return {
|
||||
'name': str(subjob),
|
||||
'current_frame': subjob.current_frame,
|
||||
'total_frame': subjob.end_frame - subjob.start_frame + 1
|
||||
}
|
||||
|
||||
|
||||
class JobDispatcher:
|
||||
|
||||
@staticmethod
|
||||
def process_workpile(worker_list, project_path, workpile):
|
||||
checked_worker_list = []
|
||||
remote_paths = {}
|
||||
for worker_param in worker_list:
|
||||
worker = Worker.get_worker(worker_param[0])
|
||||
if worker.check_online():
|
||||
checked_worker_list.append((worker, worker_param[1],))
|
||||
|
||||
dispatched_jobs = {w: [] for w in checked_worker_list}
|
||||
for blend_file, scene in workpile:
|
||||
dispatch = JobDispatcher.dispatch_scene_to_workers(
|
||||
checked_worker_list,
|
||||
project_path, blend_file, scene)
|
||||
for worker, job in dispatch.items():
|
||||
dispatched_jobs[worker].append(job)
|
||||
|
||||
for (worker, joblist) in dispatched_jobs.items():
|
||||
worker[0].process_joblist(project_path, worker[1], joblist)
|
||||
|
||||
return dispatched_jobs
|
||||
|
||||
@staticmethod
|
||||
def dispatch_scene_to_workers(worker_list, project_path, blend_file, scene):
|
||||
blend = bf.open_blend(blend_file)
|
||||
scenes_info = retrieve_blenderfile_scenes_info(blend)
|
||||
if scene not in scenes_info:
|
||||
raise ValueError
|
||||
|
||||
start_frame, end_frame = scenes_info[scene]
|
||||
|
||||
undispatched_frames = end_frame - start_frame + 1
|
||||
available_workers = len(worker_list)
|
||||
|
||||
next_start_frame = start_frame
|
||||
round_batch = False
|
||||
job_dispatch = {}
|
||||
for worker, remote_project_path in worker_list:
|
||||
if not round_batch and undispatched_frames % available_workers != 0:
|
||||
batch = math.ceil(undispatched_frames / available_workers)
|
||||
elif not round_batch:
|
||||
batch = undispatched_frames // available_workers
|
||||
round_batch = True
|
||||
|
||||
job = Job(
|
||||
blend_file.replace(project_path, remote_project_path),
|
||||
scene, next_start_frame, next_start_frame + batch - 1,
|
||||
project_path, remote_project_path)
|
||||
|
||||
job_dispatch[(worker, remote_project_path,)] = job
|
||||
|
||||
next_start_frame += batch
|
||||
undispatched_frames -= batch
|
||||
available_workers -= 1
|
||||
|
||||
return job_dispatch
|
||||
|
||||
|
||||
def main():
|
||||
host = ''
|
||||
port = 8026
|
||||
|
||||
root = Index()
|
||||
|
||||
root.putChild(b'status', StatusResource())
|
||||
root.putChild(b'workpile', WorkpileResource())
|
||||
# root.putChild(b'cancel', CancelResource)
|
||||
site = server.Site(root)
|
||||
reactor.listenTCP(port, site)
|
||||
|
||||
logging.info('Starting server {}:{}'.format(host, port))
|
||||
reactor.run()
|
||||
# worker_list = [('localhost:8025', '/home/ewandor/projects/dev/blender/PushBlendPull/',)]
|
||||
# project_path = '/home/ewandor/projects/dev/blender/PushBlendPull/'
|
||||
# dispatch_scene_to_workers(
|
||||
# worker_list,
|
||||
# project_path,
|
||||
# '/home/ewandor/projects/dev/blender/PushBlendPull/tests/pitou.blend',
|
||||
# 'thom'
|
||||
# )
|
||||
|
||||
|
||||
def retrieve_blendfile_assets(blendfile):
|
||||
assets_dna_type = ['Image', 'bSound', 'MovieClip']
|
||||
assets = []
|
||||
for block in blendfile.blocks:
|
||||
if block.dna_type_name in assets_dna_type and block.get(b'name') != '':
|
||||
assets.append(block.get(b'name'))
|
||||
|
||||
return assets
|
||||
|
||||
|
||||
def retrieve_blenderfile_scenes_info(blendfile):
|
||||
scenes_info = {}
|
||||
scenes = [block for block in blendfile.blocks if block.code == b'SC']
|
||||
for scene in scenes:
|
||||
scenes_info[scene.get((b'id', b'name'))[2:]] = (
|
||||
scene.get((b'r', b'sfra')),
|
||||
scene.get((b'r', b'efra')),
|
||||
)
|
||||
|
||||
return scenes_info
|
||||
|
||||
|
||||
# blend1 = bf.open_blend('tests/test_blendfile.blend1')
|
||||
# blend = bf.open_blend('tests/pitou.blend')
|
||||
#
|
||||
# seqs = [block for block in blend.blocks if block.dna_type_name == 'Sequence']
|
||||
#
|
||||
# for block in blend.blocks:
|
||||
# print(block.code, '-', block.dna_type_name)
|
||||
# print('coucou')
|
||||
# for block in blend1.blocks:
|
||||
# print(block.code, '-', block.dna_type_name)
|
||||
# # assets = retrieve_blendfile_assets(blend)
|
||||
# scenes_info = retrieve_blenderfile_scenes_info(blend)
|
||||
#
|
||||
#
|
||||
# print(scenes_info[2][0])
|
||||
def get_index_content():
|
||||
return '''<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<input type="submit" value="track_status" onclick="return get_status();" />
|
||||
<ul id="events"></ul>
|
||||
<form action="/workpile" method="post" target="_blank" onsubmit="return submit_workpile()" id="form_workpile">
|
||||
farms<br/>
|
||||
<input type="checkbox" name="workers" value="hawat:8025:/home/ggentile/projects/dev/blender/PushBlendPull/" checked>hawat:8025<br/>
|
||||
scenes<br/>
|
||||
<input type="checkbox" name="workpile" value="/home/ewandor/projects/dev/blender/PushBlendPull/tests/pitou.blend>pitou"/>//tests/pitou.blend>pitou<br/>
|
||||
<input type="checkbox" name="workpile" value="/home/ewandor/projects/dev/blender/PushBlendPull/tests/pitou.blend>thom"/>//tests/pitou.blend>thom<br/>
|
||||
<input type="checkbox" name="workpile" value="/home/ewandor/projects/dev/blender/PushBlendPull/tests/thom.blend>Scene"/>//tests/thom.blend>Scene<br/>
|
||||
project_path<input type="text" name="project_path" value="/home/ewandor/projects/dev/blender/PushBlendPull/"><br/>
|
||||
<input type="submit" value="send_job"/>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
var listening = false;
|
||||
function submit_workpile() {
|
||||
submit_ajax(
|
||||
document.getElementById("form_workpile"),
|
||||
function() {get_status();}
|
||||
);
|
||||
return false;
|
||||
}
|
||||
function submit_ajax(form, callback) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == XMLHttpRequest.DONE && request.status == 200) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
request.open("POST", form.action);
|
||||
request.send(new FormData(form));
|
||||
update_queue()
|
||||
}
|
||||
function get_status() {
|
||||
if (!listening) {
|
||||
var eventList = document.getElementById("events");
|
||||
while (eventList.firstChild) {
|
||||
eventList.removeChild(eventList.firstChild);
|
||||
}
|
||||
var evtSource = new EventSource("http://localhost:8025/status");
|
||||
evtSource.onmessage = function(e) {
|
||||
var newElement = document.createElement("li");
|
||||
newElement.innerHTML = "message: " + e.data;
|
||||
eventList.appendChild(newElement);
|
||||
if (eventList.childNodes.length > 5) {
|
||||
eventList.removeChild(eventList.firstChild);
|
||||
}
|
||||
}
|
||||
listening = true;
|
||||
}
|
||||
}
|
||||
|
||||
function update_queue() {
|
||||
var request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == XMLHttpRequest.DONE && request.status == 200) {
|
||||
var response = JSON.parse(request.responseText);
|
||||
var select = document.getElementById("jobs");
|
||||
while (select.firstChild) {
|
||||
select.removeChild(select.firstChild);
|
||||
}
|
||||
response.queue.forEach(function(job) {
|
||||
var newElement = document.createElement("option");
|
||||
newElement.text = job;
|
||||
newElement.value = job;
|
||||
select.appendChild(newElement);
|
||||
});
|
||||
}
|
||||
};
|
||||
request.open('GET', "http://localhost:8025/job", true);
|
||||
request.send(null);
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
document.getElementById("queued_jobs").addEventListener("click", update_queue, false);
|
||||
}, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>'''.encode()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,262 @@
|
||||
import bpy
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
from queue import Queue
|
||||
from threading import Thread, Event
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
|
||||
from bpy.types import Panel, Operator, PropertyGroup
|
||||
from bpy.props import (StringProperty,
|
||||
BoolProperty,
|
||||
IntProperty,
|
||||
FloatProperty,
|
||||
EnumProperty,
|
||||
PointerProperty,
|
||||
CollectionProperty
|
||||
)
|
||||
|
||||
|
||||
class BlendFileProperty(PropertyGroup):
|
||||
path = StringProperty(subtype="FILE_PATH")
|
||||
filename = StringProperty(subtype="FILE_NAME")
|
||||
blending_enabled = BoolProperty(
|
||||
name="Enable or Disable",
|
||||
description="Enable blend file for blending",
|
||||
default=False)
|
||||
status = StringProperty(
|
||||
name="Status",
|
||||
description="Status of current file",
|
||||
default=""
|
||||
)
|
||||
|
||||
|
||||
class PushBlendPullProperties(PropertyGroup):
|
||||
project_folder = StringProperty(
|
||||
name="Project folder",
|
||||
description="Path to project folder",
|
||||
default="//",
|
||||
subtype="DIR_PATH"
|
||||
)
|
||||
remote_host = StringProperty(
|
||||
name="Remote host",
|
||||
description="Address or host of remote",
|
||||
defaul =""
|
||||
)
|
||||
remote_folder = StringProperty(
|
||||
name="Remote project folder",
|
||||
description="Path to remote project folder",
|
||||
default=""
|
||||
)
|
||||
blend_file_list = CollectionProperty(type=BlendFileProperty)
|
||||
upload_status = StringProperty(
|
||||
name="Upload status",
|
||||
description="Status of current upload",
|
||||
default=""
|
||||
)
|
||||
|
||||
|
||||
class ProjectManagerRefreshBlenFileOperator(Operator):
|
||||
bl_idname = "wm.pm_refresh_blend"
|
||||
bl_label = "Refresh Blender Files"
|
||||
|
||||
def execute(self, context):
|
||||
blendFileList = context.window_manager.project_manager.blend_file_list
|
||||
enabled = []
|
||||
for blendFile in blendFileList:
|
||||
if blendFile.blending_enabled:
|
||||
enabled.append(blendFile.path)
|
||||
|
||||
blendFileList.clear()
|
||||
path = bpy.path.abspath(context.window_manager.project_manager.project_folder)
|
||||
for root, dirs, files in os.walk(path):
|
||||
for file in files:
|
||||
if file.endswith(".blend"):
|
||||
blendFile = blendFileList.add()
|
||||
blendFile.filename = os.path.splitext(file)[0]
|
||||
blendFile.path = os.path.join(root, file)
|
||||
blendFile.name = bpy.path.relpath(blendFile.path)
|
||||
blendFile.blending_enabled = blendFile.path in enabled
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class ProjectManagerStartBlend(Operator):
|
||||
bl_idname = "wm.project_manager"
|
||||
bl_label = "Start blend"
|
||||
|
||||
_timer = None
|
||||
_queue = None
|
||||
_worker = None
|
||||
|
||||
def execute(self, context):
|
||||
wm = context.window_manager
|
||||
|
||||
self._queue = Queue()
|
||||
self._worker = RemoteThreadHandler(
|
||||
self._queue,
|
||||
wm.project_manager.blend_file_list,
|
||||
bpy.path.abspath(wm.project_manager.project_folder),
|
||||
wm.project_manager.remote_host,
|
||||
wm.project_manager.remote_folder
|
||||
)
|
||||
self._worker.start()
|
||||
|
||||
self._timer = wm.event_timer_add(1, context.window)
|
||||
wm.modal_handler_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def modal(self, context, event):
|
||||
if event.type in {'ESC'}:
|
||||
self.cancel(context)
|
||||
print("canceled")
|
||||
return {'CANCELLED'}
|
||||
|
||||
if event.type == 'TIMER':
|
||||
while not self._queue.empty():
|
||||
self.handle_message(self._queue.get(), context.window_manager.project_manager)
|
||||
self._queue.task_done()
|
||||
if not self._worker.is_alive():
|
||||
self.cancel(context)
|
||||
print("finished")
|
||||
return {'FINISHED'}
|
||||
|
||||
return {'PASS_THROUGH'}
|
||||
|
||||
def cancel(self, context):
|
||||
wm = context.window_manager
|
||||
wm.event_timer_remove(self._timer)
|
||||
self._worker.stop()
|
||||
|
||||
def handle_message(self, msg, pm):
|
||||
if msg[0] == "push":
|
||||
print(msg)
|
||||
pm.upload_status = "push "+msg[1]
|
||||
elif msg[0] == "pull":
|
||||
msg[2].status = "pull "+msg[1]
|
||||
elif msg[0] == "blend":
|
||||
msg[2].status = "blend "+msg[1]
|
||||
|
||||
|
||||
class RemoteThreadHandler(Thread):
|
||||
def __init__(self, q, blendFileList, localFolder, remoteHost, remoteFolder):
|
||||
super(RemoteThreadHandler, self).__init__()
|
||||
|
||||
self._queue = q
|
||||
self.blendFileList = blendFileList
|
||||
self.remotePath = "{}:{}".format(remoteHost,remoteFolder)
|
||||
self.localFolder = localFolder
|
||||
self.remoteHost = remoteHost
|
||||
self.remoteFolder = remoteFolder
|
||||
|
||||
self._stop_event = Event()
|
||||
|
||||
def stop(self):
|
||||
self._stop_event.set()
|
||||
|
||||
def stopped(self):
|
||||
return self._stop_event.is_set()
|
||||
|
||||
ARGS_PUSH = "rsync -azP --info=progress2 --exclude '*.blend1' --exclude 'README.md' {} {}"
|
||||
ARGS_PULL = "rsync -azP --info=progress2 --exclude '*.blend' {} {}"
|
||||
ARGS_BLEND = "ssh -t hawat 'blender -b {} -a'"
|
||||
|
||||
REGEX_PUSHPULL_STATUS = "\d+%"
|
||||
|
||||
def handleLinePush(self, line, current_file=None):
|
||||
if line == 'end':
|
||||
self._queue.put(("push", "end",))
|
||||
return
|
||||
t = re.search(self.REGEX_PUSHPULL_STATUS, line)
|
||||
if t is not None:
|
||||
self._queue.put(("push", t[0],))
|
||||
|
||||
def handleLinePull(self, line, current_file):
|
||||
if line == 'end':
|
||||
self._queue.put(("pull", "end", current_file,))
|
||||
return
|
||||
t = re.search(self.REGEX_PUSHPULL_STATUS, line)
|
||||
if t is not None:
|
||||
self._queue.put(("pull", t[0], current_file,))
|
||||
|
||||
def run(self):
|
||||
self.start_process_handle_queue(self.ARGS_PUSH.format(self.localFolder, self.remotePath), self.handleLinePush)
|
||||
|
||||
for blendFile in self.blendFileList:
|
||||
if blendFile.blending_enabled:
|
||||
remoteBlendFilePath = blendFile.path.replace(self.localFolder, self.remoteFolder)
|
||||
self.start_process_handle_queue(self.ARGS_BLEND.format(remoteBlendFilePath), self.handleLineBlend, blendFile)
|
||||
|
||||
pullWorker = Thread(
|
||||
target=self.start_process_handle_queue,
|
||||
args=(self.ARGS_PULL.format(self.remotePath, self.localFolder) , self.handleLinePull, blendFile)
|
||||
)
|
||||
pullWorker.start()
|
||||
|
||||
def start_process_handle_queue(self, args, lineHandler, current_file = None):
|
||||
if self.stopped():
|
||||
return
|
||||
print(args)
|
||||
p = Popen(args, shell=True, stdout=PIPE, stderr=STDOUT, universal_newlines=True, bufsize=1)
|
||||
last_line = ''
|
||||
while not self.stopped():
|
||||
line = p.stdout.readline()
|
||||
if line == '':
|
||||
if p.poll() is not None:
|
||||
break
|
||||
elif line != last_line:
|
||||
lineHandler(line, current_file)
|
||||
last_line = line
|
||||
|
||||
if self.stopped():
|
||||
p.kill()
|
||||
|
||||
lineHandler('end', current_file)
|
||||
|
||||
|
||||
class ProjectManagerPanel(Panel):
|
||||
bl_idname = "OBJECT_PT_project_manager"
|
||||
bl_label = "Project Management"
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_context = ""
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator(ProjectManagerStartBlend.bl_idname)
|
||||
|
||||
configuration = context.window_manager.project_manager
|
||||
layout.label(text="Configuration")
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(configuration, "project_folder")
|
||||
row = layout.row(align=True)
|
||||
row.prop(configuration, "remote_host")
|
||||
row = layout.row(align=True)
|
||||
row.prop(configuration, "remote_folder")
|
||||
row = layout.row(align=True)
|
||||
row.prop(configuration, "upload_status")
|
||||
|
||||
layout.operator(ProjectManagerRefreshBlenFileOperator.bl_idname)
|
||||
fileList = configuration.blend_file_list
|
||||
for blendFile in sorted(fileList.values(), key=lambda file: file.path):
|
||||
row = layout.row()
|
||||
row.prop(blendFile, "blending_enabled", text=bpy.path.relpath(blendFile.path))
|
||||
row.prop(blendFile, "status", text="")
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
bpy.types.WindowManager.project_manager = PointerProperty(type=ProjectManagerProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
||||
del bpy.types.WindowManager.project_manage
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
Binary file not shown.
Binary file not shown.
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo $1 "start"
|
||||
sleep 1
|
||||
echo $1 "1/4"
|
||||
sleep 1
|
||||
echo $1 "2/4"
|
||||
sleep 1
|
||||
echo $1 "3/4"
|
||||
sleep 1
|
||||
echo $1 "4/4"
|
||||
sleep 1
|
||||
echo $1 "fin"
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,135 @@
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import logging
|
||||
|
||||
from queue import Queue, Empty
|
||||
from threading import Thread, Event
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
import requests
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.web import server, resource, http
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
class Index(resource.Resource):
|
||||
isLeaf = False
|
||||
|
||||
def getChild(self, path, request):
|
||||
if path == b'':
|
||||
return self
|
||||
return resource.Resource.getChild(self, path, request)
|
||||
|
||||
def render_GET(self, request):
|
||||
return ''
|
||||
|
||||
|
||||
class StatusResource(resource.Resource):
|
||||
isLeaf = True
|
||||
|
||||
def __init__(self):
|
||||
self.subscriptions = []
|
||||
self.last_status = ('idle',)
|
||||
|
||||
def response_callback(self, err, request):
|
||||
request.finish
|
||||
logging.info('Request Handler: connection either resulted in error or client closed')
|
||||
self.subscriptions.remove(request)
|
||||
|
||||
def add_event_stream_headers(self, request):
|
||||
request.setHeader('Content-Type', 'text/event-stream; charset=utf-8')
|
||||
request.setHeader("Access-Control-Allow-Origin", "*")
|
||||
return request
|
||||
|
||||
def add_json_headers(self, request):
|
||||
request.setHeader('Content-Type', 'application/json; charset=utf-8')
|
||||
return request
|
||||
|
||||
def publish(self, status):
|
||||
self.last_status = status
|
||||
for req in self.subscriptions:
|
||||
self.push_sse_mesage(req=req, msg=status)
|
||||
|
||||
def push_sse_mesage(self, req, msg):
|
||||
event_line = "data: {}\r\n".format(json.dumps(msg)) + '\r\n'
|
||||
req.write(event_line.encode())
|
||||
|
||||
def render_GET(self, request):
|
||||
accept_header = request.requestHeaders.getRawHeaders(b'accept')
|
||||
if b'text/event-stream' in accept_header:
|
||||
request = self.add_event_stream_headers(request) # 2. Format Headers
|
||||
request.write("")
|
||||
request.notifyFinish().addBoth(self.response_callback, request)
|
||||
self.subscriptions.append(request)
|
||||
logging.info('Request Handler: new subscriber ({})'.format(len(self.subscriptions)))
|
||||
|
||||
return server.NOT_DONE_YET
|
||||
else:
|
||||
return json.dumps(self.last_status).encode()
|
||||
|
||||
|
||||
class JobResource(resource.Resource):
|
||||
isLeaf = True
|
||||
|
||||
def __init__(self, fake_worker):
|
||||
self.fake_worker = fake_worker
|
||||
|
||||
def render_POST(self, request):
|
||||
self.fake_worker.start_fake_working()
|
||||
return b'OK'
|
||||
|
||||
def render_GET(self, request):
|
||||
return json.dumps({
|
||||
"current_job": '',
|
||||
"queue": []
|
||||
}).encode()
|
||||
|
||||
|
||||
def main():
|
||||
host = ''
|
||||
port = 8025
|
||||
|
||||
for x in range(1, 255):
|
||||
interface = '127.0.0.{}'.format(x)
|
||||
root = Index()
|
||||
status_output = StatusResource()
|
||||
worker = FakeWorker(status_output)
|
||||
root.putChild(b'status', status_output)
|
||||
root.putChild(b'job', JobResource(worker))
|
||||
site = server.Site(root)
|
||||
reactor.listenTCP(port, site, interface=interface)
|
||||
|
||||
reactor.addSystemEventTrigger("after", "startup", worker.stop_worker)
|
||||
logging.info('Starting server {}:{}'.format(host, port))
|
||||
reactor.run()
|
||||
|
||||
def send_request_to_dispatcher():
|
||||
request_param = {
|
||||
'workers': '',
|
||||
'workpile': '',
|
||||
'project_path': ''
|
||||
}
|
||||
requests.post('http://localhost:8026/workpile', data=request_param)
|
||||
|
||||
class FakeWorker:
|
||||
def __init__(self, status_output):
|
||||
self._stop_working = Event()
|
||||
self._cancel_job = Event()
|
||||
self._status_output = status_output
|
||||
|
||||
self._last_output_status = ''
|
||||
self._current_job = None
|
||||
|
||||
def start_fake_working(self):
|
||||
worker_thread = Thread(target=self._do_fake_work)
|
||||
worker_thread.start()
|
||||
|
||||
def _do_fake_work(self):
|
||||
for x in range(1,1000):
|
||||
self._status_output.publish(('fakeblendfile>fakescene', "Fra:{}".format(x),))
|
||||
self._status_output.publish(('fakeblendfile>fakescene', "done",))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,480 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import json
|
||||
import logging
|
||||
|
||||
from queue import Queue, Empty
|
||||
from threading import Thread, Event
|
||||
from subprocess import Popen, PIPE, STDOUT
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.web import server, resource, http
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
class Index(resource.Resource):
|
||||
isLeaf = False
|
||||
|
||||
def getChild(self, path, request):
|
||||
if path == b'':
|
||||
return self
|
||||
return resource.Resource.getChild(self, path, request)
|
||||
|
||||
def render_GET(self, request):
|
||||
return get_index_content()
|
||||
|
||||
|
||||
class StatusResource(resource.Resource):
|
||||
isLeaf = True
|
||||
|
||||
def __init__(self):
|
||||
self.subscriptions = []
|
||||
self.last_status = ('idle',)
|
||||
|
||||
def response_callback(self, err, request):
|
||||
request.finish
|
||||
logging.info('Request Handler: connection either resulted in error or client closed')
|
||||
self.subscriptions.remove(request)
|
||||
|
||||
def add_event_stream_headers(self, request):
|
||||
request.setHeader('Content-Type', 'text/event-stream; charset=utf-8')
|
||||
request.setHeader("Access-Control-Allow-Origin", "*")
|
||||
return request
|
||||
|
||||
def add_json_headers(self, request):
|
||||
request.setHeader('Content-Type', 'application/json; charset=utf-8')
|
||||
return request
|
||||
|
||||
def publish(self, status):
|
||||
self.last_status = status
|
||||
for req in self.subscriptions:
|
||||
self.push_sse_mesage(req=req, msg=status)
|
||||
|
||||
def push_sse_mesage(self, req, msg):
|
||||
event_line = "data: {}\r\n".format(json.dumps(msg)) + '\r\n'
|
||||
req.write(event_line.encode())
|
||||
|
||||
def render_GET(self, request):
|
||||
accept_header = request.requestHeaders.getRawHeaders(b'accept')
|
||||
if b'text/event-stream' in accept_header:
|
||||
request = self.add_event_stream_headers(request) # 2. Format Headers
|
||||
request.write("")
|
||||
request.notifyFinish().addBoth(self.response_callback, request)
|
||||
self.subscriptions.append(request)
|
||||
logging.info('Request Handler: new subscriber ({})'.format(len(self.subscriptions)))
|
||||
|
||||
return server.NOT_DONE_YET
|
||||
else:
|
||||
return json.dumps(self.last_status).encode()
|
||||
|
||||
|
||||
class JobResource(resource.Resource):
|
||||
isLeaf = True
|
||||
PARAM_BLEND_FILE = b'blend_file'
|
||||
PARAM_SCENE = b'scene'
|
||||
PARAM_START_FRAME = b'start_frame'
|
||||
PARAM_END_FRAME = b'end_frame'
|
||||
|
||||
def __init__(self, worker):
|
||||
self.worker = worker
|
||||
|
||||
@classmethod
|
||||
def get_param_blend_file(cls, request):
|
||||
if cls.PARAM_BLEND_FILE not in request.args or not request.args[cls.PARAM_BLEND_FILE][0].endswith(b'.blend'):
|
||||
raise ValueError
|
||||
|
||||
blend_file = request.args[cls.PARAM_BLEND_FILE][0].decode("utf-8")
|
||||
if not os.path.isfile(blend_file):
|
||||
raise FileNotFoundError
|
||||
|
||||
return blend_file
|
||||
|
||||
@classmethod
|
||||
def get_param_scene(cls, request):
|
||||
if cls.PARAM_SCENE not in request.args or request.args[cls.PARAM_SCENE][0] == b'':
|
||||
return None
|
||||
|
||||
return request.args[cls.PARAM_SCENE][0].decode("utf-8")
|
||||
|
||||
@classmethod
|
||||
def get_param_start_frame(cls, request):
|
||||
if cls.PARAM_START_FRAME not in request.args or request.args[cls.PARAM_START_FRAME][0] == b'':
|
||||
return None
|
||||
|
||||
start_frame = request.args[cls.PARAM_START_FRAME][0]
|
||||
if not start_frame.isdigit():
|
||||
raise ValueError
|
||||
|
||||
return int(start_frame)
|
||||
|
||||
@classmethod
|
||||
def get_param_end_frame(cls, request):
|
||||
if cls.PARAM_END_FRAME not in request.args or request.args[cls.PARAM_END_FRAME][0] == b'':
|
||||
return None
|
||||
|
||||
end_frame = request.args[cls.PARAM_END_FRAME][0]
|
||||
if not end_frame.isdigit():
|
||||
raise ValueError
|
||||
|
||||
return int(end_frame)
|
||||
|
||||
def prepare_job(self, request):
|
||||
return Job(
|
||||
self.get_param_blend_file(request),
|
||||
scene=self.get_param_scene(request),
|
||||
start_frame=self.get_param_start_frame(request),
|
||||
end_frame=self.get_param_end_frame(request)
|
||||
)
|
||||
|
||||
def render_POST(self, request):
|
||||
try:
|
||||
job = self.prepare_job(request)
|
||||
logging.info('Request Handler: received a job request: {}'.format(job))
|
||||
self.worker.enqueue_job(job)
|
||||
return b'OK'
|
||||
except ValueError:
|
||||
return resource.ErrorPage(http.BAD_REQUEST, "Bad request", "Error in the request's format")
|
||||
except FileNotFoundError:
|
||||
return resource.NoResource("Blend file not found")
|
||||
|
||||
def render_GET(self, request):
|
||||
return json.dumps({
|
||||
"current_job": str(self.worker.get_current_job()),
|
||||
"queue": [str(job) for job in self.worker.get_job_queue()]
|
||||
}).encode()
|
||||
|
||||
|
||||
class CancelResource(resource.Resource):
|
||||
isLeaf = True
|
||||
PARAM_TYPE = b'type'
|
||||
PARAM_JOBS = b'jobs'
|
||||
|
||||
CANCEL_TYPE_QUEUED_JOBS = b'queued_jobs'
|
||||
CANCEL_TYPE_QUEUE = b'queue'
|
||||
CANCEL_TYPE_CURRENT_JOB = b'current_job'
|
||||
CANCEL_TYPE_ALL = b'all'
|
||||
|
||||
def __init__(self, worker):
|
||||
self.worker = worker
|
||||
|
||||
@classmethod
|
||||
def get_param_type(cls, request):
|
||||
if cls.PARAM_TYPE not in request.args:
|
||||
raise ValueError
|
||||
|
||||
param_type = request.args[cls.PARAM_TYPE][0]
|
||||
available_type = [cls.CANCEL_TYPE_QUEUED_JOBS, cls.CANCEL_TYPE_QUEUE,
|
||||
cls.CANCEL_TYPE_CURRENT_JOB, cls.CANCEL_TYPE_ALL]
|
||||
if param_type not in available_type:
|
||||
raise ValueError
|
||||
|
||||
return param_type
|
||||
|
||||
@classmethod
|
||||
def get_param_jobs(cls, request):
|
||||
if cls.PARAM_JOBS not in request.args:
|
||||
raise ValueError
|
||||
|
||||
jobs = request.args[cls.PARAM_JOBS]
|
||||
|
||||
return [job.decode("utf-8") for job in jobs]
|
||||
|
||||
def render_POST(self, request):
|
||||
logging.info('Request Handler: received cancel request')
|
||||
try:
|
||||
type_param = self.get_param_type(request)
|
||||
if type_param == self.CANCEL_TYPE_QUEUED_JOBS:
|
||||
self.worker.remove_job_list_from_queue(self.get_param_jobs(request))
|
||||
elif type_param == self.CANCEL_TYPE_QUEUE:
|
||||
self.worker.clear_job_queue()
|
||||
elif type_param == self.CANCEL_TYPE_CURRENT_JOB:
|
||||
self.worker.cancel_current_job()
|
||||
elif type_param == self.CANCEL_TYPE_ALL:
|
||||
self.worker.clear_job_queue()
|
||||
self.worker.cancel_current_job()
|
||||
except ValueError:
|
||||
return resource.ErrorPage(http.BAD_REQUEST, "Bad request", "Error in the request's format")
|
||||
|
||||
return b'OK'
|
||||
|
||||
|
||||
def main():
|
||||
host = ''
|
||||
port = 8025
|
||||
|
||||
root = Index()
|
||||
status_output = StatusResource()
|
||||
worker = QueueConsumer(status_output)
|
||||
root.putChild(b'status', status_output)
|
||||
root.putChild(b'job', JobResource(worker))
|
||||
root.putChild(b'cancel', CancelResource(worker))
|
||||
site = server.Site(root)
|
||||
reactor.listenTCP(port, site)
|
||||
|
||||
worker.start_worker()
|
||||
reactor.addSystemEventTrigger("before", "shutdown", worker.stop_worker)
|
||||
|
||||
logging.info('Starting server {}:{}'.format(host, port))
|
||||
reactor.run()
|
||||
|
||||
|
||||
class QueueConsumer:
|
||||
def __init__(self, status_output):
|
||||
self._job_queue = EditableQueue()
|
||||
self._stop_working = Event()
|
||||
self._cancel_job = Event()
|
||||
self._status_output = status_output
|
||||
|
||||
self._last_output_status = ''
|
||||
self._current_job = None
|
||||
|
||||
def start_worker(self):
|
||||
self._stop_working.clear()
|
||||
worker_thread = Thread(target=self._consume_queue)
|
||||
worker_thread.start()
|
||||
|
||||
def stop_worker(self):
|
||||
self._stop_working.set()
|
||||
|
||||
def enqueue_job(self, job):
|
||||
self._job_queue.put(job)
|
||||
|
||||
def get_job_queue(self):
|
||||
return self._job_queue.as_list()
|
||||
|
||||
def get_current_job(self):
|
||||
return self._current_job
|
||||
|
||||
def cancel_current_job(self):
|
||||
self._cancel_job.set()
|
||||
|
||||
def remove_job_list_from_queue(self, job_list):
|
||||
removed_jobs = self._job_queue.remove_sublist(job_list)
|
||||
for job in removed_jobs:
|
||||
self._status_output.publish((str(job), Job.STATUS_CANCELED,))
|
||||
|
||||
def clear_job_queue(self):
|
||||
removed_jobs = self._job_queue.clear()
|
||||
for job in removed_jobs:
|
||||
self._status_output.publish((str(job), Job.STATUS_CANCELED,))
|
||||
|
||||
def _consume_queue(self):
|
||||
while not self._stop_working.is_set():
|
||||
try:
|
||||
job = self._job_queue.get(timeout=1)
|
||||
except Empty:
|
||||
continue
|
||||
self._current_job = job
|
||||
self._execute_job()
|
||||
self._current_job = None
|
||||
|
||||
def _execute_job(self):
|
||||
logging.info('Worker: starting a new job: {}'.format(self._current_job))
|
||||
start_time = time.time()
|
||||
p = Popen(["/usr/bin/blender"] + self._current_job.get_blender_args(),
|
||||
stdout=PIPE, stderr=STDOUT, universal_newlines=True, bufsize=1)
|
||||
while not self._cancel_job.is_set():
|
||||
line = p.stdout.readline()
|
||||
if line == '':
|
||||
if p.poll() is not None:
|
||||
logging.info('Worker: job finished: {}'.format(self._current_job))
|
||||
self._status_output.publish((str(self._current_job), Job.STATUS_DONE, time.time() - start_time,))
|
||||
break
|
||||
else:
|
||||
status = self._parse_stdout(line)
|
||||
if status is not None:
|
||||
logging.info('Worker: job status is: {}'.format(status))
|
||||
self._status_output.publish((str(self._current_job), status,))
|
||||
|
||||
if self._cancel_job.is_set():
|
||||
logging.info('Worker: canceling: {}'.format(self._current_job))
|
||||
p.kill()
|
||||
self._cancel_job.clear()
|
||||
self._status_output.publish((str(self._current_job), Job.STATUS_CANCELED,))
|
||||
|
||||
def _parse_stdout(self, line):
|
||||
logging.debug('Worker: getting output: {}'.format(line))
|
||||
regex_blend_status = "Fra:\d+"
|
||||
t = re.search(regex_blend_status, line)
|
||||
if t is not None and t[0] != self._last_output_status:
|
||||
self._last_output_status = t[0]
|
||||
return t[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class EditableQueue(Queue):
|
||||
def remove_sublist(self, item_list):
|
||||
with self.not_empty:
|
||||
removed_items = []
|
||||
for item in item_list:
|
||||
try:
|
||||
self.queue.remove(item)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
removed_items.append(item)
|
||||
return removed_items
|
||||
|
||||
def clear(self):
|
||||
with self.not_empty:
|
||||
removed_items = list(self.queue)
|
||||
self.queue.clear()
|
||||
return removed_items
|
||||
|
||||
def as_list(self):
|
||||
with self.not_empty:
|
||||
return list(self.queue)
|
||||
|
||||
|
||||
class Job:
|
||||
STATUS_DONE = 'done'
|
||||
STATUS_CANCELED = 'canceled'
|
||||
|
||||
def __init__(self, blend_file, **kwargs):
|
||||
self.blend_file = blend_file
|
||||
self.scene = kwargs["scene"] if "scene" in kwargs else None
|
||||
self.start_frame = kwargs["start_frame"] if "start_frame" in kwargs else None
|
||||
self.end_frame = kwargs["end_frame"] if "end_frame" in kwargs else None
|
||||
|
||||
def __str__(self):
|
||||
return "{}>{} {}:{}".format(
|
||||
self.blend_file,
|
||||
self.scene if self.scene is not None else "default_scene",
|
||||
self.start_frame if self.start_frame is not None else "start",
|
||||
self.end_frame if self.end_frame is not None else "end"
|
||||
)
|
||||
|
||||
def get_blender_args(self):
|
||||
args = ['--background', self.blend_file]
|
||||
|
||||
if self.scene is not None:
|
||||
args.append('--scene')
|
||||
args.append(self.scene)
|
||||
|
||||
if self.start_frame is not None:
|
||||
args.append('--frame-start')
|
||||
args.append(str(self.start_frame))
|
||||
|
||||
if self.end_frame is not None:
|
||||
args.append('--frame-end')
|
||||
args.append(str(self.end_frame))
|
||||
|
||||
args.append('--render-anim')
|
||||
|
||||
return args
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, str):
|
||||
return other == str(self)
|
||||
|
||||
return other == self.blend_file and other.scene == self.scene \
|
||||
and other.start_frame == self.start_frame \
|
||||
and other.end_frame == self.end_frame
|
||||
|
||||
|
||||
def get_index_content():
|
||||
return '''<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<input type="submit" value="track_status" onclick="return start_event();" />
|
||||
<ul id="events"></ul>
|
||||
<form action="/job" method="post" target="_blank" onsubmit="return submit_job()" id="form_job">
|
||||
<input type="radio" name="blend_file" value="/home/ewandor/projects/dev/blender/PushBlendPull/tests/pitou.blend" checked>pitou<br/>
|
||||
<input type="radio" name="blend_file" value="/home/ewandor/projects/dev/blender/PushBlendPull/tests/thom.blend">thom<br/>
|
||||
scene<input type="text" name="scene" value="pitou"><br/>
|
||||
start_frame<input type="text" name="start_frame" value="1"><br/>
|
||||
end_frame<input type="text" name="end_frame" value="50"><br/>
|
||||
<input type="submit" value="send_job"/>
|
||||
</form>
|
||||
<form action="/cancel" method="post" target="_blank" onsubmit="return submit_cancel()" id="form_cancel">
|
||||
<input type="text" name="toto[tata]
|
||||
<input type="radio" name="type" value="queued_jobs" id="queued_jobs">queued_jobs<br/>
|
||||
<input type="radio" name="type" value="queue">queue<br/>
|
||||
<input type="radio" name="type" value="current_job" checked>current_job<br/>
|
||||
<input type="radio" name="type" value="all">all<br/>
|
||||
<select multiple name="jobs" id="jobs"></select>
|
||||
<input type="submit" value="cancel"/>
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
var listening = false;
|
||||
function submit_job() {
|
||||
submit_ajax(
|
||||
document.getElementById("form_job"),
|
||||
function() {start_event();}
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
function submit_cancel() {
|
||||
submit_ajax(
|
||||
document.getElementById("form_cancel"),
|
||||
function() {update_queue();}
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
function submit_ajax(form, callback) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == XMLHttpRequest.DONE && request.status == 200) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
request.open("POST", form.action);
|
||||
request.send(new FormData(form));
|
||||
update_queue()
|
||||
}
|
||||
function start_event() {
|
||||
if (!listening) {
|
||||
var eventList = document.getElementById("events");
|
||||
while (eventList.firstChild) {
|
||||
eventList.removeChild(eventList.firstChild);
|
||||
}
|
||||
var evtSource = new EventSource("/status");
|
||||
evtSource.onmessage = function(e) {
|
||||
var newElement = document.createElement("li");
|
||||
newElement.innerHTML = "message: " + e.data;
|
||||
eventList.appendChild(newElement);
|
||||
if (eventList.childNodes.length > 5) {
|
||||
eventList.removeChild(eventList.firstChild);
|
||||
}
|
||||
}
|
||||
listening = true;
|
||||
}
|
||||
}
|
||||
|
||||
function update_queue() {
|
||||
var request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function() {
|
||||
if (request.readyState == XMLHttpRequest.DONE && request.status == 200) {
|
||||
var response = JSON.parse(request.responseText);
|
||||
var select = document.getElementById("jobs");
|
||||
while (select.firstChild) {
|
||||
select.removeChild(select.firstChild);
|
||||
}
|
||||
response.queue.forEach(function(job) {
|
||||
var newElement = document.createElement("option");
|
||||
newElement.text = job;
|
||||
newElement.value = job;
|
||||
select.appendChild(newElement);
|
||||
});
|
||||
}
|
||||
};
|
||||
request.open('GET', "/job", true);
|
||||
request.send(null);
|
||||
}
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
document.getElementById("queued_jobs").addEventListener("click", update_queue, false);
|
||||
}, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>'''.encode()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user