BuildBot: unable to inject extended event handler in master.cfg - continuous-integration

Buildbot version: 1.3.0
Twisted version: 18.4.0
my master.cfg
# -*- python -*-
# ex: set filetype=python:
from buildbot.plugins import *
from config import *
import os
from hooks import CustomHandler
# ip related imports
import socket
import fcntl
import struct
def get_ip_address(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915, # SIOCGIFADDR
struct.pack('256s', bytes(ifname[:15], 'utf-8'))
)[20:24])
#--------------------------------------------------------------
# CONSTANTS
localhost = get_ip_address(NETWORK_INTERFACE)
HOME = os.environ['HOME']
#--------------------------------------------------------------
c = BuildmasterConfig = {}
####### WORKERS
c['workers'] = [worker.Worker("example-worker", "pass")]
c['protocols'] = {'pb': {'port': 9989}}
c['www'] = dict(port=8010,plugins=dict(waterfall_view={}, console_view={}, grid_view={}))
####### CHANGESOURCES
c['www']['change_hook_dialects'] = { }
c['www']['change_hook_dialects']['bitbucketcloud'] = {
'class': CustomHandler
}
####### SCHEDULERS
c['schedulers'] = []
c['schedulers'].append(schedulers.SingleBranchScheduler(
name="schedulerQueryBuilder",
change_filter=util.ChangeFilter(repository_re='.*/querybuilder',
branch_re='.*/build_bot_testing',
category='push'),
treeStableTimer=None,
builderNames=["BuilderOfQueryBuilder"]
))
c['schedulers'].append(schedulers.ForceScheduler(
name="build",
builderNames=["BuilderOfQueryBuilder"]))
####### BUILDERS
BitBucketFactory = util.BuildFactory()
BitBucketFactory.addStep(steps.Git(
repourl='git#host:A/B.git',
branch='build_bot_testing',
retryFetch=True,
clobberOnFailure=True,
mode='incremental'))
BitBucketFactory.addStep(steps.PyLint(command=['pylint', '--py3k', '--ignore ln2sql',
'--disable C,import-error,import-error,too-few-public-methods,undefined-variable',
'swagger_server'],
haltOnFailure=True))
BitBucketFactory.addStep(steps.Test(command=["pytest", "swagger_server"],
haltOnFailure=False))
BitBucketFactory.addStep(steps.ShellSequence(commands=[
util.ShellArg(command=['zip', '-r', '/home/rajat/buildbot/worker/BuilderOfQueryBuilder/build.zip',
'/home/rajat/buildbot/worker/BuilderOfQueryBuilder/build'],
logfile='ShellSequenceLog', haltOnFailure=True),
util.ShellArg(command=['scp', '-i', '/home/rajat/.ssh/id_rsa.pub', '-rp',
'/home/rajat/buildbot/worker/BuilderOfQueryBuilder/build.zip',
'rajat#192.168.0.21:/home/rajat/buildbot/querybuilder/build.zip'],
logfile='ShellSequenceLog', haltOnFailure=True),
util.ShellArg(command=['ssh', '-i', '/home/rajat/.ssh/id_rsa.pub', 'rajat#192.168.0.21',
'rm','-rf', '/home/rajat/buildbot/querybuilder/build'],
logfile='ShellSequenceLog', flunkOnFailure=True),
util.ShellArg(command=['ssh', '-i', '/home/rajat/.ssh/id_rsa.pub', 'rajat#192.168.0.21',
'unzip','-o', '/home/rajat/buildbot/querybuilder/build.zip', '-d', '/home/rajat/buildbot/querybuilder/'],
logfile='ShellSequenceLog', haltOnFailure=True),
util.ShellArg(command=['ssh', '-i', '/home/rajat/.ssh/id_rsa.pub', 'rajat#192.168.0.21',
'/home/rajat/anaconda3/envs/querybuilder/bin/pip',
'install', '--upgrade', 'setuptools'],
logfile='ShellSequenceLog', haltOnFailure=True),
util.ShellArg(command=['ssh', '-i', '/home/rajat/.ssh/id_rsa.pub', 'rajat#192.168.0.21',
'/home/rajat/anaconda3/envs/querybuilder/bin/pip', 'install', '-r', '/home/rajat/buildbot/querybuilder/build/requirements.txt'],
logfile='ShellSequenceLog', haltOnFailure=True),
util.ShellArg(command=['ssh', '-f', '-i', '/home/rajat/.ssh/id_rsa.pub', 'rajat#192.168.0.21',
'cd', '/home/rajat/buildbot/querybuilder/build','&&','nohup','/home/rajat/anaconda3/envs/querybuilder/bin/python', '-m',
'swagger_server', '&>',
'/home/rajat/logs/log.txt', '&'],
logfile='ShellSequenceLog', haltOnFailure=True),
]))
# BitBucketFactory.workdir = 'customDirName'
# Default is build in path /worker/<BuilderName>/build/<fetched files>
c['builders'] = []
c['builders'].append(util.BuilderConfig(
name="BuilderOfQueryBuilder",
workernames=["example-worker"],
factory=BitBucketFactory,
properties = {'owner': [
'foo#leadics.com'
]
}))
####### BUILDBOT SERVICES
c['services'] = []
c['services'].append(reporters.MailNotifier(fromaddr="buildbot#foo.com",
sendToInterestedUsers=True,
#extraRecipients=["foo#bar.com"],
useTls=True, relayhost="smtp.gmail.com",
smtpPort=587, smtpUser="buildbot#company.com",
smtpPassword="password"))
####### PROJECT IDENTITY
c['title'] = "BitBucket_BuildBot"
c['titleURL'] = "https://buildbot.github.io/hello-world/"
c['buildbotURL'] = "http://"+localhost+":8010/"
####### DB URL
c['db'] = {
'db_url' : "sqlite:///state.sqlite",
}
hooks.py
from buildbot.www.hooks.bitbucketcloud import BitbucketCloudEventHandler
class CustomHandler(BitbucketCloudEventHandler):
def handle_repo_push(self, payload):
changes = []
#project = payload['repository']['name']
repo_url = payload['repository']['links']['self']['href']
web_url = payload['repository']['links']['html']['href']
for payload_change in payload['push']['changes']:
if payload_change['new']:
age = 'new'
category = 'push'
else: # when new is null the ref is deleted
age = 'old'
category = 'ref-deleted'
commit_hash = payload_change[age]['target']['hash']
if payload_change[age]['type'] == 'branch':
branch = GIT_BRANCH_REF.format(payload_change[age]['name'])
elif payload_change[age]['type'] == 'tag':
branch = GIT_TAG_REF.format(payload_change[age]['name'])
change = {
'revision': commit_hash,
'revlink': '{}/commits/{}'.format(web_url, commit_hash),
'repository': repo_url,
'author': '{} <{}>'.format(payload['actor']['display_name'],
payload['actor']['username']),
'comments': 'Bitbucket Cloud commit {}'.format(commit_hash),
'branch': branch,
#'project': project,
'category': category
}
if callable(self._codebase):
change['codebase'] = self._codebase(payload)
elif self._codebase is not None:
change['codebase'] = self._codebase
changes.append(change)
return (changes, payload['repository']['scm'])
but still it's pointing to the buildbot.www.hooks.bitbucketcloud 's BitbucketCloudEventHandler
getting following error
2018-07-16 17:02:05+0530 [_GenericHTTPChannelProtocol,0,127.0.0.1] adding changes from web hook
Traceback (most recent call last):
File "/home/rajat/anaconda3/envs/buildbot/lib/python3.6/site-packages/twisted/internet/defer.py", line 1532, in unwindGenerator
return _inlineCallbacks(None, gen, Deferred())
File "/home/rajat/anaconda3/envs/buildbot/lib/python3.6/site-packages/twisted/internet/defer.py", line 1386, in _inlineCallbacks
result = g.send(result)
File "/home/rajat/anaconda3/envs/buildbot/lib/python3.6/site-packages/buildbot/www/change_hook.py", line 107, in getAndSubmitChanges
changes, src = yield self.getChanges(request)
File "/home/rajat/anaconda3/envs/buildbot/lib/python3.6/site-packages/twisted/internet/defer.py", line 1532, in unwindGenerator
return _inlineCallbacks(None, gen, Deferred())
--- <exception caught here> ---
File "/home/rajat/anaconda3/envs/buildbot/lib/python3.6/site-packages/twisted/internet/defer.py", line 1386, in _inlineCallbacks
result = g.send(result)
File "/home/rajat/anaconda3/envs/buildbot/lib/python3.6/site-packages/buildbot/www/change_hook.py", line 168, in getChanges
changes, src = yield handler.getChanges(request)
File "/home/rajat/anaconda3/envs/buildbot/lib/python3.6/site-packages/buildbot/www/hooks/bitbucketcloud.py", line 165, in getChanges
return self.process(request)
File "/home/rajat/anaconda3/envs/buildbot/lib/python3.6/site-packages/buildbot/www/hooks/bitbucketcloud.py", line 56, in process
return handler(payload)
File "/home/rajat/anaconda3/envs/buildbot/lib/python3.6/site-packages/buildbot/www/hooks/bitbucketcloud.py", line 75, in handle_repo_push
project = payload['repository']['project']['name']
builtins.KeyError: 'project'
what am i doing wrong ?
i have to override the class because bitbucket has changed their hook json and project key is removed.
any help'll be appreciated.

