Selaa lähdekoodia

- in Tool Film added the page size and page orientation in case of saving the film as PDF file
- the application workspace has now a lot more options selectable in the Edit -> Preferences -> General -> GUI Preferences

Marius Stanciu 6 vuotta sitten
vanhempi
commit
58f24cbb37
7 muutettua tiedostoa jossa 456 lisäystä ja 87 poistoa
  1. 13 3
      FlatCAMApp.py
  2. 5 0
      README.md
  3. 117 33
      flatcamGUI/PlotCanvas.py
  4. 159 6
      flatcamGUI/PreferencesUI.py
  5. 149 30
      flatcamTools/ToolFilm.py
  6. 10 12
      flatcamTools/ToolMove.py
  7. 3 3
      flatcamTools/ToolPaint.py

+ 13 - 3
FlatCAMApp.py

@@ -427,7 +427,8 @@ class App(QtCore.QObject):
             "global_gridy": 0.0393701,
             "global_snap_max": 0.001968504,
             "global_workspace": False,
-            "global_workspaceT": "A4P",
+            "global_workspaceT": "A4",
+            "global_workspace_orientation": 'p',
 
             "global_grid_context_menu": {
                 'in': [0.01, 0.02, 0.025, 0.05, 0.1],
@@ -766,6 +767,8 @@ class App(QtCore.QObject):
             "tools_film_mirror_cb": False,
             "tools_film_mirror_axis_radio": 'none',
             "tools_film_file_type_radio": 'svg',
+            "tools_film_orientation": 'p',
+            "tools_film_pagesize": 'A4',
 
             # Panel Tool
             "tools_panelize_spacing_columns": 0,
@@ -1039,6 +1042,7 @@ class App(QtCore.QObject):
             "global_snap_max": self.ui.general_defaults_form.general_gui_group.snap_max_dist_entry,
             "global_workspace": self.ui.general_defaults_form.general_gui_group.workspace_cb,
             "global_workspaceT": self.ui.general_defaults_form.general_gui_group.wk_cb,
+            "global_workspace_orientation": self.ui.general_defaults_form.general_gui_group.wk_orientation_radio,
 
             "global_plot_fill": self.ui.general_defaults_form.general_gui_group.pf_color_entry,
             "global_plot_line": self.ui.general_defaults_form.general_gui_group.pl_color_entry,
@@ -1324,6 +1328,8 @@ class App(QtCore.QObject):
             "tools_film_mirror_cb": self.ui.tools_defaults_form.tools_film_group.film_mirror_cb,
             "tools_film_mirror_axis_radio": self.ui.tools_defaults_form.tools_film_group.film_mirror_axis,
             "tools_film_file_type_radio": self.ui.tools_defaults_form.tools_film_group.file_type_radio,
+            "tools_film_orientation": self.ui.tools_defaults_form.tools_film_group.orientation_radio,
+            "tools_film_pagesize": self.ui.tools_defaults_form.tools_film_group.pagesize_combo,
 
             # Panelize Tool
             "tools_panelize_spacing_columns": self.ui.tools_defaults_form.tools_panelize_group.pspacing_columns,
@@ -1932,6 +1938,10 @@ class App(QtCore.QObject):
 
         # ############################# workspace setting signals #####################
         self.ui.general_defaults_form.general_gui_group.wk_cb.currentIndexChanged.connect(self.on_workspace_modified)
+        self.ui.general_defaults_form.general_gui_group.wk_orientation_radio.activated_custom.connect(
+            self.on_workspace_modified
+        )
+
         self.ui.general_defaults_form.general_gui_group.workspace_cb.stateChanged.connect(self.on_workspace)
 
         self.ui.general_defaults_form.general_gui_set_group.layout_combo.activated.connect(self.on_layout)
@@ -5729,7 +5739,7 @@ class App(QtCore.QObject):
 
             # change this only if the workspace is active
             if self.defaults['global_workspace'] is True:
-                self.plotcanvas.draw_workspace()
+                self.plotcanvas.draw_workspace(pagesize=self.defaults['global_workspaceT'])
 
             # adjust the grid values on the main toolbar
             dec = 6 if new_units == 'IN'else 4
@@ -6660,7 +6670,7 @@ class App(QtCore.QObject):
 
     def on_workspace_modified(self):
         self.save_defaults(silent=True)
-        self.plotcanvas.draw_workspace()
+        self.plotcanvas.draw_workspace(pagesize=self.defaults['global_workspaceT'])
 
     def on_workspace(self):
         self.report_usage("on_workspace()")

+ 5 - 0
README.md

@@ -9,6 +9,11 @@ CAD program, and create G-Code for Isolation routing.
 
 =================================================
 
+27.11.2019
+
+- in Tool Film added the page size and page orientation in case of saving the film as PDF file
+- the application workspace has now a lot more options selectable in the Edit -> Preferences -> General -> GUI Preferences
+
 26.11.2019
 
 - updated the Film Tool to allow exporting PDF and PNG file (besides the SVG file)

+ 117 - 33
flatcamGUI/PlotCanvas.py

@@ -76,7 +76,7 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
         # draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
         # all CNC have a limited workspace
 
-        self.draw_workspace()
+        self.draw_workspace(pagesize=self.fcapp.defaults["global_workspaceT"])
 
         self.line_parent = None
         self.cursor_v_line = InfiniteLine(pos=None, color=self.line_color, vertical=True,
@@ -107,37 +107,122 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
 
     # draw a rectangle made out of 4 lines on the canvas to serve as a hint for the work area
     # all CNC have a limited workspace
-    def draw_workspace(self):
-        a = np.empty((0, 0))
-
-        a4p_in = np.array([(0, 0), (8.3, 0), (8.3, 11.7), (0, 11.7)])
-        a4l_in = np.array([(0, 0), (11.7, 0), (11.7, 8.3), (0, 8.3)])
-        a3p_in = np.array([(0, 0), (11.7, 0), (11.7, 16.5), (0, 16.5)])
-        a3l_in = np.array([(0, 0), (16.5, 0), (16.5, 11.7), (0, 11.7)])
-
-        a4p_mm = np.array([(0, 0), (210, 0), (210, 297), (0, 297)])
-        a4l_mm = np.array([(0, 0), (297, 0), (297, 210), (0, 210)])
-        a3p_mm = np.array([(0, 0), (297, 0), (297, 420), (0, 420)])
-        a3l_mm = np.array([(0, 0), (420, 0), (420, 297), (0, 297)])
-
-        if self.fcapp.defaults['units'].upper() == 'MM':
-            if self.fcapp.defaults['global_workspaceT'] == 'A4P':
-                a = a4p_mm
-            elif self.fcapp.defaults['global_workspaceT'] == 'A4L':
-                a = a4l_mm
-            elif self.fcapp.defaults['global_workspaceT'] == 'A3P':
-                a = a3p_mm
-            elif self.fcapp.defaults['global_workspaceT'] == 'A3L':
-                a = a3l_mm
-        else:
-            if self.fcapp.defaults['global_workspaceT'] == 'A4P':
-                a = a4p_in
-            elif self.fcapp.defaults['global_workspaceT'] == 'A4L':
-                a = a4l_in
-            elif self.fcapp.defaults['global_workspaceT'] == 'A3P':
-                a = a3p_in
-            elif self.fcapp.defaults['global_workspaceT'] == 'A3L':
-                a = a3l_in
+    # def draw_workspace(self):
+    #     a = np.empty((0, 0))
+    #
+    #     a4p_in = np.array([(0, 0), (8.3, 0), (8.3, 11.7), (0, 11.7)])
+    #     a4l_in = np.array([(0, 0), (11.7, 0), (11.7, 8.3), (0, 8.3)])
+    #     a3p_in = np.array([(0, 0), (11.7, 0), (11.7, 16.5), (0, 16.5)])
+    #     a3l_in = np.array([(0, 0), (16.5, 0), (16.5, 11.7), (0, 11.7)])
+    #
+    #     a4p_mm = np.array([(0, 0), (210, 0), (210, 297), (0, 297)])
+    #     a4l_mm = np.array([(0, 0), (297, 0), (297, 210), (0, 210)])
+    #     a3p_mm = np.array([(0, 0), (297, 0), (297, 420), (0, 420)])
+    #     a3l_mm = np.array([(0, 0), (420, 0), (420, 297), (0, 297)])
+    #
+    #     if self.fcapp.defaults['units'].upper() == 'MM':
+    #         if self.fcapp.defaults['global_workspaceT'] == 'A4P':
+    #             a = a4p_mm
+    #         elif self.fcapp.defaults['global_workspaceT'] == 'A4L':
+    #             a = a4l_mm
+    #         elif self.fcapp.defaults['global_workspaceT'] == 'A3P':
+    #             a = a3p_mm
+    #         elif self.fcapp.defaults['global_workspaceT'] == 'A3L':
+    #             a = a3l_mm
+    #     else:
+    #         if self.fcapp.defaults['global_workspaceT'] == 'A4P':
+    #             a = a4p_in
+    #         elif self.fcapp.defaults['global_workspaceT'] == 'A4L':
+    #             a = a4l_in
+    #         elif self.fcapp.defaults['global_workspaceT'] == 'A3P':
+    #             a = a3p_in
+    #         elif self.fcapp.defaults['global_workspaceT'] == 'A3L':
+    #             a = a3l_in
+    #
+    #     self.delete_workspace()
+    #
+    #     self.b_line = Line(pos=a[0:2], color=(0.70, 0.3, 0.3, 1.0),
+    #                        antialias=True, method='agg', parent=self.view.scene)
+    #     self.r_line = Line(pos=a[1:3], color=(0.70, 0.3, 0.3, 1.0),
+    #                        antialias=True, method='agg', parent=self.view.scene)
+    #
+    #     self.t_line = Line(pos=a[2:4], color=(0.70, 0.3, 0.3, 1.0),
+    #                        antialias=True, method='agg', parent=self.view.scene)
+    #     self.l_line = Line(pos=np.array((a[0], a[3])), color=(0.70, 0.3, 0.3, 1.0),
+    #                        antialias=True, method='agg', parent=self.view.scene)
+    #
+    #     if self.fcapp.defaults['global_workspace'] is False:
+    #         self.delete_workspace()
+
+    # delete the workspace lines from the plot by removing the parent
+
+    def draw_workspace(self, pagesize):
+        pagesize_dict = dict()
+        pagesize_dict.update(
+            {
+                'A0': (841, 1189),
+                'A1': (594, 841),
+                'A2': (420, 594),
+                'A3': (297, 420),
+                'A4': (210, 297),
+                'A5': (148, 210),
+                'A6': (105, 148),
+                'A7': (74, 105),
+                'A8': (52, 74),
+                'A9': (37, 52),
+                'A10': (26, 37),
+
+                'B0': (1000, 1414),
+                'B1': (707, 1000),
+                'B2': (500, 707),
+                'B3': (353, 500),
+                'B4': (250, 353),
+                'B5': (176, 250),
+                'B6': (125, 176),
+                'B7': (88, 125),
+                'B8': (62, 88),
+                'B9': (44, 62),
+                'B10': (31, 44),
+
+                'C0': (917, 1297),
+                'C1': (648, 917),
+                'C2': (458, 648),
+                'C3': (324, 458),
+                'C4': (229, 324),
+                'C5': (162, 229),
+                'C6': (114, 162),
+                'C7': (81, 114),
+                'C8': (57, 81),
+                'C9': (40, 57),
+                'C10': (28, 40),
+
+                # American paper sizes
+                'LETTER': (8.5*25.4, 11*25.4),
+                'LEGAL': (8.5*25.4, 14*25.4),
+                'ELEVENSEVENTEEN': (11*25.4, 17*25.4),
+
+                # From https://en.wikipedia.org/wiki/Paper_size
+                'JUNIOR_LEGAL': (5*25.4, 8*25.4),
+                'HALF_LETTER': (5.5*25.4, 8*25.4),
+                'GOV_LETTER': (8*25.4, 10.5*25.4),
+                'GOV_LEGAL': (8.5*25.4, 13*25.4),
+                'LEDGER': (17*25.4, 11*25.4),
+            }
+        )
+
+        try:
+            if self.fcapp.defaults['units'].upper() == 'MM':
+                dims = pagesize_dict[pagesize]
+            else:
+                dims = (pagesize_dict[pagesize][0]/25.4, pagesize_dict[pagesize][1]/25.4)
+        except Exception as e:
+            log.debug("PlotCanvas.draw_workspace() --> %s" % str(e))
+            return
+
+        if self.fcapp.defaults['global_workspace_orientation'] == 'l':
+            dims = (dims[1], dims[0])
+
+        a = np.array([(0, 0), (dims[0], 0), (dims[0], dims[1]), (0, dims[1])])
 
         self.delete_workspace()
 
@@ -154,7 +239,6 @@ class PlotCanvas(QtCore.QObject, VisPyCanvas):
         if self.fcapp.defaults['global_workspace'] is False:
             self.delete_workspace()
 
-    # delete the workspace lines from the plot by removing the parent
     def delete_workspace(self):
         try:
             self.b_line.parent = None

+ 159 - 6
flatcamGUI/PreferencesUI.py

@@ -343,19 +343,92 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
            _("Draw a delimiting rectangle on canvas.\n"
              "The purpose is to illustrate the limits for our work.")
         )
-        self.workspace_type_lbl = QtWidgets.QLabel('%s:' % _('Wk. format'))
+        self.workspace_type_lbl = QtWidgets.QLabel('%s:' % _('Wk. size'))
         self.workspace_type_lbl.setToolTip(
            _("Select the type of rectangle to be used on canvas,\n"
              "as valid workspace.")
         )
         self.workspace_cb = FCCheckBox()
         self.wk_cb = FCComboBox()
-        self.wk_cb.addItem('A4P')
-        self.wk_cb.addItem('A4L')
-        self.wk_cb.addItem('A3P')
-        self.wk_cb.addItem('A3L')
 
-        self.wks = OptionalInputSection(self.workspace_cb, [self.workspace_type_lbl, self.wk_cb])
+        self.pagesize = dict()
+        self.pagesize.update(
+            {
+                'A0': (841, 1189),
+                'A1': (594, 841),
+                'A2': (420, 594),
+                'A3': (297, 420),
+                'A4': (210, 297),
+                'A5': (148, 210),
+                'A6': (105, 148),
+                'A7': (74, 105),
+                'A8': (52, 74),
+                'A9': (37, 52),
+                'A10': (26, 37),
+
+                'B0': (1000, 1414),
+                'B1': (707, 1000),
+                'B2': (500, 707),
+                'B3': (353, 500),
+                'B4': (250, 353),
+                'B5': (176, 250),
+                'B6': (125, 176),
+                'B7': (88, 125),
+                'B8': (62, 88),
+                'B9': (44, 62),
+                'B10': (31, 44),
+
+                'C0': (917, 1297),
+                'C1': (648, 917),
+                'C2': (458, 648),
+                'C3': (324, 458),
+                'C4': (229, 324),
+                'C5': (162, 229),
+                'C6': (114, 162),
+                'C7': (81, 114),
+                'C8': (57, 81),
+                'C9': (40, 57),
+                'C10': (28, 40),
+
+                # American paper sizes
+                'LETTER': (8.5, 11),
+                'LEGAL': (8.5, 14),
+                'ELEVENSEVENTEEN': (11, 17),
+
+                # From https://en.wikipedia.org/wiki/Paper_size
+                'JUNIOR_LEGAL': (5, 8),
+                'HALF_LETTER': (5.5, 8),
+                'GOV_LETTER': (8, 10.5),
+                'GOV_LEGAL': (8.5, 13),
+                'LEDGER': (17, 11),
+            }
+        )
+
+        page_size_list = list(self.pagesize.keys())
+
+        self.wk_cb.addItems(page_size_list)
+        # self.wk_cb.addItem('A4P')
+        # self.wk_cb.addItem('A4L')
+        # self.wk_cb.addItem('A3P')
+        # self.wk_cb.addItem('A3L')
+
+        # Page orientation
+        self.wk_orientation_label = QtWidgets.QLabel('%s:' % _("Wk. Orientation"))
+        self.wk_orientation_label.setToolTip(_("Can be:\n"
+                                               "- Portrait\n"
+                                               "- Landscape"))
+
+        self.wk_orientation_radio = RadioSet([{'label': _('Portrait'), 'value': 'p'},
+                                              {'label': _('Landscape'), 'value': 'l'},
+                                              ], stretch=False)
+
+        self.wks = OptionalInputSection(self.workspace_cb,
+                                        [
+                                            self.workspace_type_lbl,
+                                            self.wk_cb,
+                                            self.wk_orientation_label,
+                                            self.wk_orientation_radio
+                                        ])
 
         # Plot Fill Color
         self.pf_color_label = QtWidgets.QLabel('%s:' % _('Plot Fill'))
@@ -579,6 +652,8 @@ class GeneralGUIPrefGroupUI(OptionsGroupUI):
 
         self.form_box.addRow(self.workspace_lbl, self.workspace_cb)
         self.form_box.addRow(self.workspace_type_lbl, self.wk_cb)
+        self.form_box.addRow(self.wk_orientation_label, self.wk_orientation_radio)
+
         self.form_box.addRow(self.spacelabel, self.spacelabel)
         self.form_box.addRow(self.pf_color_label, self.form_box_child_1)
         self.form_box.addRow(self.pf_alpha_label, self.form_box_child_2)
@@ -4654,6 +4729,84 @@ class ToolsFilmPrefGroupUI(OptionsGroupUI):
         grid0.addWidget(self.file_type_label, 15, 0)
         grid0.addWidget(self.file_type_radio, 15, 1)
 
+        # Page orientation
+        self.orientation_label = QtWidgets.QLabel('%s:' % _("Page Orientation"))
+        self.orientation_label.setToolTip(_("Can be:\n"
+                                            "- Portrait\n"
+                                            "- Lanscape"))
+
+        self.orientation_radio = RadioSet([{'label': _('Portrait'), 'value': 'p'},
+                                           {'label': _('Landscape'), 'value': 'l'},
+                                           ], stretch=False)
+
+        grid0.addWidget(self.orientation_label, 16, 0)
+        grid0.addWidget(self.orientation_radio, 16, 1)
+
+        # Page Size
+        self.pagesize_label = QtWidgets.QLabel('%s:' % _("Page Size"))
+        self.pagesize_label.setToolTip(_("A selection of standard ISO 216 page sizes."))
+
+        self.pagesize_combo = FCComboBox()
+
+        self.pagesize = dict()
+        self.pagesize.update(
+            {
+                'Bounds': None,
+                'A0': (841, 1189),
+                'A1': (594, 841),
+                'A2': (420, 594),
+                'A3': (297, 420),
+                'A4': (210, 297),
+                'A5': (148, 210),
+                'A6': (105, 148),
+                'A7': (74, 105),
+                'A8': (52, 74),
+                'A9': (37, 52),
+                'A10': (26, 37),
+
+                'B0': (1000, 1414),
+                'B1': (707, 1000),
+                'B2': (500, 707),
+                'B3': (353, 500),
+                'B4': (250, 353),
+                'B5': (176, 250),
+                'B6': (125, 176),
+                'B7': (88, 125),
+                'B8': (62, 88),
+                'B9': (44, 62),
+                'B10': (31, 44),
+
+                'C0': (917, 1297),
+                'C1': (648, 917),
+                'C2': (458, 648),
+                'C3': (324, 458),
+                'C4': (229, 324),
+                'C5': (162, 229),
+                'C6': (114, 162),
+                'C7': (81, 114),
+                'C8': (57, 81),
+                'C9': (40, 57),
+                'C10': (28, 40),
+
+                # American paper sizes
+                'LETTER': (8.5, 11),
+                'LEGAL': (8.5, 14),
+                'ELEVENSEVENTEEN': (11, 17),
+
+                # From https://en.wikipedia.org/wiki/Paper_size
+                'JUNIOR_LEGAL': (5, 8),
+                'HALF_LETTER': (5.5, 8),
+                'GOV_LETTER': (8, 10.5),
+                'GOV_LEGAL': (8.5, 13),
+                'LEDGER': (17, 11),
+            }
+        )
+
+        page_size_list = list(self.pagesize.keys())
+        self.pagesize_combo.addItems(page_size_list)
+
+        grid0.addWidget(self.pagesize_label, 17, 0)
+        grid0.addWidget(self.pagesize_combo, 17, 1)
 
         self.layout.addStretch()
 

+ 149 - 30
flatcamTools/ToolFilm.py

@@ -9,15 +9,17 @@ from PyQt5 import QtGui, QtCore, QtWidgets
 
 from FlatCAMTool import FlatCAMTool
 from flatcamGUI.GUIElements import RadioSet, FCDoubleSpinner, FCCheckBox, \
-    OptionalHideInputSection, OptionalInputSection
+    OptionalHideInputSection, OptionalInputSection, FCComboBox
 
 from copy import deepcopy
 import logging
 from shapely.geometry import Polygon, MultiPolygon, Point
 
-from reportlab.graphics import renderPDF, renderPM
+from reportlab.graphics import renderPDF
 from reportlab.pdfgen import canvas
-from reportlab.lib.pagesizes import letter, A0, A1, A2, A3, A4, A5
+from reportlab.graphics import renderPM
+from reportlab.lib.units import inch, mm
+from reportlab.lib.pagesizes import landscape, portrait, A4
 
 from svglib.svglib import svg2rlg
 from xml.dom.minidom import parseString as parse_xml_string
@@ -268,6 +270,10 @@ class Film(FlatCAMTool):
         separator_line2.setFrameShadow(QtWidgets.QFrame.Sunken)
         grid0.addWidget(separator_line2, 17, 0, 1, 2)
 
+        self.film_param_label = QtWidgets.QLabel('<b>%s</b>' % _("Film Parameters"))
+
+        grid0.addWidget(self.film_param_label, 18, 0, 1, 2)
+
         # Scale Stroke size
         self.film_scale_stroke_entry = FCDoubleSpinner()
         self.film_scale_stroke_entry.set_range(-999.9999, 999.9999)
@@ -280,10 +286,10 @@ class Film(FlatCAMTool):
               "It means that the line that envelope each SVG feature will be thicker or thinner,\n"
               "therefore the fine features may be more affected by this parameter.")
         )
