Skip to content

Commit 2ad8c7e

Browse files
authored
Change the theme of matplotlib dynamically (#5341)
* Working in extension * Change static post office to an object. * Remove theme execution test. Not happening anymore * Fix linter problem * More linter problems
1 parent c880eef commit 2ad8c7e

24 files changed

Lines changed: 168 additions & 186 deletions

news/2 Fixes/5294.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Update matplotlib based on theme changes.

src/client/datascience/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export namespace Identifiers {
126126
export const EmptyFileName = '2DB9B899-6519-4E1B-88B0-FA728A274115';
127127
export const GeneratedThemeName = 'ipython-theme'; // This needs to be all lower class and a valid class name.
128128
export const HistoryPurpose = 'history';
129+
export const MatplotLibDefaultParams = '_VSCode_defaultMatplotlib_Params';
129130
}
130131

131132
export namespace JupyterCommands {

src/client/datascience/history/history.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { createDeferred, Deferred } from '../../common/utils/async';
2929
import * as localize from '../../common/utils/localize';
3030
import { IInterpreterService, PythonInterpreter } from '../../interpreter/contracts';
3131
import { captureTelemetry, sendTelemetryEvent } from '../../telemetry';
32-
import { EditorContexts, Identifiers, Telemetry } from '../constants';
32+
import { CssMessages, EditorContexts, Identifiers, Telemetry } from '../constants';
3333
import { JupyterInstallError } from '../jupyter/jupyterInstallError';
3434
import { JupyterKernelPromiseFailedError } from '../jupyter/jupyterKernelPromiseFailedError';
3535
import {
@@ -242,6 +242,20 @@ export class History extends WebViewHost<IHistoryMapping> implements IHistory {
242242

243243
// Pass onto our base class.
244244
super.onMessage(message, payload);
245+
246+
// After our base class handles some stuff, handle it ourselves too.
247+
switch (message) {
248+
case CssMessages.GetCssRequest:
249+
// Update the jupyter server if we have one:
250+
if (this.jupyterServer) {
251+
this.isDark().then(d => this.jupyterServer ? this.jupyterServer.setMatplotLibStyle(d) : Promise.resolve()).ignoreErrors();
252+
}
253+
break;
254+
255+
default:
256+
break;
257+
}
258+
245259
}
246260

247261
public dispose() {
@@ -765,6 +779,9 @@ export class History extends WebViewHost<IHistoryMapping> implements IHistory {
765779
private async loadJupyterServer(_restart?: boolean): Promise<void> {
766780
this.logger.logInformation('Getting jupyter server options ...');
767781

782+
// Wait for the webpanel to pass back our current theme darkness
783+
const knownDark = await this.isDark();
784+
768785
// Extract our options
769786
const options = await this.historyProvider.getNotebookOptions();
770787

@@ -773,6 +790,11 @@ export class History extends WebViewHost<IHistoryMapping> implements IHistory {
773790
// Now try to create a notebook server
774791
this.jupyterServer = await this.jupyterExecution.connectToNotebookServer(options);
775792

793+
// Before we run any cells, update the dark setting
794+
if (this.jupyterServer) {
795+
await this.jupyterServer.setMatplotLibStyle(knownDark);
796+
}
797+
776798
this.logger.logInformation('Connected to jupyter server.');
777799
}
778800

src/client/datascience/history/historyProvider.ts

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ import * as uuid from 'uuid/v4';
66
import { Disposable, Event, EventEmitter } from 'vscode';
77
import * as vsls from 'vsls/vscode';
88

9-
import { ILiveShareApi, IWorkspaceService } from '../../common/application/types';
9+
import { ILiveShareApi } from '../../common/application/types';
1010
import { IAsyncDisposable, IAsyncDisposableRegistry, IConfigurationService, IDisposableRegistry } from '../../common/types';
1111
import { createDeferred, Deferred } from '../../common/utils/async';
1212
import * as localize from '../../common/utils/localize';
1313
import { IServiceContainer } from '../../ioc/types';
1414
import { Identifiers, LiveShare, LiveShareCommands, Settings } from '../constants';
1515
import { PostOffice } from '../liveshare/postOffice';
16-
import { IHistory, IHistoryProvider, INotebookServerOptions, IThemeFinder } from '../types';
16+
import { IHistory, IHistoryProvider, INotebookServerOptions } from '../types';
1717

1818
interface ISyncData {
1919
count: number;
@@ -34,9 +34,7 @@ export class HistoryProvider implements IHistoryProvider, IAsyncDisposable {
3434
@inject(IServiceContainer) private serviceContainer: IServiceContainer,
3535
@inject(IAsyncDisposableRegistry) asyncRegistry : IAsyncDisposableRegistry,
3636
@inject(IDisposableRegistry) private disposables: IDisposableRegistry,
37-
@inject(IConfigurationService) private configService: IConfigurationService,
38-
@inject(IWorkspaceService) private workspaceService: IWorkspaceService,
39-
@inject(IThemeFinder) private themeFinder: IThemeFinder
37+
@inject(IConfigurationService) private configService: IConfigurationService
4038
) {
4139
asyncRegistry.push(this);
4240

@@ -84,16 +82,6 @@ export class HistoryProvider implements IHistoryProvider, IAsyncDisposable {
8482
const settings = this.configService.getSettings();
8583
let serverURI: string | undefined = settings.datascience.jupyterServerURI;
8684
const useDefaultConfig: boolean | undefined = settings.datascience.useDefaultConfigForJupyter;
87-
// Check for dark theme, if so set matplot lib to use dark_background settings
88-
let darkTheme: boolean | undefined = false;
89-
const workbench = this.workspaceService.getConfiguration('workbench');
90-
if (workbench) {
91-
const theme = workbench.get<string>('colorTheme');
92-
const ignoreTheme = this.configService.getSettings().datascience.ignoreVscodeTheme ? true : false;
93-
if (theme && !ignoreTheme) {
94-
darkTheme = await this.themeFinder.isThemeDark(theme);
95-
}
96-
}
9785

9886
// For the local case pass in our URI as undefined, that way connect doesn't have to check the setting
9987
if (serverURI === Settings.JupyterServerLocalLaunch) {
@@ -102,7 +90,6 @@ export class HistoryProvider implements IHistoryProvider, IAsyncDisposable {
10290

10391
return {
10492
uri: serverURI,
105-
usingDarkTheme: darkTheme,
10693
useDefaultConfig,
10794
purpose: Identifiers.HistoryPurpose
10895
};

src/client/datascience/jupyter/jupyterExecution.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ export class JupyterExecutionBase implements IJupyterExecution {
150150
connectionInfo: startInfo.connection,
151151
currentInterpreter: interpreter,
152152
kernelSpec: startInfo.kernelSpec,
153-
usingDarkTheme: options && options.usingDarkTheme ? options.usingDarkTheme : false,
154153
workingDir: options ? options.workingDir : undefined,
155154
uri: options ? options.uri : undefined,
156155
purpose: options ? options.purpose : uuid()

src/client/datascience/jupyter/jupyterServer.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,14 @@ export class JupyterServerBase implements INotebookServer {
375375
return this.connectPromise.promise;
376376
}
377377

378+
public async setMatplotLibStyle(useDark: boolean) : Promise<void> {
379+
// Reset the matplotlib style based on if dark or not.
380+
await this.executeSilently(useDark ?
381+
'matplotlib.style.use(\'dark_background\')' :
382+
`matplotlib.rcParams.update(${Identifiers.MatplotLibDefaultParams})`);
383+
384+
}
385+
378386
// Return a copy of the connection information that this server used to connect with
379387
public getConnectionInfo(): IConnection | undefined {
380388
if (!this.launchInfo) {
@@ -513,8 +521,10 @@ export class JupyterServerBase implements INotebookServer {
513521
await this.changeDirectoryIfPossible(this.launchInfo.workingDir);
514522
}
515523

524+
// Force matplotlib to inline and save the default style. We'll use this later if we
525+
// get a request to update style
516526
await this.executeSilently(
517-
`%matplotlib inline${os.EOL}import matplotlib.pyplot as plt${(this.launchInfo && this.launchInfo.usingDarkTheme) ? `${os.EOL}from matplotlib import style${os.EOL}style.use(\'dark_background\')` : ''}`,
527+
`import matplotlib${os.EOL}%matplotlib inline${os.EOL}${Identifiers.MatplotLibDefaultParams} = dict(matplotlib.rcParams)`,
518528
cancelToken
519529
);
520530
} catch (e) {

src/client/datascience/jupyter/jupyterServerFactory.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ export class JupyterServerFactory implements INotebookServer {
9898
return server.setInitialDirectory(directory);
9999
}
100100

101+
public async setMatplotLibStyle(useDark: boolean): Promise<void> {
102+
const server = await this.serverFactory.get();
103+
return server.setMatplotLibStyle(useDark);
104+
}
105+
101106
public executeObservable(code: string, file: string, line: number, id: string, silent: boolean = false): Observable<ICell[]> {
102107
// Create a wrapper observable around the actual server (because we have to wait for a promise)
103108
return new Observable<ICell[]>(subscriber => {

src/client/datascience/jupyter/liveshare/guestJupyterExecution.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ export class GuestJupyterExecution extends LiveShareParticipantGuest(JupyterExec
111111
result = await super.connectToNotebookServer(
112112
{
113113
uri: newUri,
114-
usingDarkTheme: options && options.usingDarkTheme,
115114
useDefaultConfig: options && options.useDefaultConfig,
116115
workingDir: options ? options.workingDir : undefined,
117116
purpose

src/client/datascience/jupyter/liveshare/guestJupyterServer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ export class GuestJupyterServer
9898
return Promise.resolve();
9999
}
100100

101+
public async setMatplotLibStyle(_useDark: boolean): Promise<void> {
102+
// Guest can't change the style. Maybe output a warning here?
103+
}
104+
101105
public executeObservable(code: string, file: string, line: number, id: string): Observable<ICell[]> {
102106
// Mimic this to the other side and then wait for a response
103107
this.waitForService().then(s => {

src/client/datascience/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ export interface INotebookServerLaunchInfo
4646
currentInterpreter: PythonInterpreter | undefined;
4747
uri: string | undefined; // Different from the connectionInfo as this is the setting used, not the result
4848
kernelSpec: IJupyterKernelSpec | undefined;
49-
usingDarkTheme: boolean;
5049
workingDir: string | undefined;
5150
purpose: string | undefined; // Purpose this server is for
5251
}
@@ -65,6 +64,7 @@ export interface INotebookServer extends IAsyncDisposable {
6564
waitForConnect(): Promise<INotebookServerLaunchInfo | undefined>;
6665
getConnectionInfo(): IConnection | undefined;
6766
getSysInfo() : Promise<ICell | undefined>;
67+
setMatplotLibStyle(useDark: boolean) : Promise<void>;
6868
}
6969

7070
export interface INotebookServerOptions {

0 commit comments

Comments
 (0)