File: command_cue.py

package info (click to toggle)
linux-show-player 0.5.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,896 kB
  • sloc: python: 12,408; sh: 154; makefile: 17; xml: 8
file content (171 lines) | stat: -rw-r--r-- 6,118 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# -*- coding: utf-8 -*-
#
# This file is part of Linux Show Player
#
# Copyright 2012-2016 Francesco Ceruti <ceppofrancy@gmail.com>
#
# Linux Show Player is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Linux Show Player is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Linux Show Player.  If not, see <http://www.gnu.org/licenses/>.

import subprocess

from PyQt5.QtCore import Qt, QT_TRANSLATE_NOOP
from PyQt5.QtWidgets import QVBoxLayout, QGroupBox, QLineEdit, QCheckBox

from lisp.core.decorators import async_function
from lisp.core.has_properties import Property
from lisp.cues.cue import Cue, CueState, CueAction
from lisp.ui.settings.cue_settings import CueSettingsRegistry
from lisp.ui.settings.settings_page import SettingsPage
from lisp.ui.ui_utils import translate


class CommandCue(Cue):
    """Cue able to execute system commands.

    Implemented using :class:`subprocess.Popen` with *shell=True*
    """

    Name = QT_TRANSLATE_NOOP('CueName', 'Command Cue')
    CueActions = (CueAction.Default, CueAction.Start, CueAction.Stop)

    command = Property(default='')
    no_output = Property(default=True)
    no_error = Property(default=True)
    kill = Property(default=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = translate('CueName', self.Name)

        self.__process = None
        self.__stopped = False

    def __start__(self, fade=False):
        self.__exec_command()
        return True

    @async_function
    def __exec_command(self):
        if not self.command.strip():
            return

        # If no_output is True, discard all the outputs
        std = subprocess.DEVNULL if self.no_output else None
        # Execute the command
        self.__process = subprocess.Popen(self.command, shell=True,
                                          stdout=std, stderr=std)
        rcode = self.__process.wait()

        if rcode == 0 or rcode == -9 or self.no_error:
            # If terminate normally, killed or in no-error mode
            if not self.__stopped:
                self._ended()

            self.__process = None
            self.__stopped = False
        elif not self.no_error:
            # If an error occurs and not in no-error mode
            self._error(
                translate('CommandCue', 'Process ended with an error status.'),
                translate('CommandCue', 'Exit code: ') + str(rcode))

    def __stop__(self, fade=False):
        if self.__process is not None:
            self.__stopped = True
            if self.kill:
                self.__process.kill()
            else:
                self.__process.terminate()

        return True

    __interrupt__ = __stop__


class CommandCueSettings(SettingsPage):
    Name = QT_TRANSLATE_NOOP('SettingsPageName', 'Command')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setLayout(QVBoxLayout())
        self.layout().setAlignment(Qt.AlignTop)

        self.group = QGroupBox(self)
        self.group.setLayout(QVBoxLayout(self.group))
        self.layout().addWidget(self.group)

        self.commandLineEdit = QLineEdit(self.group)
        self.group.layout().addWidget(self.commandLineEdit)

        self.noOutputCheckBox = QCheckBox(self)
        self.layout().addWidget(self.noOutputCheckBox)

        self.noErrorCheckBox = QCheckBox(self)
        self.layout().addWidget(self.noErrorCheckBox)

        self.killCheckBox = QCheckBox(self)
        self.layout().addWidget(self.killCheckBox)

        self.retranslateUi()

    def retranslateUi(self):
        self.group.setTitle(translate('CommandCue', 'Command'))
        self.commandLineEdit.setPlaceholderText(
            translate('CommandCue', 'Command to execute, as in a shell'))
        self.noOutputCheckBox.setText(
            translate('CommandCue', 'Discard command output'))
        self.noErrorCheckBox.setText(
            translate('CommandCue', 'Ignore command errors'))
        self.killCheckBox.setText(
            translate('CommandCue', 'Kill instead of terminate'))

    def enable_check(self, enabled):
        self.group.setCheckable(enabled)
        self.group.setChecked(False)

        self.noOutputCheckBox.setTristate(enabled)
        if enabled:
            self.noOutputCheckBox.setCheckState(Qt.PartiallyChecked)

        self.noErrorCheckBox.setTristate(enabled)
        if enabled:
            self.killCheckBox.setCheckState(Qt.PartiallyChecked)

        self.killCheckBox.setTristate(enabled)
        if enabled:
            self.killCheckBox.setCheckState(Qt.PartiallyChecked)

    def load_settings(self, settings):
        self.commandLineEdit.setText(settings.get('command', ''))
        self.noOutputCheckBox.setChecked(settings.get('no_output', True))
        self.noErrorCheckBox.setChecked(settings.get('no_error', True))
        self.killCheckBox.setChecked(settings.get('kill', False))

    def get_settings(self):
        settings = {}

        if not (self.group.isCheckable() and not self.group.isChecked()):
            if self.commandLineEdit.text().strip():
                settings['command'] = self.commandLineEdit.text()
        if self.noOutputCheckBox.checkState() != Qt.PartiallyChecked:
            settings['no_output'] = self.noOutputCheckBox.isChecked()
        if self.noErrorCheckBox.checkState() != Qt.PartiallyChecked:
            settings['no_error'] = self.noErrorCheckBox.isChecked()
        if self.killCheckBox.checkState() != Qt.PartiallyChecked:
            settings['kill'] = self.killCheckBox.isChecked()

        return settings


CueSettingsRegistry().add_item(CommandCueSettings, CommandCue)