示例#1
0
文件: test.py 项目: jin/stopwatch.py
 def testRunning(self):
     """Tests that the running boolean works as expected"""
     stopwatch = Stopwatch()
     self.assertTrue(stopwatch.running)
     stopwatch.stop()
     self.assertFalse(stopwatch.running)
     stopwatch.restart()
     self.assertTrue(stopwatch.running)
示例#2
0
class Orchestra:
    SAMPLE_RATE = 44100
    PLAYABLE_FORMATS = ['mp3', 'flac', 'wav', 'm4b']
    JACK = "jack"
    SSR = "ssr"

    @staticmethod
    def add_parser_arguments(parser):
        parser.add_argument("--rt", action="store_true", dest="realtime")
        parser.add_argument("-t", "--torrent", dest="torrentname", default="")
        parser.add_argument("-z", "--timefactor", dest="timefactor", type=float, default=1)
        parser.add_argument("--start", dest="start_time", type=float, default=0)
        parser.add_argument("-q", "--quiet", action="store_true", dest="quiet")
        parser.add_argument("--pretend-sequential", action="store_true", dest="pretend_sequential")
        parser.add_argument("--gui", action="store_true", dest="gui_enabled")
        parser.add_argument("--predecode", action="store_true", dest="predecode", default=True)
        parser.add_argument("--file-location", dest="file_location", default=DOWNLOAD_LOCATION)
        parser.add_argument("--fast-forward", action="store_true", dest="ff")
        parser.add_argument("--fast-forward-to-start", action="store_true", dest="ff_to_start")
        parser.add_argument("--quit-at-end", action="store_true", dest="quit_at_end")
        parser.add_argument("--loop", dest="loop", action="store_true")
        parser.add_argument("--max-passivity", dest="max_passivity", type=float)
        parser.add_argument("--max-pause-within-segment", dest="max_pause_within_segment", type=float)
        parser.add_argument("--looped-duration", dest="looped_duration", type=float)
        parser.add_argument("-o", "--output", dest="output", type=str, default=Orchestra.JACK)
        parser.add_argument("--include-non-playable", action="store_true")
        parser.add_argument("-f", "--file", dest="selected_files", type=int, nargs="+")
        parser.add_argument("--no-synth", action="store_true")
        parser.add_argument("--locate-peers", action="store_true")

    _extension_re = re.compile('\.(\w+)$')

    def __init__(self, sessiondir, tr_log, options):
        self.options = options
        self.sessiondir = sessiondir
        self.tr_log = tr_log
        self.realtime = options.realtime
        self.timefactor = options.timefactor
        self.quiet = options.quiet
        self.predecode = options.predecode
        self.file_location = options.file_location
        self._loop = options.loop
        self._max_passivity = options.max_passivity
        self.looped_duration = options.looped_duration
        self.output = options.output

        self.include_non_playable = options.include_non_playable

        if options.locate_peers:
            import geo.ip_locator
            self._peer_location = {}
            ip_locator = geo.ip_locator.IpLocator()
            for peeraddr in tr_log.peers:
                self._peer_location[peeraddr] = ip_locator.locate(peeraddr)

        if options.predecode:
            predecoder = Predecoder(tr_log, options.file_location, self.SAMPLE_RATE)
            predecoder.decode()

        if options.selected_files:
            tr_log.select_files(options.selected_files)

        self.playback_enabled = True
        self.fast_forwarding = False
        self._log_time_for_last_handled_event = 0
        self.gui = None
        self._check_which_files_are_audio()

        if options.no_synth:
            self.synth = None
        else:
            from synth_controller import SynthController
            self.synth = SynthController()

        self._create_players()
        self._prepare_playable_files()
        self.stopwatch = Stopwatch()
        self.playable_chunks = self._filter_playable_chunks(tr_log.chunks)

        if self.include_non_playable:
            self.chunks = tr_log.chunks
            self._num_selected_files = len(self.tr_log.files)
        else:
            self.chunks = self.playable_chunks
            self._num_selected_files = self._num_playable_files
        logger.debug("total num chunks: %s" % len(tr_log.chunks))
        logger.debug("num playable chunks: %s" % len(self.playable_chunks))
        logger.debug("num selected chunks: %s" % len(self.chunks))

        self._interpret_chunks_to_score(options.max_pause_within_segment)
        self._chunks_by_id = {}
        self.segments_by_id = {}
        self._playing = False
        self._quitting = False
        self.space = Space()

        if options.ff_to_start:
            self._ff_to_time = options.start_time
            self.set_time_cursor(0)
        else:
            self._ff_to_time = None
            self.set_time_cursor(options.start_time)

        self.scheduler = sched.scheduler(time.time, time.sleep)
        self._run_scheduler_thread()

        if self.output == self.SSR:
            self.ssr = SsrControl()
            self._warned_about_max_sources = False
        else:
            self.ssr = None

    def _interpret_chunks_to_score(self, max_pause_within_segment):
        self.score = Interpreter(max_pause_within_segment).interpret(self.playable_chunks, self.tr_log.files)
        if self._max_passivity:
            self._reduce_max_passivity_in_score()
        for segment in self.score:
            segment["duration"] /= self.timefactor

    def _reduce_max_passivity_in_score(self):
        previous_onset = 0
        reduced_time = 0
        for i in range(len(self.score)):
            if (self.score[i]["onset"] - reduced_time - previous_onset) > self._max_passivity:
                reduced_time += self.score[i]["onset"] - reduced_time - previous_onset - self._max_passivity
            self.score[i]["onset"] -= reduced_time
            previous_onset = self.score[i]["onset"]

    def _filter_playable_chunks(self, chunks):
        return filter(lambda chunk: (self._chunk_is_playable(chunk)),
                      chunks)


    def _chunk_is_playable(self, chunk):
        file_info = self.tr_log.files[chunk["filenum"]]
        return file_info["playable_file_index"] != -1

    def _run_scheduler_thread(self):
        self._scheduler_thread = threading.Thread(target=self._process_scheduled_events)
        self._scheduler_thread.daemon = True
        self._scheduler_thread.start()

    def _process_scheduled_events(self):
        while not self._quitting:
            self.scheduler.run()
            time.sleep(0.01)

    def _handle_visualizing_message(self, path, args, types, src, data):
        segment_id = args[0]
        segment = self.segments_by_id[segment_id]
        logger.debug("visualizing segment %s" % segment)
        if self.output == self.SSR:
            if segment["sound_source_id"]:
                channel = segment["sound_source_id"] - 1
                self._ask_synth_to_play_segment(segment, channel=channel, pan=None)
        else:
            player = self.get_player_for_segment(segment)
            self._ask_synth_to_play_segment(segment, channel=0, pan=player.spatial_position.pan)

    def _ask_synth_to_play_segment(self, segment, channel, pan):
        if self.synth:
            logger.debug("asking synth to play %s" % segment)
            file_info = self.tr_log.files[segment["filenum"]]

            self.synth.play_segment(
                segment["id"],
                segment["filenum"],
                segment["start_time_in_file"] / file_info["duration"],
                segment["end_time_in_file"] / file_info["duration"],
                segment["duration"],
                self.looped_duration,            
                channel,
                pan)
        self.scheduler.enter(
            segment["playback_duration"], 1,
            self.stopped_playing, [segment])

    def _check_which_files_are_audio(self):
        for file_info in self.tr_log.files:
            file_info["is_audio"] = self._has_audio_extension(file_info["name"])

    @staticmethod
    def _has_audio_extension(filename):
        return Orchestra._extension(filename) in Orchestra.PLAYABLE_FORMATS

    @staticmethod
    def _extension(filename):
        m = Orchestra._extension_re.search(filename)
        if m:
            return m.group(1).lower()

    def _create_players(self):
        self._player_class = WavPlayer
        self.players = []
        self._player_for_peer = dict()

    def _prepare_playable_files(self):
        if self.predecode:
            self._get_wav_files_info()
            self._load_sounds()
        else:
            raise Exception("playing wav without precoding is not supported")

    def _load_sounds(self):
        if self.synth:
            print "loading sounds"
            for filenum in range(len(self.tr_log.files)):
                file_info = self.tr_log.files[filenum]
                if file_info["playable_file_index"] != -1:
                    logger.debug("load_sound(%s)" % file_info["decoded_name"])
                    result = self.synth.load_sound(filenum, file_info["decoded_name"])
                    logger.debug("result: %s" % result)
            print "OK"

    def _get_wav_files_info(self):
        playable_file_index = 0
        for filenum in range(len(self.tr_log.files)):
            file_info = self.tr_log.files[filenum]
            file_info["playable_file_index"] = -1

            if "decoded_name" in file_info:
                file_info["duration"] = self._get_file_duration(file_info)
                if file_info["duration"] > 0:
                    file_info["num_channels"] = self._get_num_channels(file_info)
                    file_info["playable_file_index"] = playable_file_index
                    logger.debug("duration for %r: %r\n" %
                                      (file_info["name"], file_info["duration"]))
                    playable_file_index += 1

            if self.include_non_playable:
                file_info["index"] = filenum
            else:
                file_info["index"] = file_info["playable_file_index"]
        self._num_playable_files = playable_file_index

    def _get_file_duration(self, file_info):
        if "decoded_name" in file_info:
            cmd = 'soxi -D "%s"' % file_info["decoded_name"]
            try:
                stdoutdata, stderrdata = subprocess.Popen(
                    cmd, shell=True,
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
                return float(stdoutdata)
            except:
                logger.debug("failed to get duration for %s" % file_info["decoded_name"])
                return 0

    def _get_num_channels(self, file_info):
        if "decoded_name" in file_info:
            cmd = 'soxi -c "%s"' % file_info["decoded_name"]
            stdoutdata, stderrdata = subprocess.Popen(
                cmd, shell=True, stdout=subprocess.PIPE).communicate()
            return int(stdoutdata)

    def get_current_log_time(self):
        if self.fast_forwarding:
            return self._log_time_for_last_handled_event
        else:
            return self.log_time_played_from + self.stopwatch.get_elapsed_time() * self.timefactor

    def play_non_realtime(self, quit_on_end=False):
        logger.debug("entering play_non_realtime")
        if self._loop:
            while True:
                self._play_until_end()
                self.set_time_cursor(0)
        else:
            self._play_until_end()
            if quit_on_end:
                self._quitting = True
        logger.debug("leaving play_non_realtime")

    def _play_until_end(self):
        logger.debug("entering _play_until_end")
        self._playing = True
        self.stopwatch.start()
        no_more_events = False
        while self._playing and not no_more_events:
            event = self._get_next_chunk_or_segment()
            if event:
                self._handle_event(event)
            else:
                no_more_events = True
        logger.debug("leaving _play_until_end")

    def _get_next_chunk_or_segment(self):
        logger.debug("chunk index = %d, segment index = %d" % (
                self.current_chunk_index, self.current_segment_index))
        chunk = self._get_next_chunk()
        segment = self._get_next_segment()
        logger.debug("next chunk: %s" % chunk)
        logger.debug("next segment: %s" % segment)
        if chunk and segment:
            return self._choose_nearest_chunk_or_segment(chunk, segment)
        elif chunk:
            return {"type": "chunk",
                    "chunk": chunk}
        elif segment:
            return {"type": "segment",
                    "segment": segment}
        else:
            return None

    def _get_next_chunk(self):
        if self.current_chunk_index < len(self.chunks):
            return self.chunks[self.current_chunk_index]

    def _get_next_segment(self):
        if len(self.score) == 0:
            return None
        elif self.current_segment_index < len(self.score):
            return self.score[self.current_segment_index]

    def _handle_event(self, event):
        if event["type"] == "chunk":
            self.handle_chunk(event["chunk"])
            self.current_chunk_index += 1
        elif event["type"] == "segment":
            self.handle_segment(event["segment"])
            self.current_segment_index += 1
        else:
            raise Exception("unexpected event %s" % event)

    def _choose_nearest_chunk_or_segment(self, chunk, segment):
        if chunk["t"] < segment["onset"]:
            return {"type": "chunk",
                    "chunk": chunk}
        else:
            return {"type": "segment",
                    "segment": segment}
            
    def stop(self):
        if self.synth:
            self.synth.stop_all()
        self._playing = False
        self.log_time_played_from = self.get_current_log_time()
        self.stopwatch.stop()

    def handle_segment(self, segment):
        logger.debug("handling segment %s" % segment)
        player = self.get_player_for_segment(segment)
        if not player:
            logger.debug("get_player_for_segment returned None - skipping playback")

        if self.fast_forwarding:
            self._stop_ff_if_necessary()
        else:
            now = self.get_current_log_time()
            time_margin = segment["onset"] - now
            logger.debug("time_margin=%f-%f=%f" % (segment["onset"], now, time_margin))
            if not self.realtime and time_margin > 0:
                sleep_time = time_margin
                logger.debug("sleeping %f" % sleep_time)
                time.sleep(sleep_time)
        if player:
            logger.debug("player.enabled=%s" % player.enabled)
        if player and player.enabled:
            player.play(segment, pan=0.5)
        self._log_time_for_last_handled_event = segment["onset"]

    def handle_chunk(self, chunk):
        logger.debug("handling chunk %s" % chunk)
        player = self.get_player_for_chunk(chunk)
        logger.debug("get_player_for_chunk returned %s" % player)

        if self.fast_forwarding:
            self._stop_ff_if_necessary()
        else:
            now = self.get_current_log_time()
            time_margin = chunk["t"] - now
            logger.debug("time_margin=%f-%f=%f" % (chunk["t"], now, time_margin))
            if not self.realtime and time_margin > 0:
                sleep_time = time_margin
                logger.debug("sleeping %f" % sleep_time)
                time.sleep(sleep_time)
        if player:
            logger.debug("player.enabled=%s" % player.enabled)
        if player and player.enabled:
            player.visualize(chunk)
        self._log_time_for_last_handled_event = chunk["t"]

    def _stop_ff_if_necessary(self):
        if self._ff_to_time is not None and \
                self._log_time_for_last_handled_event >= self._ff_to_time:
            self._ff_to_time = None
            self.fast_forwarding = False
            self.set_time_cursor(self.log_time_played_from)

    def highlight_segment(self, segment):
        if self.gui:
            self.gui.highlight_segment(segment)

    def visualize_chunk(self, chunk, player):
        if len(self.visualizers) > 0:
            file_info = self.tr_log.files[chunk["filenum"]]
            self._chunks_by_id[chunk["id"]] = chunk
            self._tell_visualizers(
                "/chunk",
                chunk["id"],
                chunk["begin"],
                chunk["end"] - chunk["begin"],
                file_info["index"],
                player.id,
                chunk["t"])

    def visualize_segment(self, segment, player):
        if len(self.visualizers) > 0:
            if self.ssr:
                segment["sound_source_id"] = self.ssr.allocate_source()
                if not segment["sound_source_id"] and not self._warned_about_max_sources:
                    print "WARNING: max sources exceeded, skipping segment playback (this warning will not be repeated)"
                    self._warned_about_max_sources = True

            file_info = self.tr_log.files[segment["filenum"]]
            self.segments_by_id[segment["id"]] = segment
            self._tell_visualizers(
                "/segment",
                segment["id"],
                segment["begin"],
                segment["end"] - segment["begin"],
                file_info["index"],
                player.id,
                segment["t"],
                segment["playback_duration"])
        else:
            self._ask_synth_to_play_segment(segment, channel=0, pan=0.5)

    def stopped_playing(self, segment):
        logger.debug("stopped segment %s" % segment)
        if self.gui:
            self.gui.unhighlight_segment(segment)
        if len(self.visualizers) > 0:
            if self.ssr and segment["sound_source_id"]:
                self.ssr.free_source(segment["sound_source_id"])

    def play_segment(self, segment, player):
        self.segments_by_id[segment["id"]] = segment

        if self.looped_duration:
            segment["playback_duration"] = self.looped_duration
        else:
            segment["playback_duration"] = segment["duration"]

        self.visualize_segment(segment, player)

    def _send_torrent_info_to_uninformed_visualizers(self):
        for visualizer in self.visualizers:
            if not visualizer.informed_about_torrent:
                self._send_torrent_info_to_visualizer(visualizer)

    def _send_torrent_info_to_visualizer(self, visualizer):
        visualizer.send(
            "/torrent",
            self._num_selected_files,
            self.tr_log.lastchunktime(),
            self.tr_log.total_file_size(),
            len(self.chunks),
            len(self.score))
        for filenum in range(len(self.tr_log.files)):
            file_info = self.tr_log.files[filenum]
            if self.include_non_playable or file_info["playable_file_index"] != -1:
                visualizer.send(
                    "/file",
                    file_info["index"],
                    file_info["offset"],
                    file_info["length"])
        visualizer.informed_about_torrent = True

    def get_player_for_chunk(self, chunk):
        try:
            return chunk["player"]
        except KeyError:
            peer_player = self.get_player_for_peer(chunk["peeraddr"])
            chunk["player"] = peer_player
            return peer_player

    def get_player_for_segment(self, segment):
        try:
            return segment["player"]
        except KeyError:
            peer_player = self.get_player_for_peer(segment["peeraddr"])
            segment["player"] = peer_player
            return peer_player

    def get_player_for_peer(self, peeraddr):
        peer_player = None
        try:
            peer_player = self._player_for_peer[peeraddr]
        except KeyError:
            peer_player = self._create_player(peeraddr)
            self.players.append(peer_player)
            self._player_for_peer[peeraddr] = peer_player
        return peer_player

    def _create_player(self, addr):
        count = len(self.players)
        logger.debug("creating player number %d" % count)
        player = self._player_class(self, count)
        if self.options.locate_peers and self._peer_location[addr] is not None:
            x, y = self._peer_location[addr]
            location_str = "%s,%s" % (x, y)
        else:
            location_str = ""
        self._tell_visualizers(
            "/peer", player.id, addr, player.spatial_position.bearing, location_str)
        return player

    def set_time_cursor(self, log_time):
        assert not self.realtime
        logger.debug("setting time cursor at %f" % log_time)
        self.log_time_played_from = log_time
        if self._playing:
            self.stopwatch.restart()
        self.current_chunk_index = self._get_current_chunk_index()
        self.current_segment_index = self._get_current_segment_index()

    def _get_current_chunk_index(self):
        index = 0
        next_to_last_index = len(self.chunks) - 2
        while index < next_to_last_index:
            if self.chunks[index+1]["t"] >= self.log_time_played_from:
                return index
            index += 1
        return len(self.chunks) - 1

    def _get_current_segment_index(self):
        index = 0
        next_to_last_index = len(self.score) - 2
        while index < next_to_last_index:
            if self.score[index+1]["onset"] >= self.log_time_played_from:
                return index
            index += 1
        return len(self.score) - 1

    def _handle_set_listener_position(self, path, args, types, src, data):
        if self.ssr:
            x, y = args
            self.ssr.set_listener_position(x, y)

    def _handle_set_listener_orientation(self, path, args, types, src, data):
        if self.ssr:
            orientation = args[0]
            self.ssr.set_listener_orientation(orientation)

    def _handle_place_segment(self, path, args, types, src, data):
        segment_id, x, y, duration = args
        if self.ssr:
            segment = self.segments_by_id[segment_id]
            sound_source_id = segment["sound_source_id"]
            if sound_source_id is not None:
                self.ssr.place_source(sound_source_id, x, y, duration)
        else:
            pan = self._spatial_position_to_stereo_pan(x, y)
            if self.synth:
                self.synth.pan(segment_id, pan)

    def _handle_enable_smooth_movement(self, path, args, types, src, data):
        if self.ssr:
            self.ssr.enable_smooth_movement()

    def _handle_start_segment_movement_from_peer(self, path, args, types, src, data):
        segment_id, duration = args
        if self.ssr:
            segment = self.segments_by_id[segment_id]
            sound_source_id = segment["sound_source_id"]
            if sound_source_id is not None:
                player = self.get_player_for_segment(segment)
                self.ssr.start_source_movement(
                    sound_source_id, player.trajectory, duration)

    def _spatial_position_to_stereo_pan(self, x, y):
        # compare rectangular_visualizer.Visualizer.pan_segment
        # NOTE: assumes default listener position and orientation!
        return float(x) / 5 + 0.5

    def reset(self):
        self._free_sounds()
        self._tell_visualizers("/reset")

    def _free_sounds(self):
        if self.synth:
            for filenum in range(len(self.tr_log.files)):
                file_info = self.tr_log.files[filenum]
                if file_info["playable_file_index"] != -1:
                    self.synth.free_sound(filenum)

    def _tell_visualizers(self, *args):
        self._send_torrent_info_to_uninformed_visualizers()
        self.server._tell_visualizers(*args)
from sense_hat import SenseHat
from time import sleep
import time
from datetime import datetime
from stopwatch import Stopwatch
# from constants import *
from mazes import *

now = datetime.now()
current_time = now.strftime("%H:%M:%S")
stopwatch = Stopwatch()

sense = SenseHat()
sense.clear()

stopwatch.restart()

maze = maze1


class MazePhysics:
    def move_marble(self, pitch, roll, x, y):
        new_x = x
        new_y = y
        if 1 < pitch < 179 and x != 0:
            new_x -= 1
        elif 359 > pitch > 181 and x != 7:
            new_x += 1
        if 1 < roll < 179 and y != 7:
            new_y += 1
        elif 359 > roll > 181 and y != 0:
示例#4
0
class Chrono(QtWidgets.QMainWindow):
    def __init__(self):
        super(Chrono,
              self).__init__()  # Call the inherited classes __init__ method
        uic.loadUi("test" + path_separator + 'UI' + path_separator +
                   'cronometro.ui', self)  # Load the .ui file
        self.showMaximized()  # Show the GUI

        config = load_properties()
        self.current_user = config.get('UsersSection', 'currentUser')

        self.action_import.triggered.connect(self.import_db)
        self.action_import.setShortcut(QtGui.QKeySequence("Ctrl+i"))

        self.quali_label.setVisible(False)

        self.action_export.triggered.connect(self.export_db)
        self.action_export.setShortcut(QtGui.QKeySequence("Ctrl+e"))

        self.saved_message_thread = message_thread(self.saved_msg)

        styles = {'color': '(162,173,194)', 'font-size': '15px'}
        self.graph.showGrid(x=True, y=True)
        self.graph.setLabel('left', 'Tiempo por vuelta', **styles)
        self.graph.setLabel('bottom', 'Días', **styles)
        self.graph.setBackground('#fdfdff')
        self.graph.setTitle("Tiempos del paciente")

        self.lapsList.setStyleSheet(
            "QLabel#lapsList{ font-weight: bold; color: #555860; font-size: 17px; }"
        )
        self.quali_label.setStyleSheet(
            "QLabel#quali_label{ font-weight: bold; color: #fdfdff; font-size: 15px; background-color: #222628; border: 2px solid #909293; }"
        )

        self.moreInfo.setStyleSheet(
            "QPushButton#moreInfo{ border: 2px solid #717987; font-weight: bold; color: white; background-color: #a2adc2; } QPushButton#moreInfo::hover{ background-color: #BDC5D4;} QPushButton#moreInfo::pressed{background-color: #717987;}"
        )
        self.moreInfo.clicked.connect(self.show_more_info)
        self.moreInfo.hide()

        self.saveIcon.clicked.connect(self.save_times)
        self.saveIcon.hide()

        self.saveIcon.setStyleSheet(
            "QPushButton#saveIcon{ border-radius: 20px; font-weight: bold; color: white; background-color: #a2adc2; } QPushButton#saveIcon::hover{ background-color: #BDC5D4;} QPushButton#saveIcon::pressed{background-color: #717987;}"
        )
        self.startStop.setStyleSheet(
            "QPushButton#startStop{ border-radius: 20px; font-weight: bold; color: white; background-color: #a2adc2; } QPushButton#startStop::hover{ background-color: #BDC5D4;} QPushButton#startStop::pressed{background-color: #717987;}"
        )
        self.startStop.clicked.connect(self.start_crono)
        self.users.clicked.connect(self.open_users_menu)
        self.pacientesIcon.clicked.connect(self.open_patients_menu)
        self.settingsIcon.clicked.connect(self.open_settings)

        self.comboPatients.setStyleSheet(
            "QComboBox#comboPatients QAbstractItemView{ background-color: #fdfdff; color: #222628; selection-background-color: #555860; selection-color: #fdfdff;}"
        )
        self.comboPatients.setPlaceholderText("Selecciona un paciente")
        self.patients_dni = []
        self.current_patient = -1
        self.fill_combo()
        self.comboPatients.currentIndexChanged.connect(self.select_new_patient)

        self.stopwatch = Stopwatch()
        self.stopwatch.stop()

        self.saved_msg.setStyleSheet("QWidget#saved_msg{ color: black }")
        self.centralwidget.setStyleSheet(
            "QWidget#centralwidget{ background-color: #fdfdff}")
        self.barraLateral.setStyleSheet(
            "QWidget#barraLateral{ background-color: #555860; }")
        self.cronIcon.setStyleSheet(
            "QPushButton#cronIcon::hover{ border: none; background-color: #a2adc2;} QPushButton#cronIcon::pressed{background-color: #222628;}"
        )
        self.users.setStyleSheet(
            "QPushButton#users::hover{ border: none; background-color: #a2adc2;} QPushButton#users::pressed{background-color: #222628;}"
        )
        self.pacientesIcon.setStyleSheet(
            "QPushButton#pacientesIcon::hover{ border: none; background-color: #a2adc2;} QPushButton#pacientesIcon::pressed{background-color: #222628;}"
        )
        self.settingsIcon.setStyleSheet(
            "QPushButton#settingsIcon::hover{ border: none; background-color: #a2adc2;} QPushButton#settingsIcon::pressed{background-color: #222628;}"
        )

        self.izquierda.setStyleSheet(
            "QWidget#izquierda::hover{ border: none; background-color: #e9e8eb;} "
        )
        self.derecha.setStyleSheet(
            "QWidget#derecha::hover{ border: none; background-color: #e9e8eb;} "
        )

        self.laps_image = QtGui.QIcon('test/img/laps.png')
        self.restart_image = QtGui.QIcon('test/img/restart.png')
        self.start_image = QtGui.QIcon('test/img/white.png')

        self.text = ""
        self.lap_num = 0
        self.previous_time = 0
        self.lap_times = []
        self.timer = QtCore.QTimer(self)
        self.timer.timeout.connect(self.run_watch)
        self.timer.setInterval(10)
        self.mscounter = 0
        self.isreset = True
        self.showLCD()

    def open_settings(self):
        """ 
        Obri la finestra dels settings on es canviaràn els temps màxims i mínims del segments i total.
        """
        self.new_window = Settings()

    def export_db(self):
        """
        Exportar la BD actual
        """
        export_path = QtWidgets.QFileDialog.getSaveFileName(
            self, 'Save file', QtCore.QDir.homePath(),
            "Archivo SQLite (*.db)")  # Arrepleguem el nou path

        copy_file(
            load_properties().get('DatabaseSection', 'dbname'), export_path[0]
        )  # Copiem la BD de la seva path a la path donada per l'usuari
        QtWidgets.QMessageBox.information(
            self, 'Parkthon', "La base de datos ha sido exportada con éxito"
        )  # Mostrem un missatge d'éxit

    def import_db(self):
        """
        Importar una nova BD. Com que aquesta funció també és utilitzada en login.py, s'ha implementat en l'arxiu __manifest__.py
        """
        import_db(self)

    def show_more_info(self):
        """
        Mostrem més informació del pacient actual
        """
        self.window = patient_info.Patient_info(
            self.current_user,
            self.current_patient)  # Llancem la nova finestra

    def save_times(self):
        """
        Aquesta funció crida al controlador de la DB per guardar una llista de temps al pacient actual
        """
        sql_con = sqlite.sqlite_connector()  # Creem la connexió

        sql_con.save_lap_times(self.lap_times, self.current_patient,
                               self.observations.toPlainText()
                               )  # Guardem els temps en l'usuari actual

        sql_con.close()  # Tanquem la connexió
        self.saveIcon.hide(
        )  # Amaguem l'icona de guardar ja que ja s'ha guardat
        self.observations.setText("")  #Borrem el text de les observacions
        self.show_patient_graph()  # Afegim aquest ultim temps a la gràfica
        self.saved_message_thread.start(
        )  # Escomencem el thread que mostrarà que el temps ha sigut guardat

    def fill_combo(self):
        """
        Funció que plena el QComboBox amb tots els pacients del metge actual
        """
        sql_con = sqlite.sqlite_connector()  # Arrepleguem la connexió a SQLite
        patients = sql_con.get_patient_names(
            self.current_user
        )  # Arrepleguem una matriu amb les dades de tots els pacients del metge actual
        sql_con.close()  # Tanquem la connexió una vegada recuperades les dades
        for patient in patients:
            self.patients_dni.append(
                patient[2]
            )  # Ens guardem els DNI de tots els pacients de forma interna
            self.comboPatients.addItem(
                patient[0] + " " +
                patient[1])  # Afegim el nom i cognom al QComboBox

    def select_new_patient(self):
        """
        Seleccionem el nou pacient, en el que es realitzarán totes les accions com registrar nous temps, mostrar la gràfica, etc.
        """
        self.current_patient = self.patients_dni[self.comboPatients.currentIndex(
        )]  # Guardem de forma interna el DNI del pacient depenent del index del QComboBox (Per això guardavem els DNI en una llista, la cual està de forma paral.lela)
        self.show_patient_graph()  # Carreguem la gràfica del pacient
        self.moreInfo.show(
        )  # Mostrem el botó de més informació sobre el pacient

    def show_patient_graph(self):
        """
        Mostra/Carrega la gràfica del pacient
        """

        sql_con = sqlite.sqlite_connector()  # Arrepleguem el connector a la BD

        total_times = sql_con.get_patient_total_times(
            self.current_patient
        )  # Arrepleguem en forma de llista tots els temps dels pacients
        seg1_times = sql_con.get_patient_segment_times(self.current_patient,
                                                       "lap1")
        seg2_times = sql_con.get_patient_segment_times(self.current_patient,
                                                       "lap2")
        seg3_times = sql_con.get_patient_segment_times(self.current_patient,
                                                       "lap3")
        dates = sql_con.get_patient_dates(
            self.current_patient
        )  # Arrepleguem els díes en els que es van realitzar les proves (Son llistes paral.leles)

        sql_con.close()  # Tanquem la connexió
        self.graph.clear()  # Esborrem tot el que hi ha en el gràfic
        self.graph.addLegend()  # Inicialitzem la llegenda
        self.graph.plot(dates,
                        total_times,
                        name="Total",
                        pen=pg.mkPen(color=(162, 173, 194), width=4),
                        symbol='o',
                        symbolSize=10,
                        symbolBrush=(0, 0, 0))  # Mostrem les dades
        self.graph.plot(dates,
                        seg1_times,
                        name="Segmento 1",
                        pen=pg.mkPen(color=(255, 0, 0), width=4),
                        symbol='o',
                        symbolSize=5,
                        symbolBrush=(0, 0, 0))
        self.graph.plot(dates,
                        seg2_times,
                        name="Segmento 2",
                        pen=pg.mkPen(color=(255, 255, 0), width=4),
                        symbol='o',
                        symbolSize=5,
                        symbolBrush=(0, 0, 0))
        self.graph.plot(dates,
                        seg3_times,
                        name="Segmento 3",
                        pen=pg.mkPen(color=(255, 0, 255), width=4),
                        symbol='o',
                        symbolSize=5,
                        symbolBrush=(0, 0, 0))

    def record_lap(self):
        """
        Afegeix una nova lap al cronómetro
        """
        if (
                self.mscounter > 1000
        ):  # El contador ha de ser major a un segon per evitar problemes de compilació
            this_time = float(
                str(self.stopwatch)
                [:-1])  # Arrepleguem com a float el temps de la lap actual
            self.text += "Vuelta " + str(
                self.lap_num + 1
            ) + ": "  # Comencem a crear el text que es mostrarà en el número de volta
            if (self.lap_num == 0):  # Si és la primera volta
                self.text += "{:.2f}".format(
                    this_time
                )  # Al text li afegim el temps formatejat amb sols 2 decimals
                lap_type = get_lap_type(
                    self.lap_num, this_time
                )  # Arrepleguem el tipus de lap (Lleu, Moderat, Greu)
                color = get_color_type(
                    lap_type)  # Arrepleguem el color (Depenent de la lap)
            else:
                lap_type = get_lap_type(self.lap_num,
                                        this_time - self.previous_time)
                color = get_color_type(lap_type)
                self.text += "{:.2f}".format(this_time - self.previous_time)

            self.text += " - <span style='color:" + color + ";'>" + lap_type + "</span><br/>"
            self.lapsList.setText(self.text)

            self.lap_num += 1
            self.lap_times.append(
                float("{:.2f}".format(this_time - self.previous_time)))
            self.previous_time = this_time
            if (self.lap_num == 3):

                self.show_total_qualification()
                self.saveIcon.show()
                self.pause_watch()
                self.previous_time = 0
                self.text += "<span style='color: #222628;'>¡VUELTAS COMPLETADAS!</span>"
                self.lapsList.setText(self.text)

    def show_total_qualification(self):
        self.quali_label.setVisible(True)
        total_time = 0
        for time in self.lap_times:
            total_time += time
        lap_type = get_lap_type(-1, total_time)
        color = get_color_type(lap_type)

        self.quali_label.setText("Clasificación: <span style='color:" + color +
                                 ";'>" + lap_type + "</span>")

    def showLCD(self):
        """
        Aquesta funció serveix per a mostrar per pantalla el temps
        """
        if not self.isreset:  # si "isreset" es False
            self.cronNum.setText(str(self.stopwatch))
        else:
            self.cronNum.setText('0.00')

    def run_watch(self):
        """
        Executa el cronómetre de forma interna
        """
        self.mscounter += 10
        self.showLCD()

    def start_crono(self):
        """
        Comença el cronómetre de forma visible
        """
        self.saveIcon.hide()
        self.lapsList.setText("")
        if (self.current_patient == -1):
            QtWidgets.QMessageBox.critical(
                self, 'ERROR', "Primero tienes que seleccionar un paciente.")
        else:
            if (self.isreset):
                self.stopwatch.start()
                self.timer.start()

            else:
                self.stopwatch.restart()
                self.timer.restart()

            self.startStop.setIcon(
                self.laps_image)  # Passa a ser el botó de laps
            self.startStop.clicked.disconnect(
                self.start_crono)  # Desconectem l'antic slot
            self.startStop.clicked.connect(self.record_lap)  # Conectem el nou
            self.isreset = False

    def pause_watch(self):
        """
        Pausa el cronómetre quan s'aplega a 3 voltes
        """

        self.stopwatch.stop()
        self.timer.stop()
        self.startStop.setIcon(self.restart_image)
        self.startStop.clicked.disconnect(self.record_lap)
        self.startStop.clicked.connect(self.reset_watch)

    def reset_watch(self):
        """
        Reinicia el cronómetre
        """
        self.stopwatch.restart()
        self.mscounter = 0
        self.lap_num = 0
        self.isreset = True
        self.showLCD()
        self.lap_times = []
        self.text = ""
        self.quali_label.setText("")
        self.quali_label.setVisible(False)

        self.startStop.clicked.disconnect(self.reset_watch)
        self.startStop.clicked.connect(self.start_crono)
        self.start_crono()

    def open_users_menu(self):
        self.new_window = user_management.Users_management()
        self.setVisible(False)
        self.close()

    def open_patients_menu(self):
        self.new_window = patient_management.Patient_management()
        self.setVisible(False)
        self.close()

    def closeEvent(self, event):
        if (self.isVisible()):
            if (salir()):
                event.accept()
            else:
                event.ignore()
        else:
            event.ignore()
示例#5
0
class Window(Frame):
    def __init__(self, master):
        Frame.__init__(self, master)

        self.master = master

        self.master.title("SST")

        self.pack()

        # initializing mouse + keyboard objects to be used in prnt_scrpt() and del_sums_scrpt()
        self.keyboard = kb.Controller()
        self.mouse = ms.Controller()

        # Game Timer
        self.game_sw = Stopwatch()

        # Individual Role Timer Objects
        self.t_sw = Stopwatch().reset()
        self.j_sw = Stopwatch().reset()
        self.m_sw = Stopwatch().reset()
        self.a_sw = Stopwatch().reset()
        self.s_sw = Stopwatch().reset()

        # vars to store ss cds for CheckButtons
        self.top_ci = IntVar(master)
        self.jg_ci = IntVar(master)
        self.mid_ci = IntVar(master)
        self.ad_ci = IntVar(master)
        self.supp_ci = IntVar(master)

        # how many seconds each role's timer is when called
        self.topExactSecs = 0
        self.jgExactSecs = 0
        self.midExactSecs = 0
        self.adExactSecs = 0
        self.suppExactSecs = 0

        self.summs_list = [
            self.t_sw, self.j_sw, self.m_sw, self.a_sw, self.s_sw
        ]
        self.ci_list = [
            self.top_ci, self.jg_ci, self.mid_ci, self.ad_ci, self.supp_ci
        ]

        self.exactRoleSecs = [
            self.topExactSecs, self.jgExactSecs, self.midExactSecs,
            self.adExactSecs, self.suppExactSecs
        ]

        self.music_list = [
            'extras/top.mp3', 'extras/jg.mp3', 'extras/mid.mp3',
            'extras/ad.mp3', 'extras/supp.mp3'
        ]

        self.top_img = ImageTk.PhotoImage(Image.open("extras/top.png"))
        self.jg_img = ImageTk.PhotoImage(Image.open("extras/jg.png"))
        self.mid_img = ImageTk.PhotoImage(Image.open("extras/mid.png"))
        self.ad_img = ImageTk.PhotoImage(Image.open("extras/ad.png"))
        self.supp_img = ImageTk.PhotoImage(Image.open("extras/supp.png"))

        self.ci_img = ImageTk.PhotoImage(Image.open("extras/ci.png"))

        # Individual Role Buttons + images + placing them on grid
        self.tButton = Button(self,
                              height=50,
                              image=self.top_img,
                              command=self.swtch_top)
        self.tButton.image = self.top_img
        self.tButton.grid(row=0,
                          column=1,
                          columnspan=3,
                          sticky=W + E,
                          padx=5,
                          pady=(5, 1))

        self.jButton = Button(self,
                              width=10,
                              height=50,
                              image=self.jg_img,
                              command=self.swtch_jg)
        self.jButton.image = self.jg_img
        self.jButton.grid(row=1,
                          column=1,
                          columnspan=3,
                          sticky=W + E,
                          padx=5,
                          pady=1)

        self.mButton = Button(self,
                              width=10,
                              height=50,
                              image=self.mid_img,
                              command=self.swtch_mid)
        self.mButton.image = self.mid_img
        self.mButton.grid(row=2,
                          column=1,
                          columnspan=3,
                          sticky=W + E,
                          padx=5,
                          pady=1)

        self.aButton = Button(self,
                              width=10,
                              height=50,
                              image=self.ad_img,
                              command=self.swtch_ad)
        self.aButton.image = self.ad_img
        self.aButton.grid(row=3,
                          column=1,
                          columnspan=3,
                          sticky=W + E,
                          padx=5,
                          pady=1)

        self.sButton = Button(self,
                              width=10,
                              height=50,
                              image=self.supp_img,
                              command=self.swtch_supp)
        self.sButton.image = self.supp_img
        self.sButton.grid(row=4, column=1, columnspan=3, sticky=W + E, padx=5)

        self.buttonList = [
            self.tButton, self.jButton, self.mButton, self.aButton,
            self.sButton
        ]
        # Specific timer(s) for each role (to be entered manually)

        self.manTopField = Entry(self, width=10)
        self.manTopField.insert(END, '')
        self.manTopField.grid(row=0,
                              column=6,
                              columnspan=2,
                              sticky=E,
                              pady=(5, 1))
        self.manJgField = Entry(self, width=10)
        self.manJgField.insert(END, '')
        self.manJgField.grid(row=1,
                             column=6,
                             columnspan=2,
                             sticky=E,
                             pady=(5, 1))
        self.manMidField = Entry(self, width=10)
        self.manMidField.insert(END, '')
        self.manMidField.grid(row=2,
                              column=6,
                              columnspan=2,
                              sticky=E,
                              pady=(5, 1))
        self.manAdField = Entry(self, width=10)
        self.manAdField.insert(END, '')
        self.manAdField.grid(row=3,
                             column=6,
                             columnspan=2,
                             sticky=E,
                             pady=(5, 1))
        self.manSuppField = Entry(self, width=10)
        self.manSuppField.insert(END, '')
        self.manSuppField.grid(row=4,
                               column=6,
                               columnspan=2,
                               sticky=E,
                               pady=(5, 1))

        self.delay_list = [
            self.manTopField.get(),
            self.manJgField.get(),
            self.manMidField.get(),
            self.manAdField.get(),
            self.manSuppField.get()
        ]
        self.entryList = [
            self.manTopField, self.manJgField, self.manMidField,
            self.manAdField, self.manSuppField
        ]

        # Cosmic Insight Checkbuttons (for each role) + ci icon + placing them on grid
        self.t_ci = Checkbutton(self,
                                variable=self.top_ci,
                                onvalue=285,
                                offvalue=300)
        self.t_ci.grid(row=0, column=0, pady=(5, 1), padx=1)
        self.j_ci = Checkbutton(self,
                                variable=self.jg_ci,
                                onvalue=285,
                                offvalue=300)
        self.j_ci.grid(row=1, column=0, padx=1)
        self.m_ci = Checkbutton(self,
                                variable=self.mid_ci,
                                onvalue=285,
                                offvalue=300)
        self.m_ci.grid(row=2, column=0, padx=1)
        self.a_ci = Checkbutton(self,
                                variable=self.ad_ci,
                                onvalue=285,
                                offvalue=300)
        self.a_ci.grid(row=3, column=0, padx=1)
        self.s_ci = Checkbutton(self,
                                variable=self.supp_ci,
                                onvalue=285,
                                offvalue=300)
        self.s_ci.grid(row=4, column=0, padx=1)

        self.ci_image = Label(self, image=self.ci_img)
        self.ci_image.image = self.ci_image
        self.ci_image.grid(row=6, column=0, sticky=W)

        # Game Timer label + grid
        self.game_gui_label = Label(self, text="Game Timer:")
        self.game_gui_label.grid(row=6, column=3, ipadx=20)

        # Textbox to type in timer offset
        self.game_timer = Entry(self, width=10)
        self.game_timer.insert(END, '')
        self.game_timer.grid(row=6, column=5, columnspan=3, pady=10)
        self.game_timer.bind("<Return>", self.retrieve_input)

        self.bind('<FocusOut>', self.start_sums)
        self.bind('<FocusIn>', self.on_focus)

        # Clipboard_final = final string to be "copied" and printed out to the screen
        self.cb_final = ''

        # game timer offset provided by the user
        self.rel_inp = 0

        # 1st temp set of roles (used later)
        self.temp_top = 0
        self.temp_jg = 0
        self.temp_mid = 0
        self.temp_ad = 0
        self.temp_supp = 0

        # 2nd temp set of roles (used later)
        self.t_top = ''
        self.t_jg = ''
        self.t_mid = ''
        self.t_ad = ''
        self.t_supp = ''

        # indiv. role strings to be appended to newest_iteration
        self.top_string = ''
        self.jg_string = ''
        self.mid_string = ''
        self.ad_string = ''
        self.supp_string = ''

        # buffer var to be checked w/ cb_final to allow mult roles to be printed in one action/line
        self.newest_iteration = ''

        # whether or not role's button is selected currently
        self.topBool = False
        self.jgBool = False
        self.midBool = False
        self.adBool = False
        self.suppBool = False

        # keeps track whether summ is currently copied to "clipboard"
        self.t_copied = False
        self.j_copied = False
        self.m_copied = False
        self.a_copied = False
        self.s_copied = False

        self.t_valid = False
        self.j_valid = False
        self.m_valid = False
        self.a_valid = False
        self.s_valid = False

        self.manValidList = [
            self.t_valid, self.j_valid, self.m_valid, self.a_valid,
            self.s_valid
        ]

    # ALL 'swtch_foo()' methods are used to toggle state of its respective button. (probably refactor at some point)
    def swtch_top(self):
        if self.topBool:
            self.topBool = False
            self.tButton.configure(bg="SystemButtonFace")
        else:
            self.topBool = True
            self.tButton.configure(bg="green")

    def swtch_jg(self):
        if self.jgBool:
            self.jgBool = False
            self.jButton.configure(bg="SystemButtonFace")
        else:
            self.jgBool = True
            self.jButton.configure(bg="green")

    def swtch_mid(self):
        if self.midBool:
            self.midBool = False
            self.mButton.configure(bg="SystemButtonFace")
        else:
            self.midBool = True
            self.mButton.configure(bg="green")

    def swtch_ad(self):
        if self.adBool:
            self.adBool = False
            self.aButton.configure(bg="SystemButtonFace")
        else:
            self.adBool = True
            self.aButton.configure(bg="green")

    def swtch_supp(self):
        if self.suppBool:
            self.suppBool = False
            self.sButton.configure(bg="SystemButtonFace")
        else:
            self.suppBool = True
            self.sButton.configure(bg="green")

    # ========= Any "event" param added below necessary because the function calling it is passing a value =============

    # is called whenever focus is lost on window,
    # if summ's button is active, its delay is applied, timer started, button color changed, and boolean reset
    def start_sums(self, event):
        # eventually make this so that it will allow the default to be a blank entry field w/o needing 0
        # pretty low prio tbh

        # ALSO try and get summs to be printed out in order of which comes up first

        # ALSO need to copypasta da mechs to allow game timer to be written as mmss

        # ALSO need to figure out eventually how to get api to figure out whether or not CI is present

        # have a lil error handling section here! try and catch, if catch just pass it i believe
        # maybe need to lookup what the error name was (eeek! that means you will have to willingly let ur code break!)
        for q in range(len(self.delay_list)):

            try:
                self.delay_list[q] = int(self.entryList[q].get())
            except ValueError:
                self.manValidList[q] = False
            else:
                self.manValidList[q] = True

        #self.delay_list[0] = int(self.manTopField.get())
        #self.delay_list[1] = int(float(self.manJgField.get()))
        #self.delay_list[2] = int(float(self.manMidField.get()))
        #self.delay_list[3] = int(float(self.manAdField.get()))
        #self.delay_list[4] = int(float(self.manSuppField.get()))

        if self.topBool:
            self.t_sw.restart()
            self.tButton.configure(bg="SystemButtonFace")
            self.tButton["state"] = "disabled"
            self.topBool = False
            self.exactRoleSecs[0] = self.ci_list[0].get()
        elif self.manValidList[0] and self.t_sw.duration == 0:
            self.t_sw.restart()
            self.tButton.configure(bg="SystemButtonFace")
            self.tButton["state"] = "disabled"
            self.topBool = False
            self.exactRoleSecs[0] = int(self.delay_list[0] % 100 + (self.delay_list[0] // 100) * 60)\
                                 - int(self.game_sw.duration + self.rel_inp)

        if self.jgBool:
            self.j_sw.restart()
            self.jButton.configure(bg="SystemButtonFace")
            self.jButton["state"] = "disabled"
            self.jgBool = False
            self.exactRoleSecs[1] = self.ci_list[1].get()
        elif self.manValidList[1] and self.j_sw.duration == 0:
            self.j_sw.restart()
            self.jButton.configure(bg="SystemButtonFace")
            self.jButton["state"] = "disabled"
            self.jgBool = False
            self.exactRoleSecs[1] = int(self.delay_list[1] % 100 + (self.delay_list[1] // 100) * 60)\
                                 - int(self.game_sw.duration + self.rel_inp)

        if self.midBool:
            self.m_sw.restart()
            self.mButton.configure(bg="SystemButtonFace")
            self.mButton["state"] = "disabled"
            self.midBool = False
            self.exactRoleSecs[2] = self.ci_list[2].get()
        elif self.manValidList[2] and self.m_sw.duration == 0:
            self.m_sw.restart()
            self.mButton.configure(bg="SystemButtonFace")
            self.mButton["state"] = "disabled"
            self.midBool = False
            self.exactRoleSecs[2] = int(self.delay_list[2] % 100 + (self.delay_list[2] // 100) * 60)\
                                 - int(self.game_sw.duration + self.rel_inp)

        if self.adBool:
            self.a_sw.restart()
            self.aButton.configure(bg="SystemButtonFace")
            self.aButton["state"] = "disabled"
            self.adBool = False
            self.exactRoleSecs[3] = self.ci_list[3].get()
        elif self.manValidList[3] and self.a_sw.duration == 0:
            self.a_sw.restart()
            self.aButton.configure(bg="SystemButtonFace")
            self.aButton["state"] = "disabled"
            self.adBool = False
            self.exactRoleSecs[3] = int(self.delay_list[3] % 100 + (self.delay_list[3] // 100) * 60)\
                                 - int(self.game_sw.duration + self.rel_inp)

        if self.suppBool:
            self.s_sw.restart()
            self.sButton.configure(bg="SystemButtonFace")
            self.sButton["state"] = "disabled"
            self.suppBool = False
            self.exactRoleSecs[4] = self.ci_list[4].get()
        elif self.manValidList[4] and self.s_sw.duration == 0:
            self.s_sw.restart()
            self.sButton.configure(bg="SystemButtonFace")
            self.sButton["state"] = "disabled"
            self.suppBool = False
            self.exactRoleSecs[4] = int(self.delay_list[4] % 100 + (self.delay_list[4] // 100) * 60)\
                                 - int(self.game_sw.duration + self.rel_inp)

    # is called whenever focus is gained on window,
    # flushes delay buttons' and unused/fresh summ's  colors to default,
    def on_focus(self, event):
        for role in range(len(self.summs_list)):
            if self.summs_list[role].duration > 0.0:
                self.entryList[role].delete(0, 'end')
                self.entryList[role].insert(
                    0,
                    str(
                        int(self.exactRoleSecs[role] -
                            self.summs_list[role].duration)))

                # red
                if (self.exactRoleSecs[role] -
                        self.summs_list[role].duration) > 200:
                    self.entryList[role].config(background=_from_rgb((255, 0,
                                                                      0)))
                # orange
                elif 200 >= (self.exactRoleSecs[role] -
                             self.summs_list[role].duration) > 100:
                    self.entryList[role].config(background=_from_rgb((255, 100,
                                                                      0)))
                # yellow
                elif 100 >= (self.exactRoleSecs[role] -
                             self.summs_list[role].duration) > 0:
                    self.entryList[role].config(background=_from_rgb((255, 255,
                                                                      0)))

            #print(self.summs_list[role].duration)
            ## green(s)
            #if self.summs_list[role] == 0.0:
            #    print('reached')
            #    self.buttonList[role]["state"] = "normal"
            #    self.entryList[role].config(background=_from_rgb((0, 255, 0)))
            #    print('reached2')

    # called when <Enter> button is pressed -> apply's given offset + starts game timer
    def retrieve_input(self, event):
        # make this adaptive based off of number of digits
        self.rel_inp = int(self.game_timer.get())
        self.game_sw.restart()
        print(self.rel_inp)

    # generalized method to add any summ
    def add_new_summ(self, role_index, temp_role, t_role, role_string,
                     role_copied, role_name):
        temp_role = int(self.game_sw.duration + self.rel_inp - 1 +
                        self.exactRoleSecs[role_index])

        if summ_rounder(t_role, temp_role) == '59':
            role_string = str((temp_role // 60) + 1) + role_name
        else:
            role_string = str(temp_role // 60) + summ_rounder(
                t_role, temp_role) + role_name
        if role_string[:-1 * len(role_name)] in self.newest_iteration:
            role_string = role_name
        self.newest_iteration += role_string
        role_copied = True
        return temp_role, t_role, role_string, role_copied, self.newest_iteration

    # used to add new summs by bringing up chat + typing out updated string + closing out of League's chat
    def prnt_scrpt(self):
        time.sleep(.15)

        # bringing up chat
        self.keyboard.tap(kb.Key.enter)

        # typing timer(s)
        self.keyboard.type(self.cb_final)

        # selecting all
        self.keyboard.press(kb.Key.ctrl_l)
        time.sleep(.001)
        self.keyboard.press('a')
        self.keyboard.release('a')
        self.keyboard.release(kb.Key.ctrl_l)

        # copying all
        self.keyboard.press(kb.Key.ctrl_l)
        time.sleep(.001)
        self.keyboard.press('c')
        self.keyboard.release('c')
        self.keyboard.release(kb.Key.ctrl_l)

        # deleting all + esc'ing chat
        time.sleep(.001)
        self.keyboard.press(kb.Key.delete)
        self.keyboard.release(kb.Key.delete)
        self.keyboard.tap(kb.Key.enter)

        time.sleep(.15)
示例#6
0
class Game(PygameGame):
    def __init__(self):
        width = 800
        height = 500
        fps = 40
        fullCh = 255
        bgColor = (fullCh, fullCh, fullCh)
        title = "Bouncy Bouncy Revamped"
        super().__init__(width, height, fps, title, bgColor)

    def initStyles(self):
        assert(pygame.font.get_init())
        assert(pygame.image.get_extended())
        self.font = Struct()
        textSize = 16
        self.font.text = pygame.font.SysFont("Arial", textSize)
        titleSize = 30
        self.font.title = pygame.font.SysFont("Arial", titleSize)
        self.font.titleEmph = pygame.font.SysFont("Arial", titleSize,
            bold=True, italic=True)
        self.margin = 10

    def initMaps(self):
        self.maps = Maps(self.margin + 100, self.margin,
            self.width - 2*self.margin, self.height - 2*self.margin)
        self.progress = Progress(self.maps.levels, self)

    def initMainMenu(self):
        top = 130
        left = 80
        entries = ["Play", "Level select", "Instructions"]
        actions = [self.playLevel, self.doLevelMenu, self.doHelp]
        self.mainMenu = Menu(left, top, entries, actions)

    def initLevelMenu(self):
        top = 60
        left = 130
        entries = []
        actions = []
        for i, level in enumerate(self.maps.levels):
            prevLevel = self.maps.levels[i - 1]
            if i > 0 and self.progress[prevLevel]["score"] == None:
                entries.append("Level %s    Locked" % level)
                actions.append(None)
                continue
            elif self.progress[level]["score"] == None:
                entries.append("Level %s    Unlocked" % level)
            else:
                entries.append("Level %s    Highscore: %d    Best time: %s" %
                    (level, self.progress[level]["score"],
                        Stopwatch.secToMin(self.progress[level]["time"])))
            # Locally scope level
            actions.append(lambda level=level: self.playLevel(level))
        entries += ["Return to main menu", "Clear progress"]
        actions += [self.doMainMenu, self.progress.clear]
        self.levelMenu = Menu(left, top, entries, actions)

    def initPauseMenu(self):
        def play():
            self.mode = "play"
        top = 200
        left = 300
        width = 140
        height = 80
        entries = ["Continue", "Main menu", "Turn sound off"]
        actions = [play,
                   self.doMainMenu,
                   self.toggleSound]
        self.pauseMenu = Menu(left, top, entries, actions)
        background = pygame.Surface((width + self.margin, height + self.margin))
        background.fill(self.bgColor)
        pygame.draw.rect(background, (0, 0, 0), background.get_rect(), 2)
        self.pauseMenu.background = background

    def initMenus(self):
        self.initMainMenu()
        self.initLevelMenu()
        self.initPauseMenu()

    def init(self):
        self.initStyles()
        self.initMaps()
        Terrain.initImages()
        Terrain.initSounds()
        self.initMenus()
        self.stopwatch = Stopwatch()
        self.initHelp()
        self.mode = "menu"
        self.menu = self.mainMenu

    def loadLevel(self):
        terrain, self.startpos = self.maps.load(self.level)
        self.terrain = pygame.sprite.Group(*terrain)
        self.ball = Ball(*self.startpos)
        self.score = 0
        self.stopwatch.restart()
        if self.level != "test":
            highscore = self.progress[self.level]["score"]
            bestTime = self.progress[self.level]["time"]
        else:
            highscore, bestTime = None, None
        self.highscoreText = str(highscore) if highscore != None else "---"
        if bestTime != None:
            self.bestTimeText = Stopwatch.secToMin(bestTime)
        else:
            self.bestTimeText = "---"

    def nextLevel(self):
        # Update and save progress
        saved = self.progress[self.level]
        if saved["score"] == None or self.score > saved["score"]:
            saved["score"] = self.score
        duration = self.stopwatch.getSeconds()
        if saved["time"] == None or duration < saved["time"]:
            saved["time"] = duration
        self.progress.save()
        # Load next level
        Terrain.playSound("happy")
        index = self.maps.levels.index(self.level) + 1
        if index >= len(self.maps.levels):
            self.mode = "win"
        else:
            self.level = self.maps.levels[index]
            self.loadLevel()

    def initGame(self, level):
        self.level = level
        self.loadLevel()

    def killBall(self):
        Terrain.playSound("sad")
        self.loadLevel()

    def keyPressed(self, keyCode, modifier):
        #print(keyCode)
        if self.mode == "menu":
            self.menu.key(keyCode)
        elif self.mode == "play":
            if keyCode == 27:
                self.mode = "pause"
                self.menu = self.pauseMenu
        elif self.mode == "pause":
            self.menu.key(keyCode)
            if keyCode == 27:
                self.mode = "play"
        elif self.mode == "levelMenu":
            self.menu.key(keyCode)
            if keyCode == 27:
                self.mode = "menu"
                self.menu = self.mainMenu
        elif self.mode == "win":
            if keyCode == 13:
                self.mode = "menu"
                self.menu = self.mainMenu
        elif self.mode == "help":
            if keyCode == 27:
                self.mode = "menu"

    def ballFly(self, ball, cannon):
        ball.flying = True
        ball.vy = 0
        ball.vx = cannon.direction*ball.flyvx
        # Reposition the ball
        ball.y = cannon.y + ball.realR
        if cannon.direction > 0:
            ball.x = cannon.x + cannon.width + ball.realR
        else:
            ball.x = cannon.x - ball.realR

    def timerFiredPlay(self):
        if (self.ball.x < self.maps.left
                or self.ball.x > self.maps.left + self.maps.width or
                self.ball.y < self.maps.top
                or self.ball.y > self.maps.top + self.maps.height):
            # Ball off map
            self.killBall()

        collided = pygame.sprite.spritecollide(self.ball, self.terrain, False,
            Terrain.collidedFn)
        if len(collided) > 0:
            elements = Terrain.manageCollision(self.ball, collided)
            for element, direction in elements:
                result = element.interactFromDir(self.ball, direction)
                if result == "score":
                    self.score += element.points
                elif result == "win":
                    self.nextLevel()
                elif result == "fly":
                    self.ballFly(self.ball, element)
                elif result == "kill":
                    self.killBall()

        self.ball.update(self.isKeyPressed)

    def timerFired(self, dt):
        if self.mode == "play":
            self.timerFiredPlay()
            self.stopwatch.tick(dt)

    def drawMenu(self, screen):
        titleTop = 70
        titleLeft = 60
        x = drawText(screen, titleLeft, titleTop,
            text="Bouncy Bouncy ", font=self.font.title, anchor="nw")
        space = 8
        drawText(screen, x + space, titleTop, text="Revamped",
            font=self.font.titleEmph, anchor="nw")
        self.mainMenu.draw(screen, self.font.text)

    def drawGame(self, screen):
        self.terrain.draw(screen)
        self.ball.draw(screen)
        pos = 30
        drawText(screen, self.margin, pos,
            text="Level %s" % self.level, font=self.font.text, anchor="nw")
        pos = 80
        drawText(screen, self.margin, pos,
            text="Score: %d" % self.score, font=self.font.text, anchor="nw")
        pos = 110
        drawText(screen, self.margin, pos, text="Highscore:",
            font=self.font.text, anchor="nw")
        pos = 130
        drawText(screen, self.margin, pos, text=self.highscoreText,
            font=self.font.text, anchor="nw")
        self.drawGameTime(screen)

    def drawGameTime(self, screen):
        pos = 180
        drawText(screen, self.margin, pos,
            text=str(self.stopwatch), font=self.font.text, anchor="nw")
        pos = 210
        drawText(screen, self.margin, pos, text="Best time:",
            font=self.font.text, anchor="nw")
        pos = 230
        drawText(screen, self.margin, pos, text=self.bestTimeText,
            font=self.font.text, anchor="nw")

    def drawPause(self, screen):
        screen.blit(self.pauseMenu.background,
            (self.pauseMenu.x - self.margin, self.pauseMenu.y - self.margin))
        self.pauseMenu.draw(screen, self.font.text)

    def drawLevelMenu(self, screen):
        titleTop = 60
        titleLeft = 30
        drawText(screen, titleLeft, titleTop,
            text="Level select", font=self.font.text, anchor="nw")
        self.levelMenu.draw(screen, self.font.text)

    def drawWin(self, screen):
        space = 24
        screen.blit(self.pauseMenu.background,
            (self.pauseMenu.x - self.margin, self.pauseMenu.y - self.margin))
        drawText(screen, self.pauseMenu.x, self.pauseMenu.y,
            text="Congratulations!", font=self.font.text, anchor="nw")
        drawText(screen, self.pauseMenu.x, self.pauseMenu.y + space,
            text="Press Enter...", font=self.font.text, anchor="nw")

    def drawHelp(self, screen):
        top = 50
        left = 40
        drawText(screen, left, top,
            text="Instructions", font=self.font.text, anchor="nw")
        top = 80
        lineHeight = 24
        for line, text in enumerate(self.helpText):
            drawText(screen, left, top + line*lineHeight,
                text=text, font=self.font.text, anchor="nw")
        top = 350
        drawText(screen, left, top, text="Press Escape now for the main menu",
            font=self.font.text, anchor="nw")

    def redrawAll(self, screen):
        if self.mode == "menu":
            self.drawMenu(screen)
        elif self.mode == "play":
            self.drawGame(screen)
        elif self.mode == "pause":
            self.drawGame(screen)
            self.drawPause(screen)
        elif self.mode == "levelMenu":
            self.drawLevelMenu(screen)
        elif self.mode == "win":
            self.drawGame(screen)
            self.drawWin(screen)
        elif self.mode == "help":
            self.drawHelp(screen)

    def playLevel(self, level="1"):
        self.mode = "play"
        self.initGame(level)

    def doMainMenu(self):
        self.mode = "menu"
        self.menu = self.mainMenu

    def doLevelMenu(self):
        self.mode = "levelMenu"
        self.initLevelMenu()
        self.menu = self.levelMenu

    def doHelp(self):
        self.mode = "help"

    def toggleSound(self):
        if Terrain.soundsOn:
            Terrain.soundsOn = False
            return "sound off"
        else:
            Terrain.soundsOn = True
            return "sound on"

    def initHelp(self):
        self.helpText = ["Use Left/Right arrow keys to move.",
            "Avoid pits and spikes.",
            "Hold both Left/Right arrow keys to wall jump!",
            "Cannons make you fly. Hit the opposite arrow key to stop flying.",
            "Press Escape in game to pause or change settings."]
示例#7
0
class Orchestra:
    SAMPLE_RATE = 44100
    BYTES_PER_SAMPLE = 2 # mpg123, used by predecode, outputs 16-bit PCM mono
    PLAYABLE_FORMATS = ['mp3', 'flac', 'wav', 'm4b']
    JACK = "jack"
    SSR = "ssr"

    @staticmethod
    def add_parser_arguments(parser):
        parser.add_argument("--rt", action="store_true", dest="realtime")
        parser.add_argument("-t", "--torrent", dest="torrentname", default="")
        parser.add_argument("-z", "--timefactor", dest="timefactor", type=float, default=1)
        parser.add_argument("--start", dest="start_time", type=float, default=0)
        parser.add_argument("-q", "--quiet", action="store_true", dest="quiet")
        parser.add_argument("--pretend-sequential", action="store_true", dest="pretend_sequential")
        parser.add_argument("--gui", action="store_true", dest="gui_enabled")
        parser.add_argument("--fast-forward", action="store_true", dest="ff")
        parser.add_argument("--fast-forward-to-start", action="store_true", dest="ff_to_start")
        parser.add_argument("--quit-at-end", action="store_true", dest="quit_at_end")
        parser.add_argument("--loop", dest="loop", action="store_true")
        parser.add_argument("--max-pause-within-segment", type=float)
        parser.add_argument("--max-segment-duration", type=float)
        parser.add_argument("--looped-duration", dest="looped_duration", type=float)
        parser.add_argument("-o", "--output", dest="output", type=str, default=Orchestra.JACK)
        parser.add_argument("--include-non-playable", action="store_true")
        parser.add_argument("-f", "--file", dest="selected_files", type=int, nargs="+")
        parser.add_argument("--title", type=str, default="")
        parser.add_argument("--pretend-audio", dest="pretend_audio_filename")
        parser.add_argument("--capture-audio")
        parser.add_argument("--leading-pause", type=float, default=0)

    _extension_re = re.compile('\.(\w+)$')

    def __init__(self, server, sessiondir, tr_log, options):
        self.server = server
        self.options = options
        self.sessiondir = sessiondir
        self.tr_log = tr_log
        self.realtime = options.realtime
        self.timefactor = options.timefactor
        self.quiet = options.quiet
        self._loop = options.loop
        self.looped_duration = options.looped_duration
        self.output = options.output
        self.include_non_playable = options.include_non_playable
        self._leading_pause = options.leading_pause

        if server.options.locate_peers:
            self._peer_location = {}
            for peeraddr in tr_log.peers:
                self._peer_location[peeraddr] = server.ip_locator.locate(peeraddr)
            self._peers_center_location_x = self._get_peers_center_location_x()

        if options.pretend_audio_filename:
            self._pretended_file = self._fileinfo_for_pretended_audio_file()
            self._pretended_file["duration"] = self._get_file_duration(self._pretended_file)
            self._pretended_files = [self._pretended_file]
            self._files_to_play = self._pretended_files
        else:
            self._files_to_play = self.tr_log.files

        self.predecode = server.options.predecode
        if self.predecode:
            predecoder = Predecoder(
                tr_log.files, sample_rate=self.SAMPLE_RATE, location=tr_log.file_location)
            predecoder.decode(server.options.force_predecode)

            if options.pretend_audio_filename:
                predecoder = Predecoder(
                    self._pretended_files, sample_rate=self.SAMPLE_RATE)
                predecoder.decode(server.options.force_predecode)

        if options.selected_files:
            tr_log.select_files(options.selected_files)

        self.playback_enabled = True
        self.fast_forwarding = False
        self.gui = None
        self._check_which_files_are_audio()

        self._player_class = WavPlayer
        self.players = []
        self._player_for_peer = dict()

        self._prepare_playable_files()
        self.stopwatch = Stopwatch()
        self.playable_chunks = self._filter_playable_chunks(tr_log, tr_log.chunks)

        if self.include_non_playable:
            self.chunks = tr_log.chunks
            self._num_selected_files = len(self.tr_log.files)
        else:
            self.chunks = self.playable_chunks
            self._num_selected_files = self._num_playable_files
        logger.debug("total num chunks: %s" % len(tr_log.chunks))
        logger.debug("num playable chunks: %s" % len(self.playable_chunks))
        logger.debug("num selected chunks: %s" % len(self.chunks))

        self.score = self._interpret_chunks_to_score(tr_log, self.playable_chunks, options)
        self.estimated_duration = self._estimated_playback_duration(self.score, options)
        print "playback duration: %s" % datetime.timedelta(seconds=self.estimated_duration)
        self._chunks_by_id = {}
        self.segments_by_id = {}
        self._playing = False
        self._quitting = False
        self._was_stopped = False
        self.space = Space()

        self._scheduler_queue = Queue.Queue()
        self.scheduler = sched.scheduler(time.time, time.sleep)
        self._run_scheduler_thread()

        if self.output == self.SSR:
            self.ssr = SsrControl()
            self._warned_about_max_sources = False

    def init_playback(self):
        if self.server.options.no_synth:
            self.synth = None
        else:
            from synth_controller import SynthController
            self.synth = SynthController(logger)
            self.synth.launch_engine(self.server.options.sc_mode)
            self.synth.connect(self.synth.lang_port)
            self.synth.subscribe_to_info()
            if self.options.capture_audio:
                self._load_sounds()
                self._start_capture_audio()
            self._tell_visualizers("/synth_address", self.synth.lang_port)

            if self.output == self.SSR:
                self.ssr.run()

        if not self.options.capture_audio:
            self._load_sounds()

        self._log_time_for_last_handled_event = 0
        if self.options.ff_to_start:
            self._ff_to_time = self.options.start_time
            self.set_time_cursor(0)
        else:
            self._ff_to_time = None
            self.set_time_cursor(self.options.start_time)

    def _start_capture_audio(self):
        self._audio_capture_filename = self.options.capture_audio
        if os.path.exists(self._audio_capture_filename):
            os.remove(self._audio_capture_filename)
        self._audio_capture_process = subprocess.Popen(
            ["./jack_capture/jack_capture", "-f", self._audio_capture_filename, "-d", "-1",
             "-B", "65536",
             "SuperCollider:out_1", "SuperCollider:out_2"],
            shell=False,
            stdout=subprocess.PIPE)
        self._wait_until_audio_capture_started()
    
    def _wait_until_audio_capture_started(self):
        print "waiting for audio capture to start"
        while True:
            line = self._audio_capture_process.stdout.readline().rstrip("\r\n")
            m = re.match('^audio capture started at (.*)$', line)
            if m:
                audio_capture_start_time = float(m.group(1))
                self._tell_visualizers("/audio_captured_started", str(audio_capture_start_time))
                print "audio capture started"
                return

    @classmethod
    def _estimated_playback_duration(cls, score, options):
        last_segment = score[-1]
        return last_segment["onset"] / options.timefactor + last_segment["duration"]

    @classmethod
    def _interpret_chunks_to_score(cls, tr_log, chunks, options):
        score = Interpreter(options.max_pause_within_segment,
                            options.max_segment_duration).interpret(
            chunks, tr_log.files)
        for segment in score:
            segment["duration"] /= options.timefactor
        return score

    @classmethod
    def _filter_playable_chunks(cls, tr_log, chunks):
        return filter(lambda chunk: (cls._chunk_is_playable(tr_log, chunk)),
                      chunks)

    @classmethod
    def _chunk_is_playable(cls, tr_log, chunk):
        file_info = tr_log.files[chunk["filenum"]]
        return file_info["playable_file_index"] != -1

    def _run_scheduler_thread(self):
        self._scheduler_thread = threading.Thread(target=self._process_scheduled_events)
        self._scheduler_thread.daemon = True
        self._scheduler_thread.start()

    def _process_scheduled_events(self):
        while not self._quitting:
            while True:
                try:
                    delay, priority, action, arguments = self._scheduler_queue.get(True, 0.01)
                except Queue.Empty:
                    break
                self.scheduler.enter(delay, priority, action, arguments)
            self.scheduler.run()

    def _handle_visualizing_message(self, path, args, types, src, data):
        segment_id = args[0]
        segment = self.segments_by_id[segment_id]
        logger.debug("visualizing segment %s" % segment)
        player = self.get_player_for_segment(segment)
        self._ask_synth_to_play_segment(segment, channel=0, pan=player.spatial_position.pan)

    def _ask_synth_to_play_segment(self, segment, channel, pan):
        if self.synth:
            logger.debug("asking synth to play %s" % segment)
            file_info = self.tr_log.files[segment["filenum"]]

            if self.output == self.SSR:
                segment["sound_source_id"] = self.ssr.allocate_source()
                if segment["sound_source_id"] and not self._warned_about_max_sources:
                    channel = segment["sound_source_id"] - 1
                    pan = None
                else:
                    print "WARNING: max sources exceeded, skipping segment playback (this warning will not be repeated)"
                    self._warned_about_max_sources = True
                    return

            self.synth.play_segment(
                segment["id"],
                segment["audio_filenum"],
                segment["relative_start_time_in_file"],
                segment["relative_end_time_in_file"],
                segment["duration"],
                self.looped_duration,            
                channel,
                pan)
        self._scheduler_queue.put(
            (segment["playback_duration"], 1, self.stopped_playing, [segment]))

    def _check_which_files_are_audio(self):
        for file_info in self.tr_log.files:
            file_info["is_audio"] = self._has_audio_extension(file_info["name"])

    @staticmethod
    def _has_audio_extension(filename):
        return Orchestra._extension(filename) in Orchestra.PLAYABLE_FORMATS

    @staticmethod
    def _extension(filename):
        m = Orchestra._extension_re.search(filename)
        if m:
            return m.group(1).lower()

    def _prepare_playable_files(self):
        if self.predecode:
            self._num_playable_files = self._get_wav_files_info(
                self.tr_log, self.include_non_playable)
        else:
            raise Exception("playing wav without precoding is not supported")

    def _load_sounds(self):
        if self.synth:
            print "loading sounds"
            for filenum in range(len(self._files_to_play)):
                file_info = self._files_to_play[filenum]
                if file_info["playable_file_index"] != -1:
                    logger.info("load_sound(%s)" % file_info["decoded_name"])
                    result = self._load_sound_stubbornly(filenum, file_info["decoded_name"])
                    logger.info("load_sound result: %s" % result)
            print "OK"

    def _load_sound_stubbornly(self, filenum, filename):
        while True:
            result = self.synth.load_sound(filenum, filename)
            if result > 0:
                return result
            else:
                warn(logger, "synth returned %s - retrying soon" % result)
                time.sleep(1.0)

    @classmethod
    def _get_wav_files_info(cls, tr_log, include_non_playable=False):
        playable_file_index = 0
        for filenum in range(len(tr_log.files)):
            file_info = tr_log.files[filenum]
            file_info["playable_file_index"] = -1

            if "decoded_name" in file_info:
                file_info["duration"] = cls._get_file_duration(file_info)
                if file_info["duration"] > 0:
                    file_info["playable_file_index"] = playable_file_index
                    logger.debug("duration for %r: %r\n" %
                                      (file_info["name"], file_info["duration"]))
                    playable_file_index += 1

            if include_non_playable:
                file_info["index"] = filenum
            else:
                file_info["index"] = file_info["playable_file_index"]
        return playable_file_index

    @classmethod
    def _get_file_duration(cls, file_info):
        if "decoded_name" in file_info:
            statinfo = os.stat(file_info["decoded_name"])
            wav_header_size = 44
            return float((statinfo.st_size - wav_header_size) / cls.BYTES_PER_SAMPLE) / cls.SAMPLE_RATE

    def get_current_log_time(self):
        if self.fast_forwarding:
            return self._log_time_for_last_handled_event
        else:
            return self.log_time_played_from + self.stopwatch.get_elapsed_time() * self.timefactor

    def play_non_realtime(self, quit_on_end=False):
        logger.info("entering play_non_realtime")
        self._was_stopped = False
        self._num_finished_visualizers = 0
        if self._loop:
            while True:
                self._play_until_end()
                if not self._was_stopped:
                    self._wait_for_visualizers_to_finish()
                self.set_time_cursor(0)
        else:
            self._play_until_end()
            if not self._was_stopped:
                self._wait_for_visualizers_to_finish()
            if quit_on_end:
                self._quit()
        logger.info("leaving play_non_realtime")

    def _quit(self):
        if self.options.capture_audio:
            self._audio_capture_process.kill()
        self._quitting = True

    def _play_until_end(self):
        logger.info("entering _play_until_end")
        self._playing = True
        self.stopwatch.start()
        time.sleep(self._leading_pause)
        no_more_events = False
        while self._playing and not no_more_events:
            event = self._get_next_chunk_or_segment()
            if event:
                self._handle_event(event)
            else:
                no_more_events = True
        logger.info("leaving _play_until_end")

    def _get_next_chunk_or_segment(self):
        logger.debug("chunk index = %d, segment index = %d" % (
                self.current_chunk_index, self.current_segment_index))
        chunk = self._get_next_chunk()
        segment = self._get_next_segment()
        logger.debug("next chunk: %s" % chunk)
        logger.debug("next segment: %s" % segment)
        if chunk and segment:
            return self._choose_nearest_chunk_or_segment(chunk, segment)
        elif chunk:
            return {"type": "chunk",
                    "chunk": chunk}
        elif segment:
            return {"type": "segment",
                    "segment": segment}
        else:
            return None

    def _get_next_chunk(self):
        if self.current_chunk_index < len(self.chunks):
            return self.chunks[self.current_chunk_index]

    def _get_next_segment(self):
        if len(self.score) == 0:
            return None
        elif self.current_segment_index < len(self.score):
            return self.score[self.current_segment_index]

    def _handle_event(self, event):
        if event["type"] == "chunk":
            self.handle_chunk(event["chunk"])
            self.current_chunk_index += 1
        elif event["type"] == "segment":
            self.handle_segment(event["segment"])
            self.current_segment_index += 1
        else:
            raise Exception("unexpected event %s" % event)

    def _choose_nearest_chunk_or_segment(self, chunk, segment):
        if chunk["t"] < segment["onset"]:
            return {"type": "chunk",
                    "chunk": chunk}
        else:
            return {"type": "segment",
                    "segment": segment}
            
    def stop(self):
        # stop_all disabled as it also deletes ~reverb
        # if self.synth:
        #     self.synth.stop_all()
        self._was_stopped = True
        self._playing = False
        self.log_time_played_from = self.get_current_log_time()
        self.stopwatch.stop()

    def handle_segment(self, segment):
        logger.debug("handling segment %s" % segment)
        player = self.get_player_for_segment(segment)
        if not player:
            logger.debug("get_player_for_segment returned None - skipping playback")

        if self.fast_forwarding:
            self._stop_ff_if_necessary()
        else:
            now = self.get_current_log_time()
            time_margin = segment["onset"] - now
            logger.debug("time_margin=%f-%f=%f" % (segment["onset"], now, time_margin))
            if not self.realtime and time_margin > 0:
                sleep_time = time_margin
                logger.debug("sleeping %f" % sleep_time)
                time.sleep(sleep_time)
        if player:
            logger.debug("player.enabled=%s" % player.enabled)
        if player and player.enabled:
            player.play(segment, pan=0.5)
        self._log_time_for_last_handled_event = segment["onset"]

    def handle_chunk(self, chunk):
        logger.debug("handling chunk %s" % chunk)
        player = self.get_player_for_chunk(chunk)
        logger.debug("get_player_for_chunk returned %s" % player)

        if self.fast_forwarding:
            self._stop_ff_if_necessary()
        else:
            now = self.get_current_log_time()
            time_margin = chunk["t"] - now
            logger.debug("time_margin=%f-%f=%f" % (chunk["t"], now, time_margin))
            if not self.realtime and time_margin > 0:
                sleep_time = time_margin
                logger.debug("sleeping %f" % sleep_time)
                time.sleep(sleep_time)
        if player:
            logger.debug("player.enabled=%s" % player.enabled)
        if player and player.enabled:
            player.visualize(chunk)
        self._log_time_for_last_handled_event = chunk["t"]

    def _stop_ff_if_necessary(self):
        if self._ff_to_time is not None and \
                self._log_time_for_last_handled_event >= self._ff_to_time:
            self._ff_to_time = None
            self.fast_forwarding = False
            self.set_time_cursor(self.log_time_played_from)

    def highlight_segment(self, segment):
        if self.gui:
            self.gui.highlight_segment(segment)

    def visualize_chunk(self, chunk, player):
        if len(self.visualizers) > 0:
            self._inform_visualizers_about_peer(player)
            file_info = self.tr_log.files[chunk["filenum"]]
            self._chunks_by_id[chunk["id"]] = chunk
            self._tell_visualizers(
                "/chunk",
                chunk["id"],
                chunk["begin"],
                chunk["end"] - chunk["begin"],
                file_info["index"],
                player.id,
                chunk["t"])

    def visualize_segment(self, segment, player):
        if len(self.visualizers) > 0:
            self._inform_visualizers_about_peer(player)
            file_info = self.tr_log.files[segment["filenum"]]
            self.segments_by_id[segment["id"]] = segment
            self._tell_visualizers(
                "/segment",
                segment["id"],
                segment["begin"],
                segment["end"] - segment["begin"],
                file_info["index"],
                player.id,
                segment["t"],
                segment["playback_duration"])
        else:
            self._ask_synth_to_play_segment(segment, channel=0, pan=0.5)

    def stopped_playing(self, segment):
        logger.debug("stopped segment %s" % segment)
        if self.gui:
            self.gui.unhighlight_segment(segment)
        if self.output == self.SSR and segment["sound_source_id"]:
            self.ssr.free_source(segment["sound_source_id"])

    def play_segment(self, segment, player):
        self.segments_by_id[segment["id"]] = segment

        if self.looped_duration:
            segment["playback_duration"] = self.looped_duration
        else:
            segment["playback_duration"] = segment["duration"]

        self.visualize_segment(segment, player)

    def _send_torrent_info_to_uninformed_visualizers(self):
        for visualizer in self.visualizers:
            if not visualizer.informed_about_torrent:
                self._send_torrent_info_to_visualizer(visualizer)

    def _inform_visualizers_about_peer(self, player):
        for visualizer in self.visualizers:
            if player.id not in visualizer.informed_about_peer:
                if visualizer.send(
                    "/peer", player.id, player.addr, player.spatial_position.bearing,
                    player.spatial_position.pan, player.location_str):
                    visualizer.informed_about_peer[player.id] = True

    def _send_torrent_info_to_visualizer(self, visualizer):
        if not visualizer.send(
            "/torrent",
            self._num_selected_files,
            self.tr_log.lastchunktime(),
            self.tr_log.total_file_size(),
            len(self.chunks),
            len(self.score),
            self.options.title):
            return
        for filenum in range(len(self.tr_log.files)):
            file_info = self.tr_log.files[filenum]
            if self.include_non_playable or file_info["playable_file_index"] != -1:
                if not visualizer.send(
                    "/file",
                    file_info["index"],
                    file_info["offset"],
                    file_info["length"]):
                    return
        visualizer.informed_about_torrent = True

    def get_player_for_chunk(self, chunk):
        try:
            return chunk["player"]
        except KeyError:
            peer_player = self.get_player_for_peer(chunk["peeraddr"])
            chunk["player"] = peer_player
            return peer_player

    def get_player_for_segment(self, segment):
        try:
            return segment["player"]
        except KeyError:
            peer_player = self.get_player_for_peer(segment["peeraddr"])
            segment["player"] = peer_player
            return peer_player

    def get_player_for_peer(self, peeraddr):
        peer_player = None
        try:
            peer_player = self._player_for_peer[peeraddr]
        except KeyError:
            peer_player = self._create_player(peeraddr)
            self.players.append(peer_player)
            self._player_for_peer[peeraddr] = peer_player
        return peer_player

    def _create_player(self, addr):
        count = len(self.players)
        logger.debug("creating player number %d" % count)
        player = self._player_class(self, count)
        player.addr = addr
        if self.server.options.locate_peers and self._peer_location[addr] is not None:
            x, y, place_name = self._peer_location[addr]
            if place_name:
                place_name = place_name.encode("unicode_escape")
            else:
                place_name = ""
            player.location_str = "%s,%s,%s" % (x, y, place_name)

            if x < self._peers_center_location_x:
                player.spatial_position.pan = -1.0
            else:
                player.spatial_position.pan = 1.0
        else:
            player.location_str = ""
        return player

    def set_time_cursor(self, log_time):
        assert not self.realtime
        logger.debug("setting time cursor at %f" % log_time)
        self.log_time_played_from = log_time
        if self._playing:
            self.stopwatch.restart()
        self.current_chunk_index = self._get_current_chunk_index()
        self.current_segment_index = self._get_current_segment_index()

    def _get_current_chunk_index(self):
        index = 0
        next_to_last_index = len(self.chunks) - 2
        while index < next_to_last_index:
            if self.chunks[index+1]["t"] >= self.log_time_played_from:
                return index
            index += 1
        return len(self.chunks) - 1

    def _get_current_segment_index(self):
        index = 0
        next_to_last_index = len(self.score) - 2
        while index < next_to_last_index:
            if self.score[index+1]["onset"] >= self.log_time_played_from:
                return index
            index += 1
        return len(self.score) - 1

    def _handle_set_listener_position(self, path, args, types, src, data):
        if self.output == self.SSR:
            x, y = args
            self.ssr.set_listener_position(x, y)

    def _handle_set_listener_orientation(self, path, args, types, src, data):
        if self.output == self.SSR:
            orientation = args[0]
            self.ssr.set_listener_orientation(orientation)

    def _handle_place_segment(self, path, args, types, src, data):
        segment_id, x, y, duration = args
        if self.output == self.SSR:
            segment = self.segments_by_id[segment_id]
            sound_source_id = segment["sound_source_id"]
            if sound_source_id is not None:
                self.ssr.place_source(sound_source_id, x, y, duration)
        else:
            pan = self._spatial_position_to_stereo_pan(x, y)
            if self.synth:
                self.synth.pan(segment_id, pan)

    def _handle_enable_smooth_movement(self, path, args, types, src, data):
        pass # OBSOLETE after smooth movement made default

    def _handle_start_segment_movement_from_peer(self, path, args, types, src, data):
        segment_id, duration = args
        if self.output == self.SSR:
            segment = self.segments_by_id[segment_id]
            sound_source_id = segment["sound_source_id"]
            if sound_source_id is not None:
                player = self.get_player_for_segment(segment)
                self.ssr.start_source_movement(
                    sound_source_id, player.trajectory, duration)

    def _spatial_position_to_stereo_pan(self, x, y):
        # compare rectangular_visualizer.Visualizer.pan_segment
        # NOTE: assumes default listener position and orientation!
        return float(x) / 5 + 0.5

    def reset(self):
        if self.synth:
            self.synth.stop_engine()
        self._tell_visualizers("/reset")
        for visualizer in self.visualizers:
            visualizer.informed_about_torrent = False
            visualizer.informed_about_peer = {}

    def _tell_visualizers(self, *args):
        self._send_torrent_info_to_uninformed_visualizers()
        self.server._tell_visualizers(*args)

    def _fileinfo_for_pretended_audio_file(self):
        return {"offset": 0,
                "length": os.stat(self.options.pretend_audio_filename).st_size,
                "name": self.options.pretend_audio_filename,
                "playable_file_index": 0}

    def _handle_finished(self, path, args, types, src, data):
        self._num_finished_visualizers += 1

    def _wait_for_visualizers_to_finish(self):
        while self._num_finished_visualizers < len(self.visualizers):
            time.sleep(0.1)

    def _get_peers_center_location_x(self):
        if len(self._peer_location) <= 1:
            return 0
        else:
            sorted_xs = sorted([x for x,y,location_str in self._peer_location.values()])
            center_index = int((len(self._peer_location)-1) / 2)
            return float(sorted_xs[center_index] + sorted_xs[center_index+1]) / 2