Related

Frida: JVM access violation (JNI) lead to Stalker crash

I'm trying to stalk a minimal Java program that uses JNI with Frida, but I always run into access violation crashes when the JVM is shut down.
Furthermore, I only seem to ever be able to successfully stalk a JVM process (even if JIT is disabled with -Xint), if Stalker.trustThreshold is set to -1.
If I set the Stalker.trustThreshold to anything other than -1, the JVM does not crash, but the stalker does not cover any BBs from my custom JNI library native.dll (see below).
Here's my Java program:
// Main.java
public class Main {
static {
System.loadLibrary("native");
}
public static void main(String[] args) throws Exception {
Thread.sleep(20_000); // Enough time to attach to process...
new Main().sayHello();
}
private native void sayHello();
}
// Main.cpp
#include "Main.h"
#include <iostream>
#include <fstream>
#include <windows.h>
void sayHello() {
std::ofstream file;
file.open("output.tmp");
file << "Hello World\n";
file.close();
std::cout << "Current Thread id = " << GetCurrentThreadId() << "\n";
}
JNIEXPORT void JNICALL Java_Main_sayHello
(JNIEnv *, jobject) {
sayHello();
}
# Compile native library (Windows)
cl.exe /LD /EHsc /I "%JAVA_HOME%\include" /I "%JAVA_HOME%\include\win32" Main.cpp /link /DLL /DEBUG /OUT:native.dll
# Compile Java code
javac Main.java
# Run program
java -cp . -Djava.library.path=%cd% Main
My frida script looks as follows:
# frida-agent.py
import sys
import frida
def on_message(message, data):
print(str(message))
def main():
device = frida.get_local_device()
pid = -1
for proc in device.enumerate_processes():
if sys.argv[1] in [str(proc.pid), proc.name]:
pid = proc.pid
break
session = device.attach(pid)
print("Attaching to PID=" + str(pid))
script = session.create_script(
"""
Stalker.trustThreshold = -1;
const moduleMap = new ModuleMap();
moduleMap.update();
const createCoverageMap = (events) => {
moduleMap.update();
const coverageMap = {};
for (const event of events) {
const [start, _] = event;
const pStart = new NativePointer(start);
const module = moduleMap.find(pStart);
if (module) {
const offset = pStart.sub(module.base).toInt32();
if (!(module.path in coverageMap)) {
coverageMap[module.path] = 0;
}
coverageMap[module.path] += 1;
}
}
return coverageMap;
};
const stalkThread = (threadId) => {
Stalker.follow(threadId, {
events: {
call: false,
ret: false,
exec: false,
block: false,
compile: true,
},
onReceive: function (events) {
const bbEvents = Stalker.parse(events, {stringify: false, annotate: false});
send({coverage: createCoverageMap(bbEvents)});
}
});
}
const currentThreads = Process.enumerateThreads();
for (const thread of currentThreads) {
console.log("Stalking thread: " + thread.id);
stalkThread(thread.id);
}
"""
)
script.load()
script.on("message", on_message)
sys.stdin.read()
sys.exit(0)
if __name__ == "__main__":
main()
When attaching with python frida-agent.py 8232 the generated output is:
Current Thread id = 9632
#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00000287f70dae25, pid=8232, tid=0x00000000000025a0
#
# JRE version: OpenJDK Runtime Environment (8.0_292-b10) (build 1.8.0_292-b10)
# Java VM: OpenJDK 64-Bit Server VM (25.292-b10 mixed mode windows-amd64 compressed oops)
# Problematic frame:
# C 0x00000287f70dae25
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# C:\workspace\simple-jni-app\hs_err_pid8232.log
#
# If you would like to submit a bug report, please visit:
# https://github.com/AdoptOpenJDK/openjdk-support/issues
#
Do you have any idea on (1) how to prevent the JVM to crash and (2) set the Stalker.trustThreshold to anything other than -1 and still cover BBs from native.dll?
Help is very much appreciated! Thanks!

