How to rotate text in table cells? - python-docx

I'm trying to make table like this:
As you can see, the header is vertically orientated.
How can I achieve this using python-docx?
P.S. Sorry for non-translated table.

Snippet for those who are too tired to seek:
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.table import _Cell
def set_vertical_cell_direction(cell: _Cell, direction: str):
# direction: tbRl -- top to bottom, btLr -- bottom to top
assert direction in ("tbRl", "btLr")
tc = cell._tc
tcPr = tc.get_or_add_tcPr()
textDirection = OxmlElement('w:textDirection')
textDirection.set(qn('w:val'), direction) # btLr tbRl
tcPr.append(textDirection)

Related

How to round the borders of a card in a Dash app

I have the following code which gives me a scatter plot on a dbc.Card element within Dash. I'm trying to figure out what I need to do in order to make the corners of the card rounded.
Here's the code I have currently:
import dash
from dash import dcc
from dash import html
from dash import Input, Output
import dash_bootstrap_components as dbc
import plotly.express as px
import pandas as pd
# import the Caffeine dataset
caffeine = pd.read_csv('caffeine.csv')
# create scatter plot
scatter = px.scatter(caffeine,
x='Volume (ml)',
y='Caffeine (mg)',
color='type',
title='Volume x Caffeine',
height = 800,
hover_data=['drink'],
template='plotly_dark'
)
# layout of Dash app
pp.layout = dbc.Container([
dbc.Row(
dbc.Col(html.H1("Analysis of Caffeinated Drinks",
className='text-center mb-4',),
width=12
)
),
dbc.Row(
dbc.Col(
dbc.Card(
dbc.CardBody(
dcc.Graph(
figure=scatter
)
)
),
width={'size':10, 'offset':1}
)
)
I've tried using the answer on this stackoverflow question (How to round the corners of a card (body and image) in Bootstrap 4?) and as such I tried inserting a style={border-radius: '20px'} in a couple of different places but haven't had any luck producing a change.
Is there another method I could/should use, or else could someone please show me exactly what to do? I'm pretty new to Dash as well as html and css.
Many, many thanks in advance!
If you set the style in a CSS file, you need to use border-radius: 20px;, but if you set the style directly on the dbc.Card component, you have to set borderRadius=20.
I would suggest you add in your style.css a class called something like card-style and there, you will add all the CSS stuff
So, in your code, you will add the class in the className of the dbc.Card
app.layout = dbc.Container([
dbc.Row(
dbc.Col(html.H1("Analysis of Caffeinated Drinks",
className='text-center mb-4',),
width=12
)
),
dbc.Row(
[(dbc.Col(
dbc.Card(
dbc.CardBody(
html.Div(f"card {n}")
),
className="card-style"
),
width={'size':3, 'offset':1}
)) for n in range(8)]
)])
And in your assets/style.css you will add something like:
.card-style {
border: solid black 1px;
border-radius: 8px;
height: 120px;
}
It will help you to not repeat yourself by simply creating this kind of class for your elements.
You can check and test the HTML elements by pressing F12 and selecting what you're interested in;
The output of the code above is the above

calling a function which shows sub category list in a select box using observe function is working only first time

I have simple df have main category and its sub items. I made two select boxes to select main category and it automatically shows belonged items at the other select box using 'observe'
But it works only at the first time category selection and after I selected another category it didn't show sub items in an another select box.
If i put 'observe' inside the 'def' the function is worked as I wanted.
But this approach is troublesome if I have many other 'def' executing on observe on change.
I am also looking for the way to make this code simple without using global.
And also welcome any ideas to make this code simpler and lighter.
import pandas as pd
import ipywidgets as wg
from ipywidgets import *
from IPython.display import display, clear_output
df_list=[{'Category':'fruit','name':['banana','apple','mango']},\
{'Category':'animal','name':['lion','monkey','tiger','cat','dog']},\
{'Category':'body','name':['eyes','hands','ears','arms']},\
{'Category':'office','name':['table','computer','printer']}]
df=pd.DataFrame(df_list)
Category_box=wg.Select(options=list(df.Category),continuous_update=False,layout=Layout(width='30%'))
name_box=wg.Select(options=list(df[df['Category']==Category_box.value].name)[0],continuous_update=False,\
layout=Layout(width='30%'))
hbox=wg.HBox(children=[Category_box,name_box])
display(hbox)
def select_on_change(change):
if change['name'] == 'value':
global Category_box
global name_box
global hbox
Category_box=wg.Select(options=list(df.Category),continuous_update=False,layout=Layout(width='30%'),value=Category_box.value)
name_box=wg.Select(options=list(df[df['Category']==Category_box.value].name)[0],continuous_update=False,\
layout=Layout(width='30%'))
hbox=wg.HBox(children=[Category_box,name_box])
clear_output()
display(hbox)
#Category_box.observe(select_on_change)
Category_box.observe(select_on_change)
Firstly, thanks for the complete and clear example.
The solution is to not create new widgets in your function that you are observing. Instead just update the .options of your name_box widget. This way, no globals required.
import pandas as pd
import ipywidgets as wg
from ipywidgets import *
from IPython.display import display, clear_output
df_list=[{'Category':'fruit','name':['banana','apple','mango']},\
{'Category':'animal','name':['lion','monkey','tiger','cat','dog']},\
{'Category':'body','name':['eyes','hands','ears','arms']},\
{'Category':'office','name':['table','computer','printer']}]
df=pd.DataFrame(df_list)
Category_box=wg.Select(options=list(df.Category),continuous_update=False,layout=Layout(width='30%'))
name_box=wg.Select(options=list(df[df['Category']==Category_box.value].name)[0],continuous_update=False,\
layout=Layout(width='30%'))
hbox=wg.HBox(children=[Category_box,name_box])
display(hbox)
def select_on_change(change):
if change['name'] == 'value':
name_box.options = list(df[df['Category']==Category_box.value].name)[0]
Category_box.observe(select_on_change)

Conditional bipolar inside table bar chart - pandas - css

for my html representation of a pandas dataframe (python), I'd like to create something that resembles as much as possible the image below (created with Excel), that is, given a sequence of numbers, create INSIDE A TABLE, some horizontal bar charts that become green if the value is greater than zero, red if they are below zero and with the "zero" point in the axis which dynamically rescales according to the data provided. the closest I got to was by using pandas native instruments is given by the code below (http://pandas.pydata.org/pandas-docs/stable/style.html)
#done in Jupyter
from pandas import DataFrame
df = pd.DataFrame([-0.02, 0.03, 0.04, -0.05], columns=['A'])
more_than_zero = df.loc[df.loc[:,'A'] >= 0].index.values.tolist()
less_than_zero = df.loc[df.loc[:,'A'] < 0].index.values.tolist()
df.style.bar(subset=pd.IndexSlice[more_than_zero,'A'], color='#d65f5f')
#df.style.bar(subset=pd.IndexSlice[less_than_zero, 'B'], color='#7fff00')
Update: This answer lead to a pull request pandas-dev/pandas#14757 that got accepted in Pandas v0.20.0. The documentation can be found on Pandas website at style.html#Bar-charts
There's no way to do this out of the box using pandas right now (I'll try to implement that soon), but for now here's a monkey-patching solution:
def _bar_center_zero(self, s, color_positive, color_negative, width):
# Either the min or the max should reach the edge (50%, centered on zero)
m = max(abs(s.min()),abs(s.max()))
normed = s * 50 * width / (100 * m)
base = 'width: 10em; height: 80%;'
attrs_neg = (base+ 'background: linear-gradient(90deg, transparent 0%, transparent {w}%, {c} {w}%, '
'{c} 50%, transparent 50%)')
attrs_pos = (base+ 'background: linear-gradient(90deg, transparent 0%, transparent 50%, {c} 50%, {c} {w}%, '
'transparent {w}%)')
return [attrs_pos.format(c=color_positive, w=(50+x)) if x > 0
else attrs_neg.format(c=color_negative, w=(50+x))
for x in normed]
def bar_excel(self, subset=None, axis=0, color_positive='#5FBA7D',
color_negative='#d65f5f', width=100):
"""
Color the background ``color`` proptional to the values in each column.
Excludes non-numeric data by default.
.. versionadded:: 0.17.1
Parameters
----------
subset: IndexSlice, default None
a valid slice for ``data`` to limit the style application to
axis: int
color_positive: str
color_negative: str
width: float
A number between 0 or 100. The largest value will cover ``width``
percent of the cell's width
Returns
-------
self : Styler
"""
#subset = _maybe_numeric_slice(self.data, subset)
#subset = _non_reducing_slice(subset)
self.apply(self._bar_center_zero, axis=axis, subset=subset,
color_positive=color_positive, color_negative=color_negative,
width=width)
return self
Monkey-patch this to the Styler class:
pd.formats.style.Styler._bar_center_zero = _bar_center_zero
pd.formats.style.Styler.bar_excel = bar_excel
Now you can use it:
df = pd.DataFrame([-0.02, 0.03, 0.04, -0.05], columns=['A'])
df.style.bar_excel(color_positive='#5FBA7D', color_negative='#d65f5f')
Update:
I since then created a pull request on GitHub where I implemented this as well as the option of centering 'dynamically' (align='mid').
The resulting bar you can get by specifying options align and depending on the type of your data are represented below:

How to left align widget label in iPython 3?

How can I left align, rather than right align, the label in an iPython 3 widget, such as IntSlider? My ultimate goal is to left align a set of labeled widgets. This entails left aligning their labels, since the label is the leftmost element of each widget.
I've read the discussion in
Aligning TextBox Widgets in IPython Notebooks, but (a) it focuses on making more space for a right-aligned label, and (b) the proposed solution does not seem to affect the label width. (As an aside, I would be interested in finding cell-executable code that can reset the minimum label width.)
I've also read the discussion in Change the size of the label in an IPython notebook widget, but it doesn't seem to offer a simple solution.
Thanks for your help.
Addendum (2015-06-02):
Looks like widget.interactive() does not play nicely with the solution suggested by Jakob. Example:
from IPython.html import widgets
from IPython.display import display
def mySlider(text='', twidth=100, min=0, max=10, value=5):
c1 = widgets.HBox()
ints = widgets.IntSlider(min=min, max=max, value=value)
text = widgets.HTML(text, width=twidth)
c1.children = (text, ints)
return c1
s1 = mySlider('Test')
s2 = mySlider('TestTest')
s3 = mySlider('TestTestTest')
def process(a, b, c):
print([a, b, c])
widgets.interactive(
process,
a=s1.children[1].value,
b=s2.children[1].value,
c=s3.children[1].value
)
yields slider labels a, b, c with the usual alignment.
You can simply combine an IntSlider with an Html widget to create your custom widget like
from IPython.html import widgets
from IPython.display import display
def mySlider(text='', twidth=100):
c1 = widgets.HBox()
ints = widgets.IntSlider()
text = widgets.HTML(text, width=twidth)
c1.children = (text, ints)
return c1
Using this method, some widgets could look like
s1 = mySlider('Test')
s2 = mySlider('TestTest')
s3 = mySlider('TestTestTest')
display(s1,s2,s3)
Update to work with interact
To use these custom widgets with interact it is necessary to add some properties and callbacks. The interact method requires the widget.description and the widget.value arguments to setup the interactive widgets. As our container widget does not have these arguments, they are added manually. Moreover it is necessary to link the container.value with the IntSlider.value. This is once realized by a simple assignment, and more important via the on_trait_change method.
Finally, the interact methods calls the process function on widget.on_trait_change callbacks, thus the container.on_trait_change method is replaced by the IntSlider.on_trait_change call.
The updated code looks like:
def mySlider2(text='', twidth=100, min=0, max=10, value=5):
c1 = widgets.HBox()
ints = widgets.IntSlider(min=min, max=max, value=value)
text = widgets.HTML(text, width=twidth)
c1.children = (text, ints)
c1.description = text
c1.value = ints.value
def update(name, value):
c1.value = value
ints.on_trait_change(update,'value')
c1.on_trait_change = ints.on_trait_change
return c1
s1a = mySlider2('Test')
s2a = mySlider2('TestTest')
s3a = mySlider2('TestTestTest')
widgets.interactive(
process,
a=s1a,
b=s2a,
c=s3a
)

Can anyone help me understand this code (HTML table parsing in lxml, python)?

Background: I need to write an html table parser in python for HTML tables with varying colspans and rowspans. Upon some research I stumbled about this gem. It works well for simple cases without wacky colspans and rowspans, however I've run into a bug. The code assumes that if an element has a colspan of 3, it belongs to three different table headers, while it really only belongs to the table header the colspan falls in the center of. An example of this can be seen at http://en.wiktionary.org/wiki/han#Swedish (open up the declension table under the Swedish section). The code incorrectly returns that "hans" (possessive-neuter-3rd person masculine) belongs to possessive-common-3rd person masculine and possessive-plural-3rd person masculine because it has a colspan of 3. I've tried adding a check to table_to_2d_dict which would create a counter if a colspan > 1, and only count the element as a part of a header if the counter was equal to the the colspan // 2 + 1 (this returns the median of the range(1,colspan+1) which is the value of the table header which the element should be counted as). However, when I implement this check in the location specified in the code below, it doesn't work. To be honest this probably stems from my lack of understanding how this code works, so...
Question: Can someone explain what this code does and why it malfunctions as described above? If someone can implement a fix that'd be great but right now I'm primarily concerned with understanding the code. Thanks
Below is the code with comments that I've added to highlight parts of the code I understand and parts I don't.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from collections import defaultdict
def table_to_list(table):
dct = table_to_2d_dict(table)
return list(iter_2d_dict(dct))
def table_to_2d_dict(table):
result = defaultdict(lambda : defaultdict(str))
for row_i, row in enumerate(table.xpath('./tr')): #these double for loops iterate over each element in the table
for col_i, col in enumerate(row.xpath('./td|./th')):
colspan = int(col.get('colspan', 1)) #gets colspan attr of the element, if none assumes it's 1
rowspan = int(col.get('rowspan', 1)) #gets rowspan attr of the element, if none assumes it's 1
col_data = col.text_content() #gets raw text inside element
#WHAT DOES THIS DO? :(
while row_i in result and col_i in result[row_i]:
col_i += 1
for i in range(row_i, row_i + rowspan):
for j in range(col_i, col_i + colspan):
result[i][j] = col_data
return result
#what does this do? :(
def iter_2d_dict(dct):
for i, row in sorted(dct.items()):
cols = []
for j, col in sorted(row.items()):
cols.append(col)
yield cols
if __name__ == '__main__':
import lxml.html
from pprint import pprint
doc = lxml.html.parse('tables.html')
for table_el in doc.xpath('//table'):
table = table_to_list(table_el)
pprint(table)