3
votes

I want to put an image inside a button in a tree view of a custom module in odoo 9.

I have 2 buttons in my_module.xml:

<?xml version="1.0"?>
<openerp>
    <data>
        <record id="view_my_module_tree" model="ir.ui.view">
            <field name="name">my.module.tree</field>
            <field name="model">my.module</field>
            <field name="type">tree</field>
            <field name="arch" type="xml">
                <tree string="my_module_tree" create="false" delete="false">
                    <button name="button_form" icon="cat" type="object"/>
                    <button name="button_form" icon="dog" type="object"/>                   
                </tree>
            </field>
        </record>  
    </data>
</openerp>

That works if the icons 'cat.png' and dog.png' are inside 'addons/web/static/src/img/icons/'. But I want to put this icons in my custom module.

I have created the 'my_module/static/src/img/icons/' hierarchy and put both images inside, but none of them appear in the buttons.

What I am doing wrong? Do I need to put something in openerp.py file?

Edit: I have tried this

<button name="button_form" type="object" icon="/my_module/static/src/img/icons/cat.png"/> 

But it doesn't work. If I see in the browser DevTools, I can check in the html code that:

<img src="http://localhost:8069/web/static/src/img/icons//my_module/static/src/img/icons/cat.png.png">

That has sense with the source code of the WidgetButton (it appends to '/web/static/src/img/icons/' your image path, and then adds '.png' at the end But of course, that doesn't solve the problem.

2

2 Answers

2
votes

As far as i know, you have to use a link to the icon directly as odoo already appends your url in front of the resulting img tag which would be localhost:8069 assuming you're running on your localhost with port 8069

<button name="print_report" context="{'open_purchase': True}" string="Print" type="object" icon="/my_module/static/src/img/icons/images.png" class="oe_highlight"/>

Here is a screenshot from my machine with an adobe pdf icon

enter image description here

If you read the source code it would become clear why it doesn't work. that file also defines all the available core templates of different views in odoo, so you can go back and reference it anytime you don't understand how a particular tag works.

var WidgetButton = common.FormWidget.extend({
    template: 'WidgetButton',
    init: function(field_manager, node) {
        node.attrs.type = node.attrs['data-button-type'];
        this._super(field_manager, node);
        this.force_disabled = false;
        this.string = (this.node.attrs.string || '').replace(/_/g, '');
        if (JSON.parse(this.node.attrs.default_focus || "0")) {
            // TODO fme: provide enter key binding to widgets
            this.view.default_focus_button = this;
        }
        if (this.node.attrs.icon) {
            // if the icon isn't a font-awesome one, find it in the icons folder
            this.fa_icon = this.node.attrs.icon.indexOf('fa-') === 0;
            if (!this.fa_icon && (! /\//.test(this.node.attrs.icon))) {
                this.node.attrs.icon = '/web/static/src/img/icons/' + this.node.attrs.icon + '.png';
            }
        }
},

the if statement tests to see if the icon is a font awesome icon, if it's not a font awesome icon and it doesn't contain any slashes ('(! /\//.test' tests for that), then it would use the path you provided else it would use the path '/web/static/src/img/icons/' + this.node.attrs.icon + '.png' and append .png to the end of the path, which means that only .png files in that folder can be used as icons

at the bottom of the file, you can see where the new widget WidgetButton is registered as a button tag in the view.

core.form_tag_registry.add('button', WidgetButton);

If you're interested in how button's are implemented and how their attributes are parsed to generate html, you can also read the source here

from line 1446 to 1457

1446<t t-name="WidgetButton">
1447     <button type="button" t-att-class="widget.is_stat_button ? 'oe_stat_button btn btn-default' : 'oe_button oe_form_button ' + (widget.node.att     rs.class ? widget.node.attrs.class : '')"
1448         t-att-style="widget.node.attrs.style"
1449         t-att-tabindex="widget.node.attrs.tabindex"
1450         t-att-autofocus="widget.node.attrs.autofocus"
1451         t-att-accesskey="widget.node.attrs.accesskey">
1452         <img t-if="!widget.is_stat_button and widget.node.attrs.icon " t-att-src="_s + widget.node.attrs.icon" width="16" height="16"/>
1453         <div t-if="widget.is_stat_button and widget.icon_class" t-att-class="widget.icon_class"></div>
1454         <span t-if="widget.string and !widget.is_stat_button"><t t-esc="widget.string"/></span>
1455         <div t-if="widget.string and widget.is_stat_button"><t t-esc="widget.string"/></div>
1456     </button>
1457 </t>

line 1452 tells us that if the widget is not a stat button (for counting) and it's icon property is also set, then an <img> tag would be generated whose src is '_s' + the value of the icon.

'_s' is later evaluated as your current url with the appropriate port depending on where you're running Odoo from, when a new session is being created, from the source code located here qweb has a dictionary called default_dict which it uses to store some parameters

so ._s is set to origin which is your current url when you initially started the odoo server

/**
     * Setup a session
     */
    session_bind: function(origin) {
        var self = this;
        this.setup(origin);
        qweb.default_dict._s = this.origin;
        this.uid = null;
        this.username = null;
        this.user_context= {};
        this.db = null;
        this.module_loaded = {};
        _(this.module_list).each(function (mod) {
            self.module_loaded[mod] = true;
        });
        this.active_id = null;
        return this.session_init();
    },
/**

EDIT: For anyone reading this, this solution doesn't work for icons in List views (Odoo's tree view). i've submitted a PR to Odoo 8 to fix this. (hopefully it would be merged and fowarded to Odoo 9)

2
votes

And finally, the solution.

It is an odoo bug. There are inconsistencies between custom button icons in Form views and List View buttons.

More information here: https://github.com/odoo/odoo/pull/13983

Until they fix the problem, there are 3 solutions available:

Solution 1: Sleazy hacks

Change this:

<button name="button_form" icon="cat" type="object"/>

for this:

<button name="button_form" icon="../../../../../my_module/static/src/img/icons/cat" type="object"/>

Solution 2: Icons in official icons odoo folder

Put the icons in the folder /web/static/src/img/icons

Solution 3: Fix the bug manually

Edit the file: addons/web/static/src/xml/base.xml

Change this:

<button t-name="ListView.row.button" type="button"
    t-att-title="widget.string" t-att-disabled="disabled || undefined"
    t-att-class="disabled ? 'oe_list_button_disabled' : ''"
    ><img t-attf-src="#{prefix}/web/static/src/img/icons/#{widget.icon}.png"
    t-att-alt="widget.string"/></button>

for this:

<button t-name="ListView.row.button" type="button"
    t-att-title="widget.string" t-att-disabled="disabled || undefined"
    t-att-class="disabled ? 'oe_list_button_disabled' : ''"
    ><t t-if="!widget.icon.indexOf('fa-') == 0"><t t-if="!/\//.test(widget.icon)"><img t-attf-src="#{prefix}/web/static/src/img/icons/#{widget.icon}.png" /></t></t>
    <t t-if="/\//.test(widget.icon)"><img t-attf-src="#{widget.icon}" /></t>
    <i t-if="widget.icon.indexOf('fa-') == 0" t-attf-class="fa #{widget.icon}" t-att-title="widget.string"/></button>