2021-03-12 04:17:19 +00:00
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
2018-04-29 02:14:23 +00:00
# -*- coding: UTF-8 -*-
2020-05-16 21:01:00 +00:00
import os
import sys
2021-10-21 14:24:40 +00:00
from collections import defaultdict , namedtuple
2018-12-12 00:58:20 +00:00
from copy import copy
2021-10-21 14:24:40 +00:00
from itertools import groupby , zip_longest
2018-12-12 00:58:20 +00:00
2018-04-29 01:26:53 +00:00
import wx
from wx . lib . scrolledpanel import ScrolledPanel
2021-03-12 01:34:29 +00:00
from . . commands import is_command , is_command_symbol
2021-10-21 14:24:40 +00:00
from . . elements import ( AutoFill , Clone , EmbroideryElement , Polyline ,
2020-05-16 21:01:00 +00:00
SatinColumn , Stroke )
from . . elements . clone import is_clone
2021-04-10 17:55:48 +00:00
from . . gui import PresetsPanel , SimulatorPreview , WarningPanel
2018-12-16 01:21:41 +00:00
from . . i18n import _
2020-05-16 21:01:00 +00:00
from . . svg . tags import SVG_POLYLINE_TAG
2018-09-16 17:09:46 +00:00
from . . utils import get_resource_dir
2018-12-12 00:58:20 +00:00
from . base import InkstitchExtension
2018-04-29 01:26:53 +00:00
2021-10-21 14:24:40 +00:00
#ChoiceWidgets = namedtuple("ChoiceWidgets", "param widget last_initialized_choice")
def grouper ( iterable_obj , count , fillvalue = None ) :
args = [ iter ( iterable_obj ) ] * count
return zip_longest ( * args , fillvalue = fillvalue )
2018-04-29 01:26:53 +00:00
class ParamsTab ( ScrolledPanel ) :
def __init__ ( self , * args , * * kwargs ) :
self . params = kwargs . pop ( ' params ' , [ ] )
self . name = kwargs . pop ( ' name ' , None )
self . nodes = kwargs . pop ( ' nodes ' )
kwargs [ " style " ] = wx . TAB_TRAVERSAL
ScrolledPanel . __init__ ( self , * args , * * kwargs )
self . SetupScrolling ( )
self . changed_inputs = set ( )
self . dependent_tabs = [ ]
self . parent_tab = None
self . param_inputs = { }
2021-10-21 14:24:40 +00:00
self . choice_widgets = defaultdict ( list )
self . dict_of_choices = { }
2018-04-29 01:26:53 +00:00
self . paired_tab = None
self . disable_notify_pair = False
toggles = [ param for param in self . params if param . type == ' toggle ' ]
if toggles :
self . toggle = toggles [ 0 ]
self . params . remove ( self . toggle )
self . toggle_checkbox = wx . CheckBox ( self , label = self . toggle . description )
value = any ( self . toggle . values )
if self . toggle . inverse :
value = not value
self . toggle_checkbox . SetValue ( value )
self . toggle_checkbox . Bind ( wx . EVT_CHECKBOX , self . update_toggle_state )
self . toggle_checkbox . Bind ( wx . EVT_CHECKBOX , self . changed )
self . param_inputs [ self . toggle . name ] = self . toggle_checkbox
else :
self . toggle = None
2018-09-01 20:26:47 +00:00
self . param_change_indicators = { }
self . settings_grid = wx . FlexGridSizer ( rows = 0 , cols = 4 , hgap = 10 , vgap = 15 )
self . settings_grid . AddGrowableCol ( 1 , 2 )
2018-04-29 01:26:53 +00:00
self . settings_grid . SetFlexibleDirection ( wx . HORIZONTAL )
2018-09-09 04:06:47 +00:00
self . pencil_icon = wx . Image ( os . path . join ( get_resource_dir ( " icons " ) , " pencil_20x20.png " ) ) . ConvertToBitmap ( )
2018-09-01 20:26:47 +00:00
2018-04-29 01:26:53 +00:00
self . __set_properties ( )
self . __do_layout ( )
if self . toggle :
self . update_toggle_state ( )
# end wxGlade
def pair ( self , tab ) :
# print self.name, "paired with", tab.name
self . paired_tab = tab
self . update_description ( )
def add_dependent_tab ( self , tab ) :
self . dependent_tabs . append ( tab )
self . update_description ( )
def set_parent_tab ( self , tab ) :
self . parent_tab = tab
def is_dependent_tab ( self ) :
return self . parent_tab is not None
def enabled ( self ) :
if self . toggle_checkbox :
return self . toggle_checkbox . IsChecked ( )
else :
return True
def update_toggle_state ( self , event = None , notify_pair = True ) :
enable = self . enabled ( )
# print self.name, "update_toggle_state", enable
for child in self . settings_grid . GetChildren ( ) :
widget = child . GetWindow ( )
if widget :
child . GetWindow ( ) . Enable ( enable )
if notify_pair and self . paired_tab :
self . paired_tab . pair_changed ( enable )
for tab in self . dependent_tabs :
tab . dependent_enable ( enable )
if event :
event . Skip ( )
2021-10-21 14:24:40 +00:00
def update_choice_state ( self , event = None ) :
input = event . GetEventObject ( )
selection = input . GetSelection ( )
param = self . inputs_to_params [ input ]
self . update_choice_widgets ( ( param , selection ) )
self . settings_grid . Layout ( )
self . Layout ( )
if event :
event . Skip ( )
2018-04-29 01:26:53 +00:00
def pair_changed ( self , value ) :
# print self.name, "pair_changed", value
new_value = not value
if self . enabled ( ) != new_value :
self . set_toggle_state ( not value )
self . update_toggle_state ( notify_pair = False )
if self . on_change_hook :
self . on_change_hook ( self )
def dependent_enable ( self , enable ) :
if enable :
self . toggle_checkbox . Enable ( )
else :
self . set_toggle_state ( False )
self . toggle_checkbox . Disable ( )
self . update_toggle_state ( )
if self . on_change_hook :
self . on_change_hook ( self )
def set_toggle_state ( self , value ) :
if self . toggle_checkbox :
self . toggle_checkbox . SetValue ( value )
self . changed_inputs . add ( self . toggle_checkbox )
def get_values ( self ) :
values = { }
if self . toggle :
checked = self . enabled ( )
2019-06-19 19:09:56 +00:00
if self . toggle_checkbox in self . changed_inputs :
if self . toggle . inverse :
values [ self . toggle . name ] = not checked
else :
values [ self . toggle . name ] = checked
2018-04-29 01:26:53 +00:00
if not checked :
# Ignore params on this tab if the toggle is unchecked,
# because they're grayed out anyway.
return values
2021-03-04 17:40:53 +00:00
for name , input in self . param_inputs . items ( ) :
2018-04-29 01:26:53 +00:00
if input in self . changed_inputs and input != self . toggle_checkbox :
2021-03-14 08:38:36 +00:00
try :
values [ name ] = input . GetValue ( )
except AttributeError :
# dropdown
values [ name ] = input . GetSelection ( )
2018-04-29 01:26:53 +00:00
return values
def apply ( self ) :
values = self . get_values ( )
for node in self . nodes :
# print >> sys.stderr, "apply: ", self.name, node.id, values
2021-03-04 17:40:53 +00:00
for name , value in values . items ( ) :
2018-04-29 01:26:53 +00:00
node . set_param ( name , value )
def on_change ( self , callable ) :
self . on_change_hook = callable
def changed ( self , event ) :
2018-09-01 20:26:47 +00:00
input = event . GetEventObject ( )
self . changed_inputs . add ( input )
param = self . inputs_to_params [ input ]
self . enable_change_indicator ( param )
2018-04-29 01:26:53 +00:00
event . Skip ( )
if self . on_change_hook :
self . on_change_hook ( self )
def load_preset ( self , preset ) :
preset_data = preset . get ( self . name , { } )
2021-03-04 17:40:53 +00:00
for name , value in preset_data . items ( ) :
2018-04-29 01:26:53 +00:00
if name in self . param_inputs :
2021-03-22 16:06:48 +00:00
try :
self . param_inputs [ name ] . SetValue ( value )
except AttributeError :
self . param_inputs [ name ] . SetSelection ( int ( value ) )
2018-04-29 01:26:53 +00:00
self . changed_inputs . add ( self . param_inputs [ name ] )
self . update_toggle_state ( )
def save_preset ( self , storage ) :
2018-09-01 23:54:34 +00:00
storage [ self . name ] = self . get_values ( )
2018-04-29 01:26:53 +00:00
def update_description ( self ) :
if len ( self . nodes ) == 1 :
description = _ ( " These settings will be applied to 1 object. " )
else :
description = _ ( " These settings will be applied to %d objects. " ) % len ( self . nodes )
if any ( len ( param . values ) > 1 for param in self . params ) :
2021-03-04 17:40:53 +00:00
description + = " \n • " + _ ( " Some settings had different values across objects. Select a value from the dropdown or enter a new one. " )
2018-04-29 01:26:53 +00:00
if self . dependent_tabs :
if len ( self . dependent_tabs ) == 1 :
2021-03-04 17:40:53 +00:00
description + = " \n • " + _ ( " Disabling this tab will disable the following %d tabs. " ) % len ( self . dependent_tabs )
2018-04-29 01:26:53 +00:00
else :
2021-03-04 17:40:53 +00:00
description + = " \n • " + _ ( " Disabling this tab will disable the following tab. " )
2018-04-29 01:26:53 +00:00
if self . paired_tab :
2021-03-04 17:40:53 +00:00
description + = " \n • " + _ ( " Enabling this tab will disable %s and vice-versa. " ) % self . paired_tab . name
2018-04-29 01:26:53 +00:00
self . description_text = description
def resized ( self , event ) :
if not hasattr ( self , ' rewrap_timer ' ) :
self . rewrap_timer = wx . Timer ( )
self . rewrap_timer . Bind ( wx . EVT_TIMER , self . rewrap )
# If we try to rewrap every time we get EVT_SIZE then a resize is
# extremely slow.
self . rewrap_timer . Start ( 50 , oneShot = True )
event . Skip ( )
def rewrap ( self , event = None ) :
self . description . SetLabel ( self . description_text )
self . description . Wrap ( self . GetSize ( ) . x - 20 )
self . description_container . Layout ( )
if event :
event . Skip ( )
def __set_properties ( self ) :
# begin wxGlade: SatinPane.__set_properties
# end wxGlade
pass
2021-10-21 14:24:40 +00:00
#choice tuple is None or contains ("choice widget param name", "actual selection")
def update_choice_widgets ( self , choice_tuple = None ) :
if choice_tuple == None : #update all choices
for choice in self . dict_of_choices . values ( ) :
self . update_choice_widgets ( ( choice [ " param " ] . name , choice [ " widget " ] . GetSelection ( ) ) )
else :
choice = self . dict_of_choices [ choice_tuple [ 0 ] ]
last_selection = choice [ " last_initialized_choice " ]
current_selection = choice [ " widget " ] . GetSelection ( )
if last_selection != - 1 and last_selection != current_selection : #Hide the old widgets
for widget in self . choice_widgets [ ( choice [ " param " ] . name , last_selection ) ] :
widget . Hide ( )
#self.settings_grid.Detach(widget)
#choice_index = self.settings_grid.GetChildren().index(self.settings_grid.GetItem(choice["widget"])) #TODO: is there a better way to get the index in the sizer?
for widgets in grouper ( self . choice_widgets [ choice_tuple ] , 4 ) :
widgets [ 0 ] . Show ( True )
widgets [ 1 ] . Show ( True )
widgets [ 2 ] . Show ( True )
widgets [ 3 ] . Show ( True )
choice [ " last_initialized_choice " ] = current_selection
def __do_layout ( self , only_settings_grid = False ) :
2018-04-29 01:26:53 +00:00
# just to add space around the settings
box = wx . BoxSizer ( wx . VERTICAL )
summary_box = wx . StaticBox ( self , wx . ID_ANY , label = _ ( " Inkscape objects " ) )
sizer = wx . StaticBoxSizer ( summary_box , wx . HORIZONTAL )
self . description = wx . StaticText ( self )
self . update_description ( )
self . description . SetLabel ( self . description_text )
self . description_container = box
self . Bind ( wx . EVT_SIZE , self . resized )
2018-09-16 17:09:46 +00:00
sizer . Add ( self . description , proportion = 0 , flag = wx . EXPAND | wx . ALL , border = 5 )
2018-04-29 01:26:53 +00:00
box . Add ( sizer , proportion = 0 , flag = wx . ALL , border = 5 )
if self . toggle :
2018-09-01 20:26:47 +00:00
toggle_sizer = wx . BoxSizer ( wx . HORIZONTAL )
2018-09-16 17:09:46 +00:00
toggle_sizer . Add ( self . create_change_indicator ( self . toggle . name ) , proportion = 0 , flag = wx . ALIGN_CENTER_VERTICAL | wx . RIGHT , border = 5 )
2018-09-01 20:26:47 +00:00
toggle_sizer . Add ( self . toggle_checkbox , proportion = 0 , flag = wx . ALIGN_CENTER_VERTICAL )
box . Add ( toggle_sizer , proportion = 0 , flag = wx . BOTTOM , border = 10 )
2018-04-29 01:26:53 +00:00
for param in self . params :
2021-10-21 14:24:40 +00:00
col1 = self . create_change_indicator ( param . name )
2018-04-29 01:26:53 +00:00
description = wx . StaticText ( self , label = param . description )
description . SetToolTip ( param . tooltip )
2021-10-21 14:24:40 +00:00
if param . select_items != None :
col1 . Hide ( )
description . Hide ( )
for item in param . select_items :
self . choice_widgets [ item ] . extend ( [ col1 , description ] )
#else:
self . settings_grid . Add ( col1 , proportion = 0 , flag = wx . ALIGN_CENTER_VERTICAL )
2018-09-16 17:09:46 +00:00
self . settings_grid . Add ( description , proportion = 1 , flag = wx . EXPAND | wx . RIGHT | wx . ALIGN_CENTER_VERTICAL | wx . TOP , border = 5 )
2018-04-29 01:26:53 +00:00
if param . type == ' boolean ' :
if len ( param . values ) > 1 :
input = wx . CheckBox ( self , style = wx . CHK_3STATE )
input . Set3StateValue ( wx . CHK_UNDETERMINED )
else :
input = wx . CheckBox ( self )
if param . values :
input . SetValue ( param . values [ 0 ] )
input . Bind ( wx . EVT_CHECKBOX , self . changed )
2021-03-14 08:38:36 +00:00
elif param . type == ' dropdown ' :
input = wx . Choice ( self , wx . ID_ANY , choices = param . options )
input . SetSelection ( int ( param . values [ 0 ] ) )
input . Bind ( wx . EVT_CHOICE , self . changed )
2021-10-21 14:24:40 +00:00
input . Bind ( wx . EVT_CHOICE , self . update_choice_state )
self . dict_of_choices [ param . name ] = { " param " : param , " widget " : input , " last_initialized_choice " : 1 }
2018-04-29 01:26:53 +00:00
elif len ( param . values ) > 1 :
2018-09-01 20:38:53 +00:00
input = wx . ComboBox ( self , wx . ID_ANY , choices = sorted ( str ( value ) for value in param . values ) , style = wx . CB_DROPDOWN )
2018-04-29 01:26:53 +00:00
input . Bind ( wx . EVT_COMBOBOX , self . changed )
input . Bind ( wx . EVT_TEXT , self . changed )
else :
value = param . values [ 0 ] if param . values else " "
input = wx . TextCtrl ( self , wx . ID_ANY , value = str ( value ) )
input . Bind ( wx . EVT_TEXT , self . changed )
self . param_inputs [ param . name ] = input
2021-10-21 14:24:40 +00:00
col4 = wx . StaticText ( self , label = param . unit or " " )
if param . select_items != None :
input . Hide ( )
col4 . Hide ( )
for item in param . select_items :
self . choice_widgets [ item ] . extend ( [ input , col4 ] )
#else:
2018-09-16 17:09:46 +00:00
self . settings_grid . Add ( input , proportion = 1 , flag = wx . ALIGN_CENTER_VERTICAL | wx . EXPAND | wx . LEFT , border = 40 )
2021-10-21 14:24:40 +00:00
self . settings_grid . Add ( col4 , proportion = 1 , flag = wx . ALIGN_CENTER_VERTICAL )
2018-04-29 01:26:53 +00:00
2021-03-04 17:40:53 +00:00
self . inputs_to_params = { v : k for k , v in self . param_inputs . items ( ) }
2018-09-01 20:26:47 +00:00
2018-04-29 01:26:53 +00:00
box . Add ( self . settings_grid , proportion = 1 , flag = wx . ALL , border = 10 )
self . SetSizer ( box )
2021-10-21 14:24:40 +00:00
self . update_choice_widgets ( )
2018-04-29 01:26:53 +00:00
self . Layout ( )
2018-09-01 20:26:47 +00:00
def create_change_indicator ( self , param ) :
indicator = wx . Button ( self , style = wx . BORDER_NONE | wx . BU_NOTEXT , size = ( 28 , 28 ) )
indicator . SetToolTip ( _ ( ' Click to force this parameter to be saved when you click " Apply and Quit " ' ) )
indicator . Bind ( wx . EVT_BUTTON , lambda event : self . enable_change_indicator ( param ) )
self . param_change_indicators [ param ] = indicator
return indicator
def enable_change_indicator ( self , param ) :
self . param_change_indicators [ param ] . SetBitmapLabel ( self . pencil_icon )
self . param_change_indicators [ param ] . SetToolTip ( _ ( ' This parameter will be saved when you click " Apply and Quit " ' ) )
self . changed_inputs . add ( self . param_inputs [ param ] )
2018-12-16 01:21:41 +00:00
if self . on_change_hook :
2018-09-01 20:26:47 +00:00
self . on_change_hook ( self )
2018-04-29 01:26:53 +00:00
# end of class SatinPane
2018-09-16 17:09:46 +00:00
2018-04-29 01:26:53 +00:00
class SettingsFrame ( wx . Frame ) :
def __init__ ( self , * args , * * kwargs ) :
2021-06-01 11:17:00 +00:00
# This is necessary because of https://github.com/inkstitch/inkstitch/issues/1186
2021-06-24 20:25:13 +00:00
if sys . platform . startswith ( ' win32 ' ) :
2021-06-01 11:17:00 +00:00
import locale
locale . setlocale ( locale . LC_ALL , " C " )
2021-06-24 20:25:13 +00:00
lc = wx . Locale ( )
lc . Init ( wx . LANGUAGE_DEFAULT )
2021-06-01 11:17:00 +00:00
2018-04-29 01:26:53 +00:00
# begin wxGlade: MyFrame.__init__
self . tabs_factory = kwargs . pop ( ' tabs_factory ' , [ ] )
self . cancel_hook = kwargs . pop ( ' on_cancel ' , None )
wx . Frame . __init__ ( self , None , wx . ID_ANY ,
_ ( " Embroidery Params " )
)
2021-07-26 16:54:38 +00:00
icon = wx . Icon ( os . path . join ( get_resource_dir ( " icons " ) , " inkstitch256x256.png " ) )
self . SetIcon ( icon )
2018-04-29 01:26:53 +00:00
self . notebook = wx . Notebook ( self , wx . ID_ANY )
self . tabs = self . tabs_factory ( self . notebook )
for tab in self . tabs :
2018-12-16 01:21:41 +00:00
tab . on_change ( self . update_preview )
2018-04-29 01:26:53 +00:00
2018-12-16 01:21:41 +00:00
self . preview = SimulatorPreview ( self )
self . presets_panel = PresetsPanel ( self )
2021-04-10 17:55:48 +00:00
self . warning_panel = WarningPanel ( self )
self . warning_panel . Hide ( )
2018-04-29 01:26:53 +00:00
self . cancel_button = wx . Button ( self , wx . ID_ANY , _ ( " Cancel " ) )
self . cancel_button . Bind ( wx . EVT_BUTTON , self . cancel )
self . Bind ( wx . EVT_CLOSE , self . cancel )
self . use_last_button = wx . Button ( self , wx . ID_ANY , _ ( " Use Last Settings " ) )
self . use_last_button . Bind ( wx . EVT_BUTTON , self . use_last )
self . apply_button = wx . Button ( self , wx . ID_ANY , _ ( " Apply and Quit " ) )
self . apply_button . Bind ( wx . EVT_BUTTON , self . apply )
2018-12-16 01:21:41 +00:00
self . notebook . SetMinSize ( ( 800 , 600 ) )
2018-04-29 01:26:53 +00:00
self . __do_layout ( )
# end wxGlade
2018-12-16 01:21:41 +00:00
def update_preview ( self , tab ) :
self . preview . update ( )
2018-04-29 01:26:53 +00:00
2018-12-16 01:21:41 +00:00
def generate_patches ( self , abort_early ) :
# called by self.preview
2018-06-10 20:09:38 +00:00
2018-04-29 01:26:53 +00:00
patches = [ ]
nodes = [ ]
for tab in self . tabs :
tab . apply ( )
if tab . enabled ( ) and not tab . is_dependent_tab ( ) :
nodes . extend ( tab . nodes )
# sort nodes into the proper stacking order
nodes . sort ( key = lambda node : node . order )
try :
2021-04-16 16:15:22 +00:00
wx . CallAfter ( self . _hide_warning )
2018-04-29 01:26:53 +00:00
for node in nodes :
2018-12-16 01:21:41 +00:00
if abort_early . is_set ( ) :
2018-04-29 01:26:53 +00:00
# cancel; params were updated and we need to start over
return [ ]
# Making a copy of the embroidery element is an easy
# way to drop the cache in the @cache decorators used
# for many params in embroider.py.
patches . extend ( copy ( node ) . embroider ( None ) )
except SystemExit :
2021-04-16 16:15:22 +00:00
wx . CallAfter ( self . _show_warning )
2018-04-29 01:26:53 +00:00
raise
2018-09-16 17:09:46 +00:00
except Exception :
2018-04-29 01:26:53 +00:00
# Ignore errors. This can be things like incorrect paths for
# satins or division by zero caused by incorrect param values.
pass
return patches
2021-04-16 16:15:22 +00:00
def _hide_warning ( self ) :
self . warning_panel . Hide ( )
self . Layout ( )
def _show_warning ( self ) :
self . warning_panel . Show ( )
self . Layout ( )
2018-04-29 01:26:53 +00:00
def get_preset_data ( self ) :
2018-12-16 01:21:41 +00:00
# called by self.presets_panel
2018-04-29 01:26:53 +00:00
preset = { }
current_tab = self . tabs [ self . notebook . GetSelection ( ) ]
while current_tab . parent_tab :
current_tab = current_tab . parent_tab
tabs = [ current_tab ]
if current_tab . paired_tab :
tabs . append ( current_tab . paired_tab )
tabs . extend ( current_tab . paired_tab . dependent_tabs )
tabs . extend ( current_tab . dependent_tabs )
for tab in tabs :
tab . save_preset ( preset )
return preset
2018-12-16 01:21:41 +00:00
def apply_preset_data ( self , preset_data ) :
# called by self.presets_panel
2018-04-29 01:26:53 +00:00
for tab in self . tabs :
2018-12-16 01:21:41 +00:00
tab . load_preset ( preset_data )
2018-04-29 01:26:53 +00:00
2018-12-16 01:21:41 +00:00
self . preview . update ( )
2018-04-29 01:26:53 +00:00
def _apply ( self ) :
for tab in self . tabs :
tab . apply ( )
def apply ( self , event ) :
self . _apply ( )
2018-12-16 01:21:41 +00:00
self . presets_panel . store_preset ( " __LAST__ " , self . get_preset_data ( ) )
2018-04-29 01:26:53 +00:00
self . close ( )
def use_last ( self , event ) :
2018-12-16 01:21:41 +00:00
self . preview . disable ( )
self . presets_panel . load_preset ( " __LAST__ " )
2018-04-29 01:26:53 +00:00
self . apply ( event )
def close ( self ) :
2018-12-16 01:21:41 +00:00
self . preview . close ( )
2018-04-29 01:26:53 +00:00
self . Destroy ( )
def cancel ( self , event ) :
if self . cancel_hook :
self . cancel_hook ( )
self . close ( )
def __do_layout ( self ) :
# begin wxGlade: MyFrame.__do_layout
sizer_1 = wx . BoxSizer ( wx . VERTICAL )
2018-09-16 17:09:46 +00:00
# self.sizer_3_staticbox.Lower()
2018-04-29 01:26:53 +00:00
sizer_3 = wx . BoxSizer ( wx . HORIZONTAL )
for tab in self . tabs :
self . notebook . AddPage ( tab , tab . name )
2021-04-10 17:55:48 +00:00
sizer_1 . Add ( self . warning_panel , 0 , flag = wx . EXPAND | wx . ALL , border = 10 )
2018-09-16 17:09:46 +00:00
sizer_1 . Add ( self . notebook , 1 , wx . EXPAND | wx . LEFT | wx . TOP | wx . RIGHT , 10 )
2018-12-16 01:21:41 +00:00
sizer_1 . Add ( self . presets_panel , 0 , flag = wx . EXPAND | wx . ALL , border = 10 )
2021-03-04 17:40:53 +00:00
sizer_3 . Add ( self . cancel_button , 0 , wx . RIGHT , 5 )
sizer_3 . Add ( self . use_last_button , 0 , wx . RIGHT | wx . BOTTOM , 5 )
sizer_3 . Add ( self . apply_button , 0 , wx . RIGHT | wx . BOTTOM , 5 )
2018-04-29 01:26:53 +00:00
sizer_1 . Add ( sizer_3 , 0 , wx . ALIGN_RIGHT , 0 )
self . SetSizer ( sizer_1 )
sizer_1 . Fit ( self )
2021-03-28 08:55:31 +00:00
# prevent the param dialog to become smaller than 500*400
# otherwise the scrollbar jumps to the side and produces a
# deprecation warning in wxpythons scrolledpanel.py line 225 -
# which is expecting an integer, but uses previously a division
self . SetSizeHints ( 500 , 400 )
2018-04-29 01:26:53 +00:00
self . Layout ( )
# end wxGlade
2018-09-16 17:09:46 +00:00
2018-06-13 00:18:55 +00:00
class NoValidObjects ( Exception ) :
pass
2018-09-16 17:09:46 +00:00
2018-04-29 02:14:23 +00:00
class Params ( InkstitchExtension ) :
2018-04-29 01:26:53 +00:00
def __init__ ( self , * args , * * kwargs ) :
self . cancelled = False
InkstitchExtension . __init__ ( self , * args , * * kwargs )
def embroidery_classes ( self , node ) :
element = EmbroideryElement ( node )
classes = [ ]
2021-03-12 01:34:29 +00:00
if not is_command ( node ) and not is_command_symbol ( node ) :
2020-05-16 21:01:00 +00:00
if node . tag == SVG_POLYLINE_TAG :
classes . append ( Polyline )
elif is_clone ( node ) :
classes . append ( Clone )
else :
if element . get_style ( " fill " , ' black ' ) and not element . get_style ( " fill-opacity " , 1 ) == " 0 " :
classes . append ( AutoFill )
2021-10-21 14:24:40 +00:00
#classes.append(Fill)
2020-05-16 21:01:00 +00:00
if element . get_style ( " stroke " ) is not None :
classes . append ( Stroke )
if element . get_style ( " stroke-dasharray " ) is None :
classes . append ( SatinColumn )
2018-04-29 01:26:53 +00:00
return classes
def get_nodes_by_class ( self ) :
nodes = self . get_nodes ( )
nodes_by_class = defaultdict ( list )
for z , node in enumerate ( nodes ) :
for cls in self . embroidery_classes ( node ) :
element = cls ( node )
element . order = z
nodes_by_class [ cls ] . append ( element )
2021-03-04 17:40:53 +00:00
return sorted ( list ( nodes_by_class . items ( ) ) , key = lambda cls_nodes : cls_nodes [ 0 ] . __name__ )
2018-04-29 01:26:53 +00:00
def get_values ( self , param , nodes ) :
getter = ' get_param '
if param . type in ( ' toggle ' , ' boolean ' ) :
getter = ' get_boolean_param '
else :
getter = ' get_param '
2021-03-04 17:40:53 +00:00
values = [ item for item in ( getattr ( node , getter ) ( param . name , param . default ) for node in nodes ) if item is not None ]
2018-04-29 01:26:53 +00:00
return values
def group_params ( self , params ) :
def by_group_and_sort_index ( param ) :
2021-03-04 17:40:53 +00:00
return param . group or " " , param . sort_index
2018-04-29 01:26:53 +00:00
def by_group ( param ) :
2021-03-04 17:40:53 +00:00
return param . group or " "
2018-04-29 01:26:53 +00:00
return groupby ( sorted ( params , key = by_group_and_sort_index ) , by_group )
2018-09-16 17:09:46 +00:00
def sort_tabs ( self , tabs ) :
def tab_sort_key ( tab ) :
parent = tab . parent_tab or tab
sort_key = (
# For Stroke and SatinColumn, place the one that's
# enabled first. Place dependent tabs first too.
parent . toggle and parent . toggle_checkbox . IsChecked ( ) ,
# If multiple tabs are enabled, make sure dependent
# tabs are grouped with the parent.
2021-03-04 17:40:53 +00:00
parent and parent . name ,
2018-09-16 17:09:46 +00:00
# Within parent/dependents, put the parent first.
tab == parent
)
return sort_key
tabs . sort ( key = tab_sort_key , reverse = True )
def pair_tabs ( self , tabs ) :
for tab in tabs :
if tab . toggle and tab . toggle . inverse :
for other_tab in tabs :
if other_tab != tab and other_tab . toggle . name == tab . toggle . name :
tab . pair ( other_tab )
other_tab . pair ( tab )
def assign_parents ( self , tabs , parent_tab ) :
for tab in tabs :
if tab != parent_tab :
parent_tab . add_dependent_tab ( tab )
tab . set_parent_tab ( parent_tab )
2018-04-29 01:26:53 +00:00
def create_tabs ( self , parent ) :
tabs = [ ]
2018-06-13 00:18:55 +00:00
nodes_by_class = self . get_nodes_by_class ( )
if not nodes_by_class :
raise NoValidObjects ( )
2018-04-29 01:26:53 +00:00
for cls , nodes in self . get_nodes_by_class ( ) :
params = cls . get_params ( )
for param in params :
param . values = list ( set ( self . get_values ( param , nodes ) ) )
parent_tab = None
new_tabs = [ ]
2021-03-04 17:40:53 +00:00
2018-04-29 01:26:53 +00:00
for group , params in self . group_params ( params ) :
2018-09-16 17:09:46 +00:00
tab_name = group or cls . element_name
tab = ParamsTab ( parent , id = wx . ID_ANY , name = tab_name , params = list ( params ) , nodes = nodes )
2018-04-29 01:26:53 +00:00
new_tabs . append ( tab )
2021-03-04 17:40:53 +00:00
if group == " " :
2018-04-29 01:26:53 +00:00
parent_tab = tab
2018-09-16 17:09:46 +00:00
self . assign_parents ( new_tabs , parent_tab )
2018-04-29 01:26:53 +00:00
tabs . extend ( new_tabs )
2018-09-16 17:09:46 +00:00
self . pair_tabs ( tabs )
self . sort_tabs ( tabs )
2018-04-29 01:26:53 +00:00
return tabs
def cancel ( self ) :
self . cancelled = True
def effect ( self ) :
2018-06-13 00:18:55 +00:00
try :
app = wx . App ( )
frame = SettingsFrame ( tabs_factory = self . create_tabs , on_cancel = self . cancel )
2018-08-17 05:16:34 +00:00
# position left, center
current_screen = wx . Display . GetFromPoint ( wx . GetMousePosition ( ) )
display = wx . Display ( current_screen )
display_size = display . GetClientArea ( )
frame_size = frame . GetSize ( )
2021-03-04 17:40:53 +00:00
frame . SetPosition ( ( int ( display_size [ 0 ] ) , int ( display_size [ 3 ] / 2 - frame_size [ 1 ] / 2 ) ) )
2018-08-17 05:16:34 +00:00
2018-06-13 00:18:55 +00:00
frame . Show ( )
app . MainLoop ( )
if self . cancelled :
# This prevents the superclass from outputting the SVG, because we
# may have modified the DOM.
sys . exit ( 0 )
except NoValidObjects :
self . no_elements_error ( )