-        grid0.addWidget(self.film_scale_stroke_label, 18, 0)
-        grid0.addWidget(self.film_scale_stroke_entry, 18, 1)
+        grid0.addWidget(self.film_scale_stroke_label, 19, 0)
+        grid0.addWidget(self.film_scale_stroke_entry, 19, 1)
 
-        grid0.addWidget(QtWidgets.QLabel(''), 19, 0)
+        grid0.addWidget(QtWidgets.QLabel(''), 20, 0)
 
         # Film Type
         self.film_type = RadioSet([{'label': _('Positive'), 'value': 'pos'},
@@ -298,8 +304,8 @@ class Film(FlatCAMTool):
               "with white on a black canvas.\n"
               "The Film format is SVG.")
         )
-        grid0.addWidget(self.film_type_label, 20, 0)
-        grid0.addWidget(self.film_type, 20, 1)
+        grid0.addWidget(self.film_type_label, 21, 0)
+        grid0.addWidget(self.film_type, 21, 1)
 
         # Boundary for negative film generation
         self.boundary_entry = FCDoubleSpinner()
@@ -318,8 +324,8 @@ class Film(FlatCAMTool):
               "white color like the rest and which may confound with the\n"
               "surroundings if not for this border.")
         )
-        grid0.addWidget(self.boundary_label, 21, 0)
-        grid0.addWidget(self.boundary_entry, 21, 1)
+        grid0.addWidget(self.boundary_label, 22, 0)
+        grid0.addWidget(self.boundary_entry, 22, 1)
 
         self.boundary_label.hide()
         self.boundary_entry.hide()