How to convert grpc.ServerInterceptor to grcp.aio.ServerInterceptor

I am trying to implement async ServerInterceptor [grcp.aio.ServerInterceptor]. My current synchronous ServerInterceptor looks like this https://github.com/zhyon404/python-grpc-prometheus/blob/master/python_grpc_prometheus/prometheus_server_interceptor.py#L48. When i try to use grpc.aio.ServerInterceptor and start the server,
My Server code
from grpc_opentracing import open_tracing_server_interceptor
from grpc_opentracing.grpcext import intercept_server
import PromServerInterceptor
class MyServicer():
async def _start_async_server(self, tracer=None,service, grpc_port=8083, http_port=8080):
tracing_interceptor = open_tracing_server_interceptor(tracer)
server = aio.server(nterceptors=(PromServerInterceptor(),))
server = intercept_server(server, tracing_interceptor)
my_service_pb2_grpc.add_MyServicer_to_server(service, server)
server.add_insecure_port("[::]:" + str(grpc_port))
await server.start()
logger.info("Started prometheus server at port %s", http_port)
prometheus_client.start_http_server(http_port)
await server.wait_for_termination()
def async_serve(self, tracer=None, service, grpc_port=8083, http_port=8080):
loop = asyncio.get_event_loop()
loop.create_task(self._start_async_server(service, tracer, grpc_port, http_port))
loop.run_forever()
Following are the lib versions i am using:
grpcio=1.32.0
grpcio-opentracing==1.1.4
I see the following error:
File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 646, in grpc._cython.cygrpc._handle_exceptions
File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 745, in _handle_rpc
File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 511, in _handle_unary_unary_rpc
File "src/python/grpcio/grpc/_cython/_cygrpc/aio/server.pyx.pxi", line 368, in _finish_handler_with_unary_response
File "prometheus_server_interceptor.py", line 93, in new_behavior
rsp = await behavior(request_or_iterator, service_context)
File "/anaconda3/lib/python3.7/site-packages/grpc_opentracing/grpcext/_interceptor.py", line 272, in adaptation
_UnaryServerInfo(self._method), handler)
File "/anaconda3/lib/python3.7/site-packages/grpc_opentracing/_server.py", line 145, in intercept_unary
timeout=servicer_context.time_remaining(),
AttributeError: 'grpc._cython.cygrpc._ServicerContext' object has no attribute 'time_remaining
Following is my PromServerInterceptor implemenation:
from grpc import aio
import grpc
from timeit import default_timer
from python_grpc_prometheus.server_metrics import (SERVER_HANDLED_LATENCY_SECONDS,
SERVER_HANDLED_COUNTER,
SERVER_STARTED_COUNTER,
SERVER_MSG_RECEIVED_TOTAL,
SERVER_MSG_SENT_TOTAL)
from python_grpc_prometheus.util import type_from_method
from python_grpc_prometheus.util import code_to_string
def _wrap_rpc_behavior(handler, fn):
if handler is None:
return None
if handler.request_streaming and handler.response_streaming:
behavior_fn = handler.stream_stream
handler_factory = grpc.stream_stream_rpc_method_handler
elif handler.request_streaming and not handler.response_streaming:
behavior_fn = handler.stream_unary
handler_factory = grpc.stream_unary_rpc_method_handler
elif not handler.request_streaming and handler.response_streaming:
behavior_fn = handler.unary_stream
handler_factory = grpc.unary_stream_rpc_method_handler
else:
behavior_fn = handler.unary_unary
handler_factory = grpc.unary_unary_rpc_method_handler
return handler_factory(fn(behavior_fn,
handler.request_streaming,
handler.response_streaming),
request_deserializer=handler.request_deserializer,
response_serializer=handler.response_serializer)
def split_call_details(handler_call_details, minimum_grpc_method_path_items=3):
parts = handler_call_details.method.split("/")
if len(parts) < minimum_grpc_method_path_items:
return '', '', False
grpc_service, grpc_method = parts[1:minimum_grpc_method_path_items]
return grpc_service, grpc_method, True
class PromServerInterceptor(aio.ServerInterceptor):
async def intercept_service(self, continuation, handler_call_details):
handler = await continuation(handler_call_details)
if handler is None:
return handler
# only support unary
if handler.request_streaming or handler.response_streaming:
return handler
grpc_service, grpc_method, ok = split_call_details(handler_call_details)
if not ok:
return continuation(handler_call_details)
grpc_type = type_from_method(handler.request_streaming, handler.response_streaming)
SERVER_STARTED_COUNTER.labels(
grpc_type=grpc_type,
grpc_service=grpc_service,
grpc_method=grpc_method).inc()
def latency_wrapper(behavior, request_streaming, response_streaming):
async def new_behavior(request_or_iterator, service_context):
start = default_timer()
SERVER_MSG_RECEIVED_TOTAL.labels(
grpc_type=grpc_type,
grpc_service=grpc_service,
grpc_method=grpc_method
).inc()
# default
code = code_to_string(grpc.StatusCode.UNKNOWN)
try:
rsp = await behavior(request_or_iterator, service_context)
if service_context._state.code is None:
code = code_to_string(grpc.StatusCode.OK)
else:
code = code_to_string(service_context._state.code)
SERVER_MSG_SENT_TOTAL.labels(
grpc_type=grpc_type,
grpc_service=grpc_service,
grpc_method=grpc_method
).inc()
return rsp
except grpc.RpcError as e:
if isinstance(e, grpc.Call):
code = code_to_string(e.code())
raise e
finally:
SERVER_HANDLED_COUNTER.labels(
grpc_type=grpc_type,
grpc_service=grpc_service,
grpc_method=grpc_method,
grpc_code=code
).inc()
SERVER_HANDLED_LATENCY_SECONDS.labels(
grpc_type=grpc_type,
grpc_service=grpc_service,
grpc_method=grpc_method).observe(max(default_timer() - start, 0))
return new_behavior
return _wrap_rpc_behavior(handler, latency_wrapper)
The interceptor from grpc.aio can look like:
class RequestIdInterceptor(grpc.aio.ServerInterceptor):
async def intercept_service(self, continuation, handler_call_details):
for (header, value) in handler_call_details.invocation_metadata:
if header == "request_id":
...
break
return await continuation(handler_call_details)
To convert it you need to add async for intercept_service and await for continuation.

