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
* added adaptive positioning for DropCutter strategy (improves precision)
* allow conventional/climb milling style for ContourFollow and Engrave strategies
* 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
* unify DropCutter behaviour for models that are higher than the defined bounding box
* always move up to safety height in this case
......
......@@ -4957,6 +4957,7 @@ upon interesting bugs and weird results.</property>
<property name="title" translatable="yes">PyCAM Preferences</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">normal</property>
<property name="transient_for">ProjectWindow</property>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
......@@ -4978,6 +4979,7 @@ upon interesting bugs and weird results.</property>
<object class="GtkNotebook" id="notebook1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tab_pos">left</property>
<child>
<object class="GtkVBox" id="vbox7">
<property name="visible">True</property>
......@@ -5005,7 +5007,8 @@ upon interesting bugs and weird results.</property>
<property name="receives_default">False</property>
<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.
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>
</object>
<packing>
......@@ -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="n_rows">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>
<child>
<object class="GtkLabel" id="UnitLabel">
......@@ -5029,6 +5032,8 @@ It is significantly faster, but the current release of ODE contains a nasty bug
<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>
......@@ -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="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>
......@@ -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="label" translatable="yes">Boundary mode:</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="BoundaryModeControl">
......@@ -5072,10 +5083,14 @@ It is significantly faster, but the current release of ODE contains a nasty bug
<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>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
......@@ -5134,7 +5149,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug
<child>
<object class="GtkHBox" id="hbox28">
<property name="visible">True</property>
<property name="spacing">4</property>
<property name="spacing">50</property>
<child>
<object class="GtkLabel" id="SafetyHeightLabel">
<property name="visible">True</property>
......@@ -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>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
......@@ -5296,6 +5312,7 @@ It is significantly faster, but the current release of ODE contains a nasty bug
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="position">1</property>
</packing>
</child>
......@@ -6075,8 +6092,459 @@ Hotkey: &lt;p&gt;</property>
<property name="tab_fill">False</property>
</packing>
</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>
<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>
</packing>
</child>
......@@ -6808,8 +7276,8 @@ Any selected group of dimensions will be scaled accordingly.</property>
<object class="GtkLabel" id="ProcessPoolDisabledLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Multiprocessing 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.
<property name="label" translatable="yes">The server mode is currently not enabled on your system.
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>
<property name="use_markup">True</property>
<property name="wrap">True</property>
......@@ -7266,4 +7734,18 @@ Please read the description of the Server Mode (linked below) to understand the
<property name="upper">5</property>
<property name="step_increment">0.10000000000000001</property>
</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>
......@@ -46,10 +46,12 @@ import gobject
import webbrowser
import ConfigParser
import urllib
import string
import time
import logging
import datetime
import traceback
import random
import re
import os
import sys
......@@ -608,14 +610,9 @@ class ProjectGui:
# repaint the 3d view after a color change
obj.connect("color-set", self.update_view)
# set the availability of ODE
enable_ode_control = self.gui.get_object("SettingEnableODE")
if not pycam.Utils.threading.is_multiprocessing_enabled \
and pycam.Physics.ode_physics.is_ode_available():
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)
self.enable_ode_control = self.gui.get_object("SettingEnableODE")
self.settings.add_item("enable_ode", self.enable_ode_control.get_active,
self.enable_ode_control.set_active)
skip_obj = self.gui.get_object("DrillProgressFrameSkipControl")
self.settings.add_item("drill_progress_max_fps", skip_obj.get_value, skip_obj.set_value)
sim_detail_obj = self.gui.get_object("SimulationDetailsValue")
......@@ -787,6 +784,39 @@ class ProjectGui:
location_control.get_text, location_control.set_text)
self.gui.get_object(browse_button).connect("clicked",
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
self.status_bar = self.gui.get_object("StatusBar")
# menu bar
......@@ -859,6 +889,8 @@ class ProjectGui:
self.update_support_grid_controls()
self.update_scale_controls()
self.update_gcode_controls()
self.update_ode_settings()
self.update_parallel_processes_settings()
self.update_model_type_related_controls()
def update_model_type_related_controls(self):
......@@ -871,6 +903,14 @@ class ProjectGui:
path_mode = self.settings.get("gcode_path_mode")
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_wrapper(self, *args, **kwargs):
if self._progress_running:
......@@ -1109,7 +1149,93 @@ class ProjectGui:
self.grid_adjustment_selector.set_active(old_index)
else:
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):
location = self.get_filename_via_dialog(title="Select the executable " \
+ "for '%s'" % key, mode_load=True)
......@@ -2778,10 +2904,10 @@ class ProjectGui:
if status:
self.menubar.set_sensitive(False)
self.task_pane.set_sensitive(False)
self._progress_start_time = time.time()
self.update_progress_bar("", 0)
self.progress_cancel_button.set_sensitive(True)
self.progress_widget.show()
self._progress_start_time = time.time()
else:
self.progress_widget.hide()
self.task_pane.set_sensitive(True)
......@@ -2835,14 +2961,15 @@ class ProjectGui:
# This restriction improves performance and reduces the
# "snappiness" of the GUI.
if (self._last_gtk_events_time is None) \
or (percent is None) \
or text \
or (self._last_gtk_events_time + 1 < current_time):
while gtk.events_pending():
gtk.main_iteration()
if percent:
# We don't store the timining if percent=0 or percent=None.
if not text or (self._progress_start_time + 5 < current_time):
# We don't store the timining if the text was changed.
# 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
# return if the user requested a break
return self._progress_cancel_requested
......
......@@ -25,6 +25,7 @@ import pycam.Utils.log
#import multiprocessing
import Queue
import signal
import socket
import platform
import random
import uuid
......@@ -40,7 +41,11 @@ try:
def _run_server(cls, *args):
# make sure that the server ignores SIGINT (KeyboardInterrupt)
signal.signal(signal.SIGINT, signal.SIG_IGN)
SyncManager._run_server(*args)
# prevent connection errors to trigger exceptions
try:
SyncManager._run_server(*args)
except socket.error, err_msg:
pass
except ImportError:
pass
......@@ -77,12 +82,35 @@ def is_pool_available():
return not __manager is None
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
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():
global __manager
if __manager is None:
......@@ -91,12 +119,12 @@ def get_pool_statistics():
return __manager.statistics().get_worker_statistics()
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
if __multiprocessing:
# kill the manager and clean everything up for a re-initialization
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
# due to "pickle errors". How to reproduce: run the standalone binary
# with "--enable-server --server-auth-key foo".
......@@ -121,7 +149,7 @@ def init_threading(number_of_processes=None, enable_server=False, remote=None,
remote = None
run_server = None
server_credentials = ""
if not is_server_mode_available():
if not is_windows_parallel_processing_available():
# Running multiple processes with the Windows standalone executable
# causes "WindowsError: invalid handle" error messages. The processes
# can't communicate - thus no results are returned.
......@@ -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)]
__task_source_uuid = str(uuid.uuid1())
if remote is None:
address = ('', DEFAULT_PORT)
address = ('localhost', local_port)
else:
if ":" in remote:
host, port = remote.split(":", 1)
......@@ -214,12 +242,19 @@ def init_threading(number_of_processes=None, enable_server=False, remote=None,
TaskManager.register("cache")
__manager = TaskManager(address=address, authkey=server_credentials)
# run the local server, connect to a remote one or begin serving
if remote is None:
__manager.start()
log.info("Started a local server.")
else:
__manager.connect()
log.info("Connected to a remote task server.")
try:
if remote is None:
__manager.start()
log.info("Started a local server.")
else:
__manager.connect()
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
__closing = __manager.Value("b", False)
if __num_of_processes > 0:
......@@ -237,7 +272,7 @@ def init_threading(number_of_processes=None, enable_server=False, remote=None,
spawner.join()
def cleanup():
global __manager, __closing
global __multiprocessing, __manager, __closing
if __multiprocessing and __closing:
log.debug("Shutting down process handler")
try:
......@@ -256,6 +291,7 @@ def cleanup():
__manager._process.terminate()
__manager = None
__closing = None
__multiprocessing = None
def _spawn_daemon(manager, number_of_processes, worker_uuid_list):
""" 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