# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import pandas as pd
import plotly.graph_objects as go
import plotly.subplots as sp
import plotly.express as px
def readWriteAppend(data, name, rowA, rowB, group, colorIdx):
if rowA.empty or rowB.empty:
return
data['processName'].append(name)
data['threadName'].append(rowA['threadName'])
data['name'].append("ReadWrite")
data['begin'].append(rowA['begin'])
data['duration'].append(rowB['begin']-rowA['begin'])
data['fromName'].append(rowA['command'])
data['toName'].append(rowB['command'])
data['fromCounter'].append(rowA['counter'])
data['toCounter'].append(rowB['counter'])
data['group'].append(group)
data['color'].append(px.colors.qualitative.Plotly[colorIdx])
def recursiveAdd(data, dFromRowD, dfd, dfp, writeCmd, readCmd, trackName, counter):
dFromRow = dFromRowD.squeeze()
dToMask = (dfd['name']=="writeCommand") & (dfd['command']==writeCmd) & (dfd['begin']>dFromRow['begin'])
dToRowD = dfd.loc[ dToMask ].head(1)
dToRow = dToRowD.squeeze()
if dToRowD.empty: return
dfd.drop([dFromRowD.index[0], dToRowD.index[0]], errors='ignore', inplace=True)
pFromMask = (dfp['name']=="readCommand") & (dfp['command']==writeCmd) & (dfp['counter']==dToRow['counter'])
pFromRowD = dfp.loc[ pFromMask ].head(1)
pFromRow = pFromRowD.squeeze()
if pFromRowD.empty: return
pToMask = (dfp['name']=="writeCommand") & (dfp['command']==readCmd) & (dfp['begin']>pFromRow['begin'])
pToRowD = dfp.loc[ pToMask ].head(1)
pToRow = pToRowD.squeeze()
if pToRowD.empty: return
dfp.drop([pFromRowD.index[0], pToRowD.index[0]], errors='ignore', inplace=True)
name = trackName + " (" + str(counter) + ")"
readWriteAppend(data, name, dFromRow, dToRow, counter, 0)
readWriteAppend(data, name, pFromRow, pToRow, counter, counter+1)
dFromMask = (dfd['name']=="readCommand") & (dfd['command']==readCmd) & (dfd['counter']==pToRow['counter'])
dFromRowD = dfd.loc[ dFromMask ].head(1)
if not dFromRowD.empty:
recursiveAdd(data, dFromRowD, dfd, dfp, writeCmd, readCmd, trackName, counter)
def createDsToPuppetDataframe(dfdo, dfpo, writeCmd, readCmd, trackName):
if dfdo.empty or dfpo.empty:
return pd.DataFrame()
cmdMask = lambda df: (df['type'] == "I") & (df['category']=="Update") & ((df['command'] == writeCmd) | (df['command'] == readCmd))
dfd = dfdo.loc[cmdMask(dfdo) | (dfdo['category'] == "Initialize")].copy()
dfp = dfpo.loc[cmdMask(dfpo)].copy()
data = {
"processName": [],
"threadName": [],
"name": [],
"begin": [],
"duration": [],
"fromName": [],
"toName": [],
"fromCounter": [],
"toCounter": [],
"group": [],
"color": []
}
dFromMask = (dfd['category']=="Initialize") & (dfd['name']=="Initialize") & (dfd['command']=="Initialize")
dFromRowD = dfd.loc[ dFromMask ].head(1)
group = 0
nothingChanged = False
while not nothingChanged:
size = len(data['processName'])
recursiveAdd(data, dFromRowD, dfd, dfp, writeCmd, readCmd, trackName, group)
nothingChanged = size == len(data['processName'])
group = group + 1
df = pd.DataFrame(data)
return df
def createDsToPuppetTrace(df):
cd = list(zip(
df.fromName.tolist(),
df.toName.tolist(),
df.duration.tolist(),
df.fromCounter.tolist(),
df.toCounter.tolist(),
df.name.tolist()))
return go.Bar(
base = df.begin,
x = df.duration,
y = df.processName,
marker = dict(color=df.color),
insidetextanchor = 'middle',
orientation = 'h',
name = df.processName.iloc[0],
customdata = cd,
hovertemplate =
'%{customdata[5]}
' +
'Name From: %{customdata[0]} (%{customdata[3]})
' +
'Name To: %{customdata[1]} (%{customdata[4]})
' +
'Time From: %{base} To: %{x}
' +
'Duration: %{customdata[2]}',
)
def addDsToPuppetTrace(figure, pos, dfd, dfp, writeCmd, readCmd, trackName):
df = createDsToPuppetDataframe(dfd, dfp, writeCmd, readCmd, trackName)
if not df.empty:
figure.add_trace(createDsToPuppetTrace(df), row=pos, col=1)
def createTimeTrace(df):
cd = list(zip(df.command.tolist(), df.duration.tolist()))
return go.Bar(
base = df.begin,
x = df.duration,
y = df.processName,
text = df.name,
insidetextanchor = 'middle',
orientation = 'h',
name = df.processName[0],
customdata = cd,
hovertemplate =
'%{text}
' +
'Command: %{customdata[0]}
' +
'From: %{base: .1f} To: %{x: .1f}
' +
'Duration: %{customdata[1]: .1f}',
)
def addTimeTrace(figure, pos, df):
if not df.empty:
figure.add_trace(createTimeTrace(df), row=pos, col=1)
colors = {
'background': '#111111',
'text': '#7FDBFF',
}
style = dict(
plot_bgcolor=colors['background'],
paper_bgcolor=colors['background'],
font_color=colors['text']
)
axis = dict(
titlefont_size=16,
tickfont_size=14,
showgrid=False,
)
def create(dfs):
dfd = dfs['QmlDesigner']
dfe = dfs['Puppet']['Editor']
dfp = dfs['Puppet']['Preview']
dfr = dfs['Puppet']['Render']
figureCount = 2
titles = ["DS To Puppet", "Duration events"]
figure = sp.make_subplots(rows=figureCount, cols=1, subplot_titles=titles, vertical_spacing=0.3, shared_xaxes=True)
addDsToPuppetTrace(figure, 1, dfd, dfr, "ChangeAuxiliaryCommand", "PixmapChangedCommand", "ChangeAux to PixChanged")
addDsToPuppetTrace(figure, 1, dfd, dfp, "ChangeAuxiliaryCommand", "StatePreviewImageChangedCommand", "ChangeAux to PrevImgChanged")
addTimeTrace(figure, 2, dfe.loc[ dfe['type'] != 'I' ].reset_index())
addTimeTrace(figure, 2, dfp.loc[ dfp['type'] != 'I' ].reset_index())
addTimeTrace(figure, 2, dfr.loc[ dfr['type'] != 'I' ].reset_index())
addTimeTrace(figure, 2, dfd.loc[ dfd['type'] != 'I' ].reset_index())
figure.update_layout(barmode='stack', height=500, **style)
figure.update_xaxes(dict(**axis, title='Time (ms)', tickmode = 'auto', showticklabels=True, linecolor='green'))
figure.update_yaxes(dict(**axis, fixedrange=True, type='category'))
return figure