The most straightforward solution I'd see and would generally want to use, if you have two classes which are nearly identical but only differ in how they output data, is make one class which has what is common and depend on an abstraction for the output.
Don't really see a more straightforward way than that. Of course
IOutput could be one or more function objects in C++ as opposed to an interface as one example, but same kind of basic idea. And you can use composition to inject an instance of a window with the appropriate outputter on construction which it can store as a member as opposed to subtyping windows (which would lead to more coupling). Another C++ specific alternative is to make
Window a class template and generate the two versions of the window (one plotting window, one debug window), still relying on the same basic interface/abstraction (also same basic idea).
Main trick is to design that appropriate
IOutput interface. For example, if you need to store Qt widgets for the "debug" version and add them to the window layout, you might need to pass the window's layout along to add any appropriate widgets to it, and then call an abstract
output kind of function to update the outputter's widget appropriately. Or you might optionally return a QWidget in
IOutput which the window adds to its layout. Something of this sort. You need some way to grab the widget for an outputter for the window to add to its layout through this abstraction.
And likewise these two functions:
void write_console(QString const &msg);
Need to be unified into one
output idea/signature. They become the analogical abstract
output function. So you want to pass something along which provides enough information for both outputters to do their thing: for the plotter to plot the data and for the debug version to convert it to a string and show it inside a text field.
I'd need to see a bit more code to suggest the most straightforward way to come up with the design of the analogical
IOutput. But hopefully you get the idea and can see how to do it rather quickly, and that's how I'd suggest to tackle this problem. You have the concrete window and it depends on an abstract outputter interface, and different concrete outputters can substitute the appropriate functionality required for a specific kind of output.
What is the best, cleanest way to "share" the UI elements and their
corresponding member function handlers with both modes (classes)?
Just for this general type of problem, see if you can invert the thinking a bit. Instead of thinking how you share the same redundant code in two classes that want to share most of the same code, implement the code once and think about how to create an abstraction which allows the unique parts, not shared, to diverge. Similar strategy if you are refactoring one class in hindsight. Instead of copying and pasting its code somewhere else only to change a little bit, see how you can refactor the class to avoid directly depending on concrete details for the parts that need to diverge while coming up with an abstraction to depend on instead.
It's the key to kind of arriving at these types of abstractions upfront rather than in hindsight. For a basic CS-type example, instead of implementing a quicksort for two different random-access data structures and then trying to figure out how to "share the code", implement it one time away from any specific data structure and as you are implementing, come up with the appropriate abstraction (random-access iterator concept, e.g.) that allows it to work for any conforming data structure.