Failed to find application object 'server' in 'app'

I am trying to serve a Dash app using Heroku. I have 5 files in my app:
.gitignore
venv
*.pyc
.DS_Store
.env
app.py
import os
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
# Read in the data
districts_change = pd.read_csv("https://github.com/thedatasleuth/New-York-Congressional-Districts/blob/master/districts_change.csv?raw=True")
df = districts_change.drop(['TOTAL'], axis=1)
# Get a list of all the districts
districts = districts_change['DISTRICT'].unique()
# Create the app
app = dash.Dash()
# Populate the layout with HTML and graph components
app.layout = html.Div([
html.H2("New York Congressional Districts"),
html.Div(
[
dcc.Dropdown(
id="DISTRICT",
options=[{
'label': 'District {}'.format(i),
'value': i
} for i in districts],
value='All Districts'),
],
style={'width': '25%',
'display': 'inline-block'}),
dcc.Graph(id='funnel-graph'),
])
# Add the callbacks to support the interactive componets
#app.callback(
dash.dependencies.Output('funnel-graph', 'figure'),
[dash.dependencies.Input('DISTRICT', 'value')])
def update_graph(Districts):
if Districts == "All Districts":
df_plot = df.copy()
else:
df_plot = df[df['DISTRICT'] == Districts]
trace1 = go.Bar(x=df_plot ['Year'], y=df_plot [('DEM')], name='DEM')
trace2 = go.Bar(x=df_plot ['Year'], y=df_plot [('REP')], name='REP')
trace3 = go.Bar(x=df_plot ['Year'], y=df_plot [('CON')], name='CON')
trace4 = go.Bar(x=df_plot ['Year'], y=df_plot [('WOR')], name='WOR')
trace5 = go.Bar(x=df_plot ['Year'], y=df_plot [('IND')], name='IND')
trace6 = go.Bar(x=df_plot ['Year'], y=df_plot [('GRE')], name='GRE')
trace7 = go.Bar(x=df_plot ['Year'], y=df_plot [('WEP')], name='WEP')
trace8 = go.Bar(x=df_plot ['Year'], y=df_plot [('REF')], name='REF')
trace9 = go.Bar(x=df_plot ['Year'], y=df_plot [('OTH')], name='OTH')
trace10 = go.Bar(x=df_plot ['Year'], y=df_plot [('BLANK')], name='BLANK')
return {
'data': [trace1, trace2, trace3, trace4, trace5,
trace6, trace7, trace8, trace9, trace10],
'layout':
go.Layout(
title='District {}'.format(Districts),
barmode='group')
}
if __name__ == '__main__':
app.server.run(debug=True)
Procfile
web: gunicorn app:server
requirements.txt
Flask==1.0.2
gunicorn==19.9.0
dash==0.26.5
dash-core-components==0.29.0
dash-html-components==0.12.0
dash-renderer==0.13.2
plotly==3.2.1
pandas==0.23.1
pandas-datareader==0.6.0
runtime.txt
python-3.6.6
I am able to build the app successfully in Heroku, however, I am unable to serve it properly, given this error message:
Failed to find application object 'server' in 'app'
What server is it talking about? Usually when I get this error I just add whatever is missing to the requirements.txt file.
I added: server = app.server right under app = dash.Dash()

