summaryrefslogtreecommitdiff
path: root/lib/gui/simulator.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gui/simulator.py')
-rw-r--r--lib/gui/simulator.py193
1 files changed, 135 insertions, 58 deletions
diff --git a/lib/gui/simulator.py b/lib/gui/simulator.py
index e1357432..5cde0581 100644
--- a/lib/gui/simulator.py
+++ b/lib/gui/simulator.py
@@ -84,15 +84,11 @@ class ControlPanel(wx.Panel):
self.btnReverse = wx.BitmapToggleButton(self, -1, style=self.button_style)
self.btnReverse.Bind(wx.EVT_TOGGLEBUTTON, self.on_reverse_button)
self.btnReverse.SetBitmap(self.load_icon('reverse'))
- self.btnReverse.SetToolTip(_('Animate in reverse (arrow right)'))
+ self.btnReverse.SetToolTip(_('Animate in reverse (arrow left)'))
self.btnPlay = wx.BitmapToggleButton(self, -1, style=self.button_style)
self.btnPlay.Bind(wx.EVT_TOGGLEBUTTON, self.on_play_button)
self.btnPlay.SetBitmap(self.load_icon('play'))
self.btnPlay.SetToolTip(_('Play (P)'))
- self.btnPause = wx.BitmapToggleButton(self, -1, style=self.button_style)
- self.btnPause.Bind(wx.EVT_TOGGLEBUTTON, self.on_pause_button)
- self.btnPause.SetBitmap(self.load_icon('pause'))
- self.btnPause.SetToolTip(_('Pause (P)'))
self.btnRestart = wx.Button(self, -1, style=self.button_style)
self.btnRestart.Bind(wx.EVT_BUTTON, self.animation_restart)
self.btnRestart.SetBitmap(self.load_icon('restart'))
@@ -101,14 +97,16 @@ class ControlPanel(wx.Panel):
self.btnNpp.Bind(wx.EVT_TOGGLEBUTTON, self.toggle_npp)
self.btnNpp.SetBitmap(self.load_icon('npp'))
self.btnNpp.SetToolTip(_('Display needle penetration point (O)'))
- self.slider = SimulatorSlider(self, -1, value=1, minValue=1, maxValue=2)
+ self.slider = SimulatorSlider(self, -1, value=1, minValue=1, maxValue=self.stitch_plan.num_stitches)
self.slider.Bind(wx.EVT_SLIDER, self.on_slider)
- self.stitchBox = IntCtrl(self, -1, value=1, min=1, max=2, limited=True, allow_none=True, style=wx.TE_PROCESS_ENTER)
+ self.stitchBox = IntCtrl(self, -1, value=1, min=1, max=self.stitch_plan.num_stitches,
+ size=((100, -1)), limited=True, allow_none=True, style=wx.TE_PROCESS_ENTER)
self.stitchBox.Bind(wx.EVT_LEFT_DOWN, self.on_stitch_box_focus)
self.stitchBox.Bind(wx.EVT_SET_FOCUS, self.on_stitch_box_focus)
self.stitchBox.Bind(wx.EVT_TEXT_ENTER, self.on_stitch_box_focusout)
self.stitchBox.Bind(wx.EVT_KILL_FOCUS, self.on_stitch_box_focusout)
self.Bind(wx.EVT_LEFT_DOWN, self.on_stitch_box_focusout)
+ self.totalstitchText = wx.StaticText(self, -1, label=f"/ { self.stitch_plan.num_stitches }")
self.btnJump = wx.BitmapToggleButton(self, -1, style=self.button_style)
self.btnJump.SetToolTip(_('Show jump stitches'))
self.btnJump.SetBitmap(self.load_icon('jump'))
@@ -129,14 +127,8 @@ class ControlPanel(wx.Panel):
# Layout
self.hbSizer1 = wx.BoxSizer(wx.HORIZONTAL)
self.hbSizer1.Add(self.slider, 1, wx.EXPAND | wx.RIGHT, 10)
- self.hbSizer1.Add(self.stitchBox, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
-
- self.command_sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Command")), wx.VERTICAL)
- self.command_text = wx.StaticText(self, wx.ID_ANY, label="", style=wx.ALIGN_CENTRE_HORIZONTAL | wx.ST_NO_AUTORESIZE)
- self.command_text.SetFont(wx.Font(wx.FontInfo(20).Bold()))
- self.command_text.SetMinSize(self.get_max_command_text_size())
- self.command_sizer.Add(self.command_text, 0, wx.EXPAND | wx.ALL, 10)
- self.hbSizer1.Add(self.command_sizer, 0, wx.EXPAND)
+ self.hbSizer1.Add(self.stitchBox, 0, wx.ALIGN_CENTER | wx.RIGHT, 10)
+ self.hbSizer1.Add(self.totalstitchText, 0, wx.ALIGN_CENTER | wx.RIGHT, 10)
self.controls_sizer = wx.StaticBoxSizer(wx.StaticBox(self, wx.ID_ANY, _("Controls")), wx.HORIZONTAL)
self.controls_inner_sizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -147,7 +139,6 @@ class ControlPanel(wx.Panel):
self.controls_inner_sizer.Add(self.btnReverse, 0, wx.EXPAND | wx.ALL, 2)
self.controls_inner_sizer.Add(self.btnForward, 0, wx.EXPAND | wx.ALL, 2)
self.controls_inner_sizer.Add(self.btnPlay, 0, wx.EXPAND | wx.ALL, 2)
- self.controls_inner_sizer.Add(self.btnPause, 0, wx.EXPAND | wx.ALL, 2)
self.controls_inner_sizer.Add(self.btnRestart, 0, wx.EXPAND | wx.ALL, 2)
self.controls_sizer.Add((1, 1), 1)
self.controls_sizer.Add(self.controls_inner_sizer, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 10)
@@ -325,11 +316,8 @@ class ControlPanel(wx.Panel):
def update_speed_text(self):
self.speed_text.SetLabel(self.format_speed_text(self.speed * self.direction))
- def get_max_command_text_size(self):
- extents = [self.command_text.GetTextExtent(command) for command in COMMAND_NAMES]
- return max(extents, key=lambda extent: extent.x)
-
def on_slider(self, event):
+ self.animation_pause()
stitch = event.GetEventObject().GetValue()
self.stitchBox.SetValue(stitch)
@@ -343,7 +331,6 @@ class ControlPanel(wx.Panel):
self.current_stitch = stitch
self.slider.SetValue(stitch)
self.stitchBox.SetValue(stitch)
- self.command_text.SetLabel(COMMAND_NAMES[command])
def on_stitch_box_focus(self, event):
self.animation_pause()
@@ -353,7 +340,11 @@ class ControlPanel(wx.Panel):
def on_stitch_box_focusout(self, event):
self.SetAcceleratorTable(self.accel_table)
stitch = self.stitchBox.GetValue()
- self.parent.SetFocus()
+ # We now want to remove the focus from the stitchBox.
+ # In Windows it won't work if we set focus to self.parent, while setting the focus to the
+ # top level would work. This in turn would activate the trim button in Linux. So let's
+ # set the focus on the slider instead where it doesn't cause any harm in any of the operating systems
+ self.slider.SetFocus()
if stitch is None:
stitch = 1
@@ -363,6 +354,7 @@ class ControlPanel(wx.Panel):
if self.drawing_panel:
self.drawing_panel.set_current_stitch(stitch)
+ event.Skip()
def animation_slow_down(self, event):
""""""
@@ -374,25 +366,26 @@ class ControlPanel(wx.Panel):
def animation_pause(self, event=None):
self.drawing_panel.stop()
+ self.btnPlay.SetBitmap(self.load_icon('play'))
def animation_start(self, event=None):
self.drawing_panel.go()
+ self.btnPlay.SetBitmap(self.load_icon('pause'))
def on_start(self):
- self.btnPause.SetValue(False)
self.btnPlay.SetValue(True)
+ self.btnPlay.SetBitmap(self.load_icon('pause'))
def on_stop(self):
- self.btnPause.SetValue(True)
self.btnPlay.SetValue(False)
-
- def on_pause_button(self, event):
- """"""
- self.animation_pause()
+ self.btnPlay.SetBitmap(self.load_icon('play'))
def on_play_button(self, event):
- """"""
- self.animation_start()
+ play = self.btnPlay.GetValue()
+ if play:
+ self.animation_start()
+ else:
+ self.animation_pause()
def play_or_pause(self, event):
if self.drawing_panel.animating:
@@ -726,7 +719,10 @@ class DrawingPanel(wx.Panel):
def set_current_stitch(self, stitch):
self.current_stitch = stitch
self.clamp_current_stitch()
- self.control_panel.on_current_stitch(self.current_stitch, self.commands[self.current_stitch])
+ command = self.commands[self.current_stitch]
+ self.control_panel.on_current_stitch(self.current_stitch, command)
+ statusbar = self.GetTopLevelParent().statusbar
+ statusbar.SetStatusText(_("Command: %s") % COMMAND_NAMES[command])
self.stop_if_at_end()
self.Refresh()
@@ -833,19 +829,13 @@ class ColorSection:
class SimulatorSlider(wx.Panel):
PROXY_EVENTS = (wx.EVT_SLIDER,)
- def __init__(self, parent, id=wx.ID_ANY, *args, **kwargs):
+ def __init__(self, parent, id=wx.ID_ANY, minValue=0, maxValue=1, **kwargs):
super().__init__(parent, id)
- kwargs['style'] = wx.SL_HORIZONTAL | wx.SL_LABELS
+ kwargs['style'] = wx.SL_HORIZONTAL | wx.SL_VALUE_LABEL | wx.SL_TOP | wx.ALIGN_TOP
- self.sizer = wx.BoxSizer(wx.VERTICAL)
- self.slider = wx.Slider(self, *args, **kwargs)
- self.sizer.Add(self.slider, 0, wx.EXPAND)
-
- # add 33% additional vertical space for marker icons
- size = self.sizer.CalcMin()
- self.sizer.Add((10, size.height // 3), 1, wx.EXPAND)
- self.SetSizerAndFit(self.sizer)
+ self._height = self.GetTextExtent("M").y * 4
+ self.SetMinSize((self._height, self._height))
self.marker_lists = {
"trim": MarkerList("trim"),
@@ -855,31 +845,45 @@ class SimulatorSlider(wx.Panel):
}
self.marker_pen = wx.Pen(wx.Colour(0, 0, 0))
self.color_sections = []
- self.margin = 13
- self.color_bar_start = 0.25
+ self.margin = 15
+ self.tab_start = 0
+ self.tab_width = 0.2
+ self.tab_height = 0.2
+ self.color_bar_start = 0.3
self.color_bar_thickness = 0.25
- self.marker_start = 0.375
+ self.marker_start = self.color_bar_start
self.marker_end = 0.75
self.marker_icon_start = 0.75
- self.marker_icon_size = size.height // 3
+ self.marker_icon_size = self._height // 4
+
+ self._min = minValue
+ self._max = maxValue
+ self._value = 0
+ self._tab_rect = None
+
+ if sys.platform == "darwin":
+ self.margin = 8
self.Bind(wx.EVT_PAINT, self.on_paint)
self.Bind(wx.EVT_ERASE_BACKGROUND, self.on_erase_background)
+ self.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_down)
+ self.Bind(wx.EVT_LEFT_UP, self.on_mouse_up)
+ self.Bind(wx.EVT_MOTION, self.on_mouse_motion)
def SetMax(self, value):
- self.slider.SetMax(value)
+ self._max = value
+ self.Refresh()
def SetMin(self, value):
- self.slider.SetMin(value)
+ self._min = value
+ self.Refresh()
def SetValue(self, value):
- self.slider.SetValue(value)
+ self._value = value
+ self.Refresh()
- def Bind(self, event, callback, *args, **kwargs):
- if event in self.PROXY_EVENTS:
- self.slider.Bind(event, callback, *args, **kwargs)
- else:
- super().Bind(event, callback, *args, **kwargs)
+ def GetValue(self):
+ return self._value
def add_color_section(self, color, start, end):
self.color_sections.append(ColorSection(color, start, end))
@@ -902,14 +906,16 @@ class SimulatorSlider(wx.Panel):
def on_paint(self, event):
dc = wx.BufferedPaintDC(self)
- background_brush = wx.Brush(self.GetTopLevelParent().GetBackgroundColour(), wx.SOLID)
- dc.SetBackground(background_brush)
+ if not sys.platform.startswith("win"):
+ # Without this, the background color will be white.
+ background_brush = wx.Brush(self.GetTopLevelParent().GetBackgroundColour(), wx.SOLID)
+ dc.SetBackground(background_brush)
dc.Clear()
gc = wx.GraphicsContext.Create(dc)
width, height = self.GetSize()
- min_value = self.slider.GetMin()
- max_value = self.slider.GetMax()
+ min_value = self._min
+ max_value = self._max
spread = max_value - min_value
def _value_to_x(value):
@@ -924,6 +930,22 @@ class SimulatorSlider(wx.Panel):
gc.DrawRectangle(start_x, height * self.color_bar_start,
end_x - start_x, height * self.color_bar_thickness)
+ gc.SetPen(wx.Pen(wx.Colour(255, 255, 255), 1))
+ gc.SetBrush(wx.Brush(wx.Colour(0, 0, 0)))
+
+ value_x = _value_to_x(self._value)
+ tab_height = self.tab_height * height
+ tab_width = self.tab_width * height
+ tab_x = value_x - tab_width / 2
+ tab_y = height * self.tab_start
+ self._tab_rect = wx.Rect(round(tab_x), round(tab_y), round(tab_width), round(tab_height))
+ gc.DrawRectangle(
+ value_x - 1.5, 0,
+ 3, height * (self.color_bar_start + self.color_bar_thickness))
+ gc.SetPen(wx.NullPen)
+ gc.DrawRectangle(value_x - tab_width/2, height * self.tab_start,
+ tab_width, tab_height)
+
gc.SetPen(self.marker_pen)
for marker_list in self.marker_lists.values():
if marker_list.enabled:
@@ -943,6 +965,49 @@ class SimulatorSlider(wx.Panel):
# supposedly this prevents flickering?
pass
+ def is_in_tab(self, point):
+ return self._tab_rect and self._tab_rect.Inflate(2).Contains(point)
+
+ def set_value_from_position(self, point):
+ width, height = self.GetSize()
+ min_value = self._min
+ max_value = self._max
+ spread = max_value - min_value
+ value = round((point.x - self.margin) * spread / (width - 2 * self.margin))
+ value = max(value, self._min)
+ value = min(value, self._max)
+ self.SetValue(round(value))
+
+ event = wx.CommandEvent(wx.wxEVT_COMMAND_SLIDER_UPDATED, self.GetId())
+ event.SetInt(value)
+ event.SetEventObject(self)
+ self.GetEventHandler().ProcessEvent(event)
+
+ def on_mouse_down(self, event):
+ click_pos = event.GetPosition()
+ if self.is_in_tab(click_pos):
+ debug.log("drag start")
+ self.CaptureMouse()
+ self.set_value_from_position(click_pos)
+ self.Refresh()
+ else:
+ width, height = self.GetSize()
+ relative_y = click_pos.y / height
+ if relative_y > self.color_bar_start and relative_y - self.color_bar_start < self.color_bar_thickness:
+ self.set_value_from_position(click_pos)
+ self.Refresh()
+
+ def on_mouse_motion(self, event):
+ if self.HasCapture() and event.Dragging() and event.LeftIsDown():
+ self.set_value_from_position(event.GetPosition())
+ self.Refresh()
+
+ def on_mouse_up(self, event):
+ if self.HasCapture():
+ self.ReleaseMouse()
+ self.set_value_from_position(event.GetPosition())
+ self.Refresh()
+
class SimulatorPanel(wx.Panel):
""""""
@@ -993,6 +1058,8 @@ class EmbroiderySimulator(wx.Frame):
target_duration = kwargs.pop('target_duration', None)
wx.Frame.__init__(self, *args, **kwargs)
+ self.SetWindowStyle(wx.FRAME_FLOAT_ON_PARENT)
+
sizer = wx.BoxSizer(wx.HORIZONTAL)
self.simulator_panel = SimulatorPanel(self,
stitch_plan=stitch_plan,
@@ -1000,7 +1067,17 @@ class EmbroiderySimulator(wx.Frame):
stitches_per_second=stitches_per_second)
sizer.Add(self.simulator_panel, 1, wx.EXPAND)
- self.SetSizeHints(sizer.CalcMin())
+ self.statusbar = self.CreateStatusBar()
+
+ # SetSizeHints seems to be ignored in macOS, so we have to adjust size manually
+ # self.SetSizeHints(sizer.CalcMin())
+ frame_width, frame_height = self.GetSize()
+ sizer_width, sizer_height = sizer.CalcMin()
+ size_diff = frame_width - sizer_width
+ if size_diff < 0:
+ frame_x, frame_y = self.GetPosition()
+ self.SetPosition((frame_x + size_diff, frame_y))
+ self.SetSize((sizer_width, frame_height))
self.Bind(wx.EVT_CLOSE, self.on_close)