question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Elm Architecture / Flux / Redux-style Global State Store

See original GitHub issue

Hello Chris,

I found re-wx from a reply to my HackerNews comment at https://news.ycombinator.com/item?id=28328165.

I’d like to build a re-wx app with a global state store and the Model-View-Update / The Elm Architecture / Flux / Redux pattern. Here’s my use case:

1.) Have a main thread running the GUI that users click on buttons and see readouts of the state in Gauges and StaticTexts 2.) Have several other threads running in the app that are doing background tasks. It’s important to know that these background tasks aren’t triggered from the user interacting with the UI except for the initial launch, which should spawn 3 threads that start doing stuff (downloading) in the background.

As these tasks get completed, the background threads send messages using a Queue. An update function takes in the current state, a message from the queue, and produces a new global state. Ideally, that would cause the re-wx app to render and show updated messages in the Gauge and StaticText readouts of the download progress.

Essentially a multithreaded downloader?

Would you be able to share a toy script of how you’d implement a global state store and update that state store from outside the re-wx app but still cause the re-wx app to render?

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
z3ugmacommented, Aug 30, 2021

Sweet, the Child components example is working with 0.0.6. Thank you for helping out!

1reaction
chriskiehlcommented, Aug 29, 2021

Sure, here’s a small example to get you started.

import threading
import time
import wx
from concurrent.futures import ThreadPoolExecutor
from random import randint
from wx.lib.pubsub import pub

from components import StaticText
from rewx import Component, wsx, render, create_element
from rewx.components import Block, Button, Gauge


def fake_download(id):
    # pick an arbitrary amount of seconds for the "download" to take
    duration = randint(3, 7)
    # just to make it more visually exciting, we chunk each second
    # into 10 sub-steps
    duration_steps = duration * 10
    step_size = 100 / duration_steps

    for i in range(duration_steps+1):
        time.sleep(0.01)
        # CallAfter is used to safely send an action to the
        # main WX Thread
        print(id, step_size * i)
        wx.CallAfter(pub.sendMessage, 'download_update', item={
            'id': id,
            'percent': step_size * i,
            'status': 'Downloading',
            'eta': duration,
        })
    # Gauge's display lags behind its actual value because of
    # how it animates. So, we give a final sleep here after
    # completing our fake work just so that gauge's animation has
    # time to catch back up.
    time.sleep(1)



class FooGauge(Component):
    def __init__(self, props):
        super().__init__(props)
        self.props = props
        self.state = {
            "download_1": 0,
            "download_2": 0,
            # in practice, these downloads would be stored in
            # a proper data structure. They're stored flatly here
            # just for example.
            'status': 'READY'
        }
        pub.subscribe(self.update_downloads, 'download_update')

    def finish_download(self):
        wx.MessageDialog(None, "Download complete!", 'Alert').ShowModal()
        self.set_state({**self.state, 'status': 'READY'})

    def update_downloads(self, item):
        self.set_state({
            **self.state,
            item['id']: item['percent']
        })

    def start_download(self, event):
        # a central rule in GUI programming is to not block the main thread. 
        # So, we spin up a quick thread so that it can handle spawning and waiting 
        # for the ThreadPool Executor without blocking the main thread here
        self.set_state({**self.state, "status": 'DOWNLOADING'})
        thread = threading.Thread(target=self.start_fake_downloads)
        thread.start()

    def start_fake_downloads(self):
        with ThreadPoolExecutor() as executor:
            results = list(executor.map(fake_download, ['download_1', 'download_2']))
            wx.CallAfter(self.finish_download)


    def render(self):
        return wsx(
            [Block, {},
             [Block, {'orient': wx.HORIZONTAL},
              [StaticText, {'label': 'Download #1'}],
              [Gauge, {"value": self.state["download_1"],
                       "range": 100}]],
             [Block, {'orient': wx.HORIZONTAL},
              [StaticText, {'label': 'Download #2'}],
              [Gauge, {"value": self.state["download_2"],
                       "range": 100}]],
             [Button, {"label": "Download" if self.state['status'] == 'READY' else 'In Progress',
                       'enabled': self.state['status'] == 'READY',
                       "on_click": self.start_download,
                       "flag": wx.CENTER | wx.ALL}],
            ]
        )


if __name__ == "__main__":
    app = wx.App()

    frame = wx.Frame(None, title="Gauge With Update")
    clock = render(create_element(FooGauge, {}), frame)
    frame.Show()
    app.MainLoop()

gauge

You can scale this out to pretty complex things. Here’s a youtube-download clone in re-wx.

youtube-dl-clone

Read more comments on GitHub >

github_iconTop Results From Across the Web

Prior Art | Redux
Elm is a functional programming language inspired by Haskell and created by Evan Czaplicki. It enforces a “model view update” architecture, ...
Read more >
Redux: An Architectural Style Inspired by Flux - InfoQ
Unlike Flux, there is a single store in Redux, containing the state for the entire application. The store is organized in a tree...
Read more >
what is the difference between Elm and redux architecture
The global state can be thought of as the datastore of resources (and settings) and individual components can work and function in isolation...
Read more >
In-Depth Overview | Flux - Meta Open Source
Flux is the application architecture that Facebook uses for building client-side web applications. It complements React's composable view ...
Read more >
Redux and Reframe II: Influences and Origins | by Sean Para
Redux was inspired in large part by Facebook's Flux architecture. ... object then changes the application's global state in the Redux store.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found