From f02fb288a4ae67a4e0e3cf64cc257378a67acc71 Mon Sep 17 00:00:00 2001 From: Valdas2000 <35214527+Valdas2000@users.noreply.github.com> Date: Thu, 11 Jan 2018 23:28:12 +0300 Subject: [PATCH 1/3] Add files via upload The commitment introducethe following major changes: 1- the project migrated on python3 2- the project migrated to QT5 Enhansments: ------------- 1 - Templates got Align line on contrast background 2 - Thin markers in editor 3 - Updated PNG Known Issues: -------------- 1 - Export 3DS does not work propertly Installetion: -------------- 1. Download fresh python setup from https://www.python.org/downloads/ (now it is 3.6.4) do not forget instaletion path 2. Setup libraries pip install pyQT5 pip install future pip install Pillow --- README.md | 23 ++++ mac.spec | 4 +- qt_config.py | 186 ++++++++++++++++--------------- qt_driver.py | 308 +++++++++++++++++++++++++++++---------------------- qt_fig.py | 74 +++++++++---- qt_test.py | 9 +- qt_utils.py | 30 ++--- serialize.py | 26 +++-- 8 files changed, 387 insertions(+), 273 deletions(-) diff --git a/README.md b/README.md index 1ecc0b1..7e29e1a 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,26 @@ joints. For more information, [see the pyRouterJig website](http://lowrie.github.io/pyRouterJig/) +The commitment introducethe following major changes: +1- the project migrated on python3 +2- the project migrated to QT5 + +Enhansments: +------------- +1 - Templates got Align line on contrast background +2 - Thin markers in editor +3 - Updated PNG + +Known Issues: +-------------- +1 - Export 3DS does not work propertly + +Installetion: +-------------- +1. Download fresh python setup from https://www.python.org/downloads/ (now it is 3.6.4) +do not forget instaletion path +2. Setup libraries +pip install pyQT5 +pip install future +pip install Pillow + diff --git a/mac.spec b/mac.spec index 5f553ec..2fc76f6 100644 --- a/mac.spec +++ b/mac.spec @@ -15,8 +15,8 @@ a = Analysis(['pyRouterJig.py'], cipher=block_cipher) # Explicitly add libraries that pyinstaller misses -a.binaries += [('libQtCore.4.dylib', '/anaconda/lib/libQtCore.4.dylib', 'BINARY') ] -a.binaries += [('libQtGui.4.dylib', '/anaconda/lib/libQtGui.4.dylib', 'BINARY') ] +a.binaries += [('libQtCore.5.dylib', '/anaconda/lib/libQtCore.5.dylib', 'BINARY') ] +a.binaries += [('libQtGui.5.dylib', '/anaconda/lib/libQtGui.5.dylib', 'BINARY') ] a.binaries += [('libpng16.16.dylib', '/anaconda/lib/libpng16.16.dylib', 'BINARY') ] # Remove various unused files from distribution diff --git a/qt_config.py b/qt_config.py index 3cd635d..807f8a6 100644 --- a/qt_config.py +++ b/qt_config.py @@ -24,7 +24,7 @@ from __future__ import print_function import os, sys -from PyQt4 import QtCore, QtGui +from PyQt5 import QtCore, QtGui, QtWidgets import config_file import qt_utils @@ -38,7 +38,7 @@ def form_line(label, widget=None, tooltip=None): and returns the QLayout ''' - grid = QtGui.QGridLayout() + grid = QtWidgets.QGridLayout() line = qt_utils.create_hline() line.setMinimumWidth(20) if tooltip is not None: @@ -59,12 +59,12 @@ def is_positive(v): def is_nonnegative(v): return v >= 0 -class Color_Button(QtGui.QPushButton): +class Color_Button(QtWidgets.QPushButton): ''' A QPushButton that is simply a rectangle of a single color. ''' def __init__(self, color, parent): - QtGui.QPushButton.__init__(self, parent) + QtWidgets.QPushButton.__init__(self, parent) size = QtCore.QSize(80, 20) self.setFixedSize(size) self.set_color(color) @@ -83,15 +83,15 @@ def add_color_to_dialog(color): ''' Adds color to the QColorDialog custom colors. ''' - count = QtGui.QColorDialog.customCount() - cprev = QtGui.QColorDialog.customColor(0) + count = QtWidgets.QColorDialog.customCount() + cprev = QtWidgets.QColorDialog.customColor(0) # shift all the current colors by one index for i in range(1, count): - c = QtGui.QColorDialog.customColor(i) - QtGui.QColorDialog.setCustomColor(i, cprev) + c = QtWidgets.QColorDialog.customColor(i) + QtWidgets.QColorDialog.setCustomColor(i, cprev) cprev = c # add the new color to the first index - QtGui.QColorDialog.setCustomColor(0, color.rgba()) + QtWidgets.QColorDialog.setCustomColor(0, color.rgba()) class Misc_Value(object): ''' @@ -115,12 +115,12 @@ def set_value_from_string(self, s): raise router.Router_Exception(msg) self.value = value -class Config_Window(QtGui.QDialog): +class Config_Window(QtWidgets.QDialog): ''' Qt interface to config file parameters ''' def __init__(self, config, units, parent=None): - QtGui.QDialog.__init__(self, parent) + QtWidgets.QDialog.__init__(self, parent) self.config = config self.new_config = self.config.__dict__.copy() self.line_edit_width = 80 @@ -135,12 +135,12 @@ def __init__(self, config, units, parent=None): self.board = router.Board(self.bit, width=board_width) # Form the tabs and their contents - title_label = QtGui.QLabel('pyRouterJig Preferences') + title_label = QtWidgets.QLabel('pyRouterJig Preferences') title_label.setAlignment(QtCore.Qt.AlignHCenter) - vbox = QtGui.QVBoxLayout() + vbox = QtWidgets.QVBoxLayout() vbox.addWidget(title_label) - tabs = QtGui.QTabWidget() + tabs = QtWidgets.QTabWidget() tabs.addTab(self.create_output(), 'Output') tabs.addTab(self.create_boards(), 'Boards') @@ -158,25 +158,25 @@ def __init__(self, config, units, parent=None): def create_units(self): '''Creates the layout for units preferences''' - w = QtGui.QWidget() - vbox = QtGui.QVBoxLayout() + w = QtWidgets.QWidget() + vbox = QtWidgets.QVBoxLayout() - mesg = QtGui.QLabel('WARNING:' + mesg = QtWidgets.QLabel('WARNING:' ' Changing any Units settings will require pyRouterJig' ' to restart and your present joint will be lost.') mesg.setWordWrap(True) vbox.addWidget(mesg) - self.cb_units_label = QtGui.QLabel('Unit System:') - self.cb_units = QtGui.QComboBox(self) + self.cb_units_label = QtWidgets.QLabel('Unit System:') + self.cb_units = QtWidgets.QComboBox(self) self.cb_units.addItem('Metric') self.cb_units.addItem('English') self.cb_units.activated.connect(self._on_units) grid = form_line(self.cb_units_label, self.cb_units) vbox.addLayout(grid) - self.le_num_incr_label = QtGui.QLabel(self.units_label(self.config.metric)) - self.le_num_incr = QtGui.QLineEdit(w) + self.le_num_incr_label = QtWidgets.QLabel(self.units_label(self.config.metric)) + self.le_num_incr = QtWidgets.QLineEdit(w) self.le_num_incr.setFixedWidth(self.line_edit_width) self.le_num_incr.editingFinished.connect(self._on_num_incr) tt = 'The number of increments per unit length.' @@ -192,7 +192,8 @@ def set_wood_combobox(self): Sets the entries for the wood combox box, and resets the default wood. ''' (woods, patterns) = qt_utils.create_wood_dict(self.new_config['wood_images']) - woodnames = woods.keys() + #python 3 + woodnames = list( woods.keys() ) woodnames.extend(patterns.keys()) self.cb_wood.clear() self.cb_wood.addItems(woodnames) @@ -205,20 +206,20 @@ def set_wood_combobox(self): def create_boards(self): '''Creates the layout for boards preferences''' - w = QtGui.QWidget() - vbox = QtGui.QVBoxLayout() + w = QtWidgets.QWidget() + vbox = QtWidgets.QVBoxLayout() us = self.units.units_string(withParens=True) - self.le_board_width_label = QtGui.QLabel('Initial Board Width{}:'.format(us)) - self.le_board_width = QtGui.QLineEdit(w) + self.le_board_width_label = QtWidgets.QLabel('Initial Board Width{}:'.format(us)) + self.le_board_width = QtWidgets.QLineEdit(w) self.le_board_width.setFixedWidth(self.line_edit_width) self.le_board_width.editingFinished.connect(self._on_board_width) tt = 'The initial board width when pyRouterJig starts.' grid = form_line(self.le_board_width_label, self.le_board_width, tt) vbox.addLayout(grid) - self.le_db_thick_label = QtGui.QLabel('Initial Double Board Thickness{}:'.format(us)) - self.le_db_thick = QtGui.QLineEdit(w) + self.le_db_thick_label = QtWidgets.QLabel('Initial Double Board Thickness{}:'.format(us)) + self.le_db_thick = QtWidgets.QLineEdit(w) self.le_db_thick.setFixedWidth(self.line_edit_width) self.le_db_thick.editingFinished.connect(self._on_db_thick) tt = 'The initial double-board thickness when pyRouterJig starts.' @@ -226,22 +227,23 @@ def create_boards(self): vbox.addLayout(grid) (woods, patterns) = qt_utils.create_wood_dict(self.config.wood_images) - woodnames = woods.keys() + #python 3 + woodnames = list(woods.keys()) woodnames.extend(patterns.keys()) - self.cb_wood_label = QtGui.QLabel('Default Wood Fill:') - self.cb_wood = QtGui.QComboBox(self) + self.cb_wood_label = QtWidgets.QLabel('Default Wood Fill:') + self.cb_wood = QtWidgets.QComboBox(self) self.set_wood_combobox() self.cb_wood.activated.connect(self._on_wood) tt = 'The default wood fill for each board.' grid = form_line(self.cb_wood_label, self.cb_wood, tt) vbox.addLayout(grid) - self.le_wood_images_label = QtGui.QLabel('Wood Images Folder:') - self.le_wood_images = QtGui.QLineEdit(w) + self.le_wood_images_label = QtWidgets.QLabel('Wood Images Folder:') + self.le_wood_images = QtWidgets.QLineEdit(w) self.le_wood_images.editingFinished.connect(self._on_wood_images) tt = 'Location of wood images.' self.le_wood_images.setToolTip(tt) - grid = QtGui.QGridLayout() + grid = QtWidgets.QGridLayout() grid.addWidget(qt_utils.create_vline(), 0, 0, 4, 1) grid.addWidget(qt_utils.create_vline(), 0, 3, 4, 1) grid.addWidget(qt_utils.create_hline(), 0, 0, 1, 4) @@ -257,28 +259,28 @@ def create_boards(self): def create_bit(self): '''Creates the layout for bit preferences''' - w = QtGui.QWidget() - vbox = QtGui.QVBoxLayout() + w = QtWidgets.QWidget() + vbox = QtWidgets.QVBoxLayout() us = self.units.units_string(withParens=True) - self.le_bit_width_label = QtGui.QLabel('Initial Bit Width{}:'.format(us)) - self.le_bit_width = QtGui.QLineEdit(w) + self.le_bit_width_label = QtWidgets.QLabel('Initial Bit Width{}:'.format(us)) + self.le_bit_width = QtWidgets.QLineEdit(w) self.le_bit_width.setFixedWidth(self.line_edit_width) self.le_bit_width.editingFinished.connect(self._on_bit_width) tt = 'The initial bit width when pyRouterJig starts.' grid = form_line(self.le_bit_width_label, self.le_bit_width, tt) vbox.addLayout(grid) - self.le_bit_depth_label = QtGui.QLabel('Initial Bit Depth{}:'.format(us)) - self.le_bit_depth = QtGui.QLineEdit(w) + self.le_bit_depth_label = QtWidgets.QLabel('Initial Bit Depth{}:'.format(us)) + self.le_bit_depth = QtWidgets.QLineEdit(w) self.le_bit_depth.setFixedWidth(self.line_edit_width) self.le_bit_depth.editingFinished.connect(self._on_bit_depth) tt = 'The initial bit depth when pyRouterJig starts.' grid = form_line(self.le_bit_depth_label, self.le_bit_depth, tt) vbox.addLayout(grid) - self.le_bit_angle_label = QtGui.QLabel('Initial Bit Angle (deg.):') - self.le_bit_angle = QtGui.QLineEdit(w) + self.le_bit_angle_label = QtWidgets.QLabel('Initial Bit Angle (deg.):') + self.le_bit_angle = QtWidgets.QLineEdit(w) self.le_bit_angle.setFixedWidth(self.line_edit_width) self.le_bit_angle.editingFinished.connect(self._on_bit_angle) tt = 'The initial bit angle when pyRouterJig starts.' @@ -291,21 +293,21 @@ def create_bit(self): def create_colors(self): '''Creates the layout for color preferences''' - w = QtGui.QWidget() - vbox = QtGui.QVBoxLayout() + w = QtWidgets.QWidget() + vbox = QtWidgets.QVBoxLayout() - self.cb_print_color = QtGui.QCheckBox('Print in Color', w) + self.cb_print_color = QtWidgets.QCheckBox('Print in Color', w) self.cb_print_color.stateChanged.connect(self._on_print_color) self.cb_print_color.setToolTip('If true, print in color. Otherwise, converts to black and white.') vbox.addWidget(self.cb_print_color) - grid = QtGui.QGridLayout() + grid = QtWidgets.QGridLayout() flag_label = QtCore.Qt.AlignRight flag_color = QtCore.Qt.AlignLeft row = 0 col = 0 - self.btn_canvas_background_label = QtGui.QLabel('Canvas Background') + self.btn_canvas_background_label = QtWidgets.QLabel('Canvas Background') self.btn_canvas_background = Color_Button(self.new_config['canvas_background'], w) self.btn_canvas_background.clicked.connect(lambda: self._on_set_color('canvas_background', self.btn_canvas_background)) tt = 'Sets the background color of the canvas.' @@ -314,7 +316,7 @@ def create_colors(self): grid.addWidget(self.btn_canvas_background, row, col+1, flag_color) row += 1 - self.btn_canvas_foreground_label = QtGui.QLabel('Canvas Foreground') + self.btn_canvas_foreground_label = QtWidgets.QLabel('Canvas Foreground') self.btn_canvas_foreground = Color_Button(self.new_config['canvas_foreground'], w) self.btn_canvas_foreground.clicked.connect(lambda: self._on_set_color('canvas_foreground', self.btn_canvas_foreground)) tt = 'Sets the foreground color of the canvas.' @@ -323,7 +325,7 @@ def create_colors(self): grid.addWidget(self.btn_canvas_foreground, row, col+1, flag_color) row += 1 - self.btn_board_background_label = QtGui.QLabel('Board Background') + self.btn_board_background_label = QtWidgets.QLabel('Board Background') self.btn_board_background = Color_Button(self.new_config['board_background'], w) self.btn_board_background.clicked.connect(lambda: self._on_set_color('board_background', self.btn_board_background)) tt = 'Sets the top board background color.' @@ -332,7 +334,7 @@ def create_colors(self): grid.addWidget(self.btn_board_background, row, col+1, flag_color) row += 1 - self.btn_board_foreground_label = QtGui.QLabel('Board Foreground') + self.btn_board_foreground_label = QtWidgets.QLabel('Board Foreground') self.btn_board_foreground = Color_Button(self.new_config['board_foreground'], w) self.btn_board_foreground.clicked.connect(lambda: self._on_set_color('board_foreground', self.btn_board_foreground)) tt = 'Sets the top board foreground color.' @@ -343,7 +345,7 @@ def create_colors(self): max_row = row row = 0 col += 2 - self.btn_pass_color_label = QtGui.QLabel('Pass') + self.btn_pass_color_label = QtWidgets.QLabel('Pass') self.btn_pass_color = Color_Button(self.new_config['pass_color'], w) self.btn_pass_color.clicked.connect(lambda: self._on_set_color('pass_color', self.btn_pass_color)) tt = 'Sets the template foreground color for each pass.' @@ -352,7 +354,7 @@ def create_colors(self): grid.addWidget(self.btn_pass_color, row, col+1, flag_color) row += 1 - self.btn_pass_alt_color_label = QtGui.QLabel('Pass-Alt') + self.btn_pass_alt_color_label = QtWidgets.QLabel('Pass-Alt') self.btn_pass_alt_color = Color_Button(self.new_config['pass_alt_color'], w) self.btn_pass_alt_color.clicked.connect(lambda: self._on_set_color('pass_alt_color', self.btn_pass_alt_color)) tt = 'Sets the template foreground alternate color for each pass.' @@ -361,7 +363,7 @@ def create_colors(self): grid.addWidget(self.btn_pass_alt_color, row, col+1, flag_color) row += 1 - self.btn_center_color_label = QtGui.QLabel('Center Pass') + self.btn_center_color_label = QtWidgets.QLabel('Center Pass') self.btn_center_color = Color_Button(self.new_config['center_color'], w) self.btn_center_color.clicked.connect(lambda: self._on_set_color('center_color', self.btn_center_color)) tt = 'Sets the template foreground color for the center pass.' @@ -370,7 +372,7 @@ def create_colors(self): grid.addWidget(self.btn_center_color, row, col+1, flag_color) row += 1 - self.btn_watermark_color_label = QtGui.QLabel('Watermark') + self.btn_watermark_color_label = QtWidgets.QLabel('Watermark') self.btn_watermark_color = Color_Button(self.new_config['watermark_color'], w) self.btn_watermark_color.clicked.connect(lambda: self._on_set_color('watermark_color', self.btn_watermark_color)) tt = 'Sets the watermark color.' @@ -379,11 +381,11 @@ def create_colors(self): grid.addWidget(self.btn_watermark_color, row, col+1, flag_color) vbox.addLayout(grid) - grid = QtGui.QGridLayout() + grid = QtWidgets.QGridLayout() row = 0 col = 0 - self.btn_template_margin_background_label = QtGui.QLabel('Template Margin Background') + self.btn_template_margin_background_label = QtWidgets.QLabel('Template Margin Background') self.btn_template_margin_background = Color_Button(self.new_config['template_margin_background'], w) self.btn_template_margin_background.clicked.connect(lambda: self._on_set_color('template_margin_background', self.btn_template_margin_background)) tt = 'Sets the template margin background color.' @@ -392,7 +394,7 @@ def create_colors(self): grid.addWidget(self.btn_template_margin_background, row, col+1, flag_color) row += 1 - self.btn_template_margin_foreground_label = QtGui.QLabel('Template Margin Foreground') + self.btn_template_margin_foreground_label = QtWidgets.QLabel('Template Margin Foreground') self.btn_template_margin_foreground = Color_Button(self.new_config['template_margin_foreground'], w) self.btn_template_margin_foreground.clicked.connect(lambda: self._on_set_color('template_margin_foreground', self.btn_template_margin_foreground)) tt = 'Sets the template margin foreground color.' @@ -408,52 +410,52 @@ def create_colors(self): def create_output(self): '''Creates the layout for output preferences''' - w = QtGui.QWidget() - vbox = QtGui.QVBoxLayout() + w = QtWidgets.QWidget() + vbox = QtWidgets.QVBoxLayout() - self.cb_show_caul = QtGui.QCheckBox('Show Caul Template', w) + self.cb_show_caul = QtWidgets.QCheckBox('Show Caul Template', w) self.cb_show_caul.stateChanged.connect(self._on_show_caul) self.cb_show_caul.setToolTip('Display the template for clamping cauls') vbox.addWidget(self.cb_show_caul) - self.cb_show_finger_widths = QtGui.QCheckBox('Show Finger Widths', w) + self.cb_show_finger_widths = QtWidgets.QCheckBox('Show Finger Widths', w) self.cb_show_finger_widths.stateChanged.connect(self._on_show_finger_widths) self.cb_show_finger_widths.setToolTip('Display the width of each finger') vbox.addWidget(self.cb_show_finger_widths) - self.cb_show_fit = QtGui.QCheckBox('Show Fit', w) + self.cb_show_fit = QtWidgets.QCheckBox('Show Fit', w) self.cb_show_fit.stateChanged.connect(self._on_show_fit) self.cb_show_fit.setToolTip('Display fit of joint') vbox.addWidget(self.cb_show_fit) - self.cb_rpid = QtGui.QCheckBox('Show Router Pass Identifiers', w) + self.cb_rpid = QtWidgets.QCheckBox('Show Router Pass Identifiers', w) self.cb_rpid.stateChanged.connect(self._on_rpid) self.cb_rpid.setToolTip('On each router pass, label its identifier') vbox.addWidget(self.cb_rpid) - self.cb_rploc = QtGui.QCheckBox('Show Router Pass Locations', w) + self.cb_rploc = QtWidgets.QCheckBox('Show Router Pass Locations', w) self.cb_rploc.stateChanged.connect(self._on_rploc) self.cb_rploc.setToolTip('On each router pass, label its distance from the right edge') vbox.addWidget(self.cb_rploc) - self.le_printsf_label = QtGui.QLabel('Print Scale Factor:') - self.le_printsf = QtGui.QLineEdit(w) + self.le_printsf_label = QtWidgets.QLabel('Print Scale Factor:') + self.le_printsf = QtWidgets.QLineEdit(w) self.le_printsf.setFixedWidth(self.line_edit_width) self.le_printsf.editingFinished.connect(self._on_printsf) tt = 'Scale output by this factor when printing.' grid = form_line(self.le_printsf_label, self.le_printsf, tt) vbox.addLayout(grid) - self.le_min_image_label = QtGui.QLabel('Min Image Width (pixels):') - self.le_min_image = QtGui.QLineEdit(w) + self.le_min_image_label = QtWidgets.QLabel('Min Image Width (pixels):') + self.le_min_image = QtWidgets.QLineEdit(w) self.le_min_image.setFixedWidth(self.line_edit_width) self.le_min_image.editingFinished.connect(self._on_min_image) tt = 'On save image, minimum width of image.' grid = form_line(self.le_min_image_label, self.le_min_image, tt) vbox.addLayout(grid) - self.le_max_image_label = QtGui.QLabel('Max Image Width (pixels):') - self.le_max_image = QtGui.QLineEdit(w) + self.le_max_image_label = QtWidgets.QLabel('Max Image Width (pixels):') + self.le_max_image = QtWidgets.QLineEdit(w) self.le_max_image.setFixedWidth(self.line_edit_width) self.le_max_image.editingFinished.connect(self._on_max_image) tt = 'On save image, maximum width of image.' @@ -465,36 +467,36 @@ def create_output(self): def create_misc(self): '''Creates the layout for misc preferences''' - w = QtGui.QWidget() - vbox = QtGui.QVBoxLayout() + w = QtWidgets.QWidget() + vbox = QtWidgets.QVBoxLayout() us = self.units.units_string(withParens=True) - self.le_min_finger_width_label = QtGui.QLabel('Min Finger Width{}:'.format(us)) - self.le_min_finger_width = QtGui.QLineEdit(w) + self.le_min_finger_width_label = QtWidgets.QLabel('Min Finger Width{}:'.format(us)) + self.le_min_finger_width = QtWidgets.QLineEdit(w) self.le_min_finger_width.setFixedWidth(self.line_edit_width) self.le_min_finger_width.editingFinished.connect(self._on_min_finger_width) tt = 'The minimum allowable finger width. Currently, only enforced for Equal Spacing.' grid = form_line(self.le_min_finger_width_label, self.le_min_finger_width, tt) vbox.addLayout(grid) - self.le_caul_trim_label = QtGui.QLabel('Caul Trim{}:'.format(us)) - self.le_caul_trim = QtGui.QLineEdit(w) + self.le_caul_trim_label = QtWidgets.QLabel('Caul Trim{}:'.format(us)) + self.le_caul_trim = QtWidgets.QLineEdit(w) self.le_caul_trim.setFixedWidth(self.line_edit_width) self.le_caul_trim.editingFinished.connect(self._on_caul_trim) tt = 'The distance from the edge of each finger to the edge of the corresponding caul finger.' grid = form_line(self.le_caul_trim_label, self.le_caul_trim, tt) vbox.addLayout(grid) - self.le_warn_gap_label = QtGui.QLabel('Warning gap{}:'.format(us)) - self.le_warn_gap = QtGui.QLineEdit(w) + self.le_warn_gap_label = QtWidgets.QLabel('Warning gap{}:'.format(us)) + self.le_warn_gap = QtWidgets.QLineEdit(w) self.le_warn_gap.setFixedWidth(self.line_edit_width) self.le_warn_gap.editingFinished.connect(self._on_warn_gap) tt = 'If the gap in the joint exceeds this value, warn the user.' grid = form_line(self.le_warn_gap_label, self.le_warn_gap, tt) vbox.addLayout(grid) - self.le_warn_overlap_label = QtGui.QLabel('Warning overlap{}:'.format(us)) - self.le_warn_overlap = QtGui.QLineEdit(w) + self.le_warn_overlap_label = QtWidgets.QLabel('Warning overlap{}:'.format(us)) + self.le_warn_overlap = QtWidgets.QLineEdit(w) self.le_warn_overlap.setFixedWidth(self.line_edit_width) self.le_warn_overlap.editingFinished.connect(self._on_warn_overlap) tt = 'If the overlap in the joint exceeds this value, warn the user.' @@ -508,16 +510,16 @@ def create_misc(self): def create_buttons(self): '''Creates the layout for the buttons''' - hbox_btns = QtGui.QHBoxLayout() + hbox_btns = QtWidgets.QHBoxLayout() - btn_cancel = QtGui.QPushButton('Cancel', self) + btn_cancel = QtWidgets.QPushButton('Cancel', self) btn_cancel.clicked.connect(self._on_cancel) btn_cancel.setAutoDefault(False) btn_cancel.setFocusPolicy(QtCore.Qt.ClickFocus) btn_cancel.setToolTip('Discard any preference changes and continue.') hbox_btns.addWidget(btn_cancel) - self.btn_save = QtGui.QPushButton('Save', self) + self.btn_save = QtWidgets.QPushButton('Save', self) self.btn_save.clicked.connect(self._on_save) self.btn_save.setAutoDefault(False) self.btn_save.setFocusPolicy(QtCore.Qt.ClickFocus) @@ -602,9 +604,9 @@ def _on_save(self): do_restart = False if self.change_state == 2: # Units were changes, so ask for a restart - box = QtGui.QMessageBox(self) + box = QtWidgets.QMessageBox(self) box.setTextFormat(QtCore.Qt.RichText) - box.setIcon(QtGui.QMessageBox.NoIcon) + box.setIcon(QtWidgets.QMessageBox.NoIcon) box.setText('Warning!') question = 'You have changed a Units setting, which'\ ' requires pyRouterJig to restart to take effect.'\ @@ -613,8 +615,8 @@ def _on_save(self): ' Press Cancel to discard the changes to preferences that'\ ' you have made.' box.setInformativeText(question) - buttonRestart = box.addButton('Restart', QtGui.QMessageBox.AcceptRole) - buttonCancel = box.addButton('Cancel', QtGui.QMessageBox.AcceptRole) + buttonRestart = box.addButton('Restart', QtWidgets.QMessageBox.AcceptRole) + buttonCancel = box.addButton('Cancel', QtWidgets.QMessageBox.AcceptRole) box.setDefaultButton(buttonCancel) box.raise_() box.exec_() @@ -804,7 +806,7 @@ def _on_printsf(self): else: msg = 'Unable to set Print Scale Factor to: {}
'\ 'Set to a positive number.'.format(s) - QtGui.QMessageBox.warning(self, 'Error', msg) + QtWidgets.QMessageBox.warning(self, 'Error', msg) self.le_printsf.setText(str(self.new_config['print_scale_factor'])) @QtCore.pyqtSlot() @@ -827,7 +829,7 @@ def _on_min_image(self): else: msg = 'Unable to set Min Image Width to: {}
'\ 'Set to a positive integer.'.format(s) - QtGui.QMessageBox.warning(self, 'Error', msg) + QtWidgets.QMessageBox.warning(self, 'Error', msg) self.le_min_image.setText(str(self.new_config['min_image_width'])) @QtCore.pyqtSlot() @@ -851,7 +853,7 @@ def _on_max_image(self): else: msg = 'Unable to set Max Image Width to: {}
'\
'Set to a positive integer >= Min Image Width ({})'.format(s, min_value)
- QtGui.QMessageBox.warning(self, 'Error', msg)
+ QtWidgets.QMessageBox.warning(self, 'Error', msg)
self.le_max_image.setText(str(self.new_config['max_image_width']))
@QtCore.pyqtSlot()
@@ -909,8 +911,8 @@ def _on_set_color(self, name, btn):
if self.config.debug:
print('qt_config:set_color {}'.format(name))
init_color = QtGui.QColor(*self.new_config[name])
- flags = QtGui.QColorDialog.ShowAlphaChannel | QtGui.QColorDialog.DontUseNativeDialog
- color = QtGui.QColorDialog.getColor(init_color, self, 'Select {}'.format(name), flags)
+ flags = QtWidgets.QColorDialog.ShowAlphaChannel | QtWidgets.QColorDialog.DontUseNativeDialog
+ color = QtWidgets.QColorDialog.getColor(init_color, self, 'Select {}'.format(name), flags)
if color.isValid():
self.new_config[name] = color.getRgb()
self.update_state(name)
diff --git a/qt_driver.py b/qt_driver.py
index 07dd0b7..576bba2 100644
--- a/qt_driver.py
+++ b/qt_driver.py
@@ -24,6 +24,12 @@
from __future__ import print_function
from future.utils import lrange
from builtins import str
+from PIL import Image
+from PIL import PngImagePlugin
+try:
+ from StringIO import BytesIO
+except ImportError:
+ from io import BytesIO
import os, sys, traceback, webbrowser, copy, shutil
@@ -38,14 +44,14 @@
import serialize
import threeDS
-from PyQt4 import QtCore, QtGui
+from PyQt5 import QtCore, QtGui, QtWidgets
-class Driver(QtGui.QMainWindow):
+class Driver(QtWidgets.QMainWindow):
'''
Qt driver for pyRouterJig
'''
def __init__(self, parent=None):
- QtGui.QMainWindow.__init__(self, parent)
+ QtWidgets.QMainWindow.__init__(self, parent)
sys.excepthook = self.exception_hook
self.except_handled = False
@@ -135,9 +141,9 @@ def load_config(self):
if r == 1:
# The config file does not exist. Ask the user whether they want metric or english
# units
- box = QtGui.QMessageBox(self)
+ box = QtWidgets.QMessageBox(self)
box.setTextFormat(QtCore.Qt.RichText)
- box.setIcon(QtGui.QMessageBox.NoIcon)
+ box.setIcon(QtWidgets.QMessageBox.NoIcon)
box.setText('Welcome to pyRouterJig !')
question = 'Please select a unit system below.'\
' The configuration file {} '\
@@ -146,8 +152,8 @@ def load_config(self):
' may be changed later by selecting Preferences under'\
' the {} menu. {} '\
'has been created. The old version was saved'\
@@ -193,8 +199,8 @@ def load_config(self):
def center(self):
'''Centers the app in the screen'''
frameGm = self.frameGeometry()
- screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
- centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
+ screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
+ centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
frameGm.moveCenter(centerPoint)
self.move(frameGm.topLeft())
@@ -212,7 +218,7 @@ def exception_hook(self, etype, value, trace):
tmp = traceback.format_exception_only(etype, value)
exception = '\n'.join(tmp)
- QtGui.QMessageBox.warning(self, 'Error', exception)
+ QtWidgets.QMessageBox.warning(self, 'Error', exception)
self.except_handled = False
def create_menu(self):
@@ -228,13 +234,13 @@ def create_menu(self):
file_menu = self.menubar.addMenu('File')
- open_action = QtGui.QAction('&Open File...', self)
+ open_action = QtWidgets.QAction('&Open File...', self)
open_action.setShortcut('Ctrl+O')
open_action.setStatusTip('Opens a previously saved image of joint')
open_action.triggered.connect(self._on_open)
file_menu.addAction(open_action)
- save_action = QtGui.QAction('&Save File...', self)
+ save_action = QtWidgets.QAction('&Save File...', self)
save_action.setShortcut('Ctrl+S')
save_action.setStatusTip('Saves an image of the joint to a file')
save_action.triggered.connect(self._on_save)
@@ -242,7 +248,7 @@ def create_menu(self):
file_menu.addSeparator()
- print_action = QtGui.QAction('&Print...', self)
+ print_action = QtWidgets.QAction('&Print...', self)
print_action.setShortcut('Ctrl+P')
print_action.setStatusTip('Print the figure')
print_action.triggered.connect(self._on_print)
@@ -250,7 +256,7 @@ def create_menu(self):
file_menu.addSeparator()
- exit_action = QtGui.QAction('&Quit pyRouterJig', self)
+ exit_action = QtWidgets.QAction('&Quit pyRouterJig', self)
exit_action.setShortcut('Ctrl+Q')
exit_action.setStatusTip('Quit pyRouterJig')
exit_action.triggered.connect(self._on_exit)
@@ -258,7 +264,7 @@ def create_menu(self):
file_menu.addAction(exit_action)
# comment out for now...
- #table_action = QtGui.QAction('Print Table...', self)
+ #table_action = QtWidgets.QAction('Print Table...', self)
#table_action.setStatusTip('Print a table of router pass locations')
#table_action.triggered.connect(self._on_print_table)
#file_menu.addAction(table_action)
@@ -267,25 +273,25 @@ def create_menu(self):
view_menu = self.menubar.addMenu('View')
- self.caul_action = QtGui.QAction('Caul Template', self, checkable=True)
+ self.caul_action = QtWidgets.QAction('Caul Template', self, checkable=True)
self.caul_action.setStatusTip('Toggle caul template')
self.caul_action.triggered.connect(self._on_caul)
view_menu.addAction(self.caul_action)
self.caul_action.setChecked(self.config.show_caul)
- self.finger_size_action = QtGui.QAction('Finger Widths', self, checkable=True)
+ self.finger_size_action = QtWidgets.QAction('Finger Widths', self, checkable=True)
self.finger_size_action.setStatusTip('Toggle viewing finger sizes')
self.finger_size_action.triggered.connect(self._on_finger_sizes)
view_menu.addAction(self.finger_size_action)
self.finger_size_action.setChecked(self.config.show_finger_widths)
- self.fit_action = QtGui.QAction('Fit', self, checkable=True)
+ self.fit_action = QtWidgets.QAction('Fit', self, checkable=True)
self.fit_action.setStatusTip('Toggle showing fit of joint')
self.fit_action.triggered.connect(self._on_fit)
view_menu.addAction(self.fit_action)
self.fit_action.setChecked(self.config.show_fit)
- self.zoom_action = QtGui.QAction('Zoom Mode', self, checkable=True)
+ self.zoom_action = QtWidgets.QAction('Zoom Mode', self, checkable=True)
self.zoom_action.setStatusTip('Toggle zoom mode')
self.zoom_action.triggered.connect(self._on_zoom)
view_menu.addAction(self.zoom_action)
@@ -295,13 +301,13 @@ def create_menu(self):
view_menu.addSeparator()
pass_menu = view_menu.addMenu('Router Passes')
- self.pass_id_action = QtGui.QAction('Identifiers', self, checkable=True)
+ self.pass_id_action = QtWidgets.QAction('Identifiers', self, checkable=True)
self.pass_id_action.setStatusTip('Toggle viewing router pass identifiers')
self.pass_id_action.triggered.connect(self._on_pass_id)
pass_menu.addAction(self.pass_id_action)
self.pass_id_action.setChecked(self.config.show_router_pass_identifiers)
- self.pass_location_action = QtGui.QAction('Locations', self, checkable=True)
+ self.pass_location_action = QtWidgets.QAction('Locations', self, checkable=True)
self.pass_location_action.setStatusTip('Toggle viewing router pass locations')
self.pass_location_action.triggered.connect(self._on_pass_location)
pass_menu.addAction(self.pass_location_action)
@@ -310,7 +316,7 @@ def create_menu(self):
# The Mac automatically adds full screen to the View menu, but do so for other platforms
#if not utils.isMac():
view_menu.addSeparator()
- fullscreen_action = QtGui.QAction('Full Screen Mode', self, checkable=True)
+ fullscreen_action = QtWidgets.QAction('Full Screen Mode', self, checkable=True)
fullscreen_action.setShortcut('Ctrl+F')
fullscreen_action.setStatusTip('Toggle full-screen mode')
fullscreen_action.triggered.connect(self._on_fullscreen)
@@ -320,7 +326,7 @@ def create_menu(self):
tools_menu = self.menubar.addMenu('Tools')
- screenshot_action = QtGui.QAction('Screenshot...', self)
+ screenshot_action = QtWidgets.QAction('Screenshot...', self)
screenshot_action.setShortcut('Ctrl+W')
screenshot_action.setStatusTip('Saves an image of the pyRouterJig window to a file')
screenshot_action.triggered.connect(self._on_screenshot)
@@ -329,7 +335,7 @@ def create_menu(self):
# We need to make this action persistent, so that we can
# enable and disable it (until all of its functionality is
# written)
- self.threeDS_action = QtGui.QAction('&Export 3DS...', self)
+ self.threeDS_action = QtWidgets.QAction('&Export 3DS...', self)
self.threeDS_action.setShortcut('Ctrl+E')
self.threeDS_action.setStatusTip('Export the joint to a 3DS file')
self.threeDS_action.triggered.connect(self._on_3ds)
@@ -338,7 +344,7 @@ def create_menu(self):
tools_menu.addSeparator()
- pref_action = QtGui.QAction('Preferences...', self)
+ pref_action = QtWidgets.QAction('Preferences...', self)
pref_action.setShortcut('Ctrl+,')
pref_action.setStatusTip('Open preferences')
pref_action.triggered.connect(self._on_preferences)
@@ -348,7 +354,7 @@ def create_menu(self):
help_menu = self.menubar.addMenu('Help')
- about_action = QtGui.QAction('&About pyRouterJig', self)
+ about_action = QtWidgets.QAction('&About pyRouterJig', self)
about_action.setShortcut('Ctrl+A')
about_action.setStatusTip('About this program')
about_action.triggered.connect(self._on_about)
@@ -356,7 +362,7 @@ def create_menu(self):
view_menu.addSeparator()
- doclink_action = QtGui.QAction('&Documentation...', self)
+ doclink_action = QtWidgets.QAction('&Documentation...', self)
doclink_action.setStatusTip('Opens documentation page in web browser')
doclink_action.triggered.connect(self._on_doclink)
help_menu.addAction(doclink_action)
@@ -403,7 +409,7 @@ def create_widgets(self):
'''
Creates all of the widgets in the main panel
'''
- self.main_frame = QtGui.QWidget()
+ self.main_frame = QtWidgets.QWidget()
lineEditWidth = 80
us = self.units.units_string(withParens=True)
@@ -412,34 +418,34 @@ def create_widgets(self):
self.fig = qt_fig.Qt_Fig(self.template, self.boards, self.config)
self.fig.canvas.setParent(self.main_frame)
self.fig.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
- self.fig.canvas.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
+ self.fig.canvas.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
self.fig.canvas.setFocus()
# Board width line edit
- self.le_board_width_label = QtGui.QLabel('Board Width{}'.format(us))
- self.le_board_width = QtGui.QLineEdit(self.main_frame)
+ self.le_board_width_label = QtWidgets.QLabel('Board Width{}'.format(us))
+ self.le_board_width = QtWidgets.QLineEdit(self.main_frame)
self.le_board_width.setFixedWidth(lineEditWidth)
self.le_board_width.setText(self.units.increments_to_string(self.boards[0].width))
self.le_board_width.editingFinished.connect(self._on_board_width)
- self.le_board_width.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
+ self.le_board_width.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
# Bit width line edit
- self.le_bit_width_label = QtGui.QLabel('Bit Width{}'.format(us))
- self.le_bit_width = QtGui.QLineEdit(self.main_frame)
+ self.le_bit_width_label = QtWidgets.QLabel('Bit Width{}'.format(us))
+ self.le_bit_width = QtWidgets.QLineEdit(self.main_frame)
self.le_bit_width.setFixedWidth(lineEditWidth)
self.le_bit_width.setText(self.units.increments_to_string(self.bit.width))
self.le_bit_width.editingFinished.connect(self._on_bit_width)
# Bit depth line edit
- self.le_bit_depth_label = QtGui.QLabel('Bit Depth{}'.format(us))
- self.le_bit_depth = QtGui.QLineEdit(self.main_frame)
+ self.le_bit_depth_label = QtWidgets.QLabel('Bit Depth{}'.format(us))
+ self.le_bit_depth = QtWidgets.QLineEdit(self.main_frame)
self.le_bit_depth.setFixedWidth(lineEditWidth)
self.le_bit_depth.setText(self.units.increments_to_string(self.bit.depth))
self.le_bit_depth.editingFinished.connect(self._on_bit_depth)
# Bit angle line edit
- self.le_bit_angle_label = QtGui.QLabel('Bit Angle (deg.)')
- self.le_bit_angle = QtGui.QLineEdit(self.main_frame)
+ self.le_bit_angle_label = QtWidgets.QLabel('Bit Angle (deg.)')
+ self.le_bit_angle = QtWidgets.QLineEdit(self.main_frame)
self.le_bit_angle.setFixedWidth(lineEditWidth)
self.le_bit_angle.setText('%g' % self.bit.angle)
self.le_bit_angle.editingFinished.connect(self._on_bit_angle)
@@ -448,8 +454,8 @@ def create_widgets(self):
self.le_boardm_label = []
self.le_boardm = []
for i in lrange(2):
- self.le_boardm_label.append(QtGui.QLabel('Thickness{}'.format(us)))
- self.le_boardm.append(QtGui.QLineEdit(self.main_frame))
+ self.le_boardm_label.append(QtWidgets.QLabel('Thickness{}'.format(us)))
+ self.le_boardm.append(QtWidgets.QLineEdit(self.main_frame))
self.le_boardm[i].setFixedWidth(lineEditWidth)
s = self.units.increments_to_string(self.boards[i+2].dheight)
self.le_boardm[i].setText(s)
@@ -468,10 +474,10 @@ def create_widgets(self):
self.cb_wood.append(self.create_wood_combo_box(woods, patterns, True))
self.cb_wood.append(self.create_wood_combo_box(woods, patterns, True))
self.cb_wood_label = []
- self.cb_wood_label.append(QtGui.QLabel('Top Board'))
- self.cb_wood_label.append(QtGui.QLabel('Bottom Board'))
- self.cb_wood_label.append(QtGui.QLabel('Double Board'))
- self.cb_wood_label.append(QtGui.QLabel('Double-Double Board'))
+ self.cb_wood_label.append(QtWidgets.QLabel('Top Board'))
+ self.cb_wood_label.append(QtWidgets.QLabel('Bottom Board'))
+ self.cb_wood_label.append(QtWidgets.QLabel('Double Board'))
+ self.cb_wood_label.append(QtWidgets.QLabel('Double-Double Board'))
# Disable double* boards, for now
self.le_boardm[0].setEnabled(False)
@@ -490,33 +496,33 @@ def create_widgets(self):
# ...first slider
p = params['Spacing']
- self.es_slider0_label = QtGui.QLabel(labels[0])
- self.es_slider0 = QtGui.QSlider(QtCore.Qt.Horizontal, self.main_frame)
+ self.es_slider0_label = QtWidgets.QLabel(labels[0])
+ self.es_slider0 = QtWidgets.QSlider(QtCore.Qt.Horizontal, self.main_frame)
self.es_slider0.setFocusPolicy(QtCore.Qt.StrongFocus)
self.es_slider0.setMinimum(p.vMin)
self.es_slider0.setMaximum(p.vMax)
self.es_slider0.setValue(p.v)
- self.es_slider0.setTickPosition(QtGui.QSlider.TicksBelow)
+ self.es_slider0.setTickPosition(QtWidgets.QSlider.TicksBelow)
utils.set_slider_tick_interval(self.es_slider0)
self.es_slider0.valueChanged.connect(self._on_es_slider0)
- self.es_slider0.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
+ self.es_slider0.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
# ...second slider
p = params['Width']
- self.es_slider1_label = QtGui.QLabel(labels[1])
- self.es_slider1 = QtGui.QSlider(QtCore.Qt.Horizontal, self.main_frame)
+ self.es_slider1_label = QtWidgets.QLabel(labels[1])
+ self.es_slider1 = QtWidgets.QSlider(QtCore.Qt.Horizontal, self.main_frame)
self.es_slider1.setFocusPolicy(QtCore.Qt.StrongFocus)
self.es_slider1.setMinimum(p.vMin)
self.es_slider1.setMaximum(p.vMax)
self.es_slider1.setValue(p.v)
- self.es_slider1.setTickPosition(QtGui.QSlider.TicksBelow)
+ self.es_slider1.setTickPosition(QtWidgets.QSlider.TicksBelow)
utils.set_slider_tick_interval(self.es_slider1)
self.es_slider1.valueChanged.connect(self._on_es_slider1)
- self.es_slider1.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
+ self.es_slider1.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
# ...check box for centering
p = params['Centered']
- self.cb_es_centered = QtGui.QCheckBox(labels[2], self.main_frame)
+ self.cb_es_centered = QtWidgets.QCheckBox(labels[2], self.main_frame)
self.cb_es_centered.setChecked(True)
self.cb_es_centered.stateChanged.connect(self._on_cb_es_centered)
@@ -527,71 +533,71 @@ def create_widgets(self):
# ...combox box for fingers
p = params['Fingers']
- self.cb_vsfingers_label = QtGui.QLabel(labels[0])
+ self.cb_vsfingers_label = QtWidgets.QLabel(labels[0])
self.cb_vsfingers = qt_utils.PreviewComboBox(self.main_frame)
self.cb_vsfingers.setFocusPolicy(QtCore.Qt.StrongFocus)
self.update_cb_vsfingers(p.vMin, p.vMax, p.v)
# Edit spacing widgets
- edit_btn_undo = QtGui.QPushButton('Undo', self.main_frame)
+ edit_btn_undo = QtWidgets.QPushButton('Undo', self.main_frame)
edit_btn_undo.clicked.connect(self._on_edit_undo)
edit_btn_undo.setToolTip('Undo the last change')
- edit_btn_add = QtGui.QPushButton('Add', self.main_frame)
+ edit_btn_add = QtWidgets.QPushButton('Add', self.main_frame)
edit_btn_add.clicked.connect(self._on_edit_add)
edit_btn_add.setToolTip('Add a cut (if there is space to add cuts)')
- edit_btn_del = QtGui.QPushButton('Delete', self.main_frame)
+ edit_btn_del = QtWidgets.QPushButton('Delete', self.main_frame)
edit_btn_del.clicked.connect(self._on_edit_del)
edit_btn_del.setToolTip('Delete the active cuts')
- edit_move_label = QtGui.QLabel('Move')
+ edit_move_label = QtWidgets.QLabel('Move')
edit_move_label.setToolTip('Moves the active cuts')
- edit_btn_moveL = QtGui.QToolButton(self.main_frame)
+ edit_btn_moveL = QtWidgets.QToolButton(self.main_frame)
edit_btn_moveL.setArrowType(QtCore.Qt.LeftArrow)
edit_btn_moveL.clicked.connect(self._on_edit_moveL)
edit_btn_moveL.setToolTip('Move active cuts to left 1 increment')
- edit_btn_moveR = QtGui.QToolButton(self.main_frame)
+ edit_btn_moveR = QtWidgets.QToolButton(self.main_frame)
edit_btn_moveR.setArrowType(QtCore.Qt.RightArrow)
edit_btn_moveR.clicked.connect(self._on_edit_moveR)
edit_btn_moveR.setToolTip('Move active cuts to right 1 increment')
- edit_widen_label = QtGui.QLabel('Widen')
+ edit_widen_label = QtWidgets.QLabel('Widen')
edit_widen_label.setToolTip('Widens the active cuts')
- edit_btn_widenL = QtGui.QToolButton(self.main_frame)
+ edit_btn_widenL = QtWidgets.QToolButton(self.main_frame)
edit_btn_widenL.setArrowType(QtCore.Qt.LeftArrow)
edit_btn_widenL.clicked.connect(self._on_edit_widenL)
edit_btn_widenL.setToolTip('Widen active cuts 1 increment on left side')
- edit_btn_widenR = QtGui.QToolButton(self.main_frame)
+ edit_btn_widenR = QtWidgets.QToolButton(self.main_frame)
edit_btn_widenR.setArrowType(QtCore.Qt.RightArrow)
edit_btn_widenR.clicked.connect(self._on_edit_widenR)
edit_btn_widenR.setToolTip('Widen active cuts 1 increment on right side')
- edit_trim_label = QtGui.QLabel('Trim')
+ edit_trim_label = QtWidgets.QLabel('Trim')
edit_trim_label.setToolTip('Trims the active cuts')
- edit_btn_trimL = QtGui.QToolButton(self.main_frame)
+ edit_btn_trimL = QtWidgets.QToolButton(self.main_frame)
edit_btn_trimL.setArrowType(QtCore.Qt.LeftArrow)
edit_btn_trimL.clicked.connect(self._on_edit_trimL)
edit_btn_trimL.setToolTip('Trim active cuts 1 increment on left side')
- edit_btn_trimR = QtGui.QToolButton(self.main_frame)
+ edit_btn_trimR = QtWidgets.QToolButton(self.main_frame)
edit_btn_trimR.setArrowType(QtCore.Qt.RightArrow)
edit_btn_trimR.clicked.connect(self._on_edit_trimR)
edit_btn_trimR.setToolTip('Trim active cuts 1 increment on right side')
- edit_btn_toggle = QtGui.QPushButton('Toggle', self.main_frame)
+ edit_btn_toggle = QtWidgets.QPushButton('Toggle', self.main_frame)
edit_btn_toggle.clicked.connect(self._on_edit_toggle)
edit_btn_toggle.setToolTip('Toggles the cut at cursor between active and deactive')
- edit_btn_cursorL = QtGui.QToolButton(self.main_frame)
+ edit_btn_cursorL = QtWidgets.QToolButton(self.main_frame)
edit_btn_cursorL.setArrowType(QtCore.Qt.LeftArrow)
edit_btn_cursorL.clicked.connect(self._on_edit_cursorL)
edit_btn_cursorL.setToolTip('Move cut cursor to left')
- edit_btn_cursorR = QtGui.QToolButton(self.main_frame)
+ edit_btn_cursorR = QtWidgets.QToolButton(self.main_frame)
edit_btn_cursorR.setArrowType(QtCore.Qt.RightArrow)
edit_btn_cursorR.clicked.connect(self._on_edit_cursorR)
edit_btn_cursorR.setToolTip('Move cut cursor to right')
- edit_btn_activate_all = QtGui.QPushButton('All', self.main_frame)
+ edit_btn_activate_all = QtWidgets.QPushButton('All', self.main_frame)
edit_btn_activate_all.clicked.connect(self._on_edit_activate_all)
edit_btn_activate_all.setToolTip('Set all cuts to be active')
- edit_btn_deactivate_all = QtGui.QPushButton('None', self.main_frame)
+ edit_btn_deactivate_all = QtWidgets.QPushButton('None', self.main_frame)
edit_btn_deactivate_all.clicked.connect(self._on_edit_deactivate_all)
edit_btn_deactivate_all.setToolTip('Set no cuts to be active')
@@ -607,17 +613,17 @@ def create_widgets(self):
# vbox contains all of the widgets in the main frame, positioned
# vertically
- vbox = QtGui.QVBoxLayout()
+ vbox = QtWidgets.QVBoxLayout()
# Add the figure canvas to the top
vbox.addWidget(self.fig.canvas)
# hbox contains all of the control widgets
# (everything but the canvas)
- hbox = QtGui.QHBoxLayout()
+ hbox = QtWidgets.QHBoxLayout()
# this grid contains all the lower-left input stuff
- grid = QtGui.QGridLayout()
+ grid = QtWidgets.QGridLayout()
grid.addWidget(qt_utils.create_hline(), 0, 0, 2, 9, QtCore.Qt.AlignTop)
grid.addWidget(qt_utils.create_vline(), 0, 0, 9, 1)
@@ -668,14 +674,14 @@ def create_widgets(self):
hbox.addLayout(grid)
# Create the layout of the Equal spacing controls
- hbox_es = QtGui.QHBoxLayout()
+ hbox_es = QtWidgets.QHBoxLayout()
- vbox_es_slider0 = QtGui.QVBoxLayout()
+ vbox_es_slider0 = QtWidgets.QVBoxLayout()
vbox_es_slider0.addWidget(self.es_slider0_label)
vbox_es_slider0.addWidget(self.es_slider0)
hbox_es.addLayout(vbox_es_slider0)
- vbox_es_slider1 = QtGui.QVBoxLayout()
+ vbox_es_slider1 = QtWidgets.QVBoxLayout()
vbox_es_slider1.addWidget(self.es_slider1_label)
vbox_es_slider1.addWidget(self.es_slider1)
hbox_es.addLayout(vbox_es_slider1)
@@ -685,18 +691,18 @@ def create_widgets(self):
# Create the layout of the Variable spacing controls. Given only one
# item, this is overkill, but the coding allows us to add additional
# controls later.
- hbox_vs = QtGui.QHBoxLayout()
+ hbox_vs = QtWidgets.QHBoxLayout()
hbox_vs.addWidget(self.cb_vsfingers_label)
hbox_vs.addWidget(self.cb_vsfingers)
hbox_vs.addStretch(1)
# Create the layout of the edit spacing controls
- hbox_edit = QtGui.QHBoxLayout()
- grid_edit = QtGui.QGridLayout()
+ hbox_edit = QtWidgets.QHBoxLayout()
+ grid_edit = QtWidgets.QGridLayout()
grid_edit.addWidget(qt_utils.create_hline(), 0, 0, 2, 16, QtCore.Qt.AlignTop)
grid_edit.addWidget(qt_utils.create_hline(), 2, 0, 2, 16, QtCore.Qt.AlignTop)
grid_edit.addWidget(qt_utils.create_vline(), 0, 0, 6, 1)
- label_active_cut_select = QtGui.QLabel('Active Cut Select')
+ label_active_cut_select = QtWidgets.QLabel('Active Cut Select')
label_active_cut_select.setToolTip('Tools that select the active cuts')
grid_edit.addWidget(label_active_cut_select, 1, 1, 1, 3, QtCore.Qt.AlignHCenter)
grid_edit.addWidget(edit_btn_toggle, 3, 1, 1, 2, QtCore.Qt.AlignHCenter)
@@ -705,7 +711,7 @@ def create_widgets(self):
grid_edit.addWidget(edit_btn_activate_all, 3, 3)
grid_edit.addWidget(edit_btn_deactivate_all, 4, 3)
grid_edit.addWidget(qt_utils.create_vline(), 0, 4, 6, 1)
- label_active_cut_ops = QtGui.QLabel('Active Cut Operators')
+ label_active_cut_ops = QtWidgets.QLabel('Active Cut Operators')
label_active_cut_ops.setToolTip('Edit operations applied to active cuts')
grid_edit.addWidget(label_active_cut_ops, 1, 5, 1, 10, QtCore.Qt.AlignHCenter)
grid_edit.addWidget(edit_move_label, 3, 5, 1, 2, QtCore.Qt.AlignHCenter)
@@ -731,14 +737,14 @@ def create_widgets(self):
hbox_edit.addWidget(edit_btn_undo)
# Add the spacing layouts as Tabs
- self.tabs_spacing = QtGui.QTabWidget()
- tab_es = QtGui.QWidget()
+ self.tabs_spacing = QtWidgets.QTabWidget()
+ tab_es = QtWidgets.QWidget()
tab_es.setLayout(hbox_es)
self.tabs_spacing.addTab(tab_es, 'Equal')
- tab_vs = QtGui.QWidget()
+ tab_vs = QtWidgets.QWidget()
tab_vs.setLayout(hbox_vs)
self.tabs_spacing.addTab(tab_vs, 'Variable')
- tab_edit = QtGui.QWidget()
+ tab_edit = QtWidgets.QWidget()
tab_edit.setLayout(hbox_edit)
self.tabs_spacing.addTab(tab_edit, 'Editor')
self.tabs_spacing.currentChanged.connect(self._on_tabs_spacing)
@@ -754,7 +760,7 @@ def create_widgets(self):
self.tabs_spacing.setCurrentIndex(self.spacing_index)
# either add the spacing Tabs to the right of the line edits
- vbox_tabs = QtGui.QVBoxLayout()
+ vbox_tabs = QtWidgets.QVBoxLayout()
vbox_tabs.addWidget(self.tabs_spacing)
vbox_tabs.addWidget(self.le_description)
vbox_tabs.addStretch(1)
@@ -858,25 +864,25 @@ def create_status_bar(self):
fmL = QtGui.QFontMetrics(fontL)
fit_text = 'Fit:'
- fit = QtGui.QLabel(fit_text)
+ fit = QtWidgets.QLabel(fit_text)
fit.setFont(fontL)
fit.setFixedWidth(fmL.width(fit_text))
fit.setToolTip(tt_fit)
status_text = 'Status:'
- status = QtGui.QLabel(status_text)
+ status = QtWidgets.QLabel(status_text)
status.setFont(fontL)
status.setFixedWidth(fmL.width(status_text))
status.setToolTip(tt_message)
# Create the label widgets that will change their text
- style = QtGui.QFrame.Panel | QtGui.QFrame.Raised
- self.status_message_label = QtGui.QLabel('MESSAGE')
+ style = QtWidgets.QFrame.Panel | QtWidgets.QFrame.Raised
+ self.status_message_label = QtWidgets.QLabel('MESSAGE')
self.status_message_label.setFont(font)
self.status_message_label.setFrameStyle(style)
self.status_message_label.setToolTip(tt_message)
- self.status_fit_label = QtGui.QLabel('FIT')
+ self.status_fit_label = QtWidgets.QLabel('FIT')
w = fm.width('Max gap = 0.0000 mm Max overlap = 0.0000 mm')
self.status_fit_label.setFixedWidth(w)
self.status_fit_label.setFont(font)
@@ -1056,11 +1062,11 @@ def _on_tabs_spacing(self, index):
msg = 'You are exiting the Editor, which will discard'\
' any changes made in the Editor.'\
'\n\nAre you sure you want to do this?'
- reply = QtGui.QMessageBox.question(self, 'Message', msg,
- QtGui.QMessageBox.Yes,
- QtGui.QMessageBox.No)
+ reply = QtWidgets.QMessageBox.question(self, 'Message', msg,
+ QtWidgets.QMessageBox.Yes,
+ QtWidgets.QMessageBox.No)
- if reply == QtGui.QMessageBox.No:
+ if reply == QtWidgets.QMessageBox.No:
self.tabs_spacing.setCurrentIndex(self.spacing_index)
return
self.reinit_spacing()
@@ -1207,8 +1213,6 @@ def _on_save(self, do_screenshot=False):
# Form the default filename prefix
prefix = 'pyrouterjig'
-# if self.description is not None:
-# prefix = self.description
suffix = 'png'
if self.screenshot_index is None:
self.screenshot_index = utils.get_file_index(self.working_dir, prefix, suffix)
@@ -1224,14 +1228,15 @@ def _on_save(self, do_screenshot=False):
else:
# This is the simple approach to set the filename, but doesn't allow
# us to update the working_dir, if the user changes it.
- #filename = QtGui.QFileDialog.getSaveFileName(self, 'Save file', \
+ #filename = QtWidgets.QFileDialog.getSaveFileName(self, 'Save file', \
# defname, 'Portable Network Graphics (*.png)')
# ... so here is now we do it:
- dialog = QtGui.QFileDialog(self, 'Save file', defname, \
- 'Portable Network Graphics (*.png)')
+ dialog = QtWidgets.QFileDialog(self, 'Save file', defname, \
+ 'Portable Network Graphics (*.png)')
+
dialog.setDefaultSuffix(suffix)
- dialog.setFileMode(QtGui.QFileDialog.AnyFile)
- dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
+ dialog.setFileMode(QtWidgets.QFileDialog.AnyFile)
+ dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
filename = None
if dialog.exec_():
filenames = dialog.selectedFiles()
@@ -1246,16 +1251,43 @@ def _on_save(self, do_screenshot=False):
return
# Save the file with metadata
+
if do_screenshot:
- image = QtGui.QPixmap.grabWindow(self.winId()).toImage()
+ p_screen=QtWidgets.QApplication.primaryScreen()
+ scr_rect = p_screen.geometry();
+ image = QtGui.QScreen.grabWindow(p_screen, scr_rect.x(), scr_rect.y(), scr_rect.width(), scr_rect.height()).toImage()
+ #, scr_rect.x, scr_rect.y, scr_rect.width, scr_rect.height)
+ #.toImage()
else:
image = self.fig.image(self.template, self.boards, self.bit, self.spacing,
self.woods, self.description)
s = serialize.serialize(self.bit, self.boards, self.spacing,
self.config)
- image.setText('pyRouterJig', s)
- r = image.save(filename, 'png')
+
+ #we using UUEC encoding so need more attributes in the image file
+ #QT5 does not work propertly with PNG text use PIL as workaround
+
+ #Save QT image into stream and get it back into PIL to avoid native pil conversion risks
+ buffer = QtCore.QBuffer()
+ buffer.open(QtCore.QIODevice.ReadWrite)
+ image.save(buffer,"PNG")
+ pio=BytesIO()
+ pio.write(buffer.data())
+ pio.seek(0)
+ buffer.close()
+ pilimg = Image.open(pio)
+
+ info = PngImagePlugin.PngInfo()
+ info.add_text('pyRouterJig',s)
+ info.add_text('pyRouterJig_v',utils.VERSION)
+
+ r = True
+ try:
+ pilimg.save(filename,'png',pnginfo=info)
+ except OSError:
+ r = False
+
if r:
self.status_message('Saved to file %s' % filename)
if self.screenshot_index is not None:
@@ -1280,31 +1312,38 @@ def _on_open(self):
msg = 'Current joint not saved.'\
' Opening a new file will overwrite the current joint.'\
'\n\nAre you sure you want to do this?'
- reply = QtGui.QMessageBox.question(self, 'Message', msg,
- QtGui.QMessageBox.Yes,
- QtGui.QMessageBox.No)
+ reply = QtWidgets.QMessageBox.question(self, 'Message', msg,
+ QtWidgets.QMessageBox.Yes,
+ QtWidgets.QMessageBox.No)
- if reply == QtGui.QMessageBox.No:
+ if reply == QtWidgets.QMessageBox.No:
return
# Get the file name
- filename = QtGui.QFileDialog.getOpenFileName(self, 'Open file', \
- self.working_dir, \
- 'Portable Network Graphics (*.png)')
- filename = str(filename).strip()
+ filename, _filter = QtWidgets.QFileDialog.getOpenFileName(self, 'Open file', \
+ self.working_dir, \
+ 'Portable Network Graphics (*.png)')
+
+
+ #filename = str(filename).strip()
if len(filename) == 0:
self.status_message('File open aborted', warning=True)
return
# From the image file, parse the metadata.
- image = QtGui.QImage(filename)
- s = image.text('pyRouterJig') # see setText in _on_save
+ #image = QtGui.QImage
+ #image.Load(filename)
+ image = Image.open(filename)
+ s=image.info['pyRouterJig'];
+
if len(s) == 0:
msg = 'File %s does not contain pyRouterJig data. The PNG file'\
' must have been saved using pyRouterJig.' % filename
- QtGui.QMessageBox.warning(self, 'Error', msg)
+ QtWidgets.QMessageBox.warning(self, 'Error', msg)
return
- (self.bit, self.boards, sp, sp_type) = serialize.unserialize(s, self.config)
+
+ #backword compatimility
+ (self.bit, self.boards, sp, sp_type) = serialize.unserialize(s, self.config, ('pyRouterJig_v' in image.info.keys()) )
# Reset the dependent data
self.units = self.bit.units
@@ -1314,11 +1353,15 @@ def _on_open(self):
# ... set the wood selection for each board. If the wood does not
# exist, set to a wood we know exists. This can happen if the wood
# image files don't exist across users.
+ # self.boards[i].wood is newstr type use str(self.boards[i].wood) is for old files compatibility
for i in lrange(4):
if self.boards[i].wood is None:
self.boards[i].set_wood('NONE')
- elif self.boards[i].wood not in self.woods.keys():
+ elif str(self.boards[i].wood) not in self.woods.keys():
self.boards[i].set_wood('DiagCrossPattern')
+
+ #bacword compatibility fix
+ self.boards[i].wood = str(self.boards[i].wood)
j = self.cb_wood[i].findText(self.boards[i].wood)
self.cb_wood[i].setCurrentIndex(j)
@@ -1384,10 +1427,10 @@ def _on_3ds(self):
# Get the file name
defname = os.path.join(self.working_dir, fname)
- dialog = QtGui.QFileDialog(self, 'Export joint', defname, \
+ dialog = QtWidgets.QFileDialog(self, 'Export joint', defname, \
'Autodesk 3DS file (*.3ds)')
- dialog.setFileMode(QtGui.QFileDialog.AnyFile)
- dialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
+ dialog.setFileMode(QtWidgets.QFileDialog.AnyFile)
+ dialog.setAcceptMode(QtWidgets.QFileDialog.AcceptSave)
filename = None
if dialog.exec_():
filenames = dialog.selectedFiles()
@@ -1441,7 +1484,7 @@ def _on_about(self):
if self.config.debug:
print('_on_about')
- box = QtGui.QMessageBox(self)
+ box = QtWidgets.QMessageBox(self)
s = 'Welcome to pyRouterJig !'
s += ' '\
'Set to a positive value, such as: {}'.format(s, val)
try:
- width = self.units.string_to_increments(s)
+ if self.units.metric:
+ width = self.units.string_to_float(s)
+ else:
+ width = self.units.string_to_increments(s)
except:
raise Router_Exception(msg)
if width <= 0:
raise Router_Exception(msg)
- halfwidth = width // 2
- if 2 * halfwidth != width:
- msg += ' Bit Width must be an even number of increments. '\
- 'The increment size is: {} '\
- ''.format(self.units.increments_to_string(1, True))
- raise Router_Exception(msg)
+ #halfwidth = width // 2
+ #if 2 * halfwidth != width:
+ # msg += ' Bit Width must be an even number of increments. '\
+ # 'The increment size is: {} '\
+ # ''.format(self.units.increments_to_string(1, True))
+ # raise Router_Exception(msg)
self.width = width
- self.halfwidth = halfwidth
self.reinit()
def set_depth_from_string(self, s):
'''
@@ -152,19 +170,19 @@ def reinit(self):
Reinitializes internal attributes that are dependent on width
and angle.
'''
- self.halfwidth = self.width // 2
- self.offset = 0 # ensure exactly 0 for angle == 0
+ self.midline = Decimal(repr(self.width))
+ self.depth_0 = Decimal(repr(self.depth))
+ self.width_f = Decimal(repr(self.width))
+
if self.angle > 0:
- self.offset = self.depth * math.tan(self.angle * math.pi / 180)
- self.neck = self.width - 2 * self.offset
- def dovetail_correction(self):
- '''
- Correction for rounding the offset
- '''
- if utils.my_round(self.neck) + 2 * utils.my_round(self.offset) < self.width:
- return 1
- else:
- return 0
+ tan = Decimal(math.tan(self.angle * math.pi / 180))
+ offset = Decimal(self.depth) * tan
+ self.midline = self.width_f - offset
+ self.midline = self.midline.to_integral_value( rounding=ROUND_HALF_DOWN)
+ self.depth_0 = (self.width_f - self.midline) / tan
+
+ self.overhang = (self.width_f - self.midline) / 2
+
class My_Rectangle(object):
'''
@@ -297,29 +315,29 @@ def _do_cuts(self, bit, cuts, y_nocut, y_cut):
x = []
y = []
if cuts[0].xmin > 0:
- x = [self.xL()]
- y = [y_nocut]
+ x = [Decimal(self.xL())]
+ y = [Decimal(y_nocut)]
# loop through the cuts and add them to the perimeter
for c in cuts:
if c.xmin > 0:
# on the surface, start of cut
- x.append(c.xmin + x[0] + bit.offset)
- y.append(y_nocut)
+ x.append(c.xmin + x[0] + bit.overhang)
+ y.append(Decimal(y_nocut))
# at the cut depth, start of cut
x.append(c.xmin + self.xL())
- y.append(y_cut)
+ y.append(Decimal(y_cut))
# at the cut depth, end of cut
x.append(c.xmax + self.xL())
- y.append(y_cut)
+ y.append(Decimal(y_cut))
if c.xmax < self.width:
# at the surface, end of cut
- x.append(c.xmax + self.xL() - bit.offset)
- y.append(y_nocut)
+ x.append(c.xmax + self.xL() - bit.overhang)
+ y.append(Decimal(y_nocut))
# add the last point on the top and bottom, at the right edge,
# accounting for whether the last cut includes this edge or not.
if cuts[-1].xmax < self.width:
x.append(self.xL() + self.width)
- y.append(y_nocut)
+ y.append(Decimal(y_nocut))
return (x, y)
def do_all_cuts(self, bit):
'''
@@ -473,6 +491,7 @@ def triangulate(self, bit):
class Cut(object):
'''
Cut description.
+ The cut values in Decimals to simplify rounding
Attributes:
@@ -483,12 +502,17 @@ class Cut(object):
on the cut
'''
def __init__(self, xmin, xmax):
- self.xmin = xmin
- self.xmax = xmax
+ self.xmin = Decimal(xmin)
+ self.xmax = Decimal(xmax)
self.passes = []
+ #Presission value is about 1/64 inch (the exact 1/64 = 0.0156 so we fine for bouth mesument systems)
+ self.precision = Decimal('0.01')
+
def validate(self, bit, board):
'''
Checks whether the attributes of the cut are valid.
+ Because we works with floating point it is yet a rounding error
+ Comparisions made with respect to precision
'''
if self.xmin >= self.xmax:
raise Router_Exception('cut xmin = %d, xmax = %d: '\
@@ -500,50 +524,46 @@ def validate(self, bit, board):
raise Router_Exception('cut xmin = %d, xmax = %d:'
' Must have xmax < board width (%d)!'\
% (self.xmin, self.xmax, board.width))
- if self.xmax - self.xmin < bit.width and self.xmin > 0 and self.xmax < board.width:
- raise Router_Exception('cut xmin = %d, xmax = %d: '\
- 'Bit width (%d) too large for this cut!'\
- % (self.xmin, self.xmax, bit.width))
+ if ( bit.width_f - (self.xmax - self.xmin) ) > self.precision and self.xmin > 0 and self.xmax < board.width:
+ raise Router_Exception('cut xmin = %f, xmax = %f ): '\
+ 'Bit width (%f) delta too large for this cut!'\
+ % (self.xmin, self.xmax, bit.width_f))
def make_router_passes(self, bit, board):
'''Computes passes for the given bit.'''
- # The logic below assumes bit.width is even
- if bit.width % 2 != 0:
- Router_Exception('Router-bit width must be even!')
+ # The updated logic below assumes bit.width is even for stright bits only
+
self.validate(bit, board)
# set current extents of the uncut region
xL = self.xmin
xR = self.xmax
+ bitwidth = Decimal(repr(bit.width))
+ halfwidth = bitwidth / 2
# alternate between the left and right sides of the overall cut to make the passes
remainder = xR - xL
self.passes = []
while remainder > 0:
- # start with a pass on the right side of cut
- p = xR - bit.halfwidth
- if p - bit.halfwidth >= self.xmin or self.xmin == 0:
- self.passes.append(p)
- xR -= bit.width
- # if anything to cut remains, do a pass on the far left side
+ p0 = utils.math_round(xR - halfwidth) # right size cut
+ p1 = utils.math_round(xL + halfwidth) # left size cut
+
+ if self.xmax <= board.width and ( self.xmin - (p0 - halfwidth) < self.precision or self.xmin == 0 ):
+ self.passes.append( int(p0) )
+
+ if p0 != p1 and ( (p1 +halfwidth) - self.xmax < self.precision or self.xmax == board.width ):
+ self.passes.append( int(p1) )
+
+ xR -= bitwidth
+ xL += bitwidth
+
remainder = xR - xL
- if remainder > 0:
- p = xL + bit.halfwidth
- if p + bit.halfwidth <= self.xmax or self.xmax == board.width:
- self.passes.append(p)
- xL += bit.width
- remainder = xR - xL
- # at this stage, we've done the same number of left and right passes, so if
- # there's only one more pass needed, center it.
- if remainder > 0 and remainder <= bit.width:
- p = (xL + xR) // 2
- self.passes.append(p)
- remainder = 0
+
# Sort the passes
self.passes = sorted(self.passes)
# Error checking:
for p in self.passes:
- if (self.xmin > 0 and p - bit.halfwidth < self.xmin) or \
- (self.xmax < board.width and p + bit.halfwidth > self.xmax):
- raise Router_Exception('cut xmin = %d, xmax = %d, pass = %d: '\
- 'Bit width (%d) too large for this cut!'\
+ if (self.xmin > 0 and (self.xmin - (p - halfwidth)) > self.precision) or \
+ (self.xmax < board.width and ((p + halfwidth) - self.xmax ) > self.precision ):
+ raise Router_Exception('cut xmin = %f, xmax = %f, pass = %f: '\
+ 'Bit width (%f) too large for this cut!'\
% (self.xmin, self.xmax, p, bit.width))
def adjoining_cuts(cuts, bit, board):
@@ -557,27 +577,33 @@ def adjoining_cuts(cuts, bit, board):
Returns an array of Cut objects
'''
nc = len(cuts)
+ offset = bit.width_f-bit.midline
adjCuts = []
# if the left-most input cut does not include the left edge, add an
# adjoining cut that includes the left edge
if cuts[0].xmin > 0:
left = 0
- right = utils.my_round(cuts[0].xmin + bit.offset) - board.dheight
+ right = cuts[0].xmin + offset - board.dheight
if right - left >= board.dheight:
- adjCuts.append(Cut(left, right))
+ adjCuts.append( Cut( left, round(right, 4) ) )
+
# loop through the input cuts and form an adjoining cut, formed
# by looking where the previous cut ended and the current cut starts
for i in lrange(1, nc):
- left = utils.my_round(cuts[i-1].xmax - bit.offset + board.dheight)
- right = max(left + bit.width, utils.my_round(cuts[i].xmin + bit.offset) - board.dheight)
- adjCuts.append(Cut(left, right))
+ left = cuts[i-1].xmax - offset + board.dheight
+ right = cuts[i].xmin + offset - board.dheight
+ adjCuts.append( Cut( left, right ) )
+
# if the right-most input cut does not include the right edge, add an
# adjoining cut that includes this edge
if cuts[-1].xmax < board.width:
- left = utils.my_round(cuts[-1].xmax - bit.offset) + board.dheight
- right = board.width
+ left = cuts[-1].xmax - offset + board.dheight
+ right = Decimal(board.width)
if right - left >= board.dheight:
- adjCuts.append(Cut(left, right))
+ adjCuts.append(Cut( left, right))
+ # for runtime debug only
+ #print('adjoining_cuts cuts:')
+ #dump_cuts(adjCuts)
return adjCuts
def caul_cuts(cuts, bit, board, trim):
@@ -702,66 +728,21 @@ def __init__(self, template, boards, bit, spacing, margins, config):
def compute_fit(self):
'''
Sets the maximum gap and overlap over all joints.
+ The gap is already computed in the bit object
'''
- # Determine the board indices for each joint:
- # [board index top_cut, board index bottom_cut]
- if self.boards[2].active:
- joints = [[1, 2]]
- if self.boards[3].active:
- joints.append([2, 3])
- joints.append([3, 0])
- else:
- joints.append([2, 0])
- else:
- joints = [[1, 0]]
-
- # Load up the coordinates for the boards
- coords = []
- for i in range(4):
- if self.boards[i].active:
- coords.append(self.boards[i].do_all_cuts(self.bit))
-
- # Determine the max gap and overlap
self.max_gap = 0
self.max_overlap = 0
- for j in joints:
- # Here, bottom and top are with respect to the joint, not the
- # boards, so they're flipped. Roughly, should have ybot < ytop.
- xbot = coords[j[0]][0]
- ybot = coords[j[0]][1]
- xtop = coords[j[1]][2]
- ytop = coords[j[1]][3]
- n = len(xtop)
- yshift = self.boards[j[1]].yB() - self.boards[j[0]].yT() +\
- self.bit.depth
- for i in range(n):
- ybot[i] += yshift
- for i in range(n - 1):
- d = gap_overlap((xbot[i], xbot[i+1]),
- (ybot[i], ybot[i+1]),
- (xtop[i], xtop[i+1]),
- (ytop[i], ytop[i+1]))
- if d > 0:
- self.max_gap = max(self.max_gap, d)
- else:
- self.max_overlap = max(self.max_overlap, -d)
-
-def gap_overlap(xbot, ybot, xtop, ytop):
- '''
- Returns the distance between the midpoint of (xbot, ybot) to the
- line (xtop, ytop). If positive, then a gap; otherwise, an overlap.
- '''
- # find unit normal from bottom line
- dxbot = xbot[1] - xbot[0]
- dybot = ybot[1] - ybot[0]
- norm = 1.0 / math.sqrt(dxbot * dxbot + dybot * dybot)
- nx = -dybot * norm
- ny = dxbot * norm
- # vector connecting midpoints
- dx = 0.5 * (xtop[1] + xtop[0] - xbot[1] - xbot[0])
- dy = 0.5 * (ytop[1] + ytop[0] - ybot[1] - ybot[0])
- # the dot product is our result
- return dx * nx + dy * ny
+ if self.bit.gap > 0:
+ self.max_gap = self.bit.gap
+ else:
+ self.max_overlap = -self.bit.gap
+
+#No need such calculation anymore
+#def gap_overlap(xbot, ybot, xtop, ytop):
+# '''
+# Returns the distance between the midpoint of (xbot, ybot) to the
+# line (xtop, ytop). If positive, then a gap; otherwise, an overlap.
+# '''
def create_title(boards, bit, spacing):
'''
diff --git a/spacing.py b/spacing.py
index 08c7e8e..6e112a8 100644
--- a/spacing.py
+++ b/spacing.py
@@ -25,6 +25,7 @@
from __future__ import division
from future.utils import lrange
+from decimal import *
import math
import copy
from operator import attrgetter
@@ -32,9 +33,12 @@
import utils
def dump_cuts(cuts):
- '''Dumps the cuts to the screen...this is for debugging.'''
+ '''Dumps the cuts to the screen...this is for debugging
+ in column form.
+ '''
+ print('Min\tMax')
for c in cuts:
- print(c.xmin, c.xmax)
+ print( '{1:3f}\t{1:3f}'.format(c.xmin, c.xmax) )
class Spacing_Exception(Exception):
'''
@@ -84,6 +88,7 @@ class Base_Spacing(object):
labels = []
def __init__(self, bit, boards, config):
+ getcontext().prec = 6 # overkilled prec (actually 4 is enough)
self.description = 'NONE'
self.bit = bit
self.boards = boards
@@ -125,8 +130,8 @@ def __init__(self, bit, boards, config):
dh2 = 2 * self.dhtot
t = [Spacing_Param(0, self.boards[0].width // 4 + dh2, 0),\
- Spacing_Param(self.bit.width + dh2, self.boards[0].width // 2 + dh2,\
- self.bit.width + dh2),\
+ Spacing_Param(self.bit.midline + dh2, self.boards[0].width // 2 + dh2, \
+ self.bit.midline + dh2),\
Spacing_Param(None, None, True)]
self.params = {}
for i in lrange(len(t)):
@@ -136,50 +141,80 @@ def set_cuts(self):
'''
Sets the cuts to make the joint
'''
- spacing = self.params['Spacing'].v - 2 * self.dhtot
- width = self.params['Width'].v
+
+ # on local variables init
+ # we have to care about imperial values and convert them to increments before use
+ spacing = self.params['Spacing'].v
+ width = Decimal( math.floor(self.params['Width'].v) )
+
+ if not self.bit.units.metric and width < 1.:
+ spacing = self.bit.units.inches_to_increments(self.params['Spacing'].v)
+ width = Decimal( math.floor(self.bit.units.inches_to_increments(self.params['Width'].v ) ))
+
+ shift = Decimal(self.bit.midline % 2) / 2 # offset to keep cut center mm count
centered = self.params['Centered'].v
+ neck_width = width + spacing
+ overhang = self.bit.overhang
board_width = self.boards[0].width
units = self.bit.units
label = units.increments_to_string(spacing, True)
+
+ min_interior = utils.my_round(self.dhtot + self.bit.overhang)
+ # min_finger_width means most thin wood at the corner
+ min_finger_width = max(1, units.abstract_to_increments(self.config.min_finger_width))
+
+ if centered or \
+ self.bit.angle > 0: # always symm. for dovetail
+ # put a cut at the center of the board with half of inctrmrnt prec.
+ xMid = Decimal(board_width // 2) - shift + (width % 2) / 2
+ left = Decimal(max(0, xMid - width / 2))
+ else:
+ xMid = board_width # - width / 2
+ # keep corner finger and groove equality on the board edges
+ left = ( board_width % (width + neck_width) ) // 2
+ if (left - overhang ) < min_finger_width:
+ left = 0
+
+ # Note the Width slider measures "midline" but indicates the actual cut space
+ # show actual maximum cut with for dovetails
self.labels = self.keys[:]
self.labels[0] += ': ' + label
- self.labels[1] += ': ' + units.increments_to_string(width, True)
+ self.labels[1] += ': ' + units.increments_to_string(width + overhang * 2, True)
self.description = 'Equally spaced (' + self.labels[0] + \
', ' + self.labels[1] + ')'
self.cuts = [] # return value
- neck_width = width + spacing - 2 * utils.my_round(self.bit.offset)
- if neck_width < 1:
- raise Spacing_Exception('Specified bit paramters give a zero'
- ' or negative cut width (%d increments) at'
- ' the surface! Please change the'
- ' bit parameters width, depth, or angle.' % neck_width)
- # put a cut at the center of the board
- xMid = board_width // 2
- if centered or \
- self.bit.angle > 0: # always symm. for dovetail
- left = max(0, xMid - width // 2)
- else:
- left = max(0, (xMid // width) * width)
- right = min(board_width, left + width)
- self.cuts.append(router.Cut(left, right))
+
+ right = Decimal( min(board_width, left + width) )
+ self.cuts.append( router.Cut( left - overhang, right + overhang ) )
+
# do left side of board
- i = left - neck_width
- min_interior = utils.my_round(self.dhtot + self.bit.offset)
- min_finger_width = max(1, units.abstract_to_increments(self.config.min_finger_width))
- while i > 0:
- li = max(i - width, 0)
- if i - li > min_finger_width and i > min_interior:
- self.cuts.append(router.Cut(li, i))
- i = li - neck_width
+ i = left
+
+ while left > 0:
+ i -= neck_width
+ left = i - width
+
+ # prevent thin first cut
+ if left < min_finger_width:
+ left = 0
+ if (i - overhang ) > min_finger_width and (i - left - overhang * 2) > min_interior:
+ self.cuts.append(router.Cut(max(0,left - overhang), i + overhang) )
+ i = left
+
# do right side of board
- i = right + neck_width
- while i < board_width:
- ri = min(i + width, board_width)
- if ri - i > min_finger_width and board_width - i > min_interior:
- self.cuts.append(router.Cut(i, ri))
- i = ri + neck_width
+ i = right
+ while right < board_width:
+ i += neck_width
+ right = i + width
+ # prevent thin last cut
+ # devetail may cut off corner finger
+ if (board_width - right ) < min_finger_width:
+ right = board_width
+ if (board_width - i + overhang) > min_finger_width and (right - i - overhang * 2) > min_interior:
+ self.cuts.append(router.Cut(i - overhang, min(board_width, right + overhang) ) )
+ i = right
+
# If we have only one cut the entire width of the board, then
# the board width is too small for the bit
if self.cuts[0].xmin == 0 and self.cuts[0].xmax == board_width:
@@ -198,7 +233,7 @@ class Variable_Spaced(Base_Spacing):
'''
Computes variable-spaced cuts, where the center cut (always centered on
board) is the widest, with each cut decreasing linearly as you move to the edge.
-
+ arithmetical progression wirks just fine for such task
Parameters that control the spacing are:
Fingers: Roughly the number of full fingers on either the A or B board.
@@ -207,15 +242,13 @@ class Variable_Spaced(Base_Spacing):
def __init__(self, bit, boards, config):
Base_Spacing.__init__(self, bit, boards, config)
- # eff_width is the effective width, an average of the bit width
- # and the neck width
- self.eff_width = utils.my_round(self.bit.width - self.bit.offset) + 2 * self.dhtot
- self.eff_width += self.bit.dovetail_correction()
- self.wb = self.boards[0].width // self.eff_width
- self.alpha = (self.wb + 1) % 2
+
+
# min and max number of fingers
- self.mMin = max(3 - self.alpha, utils.my_round(math.ceil(math.sqrt(self.wb))))
- self.mMax = utils.my_round((self.wb - 1.0 + self.alpha) // 2)
+ self.mMin = 3 #we actually can set 2 fingers but tests does not pass this value
+ overhang = self.bit.overhang
+ self.mMax = int((self.boards[0].width // (self.bit.midline + self.dhtot ) ) // 2 + 1)
+
if self.mMax < self.mMin:
raise Spacing_Exception('Unable to compute a variable-spaced'\
' joint for the board and bit parameters'\
@@ -229,59 +262,97 @@ def __init__(self, bit, boards, config):
def set_cuts(self):
'''
Sets the cuts to make the joint
- '''
- board_width = self.boards[0].width
- m = self.params['Fingers'].v
- self.labels = [self.keys[0] + ':']
- self.description = 'Variable Spaced (' + self.keys[0] + ': {})'.format(m)
- # c is the ideal center-cut width
- c = self.eff_width * ((m - 1.0) * self.wb - \
- m * (m + 1.0) + self.alpha * m) /\
- (m * m - 2.0 * m - 1.0 + self.alpha)
- # d is the ideal decrease in finger width for each finger away from center finger
- d = (c - self.eff_width) / (m - 1.0)
+ S - progresion summary (half length of the board)
+ n - number of fingers (actually number of parts per half of the board)
+ d - the difference between terms of the arithmetic progression
+ a1 - first cut width (it must be symmetric at center of the board)
+ an - the last cut or finger
+ because a1 is at the board center we got:
+ S = (n * 2*a1+(n-1) * d / 2 - a1/2
+ from this equation we solve a1
+ a1 = ( (2 * S) - (n - 1) * n * d ) / (2 * n - 1)
+ the next task is to find the best possible d (I love big numbers)
+ '''
+ min_finger_width = Decimal( self.bit.units.abstract_to_increments(self.config.min_finger_width) + self.dhtot)
+ min_interior = self.bit.midline + self.dhtot * 2
+ S = math.floor(self.boards[0].width / 2) # half board width
+ shift = Decimal( (self.bit.midline ) % 2) / 2 # offset to keep cut senter
+
+ n = int(self.params['Fingers'].v) # number of cuts
+ d = -16 # d is the ideal decrease in finger width for each finger away from center finger
+ overhang = self.bit.overhang # offset from midline to the end of cut
+ a1 = 0 # center cut
+ an = 0 # last cut
+
+ # Iterate to get perfect d value
+ # it is also possible to reverse d sometime to get cutc wider to the board corner
+ # the corner cut can't be too thin
+ # the minimum of pre-last cut should be not less than bit midline and extra bords width
+ while (an < min_interior or (an + d) <= min_finger_width) and d <= 0:
+ d += 1
+ a1 = utils.math_round( ( (2 * S) - (n - 1) * n * d ) / Decimal(2 * n - 1) )
+ an = a1 + Decimal(n - 2) * d
+
+ if d > 0 :
+ d = 0
+ a1 = min_interior
+ an = min_interior
+ delta = 0
+ else:
+ SP = (a1 + d + an) * (n - 1) + a1
+ delta = self.boards[0].width - SP
+
+
# compute fingers on one side of the center and the center and store them
# in increments. Keep a running total of sizes.
- increments = [0] * (m + 1)
- ivals = 0
- for i in lrange(1, m + 1):
- increments[i] = max(self.bit.width, int(c - d * i))
- ivals += 2 * increments[i]
- # Set the center increment. This takes up the slop in the rounding and increment
- # resolution.
- increments[0] = board_width - ivals
- if increments[0] < increments[1]:
- # The center increment is narrower than the adjacent increment,
- # so reset it to the adjacent increment and get rid of a finger.
- increments[0] = increments[1]
- m -= 1
+ increments = [Decimal(0)] * int(n)
+ for i in lrange(0, n):
+ increments[i] = a1 + d * i
+ if increments[i] < min_interior:
+ increments[i] = min_interior
+
+ # wide last cut
+ if delta >= 2:
+ increments[-1] += delta // 2
+ delta -= (delta // 2) * 2
+
+ # wide center cut in case the delta is a 1 increment
+ if delta == 1:
+ increments[0] += delta
+ delta = 0
+
+ if increments[-1] > increments[-2] :
+ increments[-1] = increments[-2]
+
if self.config.debug:
print('v-s increments', increments)
- # Adjustments for dovetails
- deltaP = self.bit.width + 2 * self.dhtot - self.eff_width
- deltaM = utils.my_round(self.eff_width - self.bit.neck - 2 * self.dhtot) - \
- self.bit.dovetail_correction()
+
# put a cut at the center of the board
- xMid = board_width // 2
- width = increments[0] + deltaP
- left = max(0, xMid - width // 2)
- right = min(board_width, left + width)
- self.cuts = [router.Cut(left, right)]
- # do the remaining cuts
+ xMid = S + shift - Decimal(increments[0] % 2) / 2
+ neck = Decimal(increments[0]) / 2
+ left = xMid - neck
+ right = xMid + neck
+ self.labels = [self.keys[0] + ':']
+ self.description = 'Variable Spaced (' + self.keys[0] + ': {})\nML:{} SYM:{} SP[0]:{} PD: {}'.format(n, self.bit.midline, xMid, increments[0], self.bit.depth_0)
+
+ self.cuts = [router.Cut(left - overhang, right + overhang)]
+
do_cut = False
- for i in lrange(1, m + 1):
- if do_cut:
- width = increments[i] + deltaP
- farLeft = max(0, left - width)
- self.cuts.append(router.Cut(farLeft, left))
- farRight = min(board_width, right + width)
- self.cuts.append(router.Cut(right, farRight))
- else:
- width = increments[i] - deltaM
- farLeft = max(0, left - width)
- farRight = min(board_width, right + width)
- left = farLeft
- right = farRight
+ for i in lrange(1, n):
+ if do_cut :
+ # cut width
+ l_left = left - increments[i] - overhang
+ r_right = right + increments[i] + overhang
+ #prevent thin cuts
+ if l_left < min_finger_width:
+ l_left = 0
+ if (self.boards[0].width - r_right) < min_finger_width:
+ r_right = self.boards[0].width
+ self.cuts.append(router.Cut(max(0, l_left) , left + overhang))
+ self.cuts.append(router.Cut(right - overhang, r_right))
+
+ left -= increments[i]
+ right +=increments[i]
do_cut = (not do_cut)
# sort the cuts in increasing x
self.cuts = sorted(self.cuts, key=attrgetter('xmin'))
@@ -323,11 +394,12 @@ def get_limits(self, f):
'''
xmin = 0
xmax = self.boards[0].width
- neck_width = utils.my_round(self.bit.neck)
+ midline = utils.my_round(self.bit.midline)
+ overhang = self.bit.overhang
if f > 0:
- xmin = self.cuts[f - 1].xmax + neck_width
+ xmin = self.cuts[f - 1].xmax + midline - overhang * 2
if f < len(self.cuts) - 1:
- xmax = self.cuts[f + 1].xmin - neck_width
+ xmax = self.cuts[f + 1].xmin - midline + overhang * 2
return (xmin, xmax)
def check_limits(self, f):
@@ -347,23 +419,27 @@ def undo(self):
def cut_move_left(self):
'''
Moves the active cuts 1 increment to the left
+ with min finger with respect
'''
cuts_save = copy.deepcopy(self.cuts)
op = []
noop = []
+ min_finger_width = self.bit.units.abstract_to_increments(self.config.min_finger_width)
delete_cut = False
for f in self.active_cuts:
c = self.cuts[f]
- c.xmin = max(0, c.xmin - 1)
- if c.xmax == 1:
+ c.xmin -= 1
+ if c.xmin <= min_finger_width:
+ c.xmin = 0
+ if (c.xmax - self.bit.overhang) <= min_finger_width:
# note its possible for only one cut to be deleted
delete_cut = True
elif c.xmax == self.boards[0].width:
# if on the right end, create a new finger if it gets too wide
- wNew = self.bit.width + 2 * self.dhtot
+ wNew = self.bit.midline + (self.dhtot + self.bit.overhang) * 2
w = c.xmax - c.xmin
- if w > wNew:
- c.xmax -= 1
+ if (w - wNew) >= min_finger_width:
+ c.xmax = c.xmin + (self.dhtot * 2 + self.bit.width_f)
else:
c.xmax -= 1
msg = ''
@@ -390,23 +466,28 @@ def cut_move_left(self):
def cut_move_right(self):
'''
Moves the active cuts 1 increment to the right
+ with min finger with respect
'''
cuts_save = copy.deepcopy(self.cuts)
op = []
noop = []
delete_cut = False
+ min_finger_width = self.bit.units.abstract_to_increments(self.config.min_finger_width)
+
for f in self.active_cuts:
c = self.cuts[f]
- c.xmax = min(self.boards[0].width, c.xmax + 1)
- if c.xmin == self.boards[0].width - 1:
+ c.xmax += 1
+ if self.boards[0].width - c.xmax < min_finger_width:
+ c.xmax = self.boards[0].width
+ if (c.xmin + self.bit.overhang + min_finger_width) >= self.boards[0].width:
# note its possible for only one cut to be deleted
delete_cut = True
elif c.xmin == 0:
# if on the left end, create a new finger if it gets too wide
- wNew = self.bit.width + 2 * self.dhtot
+ wNew = self.bit.midline + (self.dhtot + self.bit.overhang) * 2
w = c.xmax - c.xmin
- if w > wNew:
- c.xmin = 1
+ if (w - wNew) >= min_finger_width:
+ c.xmin = c.xmax - (self.dhtot * 2 + self.bit.width_f)
else:
c.xmin += 1
msg = ''
@@ -434,6 +515,7 @@ def cut_widen_left(self):
'''
Increases the active cuts width on the left side by 1 increment
'''
+ min_finger_width = self.bit.units.abstract_to_increments(self.config.min_finger_width)
cuts_save = copy.deepcopy(self.cuts)
op = []
noop = []
@@ -442,6 +524,8 @@ def cut_widen_left(self):
(xmin, xmax) = self.get_limits(f)
if c.xmin > xmin:
c.xmin -= 1
+ if c.xmin < min_finger_width:
+ c.xmin = 0
self.cuts[f] = c
op.append(f)
else:
@@ -462,6 +546,7 @@ def cut_widen_right(self):
'''
Increases the active cuts width on the right side by 1 increment
'''
+ min_finger_width = self.bit.units.abstract_to_increments(self.config.min_finger_width)
cuts_save = copy.deepcopy(self.cuts)
op = []
noop = []
@@ -470,6 +555,8 @@ def cut_widen_right(self):
(xmin, xmax) = self.get_limits(f)
if c.xmax < xmax:
c.xmax += 1
+ if self.boards[0].width - c.xmax < min_finger_width:
+ c.xmax = self.boards[0].width
self.cuts[f] = c
op.append(f)
else:
@@ -493,15 +580,21 @@ def cut_trim_left(self):
cuts_save = copy.deepcopy(self.cuts)
op = []
noop = []
+ min_finger_width = self.bit.units.abstract_to_increments(self.config.min_finger_width)
+
for f in self.active_cuts:
c = self.cuts[f]
- wmin = self.bit.width + 2 * self.dhtot
- if c.xmax == self.boards[0].width:
- wmin = 1
- if c.xmax - c.xmin <= wmin:
- noop.append(f)
+ wmin = self.bit.width_f + 2 * self.dhtot
+ if c.xmin == 0:
+ c.xmin = max(0, c.xmax - self.bit.width_f - 2 * self.dhtot)
+ if c.xmin < min_finger_width:
+ c.xmin = 0
else:
c.xmin += 1
+
+ if c.xmax < self.boards[0].width and c.xmin > 0 and (c.xmax - c.xmin) < wmin:
+ noop.append(f)
+ else:
self.cuts[f] = c
op.append(f)
if len(noop) > 0:
@@ -523,15 +616,20 @@ def cut_trim_right(self):
cuts_save = copy.deepcopy(self.cuts)
op = []
noop = []
+ min_finger_width = self.bit.units.abstract_to_increments(self.config.min_finger_width)
+
for f in self.active_cuts:
c = self.cuts[f]
- wmin = self.bit.width + 2 * self.dhtot
- if c.xmin == 0:
- wmin = 1
- if c.xmax - c.xmin <= wmin:
- noop.append(f)
+ wmin = self.bit.width_f + 2 * self.dhtot
+ if c.xmax == self.boards[0].width:
+ c.xmax = min(self.boards[0].width, c.xmin + self.bit.width_f + 2 * self.dhtot)
+ if self.boards[0].width - c.xmax < min_finger_width:
+ c.xmax = self.boards[0].width
else:
c.xmax -= 1
+ if c.xmax < self.boards[0].width and c.xmin > 0 and (c.xmax - c.xmin) < wmin:
+ noop.append(f)
+ else :
self.cuts[f] = c
op.append(f)
if len(noop) > 0:
@@ -632,40 +730,49 @@ def cut_add(self):
Adds a cut to the first location possible, searching from the left.
The active cut is set the the new cut.
'''
- neck_width = utils.my_round(self.bit.neck)
+ overhang = self.bit.overhang
+ midline = self.bit.midline
index = None
cuts_save = copy.deepcopy(self.cuts)
- if self.cuts[0].xmin > self.bit.neck:
+ min_finger_width = math.floor(self.bit.units.abstract_to_increments(self.config.min_finger_width)) + 1
+ wadd = min_finger_width + self.dhtot
+ if self.cuts[0].xmin > self.bit.midline - overhang + wadd:
if self.config.debug:
print('add at left')
index = 0
xmin = 0
- xmax = self.cuts[0].xmin - neck_width
- wadd = 2 * self.bit.width + neck_width
- wdelta = self.bit.width - neck_width
+ xmax = xmin + wadd + overhang
+
+ wadd = 2 * (self.bit.midline + self.dhtot)
+ wdelta = overhang * 2
+
for i in lrange(1, len(self.cuts)):
- if self.cuts[i].xmin - self.cuts[i - 1].xmax + wdelta >= wadd:
+ if self.cuts[i].xmin - self.cuts[i - 1].xmax + wdelta >= wadd + self.bit.midline:
if self.config.debug:
print('add in cut')
index = i
- xmin = self.cuts[i - 1].xmax + neck_width
- xmax = xmin + self.bit.width
+ xmin = self.cuts[i - 1].xmax - overhang + midline
+ xmax = xmin + self.bit.midline + overhang + 2 * self.dhtot
+ xmin -= overhang
break
- elif self.cuts[i].xmax - self.cuts[i].xmin >= wadd:
+
+ elif (self.cuts[i].xmax - self.cuts[i].xmin - wdelta) >= wadd + self.bit.midline:
if self.config.debug:
print('add in cut')
index = i + 1
- xmin = self.cuts[i].xmax - self.bit.width
- xmax = self.cuts[i].xmax
- self.cuts[i].xmax = self.cuts[i].xmin + self.bit.width
+ xmax = self.cuts[i].xmin + self.bit.midline + (overhang + self.dhtot) * 2
+ xmin = xmax + self.bit.midline - 2 * overhang
+ t = self.cuts[i].xmax
+ self.cuts[i].xmax = xmax
+ xmax = t
break
if index is None and \
- self.cuts[-1].xmax < self.boards[0].width - self.bit.neck:
+ self.cuts[-1].xmax < self.boards[0].width - overhang:
if self.config.debug:
print('add at right')
index = len(self.cuts)
xmax = self.boards[0].width
- xmin = self.cuts[-1].xmax + neck_width
+ xmin = self.cuts[-1].xmax - overhang
if index is None:
return ('Unable to add cut', True)
self.undo_cuts.append(cuts_save)
diff --git a/utils.py b/utils.py
index b0a3a3c..90b702f 100644
--- a/utils.py
+++ b/utils.py
@@ -23,7 +23,7 @@
'''
from __future__ import division
from __future__ import print_function
-
+from decimal import *
import math, fractions, os, glob, platform
VERSION = '0.9.3'
@@ -34,6 +34,12 @@ def my_round(f):
'''
return int(round(f))
+def math_round(no):
+ '''
+ Return mathimatical round to integer
+ '''
+ return int(no//1 + ((no%1)/Decimal('0.5'))//1)
+
def isMac():
return (platform.system() == 'Darwin')
@@ -54,7 +60,7 @@ def __init__(self, english_separator, whole=0, numerator=0, denominator=None):
else:
self.sign = 1
self.whole = abs(whole)
- self.numerator = abs(numerator)
+ self.numerator = int(abs(numerator))
self.denominator = denominator
self.english_separator = english_separator
def reduce(self):
@@ -64,10 +70,13 @@ def reduce(self):
'''
if self.denominator is None or self.numerator == 0:
return
+ # directly convert to integer because of direct access posibility
+ self.numerator = int(self.numerator)
+ self.denominator = int(self.denominator)
dwhole = self.numerator // self.denominator
self.whole += dwhole
- self.numerator -= dwhole * self.denominator
- gcd = fractions.gcd(self.numerator, self.denominator)
+ self.numerator -= dwhole * self.denominator
+ gcd = math.gcd(self.numerator, self.denominator) #Python3 requres math.gcd
self.numerator /= gcd
self.denominator /= gcd
def to_string(self):
@@ -175,9 +184,10 @@ def increments_to_string(self, increments, with_units=False):
'''
A string representation of the value increments, converted to
its respective units.
+ metric conversion requires fixed point rounding
'''
if self.metric:
- r = '%g' % (increments / float(self.num_increments))
+ r = '%g' % ( Decimal(increments) / Decimal(self.num_increments) )
else:
allow_denoms = [1, 2, 4, 8, 16, 32, 64]
if isinstance(increments, float):
Version: %s
' % utils.VERSION
box.setText(s + self.doc.short_desc() + self.doc.license())
@@ -1495,15 +1538,16 @@ def _on_exit(self):
if self.config.debug:
print('_on_exit')
if self.file_saved:
- QtGui.qApp.quit()
+ #QtGui.qApp.quit()
+ QtWidgets.qApp.quit()
else:
msg = 'Figure was changed but not saved. Are you sure you want to quit?'
- reply = QtGui.QMessageBox.question(self, 'Message', msg,
- QtGui.QMessageBox.Yes,
- QtGui.QMessageBox.No)
+ reply = QtWidgets.QMessageBox.question(self, 'Message', msg,
+ QtWidgets.QMessageBox.Yes,
+ QtWidgets.QMessageBox.No)
- if reply == QtGui.QMessageBox.Yes:
- QtGui.qApp.quit()
+ if reply == QtWidgets.QMessageBox.Yes:
+ QtWidgets.qApp.quit()
@QtCore.pyqtSlot()
def _on_doclink(self):
@@ -1943,7 +1987,7 @@ def run():
# QtGui.QApplication.setStyle('motif')
# QtGui.QApplication.setStyle('cde')
- app = QtGui.QApplication(sys.argv)
+ app = QtWidgets.QApplication(sys.argv)
driver = Driver()
driver.show()
driver.center()
diff --git a/qt_fig.py b/qt_fig.py
index 18c262f..008d6ff 100644
--- a/qt_fig.py
+++ b/qt_fig.py
@@ -30,7 +30,7 @@
import router
import utils
-from PyQt4 import QtCore, QtGui
+from PyQt5 import QtCore, QtGui, QtWidgets
def paint_text(painter, text, coord, flags, shift=(0, 0), angle=0, fill_color=None):
'''
@@ -82,14 +82,14 @@ def paint_text(painter, text, coord, flags, shift=(0, 0), angle=0, fill_color=No
painter.setTransform(transform)
return rect
-class Qt_Fig(QtGui.QWidget):
+class Qt_Fig(QtWidgets.QWidget):
'''
Interface to the qt_driver, using Qt to draw the boards and template.
The attribute "canvas" is self, to mimic the old interface to matplotlib,
which this class replaced.
'''
def __init__(self, template, boards, config):
- QtGui.QWidget.__init__(self)
+ QtWidgets.QWidget.__init__(self)
self.canvas = self
self.config = config
self.fig_width = -1
@@ -233,10 +233,10 @@ def print(self, template, boards, bit, spacing, woods, description):
self.config)
# Print through the preview dialog
- printer = QtGui.QPrinter(QtGui.QPrinter.HighResolution)
- printer.setOrientation(QtGui.QPrinter.Landscape)
- printer.setPageMargins(0, 0, 0, 0, QtGui.QPrinter.Inch)
- pdialog = QtGui.QPrintPreviewDialog(printer)
+ printer = QtPrintSupport.QPrinter(QtPrintSupport.QPrinter.HighResolution)
+ printer.setOrientation(QtPrintSupport.QPrinter.Landscape)
+ printer.setPageMargins(0, 0, 0, 0, QtPrintSupport.QPrinter.Inch)
+ pdialog = QtPrintSupport.QPrintPreviewDialog(printer)
pdialog.setModal(True)
pdialog.paintRequested.connect(self.preview_requested)
return pdialog.exec_()
@@ -358,10 +358,12 @@ def paint_all(self, painter, dpi=None):
painter.translate(x, y)
self.transform = painter.transform()
+
# draw the objects
self.draw_boards(painter)
self.draw_template(painter)
self.draw_title(painter)
+ self.draw_finger_sizes(painter)
if self.config.show_finger_widths:
self.draw_finger_sizes(painter)
@@ -474,9 +476,15 @@ def draw_alignment(self, painter):
# draw the alignment lines on both templates
x = board_T.xR() + self.geom.bit.width // 2
+
pen = QtGui.QPen(QtCore.Qt.SolidLine)
pen.setColor(self.colors['template_margin_foreground'])
- painter.setPen(pen)
+ pen.setWidthF(0)
+
+ bg_pen=QtGui.QPen(QtCore.Qt.SolidLine)
+ bg_pen.setColor(QtGui.QColor('White'))
+ bg_pen.setWidthF(0)
+
self.set_font_size(painter, 'template')
label = 'ALIGN'
flags = QtCore.Qt.AlignTop | QtCore.Qt.AlignHCenter
@@ -484,8 +492,12 @@ def draw_alignment(self, painter):
if b is not None:
y1 = b.yB()
y2 = b.yT()
+ painter.setPen(pen)
painter.drawLine(x, y1, x, y2)
paint_text(painter, label, (x, (y1 + y2) // 2), flags, (0, 0), -90)
+ painter.setPen(bg_pen)
+ painter.drawLine(QtCore.QPointF(x-0.5, y1+0.5),QtCore.QPointF( x-0.5, y2-0.5) )
+ painter.drawLine(QtCore.QPointF(x+0.5, y1+0.5),QtCore.QPointF(x+0.5, y2-0.5) )
def draw_template_rectangle(self, painter, r, b):
'''
@@ -527,12 +539,17 @@ def draw_template(self, painter):
pen_canvas = QtGui.QPen(QtCore.Qt.SolidLine)
pen_canvas.setColor(self.colors['canvas_foreground'])
+ pen_canvas.setWidthF(0)
penA = QtGui.QPen(QtCore.Qt.SolidLine)
penA.setColor(self.colors['pass_color'])
+ penA.setWidthF(0)
penB = QtGui.QPen(QtCore.Qt.DashLine)
penB.setColor(self.colors['pass_alt_color'])
+ penB.setWidthF(0)
+ painter.setPen(pen_canvas)
self.draw_template_rectangle(painter, rect_T, board_T)
+
if boards[3].active:
rect_TDD = self.geom.rect_TDD
board_TDD = self.geom.board_TDD
@@ -670,6 +687,7 @@ def draw_template(self, painter):
else:
pen = QtGui.QPen(QtCore.Qt.DashLine)
pen.setColor(self.colors['center_color'])
+ pen.setWidthF(0)
painter.setPen(pen)
painter.drawLine(xMid, rect_caul.yB(), xMid, rect_caul.yT())
painter.setPen(self.colors['template_margin_foreground'])
@@ -679,6 +697,7 @@ def draw_template(self, painter):
# Label the templates
pen = QtGui.QPen(QtCore.Qt.DashLine)
pen.setColor(self.colors['center_color'])
+ pen.setWidthF(0)
self.set_font_size(painter, 'template_labels')
if len(centerline) > 0:
label_bottom += '\nCenter: ' + centerline[0]
@@ -735,7 +754,9 @@ def draw_one_board(self, painter, board, bit, fill_color):
color = self.colors['board_foreground']
brush = QtGui.QBrush(color, icon)
(inverted, invertable) = self.transform.inverted()
- brush.setMatrix(inverted.toAffine())
+ #setMatrix is not offered anymore
+ #brush.setMatrix(inverted.toAffine())
+ brush.setTransform(inverted)
painter.setBrush(brush)
painter.drawPolygon(poly)
painter.restore()
@@ -755,6 +776,7 @@ def draw_boards(self, painter):
self.set_font_size(painter, 'boards')
pen = QtGui.QPen(QtCore.Qt.SolidLine)
pen.setColor(self.colors['canvas_foreground'])
+ pen.setWidthF(0)
painter.setPen(pen)
x1 = self.geom.boards[0].xL() - self.geom.bit.width // 2
@@ -822,17 +844,17 @@ def draw_active_cuts(self, painter):
If the spacing supports it, highlight the active cuts and
draw their limits
'''
- # draw the perimeter of the cursor cut
+
f = self.geom.spacing.cursor_cut
if f is None:
return
- poly = self.cut_polygon(self.geom.boards[0].bottom_cuts[f])
- painter.save()
- pen = QtGui.QPen(QtCore.Qt.blue)
- pen.setWidth(1)
- painter.setPen(pen)
- painter.drawPolyline(poly)
- painter.restore()
+
+ pen = QtGui.QPen()
+ pen.setWidthF(0)
+
+ # get the perimeter of the cursor
+ cursor_poly = self.cut_polygon(self.geom.boards[0].bottom_cuts[f])
+
# initialize limits
xminG = self.geom.boards[0].width
@@ -842,7 +864,7 @@ def draw_active_cuts(self, painter):
painter.save()
brush = QtGui.QBrush(QtGui.QColor(255, 0, 0, 75))
painter.setBrush(brush)
- pen = QtGui.QPen(QtCore.Qt.red)
+ pen.setColor(QtCore.Qt.red)
painter.setPen(pen)
fcolor = QtGui.QColor(self.colors['board_background'])
fcolor.setAlphaF(1.0)
@@ -863,16 +885,26 @@ def draw_active_cuts(self, painter):
painter.restore()
# draw the limits
+ # using QPointF to avoid border marks overlap with cursor
painter.save()
xminG += self.geom.boards[0].xL()
xmaxG += self.geom.boards[0].xL()
yB = self.geom.boards[0].yB()
yT = self.geom.boards[0].yT()
- painter.setPen(QtCore.Qt.green)
- painter.drawLine(xminG, yB, xminG, yT)
- painter.drawLine(xmaxG, yB, xmaxG, yT)
+ pen.setColor(QtCore.Qt.green)
+ painter.setPen(pen)
+ painter.drawLine(QtCore.QPointF(xminG - 0.5, yB), QtCore.QPointF(xminG - 0.5, yT) )
+ painter.drawLine(QtCore.QPointF(xmaxG + 0.5, yB), QtCore.QPointF(xmaxG + 0.5, yT) )
painter.restore()
+ # draw the perimeter of the cursor cut at the end to get it always visible
+ painter.save()
+ pen.setColor(QtCore.Qt.blue)
+ painter.setPen(pen)
+ painter.drawPolyline(cursor_poly)
+ painter.restore()
+
+
def draw_title(self, painter):
'''
Draws the title
diff --git a/qt_test.py b/qt_test.py
index c898b28..8a894a0 100644
--- a/qt_test.py
+++ b/qt_test.py
@@ -28,11 +28,12 @@
import unittest
from qt_driver import Driver
import utils
-from PyQt4 import QtGui
-from PyQt4 import QtCore
-from PyQt4.QtTest import QTest
+from PyQt5 import QtGui
+from PyQt5 import QtCore
+from PyQt5 import QtWidgets
+from PyQt5.QtTest import QTest
-app = QtGui.QApplication(sys.argv)
+app = QtWidgets.QApplication(sys.argv)
class Case(object):
def __init__(self, angle, width, depth, spacing, board_width=7):
diff --git a/qt_utils.py b/qt_utils.py
index 4ea258b..717465c 100644
--- a/qt_utils.py
+++ b/qt_utils.py
@@ -29,7 +29,7 @@
import utils
import router
-from PyQt4 import QtCore, QtGui
+from PyQt5 import QtCore, QtGui, QtWidgets
def set_router_value(line_edit, obj, attr, setter, is_float=False, bit=None):
# With editingFinished, we also need to check whether the
@@ -61,13 +61,13 @@ def set_router_value(line_edit, obj, attr, setter, is_float=False, bit=None):
except router.Router_Exception as e:
# Notify the user of the error, and set the line editor back to its
# original value
- QtGui.QMessageBox.warning(line_edit.parentWidget(), 'Error', e.msg)
+ QtWidgets.QMessageBox.warning(line_edit.parentWidget(), 'Error', e.msg)
old_value = getattr(obj, attr)
text = units.increments_to_string(old_value)
line_edit.setText(text)
return None
-class PreviewComboBox(QtGui.QComboBox):
+class PreviewComboBox(QtWidgets.QComboBox):
'''
This comboxbox emits "activated" when hidePopup is called. This allows
for a combobox with a preview mode, so that as each selection is
@@ -77,20 +77,20 @@ class PreviewComboBox(QtGui.QComboBox):
'''
def __init__(self, parent):
- QtGui.QComboBox.__init__(self, parent)
+ QtWidgets.QComboBox.__init__(self, parent)
def hidePopup(self):
- QtGui.QComboBox.hidePopup(self)
+ QtWidgets.QComboBox.hidePopup(self)
#print('hidePopup')
self.activated.emit(self.currentIndex())
-class ShadowTextLineEdit(QtGui.QLineEdit):
+class ShadowTextLineEdit(QtWidgets.QLineEdit):
'''
This line edit sets a grayed shadow text, until focus is received and text
is entered. Shadow text is the text displayed when the line edit is empty.
'''
def __init__(self, parent, shadow_text):
- QtGui.QLineEdit.__init__(self, parent)
+ QtWidgets.QLineEdit.__init__(self, parent)
self.shadow_text = shadow_text
self.initialize_shadow()
@@ -100,14 +100,14 @@ def initialize_shadow(self):
self.has_real_text = False
def focusInEvent(self, event):
- QtGui.QLineEdit.focusInEvent(self, event)
+ QtWidgets.QLineEdit.focusInEvent(self, event)
# If no real text, clear the shadow text and darken the text
if not self.has_real_text:
self.clear()
self.setStyleSheet('color: black;')
def focusOutEvent(self, event):
- QtGui.QLineEdit.focusOutEvent(self, event)
+ QtWidgets.QLineEdit.focusOutEvent(self, event)
# If there's no text, set it back to the shadow
if len(str(self.text())) == 0:
self.initialize_shadow()
@@ -116,22 +116,22 @@ def focusOutEvent(self, event):
def set_line_style(line):
'''Sets the style for create_vline() and create_hline()'''
- line.setFrameShadow(QtGui.QFrame.Raised)
+ line.setFrameShadow(QtWidgets.QFrame.Raised)
line.setLineWidth(1)
line.setMidLineWidth(1)
- line.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
+ line.setSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
def create_vline():
'''Creates a vertical line'''
- vline = QtGui.QFrame()
- vline.setFrameStyle(QtGui.QFrame.VLine)
+ vline = QtWidgets.QFrame()
+ vline.setFrameStyle(QtWidgets.QFrame.VLine)
set_line_style(vline)
return vline
def create_hline():
'''Creates a horizontal line'''
- hline = QtGui.QFrame()
- hline.setFrameStyle(QtGui.QFrame.HLine)
+ hline = QtWidgets.QFrame()
+ hline.setFrameStyle(QtWidgets.QFrame.HLine)
set_line_style(hline)
return hline
diff --git a/serialize.py b/serialize.py
index 0ce6bf3..4b374f6 100644
--- a/serialize.py
+++ b/serialize.py
@@ -24,10 +24,10 @@
from __future__ import print_function
from future.utils import lrange
try:
- from StringIO import StringIO
+ from StringIO import StringIO, BytesIO
except ImportError:
- from io import StringIO
-
+ from io import StringIO, BytesIO
+import binascii
import pickle
import router
import utils
@@ -38,7 +38,9 @@ def serialize(bit, boards, sp, config):
Serializes the arguments. Returns the serialized string, which can
later be used to reconstruct the arguments using unserialize()
'''
- out = StringIO()
+ #out = StringIO()
+ out = BytesIO()
+
p = pickle.Pickler(out)
# Save code version
p.dump(utils.VERSION)
@@ -72,13 +74,23 @@ def serialize(bit, boards, sp, config):
out.close()
if config.debug:
print('size of pickle', len(s))
- return s
+ ret = binascii.b2a_qp(s).decode('utf-8')
+ #binascii.b2a_base64(s).decode("utf-8", "strict")
+ return ret
-def unserialize(s, config):
+def unserialize(s, config, newFormat=False):
'''
Unserializes the string s, and returns the tuple (bit, boards, spacing)
'''
- inp = StringIO(s)
+ inp = '';
+ # new format uue encoding support
+ if newFormat:
+ s = binascii.a2b_qp(s)
+ else:
+ s = s.encode()
+
+ inp = BytesIO(s)
+
u = pickle.Unpickler(inp)
version = u.load()
if config.debug:
From bd9be0e881810109d64924b347509430d82e257b Mon Sep 17 00:00:00 2001
From: Valdas2000 <35214527+Valdas2000@users.noreply.github.com>
Date: Fri, 12 Jan 2018 11:27:22 +0300
Subject: [PATCH 2/3] [~] Fix Printer issue.
The import got not contain QtPrintSupport
Now print function works with no failures
---
qt_fig.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/qt_fig.py b/qt_fig.py
index 008d6ff..3d5ea1f 100644
--- a/qt_fig.py
+++ b/qt_fig.py
@@ -30,7 +30,7 @@
import router
import utils
-from PyQt5 import QtCore, QtGui, QtWidgets
+from PyQt5 import QtCore, QtGui, QtWidgets, QtPrintSupport
def paint_text(painter, text, coord, flags, shift=(0, 0), angle=0, fill_color=None):
'''
From d36409a58f22e7402c26734887988adbc0e69f55 Mon Sep 17 00:00:00 2001
From: Valdas <35214527+Valdas2000@users.noreply.github.com>
Date: Thu, 8 Feb 2018 16:24:48 +0300
Subject: [PATCH 3/3] [+] Imperial bits in Metric scales
Changes:
1 - Midline used everywhere for calculations
2 - Allow even and odd bits for dovetail joins
3 - min_finger_width respected across all functionality
4 - gap calculation optimized
5 - Midline depended on bit height ( so usually there are 2 optimal height values for a dovetail bit)
6 - qt_test got back (QT5 migration)
----------------
Fixes:
1 - Screenshot got back (QT5 fix)
2 - Mouse events for zooming got back (QT5 fix)
3 - Save configuration fixed (typo in initial version)
4 - Application Works fine on multi-screen (up to 4 screen configuration verified)
TODO:
1 - optimize router cuts ( cuts more that half bit diameter are not welcomed), edge cuts must be delicate
2 - show optimal bit height for dovetails (bit.height_0)
3 - update signs for templates stripes showing bit info and optimal height
4 - allow reverse in variable spacing (probably it's better to put a slider for fine tune d value)
5 - Disallow fractional diameters for straight bits in metric
Limitations:
1 - Metric users must care. Fractional values allowed only for dovetails. Fractional in dovetails causes error
2 - works well on 1mm precision (other values are not well tested)
3 - Alignment line fixed to 0 (it will be fine to adopt it to edge point)
---
qt_driver.py | 10 +-
qt_fig.py | 24 ++--
qt_test.py | 4 +-
router.py | 231 +++++++++++++++-----------------
spacing.py | 365 +++++++++++++++++++++++++++++++++------------------
utils.py | 20 ++-
6 files changed, 378 insertions(+), 276 deletions(-)
diff --git a/qt_driver.py b/qt_driver.py
index 576bba2..ac5a732 100644
--- a/qt_driver.py
+++ b/qt_driver.py
@@ -24,6 +24,7 @@
from __future__ import print_function
from future.utils import lrange
from builtins import str
+from decimal import *
from PIL import Image
from PIL import PngImagePlugin
try:
@@ -31,7 +32,7 @@
except ImportError:
from io import BytesIO
-import os, sys, traceback, webbrowser, copy, shutil
+import os, sys, traceback, webbrowser, copy, shutil, math
import qt_fig
import qt_config
@@ -1254,10 +1255,8 @@ def _on_save(self, do_screenshot=False):
if do_screenshot:
p_screen=QtWidgets.QApplication.primaryScreen()
- scr_rect = p_screen.geometry();
- image = QtGui.QScreen.grabWindow(p_screen, scr_rect.x(), scr_rect.y(), scr_rect.width(), scr_rect.height()).toImage()
- #, scr_rect.x, scr_rect.y, scr_rect.width, scr_rect.height)
- #.toImage()
+ image = p_screen.grabWindow(self.winId())
+
else:
image = self.fig.image(self.template, self.boards, self.bit, self.spacing,
self.woods, self.description)
@@ -1980,6 +1979,7 @@ def run():
'''
Sets up and runs the application
'''
+ getcontext().prec = 4
# QtGui.QApplication.setStyle('plastique')
# QtGui.QApplication.setStyle('windows')
# QtGui.QApplication.setStyle('windowsxp')
diff --git a/qt_fig.py b/qt_fig.py
index 3d5ea1f..bdc2471 100644
--- a/qt_fig.py
+++ b/qt_fig.py
@@ -23,6 +23,7 @@
'''
from __future__ import print_function
from __future__ import division
+from decimal import Decimal
from future.utils import lrange
import time
@@ -363,7 +364,7 @@ def paint_all(self, painter, dpi=None):
self.draw_boards(painter)
self.draw_template(painter)
self.draw_title(painter)
- self.draw_finger_sizes(painter)
+ #self.draw_finger_sizes(painter)
if self.config.show_finger_widths:
self.draw_finger_sizes(painter)
@@ -826,9 +827,9 @@ def cut_polygon(self, c):
xLB = xLT
xRB = xRT
if c.xmin > 0:
- xLB += self.geom.bit.offset
+ xLB += self.geom.bit.overhang
if c.xmax < self.geom.boards[0].width:
- xRB -= self.geom.bit.offset
+ xRB -= self.geom.bit.overhang
yB = boards[0].yB()
yT = yB + self.geom.bit.depth
poly = QtGui.QPolygonF()
@@ -893,8 +894,9 @@ def draw_active_cuts(self, painter):
yT = self.geom.boards[0].yT()
pen.setColor(QtCore.Qt.green)
painter.setPen(pen)
- painter.drawLine(QtCore.QPointF(xminG - 0.5, yB), QtCore.QPointF(xminG - 0.5, yT) )
- painter.drawLine(QtCore.QPointF(xmaxG + 0.5, yB), QtCore.QPointF(xmaxG + 0.5, yT) )
+ half = Decimal(0.5)
+ painter.drawLine(QtCore.QPointF(xminG - half, yB), QtCore.QPointF(xminG - half, yT) )
+ painter.drawLine(QtCore.QPointF(xmaxG + half, yB), QtCore.QPointF(xmaxG + half, yT) )
painter.restore()
# draw the perimeter of the cursor cut at the end to get it always visible
@@ -919,6 +921,7 @@ def draw_title(self, painter):
def draw_finger_sizes(self, painter):
'''
Annotates the finger sizes on each board
+ with 3 digit precision
'''
units = self.geom.bit.units
self.set_font_size(painter, 'template')
@@ -941,7 +944,7 @@ def draw_finger_sizes(self, painter):
for c in bcuts:
x = self.geom.boards[1].xL() + (c.xmin + c.xmax) // 2
y = self.geom.boards[1].yT()
- label = units.increments_to_string(c.xmax - c.xmin)
+ label = units.increments_to_string( round(c.xmax - c.xmin, 3) )
p = (x, y)
paint_text(painter, label, p, flags, shift, fill_color=fcolor)
# ... do the A cuts
@@ -950,7 +953,7 @@ def draw_finger_sizes(self, painter):
for c in acuts:
x = self.geom.boards[0].xL() + (c.xmin + c.xmax) // 2
y = self.geom.boards[0].yB()
- label = units.increments_to_string(c.xmax - c.xmin)
+ label = units.increments_to_string( round(c.xmax - c.xmin, 3) )
p = (x, y)
paint_text(painter, label, p, flags, shift, fill_color=fcolor)
@@ -1016,8 +1019,9 @@ def wheelEvent(self, event):
return
if self.config.debug:
- print('qt_fig.wheelEvent', event.delta())
- self.scaling *= 1 + 0.05 * event.delta() / 120
+ print('qt_fig.wheelEvent', event.angleDelta())
+ ppp = event.angleDelta() / 120
+ self.scaling *= 1 + 0.05 * ppp.y()
self.update()
def mousePressEvent(self, event):
@@ -1065,7 +1069,7 @@ def mouseMoveEvent(self, event):
event.ignore()
return
- pos = self.base_transform.map(event.posF())
+ pos = self.base_transform.map(event.localPos())
if self.config.debug:
print('mouse moved here: {} {}'.format(pos.x(), pos.y()))
diffx = (pos.x() - self.mouse_pos.x())
diff --git a/qt_test.py b/qt_test.py
index 8a894a0..6d9d8a0 100644
--- a/qt_test.py
+++ b/qt_test.py
@@ -67,7 +67,7 @@ def setUp(self):
self.d.show()
self.d.raise_()
self.debug = self.d.config.debug
- QTest.qWaitForWindowShown(self.d)
+ QTest.qWaitForWindowExposed(self.d)
if not utils.isMac():
self.d.working_dir = 'Z:\Windows\pyRouterJig\images'
def test_options(self):
@@ -78,7 +78,7 @@ def test_defaults(self):
self.assertEqual(str(self.d.le_bit_depth.text()), '3/4')
self.assertEqual(str(self.d.le_bit_angle.text()), '0')
def screenshot(self, do_screenshot=True):
- QTest.qWaitForWindowShown(self.d)
+ QTest.qWaitForWindowExposed(self.d)
QTest.qWait(100)
self.d._on_save(do_screenshot)
def test_screenshots(self):
diff --git a/router.py b/router.py
index 488eded..6ee6936 100644
--- a/router.py
+++ b/router.py
@@ -26,6 +26,7 @@
from future.utils import lrange
import math
+from decimal import *
import utils
class Router_Exception(Exception):
@@ -82,6 +83,14 @@ class Router_Bit(object):
neck: width of bit at board surface.
+ midline: disstance between cuts in incremental units
+
+ depth_0: optimal depth with perfect fit
+
+ width_f: perfect width with no rounding
+
+ gap: the calculated disstance to perfect fit value
+
halfwidth: half of width
'''
def __init__(self, units, width, depth, angle=0):
@@ -89,6 +98,13 @@ def __init__(self, units, width, depth, angle=0):
self.width = width
self.depth = depth
self.angle = angle
+
+ self.midline = Decimal('0')
+ self.depth_0 = Decimal('0')
+ self.width_f = Decimal(repr(width))
+ self.overhang = (self.width_f - self.midline) / 2
+ self.gap= Decimal('0')
+
self.reinit()
def set_width_from_string(self, s):
'''
@@ -101,19 +117,21 @@ def set_width_from_string(self, s):
msg = 'Unable to set Bit Width to: {}