PySide PyQt QDataWidgetMapper

I try to connect a QtGui.QPlainTextEdit to a Model with QDataWidgetMapper.
I dont get any errors, just nothing in the TextEdit.
I dont get it and i cant find good example Code.
Here is some ExampleCode.
I really hope that someone could help me.
from PySide import QtCore, QtGui
import sys
class ComponentsListModel(QtCore.QAbstractListModel):
def __init__(self, components=[], parent = None):
super(ComponentsListModel, self).__init__(parent=None)
self.components = components
self.list = parent
def rowCount(self, parent):
return len(self.components)
def data(self, index, role):
row = index.row()
if role == QtCore.Qt.DisplayRole:#index.isValid() and
value = self.components[row]
return value
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self._build_ui()
def _build_ui(self):
self.layout = QtGui.QVBoxLayout()
self.listView = QtGui.QListView()
self.model = ComponentsListModel(components = ['1', '2', '3'])
self.listView.setModel(self.model)
self.text = QtGui.QPlainTextEdit()
self.layout.addWidget(self.listView)
self.layout.addWidget(self.text)
self.setLayout(self.layout)
self._mapper = QtGui.QDataWidgetMapper(self)
self._mapper.setModel(self.model)
self._mapper.setSubmitPolicy(QtGui.QDataWidgetMapper.AutoSubmit)
self._mapper.addMapping(self.text, 0)
self._mapper.toFirst()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
app.setStyle('Plastique')
mySW = MainWindow()
mySW.show()
sys.exit(app.exec_())
You will need to add a condition for Qt.EditRole in your data function inside ComponentsListModel class
if role == Qt.EditRole:
value = self.components[row]
return value

tkinter.ttk.Treeview root node icon/image does not appear

Problem: I am not able to get an icon image to appear next to the root node in tkinter.ttk.Treeview. Below is a test code I used. It executed w/o errors but image did not appear on the left of root node. I have tried using the full path name of the image file but that did not work. Also, I have tried using PIL.ImageTk.PhotoImage to open the image file but that did not work either. Instead an error as shown below appeared.
Question: How do I get an icon image to appear on the left of the root node (or any node) of tkinter.ttk.Treeview?
Test Code:
import os
import tkinter as tk
import tkinter.ttk as ttk
from PIL import Image, ImageTk
class App(ttk.Frame):
def __init__(self, master, path):
ttk.Frame.__init__(self, master)
self.tree = ttk.Treeview(self)
ysb = ttk.Scrollbar(self, orient='vertical', command=self.tree.yview)
xsb = ttk.Scrollbar(self, orient='horizontal', command=self.tree.xview)
self.tree.configure(yscroll=ysb.set, xscroll=xsb.set)
self.tree.heading('#0', text='Directory', anchor='w')
abspath = os.path.abspath(path)
i = './icon/Home-icon_16.gif'
root_pic = tk.PhotoImage(file=i)
#root_pic = ImageTk.PhotoImage(i)
root_node = self.tree.insert('', 'end', text=abspath, open=True, image=root_pic)
l1_node = self.tree.insert(root_node, 'end', text='level 1', open=True)
l2_node = self.tree.insert(l1_node, 'end', text='level 2', open=True)
l3_node = self.tree.insert(l2_node, 'end', text='level 3', open=True)
l2a_node = self.tree.insert(l1_node, 'end', text='level 2a', open=True)
l3a_node = self.tree.insert(l2a_node, 'end', text='level 3a', open=True)
self.tree.grid(row=0, column=0)
ysb.grid(row=0, column=1, sticky='ns')
xsb.grid(row=1, column=0, sticky='ew')
self.grid()
root = tk.Tk()
path_to_my_project = os.getcwd()
app = App(root, path=path_to_my_project)
app.mainloop()
Error msg from using PIL.ImageTk.PhotoImage:
root_pic = ImageTk.PhotoImage(i)
File "/usr/lib/python3/dist-packages/PIL/ImageTk.py", line 108, in __init__
mode = Image.getmodebase(mode)
File "/usr/lib/python3/dist-packages/PIL/Image.py", line 296, in getmodebase
return ImageMode.getmode(mode).basemode
File "/usr/lib/python3/dist-packages/PIL/ImageMode.py", line 52, in getmode
return _modes[mode]
KeyError: './icon/Home-icon_16.gif'
Home-icon_16.gif:
Applications: python3.5 ver3.5.1-10; python3-tk ver3.5.1-1; tk8.6 ver8.6.5-1; python3-pil.imagetk:amd64 ver3.1.2-0ubuntu1
Try creating PIL image first with Image.open('file_path') and then do Photoimage. Also, you need to keep a reference to the PhotoImage or it wont show in tkinter.
import os
import tkinter as tk
import tkinter.ttk as ttk
from PIL import Image, ImageTk
class App(ttk.Frame):
def __init__(self, master, path):
ttk.Frame.__init__(self, master)
self.tree = ttk.Treeview(self)
ysb = ttk.Scrollbar(self, orient='vertical', command=self.tree.yview)
xsb = ttk.Scrollbar(self, orient='horizontal', command=self.tree.xview)
self.tree.configure(yscroll=ysb.set, xscroll=xsb.set)
self.tree.heading('#0', text='Directory', anchor='w')
abspath = os.path.abspath(path)
i = './icon/Home-icon_16.gif'
root_pic1 = Image.open(i) # Open the image like this first
self.root_pic2 = ImageTk.PhotoImage(root_pic1) # Then with PhotoImage. NOTE: self.root_pic2 = and not root_pic2 =
root_node = self.tree.insert('', 'end', text=abspath, open=True, image=self.root_pic2)
l1_node = self.tree.insert(root_node, 'end', text='level 1', open=True)
l2_node = self.tree.insert(l1_node, 'end', text='level 2', open=True)
l3_node = self.tree.insert(l2_node, 'end', text='level 3', open=True)
l2a_node = self.tree.insert(l1_node, 'end', text='level 2a', open=True)
l3a_node = self.tree.insert(l2a_node, 'end', text='level 3a', open=True)
self.tree.grid(row=0, column=0)
ysb.grid(row=0, column=1, sticky='ns')
xsb.grid(row=1, column=0, sticky='ew')
self.grid()
root = tk.Tk()
path_to_my_project = os.getcwd()
app = App(root, path=path_to_my_project)
app.mainloop()

Resources