是否存在用于在特定视口和位置上打开应用程序窗口的脚本(或软件)?


8

因此,我在Unity(带有Compiz)中有8个虚拟桌面,因为我同时从事许多项目。

问题是,每次我需要重新启动或不小心关闭Chrome(这是我需要工作的很大一部分窗口)时,我都必须手动再次打开这些窗口,然后进行设置(打开文件,转到正确的位置网址等)。

您将如何编写一个脚本来为我做所有这一切?即:1)打开窗口2)在正确的虚拟屏幕上将它们置于正确的坐标中

(1)很明显,对于Google Chrome,您只需运行“ google-chrome”。但是,您如何将其放在正确的位置呢?(2)

还是已经存在一个脚本/软件可以为我做?


我可以尝试编写一个脚本,用于在启动时将所需的任何窗口放置在适当的桌面上,但是可能要花几天时间,因为下周将有决赛。它将涉及wmctrl,就像一个通过脚本或终端控制窗口的软件。但是对于重新启动窗口,这可能会带来更多挑战
Sergiy Kolodyazhnyy 2015年

尽管您特别询问过Unity,但值得注意的是KDE有一个名为kstart的程序(大多数未记录)可以为您完成此任务。它在KDE程序中效果最佳,但在其他程序中也取得了一些成功。

Answers:


14

可以做得很好,但是您需要对Unity /视口有一些了解。我希望下面的故事是可以理解的,如果不能理解,请发表评论。

如果您使用正确的参数运行以下脚本,则可以使用以下脚本在任何视口中的任何位置打开任何应用程序的窗口。剧本是经过编辑的版本这一个,但现在准备把窗户上的跨越虚拟桌面

1.了解视口和窗口坐标

Unity中的工作区

在Unity中,与其他窗口管理器不同,实际上您只有一个跨工作区,该工作区被划分为视口。在您的情况下,您的工作区分为八个视口。

窗户的位置如何定义

窗口位置,作为命令的输出:

wmctrl -lG
(you need to have wmctrl installed to run the command)

被描述为对于当前视口左上角的位置:


因此,如果您在视口上1
视口2上的窗口可以位于例如1700(x方向)x 500(y方向)上
(我的屏幕是1680x1050)

在此处输入图片说明


但是,如果您在视口6上:
同一窗口将位于20(x),-550(y)上 在此处输入图片说明


正确使用这些坐标对于使用正确的参数运行脚本非常重要,如下所述:

2.如何使用脚本

以下脚本可用于在虚拟(跨越)工作区上放置应用程序的新窗口。

  1. 确保wmctrl已安装:

    sudo apt-get install wmctrl
    
  2. 将以下脚本复制到一个空文件中,将其另存为setwindow(无扩展名)~/bin。创建目录(如果尚不存在)。使脚本可执行

  3. 如果您刚刚创建~/bin,请运行命令source ~/.profile或注销/登录以使目录在中可用$PATH
  4. 测试运行命令:

    setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size>
    

    例如

    setwindow gedit 100 100 200 200
    

    gedit窗口应显示在当前视口上。

笔记:

  • 请记住,并非所有应用程序都允许窗口大小低于特定宽度或高度。gedit我的系统上窗口的最小宽度为例如appr。470像素
  • 仅当整个窗口适合目标视口时,脚本才能正常工作,并相应地选择坐标/尺寸。另外请注意,Unity Launcher和面板使用的空格(!)可能会影响窗口的位置。
  • 使用负片<x_position>将窗口放置在当前视口的左侧
  • 使用负片<y_position>将窗口放置在当前视口上方
  • 要一次在不同视口上打开新窗口,只需链接命令即可。在“ Long story”示例中查看视口设置,如果我在视口1上,则可以使用以下命令在视口1、2、3和4上打开gedit窗口:

    setwindow gedit 100 100 200 200&&setwindow gedit 1780 100 200 200&&setwindow gedit 3460 100 200 200&&setwindow gedit 5140 100 200 200
    

剧本

#!/usr/bin/env python3
import subprocess
import time
import sys

app = sys.argv[1]

get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
subprocess.Popen(["/bin/bash", "-c", app])
# fix exception for Chrome (command = google-chrome-stable, but processname = chrome)
app = "chrome" if "chrome" in app else app
while t < 30:      
    ws2 = [w.split()[0:3] for w in get("wmctrl -lp").splitlines() if not w in ws1]
    procs = [[(p, w[0]) for p in get("ps -e ww").splitlines() \
              if app in p and w[2] in p] for w in ws2]
    if len(procs) > 0:
        w_id = procs[0][0][1]
        cmd1 = "wmctrl -ir "+w_id+" -b remove,maximized_horz"
        cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert"
        cmd3 = "wmctrl -ir "+procs[0][0][1]+" -e 0,"+sys.argv[2]+","+sys.argv[3]+","+sys.argv[4]+","+sys.argv[5]
        for cmd in [cmd1, cmd2, cmd3]:   
            subprocess.call(["/bin/bash", "-c", cmd])
        break
    time.sleep(0.5)
    t = t+1



编辑:懒惰的版本

如果您只想输入坐标和尺寸,就像在当前视口上打开一个窗口,然后将目标视口作为参数(无需计算任何内容),然后使用下面的版本...

如果像脚本的第一个版本一样进行设置,则可以使用以下命令运行它:

setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size> <targeted_viewport>

一个例子:打开一个Google-Chrome位于窗口20, 20,尺寸300x300,上视区5

setwindow google-chrome 20 20 300 300 5

设置与脚本的第一个版本几乎相同。
请注意,如果定义的窗口(位置/大小)完全适合目标视口,则此脚本也只能正常工作。

剧本:

#!/usr/bin/env python3
import subprocess
import time
import sys

app = sys.argv[1]
target_vp = int(sys.argv[6])

def get_res():
    # get resolution
    xr = subprocess.check_output(["xrandr"]).decode("utf-8").split()
    pos = xr.index("current")
    return [int(xr[pos+1]), int(xr[pos+3].replace(",", "") )]

res = get_res()

def current(set_vp):
    # get the current viewport
    vp_data = subprocess.check_output(
        ["wmctrl", "-d"]
        ).decode("utf-8").split()
    dt = [int(n) for n in vp_data[3].split("x")]
    cols = int(dt[0]/res[0])
    rows = int(dt[1]/res[1])    
    curr_vpdata = [int(n) for n in vp_data[5].split(",")]
    curr_col = int(curr_vpdata[0]/res[0])
    curr_row = int(curr_vpdata[1]/res[1])
    curr_vp = curr_col+curr_row*cols+1
    # calculate the vector to the origin from the current viewport (in resolution units)
    vec_curr = vector(curr_vp, cols)
    # calculate the vector to the origin from the targeted viewport
    vec_set = vector(set_vp, cols)
    # calculate the vector between current and targeted viewport
    vec_relative = [vec_set[0] - vec_curr[0],
                    vec_set[1] - vec_curr[1]]
    # calculate needed correction (absolute)
    relative = [vec_relative[0]*res[0],
                vec_relative[1]*res[1]]
    return relative

def vector(vp, cols):
    rem = vp%cols
    vec_x = rem-1 if rem != 0 else cols-1
    vec_y = int((vp-1)/cols)
    return [vec_x, vec_y]

res = get_res() # nieuw
get = lambda x: subprocess.check_output(["/bin/bash", "-c", x]).decode("utf-8")
ws1 = get("wmctrl -lp"); t = 0
# check for additional arguments to run the application
try:
    subprocess.Popen(["/bin/bash", "-c", app+" "+sys.argv[7]])  
except IndexError:
    subprocess.Popen(["/bin/bash", "-c", app])

# fix exception for Chrome (command = google-chrome-stable, but processname = chrome)
app = "chrome" if "chrome" in app else app
while t < 30:      
    ws2 = [w.split()[0:3] for w in get("wmctrl -lp").splitlines() if not w in ws1]
    procs = [[(p, w[0]) for p in get("ps -e ww").splitlines() \
              if app in p and w[2] in p] for w in ws2]
    if len(procs) > 0:
        w_id = procs[0][0][1]
        cmd1 = "wmctrl -ir "+w_id+" -b remove,maximized_horz"
        cmd2 = "wmctrl -ir "+w_id+" -b remove,maximized_vert"
        # calculate the correction, related to the current workspace, marge for launcher and panel
        pos_x = int(sys.argv[2]); pos_y = int(sys.argv[3]); x_marge = 65; y_marge = 35
        pos_x = pos_x if pos_x > x_marge else x_marge; pos_y = pos_y if pos_y > y_marge else y_marge
        x_relative = pos_x+current(target_vp)[0]
        y_relative = pos_y+current(target_vp)[1]
        # correct possible inaccurately set width / height
        x_size = res[0]; y_size = res[1]
        set_width = int(sys.argv[4]); set_height = int(sys.argv[5])
        width = set_width if set_width+x_marge+pos_x < x_size else x_size - pos_x - x_marge
        height = set_height if set_height+y_marge+pos_y < y_size else y_size - pos_y - y_marge
        cmd3 = "wmctrl -ir "+w_id+" -e 0,"+str(x_relative)+","+str(y_relative)+","+str(width)+","+str(height)
        for cmd in [cmd1, cmd2, cmd3]:   
            subprocess.call(["/bin/bash", "-c", cmd])
        break
    time.sleep(0.5)
    t = t+1


使用参数打开应用程序窗口

要完成工作,请完全回答您的问题:

如果您以如下方式运行脚本:

setwindow google-chrome 20 20 300 300 5

它将在目标桌面上打开一个默认窗口。
但是,使用最新版本的脚本,您可以添加其他参数来打开应用程序窗口,例如url

setwindow <application> <x_position> <y_position> <horizontal_size> <vertical_size> <targeted_viewport> <(optional)_argument>

例如:

setwindow google-chrome 0 0 600 600 3 "--new-window http://askubuntu.com"

如果(额外)参数包含空格,请使用引号。上面的示例将google-chrome在视口3上打开一个窗口,打开url http://askubuntu.com

您可以在一个命令中链接命令以在不同的工作区上打开多个窗口/ URL,例如:

setwindow google-chrome 0 0 600 600 8 "--new-window http://askubuntu.com"&&setwindow google-chrome 0 0 600 600 7 "--new-window www.google.com"

@snitko感谢您提出一个很好的问题,这是一个很好的挑战:)
Jacob Vlijm 2015年

我注意到当我使用脚本时,窗口会有一点偏移。因此,例如,如果我在坐标0 0处打开,则实际上它在右侧和底部的位置稍微多一点(偏移量约为10px)。没关系,但是问题实际上出在第二个脚本上:第二个脚本中的偏移量在横轴上奇怪地更大。我认为它约为〜50px。你知道为什么吗?在这种情况下,设置负坐标无济于事。
snitko

另一个问题:如何全屏打开窗口?
snitko

更新:第二个脚本的偏移量似乎与左侧统一坞的宽度相同(尽管已隐藏)。
snitko

对于那些感兴趣的人,我实现了Desktopen:github.com/snitko/desktopen
snitko 2016年

1

这通过略微修改的脚本扩展了@Jacob Vlijim的出色答案setwindow

#!/usr/bin/env python

import time
import argparse
import subprocess

DEFAULT_WIDTH = '1920'
DEFAULT_HEIGHT = '1080'


def get_window_list():
    window_list = subprocess.check_output(['/bin/bash', '-c', 'wmctrl -l'])
    parsed_list = []
    for line in window_list.splitlines():
        window_info = line.split()
        if window_info[1] != '-1':
            parsed_list.append(window_info[0])
    return parsed_list


def main(params):
    old_list = get_window_list()
    subprocess.Popen(['/bin/bash', '-c', params.command])

    def get_diff(old):
        new_list = get_window_list()
        return list(set(new_list) - set(old))

    diff = get_diff(old_list)
    x = 0
    while not diff:
        if x == 10:
            print 'window not found'
            return
        x += 1
        diff = get_diff(old_list)
        time.sleep(1)
    if len(diff) > 1:
        raise Exception(diff)
    window_id = diff[0]
    command_list = []
    command_list.append('wmctrl -ir %s -t %s' % (window_id, params.desktop))
    command_list.append('wmctrl -ir %s -b remove,maximized_horz,maximized_vert'
        % window_id)
    command_list.append('wmctrl -ir %s -e 0,%s,%s,%s,%s' %
        (window_id, params.x_pos, params.y_pos, params.width, params.height))
    for command in command_list:
        subprocess.call(['/bin/bash', '-c', command])

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('command', type=str)
    parser.add_argument('-d', '--desktop', default='0', type=str)
    parser.add_argument('-x', '--x-pos', default='0', type=str)
    parser.add_argument('-y', '--y-pos', default='0', type=str)
    parser.add_argument('-w', '--width', default=DEFAULT_WIDTH, type=str)
    parser.add_argument('-t', '--height', default=DEFAULT_HEIGHT, type=str)
    args = parser.parse_args()
    main(args)

更改说明:

  1. python3python(只是个人喜好)
  2. sys.argvargparse获得更好的命令行界面
  3. 严格的窗口ID(而非进程ID)窗口解析
    • 一些程序将单个进程ID用于多个窗口
  4. while 循环0.5秒至1完整的睡眠时间
  5. 更详细/可读的变量名称和pep8遵循性
  6. 屏幕尺寸的全局常量变量,而不是xrandr依赖

注意:这只是我为在Debian Jessie LXDE上个人使用而编写的稍微改进的版本。您的结果可能会有所不同。


0

对于那些感兴趣的人,我已经实现了Desktopen:github.com/snitko/desktopen

它允许您编写脚本以非常友好的方式在不同视口上打开窗口并进行显示。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.