Skip to content

Commit 84464b2

Browse files
committed
introduced splits, delays and readme
1 parent 7c36399 commit 84464b2

7 files changed

+170
-37
lines changed

README.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Catmux
2+
3+
## DISCLAIMER - IN HEAVY DEVELOPMENT
4+
This package is currently in early development phase. It's interface might completely change and/or
5+
functionality might be moved around or even removed without notice. Use it at your own risks and
6+
report issues and problems :)
7+
8+
9+
## About
10+
Catmux is a little helper to run a tmux session with multiple windows and panes with just one
11+
command. It is inspired and functionally very similar to [tmuxinator](https://github.com/tmuxinator/tmuxinator), but without the project management feature which is in my opinion kind of an overhead.
12+
13+
Session configs can be stored anywhere and have to given as an argument.
14+
15+

etc/example_session.yaml

+40-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,49 @@
11
---
22
common:
33
before_commands:
4-
- fzirob change_environment
4+
- fzirob change_environment # before commands can be specified global and per window
5+
default_window: README # This window will be visible when first attaching to the session
56
windows:
67
- name: roscore
8+
splits:
9+
- commands:
10+
- roscore
11+
delay: 1 # Use this to wait before starting the next window
12+
- name: tiled
13+
layout: tiled
14+
splits:
15+
- commands:
16+
- echo "hello"
17+
- echo "world"
18+
- commands:
19+
- echo "1"
20+
- commands:
21+
- echo "2"
22+
- commands:
23+
- echo "3"
24+
- commands:
25+
- echo "4"
26+
- name: left-right
27+
layout: even-vertical
28+
splits:
29+
- commands:
30+
- echo "left"
31+
- commands:
32+
- echo "right"
33+
- name: top-bottom
34+
layout: even-horizontal
35+
splits:
36+
- commands:
37+
- echo "top"
38+
- commands:
39+
- echo "bottom"
40+
- name: README
41+
commands: # This looks a bit weird, but gives nicer colors
42+
- clear && tput bold && tput setaf 6 && roscat catmux readme_tmux.txt
43+
- tput sgr0
44+
- name: no_split
45+
# If just one split is desired, just skip it
746
commands:
847
- echo "hello"
9-
- name: spare
1048

1149

etc/readme_tmux.txt

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Hello there,
2+
3+
you just started the catmux example. This launched a tmux shell with a lot of open windows.
4+
If you know your way around tmux, have fun :)
5+
If you dont know tmux very well, have a look at this tutorial:
6+
In this tmux config mouse mode is enabled, so you should be able to scroll with the mouse and select splits and tabs with the mouse. The tabbar is at the bottom, btw.
7+
For your convenience, we defined a couple of key mappings:
8+
9+
ALT-<Arrow-keys> navigate around splits (Called panes in tmux)
10+
CTRL-<PageUp/PageDown> toggle through tabs (Called windows in tmux)
11+
CTRL-b, d detach from current session (this keeps everything running)
12+
(Press CTRL-b, release both and then press d)
13+
CTRL-b, SHIFT-k Kill the complete session

src/catmux/session.py

+14-17
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import yaml
2626
from window import Window
27+
import tmux_wrapper as tmux
2728

2829

2930
class Session(object):
@@ -52,7 +53,6 @@ def init_from_yaml(self, yaml_data):
5253
"""Initialize config directly by an already loaded yaml structure."""
5354

5455
self.__yaml_data = yaml_data
55-
5656
self._parse_common()
5757
self._parse_windows()
5858

@@ -62,10 +62,14 @@ def run(self):
6262
print('No windows to run found')
6363
return
6464

65+
first = True
6566
for window in self._windows:
66-
window.debug()
67-
window.create()
68-
window.run()
67+
window.create(first)
68+
first = False
69+
70+
if 'default_window' in self._common:
71+
tmux.tmux_call(['select-window', '-t', self._common['default_window']])
72+
6973

7074
def _parse_common(self):
7175
if self.__yaml_data is None:
@@ -82,19 +86,12 @@ def _parse_windows(self):
8286

8387
if 'windows' in self.__yaml_data:
8488
for window in self.__yaml_data['windows']:
85-
commands = list()
86-
before_commands = list()
89+
kwargs = dict()
8790
if 'before_commands' in self._common:
88-
before_commands = self._common['before_commands']
89-
90-
if 'commands' in window:
91-
commands = window['commands']
92-
if 'before_commands' in window:
93-
before_commands = window['before_commands']
94-
self._windows.append(
95-
Window(
96-
window['name'],
97-
commands=commands,
98-
before_commands=before_commands))
91+
kwargs['before_commands'] = self._common['before_commands']
92+
93+
kwargs.update(window)
94+
95+
self._windows.append(Window(**kwargs))
9996
else:
10097
print('No window section found in session config')

