Commit 84dc26d1 authored by Dmitry Trofimov's avatar Dmitry Trofimov
Browse files

Merge branch 'pandas-view' of https://github.com/fitermay/intellij-community into pandas-view

parents e5373f61 a506ecbf
Showing with 1186 additions and 535 deletions
+1186 -535
......@@ -434,20 +434,9 @@ class BaseInterpreterInterface:
return xml
def getArray(self, attr, roffset, coffset, rows, cols, format):
xml = "<xml>"
name = attr.split("\t")[-1]
array = pydevd_vars.eval_in_context(name, self.get_namespace(), self.get_namespace())
array, metaxml, r, c, f = pydevd_vars.array_to_meta_xml(array, name, format)
xml += metaxml
format = '%' + f
if rows == -1 and cols == -1:
rows = r
cols = c
xml += pydevd_vars.array_to_xml(array, roffset, coffset, rows, cols, format)
xml += "</xml>"
return xml
return pydevd_vars.table_like_struct_to_xml(array, name, roffset, coffset, rows, cols, format)
def evaluate(self, expression):
xml = "<xml>"
......
......@@ -988,17 +988,7 @@ class InternalGetArray(InternalThreadCommand):
try:
frame = pydevd_vars.find_frame(self.thread_id, self.frame_id)
var = pydevd_vars.eval_in_context(self.name, frame.f_globals, frame.f_locals)
xml = "<xml>"
var, metaxml, rows, cols, format = pydevd_vars.array_to_meta_xml(var, self.name, self.format)
xml += metaxml
self.format = '%' + format
if self.rows == -1 and self.cols == -1:
self.rows = rows
self.cols = cols
xml += pydevd_vars.array_to_xml(var, self.roffset, self.coffset, self.rows, self.cols, self.format)
xml += "</xml>"
xml = pydevd_vars.table_like_struct_to_xml(var, self.name, self.roffset, self.coffset, self.rows, self.cols, self.format )
cmd = dbg.cmd_factory.make_get_array_message(self.sequence, xml)
dbg.writer.add_command(cmd)
except:
......
......@@ -2,8 +2,8 @@
resolution/conversion to XML.
"""
import pickle
from _pydevd_bundle.pydevd_constants import * #@UnusedWildImport
from types import * #@UnusedWildImport
from _pydevd_bundle.pydevd_constants import * # @UnusedWildImport
from types import * # @UnusedWildImport
from _pydevd_bundle.pydevd_custom_frames import get_custom_frame
from _pydevd_bundle.pydevd_xml import *
......@@ -13,7 +13,7 @@ try:
from StringIO import StringIO
except ImportError:
from io import StringIO
import sys #@Reimport
import sys # @Reimport
from _pydev_imps._pydev_saved_modules import threading
import traceback
......@@ -22,25 +22,28 @@ from _pydev_bundle.pydev_imports import Exec, quote, execfile
from _pydevd_bundle.pydevd_utils import to_string
#-------------------------------------------------------------------------- defining true and false for earlier versions
# -------------------------------------------------------------------------- defining true and false for earlier versions
try:
__setFalse = False
except:
import __builtin__
setattr(__builtin__, 'True', 1)
setattr(__builtin__, 'False', 0)
#------------------------------------------------------------------------------------------------------ class for errors
class VariableError(RuntimeError):pass
# ------------------------------------------------------------------------------------------------------ class for errors
class VariableError(RuntimeError): pass
class FrameNotFoundError(RuntimeError): pass
class FrameNotFoundError(RuntimeError):pass
def _iter_frames(initialFrame):
'''NO-YIELD VERSION: Iterates through all the frames starting at the specified frame (which will be the first returned item)'''
#cannot use yield
# cannot use yield
frames = []
while initialFrame is not None:
......@@ -49,6 +52,7 @@ def _iter_frames(initialFrame):
return frames
def dump_frames(thread_id):
sys.stdout.write('dumping frames\n')
if thread_id != get_thread_id(threading.currentThread()):
......@@ -59,21 +63,25 @@ def dump_frames(thread_id):
sys.stdout.write('%s\n' % pickle.dumps(frame))
#===============================================================================
# ===============================================================================
# AdditionalFramesContainer
#===============================================================================
# ===============================================================================
class AdditionalFramesContainer:
lock = thread.allocate_lock()
additional_frames = {} #dict of dicts
additional_frames = {} # dict of dicts
def add_additional_frame_by_id(thread_id, frames_by_id):
AdditionalFramesContainer.additional_frames[thread_id] = frames_by_id
addAdditionalFrameById = add_additional_frame_by_id # Backward compatibility
addAdditionalFrameById = add_additional_frame_by_id # Backward compatibility
def remove_additional_frame_by_id(thread_id):
del AdditionalFramesContainer.additional_frames[thread_id]
removeAdditionalFrameById = remove_additional_frame_by_id # Backward compatibility
......@@ -89,9 +97,9 @@ def find_frame(thread_id, frame_id):
""" returns a frame on the thread that has a given frame_id """
try:
curr_thread_id = get_thread_id(threading.currentThread())
if thread_id != curr_thread_id :
if thread_id != curr_thread_id:
try:
return get_custom_frame(thread_id, frame_id) #I.e.: thread_id could be a stackless frame id + thread_id.
return get_custom_frame(thread_id, frame_id) # I.e.: thread_id could be a stackless frame id + thread_id.
except:
pass
......@@ -120,12 +128,12 @@ def find_frame(thread_id, frame_id):
del frame
#Important: python can hold a reference to the frame from the current context
#if an exception is raised, so, if we don't explicitly add those deletes
#we might have those variables living much more than we'd want to.
# Important: python can hold a reference to the frame from the current context
# if an exception is raised, so, if we don't explicitly add those deletes
# we might have those variables living much more than we'd want to.
#I.e.: sys.exc_info holding reference to frame that raises exception (so, other places
#need to call sys.exc_clear())
# I.e.: sys.exc_info holding reference to frame that raises exception (so, other places
# need to call sys.exc_clear())
del curFrame
if frameFound is None:
......@@ -155,6 +163,7 @@ def find_frame(thread_id, frame_id):
traceback.print_exc()
return None
def getVariable(thread_id, frame_id, scope, attrs):
"""
returns the value of a variable
......@@ -170,14 +179,14 @@ def getVariable(thread_id, frame_id, scope, attrs):
not the frame (as we don't care about the frame in this case).
"""
if scope == 'BY_ID':
if thread_id != get_thread_id(threading.currentThread()) :
if thread_id != get_thread_id(threading.currentThread()):
raise VariableError("getVariable: must execute on same thread")
try:
import gc
objects = gc.get_objects()
except:
pass #Not all python variants have it.
pass # Not all python variants have it.
else:
frame_id = int(frame_id)
for var in objects:
......@@ -190,7 +199,7 @@ def getVariable(thread_id, frame_id, scope, attrs):
return var
#If it didn't return previously, we coudn't find it by id (i.e.: alrceady garbage collected).
# If it didn't return previously, we coudn't find it by id (i.e.: alrceady garbage collected).
sys.stderr.write('Unable to find object with id: %s\n' % (frame_id,))
return None
......@@ -328,37 +337,38 @@ def evaluate_expression(thread_id, frame_id, expression, doExec):
if frame is None:
return
#Not using frame.f_globals because of https://sourceforge.net/tracker2/?func=detail&aid=2541355&group_id=85796&atid=577329
#(Names not resolved in generator expression in method)
#See message: http://mail.python.org/pipermail/python-list/2009-January/526522.html
# Not using frame.f_globals because of https://sourceforge.net/tracker2/?func=detail&aid=2541355&group_id=85796&atid=577329
# (Names not resolved in generator expression in method)
# See message: http://mail.python.org/pipermail/python-list/2009-January/526522.html
updated_globals = {}
updated_globals.update(frame.f_globals)
updated_globals.update(frame.f_locals) #locals later because it has precedence over the actual globals
updated_globals.update(frame.f_locals) # locals later because it has precedence over the actual globals
try:
expression = str(expression.replace('@LINE@', '\n'))
if doExec:
try:
#try to make it an eval (if it is an eval we can print it, otherwise we'll exec it and
#it will have whatever the user actually did)
# try to make it an eval (if it is an eval we can print it, otherwise we'll exec it and
# it will have whatever the user actually did)
compiled = compile(expression, '<string>', 'eval')
except:
Exec(expression, updated_globals, frame.f_locals)
pydevd_save_locals.save_locals(frame)
else:
result = eval(compiled, updated_globals, frame.f_locals)
if result is not None: #Only print if it's not None (as python does)
if result is not None: # Only print if it's not None (as python does)
sys.stdout.write('%s\n' % (result,))
return
else:
return eval_in_context(expression, updated_globals, frame.f_locals)
finally:
#Should not be kept alive if an exception happens and this frame is kept in the stack.
# Should not be kept alive if an exception happens and this frame is kept in the stack.
del updated_globals
del frame
def change_attr_expression(thread_id, frame_id, attr, expression, dbg):
'''Changes some attribute in a given frame.
'''
......@@ -385,7 +395,7 @@ def change_attr_expression(thread_id, frame_id, attr, expression, dbg):
pydevd_save_locals.save_locals(frame)
return frame.f_locals[attr]
#default way (only works for changing it in the topmost frame)
# default way (only works for changing it in the topmost frame)
result = eval(expression, frame.f_globals, frame.f_locals)
Exec('%s=%s' % (attr, expression), frame.f_globals, frame.f_locals)
return result
......@@ -394,16 +404,35 @@ def change_attr_expression(thread_id, frame_id, attr, expression, dbg):
except Exception:
traceback.print_exc()
MAXIMUM_ARRAY_SIZE = 100
MAX_SLICE_SIZE = 1000
def table_like_struct_to_xml(array, name, roffset, coffset, rows, cols, format):
_, type_name, _ = get_type(array)
if type_name == 'ndarray':
array, metaxml, r, c, f = array_to_meta_xml(array, name, format)
xml = metaxml
format = '%' + f
if rows == -1 and cols == -1:
rows = r
cols = c
xml += array_to_xml(array, roffset, coffset, rows, cols, format)
elif type_name == 'DataFrame':
xml = dataframe_to_xml(array, name, roffset, coffset, rows, cols, format)
else:
raise VariableError("Do not know how to convert type %s to table" % (type_name))
return "<xml>%s</xml>" % xml
def array_to_xml(array, roffset, coffset, rows, cols, format):
xml = ""
rows = min(rows, MAXIMUM_ARRAY_SIZE)
cols = min(cols, MAXIMUM_ARRAY_SIZE)
#there is no obvious rule for slicing (at least 5 choices)
# there is no obvious rule for slicing (at least 5 choices)
if len(array) == 1 and (rows > 1 or cols > 1):
array = array[0]
if array.size > len(array):
......@@ -489,11 +518,11 @@ def array_to_meta_xml(array, name, format):
elif l == 2:
rows = min(array.shape[-2], MAX_SLICE_SIZE)
cols = min(array.shape[-1], MAX_SLICE_SIZE)
if cols < array.shape[-1] or rows < array.shape[-2]:
if cols < array.shape[-1] or rows < array.shape[-2]:
reslice = '[0:%s, 0:%s]' % (rows, cols)
array = array[0:rows, 0:cols]
#avoid slice duplication
# avoid slice duplication
if not slice.endswith(reslice):
slice += reslice
......@@ -501,8 +530,86 @@ def array_to_meta_xml(array, name, format):
if type in "biufc":
bounds = (array.min(), array.max())
xml = '<array slice=\"%s\" rows=\"%s\" cols=\"%s\" format=\"%s\" type=\"%s\" max=\"%s\" min=\"%s\"/>' % \
(slice, rows, cols, format, type, bounds[1], bounds[0])
(slice, rows, cols, format, type, bounds[1], bounds[0])
return array, xml, rows, cols, format
def dataframe_to_xml(df, name, roffset, coffset, rows, cols, format):
"""
:type df: pandas.core.frame.DataFrame
:type name: str
:type coffset: int
:type roffset: int
:type rows: int
:type cols: int
:type format: str
"""
num_rows = min(df.shape[0], MAX_SLICE_SIZE)
num_cols = min(df.shape[1], MAX_SLICE_SIZE)
if (num_rows, num_cols) != df.shape:
df = df.iloc[0:num_rows, 0: num_cols]
slice = '.iloc[0:%s, 0:%s]' % (num_rows, num_cols)
else:
slice = ''
slice = name + slice
xml = '<array slice=\"%s\" rows=\"%s\" cols=\"%s\" format=\"\" type=\"\" max=\"0\" min=\"0\"/>\n' % \
(slice, num_rows, num_cols)
if (rows, cols) == (-1, -1):
rows, cols = num_rows, num_cols
rows = min(rows, MAXIMUM_ARRAY_SIZE)
cols = min(min(cols, MAXIMUM_ARRAY_SIZE), num_cols)
# need to precompute column bounds here before slicing!
col_bounds = [None] * cols
for col in range(cols):
dtype = df.dtypes.iloc[col].kind
if dtype in "biufc":
cvalues = df.iloc[:, col]
bounds = (cvalues.min(), cvalues.max())
else:
bounds = (0, 0)
col_bounds[col] = bounds
df = df.iloc[roffset: roffset + rows, coffset: coffset + cols]
rows, cols = df.shape
def default_format(type):
if type == 'f':
return '.5f'
elif type == 'i' or type == 'u':
return 'd'
else:
return 's'
xml += "<headerdata rows=\"%s\" cols=\"%s\">\n" % (rows, cols)
format = format.replace('%', '')
col_formats = []
for col in range(cols):
label = df.axes[1].values[col]
if isinstance(label, tuple):
label = '/'.join(label)
label = str(label)
dtype = df.dtypes.iloc[col].kind
fmt = format if (dtype == 'f' and format) else default_format(dtype)
col_formats.append('%' + fmt)
bounds = col_bounds[col]
xml += '<colheader index=\"%s\" label=\"%s\" type=\"%s\" format=\"%s\" max=\"%s\" min=\"%s\" />\n' % \
(str(col), label, dtype, fmt, bounds[1], bounds[0])
for row, label in enumerate(iter(df.axes[0])):
if isinstance(label, tuple):
label = '/'.join(label)
xml += "<rowheader index=\"%s\" label = \"%s\"/>\n" % \
(str(row), label)
xml += "</headerdata>\n"
xml += "<arraydata rows=\"%s\" cols=\"%s\"/>\n" % (rows, cols)
for row in range(rows):
xml += "<row index=\"%s\"/>\n" % str(row)
for col in range(cols):
value = df.iat[row, col]
value = col_formats[col] % value
xml += var_to_xml(value, '')
return xml
......@@ -18,6 +18,8 @@ package com.jetbrains.python.debugger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author amarch
*/
......@@ -31,6 +33,10 @@ public class ArrayChunk {
private final String myFormat;
private final String myType;
private final Object[][] myData;
private final List<String> myRowLabels;
private final List<ColHeader> myColHeaders;
public ArrayChunk(@NotNull PyDebugValue value,
String slicePresentation,
......@@ -40,7 +46,7 @@ public class ArrayChunk {
String min,
String format,
String type,
@Nullable Object[][] data) {
@Nullable Object[][] data, List<String> labels, List<ColHeader> headers) {
myValue = value;
mySlicePresentation = slicePresentation;
myRows = rows;
......@@ -50,6 +56,8 @@ public class ArrayChunk {
myFormat = format;
myType = type;
myData = data;
myRowLabels = labels;
myColHeaders = headers;
}
public PyDebugValue getValue() {
......@@ -87,4 +95,50 @@ public class ArrayChunk {
public Object[][] getData() {
return myData;
}
public List<String> getRowLabels() {
return myRowLabels;
}
public List<ColHeader> getColHeaders() {
return myColHeaders;
}
public static class ColHeader
{
private final String myLabel;
private final String myType;
private final String myFormat;
private final String myMax;
private final String myMin;
public ColHeader(String label, String type, String format, String max, String min) {
myLabel = label;
myType = type;
myFormat = format;
myMax = max;
myMin = min;
}
public String getLabel() {
return myLabel;
}
public String getType() {
return myType;
}
public String getFormat() {
return myFormat;
}
public String getMax() {
return myMax;
}
public String getMin() {
return myMin;
}
}
}
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger;
import java.util.List;
public class ArrayChunkBuilder {
private PyDebugValue myValue;
private String myPresentation;
private int myRows;
private int myColumns;
private String myMax;
private String myMin;
private String myFormat;
private String myType;
private Object[][] myData = null;
private List<String> myRowLabels = null;
private List<ArrayChunk.ColHeader> myColHeaders = null;
public ArrayChunkBuilder setValue(PyDebugValue value) {
myValue = value;
return this;
}
public ArrayChunkBuilder setSlicePresentation(String presentation) {
myPresentation = presentation;
return this;
}
public ArrayChunkBuilder setRows(int rows) {
myRows = rows;
return this;
}
public ArrayChunkBuilder setColumns(int columns) {
myColumns = columns;
return this;
}
public ArrayChunkBuilder setMax(String max) {
myMax = max;
return this;
}
public ArrayChunkBuilder setMin(String min) {
myMin = min;
return this;
}
public ArrayChunkBuilder setFormat(String format) {
myFormat = format;
return this;
}
public ArrayChunkBuilder setType(String type) {
myType = type;
return this;
}
public ArrayChunkBuilder setData(Object[][] data) {
myData = data;
return this;
}
public void setRowLabels(List<String> rowLabels) {
myRowLabels = rowLabels;
}
public void setColHeaders(List<ArrayChunk.ColHeader> colHeaders) {
myColHeaders = colHeaders;
}
public ArrayChunk createArrayChunk() {
return new ArrayChunk(myValue, myPresentation, myRows, myColumns, myMax, myMin, myFormat, myType, myData, myRowLabels, myColHeaders);
}
}
\ No newline at end of file
package com.jetbrains.python.debugger.pydev;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.text.StringUtil;
import com.jetbrains.python.debugger.*;
import com.thoughtworks.xstream.io.naming.NoNameCoder;
......@@ -33,13 +34,14 @@ public class ProtocolParser {
reader.moveDown();
if ("arg".equals(reader.getNodeName())) {
signature.addArgument(readString(reader, "name", ""), readString(reader, "type", ""));
} else
if ("return".equals(reader.getNodeName())) {
}
else if ("return".equals(reader.getNodeName())) {
signature.addReturnType(readString(reader, "type", ""));
} else {
}
else {
throw new PyDebuggerException("Expected <arg> or <return>, found " + reader.getNodeName());
}
reader.moveUp();
}
......@@ -54,9 +56,11 @@ public class ProtocolParser {
boolean isAsyncio;
if (eventName.equals("threading_event")) {
isAsyncio = false;
} else if (eventName.equals("asyncio_event")) {
}
else if (eventName.equals("asyncio_event")) {
isAsyncio = true;
} else {
}
else {
throw new PyDebuggerException("Expected <threading_event> or <asyncio_event>, found " + reader.getNodeName());
}
......@@ -73,7 +77,8 @@ public class ProtocolParser {
String parentThread = readString(reader, "parent", "");
if (!parentThread.isEmpty()) {
threadingEvent = new PyThreadEvent(time, thread_id, name, parentThread, isAsyncio);
} else {
}
else {
threadingEvent = new PyThreadEvent(time, thread_id, name, isAsyncio);
}
}
......@@ -269,28 +274,57 @@ public class ProtocolParser {
public static ArrayChunk parseArrayValues(final String text, final PyFrameAccessor frameAccessor) throws PyDebuggerException {
final XppReader reader = openReader(text, false);
ArrayChunk result = null;
ArrayChunkBuilder result = new ArrayChunkBuilder();
if (reader.hasMoreChildren()) {
reader.moveDown();
if (!"array".equals(reader.getNodeName())) {
throw new PyDebuggerException("Expected <array> at first node, found " + reader.getNodeName());
}
String slice = readString(reader, "slice", null);
int rows = readInt(reader, "rows", null);
int cols = readInt(reader, "cols", null);
String format = "%" + readString(reader, "format", null);
String type = readString(reader, "type", null);
String max = readString(reader, "max", null);
String min = readString(reader, "min", null);
result =
new ArrayChunk(new PyDebugValue(slice, null, null, null, false, false, false, frameAccessor), slice, rows, cols, max, min, format,
type, null);
result.setSlicePresentation(slice);
result.setRows(readInt(reader, "rows", null));
result.setColumns(readInt(reader, "cols", null));
result.setFormat("%" + readString(reader, "format", null));
result.setType(readString(reader, "type", null));
result.setMax(readString(reader, "max", null));
result.setMin(readString(reader, "min", null));
result.setValue(new PyDebugValue(slice, null, null, null, false, false, false, frameAccessor));
reader.moveUp();
}
if ("headerdata".equals(reader.peekNextChild())) {
parseArrayHeaderData(reader, result);
}
Object[][] data = parseArrayValues(reader, frameAccessor);
return new ArrayChunk(result.getValue(), result.getSlicePresentation(), result.getRows(), result.getColumns(), result.getMax(),
result.getMin(), result.getFormat(), result.getType(), data);
result.setData(data);
return result.createArrayChunk();
}
private static void parseArrayHeaderData(XppReader reader, ArrayChunkBuilder result) throws PyDebuggerException {
List<String> rowHeaders = Lists.newArrayList();
List<ArrayChunk.ColHeader> colHeaders = Lists.newArrayList();
reader.moveDown();
while (reader.hasMoreChildren()) {
reader.moveDown();
if ("colheader".equals(reader.getNodeName())) {
colHeaders.add(new ArrayChunk.ColHeader(
readString(reader, "label", null),
readString(reader, "type", null),
readString(reader, "format", null),
readString(reader, "max", null),
readString(reader, "min", null)));
}
else if ("rowheader".equals(reader.getNodeName())) {
rowHeaders.add(readString(reader, "label", null));
}
else {
throw new PyDebuggerException("Invalid node name" + reader.getNodeName());
}
reader.moveUp();
}
result.setColHeaders(colHeaders);
result.setRowLabels(rowHeaders);
reader.moveUp();
}
public static Object[][] parseArrayValues(final XppReader reader, final PyFrameAccessor frameAccessor) throws PyDebuggerException {
......
......@@ -799,7 +799,7 @@
<add-to-group group-id="Internal"/>
</action>
<action id="PyDebugger.ViewArray" class="com.jetbrains.python.debugger.array.PyViewArrayAction" text="View as Array">
<action id="PyDebugger.ViewArray" class="com.jetbrains.python.debugger.containerview.PyViewNumericContainerAction" text="View as Array">
<add-to-group group-id="XDebugger.ValueGroup" anchor="after" relative-to-action="Debugger.Tree.AddToWatches"/>
</action>
......
......@@ -17,6 +17,8 @@ package com.jetbrains.python.debugger.array;
import com.intellij.ui.JBColor;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.debugger.containerview.ColoredCellRenderer;
import com.jetbrains.python.debugger.containerview.PyNumericViewUtil;
import javax.swing.*;
import javax.swing.border.LineBorder;
......@@ -26,14 +28,14 @@ import java.awt.*;
/**
* @author amarch
*/
class ArrayTableCellRenderer extends DefaultTableCellRenderer {
class ArrayTableCellRenderer extends DefaultTableCellRenderer implements ColoredCellRenderer {
private double myMin = Double.MIN_VALUE;
private double myMax = Double.MIN_VALUE;
private String myComplexMin;
private String myComplexMax;
private boolean myColored = true;
private String myType;
private final String myType;
public ArrayTableCellRenderer(double min, double max, String type) {
setHorizontalAlignment(CENTER);
......@@ -62,9 +64,8 @@ class ArrayTableCellRenderer extends DefaultTableCellRenderer {
if (myMax != myMin) {
if (myColored && value != null) {
try {
double rangedValue = NumpyArrayTable.getRangedValue(value.toString(), myType, myMin, myMax, myComplexMax, myComplexMin);
//noinspection UseJBColor
this.setBackground(new Color((int)Math.round(255 * rangedValue), 0, (int)Math.round(255 * (1 - rangedValue)), 130));
double rangedValue = PyNumericViewUtil.getRangedValue(value.toString(), myType, myMin, myMax, myComplexMax, myComplexMin);
this.setBackground(PyNumericViewUtil.rangedValueToColor(rangedValue));
}
catch (NumberFormatException ignored) {
}
......
......@@ -24,9 +24,11 @@ import com.intellij.openapi.util.Pair;
import com.intellij.util.ConcurrencyUtil;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.debugger.ArrayChunk;
import com.jetbrains.python.debugger.PyDebugValue;
import com.jetbrains.python.debugger.ArrayChunkBuilder;
import org.jetbrains.annotations.NotNull;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import java.util.concurrent.*;
/**
......@@ -39,7 +41,7 @@ public class AsyncArrayTableModel extends AbstractTableModel {
private int myRows;
private int myColumns;
private final NumpyArrayTable myProvider;
private final TableChunkDatasource myProvider;
private final ExecutorService myExecutorService = ConcurrencyUtil.newSingleThreadExecutor("Python async table");
......@@ -48,16 +50,14 @@ public class AsyncArrayTableModel extends AbstractTableModel {
private LoadingCache<Pair<Integer, Integer>, ListenableFuture<ArrayChunk>> myChunkCache = CacheBuilder.newBuilder().build(
new CacheLoader<Pair<Integer, Integer>, ListenableFuture<ArrayChunk>>() {
@Override
public ListenableFuture<ArrayChunk> load(final Pair<Integer, Integer> key) throws Exception {
final PyDebugValue value = myProvider.getDebugValue();
final PyDebugValue slicedValue =
new PyDebugValue(myProvider.getSliceText(), value.getType(), value.getTypeQualifier(), value.getValue(), value.isContainer(),
value.isReturnedVal(), value.isErrorOnEval(), value.getParent(), value.getFrameAccessor());
public ListenableFuture<ArrayChunk> load(@NotNull final Pair<Integer, Integer> key) throws Exception {
ListenableFutureTask<ArrayChunk> task = ListenableFutureTask.create(() -> value.getFrameAccessor()
.getArrayItems(slicedValue, key.first, key.second, Math.min(CHUNK_ROW_SIZE, getRowCount() - key.first),
Math.min(CHUNK_COL_SIZE, getColumnCount() - key.second),
myProvider.getFormat()));
ListenableFutureTask<ArrayChunk> task = ListenableFutureTask.create(() -> {
ArrayChunk chunk = myProvider.getChunk(key.first, key.second, Math.min(CHUNK_ROW_SIZE, getRowCount() - key.first),
Math.min(CHUNK_COL_SIZE, getColumnCount() - key.second));
handleChunkAdded(key.first, key.second, chunk);
return chunk;
});
myExecutorService.execute(task);
......@@ -65,7 +65,7 @@ public class AsyncArrayTableModel extends AbstractTableModel {
}
});
public AsyncArrayTableModel(int rows, int columns, NumpyArrayTable provider) {
public AsyncArrayTableModel(int rows, int columns, TableChunkDatasource provider) {
myRows = rows;
myColumns = columns;
myProvider = provider;
......@@ -73,14 +73,6 @@ public class AsyncArrayTableModel extends AbstractTableModel {
@Override
public boolean isCellEditable(int row, int col) {
//Pair<Integer, Integer> key = itemToChunkKey(row, col);
//try {
// return myChunkCache.get(key).isDone();
//}
//catch (ExecutionException e) {
// return false;
//}
//TODO: make it editable
return false;
}
......@@ -160,13 +152,11 @@ public class AsyncArrayTableModel extends AbstractTableModel {
Pair<Integer, Integer> key = itemToChunkKey(roffset * CHUNK_ROW_SIZE, coffset * CHUNK_COL_SIZE);
final Object[][] chunkData = new Object[CHUNK_ROW_SIZE][CHUNK_COL_SIZE];
for (int r = 0; r < CHUNK_ROW_SIZE; r++) {
for (int c = 0; c < CHUNK_COL_SIZE; c++) {
chunkData[r][c] = data[roffset * CHUNK_ROW_SIZE + r][coffset * CHUNK_COL_SIZE + c];
}
System.arraycopy(data[roffset * CHUNK_ROW_SIZE + r], coffset * 30, chunkData[r], 0, CHUNK_COL_SIZE);
}
myChunkCache.put(key, new ListenableFuture<ArrayChunk>() {
@Override
public void addListener(Runnable listener, Executor executor) {
public void addListener(@NotNull Runnable listener, @NotNull Executor executor) {
}
......@@ -187,15 +177,53 @@ public class AsyncArrayTableModel extends AbstractTableModel {
@Override
public ArrayChunk get() throws InterruptedException, ExecutionException {
return new ArrayChunk(chunk.getValue(), null, 0, 0, null, null, null, null, chunkData);
return new ArrayChunkBuilder().setValue(chunk.getValue()).setSlicePresentation(null).setRows(0).setColumns(0).setMax(null)
.setMin(null).setFormat(null).setType(null).setData(chunkData).createArrayChunk();
}
@Override
public ArrayChunk get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return new ArrayChunk(chunk.getValue(), null, 0, 0, null, null, null, null, chunkData);
return new ArrayChunkBuilder().setValue(chunk.getValue()).setSlicePresentation(null).setRows(0).setColumns(0).setMax(null)
.setMin(null).setFormat(null).setType(null).setData(chunkData).createArrayChunk();
}
});
}
}
handleChunkAdded(0, 0, chunk);
}
protected void handleChunkAdded(Integer rowOffset, Integer colOffset, ArrayChunk chunk) {
}
public TableModel getRowHeaderModel() {
return new RowNumberHeaderModel();
}
private class RowNumberHeaderModel extends AbstractTableModel {
@Override
public int getRowCount() {
return AsyncArrayTableModel.this.getRowCount();
}
@Override
public int getColumnCount() {
return 1;
}
@Override
public String getColumnName(int column) {
if (column == 0) {
return " ";
}
throw new IllegalArgumentException("Table only has one column");
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return Integer.toString(rowIndex);
}
}
}
\ No newline at end of file
......@@ -21,12 +21,10 @@ import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
......@@ -39,59 +37,47 @@ public class JBTableWithRowHeaders extends JBTable {
private final JBScrollPane myScrollPane;
private RowHeaderTable myRowHeaderTable;
public JBScrollPane getScrollPane() {
return myScrollPane;
}
public JBTableWithRowHeaders() {
setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);
setRowSelectionAllowed(false);
setMaxItemsForSizeCalculation(50);
setTableHeader(new CustomTableHeader(this));
getTableHeader().setDefaultRenderer(new ArrayTableForm.ColumnHeaderRenderer());
getTableHeader().setDefaultRenderer(new ColumnHeaderRenderer());
getTableHeader().setReorderingAllowed(false);
myScrollPane = new JBScrollPane(this);
JBTableWithRowHeaders.RowHeaderTable rowTable = new JBTableWithRowHeaders.RowHeaderTable(this);
myScrollPane.setRowHeaderView(rowTable);
myRowHeaderTable = new JBTableWithRowHeaders.RowHeaderTable(this);
myScrollPane.setRowHeaderView(myRowHeaderTable);
myScrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER,
rowTable.getTableHeader());
myRowHeaderTable.getTableHeader());
}
setRowHeaderTable(rowTable);
public JBScrollPane getScrollPane() {
return myScrollPane;
}
public boolean getScrollableTracksViewportWidth() {
return getPreferredSize().width < getParent().getWidth();
}
public RowHeaderTable getRowHeaderTable() {
return myRowHeaderTable;
}
public void setRowHeaderTable(RowHeaderTable rowHeaderTable) {
myRowHeaderTable = rowHeaderTable;
@Override
public void setModel(@NotNull TableModel model) {
super.setModel(model);
if (model instanceof AsyncArrayTableModel) {
myRowHeaderTable.setModel(((AsyncArrayTableModel)model).getRowHeaderModel());
}
}
public static class RowHeaderTable extends JBTable implements PropertyChangeListener, TableModelListener {
private JTable myMainTable;
private int myRowShift = 0;
public RowHeaderTable(JTable table) {
myMainTable = table;
myMainTable.getModel().addTableModelListener(this);
setFocusable(false);
setAutoCreateColumnsFromModel(false);
setSelectionModel(myMainTable.getSelectionModel());
setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
TableColumn column = new TableColumn();
column.setHeaderValue(" ");
addColumn(column);
column.setCellRenderer(new RowNumberRenderer());
getColumnModel().getColumn(0).setPreferredWidth(50);
setPreferredScrollableViewportSize(getPreferredSize());
setRowHeight(myMainTable.getRowHeight());
MouseListener[] listeners = getMouseListeners();
for (MouseListener l : listeners) {
......@@ -105,9 +91,16 @@ public class JBTableWithRowHeaders extends JBTable {
super.paintComponent(g);
}
@Override
public int getRowCount() {
return myMainTable.getRowCount();
public void setModel(@NotNull TableModel model) {
setAutoCreateColumnsFromModel(true);
super.setModel(model);
if (getColumnModel().getColumnCount() > 0) {
getColumnModel().getColumn(0).setPreferredWidth(50);
getColumnModel().getColumn(0).setCellRenderer(new RowNumberRenderer());
setPreferredScrollableViewportSize(getPreferredSize());
}
}
@Override
......@@ -116,14 +109,6 @@ public class JBTableWithRowHeaders extends JBTable {
return super.getRowHeight(row);
}
@Override
public Object getValueAt(int row, int column) {
return Integer.toString(row + myRowShift);
}
public void setRowShift(int shift) {
myRowShift = shift;
}
@Override
public boolean isCellEditable(int row, int column) {
......@@ -131,10 +116,6 @@ public class JBTableWithRowHeaders extends JBTable {
}
@Override
public void setValueAt(Object value, int row, int column) {
}
public void propertyChange(PropertyChangeEvent e) {
if ("selectionModel".equals(e.getPropertyName())) {
setSelectionModel(myMainTable.getSelectionModel());
......@@ -145,17 +126,12 @@ public class JBTableWithRowHeaders extends JBTable {
}
if ("model".equals(e.getPropertyName())) {
myMainTable.getModel().addTableModelListener(this);
revalidate();
}
}
@Override
public void tableChanged(TableModelEvent e) {
revalidate();
}
private class RowNumberRenderer extends DefaultTableCellRenderer {
private static class RowNumberRenderer extends DefaultTableCellRenderer {
public RowNumberRenderer() {
setHorizontalAlignment(SwingConstants.CENTER);
}
......@@ -189,12 +165,7 @@ public class JBTableWithRowHeaders extends JBTable {
public CustomTableHeader(JTable table) {
super();
setColumnModel(table.getColumnModel());
table.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
repaint();
}
});
table.getColumnModel().getSelectionModel().addListSelectionListener(e -> repaint());
}
@Override
......@@ -202,4 +173,39 @@ public class JBTableWithRowHeaders extends JBTable {
repaint();
}
}
public static class ColumnHeaderRenderer extends DefaultTableHeaderCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) {
super.getTableCellRendererComponent(table, value, selected, focused, row, column);
int selectedColumn = table.getSelectedColumn();
if (selectedColumn == column) {
setFont(getFont().deriveFont(Font.BOLD));
}
return this;
}
}
public static class DefaultTableHeaderCellRenderer extends DefaultTableCellRenderer {
public DefaultTableHeaderCellRenderer() {
setHorizontalAlignment(CENTER);
setHorizontalTextPosition(LEFT);
setVerticalAlignment(BOTTOM);
setOpaque(false);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
JTableHeader tableHeader = table.getTableHeader();
if (tableHeader != null) {
setForeground(tableHeader.getForeground());
}
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
return this;
}
}
}
......@@ -15,211 +15,52 @@
*/
package com.jetbrains.python.debugger.array;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.debugger.*;
import com.jetbrains.python.debugger.ArrayChunk;
import com.jetbrains.python.debugger.PyDebugValue;
import com.jetbrains.python.debugger.containerview.ColoredCellRenderer;
import com.jetbrains.python.debugger.containerview.NumericContainerViewTable;
import com.jetbrains.python.debugger.containerview.ViewNumericContainerDialog;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author amarch
*/
public class NumpyArrayTable {
private final PyDebugValue myValue;
private final PyViewArrayAction.ViewArrayDialog myDialog;
private final ArrayTableForm myComponent;
private final JTable myTable;
private Project myProject;
private PyDebuggerEvaluator myEvaluator;
private String myDtypeKind;
private ArrayTableCellRenderer myTableCellRenderer;
private AsyncArrayTableModel myPagingModel;
private final static int COLUMNS_IN_DEFAULT_SLICE = 40;
private final static int ROWS_IN_DEFAULT_SLICE = 40;
private final static int COLUMNS_IN_DEFAULT_VIEW = 1000;
private final static int ROWS_IN_DEFAULT_VIEW = 1000;
private static final Pattern PY_COMPLEX_NUMBER = Pattern.compile("([+-]?[.\\d^j]*)([+-]?[e.\\d]*j)?");
public final class NumpyArrayTable extends NumericContainerViewTable {
private final static int HUGE_ARRAY_SIZE = 1000 * 1000;
private final static String LOAD_SMALLER_SLICE = "Full slice too large and would slow down debugger, shrunk to smaller slice.";
private final static String DISABLE_COLOR_FOR_HUGE_ARRAY =
"Disable color because array too big and calculating min and max would slow down debugging.";
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.debugger.array.NumpyArrayValueProvider");
private ArrayTableCellRenderer myArrayTableCellRenderer;
public NumpyArrayTable(@NotNull Project project,
@NotNull PyViewArrayAction.ViewArrayDialog dialog, @NotNull PyDebugValue value) {
myValue = value;
myDialog = dialog;
myComponent = new ArrayTableForm(project, new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
doReslice();
}
}
}, new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
doApplyFormat();
}
}
});
myTable = myComponent.getTable();
myProject = project;
myEvaluator = new PyDebuggerEvaluator(project, getDebugValue().getFrameAccessor());
}
public ArrayTableForm getComponent() {
return myComponent;
}
private void initComponent() {
myComponent.getColoredCheckbox().setEnabled(false);
myComponent.getColoredCheckbox().addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getSource() == myComponent.getColoredCheckbox()) {
if (myTable.getRowCount() > 0 &&
myTable.getColumnCount() > 0 &&
myTable.getCellRenderer(0, 0) instanceof ArrayTableCellRenderer) {
ArrayTableCellRenderer renderer = (ArrayTableCellRenderer)myTable.getCellRenderer(0, 0);
if (myComponent.getColoredCheckbox().isSelected()) {
renderer.setColored(true);
}
else {
renderer.setColored(false);
}
}
myComponent.getScrollPane().repaint();
}
}
});
//make value name read-only
myComponent.getSliceTextField().addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
myComponent.getSliceTextField().getDocument().createGuardedBlock(0, getNodeFullName().length());
}
@Override
public void focusLost(FocusEvent e) {
RangeMarker block = myComponent.getSliceTextField().getDocument().getRangeGuard(0, getNodeFullName().length());
if (block != null) {
myComponent.getSliceTextField().getDocument().removeGuardedBlock(block);
}
}
});
}
public void disableColor() {
myTableCellRenderer.setMin(Double.MAX_VALUE);
myTableCellRenderer.setMax(Double.MIN_VALUE);
myTableCellRenderer.setColored(false);
UIUtil.invokeLaterIfNeeded(() -> {
myComponent.getColoredCheckbox().setSelected(false);
myComponent.getColoredCheckbox().setEnabled(false);
if (myTable.getColumnCount() > 0) {
myTable.setDefaultRenderer(myTable.getColumnClass(0), myTableCellRenderer);
}
});
}
public PyDebugValue getDebugValue() {
return myValue;
}
public PyFrameAccessor getEvaluator() {
return myValue.getFrameAccessor();
@NotNull ViewNumericContainerDialog dialog, @NotNull PyDebugValue value) {
super(project, dialog, value);
}
public void init() {
init(getDebugValue().getEvaluationExpression(), false);
@Override
protected AsyncArrayTableModel createTableModel(int rowCount, int columnCount) {
return new AsyncArrayTableModel(rowCount, columnCount, this);
}
public void init(final String slice, final boolean inPlace) {
initComponent();
ApplicationManager.getApplication().executeOnPooledThread(() -> {
final PyDebugValue value = getDebugValue();
PyDebugValue parent = value.getParent();
final PyDebugValue slicedValue =
new PyDebugValue(slice, value.getType(), null, value.getValue(), value.isContainer(), value.isReturnedVal(),
value.isErrorOnEval(), parent, value.getFrameAccessor());
final String format = getFormat().isEmpty() ? "%" : getFormat();
try {
initUi(value.getFrameAccessor()
.getArrayItems(slicedValue, 0, 0, -1, -1, format), inPlace);
}
catch (PyDebuggerException e) {
showError(e.getMessage());
}
});
@Override
protected ColoredCellRenderer createCellRenderer(double minValue, double maxValue, ArrayChunk chunk) {
myArrayTableCellRenderer = new ArrayTableCellRenderer(minValue, maxValue, chunk.getType());
fillColorRange(chunk.getMin(), chunk.getMax());
return myArrayTableCellRenderer;
}
private void initUi(@NotNull final ArrayChunk chunk, final boolean inPlace) {
myPagingModel = new AsyncArrayTableModel(Math.min(chunk.getRows(), ROWS_IN_DEFAULT_VIEW),
Math.min(chunk.getColumns(), COLUMNS_IN_DEFAULT_VIEW), this);
myPagingModel.addToCache(chunk);
myDtypeKind = chunk.getType();
UIUtil.invokeLaterIfNeeded(() -> {
myTable.setModel(myPagingModel);
myComponent.getSliceTextField().setText(chunk.getSlicePresentation());
myComponent.getFormatTextField().setText(chunk.getFormat());
myDialog.setTitle(getTitlePresentation(chunk.getSlicePresentation()));
myTableCellRenderer = new ArrayTableCellRenderer(Double.MIN_VALUE, Double.MIN_VALUE, chunk.getType());
fillColorRange(chunk.getMin(), chunk.getMax());
if (!isNumeric()) {
disableColor();
}
else {
myComponent.getColoredCheckbox().setEnabled(true);
}
if (!inPlace) {
myComponent.getScrollPane().getViewport().setViewPosition(new Point(0, 0));
JBTableWithRowHeaders.RowHeaderTable rowTable = ((JBTableWithRowHeaders)myTable).getRowHeaderTable();
rowTable.setRowShift(0);
}
((AsyncArrayTableModel)myTable.getModel()).fireTableDataChanged();
((AsyncArrayTableModel)myTable.getModel()).fireTableCellUpdated(0, 0);
if (myTable.getColumnCount() > 0) {
myTable.setDefaultRenderer(myTable.getColumnClass(0), myTableCellRenderer);
}
});
}
private static String getTitlePresentation(String slice) {
@Override
protected final String getTitlePresentation(String slice) {
return "Array View: " + slice;
}
private void fillColorRange(String minValue, String maxValue) {
double min;
double max;
if ("c".equals(myDtypeKind)) {
min = 0;
max = 1;
myTableCellRenderer.setComplexMin(minValue);
myTableCellRenderer.setComplexMax(maxValue);
myArrayTableCellRenderer.setComplexMin(minValue);
myArrayTableCellRenderer.setComplexMax(maxValue);
}
else if ("b".equals(myDtypeKind)) {
min = minValue.equals("True") ? 1 : 0;
......@@ -230,145 +71,15 @@ public class NumpyArrayTable {
max = Double.parseDouble(maxValue);
}
myTableCellRenderer.setMin(min);
myTableCellRenderer.setMax(max);
}
public String getSliceText() {
return myComponent.getSliceTextField().getText();
myArrayTableCellRenderer.setMin(min);
myArrayTableCellRenderer.setMax(max);
}
@Override
public boolean isNumeric() {
if (myDtypeKind != null) {
return "biufc".contains(myDtypeKind.substring(0, 1));
}
return false;
}
private void initTableModel(final boolean inPlace) {
myPagingModel = new AsyncArrayTableModel(myPagingModel.getRowCount(), myPagingModel.getColumnCount(), this);
UIUtil.invokeLaterIfNeeded(() -> {
myTable.setModel(myPagingModel);
if (!inPlace) {
myComponent.getScrollPane().getViewport().setViewPosition(new Point(0, 0));
JBTableWithRowHeaders.RowHeaderTable rowTable = ((JBTableWithRowHeaders)myTable).getRowHeaderTable();
rowTable.setRowShift(0);
}
((AsyncArrayTableModel)myTable.getModel()).fireTableDataChanged();
((AsyncArrayTableModel)myTable.getModel()).fireTableCellUpdated(0, 0);
if (myTable.getColumnCount() > 0) {
myTable.setDefaultRenderer(myTable.getColumnClass(0), myTableCellRenderer);
}
});
}
public String correctStringValue(@NotNull Object value) {
if (value instanceof String) {
String corrected = (String)value;
if (isNumeric()) {
if (corrected.startsWith("\'") || corrected.startsWith("\"")) {
corrected = corrected.substring(1, corrected.length() - 1);
}
}
return corrected;
}
else if (value instanceof Integer) {
return Integer.toString((Integer)value);
}
return value.toString();
}
public void setDtypeKind(String dtype) {
this.myDtypeKind = dtype;
}
public void showError(String message) {
myDialog.setError(message);
}
public void showInfoHint(final String message) {
UIUtil.invokeLaterIfNeeded(() -> {
if (myComponent.getSliceTextField().getEditor() != null) {
HintManager.getInstance().showInformationHint(myComponent.getSliceTextField().getEditor(), message);
}
});
}
private void doReslice() {
clearErrorMessage();
init(getSliceText(), false);
}
private void clearErrorMessage() {
showError(null);
}
private void doApplyFormat() {
reset();
}
private void reset() {
clearErrorMessage();
initTableModel(true);
}
/**
* @return double presentation from [0:1] range
*/
public static double getRangedValue(String value, String type, double min, double max, String complexMax, String complexMin) {
if ("iuf".contains(type)) {
return (Double.parseDouble(value) - min) / (max - min);
}
else if ("b".equals(type)) {
return value.equals("True") ? 1 : 0;
}
else if ("c".equals(type)) {
return getComplexRangedValue(value, complexMax, complexMin);
}
return 0;
}
/**
* type complex128 in numpy is compared by next rule:
* A + Bj > C +Dj if A > C or A == C and B > D
*/
private static double getComplexRangedValue(String value, String complexMax, String complexMin) {
Pair<Double, Double> med = parsePyComplex(value);
Pair<Double, Double> max = parsePyComplex(complexMax);
Pair<Double, Double> min = parsePyComplex(complexMin);
double range = (med.first - min.first) / (max.first - min.first);
if (max.first.equals(min.first)) {
range = (med.second - min.second) / (max.second - min.second);
}
return range;
}
private static Pair<Double, Double> parsePyComplex(@NotNull String pyComplexValue) {
if (pyComplexValue.startsWith("(") && pyComplexValue.endsWith(")")) {
pyComplexValue = pyComplexValue.substring(1, pyComplexValue.length() - 1);
}
Matcher matcher = PY_COMPLEX_NUMBER.matcher(pyComplexValue);
if (matcher.matches()) {
String real = matcher.group(1);
String imag = matcher.group(2);
if (real.contains("j") && imag == null) {
return new Pair(new Double(0.0), Double.parseDouble(real.substring(0, real.length() - 1)));
}
else {
return new Pair(Double.parseDouble(real), Double.parseDouble(imag.substring(0, imag.length() - 1)));
}
}
else {
throw new IllegalArgumentException("Not a valid python complex value: " + pyComplexValue);
}
}
public String getNodeFullName() {
return getDebugValue().getEvaluationExpression();
}
public String getFormat() {
return myComponent.getFormatTextField().getText();
}
}
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger.array;
import com.jetbrains.python.debugger.ArrayChunk;
import com.jetbrains.python.debugger.PyDebuggerException;
import org.jetbrains.annotations.NotNull;
/**
* Created by Alla on 4/23/2016.
*/
public interface TableChunkDatasource {
String correctStringValue(@NotNull Object value);
void showError(String message);
ArrayChunk getChunk(int rowOffset, int colOffset, int rows, int cols) throws PyDebuggerException;
}
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger.containerview;
import javax.swing.table.TableCellRenderer;
/**
* Created by Yuli Fiterman on 5/10/2016.
*/
public interface ColoredCellRenderer extends TableCellRenderer {
void setColored(boolean colored);
}
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.debugger.array.ArrayTableForm">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="2" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.debugger.containerview.NumericContainerRendererForm">
<grid id="27dc6" binding="myMainPanel" layout-manager="GridLayoutManager" row-count="3" column-count="5" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="608" height="375"/>
......@@ -18,7 +18,7 @@
</properties>
<border type="none"/>
<children>
<component id="c7f7c" class="com.intellij.ui.table.JBTable" binding="myTable" custom-create="true">
<component id="c7f7c" class="com.jetbrains.python.debugger.array.JBTableWithRowHeaders" binding="myTable" custom-create="true">
<constraints/>
<properties>
<autoResizeMode value="0"/>
......
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -13,144 +13,90 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger.array;
package com.jetbrains.python.debugger.containerview;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.project.Project;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.table.JBTable;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.debugger.PyDebugValue;
import com.jetbrains.python.debugger.array.JBTableWithRowHeaders;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import java.awt.*;
import java.awt.event.KeyListener;
/**
* @author amarch
* Created by Yuli Fiterman on 5/10/2016.
*/
public class ArrayTableForm {
private EditorTextField mySliceTextField;
private JCheckBox myColoredCheckbox;
private EditorTextField myFormatTextField;
private JBScrollPane myScrollPane;
private JLabel myFormatLabel;
private JPanel myFormatPanel;
private JPanel myMainPanel;
private JTable myTable;
private final Project myProject;
private KeyListener myResliceCallback;
private KeyListener myReformatCallback;
private static final String DATA_LOADING_IN_PROCESS = "Please wait, load array data.";
private static final String NOT_APPLICABLE = "View not applicable for ";
public ArrayTableForm(@NotNull Project project, KeyListener resliceCallback, KeyListener reformatCallback) {
myProject = project;
public class NumericContainerRendererForm {
protected final Project myProject;
protected final KeyListener myReformatCallback;
protected final KeyListener myResliceCallback;
protected JBScrollPane myScrollPane;
protected EditorTextField mySliceTextField;
protected JBTableWithRowHeaders myTable;
protected EditorTextField myFormatTextField;
protected JCheckBox myColoredCheckbox;
protected JPanel myFormatPanel;
protected JPanel myMainPanel;
protected JLabel myFormatLabel;
public NumericContainerRendererForm(
@NotNull Project project, KeyListener resliceCallback, KeyListener reformatCallback) {
myResliceCallback = resliceCallback;
myProject = project;
myReformatCallback = reformatCallback;
}
private void createUIComponents() {
mySliceTextField = new EditorTextField("", myProject, PythonFileType.INSTANCE) {
@Override
protected EditorEx createEditor() {
EditorEx editor = super.createEditor();
editor.getContentComponent().addKeyListener(myResliceCallback);
return editor;
}
};
myTable = new JBTableWithRowHeaders();
myScrollPane = ((JBTableWithRowHeaders)myTable).getScrollPane();
public JBTable getTable() {
return myTable;
}
myFormatTextField = new EditorTextField("", myProject, PythonFileType.INSTANCE) {
@Override
protected EditorEx createEditor() {
EditorEx editor = super.createEditor();
editor.getContentComponent().addKeyListener(myReformatCallback);
return editor;
}
};
public JPanel getMainPanel() {
return myMainPanel;
}
public EditorTextField getSliceTextField() {
return mySliceTextField;
}
public EditorTextField getFormatTextField() {
return myFormatTextField;
}
public JTable getTable() {
return myTable;
}
public JCheckBox getColoredCheckbox() {
return myColoredCheckbox;
}
//public void setDefaultStatus() {
// if (myTable != null) {
//myTable.getEmptyText().setText(DATA_LOADING_IN_PROCESS);
//}
//}
//public void setNotApplicableStatus(PyDebugValue node) {
// myTable.getEmptyText().setText(NOT_APPLICABLE + node.getName());
//}
public JComponent getMainPanel() {
return myMainPanel;
}
public JBScrollPane getScrollPane() {
return myScrollPane;
}
public static class ColumnHeaderRenderer extends DefaultTableHeaderCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) {
super.getTableCellRendererComponent(table, value, selected, focused, row, column);
int selectedColumn = table.getSelectedColumn();
if (selectedColumn == column) {
setFont(getFont().deriveFont(Font.BOLD));
}
return this;
}
public EditorTextField getFormatTextField() {
return myFormatTextField;
}
public static class DefaultTableHeaderCellRenderer extends DefaultTableCellRenderer {
public DefaultTableHeaderCellRenderer() {
setHorizontalAlignment(CENTER);
setHorizontalTextPosition(LEFT);
setVerticalAlignment(BOTTOM);
setOpaque(false);
}
protected void createUIComponents() {
mySliceTextField = new EditorTextField("", myProject, PythonFileType.INSTANCE) {
@Override
protected EditorEx createEditor() {
EditorEx editor = super.createEditor();
editor.getContentComponent().addKeyListener(myResliceCallback);
return editor;
}
};
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
JTableHeader tableHeader = table.getTableHeader();
if (tableHeader != null) {
setForeground(tableHeader.getForeground());
myFormatTextField = new EditorTextField("", myProject, PythonFileType.INSTANCE) {
@Override
protected EditorEx createEditor() {
EditorEx editor = super.createEditor();
editor.getContentComponent().addKeyListener(myReformatCallback);
return editor;
}
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
return this;
}
};
myTable = new JBTableWithRowHeaders();
myScrollPane = myTable.getScrollPane();
}
}
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger.containerview;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.project.Project;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.debugger.ArrayChunk;
import com.jetbrains.python.debugger.PyDebugValue;
import com.jetbrains.python.debugger.PyDebuggerException;
import com.jetbrains.python.debugger.array.AsyncArrayTableModel;
import com.jetbrains.python.debugger.array.TableChunkDatasource;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* Created by Yuli Fiterman 5/10/2016.
*/
public abstract class NumericContainerViewTable implements TableChunkDatasource {
protected final static int COLUMNS_IN_DEFAULT_VIEW = 1000;
protected final static int ROWS_IN_DEFAULT_VIEW = 1000;
protected static final Logger LOG = Logger.getInstance("#com.jetbrains.python.debugger.containerview.NumericContainerViewTable");
protected final PyDebugValue myValue;
protected final ViewNumericContainerDialog myDialog;
protected final NumericContainerRendererForm myComponent;
protected final JTable myTable;
protected final Project myProject;
protected String myDtypeKind;
protected ColoredCellRenderer myTableCellRenderer;
protected AsyncArrayTableModel myPagingModel;
public NumericContainerViewTable(
@NotNull Project project, @NotNull ViewNumericContainerDialog dialog, @NotNull PyDebugValue value) {
myProject = project;
myDialog = dialog;
myComponent = createForm(project, new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
doReslice();
}
}
}, new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
doApplyFormat();
}
}
});
myValue = value;
myTable = myComponent.getTable();
}
private void initUi(@NotNull final ArrayChunk chunk, final boolean inPlace) {
myPagingModel = createTableModel(Math.min(chunk.getRows(), ROWS_IN_DEFAULT_VIEW),
Math.min(chunk.getColumns(), COLUMNS_IN_DEFAULT_VIEW));
myPagingModel.addToCache(chunk);
myDtypeKind = chunk.getType();
UIUtil.invokeLaterIfNeeded(() -> {
myTable.setModel(myPagingModel);
myComponent.getSliceTextField().setText(chunk.getSlicePresentation());
myComponent.getFormatTextField().setText(chunk.getFormat());
myDialog.setTitle(getTitlePresentation(chunk.getSlicePresentation()));
myTableCellRenderer = createCellRenderer(Double.MIN_VALUE, Double.MIN_VALUE, chunk);
if (!isNumeric()) {
disableColor();
}
else {
myComponent.getColoredCheckbox().setEnabled(true);
}
if (!inPlace) {
myComponent.getScrollPane().getViewport().setViewPosition(new Point(0, 0));
}
((AsyncArrayTableModel)myTable.getModel()).fireTableDataChanged();
((AsyncArrayTableModel)myTable.getModel()).fireTableCellUpdated(0, 0);
if (myTable.getColumnCount() > 0) {
myTable.setDefaultRenderer(myTable.getColumnClass(0), myTableCellRenderer);
}
});
}
private void disableColor() {
myTableCellRenderer.setColored(false);
UIUtil.invokeLaterIfNeeded(() -> {
myComponent.getColoredCheckbox().setSelected(false);
myComponent.getColoredCheckbox().setEnabled(false);
if (myTable.getColumnCount() > 0) {
myTable.setDefaultRenderer(myTable.getColumnClass(0), myTableCellRenderer);
}
});
}
protected abstract AsyncArrayTableModel createTableModel(int rowCount, int columnCount);
protected abstract ColoredCellRenderer createCellRenderer(double minValue, double maxValue, ArrayChunk chunk);
private void initTableModel(final boolean inPlace) {
myPagingModel = createTableModel(myPagingModel.getRowCount(), myPagingModel.getColumnCount());
UIUtil.invokeLaterIfNeeded(() -> {
myTable.setModel(myPagingModel);
if (!inPlace) {
myComponent.getScrollPane().getViewport().setViewPosition(new Point(0, 0));
}
((AsyncArrayTableModel)myTable.getModel()).fireTableDataChanged();
((AsyncArrayTableModel)myTable.getModel()).fireTableCellUpdated(0, 0);
if (myTable.getColumnCount() > 0) {
myTable.setDefaultRenderer(myTable.getColumnClass(0), myTableCellRenderer);
}
});
}
@NotNull
private static NumericContainerRendererForm createForm(@NotNull Project project, KeyListener resliceCallback, KeyAdapter formatCallback) {
return new NumericContainerRendererForm(project, resliceCallback, formatCallback);
}
protected abstract String getTitlePresentation(String slice);
public final NumericContainerRendererForm getComponent() {
return myComponent;
}
private void initComponent() {
myComponent.getColoredCheckbox().setEnabled(false);
myComponent.getColoredCheckbox().addItemListener(e -> {
if (e.getSource() == myComponent.getColoredCheckbox()) {
if (myTable.getRowCount() > 0 &&
myTable.getColumnCount() > 0 &&
myTable.getCellRenderer(0, 0) instanceof ColoredCellRenderer) {
ColoredCellRenderer renderer = (ColoredCellRenderer)myTable.getCellRenderer(0, 0);
if (myComponent.getColoredCheckbox().isSelected()) {
renderer.setColored(true);
}
else {
renderer.setColored(false);
}
}
if (myTableCellRenderer != null) {
myTableCellRenderer.setColored(myComponent.getColoredCheckbox().isSelected());
}
myComponent.getScrollPane().repaint();
}
});
//make value name read-only
myComponent.getSliceTextField().addFocusListener(new FocusListener() {
@Override
public void focusGained(FocusEvent e) {
myComponent.getSliceTextField().getDocument().createGuardedBlock(0, getNodeFullName().length());
}
@Override
public void focusLost(FocusEvent e) {
RangeMarker block = myComponent.getSliceTextField().getDocument().getRangeGuard(0, getNodeFullName().length());
if (block != null) {
myComponent.getSliceTextField().getDocument().removeGuardedBlock(block);
}
}
});
}
public PyDebugValue getDebugValue() {
return myValue;
}
public void init() {
init(getDebugValue().getEvaluationExpression(), false);
}
public void init(final String slice, final boolean inPlace) {
initComponent();
myComponent.getSliceTextField().setText(slice);
ApplicationManager.getApplication().executeOnPooledThread(() -> {
final PyDebugValue value = getDebugValue();
PyDebugValue parent = value.getParent();
final PyDebugValue slicedValue =
new PyDebugValue(slice, value.getType(), null, value.getValue(), value.isContainer(), value.isReturnedVal(), value.isErrorOnEval(),
parent, value.getFrameAccessor());
final String format = getFormat().isEmpty() ? "%" : getFormat();
try {
initUi(value.getFrameAccessor()
.getArrayItems(slicedValue, 0, 0, -1, -1, format), inPlace);
}
catch (PyDebuggerException e) {
showError(e.getMessage());
}
});
}
public String getSliceText() {
return myComponent.getSliceTextField().getText();
}
@Override
public ArrayChunk getChunk(int rowOffset, int colOffset, int rows, int cols) throws PyDebuggerException {
final PyDebugValue slicedValue =
new PyDebugValue(getSliceText(), myValue.getType(), myValue.getTypeQualifier(), myValue.getValue(), myValue.isContainer(),
myValue.isErrorOnEval(), myValue.isReturnedVal(),
myValue.getParent(), myValue.getFrameAccessor());
return myValue.getFrameAccessor().getArrayItems(slicedValue, rowOffset, colOffset, rows, cols, getFormat());
}
public abstract boolean isNumeric();
@Override
public final String correctStringValue(@NotNull Object value) {
if (value instanceof String) {
String corrected = (String)value;
if (isNumeric()) {
if (corrected.startsWith("\'") || corrected.startsWith("\"")) {
corrected = corrected.substring(1, corrected.length() - 1);
}
}
return corrected;
}
else if (value instanceof Integer) {
return Integer.toString((Integer)value);
}
return value.toString();
}
@Override
public final void showError(String message) {
myDialog.setError(message);
}
protected final void doReslice() {
clearErrorMessage();
init(getSliceText(), false);
}
private void clearErrorMessage() {
showError(null);
}
protected final void doApplyFormat() {
reset();
}
private void reset() {
clearErrorMessage();
initTableModel(true);
}
public final String getNodeFullName() {
return getDebugValue().getEvaluationExpression();
}
public final String getFormat() {
return myComponent.getFormatTextField().getText();
}
}
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger.containerview;
import com.intellij.openapi.util.Pair;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by Yuli Fiterman on 5/10/2016.
*/
public class PyNumericViewUtil {
private static final Pattern PY_COMPLEX_NUMBER = Pattern.compile("([+-]?[.\\d^j]*)([+-]?[e.\\d]*j)?");
/**
* @return double presentation from [0:1] range
*/
public static double getRangedValue(String value, String type, double min, double max, String complexMax, String complexMin) {
if ("iuf".contains(type)) {
return (Double.parseDouble(value) - min) / (max - min);
}
else if ("b".equals(type)) {
return value.equals("True") ? 1 : 0;
}
else if ("c".equals(type)) {
return getComplexRangedValue(value, complexMax, complexMin);
}
return 0;
}
public static Color rangedValueToColor(double rangedValue)
{
//noinspection UseJBColor
return new Color((int)Math.round(255 * rangedValue), 0, (int)Math.round(255 * (1 - rangedValue)), 130);
}
/**
* type complex128 in numpy is compared by next rule:
* A + Bj > C +Dj if A > C or A == C and B > D
*/
private static double getComplexRangedValue(String value, String complexMax, String complexMin) {
Pair<Double, Double> med = parsePyComplex(value);
Pair<Double, Double> max = parsePyComplex(complexMax);
Pair<Double, Double> min = parsePyComplex(complexMin);
double range = (med.first - min.first) / (max.first - min.first);
if (max.first.equals(min.first)) {
range = (med.second - min.second) / (max.second - min.second);
}
return range;
}
private static Pair<Double, Double> parsePyComplex(@NotNull String pyComplexValue) {
if (pyComplexValue.startsWith("(") && pyComplexValue.endsWith(")")) {
pyComplexValue = pyComplexValue.substring(1, pyComplexValue.length() - 1);
}
Matcher matcher = PY_COMPLEX_NUMBER.matcher(pyComplexValue);
if (matcher.matches()) {
String real = matcher.group(1);
String imag = matcher.group(2);
if (real.contains("j") && imag == null) {
return new Pair<>(new Double(0.0), Double.parseDouble(real.substring(0, real.length() - 1)));
}
else {
return new Pair<>(Double.parseDouble(real), Double.parseDouble(imag.substring(0, imag.length() - 1)));
}
}
else {
throw new IllegalArgumentException("Not a valid python complex value: " + pyComplexValue);
}
}
}
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -13,33 +13,53 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger.array;
package com.jetbrains.python.debugger.containerview;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree;
import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import com.jetbrains.python.debugger.PyDebugValue;
import com.jetbrains.python.debugger.array.NumpyArrayTable;
import com.jetbrains.python.debugger.dataframe.DataFrameTable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.TreePath;
/**
* @author amarch
*/
public class PyViewArrayAction extends XDebuggerTreeActionBase {
public class PyViewNumericContainerAction extends XDebuggerTreeActionBase {
@Override
protected void perform(XValueNodeImpl node, @NotNull String nodeName, AnActionEvent e) {
Project p = e.getProject();
if (p != null && node != null && node.getValueContainer() instanceof PyDebugValue && node.isComputed()) {
final ViewArrayDialog dialog = new ViewArrayDialog(p, (PyDebugValue)(node.getValueContainer()));
PyDebugValue debugValue = (PyDebugValue)node.getValueContainer();
String nodeType = debugValue.getType();
final ViewNumericContainerDialog dialog;
if ("ndarray".equals(nodeType)) {
dialog = new ViewNumericContainerDialog(p, (dialogWrapper) -> {
NumpyArrayTable arrayTable = new NumpyArrayTable(p, dialogWrapper, debugValue);
arrayTable.init();
return arrayTable.getComponent().getMainPanel();
});
}
else if (("DataFrame".equals(nodeType))) {
dialog = new ViewNumericContainerDialog(p, (dialogWrapper) -> {
DataFrameTable dataFrameTable = new DataFrameTable(p, dialogWrapper, debugValue);
dataFrameTable.init();
return dataFrameTable.getComponent().getMainPanel();
});
}
else {
throw new IllegalStateException("Cannot render node type: " + nodeType);
}
dialog.show();
}
}
......@@ -65,54 +85,21 @@ public class PyViewArrayAction extends XDebuggerTreeActionBase {
String nodeType = debugValue.getType();
if ("ndarray".equals(nodeType)) {
e.getPresentation().setText("View as Array");
e.getPresentation().setVisible(true);
return;
}
else if (("DataFrame".equals(nodeType))) {
e.getPresentation().setText("View as DataFrame");
e.getPresentation().setVisible(true);
}
else {
e.getPresentation().setVisible(false);
}
}
}
e.getPresentation().setVisible(false);
}
protected static class ViewArrayDialog extends DialogWrapper {
private Project myProject;
private NumpyArrayTable myNumpyArrayTable;
private ViewArrayDialog(@NotNull Project project, PyDebugValue debugValue) {
super(project, false);
setModal(false);
setCancelButtonText("Close");
setCrossClosesWindow(true);
myProject = project;
myNumpyArrayTable = new NumpyArrayTable(myProject, this, debugValue);
myNumpyArrayTable.init();
init();
}
public void setError(String text) {
//todo: think about this usage
setErrorText(text);
}
@Override
@NotNull
protected Action[] createActions() {
return new Action[]{getCancelAction()};
}
@Override
protected String getDimensionServiceKey() {
return "#com.jetbrains.python.actions.view.array.PyViewArrayAction";
}
@Override
protected JComponent createCenterPanel() {
return myNumpyArrayTable.getComponent().getMainPanel();
}
public ArrayTableForm getComponent() {
return myNumpyArrayTable.getComponent();
else
{
e.getPresentation().setVisible(false);
}
}
}
}
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger.containerview;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.function.Function;
/**
* Created by Yuli Fiterman on 5/10/2016.
*/
public class ViewNumericContainerDialog extends DialogWrapper {
private final JComponent myMainPanel;
ViewNumericContainerDialog(@NotNull Project project, Function<ViewNumericContainerDialog, JComponent> tableSupplier) {
super(project, false);
setModal(false);
setCancelButtonText("Close");
setCrossClosesWindow(true);
myMainPanel = tableSupplier.apply(this);
init();
}
public void setError(String text) {
//todo: think about this usage
setErrorText(text);
}
@Override
@NotNull
protected Action[] createActions() {
return new Action[]{getCancelAction()};
}
@Override
protected String getDimensionServiceKey() {
return "#com.jetbrains.python.actions.view.array.PyViewNumericContainerAction";
}
@Override
protected JComponent createCenterPanel() {
return myMainPanel;
}
}
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.debugger.dataframe;
import com.intellij.openapi.project.Project;
import com.intellij.util.ui.UIUtil;
import com.jetbrains.python.debugger.*;
import com.jetbrains.python.debugger.array.AsyncArrayTableModel;
import com.jetbrains.python.debugger.containerview.ColoredCellRenderer;
import com.jetbrains.python.debugger.containerview.NumericContainerViewTable;
import com.jetbrains.python.debugger.array.TableChunkDatasource;
import com.jetbrains.python.debugger.containerview.NumericContainerRendererForm;
import com.jetbrains.python.debugger.containerview.ViewNumericContainerDialog;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.awt.event.*;
/**
* @author amarch
*/
/* A bunch of this is copied from NumpyArrayTable*/
public class DataFrameTable extends NumericContainerViewTable implements TableChunkDatasource {
private Project myProject;
public DataFrameTable(@NotNull Project project,
@NotNull ViewNumericContainerDialog dialog, @NotNull PyDebugValue value) {
super(project, dialog, value);
}
@Override
protected AsyncArrayTableModel createTableModel(int rowCount, int columnCount) {
return new DataFrameTableModel(rowCount, columnCount, this);
}
@Override
protected ColoredCellRenderer createCellRenderer(double minValue, double maxValue, ArrayChunk chunk) {
return new DataFrameTableCellRenderer();
}
@Override
public boolean isNumeric() {
return true;
}
protected final String getTitlePresentation(String slice) {
return "DataFrame View: " + slice;
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment