Changing a Chart Border Color in Google Apps Script - google-apps-script

I'm working in Google Apps Script, and I'm inserting a chart into my sheet through a script. I noticed when recording the creation of the chart with a macro, not all of the attributes of the chart get recorded.
For example, I record a macro and I set the background of the chart to be transparent (or any color really), then when I run that macro, the background comes back a standard white.
I am able to fix the background color by setting it in under .setOptions (see code below), but I can't seem to figure out how to change the color of a border (really I want to just get rid of it).
chart = sheet.getSheetByName("Sheet1").newChart()
.asLineChart()
.addRange(sheet.getSheetByName("ForGraphs").getRange(1, 1, 22, team_names.length + 1))
.setMergeStrategy(Charts.ChartMergeStrategy.MERGE_COLUMNS)
.setTransposeRowsAndColumns(false)
.setNumHeaders(-1)
.setHiddenDimensionStrategy(Charts.ChartHiddenDimensionStrategy.IGNORE_BOTH)
.setOption('backgroundColor.fill', "#0000ffff")
.setOption('bubble.stroke', '#000000')
.setOption('useFirstColumnAsDomain', true)
.setOption('focusTarget', 'category')
.setOption('curveType', 'none')
.setOption('legend.position', 'top')
.setOption('annotations.domain.textStyle.color', '#808080')
.setOption('textStyle.color', '#000000')
.setOption('legend.textStyle.color', '#1a1a1a')
.setOption('subtitleTextStyle.color', '#999999')
.setOption('titleTextStyle.color', '#757575')
.setOption('annotations.total.textStyle.color', '#808080')
.setOption('hAxis.slantedText', true)
.setOption('hAxis.slantedTextAngle', 0)
.setOption('hAxis.textStyle.color', '#000000')
.setOption('hAxis.titleTextStyle.color', '#000000')
.setOption('vAxes.0.minorGridlines.count', 5)
.setOption('vAxes.0.minorGridlines.color', '#f3f3f3')
.setOption('vAxes.0.textStyle.color', '#000000')
.setOption('vAxes.0.titleTextStyle.color', '#000000')
.setOption('height', 322)
.setOption('width', 659)
.setPosition(26, 5, 2, 16)
.build();
I've manually added the .setOption('backgroundColor', "#0000ffff") into this chunk.
I see in Google's official documentation that they say the backgroundColor.fill option can take an object as an input, but they neglect to say what they object will look like. I would assume it would be something with a .stroke and .strokeWeight, but that hasn't seemed to work.
Thanks so much!

It means you can set by either setOption('backgroundcolor', 'white') or setOption('backgroundcolor', {fill:'white'})
According to the document, not further option for background is supported
Line colors are set by setOptions('colors', ['blue', 'red'])
Chart area background color is set by chartArea.backgroundColor
It is stated in the document.

Related

How do I get a multiple-line subtitle in google sheet charts?

I am creating a bar chart in google sheet, recording it with a macro, and running the code for different data cases.
When the subtitle is too long, there is missing text on the chart, shown with ellipses (...)
Increasing the chart's width reveals more of the text but not all.
Increasing the chart's height does nothing! (It reveals a long title, but not a long subtitle!)
Adding a line break doesn't work. When using one, all I can see is the first line of the subtitle, while the others stay completely hidden...
How can I have a subtitle that shows all of the text I want to display?
Given that titles are responsive in both the horizontal and vertical axes, it's really odd for subtitles not to be.
Thank you
---- Edit ----
The script helps automate things, but I don't think that it adds new functionalities. That being said, the code I use is the following:
function Macro3() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
var chart = sheet.newChart()
.asBarChart()
.addRange(spreadsheet.getActiveRange())
.setMergeStrategy(Charts.ChartMergeStrategy.MERGE_COLUMNS)
.setTransposeRowsAndColumns(false)
.setNumHeaders(4)
.setHiddenDimensionStrategy(Charts.ChartHiddenDimensionStrategy.IGNORE_BOTH)
.setOption('bubble.stroke', '#000000')
.setOption('useFirstColumnAsDomain', true)
.setOption('isStacked', 'false')
.setOption('su', SpreadsheetApp.getActiveSheebtitlet().getRange("B2:B2").getValue())
.setOption('title', SpreadsheetApp.getActiveSheet().getRange("B1:B1").getValue())
.setOption('annotations.domain.textStyle.color', '#808080')
.setOption('textStyle.color', '#000000')
.setOption('legend.textStyle.color', '#1a1a1a')
.setOption('subtitleTextStyle.color', '#999999')
.setOption('titleTextStyle.color', '#757575')
.setOption('annotations.total.textStyle.color', '#808080')
.setXAxisTitle(SpreadsheetApp.getActiveSheet().getRange("B4:B4").getValue())
.setOption('hAxis.textStyle.color', '#000000')
.setYAxisTitle(SpreadsheetApp.getActiveSheet().getRange("A4:A4").getValue())
.setOption('vAxes.0.textStyle.color', '#000000')
.setPosition(2, 1, 30, 0)
.build();
sheet.insertChart(chart);
};
I wanted to include a screenshot of the Google sheet this macro is used upon, but this is my 1st post on stackoverflow and apparently I need at least 10 reputation to post images.
If you think it would help to share this screenshot and there is a neat way of doing it, please let me know.
Thanks again
In the current state it is not possible to add multiple lines to the subtitles of Google Sheets charts. Therefore I recommend you to go to Help > Help Sheets to Improve and add this request. Alternatively, you can use this template to request this functionality for Apps Script, for example, allowing EmbeddedCharts to have titles written with HTMLService.
Possible workarounds:
Change the font size according to the string length.
As I told you in the comments, you can measure the amount of words your subtitle has and according to that, apply different font sizes. For example:
function calcFontSize(subtitle){
const lenS = subtitle.split(" ").length
if(lenS > 12) return 8
if(len <= 12) return 12
}
// Inside your macro
.setOption(
'subtitleTextStyle.fontSize',
calcFontSize(sheet.getRange('B2:B2').getValue())
)
PROS : You have a "responsive" subtitle.
CONS: As you say In long texts ... The text becomes too small to read
Use Charts Service to create your chart
As this service allows you to add jump lines to your title, you can achieve what you want:
function createGoogleChart() {
// extracted from here https://developers.google.com/apps-script/reference/charts/charts
const data = Charts.newDataTable()
.addColumn(Charts.ColumnType.STRING, 'Month')
.addColumn(Charts.ColumnType.NUMBER, 'In Store')
.addColumn(Charts.ColumnType.NUMBER, 'Online')
.addRow(['January', 10, 1])
.addRow(['February', 12, 1])
.addRow(['March', 20, 2])
.addRow(['April', 25, 3])
.addRow(['May', 30, 4])
.build();
const chart = Charts.newAreaChart()
.setDataTable(data)
.setStacked()
.setRange(0, 40)
.setTitle("My title\nMy long long long long long \n long long long long \n subtitle")
.build();
SpreadsheetApp.getActiveSheet().insertImage(
chart.getAs('image/png'), 10, 10
)
}
PROS : You can achieve what you need.
CONS:
You insert a still image (not editable)
There is no default subtitle option
You have to build it from Apps Script, and adapt it to your macro

QTabWidget Access Actual Tab (not the content widget)

In this image:
I would like to access the actual tabs, rather than the content, so I can set a QPropertyAnimation on the actual tab when it is hovered on. I know how to get the hover event working, and I can get the tab index on the hover, I just can't access the actual tab when I hover on it. Is there a list of the tabs somewhere as an attribute of the QTabBar or the QTabWidget, or where can I find the tabs? Or do I have to subclass the addTab function to create the tabs individually?
Extra Info
Using PyQt5.14.1
Windows 10
Python 3.8.0
You cannot access "tabs", as they are not objects, but an abstract representation of the contents of the tab bar list.
The only way to customize their appearance is by subclassing QTabBar and overriding the paintEvent().
In order to add an over effect, you have to provide a unique animation for each tab, so you have to keep track of all tabs that are inserted or removed. The addTab, insertTab and removeTab methods are not valid options, since they are not used by QTabWidget. It uses instead tabInserted() and tabRemoved(), so those are to be overridden too.
This could be a problem with stylesheets, though, especially if you want to set fonts or margins.
Luckily, we can use the qproperty-* declaration with custom PyQt properties, and in the following example I'm using them for the tab colors.
class AnimatedTabBar(QtWidgets.QTabBar):
def __init__(self, *args):
super().__init__(*args)
palette = self.palette()
self._normalColor = palette.color(palette.Dark)
self._hoverColor = palette.color(palette.Mid)
self._selectedColor = palette.color(palette.Light)
self.animations = []
self.lastHoverTab = -1
#QtCore.pyqtProperty(QtGui.QColor)
def normalColor(self):
return self._normalColor
#normalColor.setter
def normalColor(self, color):
self._normalColor = color
for ani in self.animations:
ani.setEndValue(color)
#QtCore.pyqtProperty(QtGui.QColor)
def hoverColor(self):
return self._hoverColor
#hoverColor.setter
def hoverColor(self, color):
self._hoverColor = color
for ani in self.animations:
ani.setStartValue(color)
#QtCore.pyqtProperty(QtGui.QColor)
def selectedColor(self):
return self._selectedColor
#selectedColor.setter
def selectedColor(self, color):
self._selectedColor = color
self.update()
def tabInserted(self, index):
super().tabInserted(index)
ani = QtCore.QVariantAnimation()
ani.setStartValue(self.normalColor)
ani.setEndValue(self.hoverColor)
ani.setDuration(150)
ani.valueChanged.connect(self.update)
self.animations.insert(index, ani)
def tabRemoved(self, index):
super().tabRemoved(index)
ani = self.animations.pop(index)
ani.stop()
ani.deleteLater()
def event(self, event):
if event.type() == QtCore.QEvent.HoverMove:
tab = self.tabAt(event.pos())
if tab != self.lastHoverTab:
if self.lastHoverTab >= 0:
lastAni = self.animations[self.lastHoverTab]
lastAni.setDirection(lastAni.Backward)
lastAni.start()
if tab >= 0:
ani = self.animations[tab]
ani.setDirection(ani.Forward)
ani.start()
self.lastHoverTab = tab
elif event.type() == QtCore.QEvent.Leave:
if self.lastHoverTab >= 0:
lastAni = self.animations[self.lastHoverTab]
lastAni.setDirection(lastAni.Backward)
lastAni.start()
self.lastHoverTab = -1
return super().event(event)
def paintEvent(self, event):
selected = self.currentIndex()
qp = QtGui.QPainter(self)
qp.setRenderHints(qp.Antialiasing)
style = self.style()
fullTabRect = QtCore.QRect()
tabList = []
for i in range(self.count()):
tab = QtWidgets.QStyleOptionTab()
self.initStyleOption(tab, i)
tabRect = self.tabRect(i)
fullTabRect |= tabRect
if i == selected:
# make the selected tab slightly bigger, but ensure that it's
# still within the tab bar rectangle if it's the first or the last
tabRect.adjust(
-2 if i else 0, 0,
2 if i < self.count() - 1 else 0, 1)
pen = QtCore.Qt.lightGray
brush = self._selectedColor
else:
tabRect.adjust(1, 1, -1, 1)
pen = QtCore.Qt.NoPen
brush = self.animations[i].currentValue()
tabList.append((tab, tabRect, pen, brush))
# move the selected tab to the end, so that it can be painted "over"
if selected >= 0:
tabList.append(tabList.pop(selected))
# ensure that we don't paint over the tab base
margin = max(2, style.pixelMetric(style.PM_TabBarBaseHeight))
qp.setClipRect(fullTabRect.adjusted(0, 0, 0, -margin))
for tab, tabRect, pen, brush in tabList:
qp.setPen(pen)
qp.setBrush(brush)
qp.drawRoundedRect(tabRect, 4, 4)
style.drawControl(style.CE_TabBarTabLabel, tab, qp, self)
class Example(QtWidgets.QWidget):
def __init__(self):
super().__init__()
layout = QtWidgets.QVBoxLayout(self)
self.tabWidget = QtWidgets.QTabWidget()
layout.addWidget(self.tabWidget)
self.tabBar = AnimatedTabBar(self.tabWidget)
self.tabWidget.setTabBar(self.tabBar)
self.tabWidget.addTab(QtWidgets.QCalendarWidget(), 'tab 1')
self.tabWidget.addTab(QtWidgets.QTableWidget(4, 8), 'tab 2')
self.tabWidget.addTab(QtWidgets.QGroupBox('Group'), 'tab 3')
self.tabWidget.addTab(QtWidgets.QGroupBox('Group'), 'tab 4')
self.setStyleSheet('''
QTabBar {
qproperty-hoverColor: rgb(128, 150, 140);
qproperty-normalColor: rgb(150, 198, 170);
qproperty-selectedColor: lightgreen;
}
''')
Some final notes:
I only implemented the top tab bar orientation, if you want to use tabs in the other directions, you'll have change the margins and rectangle adjustments;
remember that using stylesheets will break the appearence of the arrow buttons;(when tabs go beyond the width of the tab bar), you'll need to set them carefully
painting of movable (draggable) tabs is broken;
right now I don't really know how to fix that;

