[PyCharm] PY-75914 Jupyter (feat): From scientific.py.tables also removed dependency to intellij.notebooks.visualization. Added suggestion to install Jupyter to view rich tables.

GitOrigin-RevId: 705a0f5f7883517da51da0f0c532eb90fd50e5ac
This commit is contained in:
Nikita Pavlenko
2024-10-01 13:04:25 +00:00
committed by intellij-monorepo-bot
parent 0220055ba2
commit 382b256f33
3 changed files with 134 additions and 84 deletions
@@ -747,6 +747,7 @@ debugger.data.view.data=Data
debugger.data.view.close=Close
debugger.data.view.resize.automatically=Resize automatically
debugger.data.view.colored.cells=Colored cells
debugger.data.view.reach.view.suggestion=Reach table viewer could be enabled
debugger.data.view.failed.to.evaluate.expression=Failed to evaluate the expression {0}
debugger.data.view.numpy.is.not.available=NumPy is not available. Install the module for proper data display.
debugger.data.view.type.is.not.supported={0} is not supported
@@ -251,6 +251,11 @@ class PyDataView(private val project: Project) : DumbAware {
PropertiesComponent.getInstance(project).setValue(COLORED_BY_DEFAULT, value, true)
}
@JvmStatic
fun setAutoResizeEnabled(project: Project, value: Boolean) {
PropertiesComponent.getInstance(project).setValue(AUTO_RESIZE, value, true)
}
fun getInstance(project: Project): PyDataView = project.service<PyDataView>()
}
}
@@ -1,105 +1,149 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.debugger.containerview;
package com.jetbrains.python.debugger.containerview
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.NlsContexts;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.util.ui.JBUI;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.debugger.PyDebugValue;
import org.jetbrains.annotations.NotNull;
import com.intellij.ide.IdeBundle
import com.intellij.ide.plugins.PluginManager
import com.intellij.ide.plugins.PluginManagerConfigurable
import com.intellij.ide.plugins.PluginManagerCore
import com.intellij.ide.util.PropertiesComponent
import com.intellij.openapi.extensions.PluginId
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.updateSettings.impl.pluginsAdvertisement.installAndEnable
import com.intellij.openapi.util.NlsContexts
import com.intellij.ui.EditorNotificationPanel
import com.intellij.ui.components.JBCheckBox
import com.intellij.util.ui.JBUI
import com.jetbrains.python.PyBundle
import com.jetbrains.python.debugger.PyDebugValue
import com.jetbrains.python.debugger.containerview.PyDataView.Companion.isAutoResizeEnabled
import com.jetbrains.python.debugger.containerview.PyDataView.Companion.isColoringEnabled
import com.jetbrains.python.debugger.containerview.PyDataView.Companion.setAutoResizeEnabled
import com.jetbrains.python.debugger.containerview.PyDataView.Companion.setColoringEnabled
import java.awt.BorderLayout
import java.awt.event.ActionEvent
import java.awt.event.ActionListener
import javax.swing.Action
import javax.swing.BoxLayout
import javax.swing.JPanel
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class PyDataViewDialog(private val myProject: Project, value: PyDebugValue) : DialogWrapper(myProject, false) {
private val mainPanel: JPanel
private val dataViewerPanel: PyDataViewerPanel
private var jupyterSuggestionPanel: JPanel? = null
public class PyDataViewDialog extends DialogWrapper {
private final JPanel myMainPanel;
private static final int TABLE_DEFAULT_WIDTH = 700;
private static final int TABLE_DEFAULT_HEIGHT = 500;
private final Project myProject;
private final PyDataViewerPanel myDataViewerPanel;
init {
isModal = false
setCancelButtonText(PyBundle.message("debugger.data.view.close"))
setCrossClosesWindow(true)
public PyDataViewDialog(@NotNull Project project, final @NotNull PyDebugValue value) {
super(project, false);
myProject = project;
setModal(false);
setCancelButtonText(PyBundle.message("debugger.data.view.close"));
setCrossClosesWindow(true);
dataViewerPanel = PyDataViewerPanel(myProject, value.frameAccessor)
dataViewerPanel.apply(value, modifier = false, commandSource = null)
dataViewerPanel.preferredSize = JBUI.size(TABLE_DEFAULT_WIDTH, TABLE_DEFAULT_HEIGHT)
myMainPanel = new JPanel(new GridBagLayout());
myDataViewerPanel = new PyDataViewerPanel(project, value.getFrameAccessor());
myDataViewerPanel.apply(value, false, /* commandSource = */ null);
myDataViewerPanel.setPreferredSize(JBUI.size(TABLE_DEFAULT_WIDTH, TABLE_DEFAULT_HEIGHT));
mainPanel = JPanel(BorderLayout())
myMainPanel.add(myDataViewerPanel, createDataViewPanelConstraints());
myDataViewerPanel.addListener(new PyDataViewerPanel.Listener() {
@Override
public void onNameChanged(@NlsContexts.TabTitle @NotNull String name) {
setTitle(name);
// If PythonPro installed and enabled.
val pythonPluginDescriptor = PluginManagerCore.getPlugin(PluginId.getId("Pythonid"))
if (isJupyterSuggestionEnabled(myProject) && pythonPluginDescriptor?.isEnabled == true) {
val jupyterPluginId = PluginId.findId("intellij.jupyter")
if (jupyterPluginId != null) {
val jupyterSuggestionPanel = createJupyterSuggestionPanel(jupyterPluginId)
this.jupyterSuggestionPanel = jupyterSuggestionPanel
mainPanel.add(jupyterSuggestionPanel, BorderLayout.NORTH)
}
});
}
addBottomElements();
mainPanel.add(dataViewerPanel, BorderLayout.CENTER)
setTitle(value.getFullName());
init();
}
protected void addBottomElements() {
final JBCheckBox colored = new JBCheckBox(PyBundle.message("debugger.data.view.colored.cells"));
final JBCheckBox resize = new JBCheckBox(PyBundle.message("debugger.data.view.resize.automatically"));
resize.setSelected(PyDataView.isAutoResizeEnabled(myProject));
colored.setSelected(PyDataView.isColoringEnabled(myProject));
colored.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myDataViewerPanel.setColored(colored.isSelected());
myDataViewerPanel.updateUI();
dataViewerPanel.addListener(object : PyDataViewerPanel.Listener {
override fun onNameChanged(name: @NlsContexts.TabTitle String) {
title = name
}
});
resize.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myDataViewerPanel.resize(resize.isSelected());
myDataViewerPanel.updateUI();
})
mainPanel.add(createBottomElements(), BorderLayout.SOUTH)
title = value.fullName
init()
}
private fun createBottomElements(): JPanel {
val colored = JBCheckBox(PyBundle.message("debugger.data.view.colored.cells"))
colored.setSelected(isColoringEnabled(myProject))
colored.addActionListener(object : ActionListener {
override fun actionPerformed(e: ActionEvent?) {
setColoringEnabled(myProject, colored.isSelected)
dataViewerPanel.isColored = colored.isSelected
}
});
})
GridBagConstraints checkBoxConstraints = createCheckBoxConstraints();
checkBoxConstraints.gridy = 1;
myMainPanel.add(colored, checkBoxConstraints);
checkBoxConstraints.gridy = 2;
myMainPanel.add(resize, checkBoxConstraints);
val resize = JBCheckBox(PyBundle.message("debugger.data.view.resize.automatically"))
resize.setSelected(isAutoResizeEnabled(myProject))
resize.addActionListener(object : ActionListener {
override fun actionPerformed(e: ActionEvent?) {
setAutoResizeEnabled(myProject, resize.isSelected)
dataViewerPanel.resize(resize.isSelected)
dataViewerPanel.updateUI()
}
})
return JPanel().apply {
layout = BoxLayout(this, BoxLayout.Y_AXIS)
add(colored)
add(resize)
}
}
@Override
protected Action @NotNull [] createActions() {
return new Action[]{getCancelAction()};
override fun createActions() = arrayOf<Action>(cancelAction)
override fun createCenterPanel() = mainPanel
private fun createJupyterSuggestionPanel(pluginId: PluginId): EditorNotificationPanel {
val panel = EditorNotificationPanel()
panel.text = PyBundle.message("debugger.data.view.reach.view.suggestion")
if (!PluginManager.isPluginInstalled(pluginId)) {
panel.createActionLabel(IdeBundle.message("plugins.advertiser.action.install.plugin.name", "Jupyter")) {
installAndEnable(myProject, setOf(pluginId), true) {
hideJupyterSuggestionPanel()
}
}
}
else {
val jupyterPluginDescriptor = PluginManagerCore.getPlugin(PluginId.findId("com.intellij.grazie.pro"))
if (jupyterPluginDescriptor != null) {
panel.createActionLabel(IdeBundle.message("plugins.advertiser.action.enable.plugin", jupyterPluginDescriptor.name)) {
PluginManagerConfigurable.showPluginConfigurableAndEnable(myProject, setOf(jupyterPluginDescriptor))
hideJupyterSuggestionPanel()
}
}
}
panel.createActionLabel(IdeBundle.message("plugins.advertiser.action.ignore.unknown.feature")) {
disableJupyterSuggestions(myProject)
hideJupyterSuggestionPanel()
}
return panel
}
@Override
protected JComponent createCenterPanel() {
return myMainPanel;
private fun hideJupyterSuggestionPanel() {
jupyterSuggestionPanel?.let {
mainPanel.remove(it)
mainPanel.revalidate()
jupyterSuggestionPanel = null
}
}
protected GridBagConstraints createDataViewPanelConstraints() {
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.weightx = 0.9;
c.weighty = 0.9;
return c;
}
companion object {
private const val TABLE_DEFAULT_WIDTH = 700
private const val TABLE_DEFAULT_HEIGHT = 500
protected GridBagConstraints createCheckBoxConstraints() {
GridBagConstraints c = new GridBagConstraints();
c.weighty = 0.05;
c.anchor = GridBagConstraints.LINE_START;
return c;
private const val JUPYTER_SUGGESTION_ENABLED_PROPERTY_KEY = "python.debugger.dataview.jupyter.suggestion.enabled"
private fun isJupyterSuggestionEnabled(project: Project) = PropertiesComponent.getInstance(project).getBoolean(JUPYTER_SUGGESTION_ENABLED_PROPERTY_KEY, true)
private fun disableJupyterSuggestions(project: Project) = PropertiesComponent.getInstance(project).setValue(JUPYTER_SUGGESTION_ENABLED_PROPERTY_KEY, false, true)
}
}
}