Author Topic: GUI with gauges in python  (Read 26966 times)

0 Members and 4 Guests are viewing this topic.

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #150 on: November 12, 2025, 07:18:14 pm »
Code: [Select]
class TestListener(can.Listener):
    def on_message_received(self, theMessage ):
         print("The timestamp: {0!s}".format( theMessage.timestamp ) )
         # rest of your code here

myListener = TestListener()

bus0 = can.Bus(interface = "socketcan", channel = "can0", receive_own_messages=True)

notifier = can.Notifier(bus0, [myListener])

counter = 0
while True:           
    print("Sending messages...")
    bus0.send(can.Message(arbitration_id=1, data=[1,2,3,4,5,6,7,8], is_extended_id=True))
    counter += 1
    if counter >= 5:
        bus0.shutdown
        break       
    time.sleep(2)

It works. Now I will try with my devboard running a CAN open system.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #151 on: November 13, 2025, 07:34:31 am »
And I see messages from my micro dev board now that I figured out that on the pi CAN adapter they have the channel labels swapped :). Thank you very much for your help. Now back to arguing about GUI's ;)
 

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 8349
  • Country: fi
    • My home page and email address
Re: GUI with gauges in python
« Reply #152 on: November 13, 2025, 10:13:38 am »
Have you noticed how both GUIs and the CAN interface take control of the "main program flow", and instead just provide events (callbacks) to your code?

This is event-driven programming.  When you wrap your mind around how this approach works, you'll find it is also easier to do parallel (threads or processes) and distributed (different processes, perhaps even running on different machines), because you design everything around events that are passed around and change the state of the core of your program.  You'll also discover the commonalities in various GUI toolkits, with their differences mellowing into "ease of use", "ease of maintenance", "ease of customization".
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #153 on: November 13, 2025, 11:13:34 am »
Yes I know what you mean. It's how I program microcontrollers, I tend to make use of interrupts. My own CAN Open stack on a microcontroller runs a lot of it's functions in the interrupt handler or markers are left by the interrupt handler to trigger other code when ready.

I prefer to work with code where rather than one monolithic block of code doing everything smaller blocks chip in as and when to update or read the state of things. It's also core to running a system that is a collection of boards that communicate.

So now that this CAN code is running I guess that instead of my "while True" the event system of the GUI will manage the cyclical calling of code?
 
The following users thanked this post: Nominal Animal

Offline paulca

  • Super Contributor
  • ***
  • Posts: 6582
  • Country: gb
Re: GUI with gauges in python
« Reply #154 on: November 13, 2025, 12:23:19 pm »
Have you noticed how both GUIs and the CAN interface take control of the "main program flow", and instead just provide events (callbacks) to your code?

This is event-driven programming.  When you wrap your mind around how this approach works, you'll find it is also easier to do parallel (threads or processes) and distributed (different processes, perhaps even running on different machines), because you design everything around events that are passed around and change the state of the core of your program.  You'll also discover the commonalities in various GUI toolkits, with their differences mellowing into "ease of use", "ease of maintenance", "ease of customization".

Yep.  It is a bit jarring at first, but when you embrace the pattern and exploit it's advantages rather than see it's restrictions as hurdles, it will "click".

In context.  Consider a contrived use case of this system.  You receive a message from a device periodically.  You want on the UI to display a "Green" status indicator beside that device to show it is online.  However, if after a predetermined time you do not receive such a message you would like it to turn red.

A listener which first checks if the message came from the device of interest, if it did and is valid, update a "last_seen" timestamp.

Here you see the first quirk of this style.  If last_seen expires it is because there were no messages and thus nothing will call this listener in the case of failure.  It is the fact that it hasn't been called that detects the failure.

It is therefore likely another thread or a request from the UI that will look at that listeners "last_seen" value and either just return it to the UI directly and let it determine age expiry or send an "Offline" or "Online" response.  The later makes the request reusable for other purposes encapsulating the logic.  The former gives more flexibility to the user code.

Of course some clever code and a single listener with a Set of devices to look for can provide a Set of last_seen values or "On/Offline" status.  You could go further and make a listener that just logs the "Last seen" for any device it sees and return a list of statuses for every device it's seen.

The golden point is... when you then add your next listener with a purpose it doesn't need to know, nor care the other exists at all.

A word of note, consider the term "idempotent".  You need to accept that messages are not always in the correct order you expect.  So having things "repeatable" without additional action is often a good strategy.
« Last Edit: November 13, 2025, 12:25:32 pm by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 
The following users thanked this post: Nominal Animal

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: GUI with gauges in python
« Reply #155 on: November 13, 2025, 01:03:42 pm »
A word of note, consider the term "idempotent".  You need to accept that messages are not always in the correct order you expect.  So having things "repeatable" without additional action is often a good strategy.

Idempotency is useful, but it shouldn't be useful for that reason.

The allowable order of event/messages being received should be defined by the specification of the FSM. That spec should encompass all useful/usable sequences, plus what happens when an out-of-sequence event/message arrives (typically ignore or panic).

I've found hardware people understand event-driven systems more quickly and thoroughly than software people. The latter tend to have only come across FSMs in the context of lectures on parsers and compilers, and don't comprehend their wide applicability and formal definitions.
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #156 on: November 13, 2025, 01:30:57 pm »
Well the way messages are interpreted is down to CAN open in my case. On my microcontroller I don't have situations where one message arrives at the wrong time as the message ID's set the priorities. I do use a setting on the CAN controller that has it send messages in order of ID precedence rather than in the order they are added to the que so if a lower ID is added it will be sent before those in the que already that are higher numbers. I don't know how the MCP2515 and similar controllers behave and if there is an option how they are setup by the driver.

They don't have much of a buffer from what I gather.

One thing I wish I could know is if the transmit buffer is full. In micro controller land when I get to the time I need to send messages to every node I do this from the send buffer empty interrupt, this way I just turn it on and it fires, the buffer is then loaded up and once near full I note where I got to and leave. Once the buffer empty interrupt fires again I go again, once all messages are done I disable the interrupt again. So simple and efficient, how do I replicate that with a PC?

Problem is fundamentally that I have a bunch of messages to send the processor (PC or micro) will do this faster than the bus can go so I need to know how to time the sending, with the micro controller I used the interrupt for the transmit buffer to give that "cadence".
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 6582
  • Country: gb
Re: GUI with gauges in python
« Reply #157 on: November 13, 2025, 01:38:35 pm »
Direct state machines tend to be avoided or abstracted in such a way as to not require such concrete transitions.

Most modern architectures based around message buses don't have ANY STATE.  So they cannot hold a state machine.  In fact a state machine in such an architecture outside of the middle ware itself would be forbidden.

Why?

FSMs are the No.1 source of bugs like event starvation, deadlock and so on and so forth.  They are basically the definition of "tightly coupled".  FSMs do not scale horizontally very well.  So when you want to run not one of these services with an FSM but 100 of them, things get complicated with that state.  They (multiple instances) become coupled on shared state and therefore are typically compile bound.  Distributed state machines are a very high brow, high level engineering concept and either avoided or delegated to the middle ware.  Worst case the statemachine state is encapsulated in the message itself.

Examples would be if you MUST absolutely have a request-response, you need to set it up that the state machine is not a property or state of the service itself, but a property of the message flow.  Thus the state itself is held and transferred in the message if it is to exist at all.

Request RequestTopic, ID: 12345 Body: Please give me a Y.
ResponseTopic12345: Here is your Y.

The only thing the service needs to do is, after sending the message, listen on the response topic.  The only "catch" is how do you transition to a "timeout state" when nothing arrives on the response.  First question is, do you need to do anything?  Do you need to do anything in the case of a timeout?  Can a timeout be handled as a "natural" process in the system.

In most message bus systems the "state" of the overall system is 100% coupled to the messages.  In the event of a system crash or outage, the services have to replay data from a strategic point to rebuild state to "current".  This is where ideompotency comes in.  The idea of "eventually consistent" also.  The services will replay messages, say from the "start of day boot", might be millions of messages.

