PY-11166 Ability to create new projects without selecting Python interpreter

Extracted python interpreter combobox
This commit is contained in:
Ekaterina Tuzova
2016-06-20 18:05:38 +03:00
parent 06c0adf82a
commit 1348755aa4
8 changed files with 275 additions and 236 deletions
@@ -1,103 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.edu.learning.ui.StudyNewProjectPanel">
<grid id="27dc6" binding="myContentPanel" layout-manager="GridLayoutManager" row-count="3" column-count="4" 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="610" height="422"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<grid id="54488" binding="myInfoPanel" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="2" column="1" row-span="1" col-span="3" vsize-policy="2" hsize-policy="4" anchor="0" fill="3" indent="2" use-parent-layout="false">
<minimum-size width="-1" height="60"/>
<preferred-size width="-1" height="170"/>
</grid>
</constraints>
<properties/>
<border type="line">
<color color="-6709600"/>
</border>
<children>
<component id="213f6" class="javax.swing.JLabel" binding="myAuthorLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value=""/>
</properties>
</component>
<component id="50229" class="javax.swing.JTextPane" binding="myDescriptionLabel">
<constraints>
<grid row="1" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="150" height="100"/>
</grid>
</constraints>
<properties>
<alignmentX value="0.0"/>
</properties>
</component>
</children>
</grid>
<component id="6c40c" class="javax.swing.JLabel" binding="myLabel">
<constraints>
<grid row="0" column="0" row-span="2" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false">
<preferred-size width="58" height="20"/>
<maximum-size width="70" height="-1"/>
</grid>
</constraints>
<properties>
<font/>
<horizontalAlignment value="10"/>
<horizontalTextPosition value="10"/>
<iconTextGap value="0"/>
<labelFor value=""/>
<text value="Courses:"/>
</properties>
</component>
<grid id="e4f37" layout-manager="GridLayoutManager" row-count="1" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="0" vgap="-1">
<margin top="0" left="20" bottom="0" right="0"/>
<constraints>
<grid row="0" column="1" row-span="2" col-span="3" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="21ac6" class="javax.swing.JComboBox" binding="myCoursesComboBox">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false">
<preferred-size width="120" height="-1"/>
</grid>
</constraints>
<properties>
<alignmentX value="0.0"/>
<alignmentY value="0.0"/>
</properties>
</component>
<component id="b24e4" class="com.intellij.openapi.ui.FixedSizeButton" binding="myBrowseButton">
<constraints>
<grid row="0" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<horizontalTextPosition value="0"/>
<text value=""/>
<toolTipText value="Select local course"/>
</properties>
</component>
<component id="7834e" class="com.intellij.openapi.ui.FixedSizeButton" binding="myRefreshButton">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="0" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<horizontalTextPosition value="0"/>
<text value=""/>
<toolTipText value="Refresh course list"/>
</properties>
</component>
</children>
</grid>
</children>
</grid>
</form>
@@ -4,7 +4,6 @@ import com.intellij.facet.ui.FacetValidatorsManager;
import com.intellij.facet.ui.ValidationResult;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.progress.ProgressManager;
@@ -12,6 +11,7 @@ import com.intellij.openapi.project.DefaultProjectFactory;
import com.intellij.openapi.project.DefaultProjectFactoryImpl;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.*;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.ui.popup.PopupStep;
@@ -19,15 +19,17 @@ import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.AncestorListenerAdapter;
import com.intellij.ui.JBColor;
import com.intellij.ui.PanelWithAnchor;
import com.jetbrains.edu.learning.StudyUtils;
import com.jetbrains.edu.learning.courseFormat.Course;
import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.stepic.CourseInfo;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import com.jetbrains.edu.learning.stepic.LoginDialog;
import com.jetbrains.edu.learning.stepic.StepicUser;
import icons.InteractiveLearningIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.AncestorEvent;
@@ -41,39 +43,38 @@ import java.util.List;
* author: liana
* data: 7/31/14.
*/
public class StudyNewProjectPanel {
private static final Logger LOG = Logger.getInstance(StudyNewProjectPanel.class);
private final String LOGIN_TO_STEPIC = "Login to Stepic";
public class StudyNewProjectPanel extends JPanel implements PanelWithAnchor {
private List<CourseInfo> myAvailableCourses = new ArrayList<CourseInfo>();
private JButton myBrowseButton;
private JComboBox<CourseInfo> myCoursesComboBox;
private ComboBox<CourseInfo> myCoursesComboBox;
private JButton myRefreshButton;
private JPanel myContentPanel;
private JLabel myAuthorLabel;
private JLabel myLabel;
private JPanel myInfoPanel;
private JTextPane myDescriptionLabel;
private JTextPane myDescriptionPane;
private JComponent myAnchor;
private final StudyProjectGenerator myGenerator;
private static final String CONNECTION_ERROR = "<html>Failed to download courses.<br>Check your Internet connection.</html>";
private static final String INVALID_COURSE = "Selected course is invalid";
private FacetValidatorsManager myValidationManager;
private boolean isComboboxInitialized;
private String LOGIN_TO_STEPIC_MESSAGE = "<html><u>Login to Stepic</u> to open the adaptive course </html>";;
private static final String LOGIN_TO_STEPIC_MESSAGE = "<html><u>Login to Stepic</u> to open the adaptive course </html>";
private static final String LOGIN_TO_STEPIC = "Login to Stepic";
public StudyNewProjectPanel(@NotNull final StudyProjectGenerator generator) {
super(new VerticalFlowLayout());
layoutPanel();
myAnchor = myCoursesComboBox;
myGenerator = generator;
myBrowseButton.setPreferredSize(new Dimension(28, 28));
myRefreshButton.setPreferredSize(new Dimension(28, 28));
initListeners();
myRefreshButton.setVisible(true);
myRefreshButton.putClientProperty("JButton.buttonType", "null");
myRefreshButton.setIcon(AllIcons.Actions.Refresh);
myLabel.setPreferredSize(new JLabel("Project name").getPreferredSize());
myContentPanel.addAncestorListener(new AncestorListenerAdapter() {
addAncestorListener(new AncestorListenerAdapter() {
@Override
public void ancestorMoved(AncestorEvent event) {
if (!isComboboxInitialized && myContentPanel.isVisible()) {
if (!isComboboxInitialized && isVisible()) {
isComboboxInitialized = true;
initCoursesCombobox();
}
@@ -81,6 +82,34 @@ public class StudyNewProjectPanel {
});
}
private void layoutPanel() {
myCoursesComboBox = new ComboBox<CourseInfo>();
final LabeledComponent<ComboBox> coursesCombo = LabeledComponent.create(myCoursesComboBox, "Courses:", BorderLayout.WEST);
add(coursesCombo);
myRefreshButton = new FixedSizeButton(coursesCombo);
myBrowseButton = new FixedSizeButton(coursesCombo);
final JPanel panel = new JPanel(new BorderLayout());
final JLabel invisibleLabel = new JLabel();
invisibleLabel.setPreferredSize(new JLabel("Location: ").getPreferredSize());
panel.add(invisibleLabel, BorderLayout.WEST);
myInfoPanel = new JPanel(new VerticalFlowLayout());
myAuthorLabel = new JLabel();
myDescriptionPane = new JTextPane();
myDescriptionPane.setEditable(true);
myDescriptionPane.setEnabled(true);
myAuthorLabel.setEnabled(true);
myDescriptionPane.setPreferredSize(new Dimension(150, 100));
myInfoPanel.add(myAuthorLabel);
myInfoPanel.add(myDescriptionPane);
myInfoPanel.setBorder(BorderFactory.createLineBorder(new JBColor(10067616, 10067616)));
panel.add(myInfoPanel, BorderLayout.CENTER);
add(panel);
}
private void initCoursesCombobox() {
myAvailableCourses =
myGenerator.getCoursesUnderProgress(false, "Getting Available Courses", ProjectManager.getInstance().getDefaultProject());
@@ -92,11 +121,11 @@ public class StudyNewProjectPanel {
final CourseInfo selectedCourse = StudyUtils.getFirst(myAvailableCourses);
final String authorsString = Course.getAuthorsString(selectedCourse.getAuthors());
myAuthorLabel.setText(!StringUtil.isEmptyOrSpaces(authorsString) ? "Author: " + authorsString : "");
myDescriptionLabel.setText(selectedCourse.getDescription());
myDescriptionLabel.setEditable(false);
myDescriptionPane.setText(selectedCourse.getDescription());
myDescriptionPane.setEditable(false);
//setting the first course in list as selected
myGenerator.setSelectedCourse(selectedCourse);
if (selectedCourse.isAdaptive() && !myGenerator.isLoggedIn()) {
setError(LOGIN_TO_STEPIC_MESSAGE);
}
@@ -107,7 +136,6 @@ public class StudyNewProjectPanel {
}
private void setupBrowseButton() {
myBrowseButton.putClientProperty("JButton.buttonType", "null");
myBrowseButton.setIcon(InteractiveLearningIcons.InterpreterGear);
final FileChooserDescriptor fileChooser = new FileChooserDescriptor(true, false, false, true, false, false) {
@Override
@@ -188,15 +216,19 @@ public class StudyNewProjectPanel {
myValidationManager.validate();
}
}
public JPanel getContentPanel() {
return myContentPanel;
}
public void registerValidators(final FacetValidatorsManager manager) {
myValidationManager = manager;
}
@Override
public JComponent getAnchor() {
return myAnchor;
}
@Override
public void setAnchor(@Nullable JComponent anchor) {
myAnchor = anchor;
}
/**
* Handles refreshing courses
@@ -248,14 +280,14 @@ public class StudyNewProjectPanel {
CourseInfo selectedCourse = (CourseInfo)cb.getSelectedItem();
if (selectedCourse == null || selectedCourse.equals(CourseInfo.INVALID_COURSE)) {
myAuthorLabel.setText("");
myDescriptionLabel.setText("");
myDescriptionPane.setText("");
setError(INVALID_COURSE);
return;
}
final String authorsString = Course.getAuthorsString(selectedCourse.getAuthors());
myAuthorLabel.setText(!StringUtil.isEmptyOrSpaces(authorsString) ? "Author: " + authorsString : "");
myCoursesComboBox.removeItem(CourseInfo.INVALID_COURSE);
myDescriptionLabel.setText(selectedCourse.getDescription());
myDescriptionPane.setText(selectedCourse.getDescription());
myGenerator.setSelectedCourse(selectedCourse);
setOK();
@@ -275,25 +307,49 @@ public class StudyNewProjectPanel {
return myInfoPanel;
}
private class AddRemoteDialog extends LoginDialog {
private class AddRemoteDialog extends DialogWrapper {
private final StudyAddRemoteCourse myRemoteCourse;
protected AddRemoteDialog() {
super();
super(null);
setTitle("Login To Stepic");
myRemoteCourse = new StudyAddRemoteCourse();
init();
}
@Nullable
@Override
protected JComponent createCenterPanel() {
return myRemoteCourse.getContentPanel();
}
@Nullable
@Override
public JComponent getPreferredFocusedComponent() {
return myRemoteCourse.getLoginField();
}
@Override
protected void doOKAction() {
if (!validateLoginAndPasswordFields()) return;
super.doJustOkAction();
super.doOKAction();
if (StringUtil.isEmptyOrSpaces(myRemoteCourse.getLogin())) {
myRemoteCourse.setError("Please, enter your login");
return;
}
if (StringUtil.isEmptyOrSpaces(myRemoteCourse.getPassword())) {
myRemoteCourse.setError("Please, enter your password");
return;
}
ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true);
final StepicUser stepicUser = StudyUtils.execCancelable(() -> EduStepicConnector.login(myLoginPanel.getLogin(),
myLoginPanel.getPassword()));
final StepicUser stepicUser = StudyUtils.execCancelable(() -> EduStepicConnector.login(myRemoteCourse.getLogin(),
myRemoteCourse.getPassword()));
if (stepicUser != null) {
stepicUser.setEmail(myLoginPanel.getLogin());
stepicUser.setPassword(myLoginPanel.getPassword());
stepicUser.setEmail(myRemoteCourse.getLogin());
stepicUser.setPassword(myRemoteCourse.getPassword());
myGenerator.myUser = stepicUser;
myGenerator.setEnrolledCoursesIds(EduStepicConnector.getEnrolledCoursesIds());
@@ -306,6 +362,6 @@ public class StudyNewProjectPanel {
setError("Failed to login");
}
}, "Signing In And Getting Stepic Course List", true, new DefaultProjectFactoryImpl().getDefaultProject());
}
}
}
}
@@ -14,7 +14,6 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.psi.PsiDirectory;
@@ -24,6 +23,7 @@ import com.jetbrains.edu.learning.courseGeneration.StudyProjectGenerator;
import com.jetbrains.edu.learning.stepic.CourseInfo;
import com.jetbrains.edu.learning.stepic.EduStepicConnector;
import com.jetbrains.edu.learning.ui.StudyNewProjectPanel;
import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
import com.jetbrains.python.newProject.PythonProjectGenerator;
import icons.InteractiveLearningPythonIcons;
import org.jetbrains.annotations.Nls;
@@ -40,6 +40,7 @@ import java.util.List;
public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator implements DirectoryProjectGenerator {
private static final Logger LOG = Logger.getInstance(PyStudyDirectoryProjectGenerator.class.getName());
private final StudyProjectGenerator myGenerator;
private static final String NO_PYTHON_INTERPRETER = "<html><u>Add</u> python interpreter.</html>";
public ValidationResult myValidationResult = new ValidationResult("selected course is not valid");
private StudyNewProjectPanel mySettingsPanel;
@@ -115,10 +116,16 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator imp
LOG.error("Can't copy test_helper.py " + exception.getMessage());
}
}
@NotNull
@Override
public ValidationResult validate(@NotNull String s) {
final Project project = ProjectManager.getInstance().getDefaultProject();
final List<Sdk> sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks();
if (sdks.isEmpty()) {
myValidationResult = new ValidationResult(NO_PYTHON_INTERPRETER);
}
return myValidationResult;
}
@@ -129,7 +136,7 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator imp
@Nullable
@Override
public JPanel extendBasePanel() throws ProcessCanceledException {
return mySettingsPanel.getContentPanel();
return mySettingsPanel;
}
public List<CourseInfo> getCourses() {
@@ -144,26 +151,25 @@ public class PyStudyDirectoryProjectGenerator extends PythonProjectGenerator imp
return myGenerator;
}
@Override
public boolean hideInterpreter() {
return true;
}
@Nullable
@Override
public BooleanFunction<PythonProjectGenerator> beforeProjectGenerated(@NotNull Sdk sdk) {
return new BooleanFunction<PythonProjectGenerator>() {
@Override
public boolean fun(PythonProjectGenerator generator) {
final List<Integer> enrolledCoursesIds = myGenerator.getEnrolledCoursesIds();
final CourseInfo course = (CourseInfo)mySettingsPanel.getCoursesComboBox().getSelectedItem();
if (course.isAdaptive() && !enrolledCoursesIds.contains(course.getId())) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(new ThrowableComputable<Boolean, RuntimeException>() {
@Override
public Boolean compute() throws RuntimeException {
ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true);
return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId()));
}
}, "Creating Course", true, ProjectManager.getInstance().getDefaultProject());
}
return true;
public BooleanFunction<PythonProjectGenerator> beforeProjectGenerated(@Nullable Sdk sdk) {
return generator -> {
final List<Integer> enrolledCoursesIds = myGenerator.getEnrolledCoursesIds();
final CourseInfo course = (CourseInfo)mySettingsPanel.getCoursesComboBox().getSelectedItem();
if (course.isAdaptive() && !enrolledCoursesIds.contains(course.getId())) {
ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> {
ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true);
return StudyUtils.execCancelable(() -> EduStepicConnector.enrollToCourse(course.getId()));
}, "Creating Course", true, ProjectManager.getInstance().getDefaultProject());
}
return true;
};
}
}
@@ -19,12 +19,8 @@ import com.intellij.ide.impl.ProjectUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.projectRoots.Sdk;
import com.jetbrains.edu.learning.PyStudyDirectoryProjectGenerator;
import com.jetbrains.edu.learning.stepic.CourseInfo;
import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
import com.jetbrains.python.newProject.actions.GenerateProjectCallback;
import com.jetbrains.python.newProject.actions.ProjectSpecificSettingsStep;
import icons.InteractiveLearningPythonIcons;
@@ -76,10 +72,6 @@ public class PyStudyIntroductionCourseAction extends AnAction {
step.setLocation(projectDir.toString());
generator.setSelectedCourse(introCourse);
final Project project = ProjectManager.getInstance().getDefaultProject();
final List<Sdk> sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks();
Sdk sdk = sdks.isEmpty() ? null : sdks.iterator().next();
step.setSdk(sdk);
callback.consume(step);
}
}
@@ -15,6 +15,7 @@
*/
package com.jetbrains.python.newProject.actions;
import com.intellij.execution.ExecutionException;
import com.intellij.ide.util.projectWizard.AbstractNewProjectStep;
import com.intellij.ide.util.projectWizard.ProjectSettingsStepBase;
import com.intellij.ide.util.projectWizard.WebProjectTemplate;
@@ -36,10 +37,13 @@ import com.intellij.util.NullableConsumer;
import com.jetbrains.python.configuration.PyConfigurableInterpreterList;
import com.jetbrains.python.newProject.PyNewProjectSettings;
import com.jetbrains.python.newProject.PythonProjectGenerator;
import com.jetbrains.python.packaging.PyPackageManager;
import com.jetbrains.python.sdk.*;
import com.jetbrains.python.sdk.flavors.PythonSdkFlavor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
public class GenerateProjectCallback implements NullableConsumer<ProjectSettingsStepBase> {
@@ -50,34 +54,17 @@ public class GenerateProjectCallback implements NullableConsumer<ProjectSettings
if (!(step instanceof ProjectSpecificSettingsStep)) return;
final ProjectSpecificSettingsStep settingsStep = (ProjectSpecificSettingsStep)step;
Sdk sdk = settingsStep.getSdk();
final Project project = ProjectManager.getInstance().getDefaultProject();
final ProjectSdksModel model = PyConfigurableInterpreterList.getInstance(project).getModel();
if (sdk instanceof PyDetectedSdk) {
final String name = sdk.getName();
VirtualFile sdkHome = ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
@Override
public VirtualFile compute() {
return LocalFileSystem.getInstance().refreshAndFindFileByPath(name);
}
});
PySdkService.getInstance().solidifySdk(sdk);
sdk = SdkConfigurationUtil.createAndAddSDK(sdkHome.getPath(), PythonSdkType.getInstance());
if (sdk != null) {
PythonSdkUpdater.updateOrShowError(sdk, null, project, null);
}
model.addSdk(sdk);
settingsStep.setSdk(sdk);
try {
model.apply();
}
catch (ConfigurationException exception) {
LOG.error("Error adding detected python interpreter " + exception.getMessage());
}
if (sdk instanceof PyDetectedSdk) {
addDetectedSdk(settingsStep, sdk);
}
Project newProject = generateProject(project, settingsStep);
else if (sdk == null) {
createAndAddVirtualEnv(settingsStep);
}
sdk = settingsStep.getSdk();
final Project newProject = generateProject(settingsStep);
if (newProject != null) {
SdkConfigurationUtil.setDirectoryProjectSdk(newProject, sdk);
final List<Sdk> sdks = PythonSdkType.getAllSdks();
@@ -90,12 +77,72 @@ public class GenerateProjectCallback implements NullableConsumer<ProjectSettings
}
}
private static void addDetectedSdk(ProjectSpecificSettingsStep settingsStep, Sdk sdk) {
final Project project = ProjectManager.getInstance().getDefaultProject();
final ProjectSdksModel model = PyConfigurableInterpreterList.getInstance(project).getModel();
final String name = sdk.getName();
VirtualFile sdkHome = ApplicationManager.getApplication().runWriteAction(new Computable<VirtualFile>() {
@Override
public VirtualFile compute() {
return LocalFileSystem.getInstance().refreshAndFindFileByPath(name);
}
});
PySdkService.getInstance().solidifySdk(sdk);
sdk = SdkConfigurationUtil.createAndAddSDK(sdkHome.getPath(), PythonSdkType.getInstance());
if (sdk != null) {
PythonSdkUpdater.updateOrShowError(sdk, null, project, null);
}
model.addSdk(sdk);
settingsStep.setSdk(sdk);
try {
model.apply();
}
catch (ConfigurationException exception) {
LOG.error("Error adding detected python interpreter " + exception.getMessage());
}
}
private static void createAndAddVirtualEnv(final ProjectSpecificSettingsStep settingsStep) {
final Project project = ProjectManager.getInstance().getDefaultProject();
final ProjectSdksModel model = PyConfigurableInterpreterList.getInstance(project).getModel();
final List<PythonSdkFlavor> flavors = PythonSdkFlavor.getApplicableFlavors(false);
String baseSdk = null;
for (PythonSdkFlavor flavor : flavors) {
final Collection<String> baseSdks = flavor.suggestHomePaths();
if (!baseSdks.isEmpty()) {
baseSdk = baseSdks.iterator().next();
}
}
if (baseSdk != null) {
final PyPackageManager packageManager = PyPackageManager.getInstance(new PyDetectedSdk(baseSdk));
try {
final String path = packageManager.createVirtualEnv(settingsStep.getProjectLocation() + "/.idea/VirtualEnvironment", false);
AbstractCreateVirtualEnvDialog.setupVirtualEnvSdk(path, true, new AbstractCreateVirtualEnvDialog.VirtualEnvCallback() {
@Override
public void virtualEnvCreated(Sdk createdSdk, boolean associateWithProject) {
settingsStep.setSdk(createdSdk);
model.addSdk(createdSdk);
try {
model.apply();
}
catch (ConfigurationException exception) {
LOG.error("Error adding created virtual env " + exception.getMessage());
}
}
});
}
catch (ExecutionException e) {
LOG.warn("Failed to create virtual env " + e.getMessage());
}
}
}
@Nullable
private static Project generateProject(@NotNull final Project project,
@NotNull final ProjectSettingsStepBase settings) {
private static Project generateProject(@NotNull final ProjectSettingsStepBase settings) {
final DirectoryProjectGenerator generator = settings.getProjectGenerator();
final String location = FileUtil.expandUserHome(settings.getProjectLocation());
return AbstractNewProjectStep.doGenerateProject(project, location, generator,
return AbstractNewProjectStep.doGenerateProject(ProjectManager.getInstance().getDefaultProject(), location, generator,
file -> computeProjectSettings(generator, (ProjectSpecificSettingsStep)settings));
}
@@ -23,8 +23,7 @@ import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.ui.LabeledComponent;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.openapi.ui.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.platform.DirectoryProjectGenerator;
import com.intellij.ui.DocumentAdapter;
@@ -103,7 +102,9 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
}
public Sdk getSdk() {
if (!(myProjectGenerator instanceof PythonProjectGenerator)) return null;
if (mySdk != null) return mySdk;
if (((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) return null;
return (Sdk)mySdkCombo.getComboBox().getSelectedItem();
}
@@ -118,7 +119,7 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
@Override
protected void registerValidators() {
super.registerValidators();
if (myProjectGenerator instanceof PythonProjectGenerator) {
if (myProjectGenerator instanceof PythonProjectGenerator && !((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) {
mySdkCombo.getComboBox().addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
@@ -136,13 +137,6 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
}
}
@Nullable
protected JPanel extendBasePanel() {
if (myProjectGenerator instanceof PythonProjectGenerator)
return ((PythonProjectGenerator)myProjectGenerator).extendBasePanel();
return null;
}
@Override
public boolean checkValid() {
myInstallFramework = false;
@@ -150,11 +144,12 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
return false;
}
if ((myProjectGenerator instanceof PythonProjectGenerator)) {
if (myProjectGenerator instanceof PythonProjectGenerator) {
final Sdk sdk = getSdk();
if (sdk == null) {
setErrorText("No Python interpreter selected");
if (!((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) {
setErrorText("No Python interpreter selected");
}
return false;
}
else if (PythonSdkType.isInvalid(sdk)) {
@@ -213,39 +208,81 @@ public class ProjectSpecificSettingsStep extends ProjectSettingsStepBase impleme
@Override
protected JPanel createBasePanel() {
final JPanel panel = super.createBasePanel();
if (myProjectGenerator instanceof PythonProjectGenerator) {
final Project project = ProjectManager.getInstance().getDefaultProject();
final List<Sdk> sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks();
VirtualEnvProjectFilter.removeAllAssociated(sdks);
Sdk compatibleSdk = sdks.isEmpty() ? null : sdks.iterator().next();
DirectoryProjectGenerator generator = getProjectGenerator();
if (generator instanceof PyFrameworkProjectGenerator && !((PyFrameworkProjectGenerator)generator).supportsPython3()) {
if (compatibleSdk != null && PythonSdkType.getLanguageLevelForSdk(compatibleSdk).isPy3K()) {
Sdk python2Sdk = PythonSdkType.findPython2Sdk(sdks);
if (python2Sdk != null) {
compatibleSdk = python2Sdk;
}
}
final BorderLayout layout = new BorderLayout();
final JPanel locationPanel = new JPanel(layout);
final JPanel panel = new JPanel(new VerticalFlowLayout(0, 2));
final LabeledComponent<TextFieldWithBrowseButton> location = createLocationComponent();
location.setLabelLocation(BorderLayout.WEST);
locationPanel.add(location, BorderLayout.CENTER);
panel.add(locationPanel);
if (((PythonProjectGenerator)myProjectGenerator).hideInterpreter()) {
addInterpreterButton(locationPanel, location);
}
else {
final LabeledComponent<PythonSdkChooserCombo> labeled = createInterpreterCombo();
UIUtil.mergeComponentsWithAnchor(labeled, location);
panel.add(labeled);
}
final Sdk preferred = compatibleSdk;
mySdkCombo = new PythonSdkChooserCombo(project, sdks, sdk -> sdk == preferred);
mySdkCombo.setButtonIcon(PythonIcons.Python.InterpreterGear);
final LabeledComponent<PythonSdkChooserCombo> labeled = LabeledComponent.create(mySdkCombo, "Interpreter");
labeled.setLabelLocation(BorderLayout.WEST);
UIUtil.mergeComponentsWithAnchor(labeled, (PanelWithAnchor)panel.getComponent(0));
panel.add(labeled);
final JPanel basePanelExtension = ((PythonProjectGenerator)myProjectGenerator).extendBasePanel();
if (basePanelExtension != null) {
UIUtil.mergeComponentsWithAnchor((PanelWithAnchor)basePanelExtension, location);
panel.add(basePanelExtension);
}
return panel;
}
final JPanel basePanelExtension = extendBasePanel();
if (basePanelExtension != null) {
panel.add(basePanelExtension);
return super.createBasePanel();
}
private void addInterpreterButton(final JPanel locationPanel, final LabeledComponent<TextFieldWithBrowseButton> location) {
final JButton interpreterButton = new FixedSizeButton(location);
interpreterButton.setIcon(PythonIcons.Python.Python);
interpreterButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final DialogBuilder builder = new DialogBuilder();
final JPanel panel = new JPanel();
final LabeledComponent<PythonSdkChooserCombo> interpreterCombo = createInterpreterCombo();
if (mySdk != null) {
mySdkCombo.getComboBox().setSelectedItem(mySdk);
}
panel.add(interpreterCombo);
builder.setCenterPanel(panel);
builder.setTitle("Select Python Interpreter");
if (builder.showAndGet()) {
mySdk = (Sdk)mySdkCombo.getComboBox().getSelectedItem();
}
}
});
locationPanel.add(interpreterButton, BorderLayout.EAST);
}
@NotNull
private LabeledComponent<PythonSdkChooserCombo> createInterpreterCombo() {
final Project project = ProjectManager.getInstance().getDefaultProject();
final List<Sdk> sdks = PyConfigurableInterpreterList.getInstance(project).getAllPythonSdks();
VirtualEnvProjectFilter.removeAllAssociated(sdks);
Sdk compatibleSdk = sdks.isEmpty() ? null : sdks.iterator().next();
DirectoryProjectGenerator generator = getProjectGenerator();
if (generator instanceof PyFrameworkProjectGenerator && !((PyFrameworkProjectGenerator)generator).supportsPython3()) {
if (compatibleSdk != null && PythonSdkType.getLanguageLevelForSdk(compatibleSdk).isPy3K()) {
Sdk python2Sdk = PythonSdkType.findPython2Sdk(sdks);
if (python2Sdk != null) {
compatibleSdk = python2Sdk;
}
}
}
return panel;
final Sdk preferred = compatibleSdk;
mySdkCombo = new PythonSdkChooserCombo(project, sdks, sdk -> sdk == preferred);
mySdkCombo.setButtonIcon(PythonIcons.Python.InterpreterGear);
return LabeledComponent.create(mySdkCombo, "Interpreter", BorderLayout.WEST);
}
@Override
@@ -67,10 +67,14 @@ public abstract class PythonProjectGenerator {
}
@Nullable
public BooleanFunction<PythonProjectGenerator> beforeProjectGenerated(@NotNull final Sdk sdk) {
public BooleanFunction<PythonProjectGenerator> beforeProjectGenerated(@Nullable final Sdk sdk) {
return null;
}
public boolean hideInterpreter() {
return false;
}
public void addErrorLabelMouseListener(@NotNull final MouseListener mouseListener) {
myErrorLabelMouseListener = mouseListener;
}
@@ -72,7 +72,7 @@ public abstract class AbstractCreateVirtualEnvDialog extends IdeaDialog {
void virtualEnvCreated(Sdk sdk, boolean associateWithProject);
}
protected static void setupVirtualEnvSdk(final String path,
public static void setupVirtualEnvSdk(final String path,
boolean associateWithProject,
VirtualEnvCallback callback) {
final VirtualFile sdkHome =