How can I display the row where the sum of ls_out is 1900? How can I improve below code?
TABLES: mara, marc.
"marc is N 181
"mara is 1 157
DATA: lt_mara TYPE TABLE OF mara,
ls_mara TYPE mara,
lt_marc TYPE TABLE OF marc,
ls_marc TYPE marc,
BEGIN OF ls_out OCCURS 0,
mtart LIKE mara-mtart,
matnr LIKE marc-matnr,
werks LIKE marc-werks,
ntgew LIKE mara-ntgew,
brgew LIKE mara-brgew,
sum LIKE mara-brgew,
color(4).
DATA: END OF ls_out.
DATA: lt_out LIKE TABLE OF ls_out,
fcat TYPE slis_t_fieldcat_alv,
ls_fcat LIKE LINE OF fcat,
layout TYPE slis_layout_alv.
FIELD-SYMBOLS: <fsym> LIKE LINE OF fcat.
PARAMETERS: p_mtart TYPE mara-mtart. "FERT
SELECT-OPTIONS: so_werks FOR marc-werks. " 1000 to 1998
SELECT * FROM mara INTO TABLE lt_mara
WHERE mtart = p_mtart.
IF sy-subrc = 0.
SELECT * FROM marc INTO TABLE lt_marc
FOR ALL ENTRIES IN lt_mara
WHERE matnr = lt_mara-matnr
AND werks IN so_werks.
LOOP AT lt_marc INTO ls_marc.
READ TABLE lt_mara INTO ls_mara
WITH KEY matnr = ls_marc-matnr.
ls_out-sum = ls_mara-brgew + ls_mara-ntgew .
MOVE-CORRESPONDING ls_marc TO ls_out.
MOVE-CORRESPONDING ls_mara TO ls_out.
APPEND ls_out TO lt_out.
CLEAR ls_out.
ENDLOOP.
ELSE.
MESSAGE TEXT-e02 TYPE 'E' .
ENDIF.
CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
EXPORTING
i_program_name = sy-repid "e merr auto
i_internal_tabname = 'LS_OUT'
i_client_never_display = 'X'
i_inclname = sy-repid
CHANGING
ct_fieldcat = fcat[]
EXCEPTIONS
inconsistent_interface = 1
program_error = 2
OTHERS = 3.
READ TABLE fcat INDEX 6 ASSIGNING <fsym>.
<fsym>-outputlen = 15.
*-conditionally populate the color
LOOP AT LS_OUT.
IF LS_OUT-sum eq 21.
LS_OUT-color = 'C311'.
ENDIF.
MODIFY LS_OUT.
ENDLOOP.
layout-info_fieldname = 'COLOR'.
CALL FUNCTION 'REUSE_ALV_GRID_DISPLAY'
EXPORTING
i_callback_program = sy-repid
i_structure_name = 'LS_OUT'
it_fieldcat = fcat[]
TABLES
t_outtab = lt_out
EXCEPTIONS
program_error = 1
OTHERS = 2.
Adding the following parameter to your call to REUSE_ALV_GRID_DISPLAY should fix your problem
is_layout = layout
If you want to handle ANY /!\ formatting of a particular cell in ALV, here s the way.
FORM update_style using
x type lvc_fname " x is 'MATNR', 'VBELN', 'BELNR'.... whatever in your fieldcatalog
y type int4 " y = 1,2,3.....
update_mask type xstring
update_style type xstring.
READ table gt_alv into gs_alv index y.
tabstyle = gs_alv-tabstyle.
READ table tabstyle into s_style with key fieldname = x.
" check if fieldname already exists in gs_alv-tabstyle (insert or update)
s_style-style = s_style-style BIT-AND update_mask.
s_style-style = s_style-style BIT-OR update_style.
" Update or insert gs_alv-tabstyle from s_style
" update gt_alv from gs_alv
ENDFORM.
How to assign a color to a row in ALV
(But you cant have Cell background and Font Color at the same time !)
perform update_style USING x y 'FFFFFFE0' update_style.
update_style can be :
'00000000'. " Disable any color
'00000011'. " White Font
'00000017'. " Red fond
'00000013'. " gray font
'00000014'. " Yellow font
'00000015'. " blue font
'00000016'. " green font
'00000018'. " orange font
'00000011'. " black font
'0000000D'. " background dark blue
'0000000A'. " background fluo blue
'00000005'. " background blue
'00000003'. " background light blue
'00000000'. " background gray
'00000001'. " background light gray
'00000004'. " backgorund yellow
'0000000C'. " background dark yellow
'00000006'. " background green
'0000000E'. " background dark green
'00000010'. " background dark orange
'0000000F'. " background red
'00000007'. " background pink
'00000008'. " background orange
'00000001'. " background standard
If you want to handle editable, bold, italic, underline... properties
perform update_style USING x y 'FFFFFF9F' '00000020'. " set bold
perform update_style USING x y 'FFFFFF9F' '00000040'. " unset bold property
perform update_style USING x y 'FFFFF9FF' '00000200'. " Underline
perform update_style USING x y 'FFFFF9FF' '00000400'. " stop underlying
perform update_style USING x y 'FFFFF57F' '00000080' " italic
perform update_style USING x y 'FF9FFFFF' '00200000' " hotspot
perform update_style USING x y 'FF9FFFFF' '00400000' " no hotspot
perform update_style USING x y 'F9E7FFFF' '00080000' " Editable
perform update_style USING x y 'F9E7FFFF' '00100000' " Non Editable
For alignments, update_mask is 'DFFFF57F'
And update_style can be :
DATA alv_style_align_left_top(4) TYPE x VALUE '00000800'.
DATA alv_style_align_center_top(4) TYPE x VALUE '00001000'.
DATA alv_style_align_right_top(4) TYPE x VALUE '00001800'.
DATA alv_style_align_left_center(4) TYPE x VALUE '00002000'.
DATA alv_style_align_center_center(4) TYPE x VALUE '00002800'.
DATA alv_style_align_right_center(4) TYPE x VALUE '00003000'.
DATA alv_style_align_left_bottom(4) TYPE x VALUE '00003800'.
DATA alv_style_align_center_bottom(4) TYPE x VALUE '00004000'.
DATA alv_style_align_right_bottom(4) TYPE x VALUE '00004800'.
If you want to handle borders, you have to modify s_style-style2 with :
'FFFBFFFF' '00040000' " Remove top border
'FFF7FFFF' '00080000' " Remove bottom border
'FFFEFFFF' '00010000' " Remove left border
'FFFDFFFF' '00020000' " Remove right border
And after all of this (even after set_table_for_first_display)
you just need to call a form or method called REFRESH
Since I wrote an implentation of class CL_GUI_ALV_GRID I dont use FORMs but methods :
METHOD REFRESH.
DATA ls_stable TYPE lvc_s_stbl.
ls_stable-row = 'X'.
ls_stable-col = 'X'.
CALL METHOD me->refresh_table_display
EXPORTING
is_stable = ls_stable
EXCEPTIONS
finished = 1
OTHERS = 2.
ENDMETHOD.
x and y are not mandatory fields so I can loop over all lines to set a property to a column (y is unset)
ASSIGN mt_outtab->* TO <tab>.
CHECK <tab> IS ASSIGNED.
LOOP AT <tab> ASSIGNING <line>.
update_style( x sy-tabix update_mask update_style )
or loop over my fieldcatalog to set a property to a line (x is unset)
LOOP at me->get_frontend_fieldcatalog( ) in ls_fcat
update_style( ls_fcat-fieldname y update_mask update_style )
For more possibilities, check link
ABAP Alv_grid Merge cells and style formating of cells
LOOP AT Lt_OUT.
IF lt_out-sum >= 50000.
lt_out-color = 'C311'.
ENDIF.
MODIFY Lt_OUT.
ENDLOOP.
layout-info_fieldname = 'COLOR".
Related
I'm trying to fit text into a bounding box.
essentially the inputs for the function would be:
text -> string (list of words that are separated by ',')
width -> int (the set width of the box)
Example 1:
** If all of the text can fit inside the bounding box and in one line then keep it that way
Inputs:
"test, test2, test3", 100
It should output something like this:
Example 2:
** If the text cant fit into the box in one line it would make the box bigger (in height) and just keep writing a line down
Inputs:
"test, test2, test3, test4, test5", 100
It should output something like this:
Example 3:
** If the longest string in the list of strings that are separated by ',' cant fit into the box in one line it would make the text smaller
Inputs:
"wikipedia_is_long12345, test, test2, test3", 100
It should output something like this:
Another Example:
I would like some hints/help,
Thanks.
I've tried making it again and I did it!
Thats the code:
def fit_text(text, text_size, text_color, max_horizontal_chars, box_outline_color, box_background_color,
box_outline_width, font_file, word_bank_outline, high_res):
"""
:param text: The text that needs to be fir inside the bounding box (words separated by ,)
:type text: str
:param text_size: The size of the text
:type text_size: int
:param text_color: The color of the text
:type text_color: Tuple(int, int, int) | str
:param max_horizontal_chars: The number of the max horizontal chars allowed in one line
:type max_horizontal_chars: int
:param box_outline_color: The color of the outline of the bounding box
:type box_outline_color: Tuple(int, int, int) | str
:param box_background_color: The color of the background of the bounding box
:type box_background_color: Tuple(int, int, int) | str
:param box_outline_width: The thickness of the outline of the bounding box
:type box_outline_width: int
:param font_file: The file of the font of the text
:type font_file: str
:param word_bank_outline: Whether there will be an outline or not
:type word_bank_outline: bool
:return: The image
:rtype: Image
"""
multiplier = 2
text_size *= multiplier //2
# Replaces every ',' to '-' because the wrapper library will associate '-' as a separator and sorts by length
text = text.split(',')
text.sort(key=len, reverse=True)
text = ','.join(text).upper()
text = text.replace(',', '-')
# Changes the text size to fit the box if the longest word cannot fit in one line
font = ImageFont.truetype(font_file, text_size)
longest_word = sorted(text.split('-'), key=len)[-1]
if longest_word[0] == ' ':
longest_word = longest_word[1:]
longest_word_size = font.getsize(longest_word)[0]
while longest_word_size >= 1100:
longest_word = sorted(text.split('-'), key=len)[-1]
if longest_word[0] == ' ':
longest_word = longest_word[1:]
longest_word_size = font.getsize(longest_word)[0]
text_size -= 1
font = ImageFont.truetype(font_file, text_size)
# Initializes the text wrapper
wrapper = textwrap.TextWrapper()
wrapper.max_lines = 3
wrapper.placeholder = '...'
wrapper.break_long_words = False
wrapper.width = max_horizontal_chars
# Wrap the text
text = wrapper.fill(text=text)
# Create a new image according to the size of the text
img = Image.new('RGBA', (font.getsize_multiline(text)[0], (font.getsize_multiline(text)[1]+20+(60 if high_res else 20)+box_outline_width+(text.count('\n')*10))), (0,0,0,0))
# Initializes the ImageDraw.Draw for the img so I can draw on it
draw = ImageDraw.Draw(im=img)
# If word_bank_outline is true it will draw a bunch or rectangles according to box_outline_width
if word_bank_outline:
w, h = img.size
for i in range(0, box_outline_width):
shape = [(0 + i, 0 + i), (w - i, h - i)]
draw.rectangle(shape, box_background_color, box_outline_color)
# Replaces every '-' back to ','
text = text.replace('-', ',').upper()
# Checks if the first char is ' ' if it is it will be cut out
if text[0] == ' ':
text = text[1:]
# Draws the text onto the bounding box
draw.multiline_text(xy=(10 + (20 if high_res else 0), 0), text=text, font=font, fill=text_color, spacing=20)
return img
Essentially the main thing that made it happen is the function ttffont.getsize which returns the size of a string in pixels and so I could know the exact size of the string and I could fit it more accurately and another thing is the library textwraper which divided the text into separate lines.
So first it will check if the longest word in the string can fit inside the bounding box if not it will loop and decrement the text size until it fits
then it will call the textwrapper.fill which will divide the text and then it will just draw the text on the image and draw the outline and then it will return the image.
The end result:
"One unit of column width is equal to the width of one character in the Normal style. For proportional fonts, the width of the character 0 (zero) is used."
So ColumnWidth in Excel is measured as a number of "0" characters which fits in a column. How can this value be converted into pixels and vice versa?
As already mentioned ColumnWidth value in Excel depends on default font of a Workbook which can be obtained via Workbook.Styles("Normal").Font. Also it depends on current screen DPI.
After carrying out some research for different fonts and sizes in Excel 2013 I've found out that we have 2 linear functions (Arial cannot be seen because it overlaps with Tahoma.):
As it can be seen in the picture the function for ColumnWidth < 1 is different from the major part of the line chart. It's calculated as a number of pixels in a column / number of pixels needed to fit one "0" character in a column.
Now let's see what a typical cell width consists of.
A - "0" character width in the Normal Style
B - left and right padding
C - 1px right margin
A can be calculated with GetTextExtentPoint32 Windows API function, but font size should be a little bit bigger. By experiment I chose +0.3pt which worked for me for different fonts with 8-48pt base size. B is (A + 1) / 4 rounded to integer using "round half up". Also screen DPI will be needed here (see Python 3 implementation below)
Here are equations for character-pixel conversion and their implementation in Python 3:
import win32print, win32gui
from math import floor
def get_screen_dpi():
dc = win32gui.GetDC(0)
LOGPIXELSX, LOGPIXELSY = 88, 90
dpi = [win32print.GetDeviceCaps(dc, i) for i in (LOGPIXELSX,
LOGPIXELSY)]
win32gui.ReleaseDC(0, dc)
return dpi
def get_text_metrics(fontname, fontsize):
"Measures '0' char size for the specified font name and size in pt"
dc = win32gui.GetDC(0)
font = win32gui.LOGFONT()
font.lfFaceName = fontname
font.lfHeight = -fontsize * dpi[1] / 72
hfont = win32gui.CreateFontIndirect(font)
win32gui.SelectObject(dc, hfont)
metrics = win32gui.GetTextExtentPoint32(dc, "0")
win32gui.ReleaseDC(0, dc)
return metrics
def ch_px(v, unit="ch"):
"""
Convert between Excel character width and pixel width.
`unit` - unit to convert from: 'ch' (default) or 'px'
"""
rd = lambda x: floor(x + 0.5) # round half up
# pad = left cell padding + right cell padding + cell border(1)
pad = rd((z + 1) / 4) * 2 + 1
z_p = z + pad # space (px) for "0" character with padding
if unit == "ch":
return v * z_p if v < 1 else v * z + pad
else:
return v / z_p if v < z_p else (v - pad) / z
font = "Calibri", 11
dpi = get_screen_dpi()
z = get_text_metrics(font[0], font[1] + 0.3)[0] # "0" char width in px
px = ch_px(30, "ch")
ch = ch_px(px, "px")
print("Characters:", ch, "Pixels:", px, "for", font)
2022 and still the same Problem... Found threads going back to 2010 having the issue...
To start of: Pixel != Points
Points are defined as 72points/inch: https://learn.microsoft.com/en-us/office/vba/language/glossary/vbe-glossary#point
Though that definition seems stupid, as a shape with a fixed width of 100points, would display the exact same size in inch on every monitor independent of monitor configuration, which is not the case.
Characters is a unit that is defined to the number of 0 characters of the default text format. A cell set to a width of 10 characters, can fit 10 "0" characters, when the cell content is formatted to the default format.
My case is that I need to place pictures into the document and place text into cells next to it. But pictures hover over the document and cells are hidden below it. Depending on the size of the Picture, more or less cells are hidden. Thus, I can't just say I place text 5 cells to the left of the picture. Autosizing a column to the contents of the cells of the column, does not account for the hovering picture.
A picture is bound to the cell that is below the top left corner of the picture. I need to set the size of that cell to the size of the picture to solve the issue.
A Picture is a Shape. A Shape returns its width as Points (Shape.Width).
A Range can be set to a cell like Worksheet.Range["A1"]. From a Range you can get the width in Characters (Range.ColumnWidth) or in Points (Range.Width). But you can only set the width of a Range in Characters (Range.ColumnWidth).
So we can retrieve the size of the Picture (Shape) in Points and need to convert them to Characters to set the cell to the correct width...
Some research showed that the Points size of a cell contains a constant for spacing (padding before and after the cell content) and probably the seperator lines between cells.
On my system:
A cell set to a width of 1 **Characters** = 9 **Points**
A cell set to a width of 2 **Characters** = 14.25 **Points**
A cell set to a width of 3 **Characters** = 19.5 **Points**
As I said, there is a constant within the Points. Thus going from 1 Characters, to 2 Characters, the difference is only the size of the letter.
SizeOfLetter = 14.25 Points - 9 Points = 5.25 Points
we can then subtract that SizeOfLetter from the Points for 1 Characters and get the Points constant.
PointsConstant = 9 Points - 5.25 Points = 3.75 Points
Verify:
Points size for a cell containing 3 "0" letters = 3SizeOfLetter + PointsConstant = 35.25 Points + 3.75 Points = 19.5 Points
As the values depend on your system, YOU CAN'T USE THOSE VALUES!
Best way is to use code to calculate it for your system:
C# code:
Excel.Application excelApp = new Excel.Application();
Excel.Workbook workbook1 = excelApp.Workbooks.Add();
Excel.Worksheet sheet1 = (Excel.Worksheet)workbook1.ActiveSheet;
// Evaluate the Points data for the document
double previousColumnWidth = (double)sheet1.Range["A1"].ColumnWidth;
sheet1.Range["A1"].ColumnWidth = 1; // Make the cell fit 1 character
double points1 = (double)sheet1.Range["A1"].Width;
sheet1.Range["A1"].ColumnWidth = 2; // Make the cell fit 2 characters
double points2 = (double)sheet1.Range["A1"].Width;
double SizeOfLetter = points2 - points1;
double PointsConstant = points1 - pointsPerCharater;
// Reset the column width
sheet1.Range["A1"].ColumnWidth = previousColumnWidth;
// Create a function for the conversion
Func<double, double> PointsToCharacters = (double points) => (points - PointsConstant ) / SizeOfLetter ;
I am trying to generate titles automatically in Gnuplot. I have an array (titleprefix) and another set of variables (a1,a2,a3...), and the title is the prefix followed by the slope (the a's). I tried this:
title(n) = sprintf("word(titleprefix,n).sprintf(\" Slope = %.3f\",%f)",a."n"+0)
Of course that did not work. Error is:
Non-numeric string found where a numeric expression was expected.
Any idea how I solve this?
Eventually, I would like to plot several curves like this:
plot f1 w l t title(1), \
f2 w l t title(2), \
f3 w l t title(3)
One option to do such things is to build the complete set title command inside such a function and then eval that.
In your case it requires a bit of brain twisting to get the quotes correct, so that the variable names are correct:
titleprefix="first second third"
a1 = 1.111
a2 = 2.222
a3 = 3.333
title(n) = 'set title "'.word(titleprefix, n).': ".sprintf("Slope = %.3f", a'.n.')'
set multiplot layout 1,3
eval(title(1))
plot x
eval(title(2))
plot 2*x
eval(title(3))
plot 3*x
unset multiplot
Alternatively, you can construct a word list of the formatted variable values when you define or calculate the variables.
a1 = 1.111111
a2 = 2.222222
a3 = 3.333333
tmp = 'a = ""'
do for [i=1:3] { tmp = tmp.".sprintf('%.3f ', a".i.")" }
eval(tmp)
plot for [i=1:3] i*x title word(a, i)
I was messing around with debug and colors.js to get more colors than the limited 4-6 colors but I'm stuck at figuring out this coloring syntax
args[0] = ' \u001b[9' + c + 'm' + name + ' '
+ '\u001b[3' + c + 'm\u001b[90m'
+ args[0] + '\u001b[3' + c + 'm'
+ ' +' + exports.humanize(ms) + '\u001b[0m';
'blue' : ['\x1B[34m', '\x1B[39m'],
'cyan' : ['\x1B[36m', '\x1B[39m'],
'green' : ['\x1B[32m', '\x1B[39m'],
'magenta' : ['\x1B[35m', '\x1B[39m'],
'red' : ['\x1B[31m', '\x1B[39m'],
'yellow' : ['\x1B[33m', '\x1B[39m'],
I know windows console allows more colors than just those six, as color /? shows
0 = Black 8 = Gray
1 = Blue 9 = Light Blue
2 = Green A = Light Green
3 = Aqua B = Light Aqua
4 = Red C = Light Red
5 = Purple D = Light Purple
6 = Yellow E = Light Yellow
7 = White F = Bright White
How do I go about understanding this syntax and adding the extra colors that windows has to offer?
Those are ANSI terminal escape codes. Specifically, they're "select graphic rendition" (SGR) escape codes, which consist of:
the "command sequence introducer", consisting of the characters \x1B (ESC) and [,
one or more numeric commands, separated by semicolons, and
the letter m, ending the code and indicating that this is an SGR code.
There are many possible numeric commands (and many other escape codes besides SGR), but the most relevant ones are:
30–37: set text color to one of the colors 0 to 7,
40–47: set background color to one of the colors 0 to 7,
39: reset text color to default,
49: reset background color to default,
1: make text bold / bright (this is the standard way to access the bright color variants),
22: turn off bold / bright effect, and
0: reset all text properties (color, background, brightness, etc.) to their default values.
Thus, for example, one could select bright purple text on a green background (eww!) with the code \x1B[35;1;42m.
Hi this may seem to be a simple question but I am having a hard time understanding how to use the colors in a plot3d.
That is what I have:
// x, y and z are matrix 4 by 100
myColors = ones(1,size(z,2));
plot3d(x,y,list(z,myColors),alpha=0,theta=270);
I would like to have myColors related to the altitude of z.
Code
If I understand correct x, y and z are something like:
x = [ 1:100 ];
y = [ 1:4 ];
z = rand( length(x), length(y) ); //Some function resulting in (100 x 4) matrix
Then you can plot it using the following code.
plot3d( x, y, z, alpha=0, theta=270);
e = gce(); // Get current entity handle.
e.color_flag = 1; // Color according to z
f = gcf(); // Get current figure handle.
f.color_map = hotcolormap(512); // Make it a heat color map
Docs
According to the plot3d docs and surface_properties docs the color_flag can be used to:
color_flag: This field is used to specify the algorithm used to set facets' colors.
Note that the rules on color_mode, foreground and hiddencolor are
still applied to this case.
...
color_flag == 1
All facets are painted using one color index per facet proportional to
z. The minimum z value is painted using the index 1 color while the
maximum z value is painted using highest color index. The edges of the
facets can be additionaly drawn depending on the value of color_mode
(see above).
Resulting image