In this context ideompotency states that if the same messages are played in the same order they will result in the same state every time.  The "out of ordering" is different and process specific.  Obviously if you have three messages sent as "OFF, ON, OFF" and you reorder then OFF, OFF, ON the resulting state is different.  Note however if you called "toggle()" in the below example, the message order would not be a problem.  Horses for courses.

The basic textbook 101 would give:

Non-Ideompotent:
toggle()

Ideompotent:
on()
off()

It doesn't matter how many times you call "off" it will always result in the same state.  WIth "toggle" you need to know the prior state to predict the output state.  Thus for "Toggle" you would require an FSM and state to control the device.  Note, this is how "Home Assistant" works in it's core.  It assumes "master" of all state machines.  The result is that it's very often out of sync, struggles when "acks" are not recieved.  An example.  If you sent "ON" to a light and it does not recieve the state transition of the lamp within a period it will send an OFF and show the bulb as off.  This is NOT flexible behaviour and more annoyingly HomeAssistant does not survive restarts or "lost in flight" conditions very well.  It's a liability.

On specific examples, I found a state machine in some of my experimental "lighting zone and control" code.

Code: [Select]
zone_state = self.cache.last_for_topic(control_topic)
            try:
                zone_state
            except NameError:
                zone_state = self.default_control_state(control_zone)

            if not zone_state or zone_state is None:
                zone_state = self.default_control_state(control_zone)

            current = zone_state["state"]
            zone_state["state"] = zone_state["next-state"]
            if current == "OFF":
                zone_state["next-state"] = "ON"
            if current == "AUTO":
                zone_state["next-state"] = "AUTO"
            if current == "ON":
                zone_state["next-state"] = "OFF"
            self.client.publish_retained(control_topic, json.dumps(zone_state))

However, you will notice the state and next state are published back onto the bus.  All the state in the function is local scope and disappears once the message is processed.

It implements a pattern that should be very familiar to MCU FSM folks.  OFF->AUTO->ON->AUTO->

But no state in the service.  It's all in the messages.

Arguably I do use "last on topic" cache as "state" that spans messages and topics.
« Last Edit: November 13, 2025, 01:53:16 pm by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: GUI with gauges in python
« Reply #158 on: November 13, 2025, 01:50:40 pm »
One thing I wish I could know is if the transmit buffer is full. In micro controller land when I get to the time I need to send messages to every node I do this from the send buffer empty interrupt, this way I just turn it on and it fires, the buffer is then loaded up and once near full I note where I got to and leave. Once the buffer empty interrupt fires again I go again, once all messages are done I disable the interrupt again. So simple and efficient, how do I replicate that with a PC?

Problem is fundamentally that I have a bunch of messages to send the processor (PC or micro) will do this faster than the bus can go so I need to know how to time the sending, with the micro controller I used the interrupt for the transmit buffer to give that "cadence".

That sounds like a software equivalent of using monostables to define hardware timing sequences; monostables are deprecated for very solid reasons!

It sounds like your system is poorly specified. In particular it seems possible that your application is allowed to generate events faster than they can be consumed, which will always enable abnormally/unacceptably long delays due to buffering issues. Such long delays can cause new problems; "bufferbloat" is a famous comms/networking example.

Traditional generic sound avoidance techniques include
  • FSMs at each end that (1) receive an event (2) send an event back to the source indicating that it has been processed (3) so the source knows that it can send another event.
  • blocking queues, but that will hang the event source

In the first case it may be useful to split events/messages into two categories which are processed independently by the receiver (maybe two separate FSMs):
  • normal application events that that should be processed in order and which could overwhelm the system. Use technique (1) above, or have a separate blocking queue as per (2)
  • infrequent supervisory and/or high priority events, which can bypass the normal application event queue
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline tggzzz

  • Super Contributor
  • ***
  • Posts: 23122
  • Country: gb
  • Numbers, not adjectives
    • Having fun doing more, with less
Re: GUI with gauges in python
« Reply #159 on: November 13, 2025, 02:06:25 pm »
Direct state machines tend to be avoided or abstracted in such a way as to not require such concrete transitions.

Most modern architectures based around message buses don't have ANY STATE.  So they cannot hold a state machine.  In fact a state machine in such an architecture outside of the middle ware itself would be forbidden.
...

You make good points, but...