src/catmux/split.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# -- BEGIN LICENSE BLOCK ----------------------------------------------
2+
3+
# catmux
4+
# Copyright (C) 2018 Felix Mauch
5+
#
6+
# This program is free software: you can redistribute it and/or modify
7+
# it under the terms of the GNU General Public License as published by
8+
# the Free Software Foundation, either version 3 of the License, or
9+
# (at your option) any later version.
10+
#
11+
# This program is distributed in the hope that it will be useful,
12+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
# GNU General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
19+
# -- END LICENSE BLOCK ------------------------------------------------
20+
21+
"""A split in a tmux session"""
22+
from __future__ import print_function
23+
24+
import tmux_wrapper as tmux
25+
26+
27+
class Split(object):
28+
29+
"""A split is a pane where commands can be executed"""
30+
31+
def __init__(self, **kwargs):
32+
"""TODO: to be defined1. """
33+
34+
if kwargs is not None:
35+
for key, value in kwargs.iteritems():
36+
setattr(self, key, value)
37+
38+
def debug(self, name='', prefix=''):
39+
"""Prints all information about this window"""
40+
print(prefix + '- Split ' + name + ':')
41+
if hasattr(self, 'commands'):
42+
print(prefix + ' commands: ')
43+
print('\t- ' + '\n\t- '.join(getattr(self, 'commands')))
44+
45+
def run(self):
46+
"Executes all configured commands"""
47+
for command in getattr(self, 'commands'):
48+
tmux.send_keys(command)

src/catmux/tmux_wrapper.py

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ def send_keys(command):
2828

2929
tmux_call(['send-keys', command, 'C-m'])
3030

31+
def split():
32+
"""Splits the current pane into two"""
33+
tmux_call(['split-window'])
34+
3135
def tmux_call(command_list):
3236
"""Executes a tmux command """
3337
tmux_cmd = ['tmux'] + command_list

src/catmux/window.py

+36-18
Original file line numberDiff line numberDiff line change
@@ -21,44 +21,62 @@
2121
"""Contains the Window object"""
2222
from __future__ import print_function
2323

24+
import time
25+
2426
import tmux_wrapper as tmux
27+
from split import Split
2528

2629

2730
class Window(object):
2831

2932
"""Class to represent a tmux window structure"""
3033

31-
def __init__(self, name, **kwargs):
34+
def __init__(self, **kwargs):
3235
"""TODO: to be defined1. """
3336

34-
self._name = name
35-
self.before_commands = list()
36-
self.commands = list()
37+
split_list = kwargs.pop('splits', None)
38+
if not split_list and 'commands' in kwargs:
39+
split_dict = dict()
40+
split_dict['commands'] = kwargs.pop('commands')
41+
split_list = [split_dict]
42+
43+
self.splits = list()
44+
for split_data in split_list:
45+
self.splits.append(Split(**split_data))
3746

3847
if kwargs is not None:
3948
for key, value in kwargs.iteritems():
4049
setattr(self, key, value)
4150

51+
self.debug()
52+
4253

4354
def debug(self):
4455
"""Prints all information about this window"""
45-
print('\n----- {} -----'.format(self._name))
56+
print('\n----- {} -----'.format(getattr(self, 'name')))
4657
if hasattr(self, 'before_commands'):
4758
print('before_commands: ')
48-
print('\n\t- '.join(getattr(self, 'before_commands')))
49-
if hasattr(self, 'before_commands'):
50-
print('commands: ')
51-
print('\n\t- '.join(getattr(self, 'commands')))
59+
print('\t- ' + '\n\t- '.join(getattr(self, 'before_commands')))
60+
print('Splits:')
61+
for counter, split in enumerate(self.splits):
62+
split.debug(name=str(counter), prefix=' ')
5263

53-
def create(self):
64+
def create(self, first=False):
5465
"""Creates the window"""
55-
tmux.tmux_call(['new-window'])
56-
tmux.tmux_call(['rename-window', self._name])
57-
for command in getattr(self, 'before_commands'):
58-
tmux.send_keys(command)
66+
if not first:
67+
tmux.tmux_call(['new-window'])
68+
tmux.tmux_call(['rename-window', getattr(self, 'name')])
69+
for counter, split in enumerate(self.splits):
70+
if counter > 0:
71+
tmux.split()
72+
73+
if hasattr(self, 'before_commands'):
74+
for cmd in getattr(self, 'before_commands'):
75+
tmux.send_keys(cmd)
76+
split.run()
5977

60-
def run(self):
61-
"Executes all configured commands"""
62-
for cmd in getattr(self, 'commands'):
63-
tmux.send_keys(cmd)
78+
if hasattr(self, 'layout'):
79+
tmux.tmux_call(['select-layout', getattr(self, 'layout')])
6480

81+
if hasattr(self, 'delay'):
82+
time.sleep(getattr(self, 'delay'))

0 commit comments

Comments
 (0)