Python Forum
what workspace is my terminal in?
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
what workspace is my terminal in?
#1
this script is intended for a Posix environment. it was developed and tested in Xubuntu 18.04 LTS with Python 3.6.8.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from os import getpid
from subprocess import PIPE,Popen

def cmdout(cmd):
    with Popen(cmd,stdout=PIPE,universal_newlines=True) as proc:
        f = proc.stdout
        out = f.read()
        f.close()
    return out

def getmyterminal(p):
    while p>1:
        ps = psmap[p]
        if any('terminal' in ps[x] for x in range(4,len(ps))):
            return ps
        p = ps[2]
    return None

def printancestry(p):
    while p>1:
        print(psrec[p])
        p = psmap[p][2]
    print(psrec[p])
    return

pid = getpid()

psout = cmdout(['ps','-ef'])
psmap = {}
plist = []
psrec = {}
for line in psout.splitlines():
    ps = line.split()
    try:
        ps[1] = int(ps[1])
        ps[2] = int(ps[2])
    except ValueError:
        continue
    psmap[ps[1]] = ps
    plist.append(ps[1])
    psrec[ps[1]] = line

#printancestry(pid)

termps = getmyterminal(pid)
termpid = termps[1]
termppid = termps[2]
assert termppid == 1

wmout = cmdout(['wmctrl','-lp'])
ws = None
for line in wmout.splitlines():
    wm = line.split()
    try:
        wm[1] = int(wm[1])
        wm[2] = int(wm[2])
    except ValueError:
        continue
    if wm[2] == termpid:
        ws = wm[1]

if isinstance(ws,int):
    print(str(ws))
it can also be downloaded from http://ipal.net/python/termws.py if you prefer that method. its sha256 checksum is c2c3c7d838dd7c1200a3482d0edb766a3b9f76c5358c022c6e8bdc6dd68cb537.

yeah, i know, line 10 is unnecessary since context management under with will close the pipe.
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#2
Interesting. I have a python program to tidy up my workspaces and windows but it is based on module ewmh instead of the wmctrl command. I'll make some tests with your program.

Currently I'm getting (on Kubuntu)
Error:
File "paillasse/whichwork.py", line 48, in <module> termpid = termps[1] TypeError: 'NoneType' object is not subscriptable
Reply
#3
i'm guessing you renamed the script to "paillasse/whichwork.py". that is extracting a part of termps which came from a function ("getmyterminal" at line 13) intending to find an ancestor process running a terminal program. maybe it didn't find any. maybe "terminal" is too much to check for under KDE. maybe it short be shortened to 'term' in line 16.

i don't have module "ewmh". is that from PyPi? i typically like to have things require only a minimal other installs or none at all. but, i really don't know if "wmctrl" comes in many distros or not.

renaming it to "whichwork.py" helps, actually, though i am unsure of your subdirectory name (clown). when it traces backwards through process command lines, searching for just "term" cold match the wrong process (i need to teach it to not start with itself). anyway, i made a new version i call "whichws.py":
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from os import getpid
from subprocess import PIPE,Popen
from sys import stderr

def cmdout(cmd):
    with Popen(cmd,stdout=PIPE,universal_newlines=True) as proc:
        return proc.stdout.read()

def getmyterminal(p):
    while p>1:
        ps = psmap[p]
        if any('term' in ps[x] for x in range(4,len(ps))):
            return ps
        p = ps[2]
    return None

def printancestry(p,**kwargs):
    while p>1:
        print(psrec[p],**kwargs)
        p = psmap[p][2]
    print(psrec[p])
    return

pid = getpid()

psout = cmdout(['ps','-ef'])
psmap = {}
plist = []
psrec = {}
for line in psout.splitlines():
    ps = line.split()
    try:
        ps[1] = int(ps[1])
        ps[2] = int(ps[2])
    except ValueError:
        continue
    psmap[ps[1]] = ps
    plist.append(ps[1])
    psrec[ps[1]] = line

termps = getmyterminal(pid)
if not termps:
    print('Terminal process not found',file=stderr,flush=True)
    printancestry(pid,file=stderr,flush=True)
    exit(3)

termpid = termps[1]
termppid = termps[2]
printancestry(pid,file=stderr,flush=True)
assert termppid == 1

wmout = cmdout(['wmctrl','-lp'])
ws = None
for line in wmout.splitlines():
    wm = line.split()
    try:
        wm[1] = int(wm[1])
        wm[2] = int(wm[2])
    except ValueError:
        continue
    if wm[2] == termpid:
        ws = wm[1]

if isinstance(ws,int):
    print(str(ws))
the new download URL is: http://ipal.net/python/whichws.py
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#4
i am more oriented to getting the output of commands as a data source due to much bash programming. i did that in Pike, too. i did it some even in C (i had string parsing down fairly solid in C). but making data structures in C made it feel like assembly since it was always function calls (i had my own mapping code, as a function using an AVL tree instead of a hash). my list code was a linked list implemented entirely as a big bunch of macros. at least it was very fast, even if it felt like coding assembly. i still feel the draw of assembly coding but i know its time is over.
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#5
The reason it doesn't work is that it doesn't find the word 'term' in the list of processes. Under KDE, my terminal application is named 'konsole'. If I use this word, it works BUT usually I'm using the tmux program inside the konsole and the konsole process doesn't appear on the list but the tmux process does, so it won't work with 'konsole' either.

There is a command line way to get the current workspace in the ubuntu forum, and it works for me:
Output:
wmctrl -d | grep -w '*'

Yes, ewmh is from pypi. I may post my program one day or the other after some refactoring. It moves all of my windows in workspaces and screens and positions predefined in a configuration file, based on the names of the windows.

As for the name of the directory, you're right, 'Paillasse' can be the name of a clown from the comedia dell'arte, I didn't even think about that. This word has different meanings in french, among which that of 'laboratory desk'. Every month I create a new directory for the month and a subdirectory named paillasse which is my laboratory desk. So if I receive a small python script and I want to play with it, I put it on this desk.
Reply
#6
so i will have it look for either 'erminal' or 'onsole'. new version:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from os import getpid
from subprocess import PIPE,Popen
from sys import stderr

def cmdout(cmd):
    with Popen(cmd,stdout=PIPE,universal_newlines=True) as proc:
        return proc.stdout.read()

def getmyterminal(p):
    while p>1:
        ps = psmap[p][2]
        for x in range(2,len(ps)):
            for y in ('ermina','onsole'):
                if y in ps[x]:
                    return ps
        p = ps[2]
    return None

def printancestry(p,**kwargs):
    while p>1:
        print(psrec[p],**kwargs)
        p = psmap[p][2]
    print(psrec[p])
    return

pid = getpid()

psout = cmdout(['ps','-ef'])
psmap = {}
plist = []
psrec = {}
for line in psout.splitlines():
    ps = line.split()
    try:
        ps[1] = int(ps[1])
        ps[2] = int(ps[2])
    except ValueError:
        continue
    psmap[ps[1]] = ps
    plist.append(ps[1])
    psrec[ps[1]] = line

termps = getmyterminal(pid)
if not termps:
    print('Terminal process not found',file=stderr,flush=True)
    printancestry(pid,file=stderr,flush=True)
    exit(3)

termpid = termps[1]
termppid = termps[2]
printancestry(pid,file=stderr,flush=True)
assert termppid == 1

wmout = cmdout(['wmctrl','-lp'])
ws = None
for line in wmout.splitlines():
    wm = line.split()
    try:
        wm[1] = int(wm[1])
        wm[2] = int(wm[2])
    except ValueError:
        continue
    if wm[2] == termpid:
        ws = wm[1]

if isinstance(ws,int):
    print(str(ws))
the same download URL has it http://ipal.net/python/whichws.py
the SHA256 is 250a107716a3f43900d0f08e4acfc260b69c99ebbc209d4dd05a911563ec1b18
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#7
another version to fix a one-off bug introduced by the previous bugfix:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from os import environ,getpid
from subprocess import PIPE,Popen
from sys import stderr

def cmdout(cmd):
    with Popen(cmd,stdout=PIPE,universal_newlines=True) as proc:
        return proc.stdout.read()

def getmyterminal(p):
    while p>1:
        ps = psmap[p]
        for x in range(3,len(ps)):
            for y in ('ermina','onsole'):
                if y in ps[x]:
                    return ps
        p = ps[2]
    return None

def printancestry(p,**kwargs):
    while p>1:
        print(psrec[p],**kwargs)
        p = psmap[p][2]
    print(psrec[p])
    return

pid = getpid()

psout = cmdout(['ps','-ef'])
psmap = {}
plist = []
psrec = {}
for line in psout.splitlines():
    ps = line.split()
    try:
        ps[1] = int(ps[1])
        ps[2] = int(ps[2])
    except ValueError:
        continue
    psmap[ps[1]] = ps
    plist.append(ps[1])
    psrec[ps[1]] = line

termps = getmyterminal(pid)
if not termps:
    print('Terminal process not found',file=stderr,flush=True)
    printancestry(pid,file=stderr,flush=True)
    exit(3)

termpid = termps[1]
termppid = termps[2]
assert termppid == 1

wmout = cmdout(['wmctrl','-lp'])
ws = None
for line in wmout.splitlines():
    wm = line.split()
    try:
        wm[1] = int(wm[1])
        wm[2] = int(wm[2])
    except ValueError:
        continue
    if wm[2] == termpid:
        ws = wm[1]

if isinstance(ws,int):
    print(str(ws))
original download URL has this update: http://ipal.net/python/whichws.py
new sha256 is dcbefd93877ae86ea9c4286268fd5061224ea7d94cd89ebf4545e7ef68dd4807
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#8
totally rewritten from scratch using more logical functions:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from os import environ,getpid
from subprocess import PIPE,Popen
from sys import argv

_termstrs = ('ermina','onsole')

_psmap = {}
_psrec = {}
_wmout = []

_lt  = (list,tuple)
_sbb = (str,bytes,bytearray)
_tv  = (TypeError,ValueError)


def cmdout(cmd):
    """Run a command line returning its output as a list of lines (strs)."""
    if not isinstance(cmd,_lt):
        raise TypeError('argument 1 (command) must be a list or tuple (of strings).')
    if not all(isinstance(x,_sbb)for x in cmd):
        raise TypeError('argument 1 (command) must be (a list or tuple of) strings.')
    with Popen(cmd,stdout=PIPE,universal_newlines=all(isinstance(x,str)for x in cmd)) as p:
        return p.stdout.read().splitlines()


def getwsdata():
    """Get window manager data as a list of output lines (list of strs)."""
    _wmout[:] = []
    for line in cmdout(['wmctrl','-lp']):
        wm = line.split()
        try:
            wm[1] = int(wm[1])
            wm[2] = int(wm[2])
        except ValueError:
            continue
        _wmout.append(wm)
    return _wmout


def getpsdata():
    """Get process data as a map by PID of lists of strs."""
    _psrec.clear()
    _psmap.clear()
    for line in cmdout(['ps','-ef']): 
        ps = line.split()
        try:
            ps[1] = int(ps[1])
            ps[2] = int(ps[2])
        except ValueError:
            continue
        _psmap[ps[1]] = ps
        _psrec[ps[1]] = line
    return


def getpidtermpid(p):
    """Get PID of terminal that given process is running under."""
    try:
        p = int(p)
    except _tv:
        return None
    getpsdata()
    while p>1:
        ps = _psmap[p]
        p = ps[2]
        for x in range(3,len(ps)):
            for y in _termstrs:
                if y in ps[x]:
                    return ps[1]
    return -1


def getpidws(p):
    """Get workspace number given PID is in -or- -1."""
    try:
        p = int(p)
    except _tv:
        return None
    getwsdata()
    for x in _wmout:
        if p==x[2]:
            return x[1]
    return None


def _main(args):
    p = getpid()
    if args[1:]:
        try:
            p = int(arg[1])
        except tv:
            pass
    t = getpidtermpid(p)
    if t is None:
        return 1
    w = getpidws(t)
    if w is not None:
        print(w)
    return 1


if __name__ == '__main__':
    try:
        result = _main(argv)
    except BrokenPipeError:
        result = 141
    except KeyboardInterrupt:
        print(flush=True)
        result = 98
    if result is None or result is True:
        result = 0
    elif result is False:
        result = 1
    exit(result)
download URL is still the same: http://ipal.net/python/whichws.py
new sha256 is: 6d7d6bf73b6e881460ec3e3b0edb09ecbccc91dc0abc2e9d79894ff950103d69
looks like i hit 3000 posts
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020