@@ -329,7 +335,7 @@ class Film(FlatCAMTool):
         self.punch_cb.setToolTip(_("When checked the generated film will have holes in pads when\n"
                                    "the generated film is positive. This is done to help drilling,\n"
                                    "when done manually."))
-        grid0.addWidget(self.punch_cb, 22, 0, 1, 2)
+        grid0.addWidget(self.punch_cb, 23, 0, 1, 2)
 
         # this way I can hide/show the frame
         self.punch_frame = QtWidgets.QFrame()
@@ -393,6 +399,7 @@ class Film(FlatCAMTool):
         separator_line3.setFrameShadow(QtWidgets.QFrame.Sunken)
         grid1.addWidget(separator_line3, 0, 0, 1, 2)
 
+        # File type
         self.file_type_radio = RadioSet([{'label': _('SVG'), 'value': 'svg'},
                                          {'label': _('PNG'), 'value': 'png'},
                                          {'label': _('PDF'), 'value': 'pdf'}
@@ -408,15 +415,96 @@ class Film(FlatCAMTool):
         grid1.addWidget(self.file_type_label, 1, 0)
         grid1.addWidget(self.file_type_radio, 1, 1)
 
+        # Page orientation
+        self.orientation_label = QtWidgets.QLabel('%s:' % _("Page Orientation"))
+        self.orientation_label.setToolTip(_("Can be:\n"
+                                            "- Portrait\n"
+                                            "- Landscape"))
+
+        self.orientation_radio = RadioSet([{'label': _('Portrait'), 'value': 'p'},
+                                           {'label': _('Landscape'), 'value': 'l'},
+                                           ], stretch=False)
+
+        grid1.addWidget(self.orientation_label, 2, 0)
+        grid1.addWidget(self.orientation_radio, 2, 1)
+
+        # Page Size
+        self.pagesize_label = QtWidgets.QLabel('%s:' % _("Page Size"))
+        self.pagesize_label.setToolTip(_("A selection of standard ISO 216 page sizes."))
+
+        self.pagesize_combo = FCComboBox()
+
+        self.pagesize = dict()
+        self.pagesize.update(
+            {
+                'Bounds': None,
+                'A0': (841*mm, 1189*mm),
+                'A1': (594*mm, 841*mm),
+                'A2': (420*mm, 594*mm),
+                'A3': (297*mm, 420*mm),
+                'A4': (210*mm, 297*mm),
+                'A5': (148*mm, 210*mm),
+                'A6': (105*mm, 148*mm),
+                'A7': (74*mm, 105*mm),
+                'A8': (52*mm, 74*mm),
+                'A9': (37*mm, 52*mm),
+                'A10': (26*mm, 37*mm),
+
+                'B0': (1000*mm, 1414*mm),
+                'B1': (707*mm, 1000*mm),
+                'B2': (500*mm, 707*mm),
+                'B3': (353*mm, 500*mm),
+                'B4': (250*mm, 353*mm),
+                'B5': (176*mm, 250*mm),
+                'B6': (125*mm, 176*mm),
+                'B7': (88*mm, 125*mm),
+                'B8': (62*mm, 88*mm),
+                'B9': (44*mm, 62*mm),
+                'B10': (31*mm, 44*mm),
+
+                'C0': (917*mm, 1297*mm),
+                'C1': (648*mm, 917*mm),
+                'C2': (458*mm, 648*mm),
+                'C3': (324*mm, 458*mm),
+                'C4': (229*mm, 324*mm),
+                'C5': (162*mm, 229*mm),
+                'C6': (114*mm, 162*mm),
+                'C7': (81*mm, 114*mm),
+                'C8': (57*mm, 81*mm),
+                'C9': (40*mm, 57*mm),
+                'C10': (28*mm, 40*mm),
+
+                # American paper sizes
+                'LETTER': (8.5*inch, 11*inch),
+                'LEGAL': (8.5*inch, 14*inch),
+                'ELEVENSEVENTEEN': (11*inch, 17*inch),
+
+                # From https://en.wikipedia.org/wiki/Paper_size
+                'JUNIOR_LEGAL': (5*inch, 8*inch),
+                'HALF_LETTER': (5.5*inch, 8*inch),
+                'GOV_LETTER': (8*inch, 10.5*inch),
+                'GOV_LEGAL': (8.5*inch, 13*inch),
+                'LEDGER': (17*inch, 11*inch),
+            }
+        )
+
+        page_size_list = list(self.pagesize.keys())
+        self.pagesize_combo.addItems(page_size_list)
+
+        grid1.addWidget(self.pagesize_label, 3, 0)
+        grid1.addWidget(self.pagesize_combo, 3, 1)
+
+        self.on_film_type(val='hide')
+
         # Buttons
         self.film_object_button = QtWidgets.QPushButton(_("Save Film"))
         self.film_object_button.setToolTip(
             _("Create a Film for the selected object, within\n"
               "the specified box. Does not create a new \n "
-              "FlatCAM object, but directly save it in SVG format\n"
-              "which can be opened with Inkscape.")
+              "FlatCAM object, but directly save it in the\n"
+              "selected format.")
         )
-        grid1.addWidget(self.film_object_button, 2, 0, 1, 2)
+        grid1.addWidget(self.film_object_button, 4, 0, 1, 2)
 
         self.layout.addStretch()
 
@@ -429,6 +517,7 @@ class Film(FlatCAMTool):
 
         self.film_type.activated_custom.connect(self.on_film_type)
         self.source_punch.activated_custom.connect(self.on_punch_source)
+        self.file_type_radio.activated_custom.connect(self.on_file_type)
 
     def on_type_obj_index_changed(self, index):
         obj_type = self.tf_type_obj_combo.currentIndex()
@@ -498,6 +587,8 @@ class Film(FlatCAMTool):
         self.film_mirror_cb.set_value(self.app.defaults["tools_film_mirror_cb"])
         self.film_mirror_axis.set_value(self.app.defaults["tools_film_mirror_axis_radio"])
         self.file_type_radio.set_value(self.app.defaults["tools_film_file_type_radio"])
+        self.orientation_radio.set_value(self.app.defaults["tools_film_orientation"])
+        self.pagesize_combo.set_value(self.app.defaults["tools_film_pagesize"])
 
     def on_film_type(self, val):
         type_of_film = val
@@ -512,6 +603,18 @@ class Film(FlatCAMTool):
             self.boundary_entry.hide()
             self.punch_cb.show()
 
+    def on_file_type(self, val):
+        if val == 'pdf':
+            self.orientation_label.show()
+            self.orientation_radio.show()
+            self.pagesize_label.show()
+            self.pagesize_combo.show()
+        else:
+            self.orientation_label.hide()
+            self.orientation_radio.hide()
+            self.pagesize_label.hide()
+            self.pagesize_combo.hide()
+
     def on_punch_source(self, val):
         if val == 'pad' and self.punch_cb.get_value():
             self.punch_size_label.show()
@@ -604,7 +707,7 @@ class Film(FlatCAMTool):
         try:
             filename, _f = QtWidgets.QFileDialog.getSaveFileName(
                 caption=_("Export positive film"),
-                directory=self.app.get_last_save_folder() + '/' + name,
+                directory=self.app.get_last_save_folder() + '/' + name + '_film',
                 filter=filter_ext)
         except TypeError:
             filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export positive film"))
@@ -647,7 +750,7 @@ class Film(FlatCAMTool):
             outname = name + "_punched"
             self.app.new_object('gerber', outname, init_func)
 
-            self.generate_positive_normal_film(outname, boxname, factor=factor)
+            self.generate_positive_normal_film(outname, boxname, factor=factor, ftype=ftype)
         else:
             log.debug("ToolFilm.Film.generate_positive_punched_film() with Pad center source started ...")
 
@@ -698,7 +801,7 @@ class Film(FlatCAMTool):
             outname = name + "_punched"
             self.app.new_object('gerber', outname, init_func)
 
-            self.generate_positive_normal_film(outname, boxname, factor=factor)
+            self.generate_positive_normal_film(outname, boxname, factor=factor, ftype=ftype)
 
     def generate_negative_film(self, name, boxname, factor, ftype='svg'):
         log.debug("ToolFilm.Film.generate_negative_film() started ...")
@@ -744,7 +847,7 @@ class Film(FlatCAMTool):
         try:
             filename, _f = QtWidgets.QFileDialog.getSaveFileName(
                 caption=_("Export negative film"),
-                directory=self.app.get_last_save_folder() + '/' + name,
+                directory=self.app.get_last_save_folder() + '/' + name + '_film',
                 filter=filter_ext)
         except TypeError:
             filename, _f = QtWidgets.QFileDialog.getSaveFileName(caption=_("Export negative film"))
@@ -785,6 +888,7 @@ class Film(FlatCAMTool):
         those are the 4 points of the bounding box of the geometry to be skewed.
         :param mirror: can be 'x' or 'y' or 'both'. Axis on which to mirror the svg geometry
         :param use_thread: if to be run in a separate thread; boolean
+        :param ftype: the type of file for saving the film: 'svg', 'png' or 'pdf'
         :return:
         """
         self.app.report_usage("export_negative()")
@@ -896,19 +1000,26 @@ class Film(FlatCAMTool):
             else:
                 try:
                     if self.units == 'INCH':
-                        from reportlab.lib.units import inch
                         unit = inch
                     else:
-                        from reportlab.lib.units import mm
                         unit = mm
 
                     doc_final = StringIO(doc_final)
-                    my_canvas = canvas.Canvas(filename, pagesize=A4)
                     drawing = svg2rlg(doc_final)
-                    my_canvas.translate(bounds[0] * unit, bounds[1] * unit)
 
-                    renderPDF.draw(drawing, my_canvas, 0, 0)
-                    my_canvas.save()
+                    p_size = self.pagesize_combo.get_value()
+                    if p_size == 'Bounds':
+                        renderPDF.drawToFile(drawing, filename)
+                    else:
+                        if self.orientation_radio.get_value() == 'p':
+                            page_size = portrait(self.pagesize[p_size])
+                        else:
+                            page_size = landscape(self.pagesize[p_size])
+
+                        my_canvas = canvas.Canvas(filename, pagesize=page_size)
+                        my_canvas.translate(bounds[0] * unit, bounds[1] * unit)
+                        renderPDF.draw(drawing, my_canvas, 0, 0)
+                        my_canvas.save()
                 except Exception as e:
                     log.debug("FilmTool.export_negative() --> PDF output --> %s" % str(e))
                     return 'fail'
@@ -955,6 +1066,7 @@ class Film(FlatCAMTool):
         :param mirror: can be 'x' or 'y' or 'both'. Axis on which to mirror the svg geometry
 
         :param use_thread: if to be run in a separate thread; boolean
+        :param ftype: the type of file for saving the film: 'svg', 'png' or 'pdf'
         :return:
         """
         self.app.report_usage("export_positive()")
@@ -1059,19 +1171,26 @@ class Film(FlatCAMTool):
             else:
                 try:
                     if self.units == 'INCH':
-                        from reportlab.lib.units import inch
                         unit = inch
                     else:
-                        from reportlab.lib.units import mm
                         unit = mm
 
                     doc_final = StringIO(doc_final)
-                    my_canvas = canvas.Canvas(filename, pagesize=A4)
                     drawing = svg2rlg(doc_final)
-                    my_canvas.translate(bounds[0]*unit, bounds[1]*unit)
 
-                    renderPDF.draw(drawing, my_canvas, 0, 0)
-                    my_canvas.save()
+                    p_size = self.pagesize_combo.get_value()
+                    if p_size == 'Bounds':
+                        renderPDF.drawToFile(drawing, filename)
+                    else:
+                        if self.orientation_radio.get_value() == 'p':
+                            page_size = portrait(self.pagesize[p_size])
+                        else:
+                            page_size = landscape(self.pagesize[p_size])
+
+                        my_canvas = canvas.Canvas(filename, pagesize=page_size)
+                        my_canvas.translate(bounds[0] * unit, bounds[1] * unit)
+                        renderPDF.draw(drawing, my_canvas, 0, 0)
+                        my_canvas.save()
                 except Exception as e:
                     log.debug("FilmTool.export_positive() --> PDF output --> %s" % str(e))
                     return 'fail'

+ 10 - 12
flatcamTools/ToolMove.py

@@ -160,11 +160,12 @@ class ToolMove(FlatCAMTool):
 
                     def job_move(app_obj):
                         with self.app.proc_container.new(_("Moving...")) as proc:
-                            try:
-                                if not obj_list:
-                                    self.app.inform.emit('[WARNING_NOTCL] %s' % _("No object(s) selected."))
-                                    return "fail"
 
+                            if not obj_list:
+                                app_obj.app.inform.emit('[WARNING_NOTCL] %s' % _("No object(s) selected."))
+                                return "fail"
+
+                            try:
                                 # remove any mark aperture shape that may be displayed
                                 for sel_obj in obj_list:
                                     # if the Gerber mark shapes are enabled they need to be disabled before move
@@ -173,10 +174,9 @@ class ToolMove(FlatCAMTool):
 
                                     try:
                                         sel_obj.replotApertures.emit()
-                                    except Exception as e:
+                                    except Exception:
                                         pass
 
-                                for sel_obj in obj_list:
                                     # offset solid_geometry
                                     sel_obj.offset((dx, dy))
 
@@ -186,15 +186,13 @@ class ToolMove(FlatCAMTool):
                                     sel_obj.options['ymin'] = b
                                     sel_obj.options['xmax'] = c
                                     sel_obj.options['ymax'] = d
-
-                                # time to plot the moved objects
-                                self.replot_signal.emit(obj_list)
                             except Exception as e:
-                                proc.done()
-                                self.app.inform.emit('[ERROR_NOTCL] %s --> %s' % ('ToolMove.on_left_click()', str(e)))
+                                log.debug('[ERROR_NOTCL] %s --> %s' % ('ToolMove.on_left_click()', str(e)))
                                 return "fail"
 
-                        proc.done()
+                            # time to plot the moved objects
+                            app_obj.replot_signal.emit(obj_list)
+
                         # delete the selection bounding box
                         self.delete_shape()
                         self.app.inform.emit('[success] %s %s' %

+ 3 - 3
flatcamTools/ToolPaint.py

@@ -381,6 +381,7 @@ class ToolPaint(FlatCAMTool, Gerber):
         self.overlap = None
         self.connect = None
         self.contour = None
+        self.select_method = None
 
         self.units = ''
         self.paint_tools = {}
@@ -966,7 +967,6 @@ class ToolPaint(FlatCAMTool, Gerber):
         self.connect = self.pathconnect_cb.get_value()
         self.contour = self.paintcontour_cb.get_value()
         self.select_method = self.selectmethod_combo.get_value()
-
         self.obj_name = self.obj_combo.currentText()
 
         # Get source object.
@@ -2018,7 +2018,7 @@ class ToolPaint(FlatCAMTool, Gerber):
             except FlatCAMApp.GracefulException:
                 proc.done()
                 return
-            except Exception as e:
+            except Exception:
                 proc.done()
                 traceback.print_stack()
                 return
@@ -2493,7 +2493,7 @@ class ToolPaint(FlatCAMTool, Gerber):
             except FlatCAMApp.GracefulException:
                 proc.done()
                 return
-            except Exception as e:
+            except Exception:
                 proc.done()
                 traceback.print_stack()
                 return