📄 uiinspect.m
字号:
propsTable = get(src,'userdata');
ud = get(propsTable, 'userdata');
obj = ud.obj;
inspectorTable = ud.inspectorTable;
[propData, propHeaders] = getPropsData(obj, ud.cbMetaData.isSelected, ud.cbInspected.isSelected, inspectorTable);
propsTableModel = javax.swing.table.DefaultTableModel(propData,propHeaders);
try
ud.obj = handle(obj,'CallbackProperties');
catch
try
ud.obj = handle(obj);
catch
% never mind...
end
end
ud.inspectorTable = inspectorTable;
set(propsTableModel, 'TableChangedCallback',@tbPropChanged, 'UserData',ud);
propsTable.setModel(propsTableModel)
try
% Try to auto-resize the columns
propsTable.setRowAutoResizes(true);
jideTableUtils = eval('com.jidesoft.grid.TableUtils;'); % prevent JIDE alert by run-time (not load-time) evaluation
jideTableUtils.autoResizeAllColumns(propsTable);
catch
% JIDE is probably unavailable - never mind...
end
% Update the header label
if ud.cbInspected.isSelected
set(ud.othersLabel,'Text',' All properties', 'ToolTipText','All properties (including those shown above)');
else
set(ud.othersLabel,'Text',' Other properties', 'ToolTipText','Properties not inspectable by the inspect table above');
end
% Disable editing all columns except the property Value
import javax.swing.*
propTextField = JTextField;
propTextField.setEditable(false); % ensure that the prop names & meta-data are not modified...
propCellEditor = DefaultCellEditor(propTextField);
propCellEditor.setClickCountToStart(intmax); % i.e, never enter edit mode...
for colIdx = 0 : propsTable.getColumnModel.getColumnCount-1
thisColumn = propsTable.getColumnModel.getColumn(colIdx);
if ~strcmp(thisColumn.getHeaderValue,'Value')
thisColumn.setCellEditor(propCellEditor);
end
end
catch
% Never mind...
disp(lasterr); rethrow(lasterror)
end
%end % updatePropsTable
%% Update component callback upon callbacksTable data change
function tbCallbacksChanged(src, evd)
try
% exit if invalid handle or already in Callback
if ~ishandle(src) || ~isempty(getappdata(src,'inCallback')) % || length(dbstack)>1 %exit also if not called from user action
return;
end
setappdata(src,'inCallback',1); % used to prevent endless recursion
% Update the object's callback with the modified value
modifiedColIdx = evd.getColumn;
modifiedRowIdx = evd.getFirstRow;
if modifiedColIdx==1 && modifiedRowIdx>=0 %sanity check - should always be true
table = evd.getSource;
object = get(src,'userdata');
cbName = strtrim(table.getValueAt(modifiedRowIdx,0));
try
cbValue = strtrim(char(table.getValueAt(modifiedRowIdx,1)));
if ~isempty(cbValue) && ismember(cbValue(1),'{[@''')
cbValue = eval(cbValue);
end
if (~ischar(cbValue) && ~isa(cbValue, 'function_handle') && (iscom(object(1)) || iscell(cbValue)))
revertCbTableModification(table, modifiedRowIdx, modifiedColIdx, cbName, object, '');
else
for objIdx = 1 : length(object)
if ~iscom(object(objIdx))
set(object(objIdx), cbName, cbValue);
else
cbs = object(objIdx).eventlisteners;
if ~isempty(cbs)
cbs = cbs(strcmpi(cbs(:,1),cbName),:);
object(objIdx).unregisterevent(cbs);
end
if ~isempty(cbValue)
object(objIdx).registerevent({cbName, cbValue});
end
end
end
end
catch
revertCbTableModification(table, modifiedRowIdx, modifiedColIdx, cbName, object, lasterr)
end
end
catch
% never mind...
end
setappdata(src,'inCallback',[]); % used to prevent endless recursion
%end % tbCallbacksChanged
%% Revert Callback table modification
function revertCbTableModification(table, modifiedRowIdx, modifiedColIdx, cbName, object, errMsg) %#ok
try
% Display a notification MsgBox
msg = 'Callbacks must be a ''string'', or a @function handle';
if ~iscom(object(1)), msg = [msg ' or a {@func,args...} construct']; end
if ~isempty(errMsg), msg = {errMsg, '', msg}; end
msgbox(msg, ['Error setting ' cbName ' callback'], 'warn');
% Revert to the current value
curValue = '';
try
if ~iscom(object(1))
curValue = charizeData(get(object(1),cbName));
else
cbs = object(1).eventlisteners;
if ~isempty(cbs)
cbs = cbs(strcmpi(cbs(:,1),cbName),:);
curValue = charizeData(cbs(1,2));
end
end
catch
% never mind... - clear the current value
end
table.setValueAt(curValue, modifiedRowIdx, modifiedColIdx);
catch
% never mind...
end
%end % revertCbTableModification
%% Update component property upon properties table data change
function tbPropChanged(src, evd)
% exit if invalid handle
if ~ishandle(src) % || length(dbstack)>1 %exit also if not called from user action
return;
end
% Update the object's property with the modified value
modifiedColIdx = evd.getColumn;
modifiedRowIdx = evd.getFirstRow;
if modifiedRowIdx>=0 %sanity check - should always be true
table = evd.getSource;
ud = get(src,'userdata');
object = ud.obj;
inspectorTable = ud.inspectorTable;
propName = strtrim(table.getValueAt(modifiedRowIdx,0));
propName = strrep(propName,'<html><font color="#C0C0C0"><i>','');
propName = strrep(propName,'<html><font color="red"><i>','');
try
propValue = strtrim(table.getValueAt(modifiedRowIdx,modifiedColIdx));
if ~isempty(propValue) && ismember(propValue(1),'{[@''')
propValue = eval(propValue);
end
for objIdx = 1 : length(object)
set(object(objIdx), propName, propValue);
end
catch
msg = {lasterr, '', ...
'Values are interpreted as strings except if enclosed by square brackets [] or curly braces {}', ...
'', 'Even simple boolean/numeric values need to be enclosed within [] brackets', ...
'For example: [0] or: [pi]'};
msgbox(msg,['Error setting ' propName ' property'],'error');
try
% Revert to the current value (temporarily disable this callback to prevent recursion)
curValue = charizeData(get(object(1),propName));
set(table, 'TableChangedCallback',[]);
table.setValueAt(curValue, modifiedRowIdx, modifiedColIdx);
set(table, 'TableChangedCallback',@tbPropChanged);
catch
% never mind...
end
end
%pause(0.2); awtinvoke(inspectorTable,'repaint(J)',2000); % not good enough...
start(timer('TimerFcn',{@repaintInspector,inspectorTable},'StartDelay',2));
end
%end % tbPropChanged
%% Repaint inspectorTable following a property modification
function repaintInspector(timerObj, timerData, inspectorTable) %#ok partially unused
inspectorTable.repaint;
%end % repaintInspector
%% Get an HTML representation of the object's properties
function dataFieldsStr = getPropsHtml(obj, dataFields)
try
% Get a text representation of the fieldnames & values
undefinedStr = '';
dataFieldsStr = ''; % just in case the following croaks...
if isempty(dataFields)
return;
end
dataFieldsStr = evalc('disp(dataFields)');
if dataFieldsStr(end)==char(10), dataFieldsStr=dataFieldsStr(1:end-1); end
% Strip out callbacks
dataFieldsStr = regexprep(dataFieldsStr,'^\s*\w*Callback(Data)?:[^\n]*$','','lineanchors');
dataFieldsStr = regexprep(dataFieldsStr,'\n\n','\n');
% HTMLize tooltip data
% First, set the fields' font based on its read-write status
try
% ensure this is a Matlab handle, not a java object
obj = handle(obj, 'CallbackProperties');
catch
% HG handles don't allow CallbackProperties...
obj = handle(obj);
end
fieldNames = fieldnames(dataFields);
for fieldIdx = 1 : length(fieldNames)
thisFieldName = fieldNames{fieldIdx};
accessFlags = get(findprop(obj,thisFieldName),'AccessFlags');
if isfield(accessFlags,'PublicSet') && strcmp(accessFlags.PublicSet,'on')
% Bolden read/write fields
thisFieldFormat = ['<b>' thisFieldName '<b>:$2'];
elseif ~isfield(accessFlags,'PublicSet')
% Undefined - probably a Matlab-defined field of com.mathworks.hg.peer.FigureFrameProxy...
thisFieldFormat = ['<font color="blue">' thisFieldName '</font>:$2'];
undefinedStr = ', <font color="blue">undefined</font>';
else % PublicSet=='off'
% Gray-out & italicize any read-only fields
thisFieldFormat = ['<font color="#C0C0C0"><i>' thisFieldName '</i></font>:<font color="#C0C0C0"><i>$2<i></font>'];
end
dataFieldsStr = regexprep(dataFieldsStr, ['([\s\n])' thisFieldName ':([^\n]*)'], ['$1' thisFieldFormat]);
end
catch
% never mind... - probably an ambiguous property name
disp(lasterr); rethrow(lasterror)
end
try
% Method 1: simple <br> list
%dataFieldsStr = strrep(dataFieldsStr,char(10),' <br> ');
% Method 2: 2x2-column <table>
dataFieldsStr = regexprep(dataFieldsStr, '^\s*([^:]+:)([^\n]*)\n^\s*([^:]+:)([^\n]*)$', '<tr><td> $1</td><td> $2</td><td> $3</td><td> $4 </td></tr>', 'lineanchors');
dataFieldsStr = regexprep(dataFieldsStr, '^[^<]\s*([^:]+:)([^\n]*)$', '<tr><td> $1</td><td> $2</td><td> </td><td> </td></tr>', 'lineanchors');
dataFieldsStr = ['(<b>modifiable</b>' undefinedStr ' & <font color="#C0C0C0"><i>read-only</i></font> fields)<p> <table cellpadding="0" cellspacing="0">' dataFieldsStr '</table>'];
catch
% never mind - bail out (Maybe matlab 6 that does not support regexprep?)
disp(lasterr); rethrow(lasterror)
end
%end % getPropsHtml
%% Update tooltip string with an object's properties data
function dataFields = updateObjTooltip(obj, uiObject)
try
if ischar(obj)
toolTipStr = obj;
else
toolTipStr = builtin('class',obj);
end
dataFields = struct; % empty struct
dataFieldsStr = '';
hgStr = '';
% Add HG annotation if relevant
if ishghandle(obj)
hgStr = ' HG Handle';
end
% Note: don't bulk-get because (1) not all properties are returned & (2) some properties cause a Java exception
% Note2: the classhandle approach does not enable access to user-defined schema.props
ch = classhandle(handle(obj));
dataFields = [];
[sortedNames, sortedIdx] = sort(get(ch.Properties,'Name'));
for idx = 1 : length(sortedIdx)
sp = ch.Properties(sortedIdx(idx));
% TODO: some fields (see EOL comment below) generate a Java Exception from: com.mathworks.mlwidgets.inspector.PropertyRootNode$PropertyListener$1$1.run
if strcmp(sp.AccessFlags.PublicGet,'on') % && ~any(strcmp(sp.Name,{'FixedColors','ListboxTop','Extent'}))
try
dataFields.(sp.Name) = get(obj, sp.Name);
catch
dataFields.(sp.Name) = '<font color="red">Error!</font>';
end
else
dataFields.(sp.Name) = '(no public getter method)';
end
end
dataFieldsStr = getPropsHtml(obj, dataFields);
catch
% Probably a non-HG java object
try
% Note: the bulk-get approach enables access to user-defined schema-props, but not to some original classhandle Properties...
dataFields = get(obj);
dataFieldsStr = getPropsHtml(obj, dataFields);
catch
% Probably a missing property getter implementation
try
% Inform the user - bail out on error
err = lasterror;
if ~ischar(obj)
dataFieldsStr = ['<p>' strrep(err.message, char(10), '<br>')];
else
dataFieldsStr = '<p>Cannot inspect fields of class names - only of objects';
end
catch
% forget it...
end
end
end
% Set the object tooltip
if ~isempty(dataFieldsStr)
toolTipStr = ['<html> <b><u><font color="red">' char(toolTipStr) '</font></u></b>' hgStr ': ' dataFieldsStr '</html>'];
end
uiObject.setToolTipText(toolTipStr);
%end % updateObjTooltip
%%%%%%%%%%%%%%%%%%%%%%%%%% TODO %%%%%%%%%%%%%%%%%%%%%%%%%
% - Enh: Cleanup internal functions, remove duplicates etc.
% - Enh: link objects to another uiinspect window for these objects
% - Enh: display object children (& link to them)
% - Enh: find a way to merge the other-properties table into the inspector table
% - Fix: some fields generate a Java Exception from: com.mathworks.mlwidgets.inspector.PropertyRootNode$PropertyListener$1$1.run
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -