GNOME-Shell-Extensions开发经验(一)Hello,world!

最近想自己写个gnome-shell扩展找到了下面两个教程
GNOME 3.0 and 3.1 Shell Extensions
More GNOME Shell Customization
这是两篇2011年的 gnome3.0 和 3.1 的教程,相信很多开发 gnome-shell 扩展的朋友也看见过这两篇教程吧,我现在用的是3.18,我想差不了太多的,毕竟都是 gnome 3,我便照着这两个教程开发,没想到这就是我开发 gnome-shell 扩展噩梦的开始。。。。。

为什么我的扩展就运行不起来呢,只好又在 github 上看了好多扩展的代码,终于发现了

main( ) 函数不再是程序入口了!!!
main( ) 函数不再是程序入口了!!!
main( ) 函数不再是程序入口了!!!

于是只能按照模板提供的方式开发扩展,上面的教程的代码根本运行不起来,求我心理阴影面积。。。


好了,吐槽完了开始正式讲讲 gnome 现在开发的基本结构,其实变化也没用太大只是把以前的 main( ) 拆成了三个函数分别是 init( ),enable( ),disable( ),上面两篇教程还是很值得学习的,只要会 JavaScript 和 GObject 五分钟就能学会开发 gnome-shell 扩展

另外我的/usr/share/gnome-shell/里为什么死活找不到./js/ui这个文件夹呀,连./js都没有。。。。如果你也找不到请移步这里,找各个组件的名字属性什么的就全靠它了

首先创建一个 gnome-shell-extension 模板,输入如下命令

# gnome-shell-extension-tool -c

就会在$HOME/.local/share/gnome-shell/extensions下创建一个模板扩展,里面有三个文件

  • extension.js 这是 gnome-shell 扩展最核心的代码,init( ),enable( ),disable( ) 三个函数就在这里面
  • stylesheet.css 这时扩展中所使用的 css 样式
  • metadata.json 这是扩展的元信息

先上 extension.js 的代码

const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;

let text, button;

function _hideHello() {
     
    Main.uiGroup.remove_actor(text);
    text = null;
}

function _showHello() {
     
    if (!text) {
        text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" });
        Main.uiGroup.add_actor(text);
    }

    text.opacity = 255;

    let monitor = Main.layoutManager.primaryMonitor;

    text.set_position(monitor.x + Math.floor(monitor.width / 2 - text.width / 2),
                      monitor.y + Math.floor(monitor.height / 2 - text.height / 2));

    Tweener.addTween(text,
                     { opacity: 0,
                       time: 2,
                       transition: 'easeOutQuad',
                       onComplete: _hideHello });
}

function init() {
     
    button = new St.Bin({ style_class: 'panel-button',
                          reactive: true,
                          can_focus: true,
                          x_fill: true,
                          y_fill: false,
                          track_hover: true });
    let icon = new St.Icon({ icon_name: 'system-run-symbolic',
                             style_class: 'system-status-icon' });

    button.set_child(icon);
    button.connect('button-press-event', _showHello);
}

function enable() {
     
    Main.panel._rightBox.insert_child_at_index(button, 0);
}

function disable() {
     
    Main.panel._rightBox.remove_child(button);
}

扩展运行如图,每点一次右上角的齿轮图标就会闪现一下 Hello,world!


GNOME-Shell-Extensions开发经验(一)Hello,world!_第1张图片

分析上面的代码,可以看出整个扩展结构其实比一个main( ) 函数变得更清晰了,enable( ) 函数负责构造,init( ) 函数负责初始化,disable( ) 函数负责构析,而看了官方代码后发现,这并不是正确编写扩展的姿势,正确姿势应该是用 lang.class 实现,修改后的代码如下

const Lang = imports.lang;
const St = imports.gi.St;
const Main = imports.ui.main;
const Atk = imports.gi.Atk;
const PanelMenu = imports.ui.panelMenu;
const Tweener = imports.ui.tweener;

const extensionName = "hello,world!";

const extension = new Lang.Class({
    Name : extensionName,
    Extends : PanelMenu.Button,

    _init : function() {
     
        this.parent(null,extensionName);
        this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;


        this._text = null;
        this._icon = new St.Icon({ icon_name: 'system-run-symbolic',
                                 style_class: 'system-status-icon' });

        this.actor.add_child(this._icon);
        this.actor.connect('button-press-event', Lang.bind(this, this._showHello));
    },



    _hideHello : function () {
     
      Main.uiGroup.remove_actor(this._text);
      this._text = null;
    },

    _showHello : function () {
     
      if (!this._text) {
        this._text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" });
        Main.uiGroup.add_actor(this._text);
      }
      this._text.opacity = 255;

      let monitor = Main.layoutManager.primaryMonitor;

      this._text.set_position(monitor.x + Math.floor(monitor.width / 2 - this._text.width / 2),
                      monitor.y + Math.floor(monitor.height / 2 - this._text.height / 2));

      Tweener.addTween(this._text,
                     { opacity: 0,
                       time: 2,
                       transition: 'easeOutQuad',
                       onComplete: this._hideHello });
      }
});

let instance;

function init() {
     }

function enable() {
     
    instance = new extension();
    Main.panel.addToStatusArea(extensionName, instance);
}

function disable() {
     
    instance = null;
}

写完了就要开始调试扩展了,坑又来了,gnome 提供了一个 javascript 的解释器 gjs 但是 gnome 提供的东西不能调试自家的扩展,运行扩展会出现这样的错误

(gjs:7179): Gjs-WARNING **: JS ERROR: Error: Requiring St, version none: Typelib file for namespace 'St' (any version) not found
@/home/oxalics/.local/share/gnome-shell/extensions/hello_world@Oxalics/extension.js:3

# export UI_TYPELIB_PATH=/usr/lib/gnome-shell也不好使,数据只在 gnome 的进程中有效

于是只有Alt+F2输入 lg 可以进入 lookingGlass 里调试,如图


GNOME-Shell-Extensions开发经验(一)Hello,world!_第2张图片

可以看见 lookGlass 的界面还是很不错的。。。。可是

错误信息竟然不显示行号
错误信息竟然不显示行号
错误信息竟然不显示行号

多么反人类的设计啊,于是我有找到种调试方法,见这里。。。可是

GNOME 不崩溃没有错误信息
GNOME 不崩溃没有错误信息
GNOME 不崩溃没有错误信息

我现在很好奇 gnome 的工程师是怎么调试程序的。。。。我的经验就是不停地 try catch 不停的用 Gio 往终端输出信息,这样勉强能定位错误。

经过大概三天的折腾,在几乎官方资料为零(过期资料还不如零呢~~~),终于开发扩展走上正轨了,我把我踩过的雷都写在这里,这些资料足够让你顺利开始开发 gnome-shell 扩展了,也希望后人少走些弯路吧

你可能感兴趣的:(Linux,gnome,扩展,javascript,开发经验)