crosstab — u_pbt_crosstab #
← Referencia de componentes · Índice de la guía
Tabla dinámica completa: áreas de filas / columnas / valores alimentadas desde un DataStore, agregaciones, filtros, formato condicional, agrupación de fechas, medidas calculadas y exportaciones CSV / Excel.
▶ Verlo en vivo — Aplicación de demostración, mosaico Crosstab: la vista previa, el código que lo genera y esta página, uno al lado del otro.
De un vistazo #
| Userobject | u_pbt_crosstab |
| Clase de items | n_pbt_crosstab_field (un campo, mediante of_field) · n_pbt_crosstab_value (un valor del área Valores, mediante of_value) |
| Sirve para | Ofrecer a sus usuarios un análisis cruzado de sus datos, que ellos mismos reorganizan, sin escribir SQL ni pasar por Excel |
| Límite en modo demo | 500 filas de origen procesadas — of_get_cell_value y of_get_cell_rows leen esas 500 filas, las mismas cifras que en pantalla; exportaciones CSV y Excel y copia (Ctrl+C) desactivadas — véase el modo demo |
El principio #
Usted proporciona al componente un conjunto de datos plano — un DataStore, es decir, cualquier consulta ya escrita en su aplicación. Del resto se encarga la tabla dinámica: deduce la lista de campos y usted los reparte en cuatro áreas.
| Área | Lo que contiene | Efecto |
|---|---|---|
| Filas | Campos de agrupación | Un nivel de filas por campo, plegable |
| Columnas | Campos de agrupación | Un nivel de encabezados de columna por campo |
| Valores | Campos numéricos y su agregación | Lo que se calcula en las celdas |
| Filtros | Campos de selección | Un filtro encima de la tabla, aplicado a todo |
Todo el cálculo se realiza en el componente: una vez transmitidos los datos, reorganizar la tabla no provoca ninguna ida y vuelta a la base de datos ni a PowerBuilder.
Inicio rápido #
// event open de la ventana
// Local variables
datastore lds
lds = create datastore
lds.dataobject = "d_sales"
lds.SetTransObject(SQLCA)
lds.Retrieve()
// 1. Transmitir los datos: los campos se deducen de las columnas
uo_crosstab.of_from_datastore(/*ads*/ lds)
// 2. Repartir los campos en las areas
uo_crosstab.of_add_row_field(/*field*/ "region")
uo_crosstab.of_add_col_field(/*field*/ "year")
uo_crosstab.of_add_value_field(/*field*/ "amount", /*agg*/ uo_crosstab.AGG_SUM)
// 3. Presentar: formato de los importes y totales generales
uo_crosstab.of_field(/*field*/ "amount").is_format = "$#,##0"
uo_crosstab.ib_row_grand_total = true
uo_crosstab.ib_col_grand_total = true
// event ue_cell_double_clicked de uo_crosstab: (string as_row_tuple_json, string as_col_tuple_json, integer ai_measure, double ad_value)
// El usuario quiere el detalle que hay detras de una cifra: abrir la lista correspondiente.
of_open_detail(as_row_tuple_json, as_col_tuple_json)
Constantes #
| Constante | Valor | Para |
|---|---|---|
TOTALS_BOTTOM · TOTALS_TOP | "bottom" "top" | is_totals_position |
VALUES_COLS · VALUES_ROWS | "cols" "rows" | is_values_axis |
THOUSANDS_LOCALE · THOUSANDS_SPACE · THOUSANDS_NONE | "locale" "space" "none" | is_thousands |
AGG_SUM · AGG_COUNT · AGG_COUNT_ROWS · AGG_DISTINCT_COUNT | "sum" "count" "countrows" "dcount" | of_add_value_field · is_agg |
AGG_AVG · AGG_MIN · AGG_MAX | "avg" "min" "max" | of_add_value_field · is_agg |
VALUEFILTER_TOP · VALUEFILTER_BOTTOM | "top" "bottom" | of_set_value_filter |
VALUEFILTER_GT · VALUEFILTER_LT · VALUEFILTER_BETWEEN | "gt" "lt" "between" | of_set_value_filter |
LABEL_GT · LABEL_LT · LABEL_BETWEEN | "gt" "lt" "between" | of_set_label_filter |
LABEL_CONTAINS · LABEL_BEGINS · LABEL_ENDS | "contains" "begins" "ends" | of_set_label_filter |
LABEL_BEFORE · LABEL_AFTER | "before" "after" | of_set_label_filter |
DATE_YEAR · DATE_QUARTER · DATE_MONTH | "year" "quarter" "month" | of_group_date_field |
Las constantes de agregación, de filtro, de fecha y de separador se leen en el componente: uo_crosstab.AGG_SUM. Las de un campo o de un valor (SHOW_*, CF_*) se leen en la clase de su handle: n_pbt_crosstab_field.CF_SCALE, n_pbt_crosstab_value.SHOW_PCT_GRAND. AGG_COUNT cuenta los valores no vacíos, como la Cuenta de Excel; AGG_COUNT_ROWS cuenta las filas, vacías incluidas.
Propiedades #
| Propiedad | Tipo | Predeterminado | Función |
|---|---|---|---|
is_totals_position | string | "bottom" | Dónde se sitúa la fila del total general: TOTALS_BOTTOM (al pie, predeterminado) o TOTALS_TOP (arriba, justo debajo de los encabezados). Solo se desplaza el total general: el subtotal de un grupo sigue en la línea del grupo |
is_values_axis | string | "cols" | Orientación de las medidas cuando hay varias: VALUES_COLS (una al lado de otra en columnas, predeterminado) o VALUES_ROWS (apiladas en filas) |
is_currency_symbol | string | "" | La moneda que el menú Formato de número de un chip de valor propone, junto a « Sin símbolo » y « % ». Vacío = la del idioma de visualización ($ en inglés, € en los demás) |
is_thousands | string | "locale" | Separador de miles de los valores que no tienen formato propio (is_format vacío y ningún Format en la columna del DataWindow) — el mismo ajuste que el menú Opciones de la cuadrícula: THOUSANDS_LOCALE (el del idioma de visualización, predeterminado), THOUSANDS_SPACE, THOUSANDS_NONE, o el separador mismo (",", ".", " ") |
ib_field_list | boolean | true | Muestra el panel de campos, donde el usuario reorganiza la tabla con el ratón |
ib_row_subtotals | boolean | true | Muestra el subtotal de cada grupo de filas, escrito en la línea del propio grupo, encima de sus miembros; desactivado, esa línea conserva su etiqueta sin cifra |
ib_col_subtotals | boolean | true | Muestra un subtotal por cada grupo de columnas |
ib_row_grand_total | boolean | true | Muestra la fila de total general bajo la tabla (la gemela de ib_col_grand_total) |
ib_col_grand_total | boolean | true | Muestra la columna de total general tras la tabla (la gemela de ib_row_grand_total). Sin campo en Columnas no hay columna de total — la única columna de valores ES el total — y la propiedad conserva su valor para cuando entre un campo. Con varias medidas, la columna Total tiene un encabezado « Total » y después un encabezado por medida, ordenable |
ib_copy_headers | boolean | true | Ctrl+C copia los encabezados con las celdas seleccionadas: los nombres de las columnas en una primera línea (la medida solo se nombra allí si la tabla la muestra: varios valores, o ningún campo en Columnas) y el miembro de cada fila — su ruta completa, « Nord / Lille » — en una primera columna, cuya esquina lleva los nombres de los campos en filas (« region / ville »). Así, un pegado en una hoja de cálculo dice qué son las cifras. false copia solo las cifras. Leída en directo, devuelta a true por of_reset |
ib_enabled | boolean | true | Atenuado: la cuadrícula sigue mostrando sus cifras — una tabla dinámica vacía no es lo mismo que una que la aplicación ha apagado — pero deja de responder al puntero |
is_theme_style | string | "" | Estilo visual del componente (constantes THEME_STYLE_*); vacío = el de la aplicación, seguido en cada cambio |
is_theme_mode | string | "" | Variante clara u oscura (constantes THEME_MODE_*); vacío = la de la aplicación, seguida en cada cambio |
il_theme_accent | long | -1 | Color de acento de este componente (-1 = acento de la aplicación, o el del tema) |
is_tooltip | string | "" | Tooltip simple mostrado al pasar el ratón por el componente |
is_super_tooltip_title | string | "" | Título del tooltip enriquecido (prevalece sobre is_tooltip) |
is_super_tooltip_text | string | "" | Texto del tooltip enriquecido (se acepta el marcado enriquecido) |
is_super_tooltip_image | string | "" | Imagen del tooltip enriquecido |
Propiedades de un campo #
of_field (string as_field) devuelve el handle de un campo: usted lo obtiene una vez y luego gobierna el campo a través de sus propiedades. El handle se crea en la primera llamada y se reutiliza después.
| Propiedad | Tipo | Predeterminado | Función |
|---|---|---|---|
is_label | string | encabezado del DataWindow | Etiqueta legible del campo ("amount" → "Facturación"). Por defecto el texto de encabezado de la columna en el DataWindow, si no su nombre; una cadena vacía lo restablece. Fijada por su código, sobrevive a of_from_datastore |
is_show | string | "normal" | Lo que muestra la celda: "normal", "pctGrand" (% del total general), "pctRow" (% de la fila), "pctCol" (% de la columna), "running" (acumulado), "diff" (diferencia respecto al anterior) |
is_conditional_formatting | string | "none" | Formato condicional: CF_NONE, CF_SCALE (escala de colores), CF_BARS (barras dentro de la celda) o CF_ICONS (una flecha por tercio: arriba, estable, abajo) |
is_format | string | Format de la columna | Formato de visualización de los valores del campo, un formato de visualización de PowerBuilder ("#,##0.00", "$#,##0;($#,##0)", "0.0%"), escrito con los separadores del idioma de visualización. Vacío = el Format de la columna del DataWindow, leído por of_from_datastore; sin uno ni otro, is_thousands y dos decimales como máximo. Releída: el formato vigente, el fijado o si no el de la columna |
Los valores de is_show y de is_conditional_formatting también están disponibles como constantes en el handle (SHOW_PCT_COL, CF_SCALE…). Fijados en el campo, is_show, is_conditional_formatting e is_format valen para todos sus valores del área Valores y para los que entren después — puestos antes de que el campo esté en Valores, se conservan, se releen tal cual y se aplican a su llegada. Para un solo valor (el mismo campo dos veces: el importe, y su parte del total), se usa el handle del valor, a continuación.
// Local variables
n_pbt_crosstab_field lnv_field
// El campo importe : su etiqueta y una escala de colores
lnv_field = uo_crosstab.of_field(/*field*/ "amount")
lnv_field.is_label = "Facturación"
lnv_field.is_conditional_formatting = lnv_field.CF_SCALE
⚠️ Cambio incompatible. Esta propiedad se llamaba
is_cf: la abreviatura no decía nada en el punto de llamada. El nombre antiguo ya no existe: un código que lo utilice no compila. La sustitución es mecánica:is_cf→is_conditional_formatting, sin cambio de valores ni de comportamiento.
Propiedades de un valor #
of_value (integer ai_position) devuelve el handle de un valor del área Valores, por su posición (1 = el primero). Un campo puede estar allí dos veces — el importe, y su parte del total general — y cada valor tiene sus propios ajustes, que prevalecen sobre los de su campo. El handle designa una posición: tras of_remove_value, of_move_value, of_remove_field, of_remove_calc_measure, of_clear_layout u of_set_layout se libera — vuelva a pedir of_value. Sus constantes SHOW_* y CF_* son las de n_pbt_crosstab_field.
| Propiedad | Tipo | Predeterminado | Función |
|---|---|---|---|
is_agg | string | la de of_add_value_field | Agregación de este valor: una constante AGG_* del componente (uo_crosstab.AGG_AVG). Una medida calculada no tiene ninguna que elegir: la asignación se ignora |
is_show | string | "normal" | Lo que muestran las celdas de este valor: SHOW_NORMAL, SHOW_PCT_GRAND, SHOW_PCT_ROW, SHOW_PCT_COL, SHOW_RUNNING, SHOW_DIFF (como en el campo) |
is_conditional_formatting | string | "none" | Formato condicional de este valor: CF_NONE, CF_SCALE, CF_BARS, CF_ICONS |
is_format | string | Format de la columna | Formato de visualización de este valor solo, un formato de PowerBuilder ("#,##0.00", "$#,##0;($#,##0)"). Vacío = el Format de la columna del DataWindow. Releída: el formato vigente |
ib_negative_red | boolean | false | Los números negativos de este valor en rojo — en pantalla y en la exportación a Excel. El mismo ajuste que la entrada « Negativos en rojo » del menú del chip |
is_label | string | "" | Etiqueta de este valor en los encabezados (« Parte de las ventas »); vacío restablece la de origen, la agregación y la etiqueta del campo (« Suma de Importe ») |
| Método | Función |
|---|---|
of_field ( ) → string | El campo que este valor agrega ("amount"); "" cuando la posición no contiene ningún valor |
// Local variables
n_pbt_crosstab_value lnv_share, lnv_amount
// El importe dos veces en Valores : la suma, luego su parte del total general
uo_crosstab.of_add_value_field(/*field*/ "amount", /*agg*/ uo_crosstab.AGG_SUM)
uo_crosstab.of_add_value_field(/*field*/ "amount", /*agg*/ uo_crosstab.AGG_SUM)
// Solo el segundo valor : su parte del total general, con su propia etiqueta
lnv_share = uo_crosstab.of_value(/*position*/ 2)
lnv_share.is_show = n_pbt_crosstab_value.SHOW_PCT_GRAND
lnv_share.is_label = "Parte de las ventas"
// El primer valor : dos decimales, los negativos en rojo
lnv_amount = uo_crosstab.of_value(/*position*/ 1)
lnv_amount.is_format = "#,##0.00"
lnv_amount.ib_negative_red = true
Métodos #
Alimentar y nombrar #
| Método | Función |
|---|---|
of_from_datastore (datastore ads) | Transmite el conjunto de datos: los campos se deducen de las columnas del DataStore, su etiqueta del texto de encabezado y el formato predeterminado de sus valores del Format de la columna. Un texto que contiene una tabulación o un salto de línea se queda en su fila; un valor CHAR pierde sus blancos de relleno; un decimal se lee con el separador del puesto; un valor vacío (NULL, texto vacío) es un solo miembro (vacío), colocado al final; las etiquetas fijadas con is_label sobreviven a la recarga. Devuelve 0 una vez cargados los datos, -5 si el DataStore no es válido o no tiene ninguna columna, -2 si el componente no está creado |
of_from_datastore (datawindow adw) | La misma carga desde un DataWindow: la tabla cruzada lee sus filas a través de un DataStore de trabajo que las comparte (ShareData, verificado) — lo que el DataWindow muestra, la tabla lo cruza. Devuelve 0, -5 cuando adw no es válido, no tiene columnas o no puede compartir sus filas (of_get_last_error dice cuál), -2 cuando el componente no está creado |
of_from_datastore (datawindowchild adwc) | La misma carga desde un DataWindowChild (un DataWindow desplegable). Mismos valores devueltos |
of_field (string as_field) | Devuelve el handle de un campo, para etiquetarlo o darle formato (véase Propiedades de un campo) |
of_value (integer ai_position) → n_pbt_crosstab_value | Devuelve el handle de un valor del área Valores, por su posición (1 = el primero), para ajustar su agregación, su visualización, su formato o su etiqueta (véase Propiedades de un valor) |
of_value_count ( ) → long | El número de valores del área Valores (un campo que está allí dos veces cuenta dos veces); 0 cuando el área está vacía o el componente no está creado |
Construir la tabla #
| Método | Función |
|---|---|
of_clear_layout ( ) | Vacía las cuatro áreas: la tabla vuelve a quedar en blanco, los datos siguen cargados. Devuelve 0 una vez aplicado, -2 si el componente no está creado |
of_add_row_field (string as_field) | Añade un campo al área Filas (el orden de las llamadas determina el orden de los niveles). Un campo que ya está en Valores se queda allí: puede estar en ambos (un recuento distinto por miembro). Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen o para una medida calculada (solo va en Valores), -2 si el componente no está creado |
of_add_col_field (string as_field) | Añade un campo al área Columnas. Un campo que ya está en Valores se queda allí: puede estar en ambos (un recuento distinto por miembro). Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen o para una medida calculada (solo va en Valores), -2 si el componente no está creado |
of_add_value_field (string as_field, string as_agg) | Añade una medida al área Valores, con su agregación (AGG_*, vacío = AGG_SUM). El campo conserva su lugar en Filas, Columnas o Filtros. Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen u otra agregación, -2 si el componente no está creado |
of_add_filter_field (string as_field) | Añade un campo al área Filtros, encima de la tabla. Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen o para una medida calculada (solo va en Valores), -2 si el componente no está creado |
of_remove_field (string as_field) | Quita un campo de la tabla: de todas las áreas en las que está (Filas, Columnas, Filtros, y cada uno de sus valores), con sus filtros — como «Quitar» desde la lista de campos. El resto de la disposición queda tal cual, y ue_layout_changed comunica la nueva. Devuelve 0 una vez aplicado, -4 cuando el campo no está en ningún área, -5 para un campo que los datos no contienen |
of_remove_value (integer ai_position) | Quita un valor del área Valores, por su posición (la 2.ª « Suma de Importe » mientras la 1.ª se queda); con el último valor de un campo que no está en ningún otro sitio se van sus filtros. Devuelve 0 una vez aplicado, -5 para una posición sin valor, -2 si el componente no está creado |
of_move_value (integer ai_from, integer ai_to) | Mueve un valor del área Valores de una posición a otra (1 = el primero): sus ajustes, los filtros y la ordenación que lo nombran lo siguen. Devuelve 0 una vez aplicado, -5 para una posición sin valor, -2 si el componente no está creado |
Las agregaciones aceptadas por of_add_value_field las publica el componente como constantes: AGG_SUM (predeterminado), AGG_COUNT (el número de valores no vacíos, como la Cuenta de Excel), AGG_COUNT_ROWS (el número de filas, vacías incluidas), AGG_DISTINCT_COUNT (recuento de valores distintos, las celdas vacías se ignoran como en Excel), AGG_AVG, AGG_MIN, AGG_MAX. Una suma sin ningún valor es una celda vacía, no 0; una « diferencia respecto al anterior » en la que una de las dos celdas está vacía también está vacía. Los porcentajes se escriben como los escribe el idioma de visualización (57.9% en inglés, 57,9 % en francés).
Totales y subtotales #
Los totales se ajustan mediante propiedades, descritas más arriba: ib_row_grand_total y ib_col_grand_total para los totales generales, ib_row_subtotals y ib_col_subtotals para los subtotales, is_totals_position para la posición de la fila del total general. El menú Opciones de la cuadrícula (el engranaje) ofrece los mismos cuatro interruptores, una entrada por eje: Fila de total general, Columna de total general, Subtotales de filas, Subtotales de columnas.
Filtrar #
| Método | Función |
|---|---|
of_set_member_filter (string as_field, string as_values_tab) | Conserva solo los valores enumerados de un campo. Los valores van separados por tabulaciones (~t) y se escriben como la cuadrícula los guarda (véase bajo la tabla); una lista vacía quita el filtro, como of_set_member_order. Un campo que no está en ningún área pasa al área Filtros, para que el filtro se vea. Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen, -2 si el componente no está creado |
of_clear_member_filter (string as_field) | Quita este filtro. Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen, -2 si el componente no está creado |
of_set_value_filter (string as_field, string as_type, double ad_a, double ad_b, integer ai_measure_index) | Filtra sobre el total de la medida n.º ai_measure_index (la primera = 1): VALUEFILTER_TOP o VALUEFILTER_BOTTOM (los ad_a primeros o últimos), VALUEFILTER_GT, VALUEFILTER_LT (por encima, por debajo de ad_a), VALUEFILTER_BETWEEN (entre ad_a y ad_b). En un campo situado bajo otro de su área, la clasificación se hace dentro de cada padre (la mejor ciudad de cada región); en el primer nivel, sobre toda la tabla. Los miembros empatados con el N-ésimo se quedan todos. El campo debe estar en Filas o en Columnas. Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen, otro tipo, un Top o un Bottom de menos de 1 miembro o una posición sin medida en Valores, -2 si el componente no está creado |
of_clear_value_filter (string as_field) | Quita este filtro. Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen, -2 si el componente no está creado |
of_set_label_filter (string as_field, string as_type, double ad_a, double ad_b) | Filtro numérico sobre el valor del campo en sí: LABEL_GT, LABEL_LT, LABEL_BETWEEN. Un campo fecha toma días, mediante la sobrecarga de texto. El campo debe estar en Filas o en Columnas. Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen, otro tipo o un campo fecha, -2 si el componente no está creado |
of_set_label_filter (string as_field, string as_type, string as_a, string as_b) | Filtro de texto sobre el valor del campo: LABEL_CONTAINS, LABEL_BEGINS, LABEL_ENDS (as_a, sin distinguir mayúsculas y minúsculas; as_b no se usa). En un campo fecha: LABEL_BEFORE, LABEL_AFTER (as_a, un día escrito yyyy-mm-dd) o LABEL_BETWEEN (as_a y as_b, días incluidos). Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen, un tipo que no conviene al campo (LABEL_CONTAINS en una fecha, LABEL_BEFORE en un texto), un texto vacío o un día que no lo es, -2 si el componente no está creado |
of_clear_label_filter (string as_field) | Quita este filtro. Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen, -2 si el componente no está creado |
Los valores que esperan of_set_member_filter y of_set_member_order son los que la cuadrícula guarda, no siempre los que muestra: en un campo derivado de una fecha, un mes se escribe "01"…"12", un trimestre "1"…"4", un año "2024"; un miembro fecha no agrupado se escribe yyyy-mm-dd. El usuario, por su parte, encuentra el filtro de etiqueta en el menú del chip de cualquier campo — texto, número o fecha.
Dar formato #
El formato de un valor se fija mediante una propiedad: of_field("amount").is_format = "$#,##0.00" para todos los valores de un campo, of_value(2).is_format para uno solo (véase Propiedades de un campo y Propiedades de un valor). Es un formato de visualización de PowerBuilder, leído por el motor de máscaras de la biblioteca; vacío, el valor toma el Format de la columna del DataWindow. Los negativos en rojo: of_value(n).ib_negative_red = true. El separador de miles de los valores sin formato sigue siendo is_thousands, la moneda del menú del chip is_currency_symbol.
| Método | Función |
|---|---|
of_set_member_order (string as_field, string as_values_tab) | Orden de visualización impuesto a los valores de un campo (separados por ~t); una cadena vacía restablece el orden natural. Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen, -2 si el componente no está creado |
Fechas y campos calculados #
| Método | Función | |
|---|---|---|
of_group_date_field (string as_field, string as_part) | Crea un campo derivado de una columna date, datetime o timestamp (una hora sola no tiene fecha que agrupar): DATE_YEAR, DATE_QUARTER o DATE_MONTH, llamado <columna>__<parte>. Se incorpora a la lista de campos y se emplea como los demás. Devuelve 0 una vez aplicado, -5 para un campo que los datos no contienen, otra parte o un campo que no contiene fechas (un texto, una hora), -2 si el componente no está creado | |
of_add_calc_field (string as_name, string as_label, string as_formula) | Campo calculado fila a fila ("[amount] * 0.8" = el neto de cada venta, sumado después como cualquier columna), utilizable en cualquier área. No para una razón de totales (precio medio): eso es of_add_calc_measure. Devuelve 0 una vez creado, -5 para un nombre o una fórmula vacíos, un nombre que contiene / o ` | , un nombre ya tomado por una columna o una fórmula que no compila — el motivo está entonces en of_get_last_error, y se dispara ue_calc_field_error —, -2` si el componente no está creado |
of_remove_calc_field (string as_name) | Quita un campo calculado. Devuelve 0 una vez aplicado, -5 si ningún campo calculado tiene este nombre, -2 si el componente no está creado | |
of_add_calc_measure (string as_name, string as_label, string as_formula) | Medida calculada celda a celda, sobre los totales ("[marge] / [ca]" = tasa de margen global). Se coloca únicamente en el área Valores. Su fórmula solo cita campos de datos: otra medida calculada se rechaza. Mismos códigos que of_add_calc_field: 0 una vez creada, -5 (el motivo en of_get_last_error, ue_calc_field_error disparado), -2 si el componente no está creado | |
of_remove_calc_measure (string as_name) | Quita una medida calculada. Devuelve 0 una vez aplicado, -5 si ninguna medida calculada tiene este nombre, -2 si el componente no está creado |
Una fórmula acepta los operadores + - * / ( ), números (12, 0.5, .5, 1e3, siempre con punto) y campos entre corchetes; ^ no es un operador. Una fórmula no válida dispara ue_calc_field_error — nada se bloquea.
Desplegar, memorizar, exportar #
| Método | Función |
|---|---|
of_expand_all ( ) · of_collapse_all ( ) | Despliega o pliega todos los grupos, de filas Y de columnas. Devuelve 0 una vez aplicado, -2 si el componente no está creado |
of_expand_to_level (integer ai_level) | Despliega hasta un nivel dado (1 = solo el primer nivel). Devuelve 0 una vez aplicado, -5 por debajo de 1, -2 si el componente no está creado |
of_get_layout ( ) → string | Devuelve el estado completo de la tabla — consérvelo tal cual y reprodúzcalo con of_set_layout |
of_set_layout (string as_state_json) | Restaura un estado obtenido anteriormente (el JSON de of_get_layout o de ue_layout_changed, tal cual). ue_layout_changed comunica la nueva disposición una vez, nada si es la que ya se muestra; los handles de los valores se liberan. Devuelve 0 una vez aplicado, -5 para un texto vacío o que no es JSON, -2 si el componente no está creado |
of_get_cell_value (string as_row_tuple_json, string as_col_tuple_json, integer ai_measure, ref double ad_value) | Lee el valor de una celda, el que la tabla muestra (0,8 bajo 80,0 %, el acumulado bajo un acumulado). Su fila y su columna son las tuplas que da ue_cell_double_clicked — {"region":"Nord","city":"Lille"}, un miembro vacío null, {} (o una cadena vacía) para el total — y ai_measure es la posición del valor en el área Valores, a partir de 1. Una tupla nombra los campos de su área desde el primero: {"region":"Nord"} es el subtotal de Nord. La respuesta no depende de la visualización: una rama plegada o subtotales ocultos se leen igualmente. Devuelve 0 con el valor en ad_value (NULL para una celda sin valor), -4 cuando la tabla no tiene esa celda (un miembro o un campo que no muestra allí) o no puede leerse ahora — llamada desde un evento que la cuadrícula disparó mientras una orden de este script esperaba: of_get_last_error lo dice, lea después del evento —, -5 para una tupla mal formada o una posición sin medida, -2 si el componente no está creado |
of_get_cell_rows (string as_row_tuple_json, string as_col_tuple_json, ref long al_rows[]) → long | Las filas del DataStore que una celda agrega (drill-through): al_rows[] recibe sus números de fila y se devuelve su número. La celda se direcciona como en of_get_cell_value, sin medida — las mismas filas alimentan todos los valores de la celda. Los números son los que las filas tienen ahora en el DataStore dado a of_from_datastore, incluso tras una ordenación o un filtro de ese DataStore; solo cuentan las filas que pasan todos los filtros de la tabla; en modo demo, las filas más allá del límite no se usan, por lo que no se dan; una fila eliminada desde la carga se omite. Recargue la cuadrícula tras un Retrieve. Devuelve el número (0 o más), -4 cuando la tabla no tiene esa celda o no puede leerse ahora (véase of_get_cell_value), -5 para una tupla mal formada, -2 si el componente no está creado |
of_export_csv (string as_path) | Escribe un archivo CSV de la vista actual (UTF-8 con BOM, punto y coma); ue_csv_saved confirma. Los números toman el separador decimal del idioma de visualización (la coma en francés, alemán, italiano, español, portugués); una etiqueta que se leería como una fórmula (= + - @) se escribe precedida de un apóstrofo. El gemelo de of_export_xlsx. Devuelve 0 una vez aplicado, -5 con un argumento no válido, -2 si el componente no está creado |
of_export_xlsx (string as_path) | Escribe un archivo Excel de la tabla tal como se muestra, formato incluido (negativos en rojo incluidos); ue_xlsx_saved lo confirma — o lo rechaza con su motivo más allá de 16 384 columnas o 1 048 576 filas, los límites de una hoja Excel. Devuelve 0 una vez aplicado, -5 con un argumento no válido, -2 si el componente no está creado |
Comunes #
| Método | Función |
|---|---|
of_reset ( ) | Devuelve el componente a su estado inicial. Devuelve 0 una vez aplicado, -2 si el componente no está creado |
of_set_redraw (boolean) | Agrupa una ráfaga de modificaciones en una sola representación. Devuelve 0 una vez aplicado, -2 si el componente no está creado |
of_save_as_png (string) · of_save_as_jpg (string) | Exporta la representación como imagen. Devuelve 0, -2 si el componente no está creado, -4 si la captura falla, -5 con una ruta vacía |
Eventos #
| Evento | Se activa cuando |
|---|---|
ue_layout_changed (string as_layout_json) | La disposición ha cambiado — por el usuario (mover un campo, cambiar una agregación, ordenar una columna, plegar un grupo…) o por el código: toda orden que cambia lo que devuelve of_get_layout (of_set_layout incluido) lo dispara una vez, y una sola vez al final de una construcción enmarcada por of_set_redraw(false) … of_set_redraw(true); nunca cuando nada cambia, ni con of_reset. as_layout_json es todo lo que of_get_layout guarda |
ue_cell_double_clicked (string as_row_tuple_json, string as_col_tuple_json, integer ai_measure, double ad_value) | Doble clic en una celda: los dos primeros argumentos describen el cruce ({"region":"Nord","city":"Lille"}, un miembro vacío vale null, {} para el total); ai_measure, la posición del valor pulsado en el área Valores, a partir de 1; y ad_value, el valor que la celda muestra — 0.8 bajo « 80,0 % », el acumulado bajo un acumulado — o NULL para una celda vacía. Las dos tuplas y ai_measure forman la dirección que of_get_cell_value vuelve a leer. Es el punto de entrada de un detalle |
ue_csv_saved (string as_path, boolean ab_ok, string as_error) | El archivo CSV se ha escrito — o no, y as_error dice por qué |
ue_xlsx_saved (string as_path, boolean ab_ok, string as_error) | El archivo Excel se ha escrito — o no, y as_error dice por qué |
ue_calc_field_error (string as_field, string as_message) | La fórmula de un campo o de una medida calculada no es válida |
ue_copy (string as_tsv) | El usuario ha copiado una selección de celdas (Ctrl+C): la cuadrícula ya ha puesto el texto en el portapapeles, el evento le entrega el mismo texto — para registrarlo o reutilizarlo en otro lugar. as_tsv es un texto separado por tabulaciones que lleva por defecto los encabezados — los nombres de las columnas en una primera línea, el miembro de cada fila (su ruta completa, « Nord / Lille ») en una primera columna — o solo las cifras con ib_copy_headers = false. En modo demo la copia es una exportación: rechazada, y la cuadrícula lo dice |
ue_ready ( ) | El componente ha terminado de cargarse; todo lo enviado antes se ha reproducido |
ue_runtime_missing ( ) | El runtime WebView2 está ausente: el componente permanece vacío |
ue_bg_color (long al_color) | El componente ha calculado el color de fondo de su tema; el userobject ya lo ha adoptado (backcolor) |
Lo que el usuario puede hacer sin una línea de código #
La tabla está viva: es todo el interés del componente. Con el panel de campos visible (ib_field_list = true), el usuario puede:
- arrastrar un campo de un área a otra y reorganizar el cruce a voluntad;
- cambiar la agregación de una medida (suma, media, recuento…);
- filtrar los valores de un campo mediante una lista de casillas;
- plegar o desplegar un grupo de filas o de columnas;
- ordenar desde un encabezado;
- seleccionar y luego copiar un bloque de celdas, con sus encabezados para que un pegado en una hoja de cálculo diga qué son las cifras (
ib_copy_headers).
Cada una de estas manipulaciones, salvo la copia (que dispara ue_copy), se notifica en ue_layout_changed: unido a of_get_layout / of_set_layout, esto permite ofrecer «vistas guardadas» a sus usuarios.
Con el teclado. La construcción de la tabla se hace sin ratón — chips, menús, plegado y ordenación: Tab lleva a un campo, a un triángulo de plegado o a un encabezado ordenable, Intro o Espacio lo dispara. Sobre un campo abre su menú — el que lleva Añadir a las filas / a las columnas / a los valores / a los filtros y Quitar: toda la construcción de la tabla pasa por ahí. Mayús+F10 o la tecla Menú también abren ese menú, y el foco sigue al campo recién movido, incluso a otra zona. Seleccionar celdas y copiarlas (Ctrl+C) requiere hoy el ratón.
Ejemplos #
Un informe de ventas completo #
// Filas: region y luego ciudad dentro de cada region
uo_crosstab.of_clear_layout()
uo_crosstab.of_add_row_field(/*field*/ "region")
uo_crosstab.of_add_row_field(/*field*/ "city")
// Columnas: una por ano
uo_crosstab.of_add_col_field(/*field*/ "year")
// Celdas: el importe total
uo_crosstab.of_add_value_field(/*field*/ "amount", /*agg*/ uo_crosstab.AGG_SUM)
// Presentacion: importes legibles, subtotales y totales generales
uo_crosstab.of_field(/*field*/ "amount").is_format = "$#,##0"
uo_crosstab.ib_row_subtotals = true
uo_crosstab.ib_row_grand_total = true
uo_crosstab.ib_col_grand_total = true
// Colorear las celdas para detectar los importes altos de un vistazo
uo_crosstab.of_field(/*field*/ "amount").is_conditional_formatting = n_pbt_crosstab_field.CF_SCALE
Etiquetas legibles #
Sus columnas suelen llamarse mt_ht o cd_reg. Cámbieles el nombre de una vez por todas, justo después de of_from_datastore.
// Primero los datos : los campos toman el nombre de las columnas
uo_crosstab.of_from_datastore(/*ads*/ lds)
// Luego una etiqueta legible para cada campo
uo_crosstab.of_field(/*field*/ "region").is_label = "Región"
uo_crosstab.of_field(/*field*/ "city").is_label = "Ciudad"
uo_crosstab.of_field(/*field*/ "category").is_label = "Categoría"
uo_crosstab.of_field(/*field*/ "year").is_label = "Año"
uo_crosstab.of_field(/*field*/ "amount").is_label = "Facturación"
uo_crosstab.of_field(/*field*/ "quantity").is_label = "Cantidad"
Analizar porcentajes en lugar de importes #
// Local variables
n_pbt_crosstab_field lnv_amount
// Partir de areas vacias y luego colocar los campos
uo_crosstab.of_clear_layout()
uo_crosstab.of_add_row_field(/*field*/ "category")
uo_crosstab.of_add_col_field(/*field*/ "year")
uo_crosstab.of_add_value_field(/*field*/ "amount", /*agg*/ uo_crosstab.AGG_SUM)
// Conservar solo dos categorias en pantalla (valores separados por una tabulacion)
uo_crosstab.of_set_member_filter(/*field*/ "category", /*values_tab*/ "Informatique~tMobilier")
// El campo importe, para ajustar como se muestra
lnv_amount = uo_crosstab.of_field(/*field*/ "amount")
// Mostrar la parte de cada celda en el total de su columna
lnv_amount.is_show = lnv_amount.SHOW_PCT_COL
// Una pequena barra en cada celda para comparar las partes de un vistazo
lnv_amount.is_conditional_formatting = lnv_amount.CF_BARS
Una medida propia: el precio medio #
Una medida calculada se evalúa sobre los totales de cada celda, no fila a fila: eso es lo que hace correcto un ratio.
// Partir de areas vacias y luego colocar los campos
uo_crosstab.of_clear_layout()
uo_crosstab.of_add_row_field(/*field*/ "region")
// Los dos totales que serviran de base al calculo
uo_crosstab.of_add_value_field(/*field*/ "amount", /*agg*/ uo_crosstab.AGG_SUM)
uo_crosstab.of_add_value_field(/*field*/ "quantity", /*agg*/ uo_crosstab.AGG_SUM)
// Precio medio = importe total dividido por la cantidad total
uo_crosstab.of_add_calc_measure(/*name*/ "avg_price", /*label*/ "Precio medio", /*formula*/ "[amount] / [quantity]")
uo_crosstab.of_field(/*field*/ "avg_price").is_format = "$#,##0.00"
// Luego colocarla entre los valores como cualquier campo (el agregado no importa: una medida se calcula)
uo_crosstab.of_add_value_field(/*field*/ "avg_price", /*agg*/ uo_crosstab.AGG_SUM)
// event ue_calc_field_error de uo_crosstab: (string as_field, string as_message)
// Formula no valida: avisar sin romper nada, la tabla sigue mostrandose.
uo_status.of_panel(/*key*/ "main").is_text = "Fórmula " + as_field + " : " + as_message
Analizar por mes, trimestre o año #
Una columna de fecha no se cruza tal cual — cada día formaría su propia fila. Derive primero el nivel deseado.
// Crear tres campos derivados de la columna sale_date
uo_crosstab.of_group_date_field(/*field*/ "sale_date", /*part*/ uo_crosstab.DATE_YEAR)
uo_crosstab.of_group_date_field(/*field*/ "sale_date", /*part*/ uo_crosstab.DATE_QUARTER)
uo_crosstab.of_group_date_field(/*field*/ "sale_date", /*part*/ uo_crosstab.DATE_MONTH)
// Despues cruzarlos como cualquier otro campo: ano en columnas, trimestre debajo
uo_crosstab.of_add_col_field(/*field*/ "sale_date__year")
uo_crosstab.of_add_col_field(/*field*/ "sale_date__quarter")
El ranking de las diez mejores regiones #
// Conservar solo las 10 regiones con el total mas alto sobre la primera medida
uo_crosstab.of_set_value_filter(/*field*/ "region", /*type*/ uo_crosstab.VALUEFILTER_TOP, /*a*/ 10, /*b*/ 0, /*measure_index*/ 1)
En un campo situado bajo otro, la clasificación se hace dentro de cada padre: la mejor ciudad de cada región, no la mejor ciudad del país. Los empates se quedan todos.
// Las ciudades bajo las regiones
uo_crosstab.of_add_row_field(/*field*/ "region")
uo_crosstab.of_add_row_field(/*field*/ "city")
// La mejor ciudad de cada region, sobre la primera medida
uo_crosstab.of_set_value_filter(/*field*/ "city", /*type*/ uo_crosstab.VALUEFILTER_TOP, /*a*/ 1, /*b*/ 0, /*measure_index*/ 1)
Exportar #
La exportación reproduce exactamente la vista actual: los mismos filtros, los mismos totales, el mismo formato.
// Hacia Excel: el archivo se escribe directamente en la ruta indicada
uo_crosstab.of_export_xlsx(/*path*/ "C:\temp\ventes.xlsx")
// event ue_xlsx_saved de uo_crosstab: (string as_path, boolean ab_ok, string as_error)
// inv_notif = un n_pbt_toaster declarado como variable de instancia de la ventana
if ab_ok then
inv_notif.is_title = "Exportación finalizada"
inv_notif.is_text = as_path
inv_notif.is_kind = inv_notif.KIND_SUCCESS
else
inv_notif.is_title = "Exportación imposible"
inv_notif.is_text = as_error
inv_notif.is_kind = inv_notif.KIND_ERROR
end if
inv_notif.of_show()
// Hacia CSV: un archivo, como para Excel; ue_csv_saved confirma
uo_crosstab.of_export_csv(/*path*/ "C:\exports\ventes.csv")
Ofrecer vistas guardadas #
// Local variables
string ls_view
// Guardar la vista actual : of_get_layout responde de inmediato
ls_view = uo_crosstab.of_get_layout()
// Conservar el contenido TAL CUAL : se reproduce sin transformacion.
of_save_view(is_current_view, ls_view)
// Mas tarde: reproducir una vista guardada
uo_crosstab.of_set_layout(/*state_json*/ of_read_view("Ventes par region"))
Bajar al detalle que hay detrás de una cifra #
// event ue_cell_double_clicked de uo_crosstab: (string as_row_tuple_json, string as_col_tuple_json, integer ai_measure, double ad_value)
// Local variables
long ll_rows[], ll_count, ll_i
// Las filas del DataStore detras de la cifra con doble clic
ll_count = uo_crosstab.of_get_cell_rows(/*row_tuple_json*/ as_row_tuple_json, /*col_tuple_json*/ as_col_tuple_json, /*rows*/ ll_rows)
// Nada que mostrar : ninguna fila, o una celda que no puede leerse ahora
if ll_count <= 0 then return
// Copiarlas en la lista de detalle : ids_sales es el DataStore dado a of_from_datastore
dw_detail.Reset()
for ll_i = 1 to ll_count
ids_sales.RowsCopy(ll_rows[ll_i], ll_rows[ll_i], Primary!, dw_detail, dw_detail.RowCount() + 1, Primary!)
next
Las dos tuplas y ai_measure forman la dirección de la celda: of_get_cell_value la vuelve a leer más tarde, tal como la tabla la muestra en ese momento.
// ue_cell_double_clicked event of uo_crosstab : the address of the cell, read again later
// Local variables
double ld_value
// Read the cell again from its address : 0 when it exists
if uo_crosstab.of_get_cell_value(/*row_tuple_json*/ as_row_tuple_json, /*col_tuple_json*/ as_col_tuple_json, /*measure*/ ai_measure, /*value*/ ld_value) = 0 then
// ld_value is what the cell shows ; NULL for an empty cell
end if
Una celda también se lee sin clic, por sus miembros: una tupla parcial designa un subtotal. Y of_remove_field quita un campo de todas sus áreas de una vez, filtros incluidos.
// The sales of Nord in 2025, first measure of Values
// Local variables
double ld_north
// A partial address : the region and the year, no other member
uo_crosstab.of_get_cell_value(/*row_tuple_json*/ '{"region":"Nord"}', /*col_tuple_json*/ '{"year":"2025"}', /*measure*/ 1, /*value*/ ld_north)
// Take the year out of the table, filters included
uo_crosstab.of_remove_field(/*field*/ "year")
Buenas prácticas #
- Llame a
of_from_datastoreuna sola vez por conjunto de datos: reorganizar la tabla después no cuesta nada, retransmitir los datos sale caro. - Ponga las etiquetas (
of_field("...").is_label) justo después deof_from_datastore: siguen al campo a todas partes, incluidos el panel de campos y las exportaciones. - Filtre en el lado SQL lo que no está destinado a ser analizado: la tabla dinámica es rápida, pero un DataStore la mitad de grande se abre el doble de rápido.
of_clear_layout()vacía las áreas sin retransmitir los datos: es la llamada adecuada para proponer varios análisis sobre la misma fuente.- Un campo de fecha se cruza siempre mediante
of_group_date_field, nunca directamente. - Deje el panel de campos visible en las pantallas de análisis y ocúltelo en los cuadros de mando fijos.
Heredado de la base común #
Estos miembros existen en todos los componentes visuales — no son propios de este. Se detallan una sola vez, en los capítulos transversales; esta tabla solo dice dónde leerlos.
| Miembros | Función | Detallado en |
|---|---|---|
of_count · of_keys_at · of_has | Recorrer lo que el componente contiene | 3.2 Los items |
of_reset | Poner el componente a cero | 3.6 Poner un componente a cero: of_reset() |
of_set_property · of_get_property · of_component_name | Controlar una propiedad por su nombre | 3.1 El motor de propiedades |
is_accessible_name · is_accessible_description | El nombre y la descripción que dice un lector de pantalla | 3.4 Propiedades y eventos opcionales |
of_register_shortcut · of_clear_shortcuts | Atajos de teclado del componente | 3.5 Los atajos de teclado |
of_is_created · of_is_ready · of_get_last_error | Si ha nacido, si está listo, qué ha fallado | 3.7 Diagnóstico |
of_save_as_png · of_save_as_jpg | Exportar el render como imagen | 3.8 Exportar la representación como imagen |
of_set_redraw | Agrupar los cambios en un solo repintado | 3.10 Buenas prácticas |
of_preload_icons | Iconos mostrados sin retardo | Visualización instantánea: of_icon |
of_set_translation | Traducir una etiqueta del componente | 5.2 Adaptar una etiqueta: of_set_translation |
of_focus_webview | Dar el foco al componente | 6.4 Teclado y foco |
of_print · of_print_to_pdf | Imprimir, o escribir un PDF | 6.9 Imprimir |
Dos ayudas no se heredan: of_icon y of_escape_markup viven en n_pbt_utils. Declare uno — n_pbt_utils lnv_utils, nada que crear — y llámelas sobre él.