/* * Project Name : Visual Python * Description : GUI-based Python code generator * File Name : Chart.js * Author : Black Logic * Note : Apps > Chart * License : GNU GPLv3 with Visual Python special exception * Date : 2021. 11. 18 * Change Date : */ //============================================================================ // [CLASS] Chart //============================================================================ define([ 'text!vp_base/html/m_visualize/chart.html!strip', 'css!vp_base/css/m_visualize/chart.css', 'vp_base/js/com/com_String', 'vp_base/js/com/com_Const', 'vp_base/js/com/com_util', 'vp_base/js/com/com_generator', 'vp_base/js/com/component/PopupComponent', 'vp_base/js/com/component/FileNavigation', 'vp_base/js/com/component/SuggestInput', 'vp_base/js/com/component/DataSelector' ], function(chartHTml, chartCss, com_String, com_Const, com_util, com_generator, PopupComponent, FileNavigation, SuggestInput, DataSelector) { /** * Chart */ class Chart extends PopupComponent { _init() { super._init(); /** Write codes executed before rendering */ this.config.dataview = false; this.config.sizeLevel = 2; this.config.checkModules = ['plt']; this.setDefaultVariables(); this.state = { kind: 'plot', ...this.state } this.package = this.plotPackage[this.state.kind]; } _bindEvent() { super._bindEvent(); let that = this; } bindEventAfterRender() { let that = this; // show option based on chart type $(this.wrapSelector('#vp_plotKind .vp-plot-item')).click(function() { // plot item $(this).parent().find('.vp-plot-item').removeClass('selected'); $(this).addClass('selected'); // load selected kind var kind = $(this).data('kind'); $(that.wrapSelector('#kind')).val(kind).prop('selected', true); var thisPackage = { ...that.plotPackage[kind] }; if (thisPackage == undefined) thisPackage = that.plotPackage['plot']; // hide all (without chart type, variable) $(that.wrapSelector('table.vp-plot-setting-table tr:not(:last)')).hide(); // show selected chart type's option page thisPackage.input && thisPackage.input.forEach(obj => { $(that.wrapSelector('#' + obj.name)).closest('tr').show(); var label = obj.label; if (label != undefined) { $(that.wrapSelector('#' + obj.name)).closest('tr').find('th').removeClass('vp-orange-text'); if (obj.required != false) { // label = "* " + obj.label; $(that.wrapSelector('#' + obj.name)).closest('tr').find('th').addClass('vp-orange-text'); $(that.wrapSelector('#' + obj.name)).attr({'required': true}); } else { $(that.wrapSelector('#' + obj.name)).attr({'required': false}); } // $(that.wrapSelector("label[for='" + obj.name + "']")).text(label); $(that.wrapSelector('#' + obj.name)).closest('tr').find('th').text(label); } }); thisPackage.variable && thisPackage.variable.forEach(obj => { $(that.wrapSelector('#' + obj.name)).closest('tr').show(); }); thisPackage.input = thisPackage.input.concat(that.addOption); that.package = thisPackage; }); // use color $(this.wrapSelector('#useColor')).change(function() { var checked = $(this).prop('checked'); if (checked == true) { // enable color selector $(that.wrapSelector('#color')).removeAttr('disabled'); } else { $(that.wrapSelector('#color')).attr('disabled', 'true'); } }) // marker selector $(this.wrapSelector('#markerSelector')).change(function() { var selected = $(this).val(); if (selected == "marker") { $(this).parent().find('#marker').show(); $(this).parent().find('#marker').val(''); } else { $(this).parent().find('#marker').hide(); $(this).parent().find('#marker').val(selected); } }); // select cmap $(this.wrapSelector('.vp-plot-cmap-wrapper')).click(function() { $(this).toggleClass('open'); }); // open file navigation $(this.wrapSelector('#vp_openFileNavigationBtn')).click(function() { let fileNavi = new FileNavigation({ type: 'save', extensions: ['png'], finish: function(filesPath, status, error) { if (filesPath.length > 0) { let { file, path } = filesPath[0]; $(that.wrapSelector('#savefigpath')).val(path); $(that.wrapSelector('#fileName')).val(file); } } }); fileNavi.open(); }); // xlimit $(this.wrapSelector('#xlimit_min')).change(function() { let xlim_min = $(that.wrapSelector('#xlimit_min')).val(); let xlim_max = $(that.wrapSelector('#xlimit_max')).val(); $(that.wrapSelector('#xlim')).val(com_util.formatString('({0}, {1})', xlim_min, xlim_max)); }); $(this.wrapSelector('#xlimit_max')).change(function() { let xlim_min = $(that.wrapSelector('#xlimit_min')).val(); let xlim_max = $(that.wrapSelector('#xlimit_max')).val(); $(that.wrapSelector('#xlim')).val(com_util.formatString('({0}, {1})', xlim_min, xlim_max)); }); $(this.wrapSelector('#ylimit_min')).change(function() { let ylim_min = $(that.wrapSelector('#ylimit_min')).val(); let ylim_max = $(that.wrapSelector('#ylimit_max')).val(); $(that.wrapSelector('#ylim')).val(com_util.formatString('({0}, {1})', ylim_min, ylim_max)); }); $(this.wrapSelector('#ylimit_max')).change(function() { let ylim_min = $(that.wrapSelector('#ylimit_min')).val(); let ylim_max = $(that.wrapSelector('#ylimit_max')).val(); $(that.wrapSelector('#ylim')).val(com_util.formatString('({0}, {1})', ylim_min, ylim_max)); }); } templateForBody() { return chartHTml; } loadState() { vpLog.display(VP_LOG_TYPE.DEVELOP, this.state); let that = this; Object.keys(this.state).forEach(key => { if (key !== 'config') { let tag = $(that.wrapSelector('#' + key)); let tagName = $(tag).prop('tagName'); let savedValue = that.state[key]; switch(tagName) { case 'INPUT': let inputType = $(tag).prop('type'); if (inputType == 'text' || inputType == 'number' || inputType == 'hidden') { $(tag).val(savedValue); break; } if (inputType == 'checkbox') { $(tag).prop('checked', savedValue); break; } break; case 'TEXTAREA': case 'SELECT': default: $(tag).val(savedValue); break; } } }); } render() { super.render(); this.renderImportOptions(); this.renderSubplotOption1(); this.renderCmapSelector(); // bind event this.bindEventAfterRender(); this.bindVariableSelector(); } renderImportOptions() { var that = this; //==================================================================== // Stylesheet suggestinput //==================================================================== var stylesheetTag = $(this.wrapSelector('#vp_plStyle')); // search available stylesheet list var code = new com_String(); // FIXME: convert it to kernelApi code.appendLine('import matplotlib.pyplot as plt'); code.appendLine('import json'); code.append(`print(json.dumps([{ 'label': s, 'value': s } for s in plt.style.available]))`); vpKernel.execute(code.toString()).then(function(resultObj) { let { result } = resultObj; // get available stylesheet list var varList = JSON.parse(result); var suggestInput = new SuggestInput(); suggestInput.setComponentID('vp_plStyle'); suggestInput.setSuggestList(function() { return varList; }); suggestInput.setPlaceholder('style name'); // suggestInput.setNormalFilter(false); $(stylesheetTag).replaceWith(function() { return suggestInput.toTagString(); }); }); //==================================================================== // System font suggestinput //==================================================================== var fontFamilyTag = $(this.wrapSelector('#vp_plFontName')); // search system font list var code = new com_String(); // FIXME: convert it to kernelApi code.appendLine('import json'); code.appendLine("import matplotlib.font_manager as fm"); code.appendLine("_ttflist = fm.fontManager.ttflist"); code.append("print(json.dumps([{'label': f.name, 'value': f.name } for f in _ttflist]))"); vpKernel.execute(code.toString()).then(function(resultObj) { let { result } = resultObj; // get available font list var varList = JSON.parse(result); var suggestInput = new SuggestInput(); suggestInput.setComponentID('vp_plFontName'); suggestInput.setSuggestList(function() { return varList; }); suggestInput.setPlaceholder('font name'); // suggestInput.setNormalFilter(false); $(fontFamilyTag).replaceWith(function() { return suggestInput.toTagString(); }); }); //==================================================================== // Events //==================================================================== $(this.wrapSelector('#vp_plImportRun')).click(function() { // generateImportCode var code = that.generateImportCode(); // create block and run it $('#vp_wrapper').trigger({ type: 'create_option_page', blockType: 'block', menuId: 'lgExe_code', menuState: { taskState: { code: code } }, afterAction: 'run' }); }); } renderSubplotOption1() { // render kind selector this.renderKindSelector(); // show marker image var optionTagList = $(this.wrapSelector('#markerSelector option')); // [0] : except typing tag for (var i = 1; i < optionTagList.length; i++) { // file name var imgFile = $(optionTagList[i]).data('img'); // bind url var url = com_Const.IMAGE_PATH + 'chart/marker/' + imgFile; $(optionTagList[i]).attr({ 'data-img': url }); } } renderKindSelector() { // chart type selector var selector = $(this.wrapSelector('#kind')); var that = this; this.plotKind.forEach(kind => { var option = document.createElement('option'); $(option).attr({ id: kind, name: 'kind', value: kind }); var span = document.createElement('span'); $(span).attr({ // class="vp-multilang" data-caption-id="imshow" class: 'vp-multilang', 'data-caption-id':kind }); span.append(document.createTextNode(that.plotKindLang[kind])) option.appendChild(span); selector.append(option); }); // select chart type using metadata var plotType = this.state.kind; // select chart $(selector).val(plotType); $(this.wrapSelector(`#vp_plotKind .vp-plot-item[data-kind="${plotType}"]`)).addClass('selected'); // hide original selector $(selector).hide(); // initial plot chart option var plotPackage = { ...this.plotPackage[plotType] }; // hide all (except allocate to, chart type selector) $(this.wrapSelector('table.vp-plot-setting-table tr:not(:last)')).hide(); // show only options for this plot type plotPackage.input && plotPackage.input.forEach(obj => { $(this.wrapSelector('#' + obj.name)).closest('tr').show(); var label = obj.label; if (label != undefined) { $(this.wrapSelector('#' + obj.name)).closest('tr').find('th').removeClass('vp-orange-text'); if (obj.required != false) { // label = "* " + obj.label; $(this.wrapSelector('#' + obj.name)).closest('tr').find('th').addClass('vp-orange-text'); $(this.wrapSelector('#' + obj.name)).attr({'required': true}); } else { $(this.wrapSelector('#' + obj.name)).attr({'required': false}); } // $(this.wrapSelector("label[for='" + obj.name + "']")).text(label); $(this.wrapSelector('#' + obj.name)).closest('tr').find('th').text(label); } }); plotPackage.variable && plotPackage.variable.forEach(obj => { $(this.wrapSelector('#' + obj.name)).closest('tr').show(); }); plotPackage.input = plotPackage.input.concat(this.addOption); this.package = plotPackage; } renderCmapSelector() { // hide original selector var cmapSelector = this.wrapSelector('#cmap'); $(cmapSelector).hide(); // cmap selector this.cmap.forEach(ctype => { var option = document.createElement('option'); $(option).attr({ 'name': 'cmap', 'value': ctype }); $(option).text(ctype == ''?'none':ctype); $(cmapSelector).append(option); }); // dynamical div list for cmap data this.cmap.forEach(ctype => { var divColor = document.createElement('div'); $(divColor).attr({ 'class': 'vp-plot-cmap-item vp-scrollbar', 'data-cmap': ctype, 'data-url': 'pandas/cmap/' + ctype + '.JPG', 'title': ctype }); $(divColor).text(ctype == ''?'none':ctype); // bind url var url = com_Const.IMAGE_PATH + 'chart/cmap/' + ctype + '.JPG'; $(divColor).css({ 'background-image' : 'url(' + url + ')' }) var selectedCmap = this.wrapSelector('#vp_selectedCmap'); // select color $(divColor).click(function() { if (!$(this).hasClass('selected')) { $(this).parent().find('.vp-plot-cmap-item.selected').removeClass('selected'); $(this).addClass('selected'); // selected cmap name $(selectedCmap).text(ctype == ''?'none':ctype); // selected cmap data-caption-id $(selectedCmap).attr('data-caption-id', ctype); // force selection $(cmapSelector).val(ctype).prop('selected', true); } }); $(this.wrapSelector('#vp_plotCmapSelector')).append(divColor); }); } bindVariableSelector() { var that = this; let xSelector = new DataSelector({ pageThis: this, id: 'x' }); $(this.wrapSelector('#x')).replaceWith(xSelector.toTagString()); let ySelector = new DataSelector({ pageThis: this, id: 'y', required: true }); $(this.wrapSelector('#y')).replaceWith(ySelector.toTagString()); let zSelector = new DataSelector({ pageThis: this, id: 'z' }); $(this.wrapSelector('#z')).replaceWith(zSelector.toTagString()); } getSelectCode() { var code = new com_String(); // variable var variable = $(this.wrapSelector('.vp-var-view-item.selected td:first')).text(); if (variable == undefined) return ""; code.append(variable); // columns var columnsTag = $(this.wrapSelector('.vp-column-select-item.selected')); if (columnsTag.length > 0) { if (columnsTag.length == 1) { code.append('['); } else { code.append('[['); } columnsTag.each((i, tag) => { if (i > 0) { code.append(', '); } var colName = $(tag).data('col'); code.append(com_util.convertToStr(colName)); }); if (columnsTag.length == 1) { code.append(']'); } else { code.append(']]'); } } // method var method = $(this.wrapSelector('.vp-method-select-item.selected')).data('method'); if (method != undefined) { code.appendFormat('.{0}', method); } return code.toString(); } refreshVariables (callback = undefined) { var that = this; var _DATA_TYPES_OF_INDEX = [ 'RangeIndex', 'CategoricalIndex', 'MultiIndex', 'IntervalIndex', 'DatetimeIndex', 'TimedeltaIndex', 'PeriodIndex', 'Int64Index', 'UInt64Index', 'Float64Index' ]; var _DATA_TYPES_OF_GROUPBY = [ 'DataFrameGroupBy', 'SeriesGroupBy' ]; var _SEARCHABLE_DATA_TYPES = [ 'DataFrame', 'Series', 'Index', 'Period', 'GroupBy', 'Timestamp' , ..._DATA_TYPES_OF_INDEX , ..._DATA_TYPES_OF_GROUPBY ]; var types = _SEARCHABLE_DATA_TYPES; // init view var viewList = $(this.wrapSelector('#vp_varViewList tbody')); $(viewList).html(''); vpKernel.getDataList(types).then(function(resultObj) { let { result } = resultObj; var jsonVars = result.replace(/'/gi, `"`); var varList = JSON.parse(jsonVars); var hasVariable = false; // variable list varList.forEach(varObj => { if (types.includes(varObj.varType) && varObj.varName[0] !== '_') { var varAttr = { 'data-var-name': varObj.varName, 'data-var-type': varObj.varType, 'value': varObj.varName }; // Add to View Table var tagRow = $(`