Message bus middleware has a very regrettable tendency to presume "ideal operation" of distributed systems. Convenient when it works, convenient for fast shipping "it works because the unit test light is green", but prone to "random" "subtle" "unreproducible" failures in the field.

Soundly engineered distributed systems should not ignore important failure mechanisms, e.g. subtly impaired behaviour at one end, Byzantine Generals problems, non-ACID behaviour, message reordering, and similar.

In every non-trivial application I have ever seen there has been state in the end application. It may not be explicit, but it is always there. Not making it explicit is a frequent cause of problems found in deployed systems, especially when maintained over several years by different people.

Yes, FSMs can have deadlock and livelock - but that is a consequence of the application. At least FSMs make it very visible when and how deadlock/livelock has occurred, and don't encourage more subtle system failures.
« Last Edit: November 13, 2025, 02:08:00 pm by tggzzz »
There are lies, damned lies, statistics - and ADC/DAC specs.
Glider pilot's aphorism: "there is no substitute for span". Retort: "There is a substitute: skill+imagination. But you can buy span".
Having fun doing more, with less
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 6582
  • Country: gb
Re: GUI with gauges in python
« Reply #160 on: November 13, 2025, 02:16:52 pm »
One thing I wish I could know is if the transmit buffer is full. In micro controller land when I get to the time I need to send messages to every node I do this from the send buffer empty interrupt, this way I just turn it on and it fires, the buffer is then loaded up and once near full I note where I got to and leave. Once the buffer empty interrupt fires again I go again, once all messages are done I disable the interrupt again. So simple and efficient, how do I replicate that with a PC?

You have LOTS of memory and processing capabilities.  You are not restrained in anywhere near the same level.

However.  The answer to your question depends a lot on how you are going to set your architecture up.  It also depends heavily on what python-can provides you with as a base layer.

The odd thing for me here is that you are interfacing with a bus, but you are not using it for your own purposes relating to the python code you are writing.

To explain.  I would split the problem into two parts.  That which raises the broadcast and that which carries it out.  So I would "publish" the broadcast to a appropriately identified "topic" and have another listener receive that message and do the iteration or batch integration if the underlying supports it.

However, in it's simplest form:

You will have a "while True" somewhere in your code to hold the main thread active.  In that simple put:

if not messageQueue.isEmpty():
    next_message = messageQueue.get()
    bus.send(next_message)

Then in your other code just take a reference to messageQueue and "put()" the message you want to send.

queue.SimpleQueue will give you a FIFO.

However there are Prioritisation queues and other non-FIFO like queues.

I had a college argue black and white with me that "A queue is a FIFO".  I argued that no, A FIFO is a queue.  But a queue is not FIFO.  Not necessarily.
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 6582
  • Country: gb
Re: GUI with gauges in python
« Reply #161 on: November 13, 2025, 02:27:52 pm »
Direct state machines tend to be avoided or abstracted in such a way as to not require such concrete transitions.

Most modern architectures based around message buses don't have ANY STATE.  So they cannot hold a state machine.  In fact a state machine in such an architecture outside of the middle ware itself would be forbidden.
...

You make good points, but...

Message bus middleware has a very regrettable tendency to presume "ideal operation" of distributed systems. Convenient when it works, convenient for fast shipping "it works because the unit test light is green", but prone to "random" "subtle" "unreproducible" failures in the field.

Soundly engineered distributed systems should not ignore important failure mechanisms, e.g. subtly impaired behaviour at one end, Byzantine Generals problems, non-ACID behaviour, message reordering, and similar.

In every non-trivial application I have ever seen there has been state in the end application. It may not be explicit, but it is always there. Not making it explicit is a frequent cause of problems found in deployed systems, especially when maintained over several years by different people.

Yes, FSMs can have deadlock and livelock - but that is a consequence of the application. At least FSMs make it very visible when and how deadlock/livelock has occurred, and don't encourage more subtle system failures.

Here is the reality that most MCU types simply forget.  Volume.  The amounts of data, conditions, processes, types, subtypes, outliers and use-cases that enterprise software supports would cover 100s or 1000s of MCUs in complexity as a single component.  This is not the same a "bloat".  This is complexity and multiplicity madated by the business processes and data and requirements.

