Skip to content

Commit b4ecdf0

Browse files
authored
Added a block for Python's None (#208)
* Replaced blockly's logic_null block with a custom blocks for Python's None. Sorted imports and customBlocks in setup_custom_blocks.ts. * Create a shadow block for None if an argument of a python function has a default value of None. * Addressed review comments.
1 parent 00afeb0 commit b4ecdf0

File tree

9 files changed

+91
-23
lines changed

9 files changed

+91
-23
lines changed

src/blocks/mrc_none.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
/**
19+
* @fileoverview Block for Python's None keyword.
20+
* @author [email protected] (Liz Looney)
21+
*/
22+
23+
24+
import * as Blockly from 'blockly';
25+
import { MRC_STYLE_NONE } from '../themes/styles'
26+
import { Order, PythonGenerator } from 'blockly/python';
27+
28+
export const BLOCK_NAME = 'mrc_none';
29+
30+
export function setup() {
31+
Blockly.Blocks[BLOCK_NAME] = {
32+
init: function() {
33+
this.setOutput(true); // no type for None
34+
this.appendDummyInput()
35+
.appendField(Blockly.Msg.NONE);
36+
this.setStyle(MRC_STYLE_NONE);
37+
this.setTooltip(Blockly.Msg.NONE_TOOLTIP);
38+
},
39+
};
40+
}
41+
42+
export function pythonFromBlock(
43+
_block: Blockly.Block,
44+
_generator: PythonGenerator,
45+
): [string, Order] {
46+
return ['None', Order.ATOMIC];
47+
}
48+
49+
export function createNoneShadowValue(): any {
50+
return {
51+
'shadow': {
52+
'type': 'mrc_none',
53+
},
54+
}
55+
}

src/blocks/setup_custom_blocks.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,44 @@
11

22
import * as CallPythonFunction from './mrc_call_python_function';
3+
import * as ClassMethodDef from './mrc_class_method_def';
4+
import * as Component from './mrc_component';
5+
import * as Event from './mrc_event';
6+
import * as EventHandler from './mrc_event_handler';
7+
import * as GetParameter from './mrc_get_parameter';
38
import * as GetPythonEnumValue from './mrc_get_python_enum_value';
49
import * as GetPythonVariable from './mrc_get_python_variable';
510
import * as ListAddItem from './mrc_list_add_item';
611
import * as MathMinMax from './mrc_math_min_max';
12+
import * as Mechanism from './mrc_mechanism';
13+
import * as MechanismComponentHolder from './mrc_mechanism_component_holder';
714
import * as MiscComment from './mrc_misc_comment';
815
import * as MiscEvaluateButIgnoreResult from './mrc_misc_evaluate_but_ignore_result';
9-
import * as SetPythonVariable from './mrc_set_python_variable';
10-
import * as ClassMethodDef from './mrc_class_method_def';
11-
import * as Mechanism from './mrc_mechanism';
12-
import * as Component from './mrc_component';
13-
import * as MechanismContainerHolder from './mrc_mechanism_component_holder';
14-
import * as Port from './mrc_port';
16+
import * as None from './mrc_none';
1517
import * as OpModeDetails from './mrc_opmode_details';
16-
import * as Event from './mrc_event';
17-
import * as GetParameter from './mrc_get_parameter';
1818
import * as ParameterMutator from './mrc_param_container'
19-
import * as EventHandler from './mrc_event_handler';
19+
import * as Port from './mrc_port';
20+
import * as SetPythonVariable from './mrc_set_python_variable';
2021

