How to create custom UI for a MATLAB application? - matlab

MATLAB possesses all features I need in my application, except for one - a 21st century look to the applications it builds. The old UI controls with the skin from the 90s just don't cut it anymore. Are there ways to customize the GUI of the compiled window itself? I know of App Designer for MATLAB (which is an alternative for GUIDE as I understood) but that's not close enough. For example, I want a complete flat design look with sprites/buttons/etc drawn in Photoshop for my built application.
Would an external C library/framework be able to build a custom GUI when compiling an application?
I've tried to google this question and it seems like nobody cares about design in the scientific community. I'm not quite sure how to phrase my question, apologies. I hope some of you have a bit of experience with this.
P.S.: Below is the look I'd like to achieve:

As described in my previous comment, and starting from R2019b, you can use uihtml components in new web-based uifigure (introduced in R2016a) to customize style of elements appearing on screen via css + javascript
Here below is a very simple example for a css-styled button:
First the matlab code (TestUiHtml.m):
function [] = TestUiHtml()
%[
% Create the figure and layout
fig = uifigure('Name', 'Test uihtml', 'Color', [1 1 1]);
grid = uigridlayout('Parent', fig, 'ColumnWidth', { '1x', '1x' }, 'RowHeight', {200, '1x' }, 'BackgroundColor', fig.Color);
% Add a uihtml element (that will act like a button using html+css+javascript)
button = uihtml('Parent', grid, 'HTMLSource', 'myway.html', 'DataChangedFcn', #(s,e)onBtnClicked(fig));
% Button layout
button.Layout.Row = 1; button.Layout.Column = [1 2];
%]
end
function [] = onBtnClicked(fig)
%[
uialert(fig, 'World!', 'Clicked', 'Icon', 'info');
%]
end
NB: Above code simply adds in the figure a uihtml component whose property HTMLSource points to the html content you want to have.
Here is the html code to describe the custom uihtml component content (myway.html):
<!DOCTYPE html>
<head>
<link rel="stylesheet" type="text/css" href="myway.css">
<script type="text/javascript" src="myway.js"></script>
</head>
<html>
<body>
<div class="container">
<button class="skewBtn blue" id="btn">Hello!</button>
</div>
</body>
</html>
NB: It is not mandatory to have a fully well-formed html code (i.e. just a <button style="...">Hello</button> would do).
The linked css code to style the html content (myway.css):
body {
background-color: transparent;
}
.container {
display: flex;
justify-content: space-evenly;
align-items: center;
flex-wrap: wrap;
height: 97vh;
}
.container .skewBtn {
width: 180px;
height: 80px;
outline: none;
cursor: pointer;
background: none;
transition: 0.5s;
font-size: 24px;
border-radius: 5px;
font-family: "Lato", sans-serif;
}
.container .skewBtn:hover {
border: none;
color: white;
font-size: 28px;
transform: scale(1.1);
}
.blue {
border: 2px solid #1976D2;
color: #1976D2;
}
.blue:hover {
background-color: #1976D2;
transition: all 0.3s ease-in-out;
}
And finally the javascript code for event/data marshalling between matlab and the html-based component ... or for more advanced programming logic, if any, for the html-content (myway.js):
function setup(htmlComponent)
{
document.getElementById("btn").addEventListener("click", function(event)
{
htmlComponent.Data = 1234; // dummy value to fire event on matlab side
});
}
Marshalling is ensure via the data property of a htmlComponent object which both sides (i.e. matlab/javascript) are listening to for changes. Matlab automatically uses jsonencode/jsondecode in the background so you can pass back and forth almost any structure/values you need directly.
And voilà ... a nice-and-responsive css-styled button in matlab's figure:
It would be of course better to refactor above code as a uibuttonex class modeled on existing uibutton property name/value pairs.
Sarting from R2021a it is eventually possible to add it to app designer library of components as described here (I personally don't use the designer and prefer to code interfaces programmatically).
Some remarks
It is not possible to directly style exiting uibutton, uipanel, uitab, etc... elements => Mathwork does not allow to access uifigure's html nor to inject css/javascript in it easily (see following undocumented-matlab thread and mlapptools project to try to cheat things). For instance a uibutton does not translate to directly <button> markup in uifigure's source but a lot of imbricated <div>, so it is very difficult to style (and will probably change from one release to another).
I would not recommend to create a single uihtml element in the figure and code everything in html+css+javascript, but I would rather recommend to create small and simple custom uibuttonex, etc elements ... else you'll have to deal with a single data blob to connect you interface with the matlab code which can be tricky and difficult to reuse.
Unfortunately it is yet not possible to create custom html-containers in which you can add other child ui-elements (custom or non-custom) ... say bye-bye to create containers similar to uix.GridFlex, uix.CardPanel from the wonderful GUI Layout toolbox or to creating nicely styled uitabex, etc... This is really a missing part !
Looking at source code of uifigure pages, custom ui-elements are also embedded in <iframe></iframe> markups, so it is not possible to inject some global css/javascript code (i.e. you'll have to repeat this for each uihtml element you create). It is not possible (or difficult) then to have multiple uihtml elements to interact with each-other (unless coding everything in a single page or passing through matlab code/events only).
Addendum : Here is a solution to get css-style from parent, unfortunately this does not solve one need to inject this css in the main uifigure source (well this can be done in javascript ... something like if !exist(parent.elements where id="mycss") => parent.document.inject(lalala))
Mathworks is going the good direction with uifigure and html in the background, but it's still a bit too locked for now and missing important containers from the GUI Layout toolbox.
NB: I tried the GUI Layout toolbox elements in uifigure ... some are working (with lot of resizing bugs), other are not working at all (GridFlex). Maybe a new version of the toolbox will come.
Charting
For modern charting you can use javascript libraries like D3js, SciChart, LightningChart, or others!!.
There is some example using D3 on fileexchange

Related

JSXGraph: Multiple boards with Moodle's Formulas and Stack

Sometimes it is handy to be able to control the placement of multiple JSXGraph boards on the html page, like on https://jsxgraph.org/wiki/index.php?title=Sine.
I know that it is possible to include more than one board in a Formulas question in Moodle, as described here: https://github.com/jsxgraph/moodleformulas_jsxgraph/blob/master/README.md#insert-more-than-one-board-into-a-question
My question is: Is it possible to gain control over the placement of boards in a Formulas question? And/or is that possible in a STACK question? – Thanks!
With STACK, it is possible to include more than one board into a question. For each JSXGraph block, STACK generates a div with an id stored in the divid variable. The divs for the boards then replace the JSXGraph blocks in the question text.
Board interaction
If you want the boards to interact with each other, you need to write the JavaScript code for the boards in a single JSXGraph block (the JavaScript code in a block is encapsulated). To do so, you should first place an empty JSXGraph block in the question text which will generate the first div with an id of the pattern "stack-jsxgraph-1". Then, you add a second block where you put in the code for the two boards. Since the naming for the divids is basically just counting up a number, you can access the divid for the first board with regular expressions in JavaScript.
Board placement
In order to control the placement, you can use some CSS and HTML. For example you can wrap two JSXGraph blocks in a div and make this div a flexbox with display: flex. The two boards will then align horizontally. You can also use a grid layout.
Example Code
The code for the question text could look like this:
<!-- Wrapper div flexbox -->
<div style="display: flex;">
<!-- Empty JSXGraph block for the first board-->
[[ jsxgraph width="200px" height="200px"]] [[/ jsxgraph ]]
<!-- JSXGraph block for the second board-->
[[ jsxgraph width="200px" height="200px"]]
/* divid variables */
const divid1 = divid.replace(/[0-9]+/g,divid.match(/[0-9]+/g)-1)
const divid2 = divid;
console.log(divid1, divid2);
/* init boards */
const board1 = JXG.JSXGraph.initBoard(divid1,{ boundingbox: [-5, 5, 5, -5], axis: true });
const board2 = JXG.JSXGraph.initBoard(divid2,{ boundingbox: [-5, 5, 5, -5], axis: true });
[[/ jsxgraph ]]
</div>
The STACK question then shows two boards:
stack question with two jsxgraph boards
This code creates two JSXGraph boards inside the second JSXGraph block that can interact with each other. The placement is done with a flexbox wrapper-div. I think this should also work for Formulas similarly. I hope this answers your question.
Following #bernhard759's hint to use CSS to steer the diagram's placement. The following CSS code produces a result that I like:
.jsxgraph-boards {
align-items: start;
justify-content: start;
margin: 0 0 1rem 0;
}
.jxgbox {
margin: 1rem 0 0 1rem;
}

TinyMCE - preserve style and function of selected element

I have decided to 'enhance' a textarea in a form with TinyMCE... however, doing so has interrupted the styling and jQuery functionality of the original element, as TinyMCE wraps that element in an iframe and a few divs. What I'd love to be able to do is to get the TinyMCE functionality (preserving text formatting, etc.) but not lose the styling and functions that I had associated with the original textarea. I looked through the TinyMCE documentation, but couldn't seem to find anything about this. Does anyone have any thoughts on how to accomplish this?
My form features the textarea like so:
<head>
<script>tinymce.init( { selector: 'textarea' } );</script>
</head>
<div class="form-element">
<div class="label-container">
<label for="body">Post</label><span class="warning">Please fill out this field</span>
</div>
<textarea id="body" class="input-field" name="body"></textarea>
</div>
but adding TinyMCE breaks the label/textarea relationship.
Also, jQuery functionality is broken, such as this validation script:
$("form").submit(function(e){
tinyMCE.triggerSave();
var inputFields = $(".input-field");
var proceed = true;
for(var i = 0; i < inputFields.length; i++){
if($(inputFields[i]).val() == ""){
$(inputFields[i]).css("border", "solid 3px #E86F3A");
$(inputFields[i]).prev().find(".warning").show();
var proceed = false;
e.preventDefault();
}
else{
$(inputFields[i]).css("border", "none");
$(inputFields[i]).prev().find(".warning").hide();
};
};
//OTHER STUFF...
});
since the textarea.input-field is no longer picked up in the inputFields variable.
So, in a nutshell, I'm looking for the TinyMCE wrapper to 'inherit' the styling and functionality of the element that it is attached to. Possible?
As you have correctly surmised when you invoke TinyMCE on a <textarea> the original <textarea> is no longer visible on the page and its replaced by an <iframe> and series of <div> elements.
If you want to keep the underlying <textarea> in sync you can use the tinymce.triggerSave() method. This will update all of the underlying <textarea> elements with the current value of the appropriate instance of TinyMCE.
You can do this when someone tries to save/submit the content or you can try to perform this when certain editor events happen (https://www.tinymce.com/docs/advanced/events/#editorevents). Unless you need real time accuracy of the contents of the <textarea> its far easier to call the triggerSave() method right before you perform you jQuery validation.
Note: Putting a border on the <textarea> won't have any impact on TinyMCE as you no longer see the underlying <textarea>. You can certainly try to add CSS to the editor's HTML in real time. The outer border of TinyMCE 4.4 has these classes attached:
class="mce-tinymce mce-container mce-panel"
...but do note that these classes could change over time so if you upgrade TinyMCE check to make sure your CSS still works before upgrading.

XmlWorker does not recognize styles on nested tables

I have 2 (or more) nested tables: the first is for layout purpose and the others are normal tables with data.
I use the first table because I need to repeat header and footer in each page, so I use
repeat-header:yes;
repeat-footer:yes;
css special rules to achieve this, and it works.
I put my document body into elements.
For data tables I need to give 1px border to the elements, so I created the css rules to do that:
table.data {
border-collapse: collapse;
margin-bottom: 15px;
width: 100%;
}
table.data td {
padding: 5px;
border: 1px solid #000;
border-collapse: collapse
}
and I set the class of my element. But it does not have borders.
It looks like overwrite the data style with the external table style, that doesn't have any border so I don't have borders in my internal tables.
My css is into tag and sometimes I write something inline, but nothing overwrites the 1px border.
I'm using Itext for android and XMLWorker (last version 5.4.4).
My template is HTML and I call from java following method:
XMLWorkerHelper.getInstance().parseXHtml
xmlworker css support is poor, look here:
http://demo.itextsupport.com/xmlworker/itextdoc/CSS-conformance-list.htm
The only fix I found is avoid to use css inheritance and use inline css in every html tag of nested tables.

How to set the style of GXT's Info.display?

I'm using GXT's Info class to display messages/notifications. I'm trying to change the style of my Info class using this :
NotificationInfo info = new NotificationInfo(); // extends Info class
// Configuration
InfoConfig config = new DefaultInfoConfig(title, message);
config.setDisplay(milliseconds);
// Formatting
info.setStyleName("background-color:#f9ff4f;text-align:center;" + "border:0x solid black; padding: 3px; font-size:11px;font-weight: bold;");
info.show(config);
But it's not working! Any Pointers?
Thanks.
This should work... (for GXT3 it does anyway)
info.getElement().applyStyles("background-color:#f9ff4f;text-align:center;" + "border:0x solid black; padding: 3px; font-size:11px;font-weight: bold;");
By the way, if you need to do a similar thing for a gwt widget (that you are using in your GXT app) you can do...
info.getElement().<XElement> cast().applyStyles("background-color:#f9ff4f;text-align:center;" + "border:0x solid black; padding: 3px; font-size:11px;font-weight: bold;");
NOTE: I am not sure about the merits of setting it inline like this, but setting a css style just fails (ok the style is set on element but the actual place you want to change gets rendered in a div in a td in table down the line and the div overrides your the value inherit from the css you set). Best practice is surely to go the full appearances route for GXT3 widgets but that's a lot of work for a small change and so I do use the above sometimes.
Applying styles is only a quick solution and not the best, because GWT CssResource has few advantages than direct CSS applying like minification, validation. As mentioned in another answer the best approach is to create an appearance for including your styles and replace the appearance of the NotificationInfo with your one. I have previously answered to a similar question. Think it will help. https://stackoverflow.com/a/15222404/1293087

Split html string into multiple pages

I am trying to develop eBook reader kind of android application. I need to split long html string(sent by server at run-time) into pages on fly based on the screen space available. Html content is an article which could contain text, images, videos etc.I am using WebView to display the html.
Can anyone give me direction towards how this can be achieved.
Thanks in advance.
Doing any sort of HTML/DOM parsing is going to drive you up the wall, I think, and means that you're effectively starting to develop your own HTML layout engine.
It's a better idea to use the CSS3 column functions. Basically, get your content to render within a fixed width-and-height column. This becomes your page. Then shift your content position left to move between pages, and wrap it in a container that will hide overflow elements.
Our HTML will basically be:
<body>
<div id="container">
<div id="content">
CONTENT GOES HERE
<span id="endMarker"></span>
</div>
</div>
<div>
<button id="previous">Previous</button>
<span id="page">Page N of M</span>
<button id="next">Next</button>
</div>
</body>
Our basic CSS is:
#container {
width: 240px;
overflow: hidden;
background-color: yellow;
}
#content {
position: relative;
height: 30em;
-moz-column-width: 240px;
-webkit-column-width: 240px;
column-width: 240px;
-moz-column-gap: 10px;
-webkit-column-gap: 10px;
column-gap: 10px;
}
Now we will set the left CSS property for the #content to switch between pages, in multiples of -250px.
We've only got to work out how many columns our content takes, and we've got paging. How to get css3 multi-column count in Javascript gives a hint: we're going to find it from the left position of #endMarker.
Here's a working example http://lateral.co.za/pages.html, with the first chapter of Moby Dick. It works in Chrome and on Android, but not in Firefox - I think because of differences in the CSS columns implementations. Since we're interested in Android here, the code is good.
The important parts are:
The CSS settings as above.
The addition of a <span id="endMarker"></span> after the content (but within the #content div)
The addition of a #previous and #next button, and a #page span, all outside the #container.
This javascript after jQuery loads:
var _column = 0;
var _columnCount = 0;
var _columnWidth = 240;
var _columnGap = 10;
$(function() {
_columnCount = Math.floor($('#endMarker').position().left/(_columnWidth + _columnGap));
setColumn = function(i) {
_column = i;
document.getElementById('content').style.left = -1 * _column * (_columnWidth + _columnGap);
$('#page').html('Page ' + (_column+1) + ' of ' + (_columnCount+1));
}
$('#next').click(function() {
if (_column==_columnCount) return;
setColumn(_column+1);
});
$('#previous').click(function() {
if (0==_column) return;
setColumn(_column-1);
});
setColumn(0);
});
That's it.
There's room for work. One might want to think about that number of columns calculation, I sort of sucked it from the referenced SO post, but haven't actually thought it through... The width of the container seems to affect the column width of the content, which doesn't entirely make sense to me.
But at least everything seems to be working without having to do any HTML parsing and own layout, at least in Chrome and on Android.
You have to parse the HTML, best would be to use some library where you can access the DOM like in JavaScript. Then you can create a custom layout for the parsed content. Does WebView supports JavaScript? That would be a good start to try with.
Obviously you can not split at arbitrary locations of the HTML file, your have to consider the HTML-Tags. After modifying the DOM or splitting the html you can provide an custom CSS file for the content to display it in a way you like and add some pagination using your library or JavaScript.
Using <span style="display:none"></span> can help you to hide content in a website. Then you don't have to split is physically (it is in memory after loading the page anyway).
Hope that helped a bit. You will not get a full solution here, but maybe you got some ideas. If you find further more specific problems it will be easier to ask more specific questions.