summaryrefslogtreecommitdiff
path: root/lib/gui
diff options
context:
space:
mode:
authorLex Neva <lexelby@users.noreply.github.com>2025-01-29 12:04:07 -0500
committerGitHub <noreply@github.com>2025-01-29 12:04:07 -0500
commit913c2700d1486284dba0583ae1b280b1aa237570 (patch)
treec165b29d0794981b5e44ab46f9838baab16b06a4 /lib/gui
parentefe3b27f17686094f74462bd81763a8197b54c6e (diff)
Sew Stack first steps (#3133)
* handle more recursive cases * scaffolding for stitch layers * scaffolding for SewStack * always use DotDict when parsing json params * add DefaultDotDict + DotDict fixes * first working SewStack (no UI yet) * ignore inkstitch_debug.log and .svg * refactor * early WIP: property grid display temporarily in stitch plan preview * start of sew stack editor extension * add layer properties panel and splitter * spacing and better icon * handle checkbox * add layer action buttons * show selected property help text in an HtmlWindow * rename * rephrase help text for tolerance * refactor into separate file * simplify structure * better property type handling * add randomization button * add random seed re-roll button * simulator preview * update preview in a few more cases * always DotDict * avoid ridiculously slow simulations * preview selected layer or all layers * edit multiple objects and save only modified properties into the SVG * better preview handling * add reverse and jitter * add stitch path jitter * fix types * fix random shuffle button * fixes * fix repeats * type hinting to please pycharm * show layer description * avoid exception in properties with multiple values * fix typing * fix new layer * draw a box around property grid and help box * confirm before closing * rename properties and fix seed * fix close/cancel logic * add buttons to undo changes and reset to default value * set not modified if default is original setting * fix invisible icon * more space for properties * fix random properties * better regulation of simulator rendering speed * Fixed timer being passed a float * fix get_json_param() default handling * fix tests * add checkbox for sew stack only * fix property help * adjustable stitch layer editor help box size, with persistence * repeat exact stitches * "fix" style * adjust for new next_element stuff --------- Co-authored-by: CapellanCitizen <thecapellancitizen@gmail.com>
Diffstat (limited to 'lib/gui')
-rw-r--r--lib/gui/simulator/control_panel.py6
-rw-r--r--lib/gui/simulator/drawing_panel.py42
-rw-r--r--lib/gui/simulator/split_simulator_window.py7
-rw-r--r--lib/gui/windows.py41
4 files changed, 80 insertions, 16 deletions
diff --git a/lib/gui/simulator/control_panel.py b/lib/gui/simulator/control_panel.py
index 9226d5de..99d1f92b 100644
--- a/lib/gui/simulator/control_panel.py
+++ b/lib/gui/simulator/control_panel.py
@@ -208,7 +208,11 @@ class ControlPanel(wx.Panel):
self.set_speed(global_settings['simulator_speed'])
return
if self.target_duration:
- self.set_speed(int(self.num_stitches / float(self.target_duration)))
+ stitches_per_second = round(self.num_stitches / float(self.target_duration))
+ if stitches_per_second < 10:
+ # otherwise it just looks weirdly slow
+ stitches_per_second = 10
+ self.set_speed(stitches_per_second)
else:
self.set_speed(self.target_stitches_per_second)
diff --git a/lib/gui/simulator/drawing_panel.py b/lib/gui/simulator/drawing_panel.py
index ced11f00..1587ecfa 100644
--- a/lib/gui/simulator/drawing_panel.py
+++ b/lib/gui/simulator/drawing_panel.py
@@ -52,8 +52,9 @@ class DrawingPanel(wx.Panel):
self.SetDoubleBuffered(True)
self.animating = False
+ self.timer = wx.Timer(self)
+ self.last_frame_start = 0
self.target_frame_period = 1.0 / self.TARGET_FPS
- self.last_frame_duration = 0
self.direction = 1
self.current_stitch = 0
self.black_pen = wx.Pen((128, 128, 128))
@@ -72,6 +73,7 @@ class DrawingPanel(wx.Panel):
self.Bind(wx.EVT_LEFT_DOWN, self.on_left_mouse_button_down)
self.Bind(wx.EVT_MOUSEWHEEL, self.on_mouse_wheel)
self.Bind(wx.EVT_SIZE, self.on_resize)
+ self.Bind(wx.EVT_TIMER, self.animate)
# wait for layouts so that panel size is set
if self.stitch_plan:
@@ -99,20 +101,30 @@ class DrawingPanel(wx.Panel):
elif self.direction == 1 and self.current_stitch < self.num_stitches:
self.go()
- def animate(self):
+ def animate(self, event=None):
if not self.animating:
return
- frame_time = max(self.target_frame_period, self.last_frame_duration)
-
- # No sense in rendering more frames per second than our desired stitches
- # per second.
- frame_time = max(frame_time, 1.0 / self.speed)
+ # Each frame, we need to advance forward some number of stitches to
+ # match the speed setting. The tricky thing is that with bigger
+ # designs, it may take a long time to render a frame. That might
+ # mean that we'll fall behind. Even if we set our Timer to 30 FPS,
+ # we may only actually manage to render 20 FPS or fewer, and the
+ # duration of each frame may vary.
+ #
+ # To deal with that, we'll figure out how many stitches to advance
+ # based on how long it took to render the last frame. We'll always
+ # be behind by one frame, but it should work out fine.
- stitch_increment = int(self.speed * frame_time)
+ now = time.time()
+ if self.last_frame_start:
+ frame_time = now - self.last_frame_start
+ else:
+ frame_time = self.target_frame_period
+ self.last_frame_start = now
+ stitch_increment = self.speed * frame_time
self.set_current_stitch(self.current_stitch + self.direction * stitch_increment)
- wx.CallLater(int(1000 * frame_time), self.animate)
def OnPaint(self, e):
dc = wx.PaintDC(self)
@@ -167,7 +179,6 @@ class DrawingPanel(wx.Panel):
stitch = 0
last_stitch = None
- start = time.time()
for pen, stitches, jumps in zip(self.pens, self.stitch_blocks, self.jumps):
canvas.SetPen(pen)
if stitch + len(stitches) < self.current_stitch:
@@ -177,13 +188,12 @@ class DrawingPanel(wx.Panel):
self.draw_needle_penetration_points(canvas, pen, stitches)
last_stitch = stitches[-1]
else:
- stitches = stitches[:self.current_stitch - stitch]
+ stitches = stitches[:int(self.current_stitch) - stitch]
if len(stitches) > 1:
self.draw_stitch_lines(canvas, pen, stitches, jumps)
self.draw_needle_penetration_points(canvas, pen, stitches)
last_stitch = stitches[-1]
break
- self.last_frame_duration = time.time() - start
if last_stitch:
self.draw_crosshair(last_stitch[0], last_stitch[1], canvas, transform)
@@ -260,7 +270,6 @@ class DrawingPanel(wx.Panel):
def load(self, stitch_plan):
self.current_stitch = 1
self.direction = 1
- self.last_frame_duration = 0
self.minx, self.miny, self.maxx, self.maxy = stitch_plan.bounding_box
self.width = self.maxx - self.minx
self.height = self.maxy - self.miny
@@ -326,6 +335,7 @@ class DrawingPanel(wx.Panel):
def stop(self):
self.animating = False
+ self.timer.Stop()
self.control_panel.on_stop()
def go(self):
@@ -335,6 +345,8 @@ class DrawingPanel(wx.Panel):
if not self.animating:
try:
self.animating = True
+ self.last_frame_start = 0
+ self.timer.Start(int(self.target_frame_period * 1000))
self.animate()
self.control_panel.on_start()
except RuntimeError:
@@ -412,8 +424,8 @@ class DrawingPanel(wx.Panel):
def set_current_stitch(self, stitch):
self.current_stitch = stitch
self.clamp_current_stitch()
- command = self.commands[self.current_stitch]
- self.control_panel.on_current_stitch(self.current_stitch, command)
+ command = self.commands[int(self.current_stitch)]
+ self.control_panel.on_current_stitch(int(self.current_stitch), command)
statusbar = self.GetTopLevelParent().statusbar
statusbar.SetStatusText(_("Command: %s") % COMMAND_NAMES[command], 2)
self.stop_if_at_end()
diff --git a/lib/gui/simulator/split_simulator_window.py b/lib/gui/simulator/split_simulator_window.py
index 72fd1143..e4b2803e 100644
--- a/lib/gui/simulator/split_simulator_window.py
+++ b/lib/gui/simulator/split_simulator_window.py
@@ -67,6 +67,13 @@ class SplitSimulatorWindow(wx.Frame):
def cancel(self, event=None):
if self.cancel_hook:
self.cancel_hook()
+ try:
+ if not self.settings_panel.confirm_close():
+ event.Veto()
+ return
+ except AttributeError:
+ pass
+
self.close(None)
def close(self, event=None):
diff --git a/lib/gui/windows.py b/lib/gui/windows.py
new file mode 100644
index 00000000..42c34cc8
--- /dev/null
+++ b/lib/gui/windows.py
@@ -0,0 +1,41 @@
+import wx
+
+
+class SimpleBox(wx.Panel):
+ """Draw a box around one window.
+
+ Usage:
+
+ window = SomeWindow(your_window_or_panel, wx.ID_ANY)
+ box = SimpleBox(your_window_or_panel, window)
+ some_sizer.Add(box, ...)
+
+ """
+
+ def __init__(self, parent, window, *args, width=1, radius=2, **kwargs):
+ super().__init__(parent, wx.ID_ANY, *args, **kwargs)
+
+ window.Reparent(self)
+ self.window = window
+ self.sizer = wx.BoxSizer(wx.VERTICAL)
+ self.sizer.Add(window, 1, wx.EXPAND | wx.ALL, 2)
+ self.SetSizer(self.sizer)
+
+ self.width = width
+ self.radius = radius
+
+ self.Bind(wx.EVT_ERASE_BACKGROUND, self.on_erase_background)
+
+ def on_erase_background(self, event):
+ dc = event.GetDC()
+ if not dc:
+ dc = wx.ClientDC(self)
+ size = self.GetClientSize()
+
+ if wx.SystemSettings().GetAppearance().IsDark():
+ dc.SetPen(wx.Pen(wx.Colour(32, 32, 32), width=self.width))
+ else:
+ dc.SetPen(wx.Pen(wx.Colour(128, 128, 128), width=self.width))
+
+ dc.SetBrush(wx.NullBrush)
+ dc.DrawRoundedRectangle(0, 0, size.x, size.y, self.radius)