2122
const customBlocks = [
2223
CallPythonFunction,
2324
ClassMethodDef,
25+
Component,
26+
Event,
27+
EventHandler,
28+
GetParameter,
2429
GetPythonEnumValue,
2530
GetPythonVariable,
2631
ListAddItem,
2732
MathMinMax,
33+
Mechanism,
34+
MechanismComponentHolder,
2835
MiscComment,
2936
MiscEvaluateButIgnoreResult,
30-
SetPythonVariable,
31-
Mechanism,
32-
Component,
33-
MechanismContainerHolder,
34-
Port,
37+
None,
3538
OpModeDetails,
36-
Event,
37-
GetParameter,
3839
ParameterMutator,
39-
EventHandler,
40+
Port,
41+
SetPythonVariable,
4042
];
4143

4244
export const setup = function(forBlock: any) {

src/blocks/tokens.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ export function customTokens(t: (key: string) => string): typeof Blockly.Msg {
4242
EVALUATE_BUT_IGNORE_RESULT: t('BLOCKLY.EVALUATE_BUT_IGNORE_RESULT'),
4343
EVALUATE_BUT_IGNORE_RESULT_TOOLTIP:
4444
t('BLOCKLY.TOOLTIP.EVALUATE_BUT_IGNORE_RESULT'),
45+
NONE: t('BLOCKLY.NONE'),
46+
NONE_TOOLTIP: t('BLOCKLY.TOOLTIP.NONE'),
4547
AUTO: t('BLOCKLY.AUTO'),
4648
TELEOP: t('BLOCKLY.TELEOP'),
4749
TEST: t('BLOCKLY.TEST'),

src/blocks/utils/value.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import { getAlias } from './python';
2323
import * as variable from './variable';
24+
import { createNoneShadowValue } from '../mrc_none';
2425

2526

2627
export function valueForFunctionArgInput(argType: string, argDefaultValue: string): any {
@@ -50,9 +51,7 @@ export function valueForFunctionArgInput(argType: string, argDefaultValue: strin
5051
break;
5152
case 'str':
5253
if (argDefaultValue === 'None') {
53-
// TODO(lizlooney): Make a block for python None
54-
// In RobotPy function hal.report(), the arg named feature has a default value of None.
55-
return null;
54+
return createNoneShadowValue()
5655
}
5756
// If argDefaultValue is surrounded by single or double quotes, it's a literal string.
5857
if (argDefaultValue.startsWith("'") && argDefaultValue.endsWith("'") ||

src/i18n/locales/en/translation.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"COMPONENTS": "Components",
5050
"EVENTS": "Events",
5151
"EVALUATE_BUT_IGNORE_RESULT": "evaluate but ignore result",
52+
"NONE": "None",
5253
"TYPE": "Type",
5354
"ENABLED": "Enabled",
5455
"DISPLAY_NAME": "Display Name",
@@ -57,6 +58,7 @@
5758
"NO_MECHANISM_CONTENTS": "No Mechanism Contents",
5859
"TOOLTIP":{
5960
"EVALUATE_BUT_IGNORE_RESULT": "Executes the connected block and ignores the result. Allows you to call a function and ignore the return value.",
61+
"NONE": "Returns None.",
6062
"OPMODE_TYPE": "What sort of OpMode this is",
6163
"OPMODE_ENABLED": "Whether the OpMode is shown on Driver Station",
6264
"OPMODE_NAME": "The name shown on the Driver Station. If blank will use the class name.",

src/i18n/locales/es/translation.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
"COMPONENTS": "Componentes",
5151
"EVENTS": "Eventos",
5252
"EVALUATE_BUT_IGNORE_RESULT": "evaluar pero ignorar resultado",
53+
"NONE": "None",
5354
"TYPE": "Tipo",
5455
"ENABLED": "Habilitado",
5556
"DISPLAY_NAME": "Nombre a Mostrar",
@@ -58,6 +59,7 @@
5859
"NO_MECHANISM_CONTENTS": "Sin Contenido de Mecanismo",
5960
"TOOLTIP": {
6061
"EVALUATE_BUT_IGNORE_RESULT": "Ejecuta el bloque conectado e ignora el resultado. Te permite llamar una función e ignorar el valor de retorno.",
62+
"NONE": "No devuelve ninguno.",
6163
"OPMODE_TYPE": "Qué tipo de OpMode es este",
6264
"OPMODE_ENABLED": "Si el OpMode se muestra en la Estación del Conductor",
6365
"OPMODE_NAME": "El nombre mostrado en la Estación del Conductor. Si está en blanco usará el nombre de la clase.",
@@ -82,4 +84,4 @@
8284
"TEST": "Prueba"
8385
}
8486
}
85-
}
87+
}

src/i18n/locales/he/translation.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"COMPONENTS": "רכיבים",
5050
"EVENTS": "אירועים",
5151
"EVALUATE_BUT_IGNORE_RESULT": "הערך אך התעלם מהתוצאה",
52+
"NONE": "אַף לֹא אֶחָד",
5253
"TYPE": "סוג",
5354
"ENABLED": "מופעל",
5455
"DISPLAY_NAME": "שם תצוגה",
@@ -57,6 +58,7 @@
5758
"NO_MECHANISM_CONTENTS": "אין תוכן מנגנון",
5859
"TOOLTIP":{
5960
"EVALUATE_BUT_IGNORE_RESULT": "מריץ את הבלוק המחובר ומתעלם מהתוצאה. מאפשר לך לקרוא לפונקציה ולהתעלם מערך ההחזרה.",
61+
"NONE": "החזרות אין.",
6062
"OPMODE_TYPE": "איזה סוג של מצב פעולה זה",
6163
"OPMODE_ENABLED": "האם מצב הפעולה מוצג בתחנת הנהג",
6264
"OPMODE_NAME": "השם המוצג בתחנת הנהג. אם ריק ישתמש בשם הכיתה.",

src/themes/styles.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export const MRC_STYLE_ENUM = 'mrc_style_enum';
2626
export const MRC_STYLE_VARIABLES = 'mrc_style_variables';
2727
export const MRC_STYLE_COMMENTS = 'mrc_style_comments';
2828
export const MRC_STYLE_MISC = 'mrc_style_misc';
29+
export const MRC_STYLE_NONE = 'mrc_style_none';
2930
export const MRC_STYLE_CLASS_BLOCKS = 'mrc_style_class_blocks';
3031
export const MRC_CATEGORY_STYLE_METHODS = 'mrc_category_style_methods';
3132
export const MRC_STYLE_MECHANISMS = 'mrc_style_mechanisms';
@@ -38,6 +39,7 @@ export const MRC_CATEGORY_STYLE_COMPONENTS = 'mrc_category_style_components';
3839
export const add_mrc_styles = function (theme: Blockly.Theme): Blockly.Theme {
3940
const procedureStyle = theme.blockStyles['procedure_blocks'];
4041
const variableStyle = theme.blockStyles['variable_blocks'];
42+
const logicStyle = theme.blockStyles['logic_blocks'];
4143

4244
theme.setBlockStyle(MRC_STYLE_FUNCTIONS, {
4345
...procedureStyle
@@ -74,12 +76,14 @@ export const add_mrc_styles = function (theme: Blockly.Theme): Blockly.Theme {
7476
colourTertiary: "#494984",
7577
hat: ""
7678
});
79+
theme.setBlockStyle(MRC_STYLE_NONE, {
80+
...logicStyle
81+
});
7782
theme.setBlockStyle(MRC_STYLE_MECHANISMS, {
7883
colourPrimary: "#5b61a5",
7984
colourSecondary: "#dedfed",
8085
colourTertiary: "#494e84",
8186
hat: ""
82-
8387
});
8488
theme.setBlockStyle(MRC_STYLE_COMPONENTS, {
8589
colourPrimary: "#5b80a5",

src/toolbox/logic_category.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const getCategory = () => (
4343
},
4444
{
4545
kind: 'block',
46-
type: 'logic_null',
46+
type: 'mrc_none',
4747
},
4848
{
4949
kind: 'block',

0 commit comments

Comments
 (0)