This means you need a lot of people to do it in any sensible period of time.  In an ideal world you could go and source yourself 100 top tier distributed engineers and they would produce a master piece that never crashed out.

The reality is VERY different though.  So the tendency is to not do that.  Instead we use existing frameworks, middleware, DBRMS, message buses, key-value dbs and all manor of "pre-build", "pre-tested" components written by a small subset of highly skilled engineers that understand that level of detail.

The average enterprise experience and skill distribution is very, very pear shaped.  The vast majority of coders are basically useless.  If you gave them a distributed state machine problem like a Singleton collection in a cluster, they will 100% screw it up.

So we don't give it to them  We don't even do it ourselves as principles.   We don't want the testing effort for one.  We don't want the headaches for two.  We make it someone elses problem.

The modern industry is not about writing good code it's about avoiding having to write any code at all, god forbid fiddly concurrency and distribution code.

Could you or I have a good stab at it, of course.  Would you trust the "generic team member majority"?  Hell no.  If you have 6 months to do the full system end to end, you wiil realise that doing the middleware for it is 6 months on it's own.  So go use Kafka and even add a higher level pattern/framework on top, like "PubSub".

It is that distributed part that causes the issues.  When everything ran in compiled time coupled monoliths it wasn't an issue.  "now is now".  Once you go distributed the problem multipies in complexity when "now" is really "then".  I watched a video recently that pointed out, if you were to stop time and examine the light on the transatlantic fibre you might find a dozen of your packets at a point along it.  Tracing back to the source you will find more.  These packets exist in a time/space location across thousands of miles and across micro-seconds and nano-seconds.  Causality and relativity abound.  It was the best way I can think of to really hit home the problem of distributed systems.  At which point can you declare "state is consistent"?  Consistent with what?  Which point in time?  None of the nodes exist in the same time context.  None of them can even agree on what time it is because of the speed of light and networking delays, the later not being consistent either.  Due to dynamnic routing the total distance is also variable.

On that note.  For the OP.  Some advice.  Do not leave "time" to chance and "it will be alright on the night".  Think carefully about it and decide if you want a "master clock" or if that should be driven by the CANBus itself or just a free-for-all of devices themselves.  Remembering that if a device sends you a timestamp, it's clock could be out of sync.

I took the rather bruttle method of publishing a master clock at 1Hz. ALL services by default subscribe that that heart beat and some use it for scheduling.  However, every service shares the same second.  All messages that don't have a timstamp on them already, get one off the master clock.
« Last Edit: November 13, 2025, 02:40:32 pm by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 6582
  • Country: gb
Re: GUI with gauges in python
« Reply #162 on: November 13, 2025, 02:52:04 pm »
On buffers and buffering in python side, we usually don't bother.  Just allocate it all at once and let the OS manage the actual mapping of pages to RAM.  You have plenty of it.

Where you need to deal with buffers usually occurs where you are integrating with lower level systems directly.  Such as bare TCP sockets or bare file handling.  They still have the classical buffer based lower levels like:

actualWritten = write( byteArray, offset, length )

and so on.

Usually these are wrapped in higher level calls that handle the buffering.  For example, you can just write a 50kb data blob to a TCP socket and it will automatically empty the 50kb data into multiple segments/packets for you.  Though you would need to check if it actually flushes and closes it too.

Where it HAS caught me out before is that on reading from streams, python will not auto detect a partial read if you just call "read()".  You get what is in the buffer "now" only.  With the lower levels you still need to detect the end of stream spread across multiple reads.

Python is like that.  So many things are abstracted out of sight that occasionally something crops up and you are like "wait, what?  I have to do that myself?  How quaint".
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #163 on: November 13, 2025, 08:11:06 pm »
So if I just keep chucking messages at the bus will the can module deal with that? The only time I have hit a buffer full error so far was when I was transmitting to a bus with no other device on it. If the module creates a sufficient size buffer of it's own it should not be a problem or I suppose I just check for the error message that tells me the buffer is full.

So now that I have a working CAN bus what GUI's will play nice? tkinter says that is will not work with asyncio I don't know how exactly the CAN module is doing it's threading but I saw some mention of asyncio. Given that setting up the listeners and notifiers can be done before calling the GUI in will this have much bearing on the GUI?
« Last Edit: November 13, 2025, 08:14:01 pm by Simon »
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #164 on: November 13, 2025, 09:19:28 pm »
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 6582
  • Country: gb
Re: GUI with gauges in python
« Reply #165 on: November 14, 2025, 10:55:45 am »
So if I just keep chucking messages at the bus will the can module deal with that? The only time I have hit a buffer full error so far was when I was transmitting to a bus with no other device on it. If the module creates a sufficient size buffer of it's own it should not be a problem or I suppose I just check for the error message that tells me the buffer is full.

Allocating a 100Mb buffer is not excessive PC land.  Usually memory is abstracted so you generally don't even specify a size on a buffer.  You just let it grow and worry if it gets too big.  Lag will be the problem.

To give you an idea... I had a server drop off line one night.  The upstream message bus publisher queued all the messages.  When I fixed it 10 hours later, it replayed ALL of them in seqeunce.  3.6million of them.

Of course there may be CAN timing aspects that will trip you up.
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 6582
  • Country: gb
Re: GUI with gauges in python
« Reply #166 on: November 14, 2025, 11:00:12 am »
GUIs.  Just beware.  GUIs are the biggest time sink in software.  They are fiddly and even with good GUI tools tedious.  There is a reason a lot of software just abandoned desktop GUIs and went to serverside Web app.  Even there it's not easy, just not bound by the infinite number of user platform variations.

This is how much I love writing GUIs.  This is the best UI I have for my home automation systems.  It's MqttExplorer :)

2697257-0
« Last Edit: November 14, 2025, 11:02:55 am by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #167 on: November 14, 2025, 11:19:12 am »
Yea unfortunately in the long run I will need to produce a GUI that looks "sexy" at least as far as "sexy" is in our industry.
 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 6582
  • Country: gb
Re: GUI with gauges in python
« Reply #168 on: November 14, 2025, 12:27:20 pm »
I'm still trying to work out your core use-cases.

I am going to assume you have a bunch of selected devices which communicate via CANBus.  You want a GUI where you can monitor these, but also edit parameters which will result in messages being sent to devices.

How many types of device?  Is it a collection of similar devices or a hodge podge?

My tendency is to think in terms of "model view controller".  The 10,000ft'er of that is.  The device is the model, or the state which you collect from the device on the backend.  The views are your GUI "frames" or "widgets" with the data or input boxes.  The controller is the code which handles requests, selects the view and provides it with the data from the model.

You could have a "DeviceAStatusView" which is just a GUI list item showing a green or red LED beside the device name/ID.  Additionally you can have a "DetailsView" if you double click that list entry.  In the DetailsView there is a button to switch to the "EditView".

Tricky part.  Not all GUI frameworks use this model or support it.  So it's really a matter of if it fits with the GUI framework you pick.

"In memory" or "In address space" it's quite easy.  The UI requests stuff by method call on a "Facade" presented by the backend for it's use:

backend.allDeviceSummaries()

Might return a list of Device data 'structs' and the UI then populates the summary list view.  When you double click, the UI pops open the details frame and sends:

backend.getDeviceDetails( deviceID )

Doing it across address spaces, from individual components the extra bit if abstracting those method calls to be remote, via any one of a dozen different ways up to enterprise middleware.

"Pull" mechanics are simple.  However for "dynamic realtime" guis, you need "push".  This is done by facilitating the backend a way to send "Update" messages to the View itself.  So when data changes the UI component is notified directly with new data.  When you have a busy UI showing dozens of things it's facinating to watch it update live.  Causes a ton of load though.

Example flow there would be:
view = GUISummaryWidgetX()
backend.assignDeviceSummaryView( view )

Then the backend will call "update(DeviceSummaryList)" everytime the data changes.
« Last Edit: November 14, 2025, 12:33:10 pm by paulca »
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #169 on: November 14, 2025, 02:29:52 pm »
It's pretty simple and again governed by the CAN Open communication standard.

Each device no matter what it is has an "object dictionary" which is basically a bunch of variables (in a class object). You can request to read from or write to a slave device by SDO protocol. A message is sent stating the object's index of interest and any data to write. The slave device responds with the data requested or a confirmation of the write request outcome. Until the slave responds no further requests should be sent to that slave device. This is used rarely though.

With SDO messages on boot up you set up the PDO messages, there are up to 4 of these per device for sending data to the master and 4 for receiving commands or data from the master. These operate like J1939 messages. If the default "mapping" of the slave is not suitable you set up the objects you want to send values to packing them into the RPDO message space and the objects you want to receive from the slave device packing those into the TPDO message space. Now that both master and slaves know what the PDO messages shall contain these are simply transmitted at regular intervals.

A sync message is sent and this usually coordinates the sending of the PDO messages.

Each slave transmits a heartbeat message so that the master knows they are still online, this messages also tells the master of the state the slave is in.

So this nicely sorts out the exchange of data and commands on the bus. And yes there is a CAN Open module but I'll look at that later as it's easy for me to write my own CAN Open stuff but harder to sort the GUI out. The device will therefore have libraries of data (class objects) that the GUI can pull data from or puts commands into.

So in the case of a motor driver one of the objects in the PDO message is the control-word for the power up of the drive which in turn will send back in a PDO message the status-word. Speed and toque demand objects are sent within PDO messages and the drive will have the actual speed and actual torque objects in it's PDO messages. These PDO messages are sent at regular intervals so I update the command object values at my pleasure and know that the message will be sent at an interval and when I want to see what the motor drive is doing I simply read the object/variable for what I want to know/display conscious that it is being updated at an interval.


 

Offline paulca

  • Super Contributor
  • ***
  • Posts: 6582
  • Country: gb
Re: GUI with gauges in python
« Reply #170 on: November 14, 2025, 02:53:55 pm »
If your application is, for the vast majority "just" a GUI for this CANBus, you could just consider it a single component and put UI and Backend in the same python process.  The backend is just the CAN access layer.

A cache might be worth considering.  The event-driven model has a tendency to leave you "single message scoped".  Adding a cache of the last N messages per device/topic can help when you want to display or act on multiple data not found in the single message you are handling right now.

A cache can just be a big "dict()" object (dict() is Nested Dictionary, Map, Assocative array).  Worry about concurrency issues if you find them, but python is usually okay with single updates in dicts/maps etc.

If your application wants to handle logic for which it doesnt need, nor want a GUI for, then it is best to split the two such that the backend can exist without a UI, ideally "headless".
"What could possibly go wrong?"
Current Open Projects:  68000 Self Build computer + OS.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #171 on: November 14, 2025, 06:29:46 pm »
The way I handle can open messages automatically updates the device dictionary + profile class object in the receive handler.

What I am confused with now is that I can't install stuff in my virtual environment. It's getting ridiculous.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #172 on: November 15, 2025, 10:40:05 am »
Looks like PySide2 does not work on python 3.11 which is what my environment installed. I can't get ImGUI to work either the very basic example they give apparently lacks the code to create the window they say should appear....

Maybe I should use tkinter for now.
« Last Edit: November 15, 2025, 11:38:17 am by Simon »
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #173 on: November 16, 2025, 05:00:58 pm »
Well I managed to put a value on the screen with tkinter that came from the CAN bus. Now I will try and write a program that helps me analyse the bus traffic and give me data, statistics and potential errors.
 

Online SimonTopic starter

  • Global Moderator
  • *****
  • Posts: 18919
  • Country: gb
  • Did that just blow up? No? might work after all !!
    • Simon's Electronics
Re: GUI with gauges in python
« Reply #174 on: November 18, 2025, 08:30:45 pm »
One thing for sure is that even such a simple script as the only running user application on a RPi 500+ is far from real time. I transmit sync messages every 10ms. The python scripts reports intervals from 7ms to 13ms, basically up to a 3ms delay on a regular basis that then has the next reception look like it came quicker.

I may just do that CAN/serial adapter so that I can grab the data in real time and and then communicate it at a more leisurely rate but with precise timestamps.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf

 

- - -
-->