Commit b039db88 authored by sumpfralle's avatar sumpfralle

added a GUI dialog for configuring multiprocessing and server mode


git-svn-id: https://pycam.svn.sourceforge.net/svnroot/pycam/trunk@833 bbaffbd6-741e-11dd-a85d-61de82d9cad9
parent 8896ea6c
...@@ -3,6 +3,7 @@ Version 0.4.1 - UNRELEASED ...@@ -3,6 +3,7 @@ Version 0.4.1 - UNRELEASED
* added adaptive positioning for DropCutter strategy (improves precision) * added adaptive positioning for DropCutter strategy (improves precision)
* allow conventional/climb milling style for ContourFollow and Engrave strategies * allow conventional/climb milling style for ContourFollow and Engrave strategies
* added support for single-line fonts text (based on fonts from QCAD) * added support for single-line fonts text (based on fonts from QCAD)
* parallel and distributed processing is configurable in a dialog
* visualize movements up to safety height properly * visualize movements up to safety height properly
* unify DropCutter behaviour for models that are higher than the defined bounding box * unify DropCutter behaviour for models that are higher than the defined bounding box
* always move up to safety height in this case * always move up to safety height in this case
......
...@@ -4957,6 +4957,7 @@ upon interesting bugs and weird results.</property> ...@@ -4957,6 +4957,7 @@ upon interesting bugs and weird results.</property>
<property name="title" translatable="yes">PyCAM Preferences</property> <property name="title" translatable="yes">PyCAM Preferences</property>
<property name="destroy_with_parent">True</property> <property name="destroy_with_parent">True</property>
<property name="type_hint">normal</property> <property name="type_hint">normal</property>
<property name="transient_for">ProjectWindow</property>
<child internal-child="vbox"> <child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox1"> <object class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -4978,6 +4979,7 @@ upon interesting bugs and weird results.</property> ...@@ -4978,6 +4979,7 @@ upon interesting bugs and weird results.</property>
<object class="GtkNotebook" id="notebook1"> <object class="GtkNotebook" id="notebook1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="tab_pos">left</property>
<child> <child>
<object class="GtkVBox" id="vbox7"> <object class="GtkVBox" id="vbox7">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -5005,7 +5007,8 @@ upon interesting bugs and weird results.</property> ...@@ -5005,7 +5007,8 @@ upon interesting bugs and weird results.</property>
<property name="receives_default">False</property> <property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">The toolpath generation is based on detecting collisions. <property name="tooltip_text" translatable="yes">The toolpath generation is based on detecting collisions.
The Open-Dynamic-Engine (ODE) is an alternative to the manual computation of these collisions. The Open-Dynamic-Engine (ODE) is an alternative to the manual computation of these collisions.
It is significantly faster, but the current release of ODE contains a nasty bug that rarely causes erroneous toolpaths at the corner of the model. Thus ODE is disabled by default.</property> It is significantly faster, but the current release of ODE contains a nasty bug that rarely causes erroneous toolpaths at the corner of the model. Thus ODE is disabled by default.
ODE can't be used in combination with parallel processing or server mode.</property>
<property name="draw_indicator">True</property> <property name="draw_indicator">True</property>
</object> </object>
<packing> <packing>
...@@ -5018,7 +5021,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5018,7 +5021,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug
<property name="visible">True</property> <property name="visible">True</property>
<property name="n_rows">2</property> <property name="n_rows">2</property>
<property name="n_columns">2</property> <property name="n_columns">2</property>
<property name="column_spacing">3</property> <property name="column_spacing">5</property>
<property name="row_spacing">3</property> <property name="row_spacing">3</property>
<child> <child>
<object class="GtkLabel" id="UnitLabel"> <object class="GtkLabel" id="UnitLabel">
...@@ -5029,6 +5032,8 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5029,6 +5032,8 @@ It is significantly faster, but the current release of ODE contains a nasty bug
<packing> <packing>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property> <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -5048,6 +5053,8 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5048,6 +5053,8 @@ It is significantly faster, but the current release of ODE contains a nasty bug
<property name="right_attach">2</property> <property name="right_attach">2</property>
<property name="top_attach">1</property> <property name="top_attach">1</property>
<property name="bottom_attach">2</property> <property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
<child> <child>
...@@ -5056,6 +5063,10 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5056,6 +5063,10 @@ It is significantly faster, but the current release of ODE contains a nasty bug
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="label" translatable="yes">Boundary mode:</property> <property name="label" translatable="yes">Boundary mode:</property>
</object> </object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child> </child>
<child> <child>
<object class="GtkComboBox" id="BoundaryModeControl"> <object class="GtkComboBox" id="BoundaryModeControl">
...@@ -5072,10 +5083,14 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5072,10 +5083,14 @@ It is significantly faster, but the current release of ODE contains a nasty bug
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="right_attach">2</property> <property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
...@@ -5134,7 +5149,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5134,7 +5149,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug
<child> <child>
<object class="GtkHBox" id="hbox28"> <object class="GtkHBox" id="hbox28">
<property name="visible">True</property> <property name="visible">True</property>
<property name="spacing">4</property> <property name="spacing">50</property>
<child> <child>
<object class="GtkLabel" id="SafetyHeightLabel"> <object class="GtkLabel" id="SafetyHeightLabel">
<property name="visible">True</property> <property name="visible">True</property>
...@@ -5142,6 +5157,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5142,6 +5157,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug
<property name="label" translatable="yes">Safety Height:</property> <property name="label" translatable="yes">Safety Height:</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property>
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
...@@ -5296,6 +5312,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug ...@@ -5296,6 +5312,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
...@@ -6075,8 +6092,459 @@ Hotkey: &lt;p&gt;</property> ...@@ -6075,8 +6092,459 @@ Hotkey: &lt;p&gt;</property>
<property name="tab_fill">False</property> <property name="tab_fill">False</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkFrame" id="MultiprocessingFrame">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="alignment15">
<property name="visible">True</property>
<property name="left_padding">12</property>
<child>
<object class="GtkVBox" id="vbox34">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkCheckButton" id="EnableParallelProcesses">
<property name="label" translatable="yes">Enable parallel processes</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="ParallelProcessSettingsBox">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<property name="spacing">5</property>
<child>
<object class="GtkAlignment" id="alignment45">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="left_padding">10</property>
<child>
<object class="GtkTable" id="table13">
<property name="visible">True</property>
<property name="n_rows">3</property>
<property name="n_columns">2</property>
<property name="column_spacing">3</property>
<property name="row_spacing">2</property>
<child>
<object class="GtkLabel" id="AvailableCoresLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Available cores:</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="AvailableCores">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">1</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="NumberOfProcesses">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
<property name="adjustment">NumberOfProcessesValue</property>
<property name="numeric">True</property>
<property name="update_policy">if-valid</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="NumberOfProcessesLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Number of processes:</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkViewport" id="viewport2">
<property name="visible">True</property>
<property name="resize_mode">queue</property>
<property name="shadow_type">none</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ZeroProcessesWarning">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">&lt;span foreground="red"&gt;Warning: no local processes enabled.
You will need a remote server.&lt;/span&gt;</property>
<property name="use_markup">True</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHSeparator" id="hseparator15">
<property name="visible">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkVBox" id="vbox36">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkHBox" id="hbox34">
<property name="visible">True</property>
<child>
<object class="GtkCheckButton" id="EnableServerMode">
<property name="label">gtk-connect</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="use_stock">True</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment46">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="left_padding">10</property>
<child>
<object class="GtkTable" id="ServerModeSettingsTable">
<property name="visible">True</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="column_spacing">3</property>
<property name="row_spacing">2</property>
<child>
<object class="GtkSpinButton" id="RemoteServerPort">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">The TCP port of the remote server. You need to set a hostname or IP as well.
PyCAM's default port is 1250.</property>
<property name="invisible_char">&#x25CF;</property>
<property name="adjustment">RemoteServerPortValue</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="RemoteServerHostname">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Type in a hostname or IP address if you want to connect to a remote PyCAM server.
Leave this field empty for a local-only server.</property>
<property name="invisible_char">&#x25CF;</property>
<property name="width_chars">18</property>
<property name="truncate_multiline">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="RemoteServerLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Remote server:</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ServerPortLocalLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Local port:</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ServerPasswordLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="label" translatable="yes">Password:</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="ServerPortLocal">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">PyCAM will try to bind to this local TCP port. Other PyCAM clients may connect to this port if they know the shared secret.
Remember that you need administrative permissions for ports below 1024.
The default port is 1250.</property>
<property name="invisible_char">&#x25CF;</property>
<property name="adjustment">ServerPortLocalValue</property>
<property name="numeric">True</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkViewport" id="viewport1">
<property name="visible">True</property>
<property name="resize_mode">queue</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox37">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkEntry" id="ServerPassword">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">All servers and clients of a processing cloud must use the same shared secret.</property>
<property name="visibility">False</property>
<property name="invisible_char">&#x25CF;</property>
<property name="truncate_multiline">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="ServerPasswordShow">
<property name="label" translatable="yes">Show password</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip_text" translatable="yes">Show or hide the shared secret in the password field.</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ServerPasswordGenerate">
<property name="label">gtk-new</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Generate a new random shared secret.</property>
<property name="use_stock">True</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options"></property>
<property name="y_options"></property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="ServerModeFrameLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Distributed processing&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ParallelProcessingDisabledLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Parallel processing is currently not available on your system.
Please check the following list of possible reasons and fix the problem, if possible.
1) You are using the Windows standalone executable.
Sadly this problem is not too easy to solve. Future releases of PyCAM should remove this limitation.
2) You are using Python 2.5 or below and the package "multiprocessing" is &lt;i&gt;not&lt;/i&gt; installed.</property>
<property name="use_markup">True</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="MultiprocessingFrameLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Settings for parallel processing&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<packing>
<property name="position">6</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="MultiprocessingSettings">
<property name="visible">True</property>
<property name="label" translatable="yes">Parallel processing</property>
</object> </object>
<packing> <packing>
<property name="position">6</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">3</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
...@@ -6808,8 +7276,8 @@ Any selected group of dimensions will be scaled accordingly.</property> ...@@ -6808,8 +7276,8 @@ Any selected group of dimensions will be scaled accordingly.</property>
<object class="GtkLabel" id="ProcessPoolDisabledLabel"> <object class="GtkLabel" id="ProcessPoolDisabledLabel">
<property name="visible">True</property> <property name="visible">True</property>
<property name="xalign">0</property> <property name="xalign">0</property>
<property name="label" translatable="yes">Multiprocessing is currently not enabled on your system. <property name="label" translatable="yes">The server mode is currently not enabled on your system.
You need to start PyCAM with the parameter "--enable-server" to allow remote workers to connect to your host. Take a look at the &lt;i&gt;Preferences&lt;/i&gt; for &lt;i&gt;Parallel processing&lt;/i&gt; if you want to enable it.
Please read the description of the Server Mode (linked below) to understand the related security implications.</property> Please read the description of the Server Mode (linked below) to understand the related security implications.</property>
<property name="use_markup">True</property> <property name="use_markup">True</property>
<property name="wrap">True</property> <property name="wrap">True</property>
...@@ -7266,4 +7734,18 @@ Please read the description of the Server Mode (linked below) to understand the ...@@ -7266,4 +7734,18 @@ Please read the description of the Server Mode (linked below) to understand the
<property name="upper">5</property> <property name="upper">5</property>
<property name="step_increment">0.10000000000000001</property> <property name="step_increment">0.10000000000000001</property>
</object> </object>
<object class="GtkAdjustment" id="NumberOfProcessesValue">
<property name="upper">1000</property>
<property name="step_increment">1</property>
</object>
<object class="GtkAdjustment" id="RemoteServerPortValue">
<property name="value">1250</property>
<property name="upper">65535</property>
<property name="step_increment">1</property>
</object>
<object class="GtkAdjustment" id="ServerPortLocalValue">
<property name="value">1250</property>
<property name="upper">65535</property>
<property name="step_increment">1</property>
</object>
</interface> </interface>
...@@ -46,10 +46,12 @@ import gobject ...@@ -46,10 +46,12 @@ import gobject
import webbrowser import webbrowser
import ConfigParser import ConfigParser
import urllib import urllib
import string
import time import time
import logging import logging
import datetime import datetime
import traceback import traceback
import random
import re import re
import os import os
import sys import sys
...@@ -608,14 +610,9 @@ class ProjectGui: ...@@ -608,14 +610,9 @@ class ProjectGui:
# repaint the 3d view after a color change # repaint the 3d view after a color change
obj.connect("color-set", self.update_view) obj.connect("color-set", self.update_view)
# set the availability of ODE # set the availability of ODE
enable_ode_control = self.gui.get_object("SettingEnableODE") self.enable_ode_control = self.gui.get_object("SettingEnableODE")
if not pycam.Utils.threading.is_multiprocessing_enabled \ self.settings.add_item("enable_ode", self.enable_ode_control.get_active,
and pycam.Physics.ode_physics.is_ode_available(): self.enable_ode_control.set_active)
self.settings.add_item("enable_ode", enable_ode_control.get_active, enable_ode_control.set_active)
else:
enable_ode_control.set_sensitive(False)
# bind dummy get/set functions to "enable_ode" (always return False)
self.settings.add_item("enable_ode", lambda: False, lambda state: None)
skip_obj = self.gui.get_object("DrillProgressFrameSkipControl") skip_obj = self.gui.get_object("DrillProgressFrameSkipControl")
self.settings.add_item("drill_progress_max_fps", skip_obj.get_value, skip_obj.set_value) self.settings.add_item("drill_progress_max_fps", skip_obj.get_value, skip_obj.set_value)
sim_detail_obj = self.gui.get_object("SimulationDetailsValue") sim_detail_obj = self.gui.get_object("SimulationDetailsValue")
...@@ -787,6 +784,39 @@ class ProjectGui: ...@@ -787,6 +784,39 @@ class ProjectGui:
location_control.get_text, location_control.set_text) location_control.get_text, location_control.set_text)
self.gui.get_object(browse_button).connect("clicked", self.gui.get_object(browse_button).connect("clicked",
self._browse_external_program_location, key) self._browse_external_program_location, key)
# parallel processing settings
self.enable_parallel_processes = self.gui.get_object(
"EnableParallelProcesses")
if pycam.Utils.threading.is_parallel_processing_available():
self.gui.get_object("ParallelProcessingDisabledLabel").hide()
else:
self.gui.get_object("ParallelProcessSettingsBox").hide()
self.enable_parallel_processes.set_sensitive(False)
self.enable_parallel_processes.set_active(False)
self.enable_parallel_processes.set_active(
pycam.Utils.threading.is_multiprocessing_enabled())
self.enable_parallel_processes.connect("toggled",
self.handle_parallel_processes_settings)
self.number_of_processes = self.gui.get_object(
"NumberOfProcesses")
self.number_of_processes.set_value(
pycam.Utils.threading.get_number_of_processes())
self.gui.get_object("ServerPortLocal").set_value(
pycam.Utils.threading.DEFAULT_PORT)
self.gui.get_object("RemoteServerPort").set_value(
pycam.Utils.threading.DEFAULT_PORT)
self.number_of_processes.connect("value-changed",
self.handle_parallel_processes_settings)
self.gui.get_object("EnableServerMode").connect("toggled",
self.initialize_multiprocessing)
self.gui.get_object("ServerPasswordGenerate").connect("clicked",
self.generate_random_server_password)
self.gui.get_object("ServerPasswordShow").connect("toggled",
self.update_parallel_processes_settings)
cpu_cores = pycam.Utils.threading.get_number_of_cores()
if cpu_cores is None:
cpu_cores = "unknown"
self.gui.get_object("AvailableCores").set_label(str(cpu_cores))
# status bar # status bar
self.status_bar = self.gui.get_object("StatusBar") self.status_bar = self.gui.get_object("StatusBar")
# menu bar # menu bar
...@@ -859,6 +889,8 @@ class ProjectGui: ...@@ -859,6 +889,8 @@ class ProjectGui:
self.update_support_grid_controls() self.update_support_grid_controls()
self.update_scale_controls() self.update_scale_controls()
self.update_gcode_controls() self.update_gcode_controls()
self.update_ode_settings()
self.update_parallel_processes_settings()
self.update_model_type_related_controls() self.update_model_type_related_controls()
def update_model_type_related_controls(self): def update_model_type_related_controls(self):
...@@ -871,6 +903,14 @@ class ProjectGui: ...@@ -871,6 +903,14 @@ class ProjectGui:
path_mode = self.settings.get("gcode_path_mode") path_mode = self.settings.get("gcode_path_mode")
self.gui.get_object("GCodeToleranceTable").set_sensitive(path_mode == 3) self.gui.get_object("GCodeToleranceTable").set_sensitive(path_mode == 3)
def update_ode_settings(self, widget=None):
if pycam.Utils.threading.is_multiprocessing_enabled() \
or not pycam.Physics.ode_physics.is_ode_available():
self.enable_ode_control.set_sensitive(False)
self.enable_ode_control.set_active(False)
else:
self.enable_ode_control.set_sensitive(True)
def progress_activity_guard(func): def progress_activity_guard(func):
def progress_activity_guard_wrapper(self, *args, **kwargs): def progress_activity_guard_wrapper(self, *args, **kwargs):
if self._progress_running: if self._progress_running:
...@@ -1110,6 +1150,92 @@ class ProjectGui: ...@@ -1110,6 +1150,92 @@ class ProjectGui:
else: else:
self.grid_adjustment_selector.set_active(-1) self.grid_adjustment_selector.set_active(-1)
@gui_activity_guard
def generate_random_server_password(self, widget=None):
all_characters = string.letters + string.digits
random_pw = "".join([random.choice(all_characters) for i in range(12)])
self.gui.get_object("ServerPassword").set_text(random_pw)
@gui_activity_guard
def update_parallel_processes_settings(self, widget=None):
parallel_settings = self.gui.get_object("ParallelProcessSettingsBox")
server_enabled = self.gui.get_object("EnableServerMode")
server_mode_settings = self.gui.get_object("ServerModeSettingsTable")
# update the show/hide state of the password
hide_password = self.gui.get_object("ServerPasswordShow").get_active()
self.gui.get_object("ServerPassword").set_visibility(hide_password)
if (self.gui.get_object("NumberOfProcesses").get_value() == 0) \
and self.enable_parallel_processes.get_active():
self.gui.get_object("ZeroProcessesWarning").show()
else:
self.gui.get_object("ZeroProcessesWarning").hide()
if self.enable_parallel_processes.get_active():
parallel_settings.set_sensitive(True)
if server_enabled.get_active():
# don't allow changes for an active connection
server_mode_settings.set_sensitive(False)
else:
server_mode_settings.set_sensitive(True)
else:
parallel_settings.set_sensitive(False)
server_enabled.set_active(False)
# check availability of ODE again (conflicts with multiprocessing)
self.update_ode_settings()
def handle_parallel_processes_settings(self, widget=None):
new_num_of_processes = self.number_of_processes.get_value()
new_enable_parallel = self.enable_parallel_processes.get_active()
old_num_of_processes = pycam.Utils.threading.get_number_of_processes()
old_enable_parallel = pycam.Utils.threading.is_multiprocessing_enabled()
if (old_num_of_processes != new_num_of_processes) \
or (old_enable_parallel != new_enable_parallel):
self.initialize_multiprocessing()
@gui_activity_guard
def initialize_multiprocessing(self, widget=None):
complete_area = self.gui.get_object("MultiprocessingFrame")
# prevent any further actions while the connection is established
complete_area.set_sensitive(False)
# wait for the above "set_sensitive" to finish
while gtk.events_pending():
gtk.main_iteration()
num_of_processes = int(self.number_of_processes.get_value())
enable_parallel = self.enable_parallel_processes.get_active()
enable_server_obj = self.gui.get_object("EnableServerMode")
enable_server = enable_server_obj.get_active()
remote_host = self.gui.get_object("RemoteServerHostname").get_text()
if remote_host:
remote_port = int(self.gui.get_object(
"RemoteServerPort").get_value())
remote = "%s:%s" % (remote_host, remote_port)
else:
remote = None
local_port = int(self.gui.get_object("ServerPortLocal").get_value())
auth_key = self.gui.get_object("ServerPassword").get_text()
if not auth_key and enable_parallel and enable_server:
log.error("You need to provide a password for this connection.")
enable_server_obj.set_active(False)
elif enable_parallel:
error = pycam.Utils.threading.init_threading(
number_of_processes=num_of_processes,
enable_server=enable_server, remote=remote,
server_credentials=auth_key, local_port=local_port)
if error:
log.error("Failed to start server: %s" % error)
pycam.Utils.threading.cleanup()
enable_server_obj.set_active(False)
else:
pycam.Utils.threading.cleanup()
log.info("Multiprocessing disabled")
# set the label of the "connect" button
if enable_server_obj.get_active():
info = gtk.stock_lookup(gtk.STOCK_DISCONNECT)
else:
info = gtk.stock_lookup(gtk.STOCK_CONNECT)
enable_server_obj.set_label(info[0])
complete_area.set_sensitive(True)
self.append_to_queue(self.update_parallel_processes_settings)
def _browse_external_program_location(self, widget=None, key=None): def _browse_external_program_location(self, widget=None, key=None):
location = self.get_filename_via_dialog(title="Select the executable " \ location = self.get_filename_via_dialog(title="Select the executable " \
+ "for '%s'" % key, mode_load=True) + "for '%s'" % key, mode_load=True)
...@@ -2778,10 +2904,10 @@ class ProjectGui: ...@@ -2778,10 +2904,10 @@ class ProjectGui:
if status: if status:
self.menubar.set_sensitive(False) self.menubar.set_sensitive(False)
self.task_pane.set_sensitive(False) self.task_pane.set_sensitive(False)
self._progress_start_time = time.time()
self.update_progress_bar("", 0) self.update_progress_bar("", 0)
self.progress_cancel_button.set_sensitive(True) self.progress_cancel_button.set_sensitive(True)
self.progress_widget.show() self.progress_widget.show()
self._progress_start_time = time.time()
else: else:
self.progress_widget.hide() self.progress_widget.hide()
self.task_pane.set_sensitive(True) self.task_pane.set_sensitive(True)
...@@ -2835,14 +2961,15 @@ class ProjectGui: ...@@ -2835,14 +2961,15 @@ class ProjectGui:
# This restriction improves performance and reduces the # This restriction improves performance and reduces the
# "snappiness" of the GUI. # "snappiness" of the GUI.
if (self._last_gtk_events_time is None) \ if (self._last_gtk_events_time is None) \
or (percent is None) \ or text \
or (self._last_gtk_events_time + 1 < current_time): or (self._last_gtk_events_time + 1 < current_time):
while gtk.events_pending(): while gtk.events_pending():
gtk.main_iteration() gtk.main_iteration()
if percent: if not text or (self._progress_start_time + 5 < current_time):
# We don't store the timining if percent=0 or percent=None. # We don't store the timining if the text was changed.
# This is especially nice for the snappines during font # This is especially nice for the snappines during font
# initialization. # initialization. This exception is only valid for the first
# five seconds of the operation.
self._last_gtk_events_time = current_time self._last_gtk_events_time = current_time
# return if the user requested a break # return if the user requested a break
return self._progress_cancel_requested return self._progress_cancel_requested
......
...@@ -25,6 +25,7 @@ import pycam.Utils.log ...@@ -25,6 +25,7 @@ import pycam.Utils.log
#import multiprocessing #import multiprocessing
import Queue import Queue
import signal import signal
import socket
import platform import platform
import random import random
import uuid import uuid
...@@ -40,7 +41,11 @@ try: ...@@ -40,7 +41,11 @@ try:
def _run_server(cls, *args): def _run_server(cls, *args):
# make sure that the server ignores SIGINT (KeyboardInterrupt) # make sure that the server ignores SIGINT (KeyboardInterrupt)
signal.signal(signal.SIGINT, signal.SIG_IGN) signal.signal(signal.SIGINT, signal.SIG_IGN)
# prevent connection errors to trigger exceptions
try:
SyncManager._run_server(*args) SyncManager._run_server(*args)
except socket.error, err_msg:
pass
except ImportError: except ImportError:
pass pass
...@@ -77,12 +82,35 @@ def is_pool_available(): ...@@ -77,12 +82,35 @@ def is_pool_available():
return not __manager is None return not __manager is None
def is_multiprocessing_enabled(): def is_multiprocessing_enabled():
return not __multiprocessing is None return bool(__multiprocessing)
def is_server_mode_available(): def is_windows_parallel_processing_available():
# server mode is disabled for the Windows standalone executable # server mode is disabled for the Windows standalone executable
return not (hasattr(sys, "frozen") and sys.frozen) return not (hasattr(sys, "frozen") and sys.frozen)
def is_parallel_processing_available():
if not is_windows_parallel_processing_available():
# Windows -> no parallel processing
return False
try:
import multiprocessing
return True
except ImportError:
return False
def get_number_of_processes():
if __num_of_processes is None:
return 1
else:
return __num_of_processes
def get_number_of_cores():
try:
import multiprocessing
return multiprocessing.cpu_count()
except ImportError:
return None
def get_pool_statistics(): def get_pool_statistics():
global __manager global __manager
if __manager is None: if __manager is None:
...@@ -91,12 +119,12 @@ def get_pool_statistics(): ...@@ -91,12 +119,12 @@ def get_pool_statistics():
return __manager.statistics().get_worker_statistics() return __manager.statistics().get_worker_statistics()
def init_threading(number_of_processes=None, enable_server=False, remote=None, def init_threading(number_of_processes=None, enable_server=False, remote=None,
run_server=False, server_credentials=""): run_server=False, server_credentials="", local_port=DEFAULT_PORT):
global __multiprocessing, __num_of_processes, __manager, __closing, __task_source_uuid global __multiprocessing, __num_of_processes, __manager, __closing, __task_source_uuid
if __multiprocessing: if __multiprocessing:
# kill the manager and clean everything up for a re-initialization # kill the manager and clean everything up for a re-initialization
cleanup() cleanup()
if (not is_server_mode_available()) and (enable_server or run_server): if (not is_windows_parallel_processing_available()) and (enable_server or run_server):
# server mode is disabled for the Windows pyinstaller standalone # server mode is disabled for the Windows pyinstaller standalone
# due to "pickle errors". How to reproduce: run the standalone binary # due to "pickle errors". How to reproduce: run the standalone binary
# with "--enable-server --server-auth-key foo". # with "--enable-server --server-auth-key foo".
...@@ -121,7 +149,7 @@ def init_threading(number_of_processes=None, enable_server=False, remote=None, ...@@ -121,7 +149,7 @@ def init_threading(number_of_processes=None, enable_server=False, remote=None,
remote = None remote = None
run_server = None run_server = None
server_credentials = "" server_credentials = ""
if not is_server_mode_available(): if not is_windows_parallel_processing_available():
# Running multiple processes with the Windows standalone executable # Running multiple processes with the Windows standalone executable
# causes "WindowsError: invalid handle" error messages. The processes # causes "WindowsError: invalid handle" error messages. The processes
# can't communicate - thus no results are returned. # can't communicate - thus no results are returned.
...@@ -184,7 +212,7 @@ def init_threading(number_of_processes=None, enable_server=False, remote=None, ...@@ -184,7 +212,7 @@ def init_threading(number_of_processes=None, enable_server=False, remote=None,
worker_uuid_list = [str(uuid.uuid1()) for index in range(__num_of_processes)] worker_uuid_list = [str(uuid.uuid1()) for index in range(__num_of_processes)]
__task_source_uuid = str(uuid.uuid1()) __task_source_uuid = str(uuid.uuid1())
if remote is None: if remote is None:
address = ('', DEFAULT_PORT) address = ('localhost', local_port)
else: else:
if ":" in remote: if ":" in remote:
host, port = remote.split(":", 1) host, port = remote.split(":", 1)
...@@ -214,12 +242,19 @@ def init_threading(number_of_processes=None, enable_server=False, remote=None, ...@@ -214,12 +242,19 @@ def init_threading(number_of_processes=None, enable_server=False, remote=None,
TaskManager.register("cache") TaskManager.register("cache")
__manager = TaskManager(address=address, authkey=server_credentials) __manager = TaskManager(address=address, authkey=server_credentials)
# run the local server, connect to a remote one or begin serving # run the local server, connect to a remote one or begin serving
try:
if remote is None: if remote is None:
__manager.start() __manager.start()
log.info("Started a local server.") log.info("Started a local server.")
else: else:
__manager.connect() __manager.connect()
log.info("Connected to a remote task server.") log.info("Connected to a remote task server.")
except (multiprocessing.AuthenticationError, socket.error), err_msg:
__manager = None
return err_msg
except EOFError:
__manager = None
return "Failed to bind to socket for unknown reasons"
# create the spawning process # create the spawning process
__closing = __manager.Value("b", False) __closing = __manager.Value("b", False)
if __num_of_processes > 0: if __num_of_processes > 0:
...@@ -237,7 +272,7 @@ def init_threading(number_of_processes=None, enable_server=False, remote=None, ...@@ -237,7 +272,7 @@ def init_threading(number_of_processes=None, enable_server=False, remote=None,
spawner.join() spawner.join()
def cleanup(): def cleanup():
global __manager, __closing global __multiprocessing, __manager, __closing
if __multiprocessing and __closing: if __multiprocessing and __closing:
log.debug("Shutting down process handler") log.debug("Shutting down process handler")
try: try:
...@@ -256,6 +291,7 @@ def cleanup(): ...@@ -256,6 +291,7 @@ def cleanup():
__manager._process.terminate() __manager._process.terminate()
__manager = None __manager = None
__closing = None __closing = None
__multiprocessing = None
def _spawn_daemon(manager, number_of_processes, worker_uuid_list): def _spawn_daemon(manager, number_of_processes, worker_uuid_list):
""" wait for items in the 'tasks' queue to appear and then spawn workers """ wait for items in the 'tasks' queue to appear and then spawn workers
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment