Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a common config file for examples. #80

Merged
merged 9 commits into from
Dec 12, 2024
Merged

Conversation

corranwebster
Copy link
Contributor

@corranwebster corranwebster commented Dec 10, 2024

This shifts the init_display functions in the examples to a tempe_config file that users can replace with something appropriate for their hardware. It includes example config files for several types of display.

This also changes the API of the Display protocol to require that it have a size attribute than can be used to constrain rendering.

Fixes #68. Fixes #81.

This shifts the `init_display` functions in the examples to a `tempe_config`
file that users can replace with something appropriate for their hardware.

Fixes #68.
@thestumbler
Copy link

Did some testing with this just now. Two observations:

On my 240x240 display, none of the examples would run without going through and editing all instances of 320 to be 240. Obviously this should be done based on the actual resolution in the display / config class.

The if __name__ == '__main__' doesn't work, at least for how I run the code. If you fire up the REPL and do an import polar_example, nothing happens. That's because __name__ evaluates to 'polar_example', not 'main'. In fact, I'm not sure how to make it be 'main'

@thestumbler
Copy link

I should add that I run examples like this via the REPL which I access by RSHELL for example. I'll try this via Thonny and see if that makes a difference.

But on MicroPython, how would one pass a name value if you wanted to invoke a certain execution path?

Oh... I notice that the original code has two paths, basically MAIN and TEST. This revision changes the logic such that there is only one possibility, MAIN. The decision to run in TEST mode (write a file) is based on failure to initialize the hardware display. Based on this point alone, maybe there's no need to test for MAIN anymore? Well, unless you want to import an example project into a bigger program, you need some way to keep it from hijacking the "real" main.

@corranwebster
Copy link
Contributor Author

corranwebster commented Dec 11, 2024

On my 240x240 display, none of the examples would run without going through and editing all instances of 320 to be 240. Obviously this should be done based on the actual resolution in the display / config class.

OK, I think I understand what's happening here: the GC9A01 driver's blit_buffer method doesn't handle the case where the destination rectangle is partially outside the screen area, and so when it calls set_window it returns immediately without changing the window, but thenblit_buffer sends the buffer bytes to the device. So this is a bug in the GC9A01 driver, I think (without having a device to test against to confirm).

All that said, I think that there is some defensive programming that Tempe should be doing to avoid drawing outside the screen, if possible. It shouldn't matter what the size of the surface is compared to the size of the screen.

ETA: this should now be fixed, I think.

@thestumbler
Copy link

Good point. On the other hand, the buffer size should probably be based on the actual screen size? If so you'd have to grab the display earlier in the program so you know it.

I'd imagine drawing outside the screen is a headache for a round screen (non trivial checks since trigonometry is involved)

@corranwebster
Copy link
Contributor Author

Good point. On the other hand, the buffer size should probably be based on the actual screen size? If so you'd have to grab the display earlier in the program so you know it.

No, buffer size is based on how much memory you can spare, the defaults are more or less fractions of my typical screen-size that work on my devices.

Tempe automatically handles tiled rendering for you if the buffer is smaller than the display (as is often the case for larger TFT displays). If you get memory errors (I've started getting them on a Pico W with the newest micropython) you can safely reduce the buffer size.

I'd imagine drawing outside the screen is a headache for a round screen (non trivial checks since trigonometry is involved)

I guess technically it's not drawing, it's all about memory transfers for these sorts of screens. I believe most round screens are backed by a square/rectangular chunk of memory where corners don't get displayed. So Tempe will render into the corners and then transfer that memory. The problems start happening when Tempe wants to write outside the available memory, really, not when it tries to draw outside the visible pixels.

@corranwebster
Copy link
Contributor Author

I should add that I run examples like this via the REPL which I access by RSHELL for example. I'll try this via Thonny and see if that makes a difference.

Ah. I don't use RSHELL, so I'm not familiar with it. It looks like it doesn't have a command to "run" a micropython script as opposed to just running a shell and manually importing. I've been using mpremote for the sorts of things that RSHELL does and running examples from my mac via:

mpremote run examples/shapes_examples.py

but Thonny should also work if you open the file and press the "run" button.

I'll think about making it easier to run in other ways, if I can - something like:

>>> from shapes_examples import main
>>> main()

should be possible, but I need to think about what to do for async examples.

But on MicroPython, how would one pass a name value if you wanted to invoke a certain execution path?

You can do it with exec, but normally it's something that is automatically set by the import mechanism.

Oh... I notice that the original code has two paths, basically MAIN and TEST. This revision changes the logic such that there is only one possibility, MAIN. The decision to run in TEST mode (write a file) is based on failure to initialize the hardware display. Based on this point alone, maybe there's no need to test for MAIN anymore? Well, unless you want to import an example project into a bigger program, you need some way to keep it from hijacking the "real" main.

The if __name__ == "__main__": stuff is mainly hard-wiring in my brain from 30 years of Python programming: it's just the way that you do things.

My constraint here is that I use these scripts for integration/regression testing as well as being examples. My test scripts are doing hacky things to run the scripts and get file output which needs to be pixel-perfect.

I'll think things through: it may be time to clean up some of the examples and put things into functions, at least in the bigger examples.

@thestumbler
Copy link

Okay. Tested the latest update. It works without modifications on my setup.

Thanks for explaining the mpremote run option. I've used rshell so much that I forget about the other options available. One thing of note ... it looks like the mpremote option runs the file from your local hard disk, which was unexpected at first. You can run a script on the device by the rshell command:

rshell repl "~ exec(open('polar_example.py').read()) ~"

Despite being unexpected, I think I like the mpremote approach better.

@thestumbler
Copy link

Noted about bigger size. But i would suggest that it be something the user could set in one place.

On my round screen, it's really round. It's not masked, at least physically. If it started life as a square, then they sawed off the excess not masked it. But clearly it works, so somehow it "handles" (ignores?) pixels outside the circle.

@corranwebster
Copy link
Contributor Author

On my round screen, it's really round. It's not masked, at least physically. If it started life as a square, then they sawed off the excess not masked it. But clearly it works, so somehow it "handles" (ignores?) pixels outside the circle.

Yep - the physical pixels aren't there but there are still memory locations which correspond to the "missing" pixels in the corner.

@corranwebster
Copy link
Contributor Author

I've refactored the examples so they all now have a main function, so you should now be able to run examples from the REPL via something like:

>>> from hello_world import main
>>> main()

I think this is more-or-less ready to merge, but I'll let it sit until tomorrow in case I think of anything else.

@thestumbler
Copy link

Thanks. I'll check that out today. Although now that you've reminded me of mpremote run I've incorporated that into my workflow along with rshell.
I'll also confirm Thonny which I sometimes use for really quick and dirty testing (and suggest to some clients).

You might like to know about the rshell rsync command. Just FYI.

@corranwebster corranwebster merged commit 84b77c0 into main Dec 12, 2024
3 checks passed
@corranwebster corranwebster deleted the feat/example-configs branch December 12, 2024 07:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Rendering should restrict itself to the screen size Make Examples easier to run with different displays
2 participants