How to display data series label in google sheets charts

I made a google Apps script to modify a chart and I would like to display the data label of the series number 0 but the line .setOption('series',{ 1:{color: '#2ecc71'}}) (where I change the color of the series 1) remove the data label of the series 0.
var Vmax =1.1*ss.getRangeByName("D285").getValue(); //get max and min here (before, it's equal to 0)
var Vmin =0.9*ss.getRangeByName("C285").getValue();
var sheet = SpreadsheetApp.getActiveSheet();
var chart = sheet.getCharts()[46];
chart = chart.modify()
.setChartType(Charts.ChartType.AREA)
.setOption('title',string)
.setOption('vAxes', {0: {textStyle: {fontSize: 10}, titleTextStyle: {fontSize : 8}, viewWindow: {min: Vmin, max:Vmax}}})
.setOption('series',{ 1:{color: '#2ecc71'}})
.setOption('titleTextStyle',{alignment:"center"})
.setOption('animation.startup',true)
.setOption('animation.duration', 5000)
.setOption('hAxis.slantedText',true)
.setPosition(290,6,0,0)
.build();
Logger.log(Vmax);
Logger.log(Vmin);
sheet.updateChart(chart);
This is what I have :
And this is what I want :
Your comments helped me solve this problem. I didn't know that the key word is annotations.
here is the code I used to update the dataLabel font and color
.setOption("series", {1 : {dataLabel: "value"}}) //this creates the data label
.setOption("series", {1: {annotations: {textStyle : {fontSize : 24, color : 'white'}}}}) //this updates the color and font size of the data label.
I run this through updateChart
I hope this helps

Is it possible to convert a Chart to and EmbeddedChart?

I'm attempting to Create a few charts out of a Sheet of data, but the charts are grabbing sort of specific data so I have found it advantageous to use the DataTableBuilder class. I am able to specify labels for the data more easily than I can from the original sheet. However, I cannot embed a Chart Class into a Sheet. Is it possible to either convert a Chart to an EmbeddedChart or use a DataTable to create an EmbeddedChart?I'm attempting to Create a few charts out of a Sheet of data, but the charts are grabbing sort of specific data so I have found it advantageous to use the DataTableBuilder class. I am able to specify labels for the data more easily than I can from the original sheet. However, I cannot embed a Chart Class into a Sheet. Is it possible to either convert a Chart to an EmbeddedChart or use a DataTable to create an EmbeddedChart?
This is the data below, and I need only the last column (5/11) and I don't need the total row. So its not a concise range, else I would just use the Embedded chart builder.
May 5/1 5/2 5/3 5/11
Critical 0 0 0 0
High 0 0 0 0
Call Immediate 4 11 4 3
Daytime Call 3 3 6 1
Totals 7 14 10 4
Below is the how I've built the Chart in which dailyTotals is a range of [0, 0, 3, 1]. This works fine, but I can't label anything.
var dailyChart = LOB.newChart()
.setChartType(Charts.ChartType.BAR)
.setOption('title', LOB.getName())
.addRange(dailyTotals)
.build();
LOB.insertChart(dailyChart);
Below is building the DataTable, this time daily totals is just an array. But this gives me labels.
dailyTable = Charts.newDataTable()
.addColumn(Charts.ColumnType.STRING, "Priority")
.addColumn(Charts.ColumnType.NUMBER, "Incidents")
.addRow('P1', dailyTotals[0])
.addRow('P2', dailyTotals[1])
.addRow('P3', dailyTotals[2])
.addRow('P4', dailyTotals[3])
.build();
How can I either use a DataTable to create an EmbeddedChart? or how can I turn a Chart into and Embedded chart?
I actually figured out a solution that worked for me before I got a response from anyone. It involves using a legend to determine which bar is which, rather than labels along the x-axis, but it totally covers my requirements and might help out some one else out.
var dailySeries = {
0:{color: 'blue', labelInLegend: 'P1'},
1:{color: 'red', labelInLegend: 'P2'},
2:{color: 'yellow', labelInLegend: 'P3'},
3:{color: 'green', labelInLegend: 'P4'}
}
var dailyChart = LOB.newChart()
.setPosition(8, 27, 0, 0)
.setChartType(Charts.ChartType.BAR)
.asColumnChart()
.setOption('title', LOB.getName())
.addRange(dailyTotals.getCell(1, 1))
.addRange(dailyTotals.getCell(2, 1))
.addRange(dailyTotals.getCell(3, 1))
.addRange(dailyTotals.getCell(4, 1))
.setOption('series', dailySeries)
.build();
LOB.insertChart(dailyChart);
You would need to re-create the below range somewhere else in the sheet and add that as range instead.
A B
P1 0
P2 0
P3 3
P4 1
The range [P1, P2, P3, P4] can also be somewhere else. Then you can add both ranges:
.addRange([P1 to P4 range])
.addRange(dailyTotals)
.setOption('useFirstColumnAsDomain','true')

Customising Google Bar Chart Using Google App Script

I am currently having some issues configuring a simple graph using Google App Scripts. I seem to be unable to find the correct documentation in order to progress any further!
I have everything hooked up pulling data from a couple of spreadsheets, so that aspect is fine!
I see that there are various ways in order to customise the looks of a chart and there are tools available for example:
http://imagecharteditor.appspot.com/
http://code.google.com/apis/ajax/playground/?type=visualization
I wish to add colours to my bar charts like in this example
http://code.google.com/apis/ajax/playground/?type=visualization#image_multicolor_bar_chart
Additionally in the first link there are options to create sections using the range marker tool. I was hoping that with these tools I could copy the code across to use in my App Script Chart.
The only way I can see this working is using .setOption(string, object)
I've tried this...
var data = Charts.newDataTable()
.addColumn(Charts.ColumnType.STRING, 'Month')
.addColumn(Charts.ColumnType.NUMBER, 'Mark Achieved')
for(var x=0; x < ChartData.length;x++){
data.addRow(ChartData[x]);
}
data.build();
var chart = Charts.newColumnChart()
.setDataTable(data)
.setDimensions(1000, 600)
.setRange(0, 100)
.setTitle('Test Scores')
.setLegendPosition(Charts.Position.BOTTOM)
.setOption('options',{cht: 'bvs', chco: 'A2C180,3D7930', max: 100})
.build();
app.add(chart);
any help would be much appreciated!
EDIT
The options you are trying to use are applicable to the static image charts (which are now deprecated), and won't work with ColumnCharts. ColumnCharts color the bars by series, not by data point, so if you want multi-colored bars, you have to separate them out into different data series. I wrote a hack that does this (see on jsfiddle for the standard javascript version). My reading of the AppsScript implementation of the Visualization API seems to preclude using calculated columns in the DataViews, but it is possible that the documentation is incomplete here. Try creating a view like this:
// add one calculated column for each month
var dataViewDefinition = Charts.newDataViewDefinition().setColumns([0, {
type: Charts.ColumnType.NUMBER,
label: 'Mark Achieved',
calc: function (dt, row) {
if (dt.getValue(row, 0) == 'January') ? dt.getValue(row, 1) : null;
}
}, {
type: Charts.ColumnType.NUMBER,
label: 'Mark Achieved',
calc: function (dt, row) {
if (dt.getValue(row, 0) == 'February') ? dt.getValue(row, 1) : null;
}
}/*...*/]);
It is probable that this needs to be tweaked, and possible that it won't work at all, in which case you would have to either change the query of the spreadsheet or rearrange the structure of the spreadsheet.
As far as adding the ranges to the chart, can you elaborate more on what you would like those to look like?