diff options
| author | Kaalleen <36401965+kaalleen@users.noreply.github.com> | 2023-10-18 15:57:49 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-18 15:57:49 +0200 |
| commit | 746a984dac0ac2df83cc4dd8c8ab9dea64d315fe (patch) | |
| tree | 27fb780457ad0a81181d9cd623dae24aaa692b6c /lib/gui/simulator.py | |
| parent | 59b97a059c1ed7ba88e3de9f5353826abb086d5c (diff) | |
Fix new param simulator for macOS and Windows (#2546)
* fix tooltip
* combine play and pause button
---------
Co-authored-by: Lex Neva
Diffstat (limited to 'lib/gui/simulator.py')
| -rw-r--r-- | lib/gui/simulator.py | 193 |
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) |
