crosstab — u_pbt_crosstab #
← Referência dos componentes · Índice do guia
Tabela dinâmica completa: zonas de linhas / colunas / valores alimentadas a partir de um DataStore, agregações, filtros, formatação condicional, agrupamento de datas, medidas calculadas e exportações CSV / Excel.
▶ Ver ao vivo — Aplicação de demonstração, mosaico Crosstab: a pré-visualização, o código que o produz e esta página, lado a lado.
Em resumo #
| Userobject | u_pbt_crosstab |
| Classe de items | n_pbt_crosstab_field (um campo, através de of_field) · n_pbt_crosstab_value (um valor da zona Valores, através de of_value) |
| Serve para | Dar aos utilizadores uma análise cruzada dos dados, que eles próprios reorganizam, sem escrever SQL nem recorrer ao Excel |
| Limite no modo de demonstração | 500 linhas de origem processadas — of_get_cell_value e of_get_cell_rows leem essas 500 linhas, os mesmos valores que no ecrã; exportações CSV e Excel e cópia (Ctrl+C) desativadas — ver o modo de demonstração |
O princípio #
O componente recebe um conjunto de dados plano — um DataStore, ou seja, qualquer consulta já escrita na aplicação. O crosstab trata do resto: deduz a lista dos campos, que devem ser distribuídos por quatro zonas.
| Zona | O que contém | Efeito |
|---|---|---|
| Linhas | Campos de agrupamento | Um nível de linhas por campo, recolhível |
| Colunas | Campos de agrupamento | Um nível de cabeçalhos de coluna por campo |
| Valores | Campos numéricos e a respetiva agregação | O que é calculado nas células |
| Filtros | Campos de seleção | Um filtro acima da tabela, aplicado a tudo |
Todo o cálculo é feito no componente: uma vez transmitidos os dados, reorganizar a tabela não desencadeia qualquer ida e volta à base de dados nem ao PowerBuilder.
Início rápido #
// event open de la fenetre
// Local variables
datastore lds
lds = create datastore
lds.dataobject = "d_sales"
lds.SetTransObject(SQLCA)
lds.Retrieve()
// 1. Transmitir os dados: os campos sao deduzidos das colunas
uo_crosstab.of_from_datastore(/*ads*/ lds)
// 2. Distribuir os campos pelas zonas
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. Apresentar: formato dos montantes e totais gerais
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)
// O utilizador quer o detalhe por tras de um numero: abrir a lista correspondente.
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 |
As constantes de agregação, de filtro, de data e de separador leem-se no componente: uo_crosstab.AGG_SUM. As de um campo ou de um valor (SHOW_*, CF_*) leem-se na classe do seu handle: n_pbt_crosstab_field.CF_SCALE, n_pbt_crosstab_value.SHOW_PCT_GRAND. AGG_COUNT conta os valores não vazios, como a Contagem do Excel; AGG_COUNT_ROWS conta as linhas, vazias incluídas.
Propriedades #
| Propriedade | Tipo | Predefinição | Função |
|---|---|---|---|
is_totals_position | string | "bottom" | Onde se coloca a linha de total geral: TOTALS_BOTTOM (no rodapé, predefinição) ou TOTALS_TOP (no topo, logo abaixo dos cabeçalhos). Só o total geral se desloca: o subtotal de um grupo fica na linha do grupo |
is_values_axis | string | "cols" | Orientação das medidas quando existe mais do que uma: VALUES_COLS (lado a lado em colunas, predefinição) ou VALUES_ROWS (empilhadas em linhas) |
is_currency_symbol | string | "" | A moeda que o menu Formato do número de um chip de valor propõe, ao lado de « Sem símbolo » e « % ». Vazio = a da língua de apresentação ($ em inglês, € nas restantes) |
is_thousands | string | "locale" | Separador de milhares dos valores que não têm formato próprio (is_format vazio e nenhum Format na coluna do DataWindow) — a mesma definição que o menu Opções da grelha: THOUSANDS_LOCALE (o da língua de apresentação, predefinição), THOUSANDS_SPACE, THOUSANDS_NONE, ou o próprio separador (",", ".", " ") |
ib_field_list | boolean | true | Mostra o painel de campos, onde o utilizador reorganiza a tabela com o rato |
ib_row_subtotals | boolean | true | Mostra o subtotal de cada grupo de linhas, escrito na linha do próprio grupo, acima dos seus membros; desativado, essa linha mantém o rótulo sem valor |
ib_col_subtotals | boolean | true | Mostra um subtotal por grupo de colunas |
ib_row_grand_total | boolean | true | Mostra a linha de total geral sob a tabela (a gémea de ib_col_grand_total) |
ib_col_grand_total | boolean | true | Mostra a coluna de total geral após a tabela (a gémea de ib_row_grand_total). Sem campo em Colunas não há coluna de total — a única coluna de valores É o total — e a propriedade mantém o seu valor para quando um campo lá entrar. Com várias medidas, a coluna Total tem um cabeçalho « Total » e depois um cabeçalho por medida, ordenável |
ib_copy_headers | boolean | true | Ctrl+C copia os cabeçalhos com as células selecionadas: os nomes das colunas numa primeira linha (a medida só é nomeada se a tabela a mostrar: vários valores, ou nenhum campo em Colunas) e o membro de cada linha — o seu caminho completo, « Nord / Lille » — numa primeira coluna, cujo canto traz os nomes dos campos em linhas (« region / ville »). Uma colagem numa folha de cálculo diz assim o que são os valores. false copia apenas os valores. Lida em direto, reposta a true por of_reset |
ib_enabled | boolean | true | Esbatido: a grelha continua a mostrar os seus valores — uma tabela dinâmica vazia não é o mesmo que uma desligada pela aplicação — mas deixa de responder ao ponteiro |
is_theme_style | string | "" | Estilo visual do componente (constantes THEME_STYLE_*); vazio = o da aplicação, seguido a cada mudança |
is_theme_mode | string | "" | Variante clara ou escura (constantes THEME_MODE_*); vazio = a da aplicação, seguida a cada mudança |
il_theme_accent | long | -1 | Cor de destaque deste componente (-1 = destaque da aplicação, ou o do tema) |
is_tooltip | string | "" | Tooltip simples apresentado ao passar sobre o componente |
is_super_tooltip_title | string | "" | Título do tooltip enriquecido (prevalece sobre is_tooltip) |
is_super_tooltip_text | string | "" | Texto do tooltip enriquecido (aceita marcação enriquecida) |
is_super_tooltip_image | string | "" | Imagem do tooltip enriquecido |
Propriedades de um campo #
of_field (string as_field) devolve o handle de um campo: obtém-se uma vez e, a partir daí, o campo é controlado através das suas propriedades. O handle é criado na primeira chamada e reutilizado depois.
| Propriedade | Tipo | Predefinição | Função |
|---|---|---|---|
is_label | string | cabeçalho do DataWindow | Etiqueta legível do campo ("amount" → "Volume de negócios"). Por defeito o texto de cabeçalho da coluna no DataWindow, senão o seu nome; uma cadeia vazia repõe-no. Definida pelo seu código, sobrevive a of_from_datastore |
is_show | string | "normal" | O que a célula apresenta: "normal", "pctGrand" (% do total geral), "pctRow" (% da linha), "pctCol" (% da coluna), "running" (acumulado), "diff" (diferença face ao anterior) |
is_conditional_formatting | string | "none" | Formatação condicional: CF_NONE, CF_SCALE (escala de cores), CF_BARS (barras na célula) ou CF_ICONS (uma seta por terço: subida, estável, descida) |
is_format | string | Format da coluna | Formato de apresentação dos valores do campo, um formato de apresentação PowerBuilder ("#,##0.00", "$#,##0;($#,##0)", "0.0%"), escrito com os separadores da língua de apresentação. Vazio = o Format da coluna do DataWindow, lido por of_from_datastore; sem um nem outro, is_thousands e no máximo duas casas decimais. Relida: o formato em vigor, o definido ou senão o da coluna |
Os valores de is_show e de is_conditional_formatting estão também disponíveis como constantes no handle (SHOW_PCT_COL, CF_SCALE…). Definidos no campo, is_show, is_conditional_formatting e is_format valem para todos os seus valores da zona Valores e para os que lá entrarem depois — definidos antes de o campo estar em Valores, são guardados, relidos tal como definidos e aplicados à sua chegada. Para um só valor (o mesmo campo duas vezes: o montante, e a sua parte do total), usa-se o handle do valor, mais abaixo.
// Local variables
n_pbt_crosstab_field lnv_field
// O campo montante : a sua etiqueta e uma escala de cores
lnv_field = uo_crosstab.of_field(/*field*/ "amount")
lnv_field.is_label = "Volume de negócios"
lnv_field.is_conditional_formatting = lnv_field.CF_SCALE
⚠️ Alteração incompatível. Esta propriedade chamava-se
is_cf: a abreviatura nada dizia no ponto de utilização. O nome antigo já não existe — um código que o utilize não compila. A substituição é mecânica:is_cf→is_conditional_formatting, sem alteração de valores nem de comportamento.
Propriedades de um valor #
of_value (integer ai_position) devolve o handle de um valor da zona Valores, pela sua posição (1 = o primeiro). Um campo pode lá estar duas vezes — o montante, e a sua parte do total geral — e cada valor tem as suas definições, que prevalecem sobre as do seu campo. O handle designa uma posição: após of_remove_value, of_move_value, of_remove_field, of_remove_calc_measure, of_clear_layout ou of_set_layout é libertado — peça of_value de novo. As suas constantes SHOW_* e CF_* são as de n_pbt_crosstab_field.
| Propriedade | Tipo | Predefinição | Função |
|---|---|---|---|
is_agg | string | a de of_add_value_field | Agregação deste valor: uma constante AGG_* do componente (uo_crosstab.AGG_AVG). Uma medida calculada não tem nenhuma a escolher: a atribuição é ignorada |
is_show | string | "normal" | O que as células deste valor apresentam: SHOW_NORMAL, SHOW_PCT_GRAND, SHOW_PCT_ROW, SHOW_PCT_COL, SHOW_RUNNING, SHOW_DIFF (como no campo) |
is_conditional_formatting | string | "none" | Formatação condicional deste valor: CF_NONE, CF_SCALE, CF_BARS, CF_ICONS |
is_format | string | Format da coluna | Formato de apresentação só deste valor, um formato PowerBuilder ("#,##0.00", "$#,##0;($#,##0)"). Vazio = o Format da coluna do DataWindow. Relida: o formato em vigor |
ib_negative_red | boolean | false | Os números negativos deste valor a vermelho — no ecrã e na exportação Excel. A mesma definição que a entrada « Negativos a vermelho » do menu do chip |
is_label | string | "" | Etiqueta deste valor nos cabeçalhos (« Parte das vendas »); vazio repõe a de origem, a agregação e a etiqueta do campo (« Soma de Montante ») |
| Método | Função |
|---|---|
of_field ( ) → string | O campo que este valor agrega ("amount"); "" quando a posição não contém nenhum valor |
// Local variables
n_pbt_crosstab_value lnv_share, lnv_amount
// O montante duas vezes em Valores : a soma, depois a sua parte do total geral
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)
// So o segundo valor : a sua parte do total geral, com a sua propria 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 das vendas"
// O primeiro valor : duas casas decimais, os negativos a vermelho
lnv_amount = uo_crosstab.of_value(/*position*/ 1)
lnv_amount.is_format = "#,##0.00"
lnv_amount.ib_negative_red = true
Métodos #
Alimentar e nomear #
| Método | Função |
|---|---|
of_from_datastore (datastore ads) | Transmite o conjunto de dados: os campos são deduzidos das colunas do DataStore, a respetiva etiqueta do texto de cabeçalho e o formato predefinido dos seus valores do Format da coluna. Um texto com uma tabulação ou uma quebra de linha fica na sua linha; um valor CHAR perde os espaços de preenchimento; um decimal lê-se com o separador do posto; um valor vazio (NULL, texto vazio) é um único membro (vazio), colocado em último; as etiquetas definidas por is_label sobrevivem ao recarregamento. Devolve 0 depois de carregados os dados, -5 se o DataStore não for válido ou não tiver qualquer coluna, -2 se o componente não estiver criado |
of_from_datastore (datawindow adw) | O mesmo carregamento a partir de um DataWindow: a tabela cruzada lê as suas linhas através de um DataStore de trabalho que as partilha (ShareData, verificado) — o que o DataWindow mostra, a tabela cruza. Devolve 0, -5 quando adw não é válido, não tem colunas ou não consegue partilhar as suas linhas (of_get_last_error diz qual), -2 quando o componente não está criado |
of_from_datastore (datawindowchild adwc) | O mesmo carregamento a partir de um DataWindowChild (um DataWindow pendente). Os mesmos valores devolvidos |
of_field (string as_field) | Devolve o handle de um campo, para o etiquetar ou formatar (ver Propriedades de um campo) |
of_value (integer ai_position) → n_pbt_crosstab_value | Devolve o handle de um valor da zona Valores, pela sua posição (1 = o primeiro), para definir a sua agregação, apresentação, formato ou etiqueta (ver Propriedades de um valor) |
of_value_count ( ) → long | O número de valores da zona Valores (um campo que lá está duas vezes conta duas vezes); 0 quando a zona está vazia ou o componente não está criado |
Construir a tabela #
| Método | Função |
|---|---|
of_clear_layout ( ) | Esvazia as quatro zonas: a tabela volta a ficar vazia, os dados permanecem carregados. Devolve 0 depois de aplicado, -2 se o componente não estiver criado |
of_add_row_field (string as_field) | Adiciona um campo à zona Linhas (a ordem das chamadas determina a ordem dos níveis). Um campo já em Valores fica lá: pode estar nos dois (uma contagem distinta por membro). Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm ou para uma medida calculada (só vai para Valores), -2 se o componente não estiver criado |
of_add_col_field (string as_field) | Adiciona um campo à zona Colunas. Um campo já em Valores fica lá: pode estar nos dois (uma contagem distinta por membro). Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm ou para uma medida calculada (só vai para Valores), -2 se o componente não estiver criado |
of_add_value_field (string as_field, string as_agg) | Adiciona uma medida à zona Valores, com a respetiva agregação (AGG_*, vazio = AGG_SUM). O campo mantém o seu lugar em Linhas, Colunas ou Filtros. Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm ou outra agregação, -2 se o componente não estiver criado |
of_add_filter_field (string as_field) | Adiciona um campo à zona Filtros, acima da tabela. Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm ou para uma medida calculada (só vai para Valores), -2 se o componente não estiver criado |
of_remove_field (string as_field) | Retira um campo da tabela: de todas as zonas onde está (Linhas, Colunas, Filtros, e cada um dos seus valores), com os seus filtros — como «Remover» a partir da lista de campos. O resto da disposição fica como está, e ue_layout_changed comunica a nova. Devolve 0 depois de aplicado, -4 quando o campo não está em nenhuma zona, -5 para um campo que os dados não contêm |
of_remove_value (integer ai_position) | Retira um valor da zona Valores, pela sua posição (a 2.ª « Soma de Montante » enquanto a 1.ª fica); com o último valor de um campo que não está em mais nenhum lado partem os seus filtros. Devolve 0 depois de aplicado, -5 para uma posição sem valor, -2 se o componente não estiver criado |
of_move_value (integer ai_from, integer ai_to) | Desloca um valor da zona Valores de uma posição para outra (1 = o primeiro): as suas definições, os filtros e a ordenação que o nomeiam acompanham-no. Devolve 0 depois de aplicado, -5 para uma posição sem valor, -2 se o componente não estiver criado |
As agregações aceites por of_add_value_field são disponibilizadas pelo componente sob a forma de constantes: AGG_SUM (predefinição), AGG_COUNT (o número de valores não vazios, como a Contagem do Excel), AGG_COUNT_ROWS (o número de linhas, vazias incluídas), AGG_DISTINCT_COUNT (contagem de valores distintos, as células vazias são ignoradas como no Excel), AGG_AVG, AGG_MIN, AGG_MAX. Uma soma sem qualquer valor é uma célula vazia, não 0; uma « diferença face ao anterior » em que uma das duas células está vazia também é vazia. As percentagens escrevem-se como a língua de apresentação as escreve (57.9% em inglês, 57,9 % em francês).
Totais e subtotais #
Os totais definem-se através de propriedades, descritas acima: ib_row_grand_total e ib_col_grand_total para os totais gerais, ib_row_subtotals e ib_col_subtotals para os subtotais, is_totals_position para a posição da linha de total geral. O menu Opções da grelha (a roda dentada) oferece os mesmos quatro interruptores, uma entrada por eixo: Linha do total geral, Coluna do total geral, Subtotais das linhas, Subtotais das colunas.
Filtrar #
| Método | Função |
|---|---|
of_set_member_filter (string as_field, string as_values_tab) | Mantém apenas os valores listados de um campo. Os valores são separados por tabulações (~t) e escrevem-se como a grelha os guarda (ver por baixo da tabela); uma lista vazia remove o filtro, como of_set_member_order. Um campo que não está em nenhuma zona passa para a zona Filtros, para que o filtro se veja. Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm, -2 se o componente não estiver criado |
of_clear_member_filter (string as_field) | Remove este filtro. Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm, -2 se o componente não estiver criado |
of_set_value_filter (string as_field, string as_type, double ad_a, double ad_b, integer ai_measure_index) | Filtra sobre o total da medida n.º ai_measure_index (a primeira = 1): VALUEFILTER_TOP ou VALUEFILTER_BOTTOM (os ad_a primeiros ou últimos), VALUEFILTER_GT, VALUEFILTER_LT (acima, abaixo de ad_a), VALUEFILTER_BETWEEN (entre ad_a e ad_b). Num campo colocado sob outro da sua zona, a classificação faz-se dentro de cada pai (a melhor cidade de cada região); no primeiro nível, sobre toda a tabela. Os membros empatados com o N-ésimo ficam todos. O campo deve estar em Linhas ou em Colunas. Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm, outro tipo, um Top ou um Bottom de menos de 1 membro ou uma posição sem medida em Valores, -2 se o componente não estiver criado |
of_clear_value_filter (string as_field) | Remove este filtro. Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm, -2 se o componente não estiver criado |
of_set_label_filter (string as_field, string as_type, double ad_a, double ad_b) | Filtro numérico sobre o próprio valor do campo: LABEL_GT, LABEL_LT, LABEL_BETWEEN. Um campo de data recebe dias, através da sobrecarga de texto. O campo deve estar em Linhas ou em Colunas. Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm, outro tipo ou um campo de data, -2 se o componente não estiver criado |
of_set_label_filter (string as_field, string as_type, string as_a, string as_b) | Filtro de texto sobre o valor do campo: LABEL_CONTAINS, LABEL_BEGINS, LABEL_ENDS (as_a, sem distinguir maiúsculas e minúsculas; as_b não é usado). Num campo de data: LABEL_BEFORE, LABEL_AFTER (as_a, um dia escrito yyyy-mm-dd) ou LABEL_BETWEEN (as_a e as_b, dias incluídos). Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm, um tipo que não convém ao campo (LABEL_CONTAINS numa data, LABEL_BEFORE num texto), um texto vazio ou um dia que não o é, -2 se o componente não estiver criado |
of_clear_label_filter (string as_field) | Remove este filtro. Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm, -2 se o componente não estiver criado |
Os valores que of_set_member_filter e of_set_member_order esperam são os que a grelha guarda, nem sempre os que apresenta: num campo derivado de uma data, um mês escreve-se "01"…"12", um trimestre "1"…"4", um ano "2024"; um membro de data não agrupado escreve-se yyyy-mm-dd. O utilizador, por seu lado, encontra o filtro de etiqueta no menu do chip de qualquer campo — texto, número ou data.
Formatar #
O formato de um valor define-se através de uma propriedade: of_field("amount").is_format = "$#,##0.00" para todos os valores de um campo, of_value(2).is_format para um só (ver Propriedades de um campo e Propriedades de um valor). É um formato de apresentação PowerBuilder, lido pelo motor de máscaras da biblioteca; vazio, o valor recebe o Format da coluna do DataWindow. Os negativos a vermelho: of_value(n).ib_negative_red = true. O separador de milhares dos valores sem formato continua a ser is_thousands, a moeda do menu do chip is_currency_symbol.
| Método | Função |
|---|---|
of_set_member_order (string as_field, string as_values_tab) | Ordem de apresentação imposta aos valores de um campo (separados por ~t); uma cadeia vazia repõe a ordem natural. Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm, -2 se o componente não estiver criado |
Datas e campos calculados #
| Método | Função | |
|---|---|---|
of_group_date_field (string as_field, string as_part) | Cria um campo derivado de uma coluna date, datetime ou timestamp (uma hora sozinha não tem data para agrupar): DATE_YEAR, DATE_QUARTER ou DATE_MONTH, chamado <coluna>__<parte>. Junta-se à lista dos campos e utiliza-se como os restantes. Devolve 0 depois de aplicado, -5 para um campo que os dados não contêm, outra parte ou um campo que não contém datas (um texto, uma hora), -2 se o componente não estiver criado | |
of_add_calc_field (string as_name, string as_label, string as_formula) | Campo calculado linha a linha ("[amount] * 0.8" = o líquido de cada venda, depois somado como qualquer coluna), utilizável em qualquer zona. Não para um rácio de totais (preço médio): isso é of_add_calc_measure. Devolve 0 depois de criado, -5 para um nome ou uma fórmula vazios, um nome que contém / ou ` | , um nome já usado por uma coluna ou uma fórmula que não compila — o motivo fica então em of_get_last_error, e ue_calc_field_error é desencadeado —, -2` se o componente não estiver criado |
of_remove_calc_field (string as_name) | Remove um campo calculado. Devolve 0 depois de aplicado, -5 se nenhum campo calculado tiver este nome, -2 se o componente não estiver criado | |
of_add_calc_measure (string as_name, string as_label, string as_formula) | Medida calculada célula a célula, sobre os totais ("[marge] / [ca]" = taxa de margem global). Coloca-se unicamente na zona Valores. A sua fórmula só cita campos de dados: outra medida calculada é recusada. Os mesmos códigos que of_add_calc_field: 0 depois de criada, -5 (o motivo em of_get_last_error, ue_calc_field_error desencadeado), -2 se o componente não estiver criado | |
of_remove_calc_measure (string as_name) | Remove uma medida calculada. Devolve 0 depois de aplicado, -5 se nenhuma medida calculada tiver este nome, -2 se o componente não estiver criado |
Uma fórmula aceita os operadores + - * / ( ), números (12, 0.5, .5, 1e3, sempre com ponto) e campos entre parênteses retos; ^ não é um operador. Uma fórmula inválida desencadeia ue_calc_field_error — nada falha.
Expandir, memorizar, exportar #
| Método | Função |
|---|---|
of_expand_all ( ) · of_collapse_all ( ) | Expande ou recolhe todos os grupos, de linhas E de colunas. Devolve 0 depois de aplicado, -2 se o componente não estiver criado |
of_expand_to_level (integer ai_level) | Expande até um determinado nível (1 = apenas o primeiro nível). Devolve 0 depois de aplicado, -5 abaixo de 1, -2 se o componente não estiver criado |
of_get_layout ( ) → string | Devolve o estado completo da tabela — guarde-o tal como está e reproduza-o com of_set_layout |
of_set_layout (string as_state_json) | Restaura um estado obtido anteriormente (o JSON de of_get_layout ou de ue_layout_changed, tal como está). ue_layout_changed comunica a nova disposição uma vez, nada se for a já apresentada; os handles dos valores são libertados. Devolve 0 depois de aplicado, -5 para um texto vazio ou que não seja JSON, -2 se o componente não estiver criado |
of_get_cell_value (string as_row_tuple_json, string as_col_tuple_json, integer ai_measure, ref double ad_value) | Lê o valor de uma célula, o que a tabela apresenta (0,8 sob 80,0 %, o acumulado sob um acumulado). A sua linha e a sua coluna são os tuplos que ue_cell_double_clicked fornece — {"region":"Nord","city":"Lille"}, um membro vazio null, {} (ou uma cadeia vazia) para o total — e ai_measure é a posição do valor na zona Valores, a partir de 1. Um tuplo nomeia os campos da sua zona a partir do primeiro: {"region":"Nord"} é o subtotal de Nord. A resposta não depende da apresentação: um ramo recolhido ou subtotais ocultos leem-se na mesma. Devolve 0 com o valor em ad_value (NULL para uma célula sem valor), -4 quando a tabela não tem essa célula (um membro ou um campo que não mostra aí) ou não pode ser lida agora — chamada a partir de um evento que a grelha desencadeou enquanto uma ordem deste script esperava: of_get_last_error di-lo, leia depois do evento —, -5 para um tuplo mal formado ou uma posição sem medida, -2 se o componente não estiver criado |
of_get_cell_rows (string as_row_tuple_json, string as_col_tuple_json, ref long al_rows[]) → long | As linhas do DataStore que uma célula agrega (drill-through): al_rows[] recebe os seus números de linha e é devolvido o seu número. A célula endereça-se como em of_get_cell_value, sem medida — as mesmas linhas alimentam todos os valores da célula. Os números são os que as linhas têm agora no DataStore dado a of_from_datastore, mesmo após uma ordenação ou um filtro desse DataStore; só contam as linhas que passam todos os filtros da tabela; no modo de demonstração, as linhas além do limite não são usadas, logo não são devolvidas; uma linha eliminada desde o carregamento é omitida. Recarregue a grelha após um Retrieve. Devolve o número (0 ou mais), -4 quando a tabela não tem essa célula ou não pode ser lida agora (ver of_get_cell_value), -5 para um tuplo mal formado, -2 se o componente não estiver criado |
of_export_csv (string as_path) | Escreve um ficheiro CSV da vista atual (UTF-8 com BOM, ponto e vírgula); ue_csv_saved confirma. Os números tomam o separador decimal da língua de apresentação (a vírgula em francês, alemão, italiano, espanhol, português); um rótulo que seria lido como uma fórmula (= + - @) é escrito precedido de um apóstrofo. O gémeo de of_export_xlsx. Devolve 0 depois de aplicado, -5 num argumento inválido, -2 se o componente não estiver criado |
of_export_xlsx (string as_path) | Escreve um ficheiro Excel da tabela tal como é apresentada, formatação incluída (negativos a vermelho incluídos); ue_xlsx_saved confirma — ou recusa com o motivo acima de 16 384 colunas ou 1 048 576 linhas, os limites de uma folha Excel. Devolve 0 depois de aplicado, -5 num argumento inválido, -2 se o componente não estiver criado |
Comuns #
| Método | Função |
|---|---|
of_reset ( ) | Repõe o componente no seu estado inicial. Devolve 0 depois de aplicado, -2 se o componente não estiver criado |
of_set_redraw (boolean) | Agrupa uma série de modificações numa única representação. Devolve 0 depois de aplicado, -2 se o componente não estiver criado |
of_save_as_png (string) · of_save_as_jpg (string) | Exporta a representação como imagem. Devolve 0, -2 se o componente não estiver criado, -4 se a captura falhar, -5 num caminho vazio |
Eventos #
| Evento | Acionado quando |
|---|---|
ue_layout_changed (string as_layout_json) | A disposição mudou — pelo utilizador (deslocar um campo, mudar uma agregação, ordenar uma coluna, recolher um grupo…) ou pelo código: qualquer ordem que mude o que of_get_layout devolve (of_set_layout incluído) desencadeia-o uma vez, e uma só vez no fim de uma construção enquadrada por of_set_redraw(false) … of_set_redraw(true); nunca quando nada muda, nem com of_reset. as_layout_json é tudo o 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) | Duplo clique numa célula: os dois primeiros argumentos descrevem o cruzamento ({"region":"Nord","city":"Lille"}, um membro vazio vale null, {} para o total), ai_measure a posição do valor clicado na zona Valores, a partir de 1, e ad_value o valor que a célula apresenta — 0.8 sob « 80,0 % », o acumulado sob um acumulado — ou NULL para uma célula vazia. Os dois tuplos e ai_measure formam o endereço que of_get_cell_value volta a ler. É o ponto de entrada de um detalhe |
ue_csv_saved (string as_path, boolean ab_ok, string as_error) | O ficheiro CSV foi escrito — ou não, e as_error diz porquê |
ue_xlsx_saved (string as_path, boolean ab_ok, string as_error) | O ficheiro Excel foi escrito — ou não, e as_error indica porquê |
ue_calc_field_error (string as_field, string as_message) | Uma fórmula de campo ou de medida calculada é inválida |
ue_copy (string as_tsv) | O utilizador copiou uma seleção de células (Ctrl+C): a grelha já colocou o texto na área de transferência, o evento devolve-lhe o mesmo texto — para o registar ou reutilizar noutro lado. as_tsv é um texto separado por tabulações que traz por defeito os cabeçalhos — os nomes das colunas numa primeira linha, o membro de cada linha (o seu caminho completo, « Nord / Lille ») numa primeira coluna — ou só os valores com ib_copy_headers = false. No modo de demonstração a cópia é uma exportação: recusada, e a grelha di-lo |
ue_ready ( ) | O componente terminou o carregamento; tudo o que foi enviado antes foi reproduzido |
ue_runtime_missing ( ) | O runtime WebView2 está ausente: o componente permanece vazio |
ue_bg_color (long al_color) | O componente calculou a cor de fundo do respetivo tema; o userobject já a adotou (backcolor) |
O que o utilizador pode fazer sem uma linha de código #
A tabela é viva: é esse todo o interesse do componente. Com o painel de campos visível (ib_field_list = true), o utilizador pode:
- arrastar um campo de uma zona para outra e reorganizar o cruzamento à vontade;
- alterar a agregação de uma medida (soma, média, contagem…);
- filtrar os valores de um campo através de uma lista de seleção;
- recolher ou expandir um grupo de linhas ou de colunas;
- ordenar através de um cabeçalho;
- selecionar e depois copiar um bloco de células, com os seus cabeçalhos para que uma colagem numa folha de cálculo diga o que são os valores (
ib_copy_headers).
Cada uma destas manipulações, exceto a cópia (que desencadeia ue_copy), é comunicada em ue_layout_changed: em conjunto com of_get_layout / of_set_layout, permite oferecer «vistas guardadas» aos utilizadores.
Com o teclado. A construção da tabela faz-se sem rato — chips, menus, dobragem e ordenação: Tab leva a um campo, a um triângulo de dobragem ou a um cabeçalho ordenável, Enter ou Espaço aciona-o. Num campo abre o respetivo menu — aquele que tem Adicionar às linhas / às colunas / aos valores / aos filtros e Remover: toda a construção da tabela passa por aí. Shift+F10 ou a tecla Menu também abrem esse menu, e o foco segue o campo acabado de mover, mesmo para outra zona. Selecionar células e copiá-las (Ctrl+C) exige hoje o rato.
Exemplos #
Um relatório de vendas completo #
// Linhas: regiao e depois cidade dentro de cada regiao
uo_crosstab.of_clear_layout()
uo_crosstab.of_add_row_field(/*field*/ "region")
uo_crosstab.of_add_row_field(/*field*/ "city")
// Colunas: uma por ano
uo_crosstab.of_add_col_field(/*field*/ "year")
// Celulas: o montante total
uo_crosstab.of_add_value_field(/*field*/ "amount", /*agg*/ uo_crosstab.AGG_SUM)
// Apresentacao: montantes legiveis, subtotais e totais gerais
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
// Colorir as celulas para identificar os grandes montantes num relance
uo_crosstab.of_field(/*field*/ "amount").is_conditional_formatting = n_pbt_crosstab_field.CF_SCALE
Etiquetas legíveis #
As colunas chamam-se muitas vezes mt_ht ou cd_reg. Devem ser renomeadas de uma vez por todas, logo a seguir a of_from_datastore.
// Primeiro os dados : os campos tomam o nome das colunas
uo_crosstab.of_from_datastore(/*ads*/ lds)
// Depois uma etiqueta legivel para cada campo
uo_crosstab.of_field(/*field*/ "region").is_label = "Região"
uo_crosstab.of_field(/*field*/ "city").is_label = "Cidade"
uo_crosstab.of_field(/*field*/ "category").is_label = "Categoria"
uo_crosstab.of_field(/*field*/ "year").is_label = "Ano"
uo_crosstab.of_field(/*field*/ "amount").is_label = "Volume de negócios"
uo_crosstab.of_field(/*field*/ "quantity").is_label = "Quantidade"
Analisar proporções em vez de montantes #
// Local variables
n_pbt_crosstab_field lnv_amount
// Partir de zonas vazias e depois colocar os 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)
// Manter apenas duas categorias no ecra (valores separados por uma tabulacao)
uo_crosstab.of_set_member_filter(/*field*/ "category", /*values_tab*/ "Informatique~tMobilier")
// O campo montante, para definir como e apresentado
lnv_amount = uo_crosstab.of_field(/*field*/ "amount")
// Mostrar a proporcao de cada celula no total da sua coluna
lnv_amount.is_show = lnv_amount.SHOW_PCT_COL
// Uma pequena barra em cada celula para comparar as proporcoes num relance
lnv_amount.is_conditional_formatting = lnv_amount.CF_BARS
Uma medida à medida: o preço médio #
Uma medida calculada é avaliada sobre os totais de cada célula, e não linha a linha: é isso que torna um rácio correto.
// Partir de zonas vazias e depois colocar os campos
uo_crosstab.of_clear_layout()
uo_crosstab.of_add_row_field(/*field*/ "region")
// Os dois totais que vao servir de base ao 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)
// Preco medio = montante total dividido pela quantidade total
uo_crosstab.of_add_calc_measure(/*name*/ "avg_price", /*label*/ "Preço médio", /*formula*/ "[amount] / [quantity]")
uo_crosstab.of_field(/*field*/ "avg_price").is_format = "$#,##0.00"
// Depois coloca-la entre os valores como qualquer campo (o agregado nao conta: uma medida calcula-se)
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 invalida: avisar sem nada quebrar, a tabela permanece visivel.
uo_status.of_panel(/*key*/ "main").is_text = "Fórmula " + as_field + " : " + as_message
Analisar por mês, trimestre ou ano #
Uma coluna de data não se cruza tal como está — cada dia daria origem à sua própria linha. É necessário derivar primeiro o nível pretendido.
// Criar tres campos derivados da coluna 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)
// Depois cruza-los como qualquer outro campo: ano em colunas, trimestre por baixo
uo_crosstab.of_add_col_field(/*field*/ "sale_date__year")
uo_crosstab.of_add_col_field(/*field*/ "sale_date__quarter")
O ranking das dez melhores regiões #
// Manter apenas as 10 regioes com maior total na primeira medida
uo_crosstab.of_set_value_filter(/*field*/ "region", /*type*/ uo_crosstab.VALUEFILTER_TOP, /*a*/ 10, /*b*/ 0, /*measure_index*/ 1)
Num campo colocado sob outro, a classificação faz-se dentro de cada pai: a melhor cidade de cada região, não a melhor cidade do país. Os empates ficam todos.
// As cidades sob as regioes
uo_crosstab.of_add_row_field(/*field*/ "region")
uo_crosstab.of_add_row_field(/*field*/ "city")
// A melhor cidade de cada regiao, na primeira medida
uo_crosstab.of_set_value_filter(/*field*/ "city", /*type*/ uo_crosstab.VALUEFILTER_TOP, /*a*/ 1, /*b*/ 0, /*measure_index*/ 1)
Exportar #
A exportação reproduz exatamente a vista em curso: os mesmos filtros, os mesmos totais, a mesma formatação.
// Para Excel: o ficheiro e escrito diretamente no caminho indicado
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 = um n_pbt_toaster declarado como variavel de instancia da window
if ab_ok then
inv_notif.is_title = "Exportação concluída"
inv_notif.is_text = as_path
inv_notif.is_kind = inv_notif.KIND_SUCCESS
else
inv_notif.is_title = "Exportação impossível"
inv_notif.is_text = as_error
inv_notif.is_kind = inv_notif.KIND_ERROR
end if
inv_notif.of_show()
// Para CSV: um ficheiro, como para o Excel; ue_csv_saved confirma
uo_crosstab.of_export_csv(/*path*/ "C:\exports\ventes.csv")
Oferecer vistas guardadas #
// Local variables
string ls_view
// Guardar a vista atual : of_get_layout responde de imediato
ls_view = uo_crosstab.of_get_layout()
// Guardar o conteudo TAL COMO ESTA : reproduz-se sem transformacao.
of_save_view(is_current_view, ls_view)
// Mais tarde: repor uma vista guardada
uo_crosstab.of_set_layout(/*state_json*/ of_read_view("Ventes par region"))
Descer ao detalhe por trás de um número #
// 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
// As linhas do DataStore por tras do numero com duplo clique
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 a mostrar : nenhuma linha, ou uma celula que nao pode ser lida agora
if ll_count <= 0 then return
// Copia-las para a lista de detalhe : ids_sales e o 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
Os dois tuplos e ai_measure formam o endereço da célula: of_get_cell_value volta a lê-lo mais tarde, tal como a tabela o apresenta nesse 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
Uma célula também se lê sem clique, pelos seus membros: um tuplo parcial designa um subtotal. E of_remove_field retira um campo de todas as suas zonas de uma só vez, filtros incluídos.
// 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")
Boas práticas #
- Chamar
of_from_datastoreuma única vez por conjunto de dados: reorganizar a tabela em seguida não tem custo, retransmitir os dados é dispendioso. - Colocar as etiquetas (
of_field("...").is_label) logo a seguir aof_from_datastore: acompanham o campo em toda a parte, incluindo no painel de campos e nas exportações. - Filtrar do lado do SQL aquilo que não se destina a ser analisado: o crosstab é rápido, mas um DataStore duas vezes mais pequeno abre-se duas vezes mais depressa.
of_clear_layout()esvazia as zonas sem retransmitir os dados: é a chamada indicada para propor várias análises sobre a mesma origem.- Um campo de data cruza-se sempre através de
of_group_date_field, nunca diretamente. - O painel de campos deve permanecer visível nos ecrãs de análise e ser ocultado nos painéis de bordo fixos.
Herdado da base comum #
Estes membros existem em todos os componentes visuais — não são próprios deste. São detalhados uma só vez, nos capítulos transversais; esta tabela apenas diz onde os ler.
| Membros | Função | Detalhado em |
|---|---|---|
of_count · of_keys_at · of_has | Percorrer o que o componente contém | 3.2 Os items |
of_reset | Repor o componente a zero | 3.6 Repor um componente a zero: of_reset() |
of_set_property · of_get_property · of_component_name | Controlar uma propriedade pelo nome | 3.1 O motor de propriedades |
is_accessible_name · is_accessible_description | O nome e a descrição que um leitor de ecrã diz | 3.4 Propriedades e eventos opcionais |
of_register_shortcut · of_clear_shortcuts | Atalhos de teclado do componente | 3.5 Os atalhos de teclado |
of_is_created · of_is_ready · of_get_last_error | Se nasceu, se está pronto, o que falhou | 3.7 Diagnóstico |
of_save_as_png · of_save_as_jpg | Exportar a renderização como imagem | 3.8 Exportar a representação como imagem |
of_set_redraw | Agrupar as alterações num único repinte | 3.10 Boas práticas |
of_preload_icons | Ícones mostrados sem atraso | Apresentação instantânea: of_icon |
of_set_translation | Traduzir uma legenda do componente | 5.2 Adaptar uma etiqueta: of_set_translation |
of_focus_webview | Dar o foco ao componente | 6.4 Teclado e focus |
of_print · of_print_to_pdf | Imprimir, ou escrever um PDF | 6.9 Imprimir |
Duas ajudas não são herdadas: of_icon e of_escape_markup vivem em n_pbt_utils. Declare um — n_pbt_utils lnv_utils, nada a criar — e chame-as nele.