Example #1
0
    def __init__(self, schid, cid, password, rootdir):
        """
        Instantiates a new object.
        @param schid: the id of the serverconnection handler
        @type schid: int
        @param cid: the id of the channel
        @type cid: int
        @param password: the password of the channel
        @type password: str
        @param rootdir: the root download directory
        @type rootdir: str
        """
        super().__init__()

        self.schid = schid
        self.cid = cid
        self.password = password
        self.rootdir = rootdir

        self.collectionFinished = Signal()
        self.collectionError = Signal()

        self.queue = {}
        self.files = {}

        PluginHost.registerCallbackProxy(self)
Example #2
0
    def __init__(self, schid, cid, password, parent=None, *, readonly=False):
        super(QAbstractItemModel, self).__init__(parent)

        self.schid = schid
        self.cid = cid
        self.password = password

        self.readonly = readonly

        self.pathChanged = Signal()
        self.error = Signal()

        self._path = None
        self.newpath = None
        self.files = []
        self.newfiles = []

        self.retcode = None
        self.renretcode = None
        self.renfile = ()

        self.titles = [self._tr("Name"), self._tr("Size"), self._tr("Type"),
                       self._tr("Last Changed")]

        PluginHost.registerCallbackProxy(self)
Example #3
0
    def __init__(self, schid, cid, password, parent=None, *, readonly=False):
        super(QAbstractItemModel, self).__init__(parent)

        self.schid = schid
        self.cid = cid
        self.password = password

        self.readonly = readonly

        self.pathChanged = Signal()
        self.error = Signal()

        self._path = None
        self.newpath = None
        self.files = []
        self.newfiles = []

        self.retcode = None
        self.renretcode = None
        self.renfile = ()

        self.titles = [
            self._tr("Name"),
            self._tr("Size"),
            self._tr("Type"),
            self._tr("Last Changed")
        ]

        PluginHost.registerCallbackProxy(self)
Example #4
0
    def __init__(self, schid, cid, password, rootdir):
        """
        Instantiates a new object.
        @param schid: the id of the serverconnection handler
        @type schid: int
        @param cid: the id of the channel
        @type cid: int
        @param password: the password of the channel
        @type password: str
        @param rootdir: the root download directory
        @type rootdir: str
        """
        super().__init__()

        self.schid = schid
        self.cid = cid
        self.password = password
        self.rootdir = rootdir

        self.collectionFinished = Signal()
        self.collectionError = Signal()

        self.queue = {}
        self.files = {}

        PluginHost.registerCallbackProxy(self)
Example #5
0
    def setup_method(self, method):
        self.signal_a = Signal(threadsafe=True)
        self.signal_b = Signal(args=['foo'])

        self.slot_a = mock.Mock(spec=lambda **kwargs: None)
        self.slot_a.return_value = None
        self.slot_b = mock.Mock(spec=lambda **kwargs: None)
        self.slot_b.return_value = None
Example #6
0
    def setup_method(self, method):
        self.signal = Signal(threadsafe=False)
        self.seen_exception = False

        def failing_slot(**args):
            raise MyTestError('die!')

        self.signal.connect(failing_slot)
Example #7
0
    def setup_method(self, method):
        self.signal_a = Signal(threadsafe=True)
        self.signal_b = Signal(args=['foo'])

        self.slot_a = mock.Mock(spec=lambda **kwargs: None)
        self.slot_a.return_value = None
        self.slot_b = mock.Mock(spec=lambda **kwargs: None)
        self.slot_b.return_value = None
Example #8
0
    def __init__(self, x, y, width, height, text='Button', text_size=25):
        self.button_down = False
        self.clicked = Signal()

        super().__init__(x, y, width, height, text=text, text_size=text_size)
        self.hold_function = self.hold
        self.release_function = self.release

        self.redraw()
Example #9
0
 def __init__(self, num, height):
     """
     height = ft
     """
     self.__num = num
     self.__height = height
     self.__people = [] # people currently on the floor (not necessarily waiting for elevators)
     self.__queue = [] # people requested the elevator and currently waiting for one
     self.__signal_elevator_requested = Signal(args=['from_floor', 'to_floor'])
     self.__signal_people_boarded = Signal(args=['elevator_id', 'floor_num', 'people'])
Example #10
0
    def setup_method(self, method):
        class MyObject(object):
            def __init__(self):
                self.called = False

            def slot(self, **kwargs):
                self.called = True

        self.obj_ref = MyObject()
        self.slot = Slot(self.obj_ref.slot, weak=True)
        self.signal = Signal()
        self.signal.connect(self.slot)
Example #11
0
    def __init__(self, floors=[], elevators=[]):
        self.__floors = floors

        # absolute coords
        self.__floor_coords = [0] * len(self.__floors)
        for i in range(1, len(self.__floor_coords)):
            self.__floor_coords[i] += self.__floor_coords[i-1] + self.__floors[i-1].height

        self.__elevators = {e.id : e for e in elevators}
        # indicates a command to an elevator to change velocity
        self.__signal_change_velocity = Signal(args=['elevator_id', 'velocity'])
        self.__signal_stop = Signal(args=['elevator_id'])
Example #12
0
    def __init__(self, x, y, width, height, texts, text_size=25):
        super().__init__(x, y, width, height)
        self.list_selected = Signal(args=['text'])

        self.font = pygame.font.SysFont("None", text_size)
        self.texts = texts
        self.text_rects = []
        self.y_offset = 0
        self.selected = -1
        self.outline_thickness = 3
        self.selected_colour = (255, 0, 0)
        self.max_offset = 0

        self.replace_list(texts)
        self.release_function = self.check_click_pos
Example #13
0
class Controller(object):
    def __init__(self, floors=[], elevators=[]):
        self.__floors = floors

        # absolute coords
        self.__floor_coords = [0] * len(self.__floors)
        for i in range(1, len(self.__floor_coords)):
            self.__floor_coords[i] += self.__floor_coords[i-1] + self.__floors[i-1].height

        self.__elevators = {e.id : e for e in elevators}
        # indicates a command to an elevator to change velocity
        self.__signal_change_velocity = Signal(args=['elevator_id', 'velocity'])
        self.__signal_stop = Signal(args=['elevator_id'])

    @property
    def signal_change_velocity(self):
        return self.__signal_change_velocity

    @property
    def signal_stop(self):
        return self.__signal_stop

    def elevator_requested(self, from_floor, to_floor, **kwargs):
        print "Controller::elevator_requested>", from_floor, '->', to_floor
        for e in self.__elevators.values():
            if e.velocity == 0:
                self.signal_change_velocity.emit(elevator_id=e.id, velocity=2)
        pass

    def elevator_position_changed(self, elevator_id, x, y, **kwargs):
        FORCED_STOP_DIST = .33 # 10cm range for allowed stop
        # if about to hit ground/roof -> stop
        if abs(y - self.__floor_coords[-1]) < FORCED_STOP_DIST and self.__elevators[elevator_id].velocity > 0:
            self.__signal_stop.emit(elevator_id=elevator_id, floor_num=len(self.__floors))
        elif abs(y - self.__floor_coords[0]) < FORCED_STOP_DIST and self.__elevators[elevator_id].velocity < 0:
            self.__signal_stop.emit(elevator_id=elevator_id, floor_num=1)
        else:
            pass
            # if near the destination floor -> stop

#        assert y <= self.__floor_coords[-1], 'elevator hit the roof'

#        print y, self.__floor_coords
        # calculate position, and if at floor, and floor is in dest list, then signal stop
        pass

    def elevator_door_closed(self, **kwargs):
        pass
Example #14
0
class TestSignalConnect(object):
    def setup_method(self, method):
        self.signal = Signal()

    def test_connect_with_kwargs(self):
        def cb(**kwargs):
            pass

        self.signal.connect(cb)

    def test_connect_without_kwargs(self):
        def cb():
            pass

        with pytest.raises(SlotMustAcceptKeywords):
            self.signal.connect(cb)
Example #15
0
class CommandConsole(cmd.Cmd):
    
    def __init__(self):
        cmd.Cmd.__init__(self)
        self.save_command = Signal(['output_dir'])
        self.quit_command = Signal()
        
    def do_save(self, line):
        self.save_command.emit(output_dir=line)
        
    def do_EOF(self, line):
        return self.do_quit(line)

    def do_quit(self, line):
        self.quit_command.emit()
        return True
Example #16
0
class TestSignalConnect(object):
    def setup_method(self, method):
        self.signal = Signal()

    def test_connect_with_kwargs(self):
        def cb(**kwargs):
            pass

        self.signal.connect(cb)

    def test_connect_without_kwargs(self):
        def cb():
            pass

        with pytest.raises(SlotMustAcceptKeywords):
            self.signal.connect(cb)
Example #17
0
class zmqReceiverLoggerThread(threading.Thread):

    data_received = Signal(args=[], threadsafe=True)

    def __init__(self, port=5556):
        super(zmqReceiverLoggerThread, self).__init__()
        self.context = zmq.Context()
        self.socket = self.context.socket(zmq.PULL)
        self.port = port
        self.interrupt = False

    def run(self):
        self.socket.bind("tcp://*:%s" % (self.port))

        while True:
            try:
                data = self.socket.recv_pyobj()
                if len(data) > 0:
                    print data
                    self.data_received.emit(args=data)
            except (KeyboardInterrupt, SystemExit):
                self.socket.close()
                self.context.term()
            else:
                pass
Example #18
0
    def setup_method(self, method):
        self.signal = Signal(threadsafe=False)
        self.seen_exception = False

        def failing_slot(**args):
            raise MyTestError('die!')

        self.signal.connect(failing_slot)
Example #19
0
class TestException(object):
    def setup_method(self, method):
        self.signal = Signal(threadsafe=False)
        self.seen_exception = False

        def failing_slot(**args):
            raise MyTestError('die!')

        self.signal.connect(failing_slot)

    def test_emit_exception(self):
        try:
            self.signal.emit()
        except MyTestError:
            self.seen_exception = True

        assert self.seen_exception
Example #20
0
class TestException(object):
    def setup_method(self, method):
        self.signal = Signal(threadsafe=False)
        self.seen_exception = False

        def failing_slot(**args):
            raise MyTestError('die!')

        self.signal.connect(failing_slot)

    def test_emit_exception(self):
        try:
            self.signal.emit()
        except MyTestError:
            self.seen_exception = True

        assert self.seen_exception
Example #21
0
    def test_semaphore(self, inspect):
        slot = mock.Mock()
        slot.side_effect = lambda **k: time.sleep(.3)

        signal = Signal('tost')
        signal.connect(slot)

        x = Task.get_or_create(signal, dict(some_kwarg='foo'))
        y = Task.get_or_create(signal, dict(some_kwarg='foo'))

        eventlet.spawn(x)
        time.sleep(.1)
        eventlet.spawn(y)
        time.sleep(.1)

        assert slot.call_count == 1
        time.sleep(.4)
        assert slot.call_count == 2
Example #22
0
    def test_semaphore(self, inspect):
        slot = mock.Mock()
        slot.side_effect = lambda **k: time.sleep(.3)

        signal = Signal('tost')
        signal.connect(slot)

        x = Task.get_or_create(signal, dict(some_kwarg='foo'))
        y = Task.get_or_create(signal, dict(some_kwarg='foo'))

        eventlet.spawn(x)
        time.sleep(.1)
        eventlet.spawn(y)
        time.sleep(.1)

        assert slot.call_count == 1
        time.sleep(.4)
        assert slot.call_count == 2
Example #23
0
    def __init__(self, result_copy=True, result_deepcopy=True):
        """
        Initialisation.  This should be overridden by subclasses to accept and
        validate the inputs presented for the operation, raising an appropriate
        Exception subclass if the inputs are found to be invalid.

        These should be stored here by the initialisation function as private
        variables in suitably sanitised form.  The core state machine object
        shall then be created and stored before the object is returned to the
        caller.
        """
        # Event object to represent when this operation is "done"
        self._done_evt = Event()

        # Signal emitted when the operation is "done"
        self.done_sig = Signal(name="done", threadsafe=True)

        # Result returned by operation
        self._result = None
        self._result_copy = result_copy
        self._result_deepcopy = result_deepcopy
Example #24
0
class TestWeakMethodSlot(object):
    def setup_method(self, method):
        class MyObject(object):
            def __init__(self):
                self.called = False

            def slot(self, **kwargs):
                self.called = True

        self.obj_ref = MyObject()
        self.slot = Slot(self.obj_ref.slot, weak=True)
        self.signal = Signal()
        self.signal.connect(self.slot)

    def test_alive(self):
        assert self.slot.is_alive

    def test_call(self):
        self.signal.emit(testing=1234)
        assert self.obj_ref.called

    def test_gc(self):
        self.obj_ref = None
        assert not self.slot.is_alive
        self.signal.emit(testing=1234)
Example #25
0
class TestWeakMethodSlot(object):
    def setup_method(self, method):

        class MyObject(object):

            def __init__(self):
                self.called = False

            def slot(self, **kwargs):
                self.called = True

        self.obj_ref = MyObject()
        self.slot = Slot(self.obj_ref.slot, weak=True)
        self.signal = Signal()
        self.signal.connect(self.slot)

    def test_alive(self):
        assert self.slot.is_alive

    def test_call(self):
        self.signal.emit(testing=1234)
        assert self.obj_ref.called

    def test_gc(self):
        self.obj_ref = None
        assert not self.slot.is_alive
        self.signal.emit(testing=1234)
Example #26
0
 def __init__(self, id, size=10, height=10):
     """
     people - list of people currently in the elevator
     size - elevator capacity in terms of people count
     velocity - velocity along y axis
     position - current y coordinate
     door_opening_time - time taken to open doors in seconds
     door_closing_time - time taken to close doors in seconds
     """
     self.__id = id
     self.__people = []
     self.__signal_person_inside = Signal(args=['person'])
     self.__signal_position_change = Signal(args=['elevator_id', 'x', 'y'])
     # direction: True = up, Down = False
     self.__signal_door_opened = Signal(args=['elevator_id', 'direction', 'people_inside', 'available_capacity', 'floor_num'])
     self.__signal_door_closed = Signal(args=['elevator_id', 'people_inside'])
     self.__size = size
     self.__velocity = 0
     self.__position = (0, 0)
     self.__height = height
     self.__door_opening_time = 1.5
     self.__door_closing_time = 2.5
Example #27
0
    def setup_method(self, method):

        class MyObject(object):

            def __init__(self):
                self.called = False

            def slot(self, **kwargs):
                self.called = True

        self.obj_ref = MyObject()
        self.slot = Slot(self.obj_ref.slot, weak=True)
        self.signal = Signal()
        self.signal.connect(self.slot)
Example #28
0
class Button(TextBox):
    def __init__(self, x, y, width, height, text='Button', text_size=25):
        self.button_down = False
        self.clicked = Signal()

        super().__init__(x, y, width, height, text=text, text_size=text_size)
        self.hold_function = self.hold
        self.release_function = self.release

        self.redraw()

    def redraw(self):
        if self.button_down:
            self.background.fill((255, 255, 255))
        else:
            super().redraw()
        #if self.visible:
        outline = (0, 0, self.rect.w, self.rect.h)
        pygame.draw.rect(self.background, self.outline_colour, outline,
                         self.outline_thickness)
        rendered_text = self.font.render(self.text, True,
                                         self.text_colour).convert_alpha()
        rect_center = self.background.get_rect().center
        text_rect = rendered_text.get_rect(center=rect_center)
        self.background.blit(rendered_text, text_rect)

    def hold(self, *args):
        if not self.button_down:
            self.button_down = True
            self.redraw()

    def release(self, *args):
        if self.button_down:
            self.button_down = False
            self.clicked.emit()
            self.redraw()
Example #29
0
    def __init__(self, result_copy=True, result_deepcopy=True):
        """
        Initialisation.  This should be overridden by subclasses to accept and
        validate the inputs presented for the operation, raising an appropriate
        Exception subclass if the inputs are found to be invalid.

        These should be stored here by the initialisation function as private
        variables in suitably sanitised form.  The core state machine object
        shall then be created and stored before the object is returned to the
        caller.
        """
        # Event object to represent when this operation is "done"
        self._done_evt = Event()

        # Signal emitted when the operation is "done"
        self.done_sig = Signal(name='done', threadsafe=True)

        # Result returned by operation
        self._result = None
        self._result_copy = result_copy
        self._result_deepcopy = result_deepcopy
Example #30
0
    def __init__(self, x, y, width, height):
        self.draw_update = Signal()

        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.rect = pygame.rect.Rect(x, y, width, height)
        self.visible = True
        self.clear_colour = (0, 0, 0)
        self.outline_colour = (255, 0, 0)
        self.outline_thickness = 3
        self.text_colour = (255, 255, 255)

        self.background = pygame.Surface((self.width, self.height))
        self.background.fill(self.clear_colour)
        self.background.set_colorkey(self.clear_colour)
        self.background = self.background.convert()

        self.hold_function = None
        self.release_function = None

        self.parent = None
        self.children = None
Example #31
0
    def __init__(self):
        # Init logging facility
        # From : http://sametmax.com/ecrire-des-logs-en-python/
        logger = logging.getLogger()
        logger.setLevel(logging.DEBUG)
        formatter = logging.Formatter("%(asctime)s :: %(levelname)s :: %(message)s")
        file_handler = RotatingFileHandler("api_log.log", "a", 1000000, 1)
        file_handler.setLevel(logging.DEBUG)
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)

        stream_handler = logging.StreamHandler()
        stream_handler.setLevel(logging.DEBUG)
        logger.addHandler(stream_handler)

        # Signals
        self.signal_MCU_state_changed = Signal(args=["alive"])
        self.signal_received_descriptor = Signal(args=["var_id", "var_type", "var_name", "var_writeable", "group_id"])
        self.signal_received_group_descriptor = Signal(args=["group_id", "group_name"])
        self.signal_received_value = Signal(args=["var_id"])

        self.distantio = distantio_protocol()
        self.protocol = Protocol(self.unused)

        # Queue holding received characters to be processed by worker process
        self.input_queue = mp.Queue()
        # Queue holding decoded frames
        self.output_queue = mp.Queue()
        # Conditions for controlling run process
        self.condition_new_rx_data = mp.Event()
        self.condition_new_rx_data.clear()
        self.condition_run_process = mp.Event()
        self.condition_run_process.clear()

        # Worker process for decoding characters
        self.producer_conn, self.consumer_conn = mp.Pipe()
        self.worker = Worker(
            self.input_queue, self.producer_conn, self.condition_new_rx_data, self.condition_run_process
        )
        self.worker.start()

        # Array containing buffers with MCU variables values
        self.variables_values = dict()
        # max size of the buffers
        self.buffer_length = 128
        # Array containing last time each individual variable was updated
        self.last_variables_update = dict()
        # Min delay in seconds between two emit value received signal
        self.emit_signal_delay = 0.1
        self.time_start = time.time()

        # Timer for monitoring MCU alive
        self.mcu_died_delay = 2.0
        self.mcu_alive_timer = threading.Timer(self.mcu_died_delay, self.on_mcu_lost_connection)

        self.variable_list = dict()
        self.connected = False

        self.datalogger = Datalogger()

        # Start MCU timer
        self.mcu_alive_timer = threading.Timer(self.mcu_died_delay, self.on_mcu_lost_connection)
        self.mcu_alive_timer.start()

        logging.info("DistantIO API initialized successfully.")
Example #32
0
class HaystackOperation(object):
    """
    A core state machine object.  This implements the basic interface presented
    for all operations in pyhaystack.
    """
    def __init__(self, result_copy=True, result_deepcopy=True):
        """
        Initialisation.  This should be overridden by subclasses to accept and
        validate the inputs presented for the operation, raising an appropriate
        Exception subclass if the inputs are found to be invalid.

        These should be stored here by the initialisation function as private
        variables in suitably sanitised form.  The core state machine object
        shall then be created and stored before the object is returned to the
        caller.
        """
        # Event object to represent when this operation is "done"
        self._done_evt = Event()

        # Signal emitted when the operation is "done"
        self.done_sig = Signal(name='done', threadsafe=True)

        # Result returned by operation
        self._result = None
        self._result_copy = result_copy
        self._result_deepcopy = result_deepcopy

    def go(self):
        """
        Start processing the operation.  This is called by the caller (so after
        all __init__ functions have executed) in order to begin the asynchronous
        operation.
        """
        # This needs to be implemented in the subclass.
        raise NotImplementedError("To be implemented in subclass %s" \
                % self.__class__.__name__)

    def wait(self, timeout=None):
        """
        Wait for an operation to finish.  This should *NOT* be called in the
        same thread as the thread executing the operation as this will
        deadlock.
        """
        self._done_evt.wait(timeout)

    @property
    def state(self):
        """
        Return the current state machine's state.
        """
        return self._state_machine.current

    @property
    def is_done(self):
        """
        Return true if the operation is complete.
        """
        return self._state_machine.is_finished()

    @property
    def is_failed(self):
        """
        Return true if the result is an Exception.
        """
        return isinstance(self._result, AsynchronousException)

    @property
    def result(self):
        """
        Return the result of the operation or raise its exception.
        Raises NotReadyError if not ready.
        """
        if not self.is_done:
            raise NotReadyError()

        if self.is_failed:
            self._result.reraise()

        if not self._result_copy:
            # Return the original instance (do not copy)
            return self._result
        elif self._result_deepcopy:
            # Return a deep copy
            return deepcopy(self._result)
        else:
            # Return a shallow copy
            return self._result.copy()

    def __repr__(self):
        """
        Return a representation of this object's state.
        """
        if self.is_failed:
            return '<%s failed>' % self.__class__.__name__
        elif self.is_done:
            return '<%s done: %s>' % (self.__class__.__name__, self._result)
        else:
            return '<%s %s>' % (self.__class__.__name__, self.state)

    def _done(self, result):
        """
        Return the result of the operation to any listeners.
        """
        self._result = result
        self._done_evt.set()
        self.done_sig.emit(operation=self)
Example #33
0
class FileCollector(pytson.Translatable):
    """
    Collects all files recursively from TS3 filetransfer directories with their
    corresponding download path.
    Emits a signal collectionFinished with a list of tuples(str, list[File])
    containing the download dir and a list of files.
    The signal collectionError(str, int) is emitted on error with the
    errorstring and the errorcode.
    """

    def __init__(self, schid, cid, password, rootdir):
        """
        Instantiates a new object.
        @param schid: the id of the serverconnection handler
        @type schid: int
        @param cid: the id of the channel
        @type cid: int
        @param password: the password of the channel
        @type password: str
        @param rootdir: the root download directory
        @type rootdir: str
        """
        super().__init__()

        self.schid = schid
        self.cid = cid
        self.password = password
        self.rootdir = rootdir

        self.collectionFinished = Signal()
        self.collectionError = Signal()

        self.queue = {}
        self.files = {}

        PluginHost.registerCallbackProxy(self)

    def __del__(self):
        PluginHost.unregisterCallbackProxy(self)

    def addFiles(self, files):
        """
        Manually adds a list of files to the collection (emitted with the
        rootdir)
        @param files: list of files to emit
        @type files: list(File)
        """
        self.files[self.rootdir] = files

    def collect(self, dirs):
        """
        Starts collecting files from a list of directories
        @param dirs: list of directories
        @type dirs: list(File)
        """
        for d in dirs:
            retcode = ts3lib.createReturnCode()
            self.queue[retcode] = d.fullpath

            err = ts3lib.requestFileList(self.schid, self.cid, self.password,
                                         d.fullpath, retcode)

            if err != ERROR_ok:
                del self.queue[retcode]
                self.collectionError.emit(self._tr("Error requesting "
                                                   "filelist of {dirname}").
                                          format(dirname=d.fullpath), err)

    def onServerErrorEvent(self, schid, errorMessage, error, returnCode,
                           extraMessage):
        if schid != self.schid or returnCode not in self.queue:
            return

        if error not in [ERROR_ok, ERROR_database_empty_result]:
            self.collectionError.emit(self._tr("Error requesting filelist "
                                               "of {dirname}").
                                      format(dirname=self.queue[returnCode]),
                                      error)

        del self.queue[returnCode]

        if not self.queue:
            self.collectionFinished.emit([(k, v)
                                          for k, v in self.files.items()])
            self.files = {}

    def onFileListEvent(self, schid, channelID, path, name, size, datetime,
                        atype, incompletesize, returnCode):
        if (schid != self.schid or self.cid != channelID or
           returnCode not in self.queue):
            return

        downpath = os.path.join(self.rootdir, *splitpath(path)[1:])
        f = File(path, name, size, datetime, atype, incompletesize)

        if f.isDirectory:
            self.collect([f])
        else:
            if downpath in self.files:
                self.files[downpath].append(f)
            else:
                self.files[downpath] = [f]
Example #34
0
class TestSignal(object):
    def setup_method(self, method):
        self.signal_a = Signal(threadsafe=True)
        self.signal_b = Signal(args=['foo'])

        self.slot_a = mock.Mock(spec=lambda **kwargs: None)
        self.slot_a.return_value = None
        self.slot_b = mock.Mock(spec=lambda **kwargs: None)
        self.slot_b.return_value = None

    def test_is_connected(self, inspect):
        self.signal_a.connect(self.slot_a)

        assert self.signal_a.is_connected(self.slot_a)
        assert not self.signal_a.is_connected(self.slot_b)
        assert not self.signal_b.is_connected(self.slot_a)
        assert not self.signal_b.is_connected(self.slot_b)

    def test_emit_one_slot(self, inspect):
        self.signal_a.connect(self.slot_a)

        self.signal_a.emit()

        self.slot_a.assert_called_once_with()
        assert self.slot_b.call_count == 0

    def test_emit_two_slots(self, inspect):
        self.signal_a.connect(self.slot_a)
        self.signal_a.connect(self.slot_b)

        self.signal_a.emit()

        self.slot_a.assert_called_once_with()
        self.slot_b.assert_called_once_with()

    def test_emit_one_slot_with_arguments(self, inspect):
        self.signal_b.connect(self.slot_a)

        self.signal_b.emit(foo='bar')

        self.slot_a.assert_called_once_with(foo='bar')
        assert self.slot_b.call_count == 0

    def test_emit_two_slots_with_arguments(self, inspect):
        self.signal_b.connect(self.slot_a)
        self.signal_b.connect(self.slot_b)

        self.signal_b.emit(foo='bar')

        self.slot_a.assert_called_once_with(foo='bar')
        self.slot_b.assert_called_once_with(foo='bar')

    def test_reconnect_does_not_duplicate(self, inspect):
        self.signal_a.connect(self.slot_a)
        self.signal_a.connect(self.slot_a)
        self.signal_a.emit()

        self.slot_a.assert_called_once_with()

    def test_disconnect_does_not_fail_on_not_connected_slot(self, inspect):
        self.signal_a.disconnect(self.slot_b)
Example #35
0
    def __init__(self, conn):
        self.connecting = Signal()
        self.connected = Signal()
        self.sleeping = Signal()
        self.disconnected = Signal()
        self.reconnecting = Signal()

        '''(remote)'''
        self.typing = Signal()
        '''(remote)'''
        self.typingPaused = Signal()


        '''(remote)'''
        self.available = Signal()
        '''(remote)'''
        self.unavailable = Signal()

        self.messageSent = Signal()

        self.messageDelivered = Signal()

        '''(fmsg)'''
        self.messageReceived = Signal()
        '''messageReceived with fmsg.author'''
        self.groupMessageReceived = Signal()


        '''
        (fmsg)
        -> media_type: image, audio, video, location, vcard
        -> data
            image:
            -> url
            -> preview
            audio, video:
            -> url
            location:
            -> latitude
            -> longitude
            -> preview
            vcard:
            -> contact (vcard format)
        '''
        self.mediaReceived = Signal()

        '''mediaReceived with fmsg.author'''
        self.groupMediaReceived = Signal()

        '''(group, author, subject)'''
        self.newGroupSubject = Signal()

        '''(group, who)'''
        self.groupAdd = Signal()

        '''(group, who)'''
        self.groupRemove = Signal()

        ''' (groups: [{subject, id, owner}]) '''
        self.groupsReceived = Signal()

        self.groupListReceived = Signal()

        self.lastSeenUpdated = Signal()

        self.sendTyping = Signal()
        self.sendPaused = Signal()
        self.getLastOnline = Signal()

        self.disconnectRequested = Signal()
        self.disconnectRequested.connect(self.onDisconnectRequested)

        self.loginFailed = Signal()
        self.loginSuccess = Signal()
        self.connectionError = Signal()
        self.conn = conn

        self.sendTyping.connect(self.conn.sendTyping)
        self.sendPaused.connect(self.conn.sendPaused)
        self.getLastOnline.connect(self.conn.getLastOnline)

        self.startPingTimer()
Example #36
0
class HaystackOperation(object):
    """
    A core state machine object.  This implements the basic interface presented
    for all operations in pyhaystack.
    """
    def __init__(self, result_copy=True, result_deepcopy=True):
        """
        Initialisation.  This should be overridden by subclasses to accept and
        validate the inputs presented for the operation, raising an appropriate
        Exception subclass if the inputs are found to be invalid.

        These should be stored here by the initialisation function as private
        variables in suitably sanitised form.  The core state machine object
        shall then be created and stored before the object is returned to the
        caller.
        """
        # Event object to represent when this operation is "done"
        self._done_evt = Event()

        # Signal emitted when the operation is "done"
        self.done_sig = Signal(name="done", threadsafe=True)

        # Result returned by operation
        self._result = None
        self._result_copy = result_copy
        self._result_deepcopy = result_deepcopy

    def go(self):
        """
        Start processing the operation.  This is called by the caller (so after
        all __init__ functions have executed) in order to begin the asynchronous
        operation.
        """
        # This needs to be implemented in the subclass.
        raise NotImplementedError("To be implemented in subclass %s" %
                                  self.__class__.__name__)

    def wait(self, timeout=None):
        """
        Wait for an operation to finish.  This should *NOT* be called in the
        same thread as the thread executing the operation as this will
        deadlock.
        """
        self._done_evt.wait(timeout)

    @property
    def state(self):
        """
        Return the current state machine's state.
        """
        return self._state_machine.current

    @property
    def is_done(self):
        """
        Return true if the operation is complete.
        """
        return self._state_machine.is_finished()

    @property
    def is_failed(self):
        """
        Return true if the result is an Exception.
        """
        return isinstance(self._result, AsynchronousException)

    @property
    def result(self):
        """
        Return the result of the operation or raise its exception.
        Raises NotReadyError if not ready.
        """
        if not self.is_done:
            raise NotReadyError()

        if self.is_failed:
            self._result.reraise()

        if not self._result_copy:
            # Return the original instance (do not copy)
            return self._result
        elif self._result_deepcopy:
            # Return a deep copy
            return deepcopy(self._result)
        else:
            # Return a shallow copy
            return self._result.copy()

    def __repr__(self):
        """
        Return a representation of this object's state.
        """
        if self.is_failed:
            return "<%s failed>" % self.__class__.__name__
        elif self.is_done:
            return "<%s done: %s>" % (self.__class__.__name__, self._result)
        else:
            return "<%s %s>" % (self.__class__.__name__, self.state)

    def _done(self, result):
        """
        Return the result of the operation to any listeners.
        """
        self._result = result
        self._done_evt.set()
        self.done_sig.emit(operation=self)
Example #37
0
class UDP_Server(object):
    def __init__(self, ip, port, ttl, type_, id_):
        handler = UDP_Communications.UDP_Server.SenderHandler(
            self.__packet_received)
        self._server = UDP_Communications.UDP_Server(handler)
        # debug=True for more outputs in the Python command line.
        self._server.debug = False
        # Turn off local adapter filter; Let it switched off
        self._server.adapter_filter(False)
        self._server.init_udp(ip, port, ttl, System.Byte(type_),
                              System.Byte(id_), True)

        if not self._server.udp_active():
            raise RuntimeError("Server is not running!")
        pass

    def send_command(self, command, **kwargs):
        data = ['command', str(command)]
        for k, v in kwargs.items():
            data.append(str(k))
            data.append(str(v))
        result = self._server.send_command(data)
        return result

    def send_data(self, **kwargs):
        data = []
        for k, v in kwargs.items():
            data.append(str(k))
            data.append(str(v))
        self._server.send_data(data)

    def send_command_wait_for_reply(self, command, data=None, **kwargs):
        dataCmd = ['command', str(command)]
        if data is not None:
            for k, v in data.items():
                dataCmd.extend([k, v])
        for k, v in kwargs.items():
            dataCmd.append(str(k))
            dataCmd.append(str(v))
        method = self._server.send_command.Overloads[
            System.Array[System.String],
            System.String("").GetType().MakeByRefType()]
        res = method(dataCmd, "")
        return res[1]

    def start_stream(self, rxType, rxId):
        result = self._server.startStream(System.Byte(rxType),
                                          System.Byte(rxId))
        return result

    def stop_stream(self, rxType, rxId):
        self._server.stopStream(System.Byte(rxType), System.Byte(rxId))

    def stop(self):
        self._server.stop_udp()

    def send_stream(self, rxType, rxId, data):
        result = self._server.write(data.tobytes(), System.Byte(rxType),
                                    System.Byte(rxId))
        return result

    def stream_data_available(self, rxType, rxId):
        result = self._server.StreamDataAvailable(System.Byte(rxType),
                                                  System.Byte(rxId))

        # Is any data available
        if result[0]:
            # Is it data from the desired stream?
            if result[1] != rxType or result[2] != rxId:
                return False

        # Return result
        return result[0]

    def get_stream_data(self, rxType, rxId):
        streamData = self._server.getStreamData(System.Byte(rxType),
                                                System.Byte(rxId))
        if streamData is None:
            return
        dataArray = []
        # NOTE: This is very inefficient; Has to be improved
        # The problem is conversion from System.Byte to e.g. numpy data type
        #for x in streamData:
        #    dataArray.append(x)
        # NOTE: For now we just convert the first 10 elements. However, everything is received.
        for x in range(9):
            dataArray.append(streamData[x])

        return dataArray

    def __packet_received(self, server, event):
        paket = event.Paket
        if paket is None:
            return
        res_packet = Packet(paket)
        self.packet_received.emit(packet=res_packet)

    packet_received = Signal(args=['packet'])
Example #38
0
class WAEventHandler(object):

    def __init__(self, conn):
        self.connecting = Signal()
        self.connected = Signal()
        self.sleeping = Signal()
        self.disconnected = Signal()
        self.reconnecting = Signal()

        '''(remote)'''
        self.typing = Signal()
        '''(remote)'''
        self.typingPaused = Signal()


        '''(remote)'''
        self.available = Signal()
        '''(remote)'''
        self.unavailable = Signal()

        self.messageSent = Signal()

        self.messageDelivered = Signal()

        '''(fmsg)'''
        self.messageReceived = Signal()
        '''messageReceived with fmsg.author'''
        self.groupMessageReceived = Signal()


        '''
        (fmsg)
        -> media_type: image, audio, video, location, vcard
        -> data
            image:
            -> url
            -> preview
            audio, video:
            -> url
            location:
            -> latitude
            -> longitude
            -> preview
            vcard:
            -> contact (vcard format)
        '''
        self.mediaReceived = Signal()

        '''mediaReceived with fmsg.author'''
        self.groupMediaReceived = Signal()

        '''(group, author, subject)'''
        self.newGroupSubject = Signal()

        '''(group, who)'''
        self.groupAdd = Signal()

        '''(group, who)'''
        self.groupRemove = Signal()

        ''' (groups: [{subject, id, owner}]) '''
        self.groupsReceived = Signal()

        self.groupListReceived = Signal()

        self.lastSeenUpdated = Signal()

        self.sendTyping = Signal()
        self.sendPaused = Signal()
        self.getLastOnline = Signal()

        self.disconnectRequested = Signal()
        self.disconnectRequested.connect(self.onDisconnectRequested)

        self.loginFailed = Signal()
        self.loginSuccess = Signal()
        self.connectionError = Signal()
        self.conn = conn

        self.sendTyping.connect(self.conn.sendTyping)
        self.sendPaused.connect(self.conn.sendPaused)
        self.getLastOnline.connect(self.conn.getLastOnline)

        self.startPingTimer()

    def onDirty(self, categories):
        '''Receive groups??'''
        pass

    def onAccountChanged(self, account_kind, expire):
        pass

    def onRelayRequest(
        self,
        pin,
        timeoutSeconds,
        idx,
        ):
        pass

    def sendPing(self):
        self.startPingTimer()
        self.conn.sendPing()

    def startPingTimer(self):
        self.pingTimer = threading.Timer(180, self.sendPing)
        self.pingTimer.start()

    def onDisconnectRequested(self):
        self.pingTimer.cancel()

    def onPing(self, idx):
        self.conn.sendPong(idx)

    def networkAvailable(self):
        pass

    def networkDisconnected(self):
        self.sleeping.emit()

    def networkUnavailable(self):
        self.disconnected.emit()

    def onUnavailable(self):
        self.conn.sendUnavailable()

    def conversationOpened(self, jid):
        pass

    def onAvailable(self):
        self.conn.sendAvailable()

    def message_received(self, fmsg):
        if hasattr(fmsg, 'type'):
            if fmsg.type == "chat":
                if fmsg.remote.endswith('@g.us'):
                    self.groupMessageReceived.emit(fmsg)
                else:
                    self.messageReceived.emit(fmsg)
            elif fmsg.type == "media":
                if fmsg.remote.endswith('@g.us'):
                    self.groupMediaReceived.emit(fmsg)
                else:
                    self.mediaReceived.emit(fmsg)
        if fmsg.wants_receipt:
            self.conn.sendMessageReceived(fmsg)

    def subjectReceiptRequested(self, to, idx):
        self.conn.sendSubjectReceived(to, idx)

    def presence_available_received(self, remote):
        if remote == self.conn.jid:
            return
        self.available.emit(remote)

    def presence_unavailable_received(self, remote):
        if remote == self.conn.jid:
            return
        self.unavailable.emit(remote)

    def typing_received(self, remote):
        self.typing.emit(remote)

    def paused_received(self, remote):
        self.typingPaused.emit(remote)

    def message_error(self, fmsg, errorCode):
        pass

    def message_status_update(self, fmsg):
        pass
Example #39
0
class Elevator(object):
    def __init__(self, id, size=10, height=10):
        """
        people - list of people currently in the elevator
        size - elevator capacity in terms of people count
        velocity - velocity along y axis
        position - current y coordinate
        door_opening_time - time taken to open doors in seconds
        door_closing_time - time taken to close doors in seconds
        """
        self.__id = id
        self.__people = []
        self.__signal_person_inside = Signal(args=['person'])
        self.__signal_position_change = Signal(args=['elevator_id', 'x', 'y'])
        # direction: True = up, Down = False
        self.__signal_door_opened = Signal(args=['elevator_id', 'direction', 'people_inside', 'available_capacity', 'floor_num'])
        self.__signal_door_closed = Signal(args=['elevator_id', 'people_inside'])
        self.__size = size
        self.__velocity = 0
        self.__position = (0, 0)
        self.__height = height
        self.__door_opening_time = 1.5
        self.__door_closing_time = 2.5

    def move(self, timedelta):
#        print '[%s] speed = %s/%s' % (self.id, self.velocity, self.__velocity)
        self.__position = Vector(0, self.__velocity) + self.__position
        self.__signal_position_change.emit(elevator_id=self.__id, x=self.__position[0], y=self.__position[1])

    def set_velocity(self, elevator_id, velocity, **kwargs):
        if self.id == elevator_id:
            self.__velocity = velocity

    def stop(self, elevator_id, floor_num, **kwargs):
        if self.id != elevator_id:
            return

        going_up = self.__velocity > 0

        self.__velocity = 0
        # open door
#        time.sleep(self.__door_opening_time)

        timer.timeout(door_openning_time * interval, callback)

    def callback():
        self.signal_door_opened.emit(elevator_id=self.id, direction=going_up, available_capacity=self.size - len(self.people), people_inside=self.people[:], floor_num=floor_num)


    def go_to(floor_no=1):
        # determine current pos
        # change velocity
        # go
        pass

    @property
    def id(self):
        return self.__id

    @property
    def size(self):
        return self.__size

    @property
    def velocity(self):
        return self.__velocity

    @property
    def position(self):
        """
        Simplified for elevator case, as it moves up/down
        Return y coordinate
        """
        return self.__position[1]

    @property
    def people(self):
        return self.__people

    @property
    def signal_person_inside(self):
        return self.__signal_person_inside

    @property
    def signal_position_change(self):
        return self.__signal_position_change

    @property
    def height(self):
        return self.__height

    @property
    def signal_door_opened(self):
        print 'signaling door opened', self.id
        return self.__signal_door_opened

    @property
    def signal_door_closed(self):
        return self.__signal_door_closed

    def people_boarded(self, elevator_id, people, **kwargs):
        if self.id != elevator_id:
            return

        self.people.extend(people[:])

        # close door
#        time.sleep(self.__door_closing_time)

        self.signal_door_closed.emit(elevator_id=self.id, people_inside=self.people[:])
Example #40
0
    def __init__(self, schid, cid, password='', path='/', parent=None, *,
                 staticpath=False, readonly=False, downloaddir=None,
                 iconpack=None):
        """
        Instantiates a new object.
        @param schid: the id of the serverconnection handler
        @type schid: int
        @param cid: the id of the channel
        @type cid: int
        @param password: password to the channel, defaults to an empty string
        @type password: str
        @param path: path to display, defaults to the root path
        @type path: str
        @param parent: parent of the dialog; optional keyword arg;
        defaults to None
        @type parent: QWidget
        @param staticpath: if set to True, the initial path can't be
        changed by the user; optional keyword arg; defaults to False
        @type staticpath: bool
        @param readonly: if set to True, the user can't download, upload
        or delete files, or create new directories; optional keyword arg;
        defaults to False
        @type readonly: bool
        @param downloaddir: directory to download files to; optional keyword
        arg; defaults to None; if set to None, the TS3 client's download
        directory is used
        @type downloaddir: str
        @param iconpack: iconpack to load icons from; optional keyword arg;
        defaults to None; if set to None, the current iconpack is used
        @type iconpack: ts3client.IconPack
        """
        super(QDialog, self).__init__(parent)
        self.setAttribute(Qt.WA_DeleteOnClose)

        iconpackopened = False
        if not iconpack:
            try:
                iconpack = ts3client.IconPack.current()
                iconpack.open()
                iconpackopened = True
            except Exception as e:
                self.delete()
                raise e

        try:
            setupUi(self, pytson.getPluginPath("ressources", "filebrowser.ui"),
                    iconpack=iconpack)

            self.statusbar = SmartStatusBar(self)
            self.layout().addWidget(self.statusbar)
            self.statusbar.hide()
        except Exception as e:
            self.delete()
            raise e

        err, cname = ts3lib.getChannelVariableAsString(schid, cid,
                                                       ChannelProperties.
                                                       CHANNEL_NAME)

        if err == ERROR_ok:
            self.setWindowTitle(self._tr("File Browser - {cname}").format(
                                cname=cname))
        else:
            self.setWindowTitle(self._tr("File Browser"))

        self.schid = schid
        self.cid = cid
        self.password = password
        self.path = None

        self.staticpath = staticpath
        self.readonly = readonly

        self.createretcode = None
        self.delretcode = None

        if not self.readonly and not downloaddir:
            cfg = ts3client.Config()
            q = cfg.query("SELECT value FROM filetransfer "
                          "WHERE key='DownloadDir'")
            del cfg

            if q.next():
                self.downloaddir = q.value("value")
            else:
                self.delete()
                raise Exception("Error getting DownloadDir from config")
        else:
            self.downloaddir = downloaddir

        if not self.readonly:
            menu = self.menu = QMenu(self)

            self.openAction = menu.addAction(QIcon(iconpack.icon("FILE_UP")),
                                             self._tr("Open"))
            self.openAction.connect("triggered()",
                                    self.on_openAction_triggered)

            self.downAction = menu.addAction(QIcon(iconpack.icon("DOWN")),
                                             self._tr("Download"))
            self.downAction.connect("triggered()", self.downloadFiles)
            self.renameAction = menu.addAction(QIcon(iconpack.icon("EDIT")),
                                               self._tr("Rename"))
            self.renameAction.connect("triggered()",
                                      self.on_renameAction_triggered)
            self.copyAction = menu.addAction(QIcon(iconpack.icon("COPY")),
                                             self._tr("Copy URL"))
            self.copyAction.connect("triggered()",
                                    self.on_copyAction_triggered)
            self.delAction = menu.addAction(QIcon(iconpack.icon("DELETE")),
                                            self._tr("Delete"))
            self.delAction.connect("triggered()", self.deleteFiles)

            self.upAction = menu.addAction(QIcon(iconpack.icon("UP")),
                                           self._tr("Upload files"))
            self.upAction.connect("triggered()", self.uploadFiles)
            self.createAction = menu.addAction(QIcon.fromTheme("folder"),
                                               self._tr("Create Folder"))
            self.createAction.connect("triggered()", self.createFolder)
            self.refreshAction = menu.addAction(QIcon(iconpack.icon(
                                                "FILE_REFRESH")),
                                                self._tr("Refresh"))
            self.refreshAction.connect("triggered()", self.refresh)

            self.allactions = [self.openAction, self.downAction,
                               self.renameAction, self.copyAction,
                               self.delAction, self.upAction,
                               self.createAction, self.refreshAction]

        self.collector = FileCollector(schid, cid, password, self.downloaddir)
        self.collector.collectionFinished.connect(self._startDownload)
        self.collector.collectionError.connect(self.showError)

        self.fileDoubleClicked = Signal()
        self.contextMenuRequested = Signal()

        self.transdlg = None

        self.listmodel = FileListModel(schid, cid, password, self,
                                       readonly=readonly)
        self.listmodel.pathChanged.connect(self.onPathChanged)
        self.listmodel.error.connect(self.showError)

        self.proxy = QSortFilterProxyModel(self)
        self.proxy.setSortRole(Qt.UserRole)
        self.proxy.setSortCaseSensitivity(Qt.CaseInsensitive)
        self.proxy.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.proxy.setSourceModel(self.listmodel)

        self.listmodel.path = path

        self._adjustUi()

        if iconpackopened:
            iconpack.close()

        PluginHost.registerCallbackProxy(self)
Example #41
0
 def setup_method(self, method):
     self.signal = Signal()
Example #42
0
class TestSignal(object):
    def setup_method(self, method):
        self.signal_a = Signal(threadsafe=True)
        self.signal_b = Signal(args=['foo'])

        self.slot_a = mock.Mock(spec=lambda **kwargs: None)
        self.slot_a.return_value = None
        self.slot_b = mock.Mock(spec=lambda **kwargs: None)
        self.slot_b.return_value = None

    def test_is_connected(self, inspect):
        self.signal_a.connect(self.slot_a)

        assert self.signal_a.is_connected(self.slot_a)
        assert not self.signal_a.is_connected(self.slot_b)
        assert not self.signal_b.is_connected(self.slot_a)
        assert not self.signal_b.is_connected(self.slot_b)

    def test_emit_one_slot(self, inspect):
        self.signal_a.connect(self.slot_a)

        self.signal_a.emit()

        self.slot_a.assert_called_once_with()
        assert self.slot_b.call_count == 0

    def test_emit_two_slots(self, inspect):
        self.signal_a.connect(self.slot_a)
        self.signal_a.connect(self.slot_b)

        self.signal_a.emit()

        self.slot_a.assert_called_once_with()
        self.slot_b.assert_called_once_with()

    def test_emit_one_slot_with_arguments(self, inspect):
        self.signal_b.connect(self.slot_a)

        self.signal_b.emit(foo='bar')

        self.slot_a.assert_called_once_with(foo='bar')
        assert self.slot_b.call_count == 0

    def test_emit_two_slots_with_arguments(self, inspect):
        self.signal_b.connect(self.slot_a)
        self.signal_b.connect(self.slot_b)

        self.signal_b.emit(foo='bar')

        self.slot_a.assert_called_once_with(foo='bar')
        self.slot_b.assert_called_once_with(foo='bar')

    def test_reconnect_does_not_duplicate(self, inspect):
        self.signal_a.connect(self.slot_a)
        self.signal_a.connect(self.slot_a)
        self.signal_a.emit()

        self.slot_a.assert_called_once_with()

    def test_disconnect_does_not_fail_on_not_connected_slot(self, inspect):
        self.signal_a.disconnect(self.slot_b)
Example #43
0
def test_named_signal_has_a_nice_repr():
    signal = Signal(name='update_stuff')
    assert repr(signal) == '<signalslot.Signal: update_stuff>'
Example #44
0
class FileBrowser(QDialog, pytson.Translatable):
    """
    Dialog to display files contained on a TS3 filepath.
    """

    def __init__(self, schid, cid, password='', path='/', parent=None, *,
                 staticpath=False, readonly=False, downloaddir=None,
                 iconpack=None):
        """
        Instantiates a new object.
        @param schid: the id of the serverconnection handler
        @type schid: int
        @param cid: the id of the channel
        @type cid: int
        @param password: password to the channel, defaults to an empty string
        @type password: str
        @param path: path to display, defaults to the root path
        @type path: str
        @param parent: parent of the dialog; optional keyword arg;
        defaults to None
        @type parent: QWidget
        @param staticpath: if set to True, the initial path can't be
        changed by the user; optional keyword arg; defaults to False
        @type staticpath: bool
        @param readonly: if set to True, the user can't download, upload
        or delete files, or create new directories; optional keyword arg;
        defaults to False
        @type readonly: bool
        @param downloaddir: directory to download files to; optional keyword
        arg; defaults to None; if set to None, the TS3 client's download
        directory is used
        @type downloaddir: str
        @param iconpack: iconpack to load icons from; optional keyword arg;
        defaults to None; if set to None, the current iconpack is used
        @type iconpack: ts3client.IconPack
        """
        super(QDialog, self).__init__(parent)
        self.setAttribute(Qt.WA_DeleteOnClose)

        iconpackopened = False
        if not iconpack:
            try:
                iconpack = ts3client.IconPack.current()
                iconpack.open()
                iconpackopened = True
            except Exception as e:
                self.delete()
                raise e

        try:
            setupUi(self, pytson.getPluginPath("ressources", "filebrowser.ui"),
                    iconpack=iconpack)

            self.statusbar = SmartStatusBar(self)
            self.layout().addWidget(self.statusbar)
            self.statusbar.hide()
        except Exception as e:
            self.delete()
            raise e

        err, cname = ts3lib.getChannelVariableAsString(schid, cid,
                                                       ChannelProperties.
                                                       CHANNEL_NAME)

        if err == ERROR_ok:
            self.setWindowTitle(self._tr("File Browser - {cname}").format(
                                cname=cname))
        else:
            self.setWindowTitle(self._tr("File Browser"))

        self.schid = schid
        self.cid = cid
        self.password = password
        self.path = None

        self.staticpath = staticpath
        self.readonly = readonly

        self.createretcode = None
        self.delretcode = None

        if not self.readonly and not downloaddir:
            cfg = ts3client.Config()
            q = cfg.query("SELECT value FROM filetransfer "
                          "WHERE key='DownloadDir'")
            del cfg

            if q.next():
                self.downloaddir = q.value("value")
            else:
                self.delete()
                raise Exception("Error getting DownloadDir from config")
        else:
            self.downloaddir = downloaddir

        if not self.readonly:
            menu = self.menu = QMenu(self)

            self.openAction = menu.addAction(QIcon(iconpack.icon("FILE_UP")),
                                             self._tr("Open"))
            self.openAction.connect("triggered()",
                                    self.on_openAction_triggered)

            self.downAction = menu.addAction(QIcon(iconpack.icon("DOWN")),
                                             self._tr("Download"))
            self.downAction.connect("triggered()", self.downloadFiles)
            self.renameAction = menu.addAction(QIcon(iconpack.icon("EDIT")),
                                               self._tr("Rename"))
            self.renameAction.connect("triggered()",
                                      self.on_renameAction_triggered)
            self.copyAction = menu.addAction(QIcon(iconpack.icon("COPY")),
                                             self._tr("Copy URL"))
            self.copyAction.connect("triggered()",
                                    self.on_copyAction_triggered)
            self.delAction = menu.addAction(QIcon(iconpack.icon("DELETE")),
                                            self._tr("Delete"))
            self.delAction.connect("triggered()", self.deleteFiles)

            self.upAction = menu.addAction(QIcon(iconpack.icon("UP")),
                                           self._tr("Upload files"))
            self.upAction.connect("triggered()", self.uploadFiles)
            self.createAction = menu.addAction(QIcon.fromTheme("folder"),
                                               self._tr("Create Folder"))
            self.createAction.connect("triggered()", self.createFolder)
            self.refreshAction = menu.addAction(QIcon(iconpack.icon(
                                                "FILE_REFRESH")),
                                                self._tr("Refresh"))
            self.refreshAction.connect("triggered()", self.refresh)

            self.allactions = [self.openAction, self.downAction,
                               self.renameAction, self.copyAction,
                               self.delAction, self.upAction,
                               self.createAction, self.refreshAction]

        self.collector = FileCollector(schid, cid, password, self.downloaddir)
        self.collector.collectionFinished.connect(self._startDownload)
        self.collector.collectionError.connect(self.showError)

        self.fileDoubleClicked = Signal()
        self.contextMenuRequested = Signal()

        self.transdlg = None

        self.listmodel = FileListModel(schid, cid, password, self,
                                       readonly=readonly)
        self.listmodel.pathChanged.connect(self.onPathChanged)
        self.listmodel.error.connect(self.showError)

        self.proxy = QSortFilterProxyModel(self)
        self.proxy.setSortRole(Qt.UserRole)
        self.proxy.setSortCaseSensitivity(Qt.CaseInsensitive)
        self.proxy.setFilterCaseSensitivity(Qt.CaseInsensitive)
        self.proxy.setSourceModel(self.listmodel)

        self.listmodel.path = path

        self._adjustUi()

        if iconpackopened:
            iconpack.close()

        PluginHost.registerCallbackProxy(self)

    def __del__(self):
        PluginHost.unregisterCallbackProxy(self)

    def _enableMenus(self, actlist):
        for act in self.allactions:
            act.setVisible(act in actlist)

    def _adjustMenu(self):
        selfiles = self.selectedFiles()
        cur = self.listmodel.fileByIndex(self.currentItem())

        if len(selfiles) == 0:
            self._enableMenus([self.upAction, self.createAction,
                               self.refreshAction])
        elif cur.isDirectory:
            self._enableMenus([self.openAction, self.downAction,
                               self.renameAction, self.copyAction,
                               self.delAction])
        else:
            self._enableMenus([self.downAction, self.renameAction,
                               self.copyAction, self.delAction])

    def _adjustUi(self):
        self.filterFrame.hide()

        self.filecountLabel.hide()

        self.downloaddirButton.setText(self.downloaddir)

        self.iconButton.setChecked(True)
        self.stack.setCurrentWidget(self.listPage)

        self.list.setModel(self.proxy)
        self.table.setModel(self.proxy)
        self.table.sortByColumn(0, Qt.AscendingOrder)

        if self.staticpath:
            self.upButton.hide()
            self.homeButton.hide()

        if self.readonly:
            self.uploadButton.hide()
            self.downloadButton.hide()
            self.directoryButton.hide()
            self.deleteButton.hide()

            self.downloaddirLabel.hide()
            self.downloaddirButton.hide()

        header = self.table.horizontalHeader()
        header.setSectionResizeMode(0, QHeaderView.Stretch)
        for i in range(1, header.count()):
            header.setSectionResizeMode(i, QHeaderView.ResizeToContents)

        self.refreshButton.connect("clicked()", self.refresh)
        self.uploadButton.connect("clicked()", self.uploadFiles)
        self.downloadButton.connect("clicked()", self.downloadFiles)
        self.deleteButton.connect("clicked()", self.deleteFiles)
        self.directoryButton.connect("clicked()", self.createFolder)
        self.list.connect("doubleClicked(QModelIndex)", self.viewDoubleClicked)
        self.table.connect("doubleClicked(QModelIndex)",
                           self.viewDoubleClicked)

    def _showTransfers(self):
        if not self.transdlg:
            self.transdlg = FileTransferDialog(self.schid, self.cid,
                                               self.password, self)
            self.transdlg.show()

    def onPathChanged(self, newpath):
        self.path = newpath
        self.pathEdit.setText(newpath)

        inroot = newpath == "/"
        self.upButton.setEnabled(not inroot)
        self.homeButton.setEnabled(not inroot)

        files = self.listmodel.currentFiles

        if not files:
            self.filecountLabel.hide()
        else:
            self.filecountLabel.show()

            fcount = 0
            dcount = 0

            for f in files:
                if f.isDirectory:
                    dcount += 1
                else:
                    fcount += 1

            fstr = self._tr("{filecount} file(s)", n=fcount).format(
                            filecount=fcount)
            dstr = self._tr("{dircount} directory(s)", n=dcount).format(
                            dircount=dcount)

            if dcount == 0:
                self.filecountLabel.setText(fstr)
            elif fcount == 0:
                self.filecountLabel.setText(dstr)
            else:
                cstr = self._tr("{dircountstr} and {fcountstr}").format(
                                dircountstr=dstr, fcountstr=fstr)
                self.filecountLabel.setText(cstr)

    def on_pathEdit_returnPressed(self):
        oldpath = self.listmodel.path
        if not self.readonly:
            self.listmodel.path = self.pathEdit.text

        self.pathEdit.text = oldpath or ""

    def on_iconButton_toggled(self, act):
        if act:
            self.stack.setCurrentWidget(self.listPage)

    def on_detailedButton_toggled(self, act):
        if act:
            self.stack.setCurrentWidget(self.tablePage)

    def on_filterButton_clicked(self):
        self.filterFrame.show()

    def on_clearButton_clicked(self):
        self.filterEdit.clear()
        self.filterFrame.hide()

    def on_filterEdit_textChanged(self, newtext):
        self.proxy.setFilterRegExp(newtext)

    def on_upButton_clicked(self):
        if self.staticpath:
            return
        if self.path == "/":
            return

        self.listmodel.path = joinpath(*splitpath(self.path)[:-1])

    def on_homeButton_clicked(self):
        if self.staticpath:
            return

        self.listmodel.path = "/"

    def refresh(self):
        self.listmodel.path = self.listmodel.path

    def on_downloaddirButton_clicked(self):
        QDesktopServices.openUrl(QUrl(self.downloaddir))

    def showError(self, prefix, errcode, msg=None):
        if not msg:
            err, msg = ts3lib.getErrorMessage(errcode)
        else:
            err = ERROR_ok

        if err != ERROR_ok:
            self.statusbar.showMessage("%s: %s" % (prefix, errcode))
        else:
            self.statusbar.showMessage("%s: %s" % (prefix, msg))

    def uploadFiles(self):
        if self.readonly:
            return

        files = QFileDialog.getOpenFileNames(self, self._tr("Upload files"),
                                             self.downloaddir)

        fca = FileCollisionAction.overwrite
        curfiles = {f.name: f for f in self.listmodel.currentFiles}
        for f in files:
            fname = os.path.split(f)[-1]
            if fname in curfiles:
                if not fca & FileCollisionAction.toall:
                    fca = FileCollisionDialog.getAction(f, curfiles[fname],
                                                        False, len(files) > 1,
                                                        self)

                if fca == 0:
                    return

                if fca & FileCollisionAction.skip:
                    if not fca & FileCollisionAction.toall:
                        fca = FileCollisionAction.overwrite
                    break

            self._showTransfers()
            self.transdlg.addUpload(self.path, f,
                                    fca & FileCollisionAction.overwrite,
                                    fca & FileCollisionAction.resume)

            if not fca & FileCollisionAction.toall:
                fca = FileCollisionAction.overwrite

    def onServerErrorEvent(self, schid, errorMessage, error, returnCode,
                           extraMessage):
        if schid != self.schid:
            return

        if returnCode == self.createretcode:
            if error == ERROR_ok:
                self.listmodel.path = self.path
            else:
                self.showError(self._tr("Error creating directory"), error,
                               errorMessage)
        elif returnCode == self.delretcode:
            if error == ERROR_ok:
                self.listmodel.path = self.path
            else:
                self.showError(self._tr("Error deleting files"), error,
                               errorMessage)

    def selectedFiles(self):
        if self.stack.currentWidget() == self.listPage:
            view = self.list
        else:
            view = self.table

        return [self.listmodel.fileByIndex(self.proxy.mapToSource(x))
                for x in view.selectionModel().selectedIndexes]

    def currentItem(self, source=True):
        if self.stack.currentWidget() == self.listPage:
            view = self.list
        else:
            view = self.table

        if source:
            return self.proxy.mapToSource(view.currentIndex())
        else:
            return view.currentIndex()

    def _startDownload(self, collection):
        """
        @param collection: list of tuples containing the download directory and
        the list of files to download to that directory
        @type collection: list[tuple(str, list[File])]
        """
        if not collection:
            return

        fca = FileCollisionAction.overwrite

        for (downdir, files) in collection:
            for f in files:
                multi = len(files) + len(collection) > 2
                fname = os.path.join(downdir, f.name)
                if os.path.isfile(fname):
                    if not fca & FileCollisionAction.toall:
                        fca = FileCollisionDialog.getAction(fname, f, True,
                                                            multi, self)

                    if fca == 0:
                        return

                    if fca & FileCollisionAction.skip:
                        if not fca & FileCollisionAction.toall:
                            fca = FileCollisionAction.overwrite
                        break

                self._showTransfers()
                self.transdlg.addDownload(f, downdir,
                                          fca & FileCollisionAction.overwrite,
                                          fca & FileCollisionAction.resume)

                if not fca & FileCollisionAction.toall:
                    fca = FileCollisionAction.overwrite

    def downloadFiles(self, files=None):
        if self.readonly:
            return

        if not files:
            selfiles = self.selectedFiles()
        else:
            selfiles = files

        if not selfiles:
            return

        downfiles = []
        downdirs = []

        for f in selfiles:
            if f.isDirectory:
                downdirs.append(f)
            else:
                downfiles.append(f)

        if not downdirs:
            self._startDownload([(self.downloaddir, downfiles)])
        else:
            if downfiles:
                self.collector.addFiles(downfiles)

            self.collector.collect(downdirs)

    def createFolder(self):
        if self.readonly:
            return

        ok = BoolResult()
        dirname = QInputDialog.getText(self, self._tr("Create Folder"),
                                       self._tr("Folder name:"),
                                       QLineEdit.Normal, "", ok)

        if not ok or dirname == "":
            return

        self.createretcode = ts3lib.createReturnCode()
        err = ts3lib.requestCreateDirectory(self.schid, self.cid,
                                            self.password,
                                            joinpath(self.path, dirname),
                                            self.createretcode)

        if err != ERROR_ok:
            self.showError(self._tr("Error creating directory"), err)

    def deleteFiles(self, files=None):
        if self.readonly:
            return

        if not files:
            selfiles = self.selectedFiles()
        else:
            selfiles = files

        if not selfiles:
            return

        if QMessageBox.question(self, self._tr("Delete files"),
                                self._tr("Do you really want to delete all "
                                         "selected files?")) == QMessageBox.No:
            return

        pathes = [f.fullpath for f in selfiles]
        self.delretcode = ts3lib.createReturnCode()
        err = ts3lib.requestDeleteFile(self.schid, self.cid, self.password,
                                       pathes, self.delretcode)

        if err != ERROR_ok:
            self.showError(self._tr("Error deleting files"), err)

    def on_table_customContextMenuRequested(self, pos):
        selfiles = self.selectedFiles()
        globpos = self.table.mapToGlobal(pos)

        if self.readonly:
            self.contextMenuRequested.emit(selfiles, globpos)
        else:
            self._adjustMenu()
            self.menu.popup(globpos)

    def on_list_customContextMenuRequested(self, pos):
        selfiles = self.selectedFiles()
        globpos = self.list.mapToGlobal(pos)

        if self.readonly:
            self.contextMenuRequested.emit(selfiles, globpos)
        else:
            self._adjustMenu()
            self.menu.popup(globpos)

    def viewDoubleClicked(self, idx):
        if not idx.isValid():
            return

        f = self.listmodel.fileByIndex(self.proxy.mapToSource(idx))
        if f.isDirectory:
            if self.staticpath:
                self.fileDoubleClicked.emit(f)
            else:
                self.listmodel.path = f.fullpath
        else:
            if self.readonly:
                self.fileDoubleClicked.emit(f)
            else:
                self.downloadFiles([f])

    def on_openAction_triggered(self):
        cur = self.listmodel.fileByIndex(self.currentItem())

        if not cur or not cur.isDirectory:
            return

        self.listmodel.path = cur.fullpath

    def on_renameAction_triggered(self):
        if self.stack.currentWidget() == self.listPage:
            view = self.list
        else:
            view = self.table

        view.edit(self.currentItem(False))

    def on_copyAction_triggered(self):
        cur = self.listmodel.fileByIndex(self.currentItem())

        if not cur:
            return

        err, host, port, _ = ts3lib.getServerConnectInfo(self.schid)

        if err == ERROR_ok:
            url = ("[URL=ts3file://{address}?port={port}&channel={cid}&"
                   "path={path}&filename={fname}&isDir={isdir}&"
                   "size={size}&fileDateTime={date}]{fname}[/URL]").format(
                   address=host, port=port, cid=self.cid,
                   path=QUrl.toPercentEncoding(cur.path), fname=cur.name,
                   isdir=1 if cur.isDirectory else 0, size=cur.size,
                   date=int(cur.datetime.timestamp()))

            QApplication.clipboard().setText(url)
        else:
            self.showError(self._tr("Error getting server connection info"),
                           err)
Example #45
0
 def __init__(self):
     cmd.Cmd.__init__(self)
     self.save_command = Signal(['output_dir'])
     self.quit_command = Signal()
Example #46
0
def test_anonymous_signal_has_nice_repr():
    signal = Signal()
    assert repr(signal) == '<signalslot.Signal: NO_NAME>'
Example #47
0
class Table:
    """
    A Table is the place where all actions takes place. It is essentially a FSM, doing different
    routines at each state. It needs to keep track of the score, roles, the rules, etc. It needs
    to ask each player for decisions and respond to them accordingly. The table will also need
    to inform any decision to the Main Screen so that it can update the screen to reflect that
    change through the use of callbacks (Signal and Slot). This call should be minimised by making
    all the changes before calling to update the screen in one go.

    FSM cycles
    ---
    Preloop - Prepare the cards once
            - Initiate Players and connect them to the Table
    1.  Shuffle and Deal out cards to Players.
    2a. Detect weak hands and ask for reshuffle.
    2b. Return to (1) if any reshuffle occurs, otherwise proceed.
    3.  Bidding round. Randomly pick a starting player, in clockwise manner
        ask for a bid until it is valid.
    3b. Proceed only if 3 consecutive skips are detected.
    3c. Ask the winner of the bid a card not in their hand.
    3d. Set up the player roles, trump suit, rounds to win for both side
    3e.  Play the game. Start with bid winner if NO TRUMP, otherwise
        Starting next to the bid winner.
    4a.  With the first player, ask for any card, excluding trump suits if trump
        is not broken
    4b. With subsequent players, ask for cards that follow the suit of the first player
        , include trump suit if trump is broken. Ask for any card if the player cannot
        follow suit.
    4c. Once all 4 players has made valid plays, announce results, update scoring. Announce
        player roles if the partner card is played. Break trump if trump is played.
    4d. Repeat 4 until 13 rounds are made. Maybe add early win if confirmed one side wins
    5.  Ask for a new game. Go back to 1 if true.

    All played cards go into a hidden discard pile.

    """
    update_table = Signal()

    def __init__(self,
                 x,
                 y,
                 width,
                 height,
                 clear_colour,
                 autoplay=False,
                 view_all_cards=False):
        # TODO: Reduce the amount of update_table call
        self.x = x
        self.y = y
        self.width = width
        self.height = height

        self.table_font = pygame.font.SysFont("None", 25)
        self.player_font = pygame.font.SysFont("None", 25)

        # For gameplay
        self.game_state = GameState.DEALING
        self.current_round = 0
        self.passes = 0
        self.current_player = 0
        self.first_player = False  # This is for bidding purposes
        self.players = []
        self.players_playzone = []
        # Table status will be made known to the player by reference
        self.table_status = {
            'played cards': [0, 0, 0, 0],
            'leading player': 0,
            'trump suit': 1,
            'trump broken': False,
            'round history': [],
            'bid': 0,
            'partner': 0,
            'partner reveal': False,
            'defender': {
                'target': 0,
                'wins': 0
            },
            'attacker': {
                'target': 0,
                'wins': 0
            }
        }

        # Prepare the surfaces for displaying
        self.background = pygame.Surface((self.width, self.height))
        self.background.fill(clear_colour)
        self.background = self.background.convert()

        # TODO: Update the drawing of the table?
        # Prepare the card with dimensions
        w_deck = min(self.height, self.width) * 0.18
        l_deck = min(self.width, self.height) * 0.7
        # This is not a deck as it will never be drawn
        self.discard_deck = cards.prepare_playing_cards(
            int(w_deck * 0.7), int(w_deck * 0.9))
        game_margins = 5

        # Players' deck positioning
        playerx = ((self.width - l_deck) // 2, game_margins,
                   (self.width - l_deck) // 2,
                   self.width - w_deck - game_margins)
        playery = (self.height - w_deck - game_margins,
                   (self.height - l_deck) // 2, game_margins,
                   (self.height - l_deck) // 2)
        h_spacing = 20
        v_spacing = 25

        # Middle playfield for announcer and player playing deck positioning
        playfield_margins = 5
        margins_with_w_deck = w_deck + playfield_margins + game_margins
        playfield_x = margins_with_w_deck
        playfield_y = margins_with_w_deck
        playfield_width = self.width - margins_with_w_deck * 2
        playfield_height = self.height - margins_with_w_deck * 2

        playdeckx = (playfield_x + (playfield_width - w_deck) / 2, playfield_x,
                     playfield_x + (playfield_width - w_deck) / 2,
                     playfield_x + playfield_width - w_deck)
        playdecky = (playfield_y + playfield_height - w_deck,
                     playfield_y + (playfield_height - w_deck) / 2,
                     playfield_y,
                     playfield_y + (playfield_height - w_deck) / 2)

        # Player stats positioning
        stats_width = 100
        self.stats_height = 100
        stats_spacing = 10
        self.player_stats_x = (playdeckx[0] - stats_width - stats_spacing,
                               playdeckx[1],
                               playdeckx[2] + w_deck + stats_spacing,
                               playdeckx[3])
        self.player_stats_y = (playdecky[0] + w_deck - self.stats_height,
                               playdecky[1] - self.stats_height -
                               stats_spacing, playdecky[2],
                               playdecky[3] + w_deck + stats_spacing)

        self.player_stats = [[], [], [], []]

        # TODO: change surface to use colorkey, maybe, if the performance is tanked
        # Prepare all the player surfaces
        for i in range(4):
            vert = i % 2 == 1
            spacing = h_spacing
            if vert:
                spacing = v_spacing

            reveal_mode = cards.DeckReveal.HIDE_ALL
            if i == 0 or view_all_cards:
                reveal_mode = cards.DeckReveal.SHOW_ALL
            self.players.append(
                Player(playerx[i],
                       playery[i],
                       l_deck,
                       w_deck,
                       spacing,
                       vert_orientation=vert,
                       deck_reveal=reveal_mode))
            self.players[i].connect_to_table(self.table_status)
            if i > 0:
                self.players[i].add_ai(ai.RandomAI(self.table_status))

            self.players_playzone.append(
                cards.Deck(playdeckx[i], playdecky[i], w_deck, w_deck, 0))
            for j in range(3):
                surf = pygame.Surface((stats_width, self.stats_height / 3),
                                      pygame.SRCALPHA)
                rendered_text = self.player_font.render(
                    "Player {0:d}".format(i), True,
                    (255, 0, 255)).convert_alpha()
                self.center_text_on_surface(
                    surf, rendered_text,
                    (255, 255, 255, 255 * VIEW_TRANSPARENT))
                self.player_stats[i].append(surf)

        if autoplay:
            self.players[0].add_ai(ai.RandomAI(self.table_status))

        # Announcer positioning and surface creation
        announcer_margins = 5
        announcer_spacing = announcer_margins + w_deck
        self.announcer_x = playfield_x + announcer_spacing
        self.announcer_y = playfield_y + announcer_spacing
        self.announcer_width = playfield_width - 2 * announcer_spacing
        self.announcer_height = playfield_height - 2 * announcer_spacing
        self.announcer_line = []
        for i in range(3):
            surf = pygame.Surface(
                (self.announcer_width, self.announcer_height / 3),
                pygame.SRCALPHA)
            self.announcer_line.append(surf)

        self.update_all_players(role=True, wins=True)

        self.write_message("Press P to play!")

        self.ongoing = False

    def center_text_on_surface(self, surf, rendered_text, clear_colour):
        line_center = surf.get_rect().center
        text_rect = rendered_text.get_rect(center=line_center)
        surf.fill(clear_colour)
        surf.blit(rendered_text, text_rect)

    def write_message(self, text, delay_time=0.5, line=0, update_now=True):
        """
        Write a message into the center board surface (announcer)
        :param text: String to be displayed on the center board
        :param delay_time: How much delay to put once the string is display
        :param line: Which line of the announcer to write to
        :return: None
        """
        if 0 <= line < len(self.announcer_line):
            print(text)
            text = text.strip('\n')
            rendered_text = self.table_font.render(
                text, True, (255, 255, 255)).convert_alpha()
            self.center_text_on_surface(
                self.announcer_line[line], rendered_text,
                (255, 255, 255, 255 * VIEW_TRANSPARENT))
            if update_now:
                self.update_table.emit()
                time.sleep(delay_time)

    def update_players_role(self, player_num, update_now=True):
        self.player_stats[player_num][1].fill(
            (255, 255, 255, 255 * VIEW_TRANSPARENT))
        if self.players[player_num].role == PlayerRole.DEFENDER:
            rendered_text = self.player_font.render(
                "Defender", True, (0, 64, 192)).convert_alpha()
            self.center_text_on_surface(
                self.player_stats[player_num][1], rendered_text,
                (255, 255, 255, 255 * VIEW_TRANSPARENT))
        elif self.players[player_num].role == PlayerRole.ATTACKER:
            rendered_text = self.player_font.render(
                "Attacker", True, (192, 0, 0)).convert_alpha()
            self.center_text_on_surface(
                self.player_stats[player_num][1], rendered_text,
                (255, 255, 255, 255 * VIEW_TRANSPARENT))
        if update_now:
            self.update_table.emit()

    def update_player_wins(self, player_num, update_now=True):
        self.player_stats[player_num][2].fill(
            (255, 255, 255, 255 * VIEW_TRANSPARENT))
        if self.players[player_num].score > 1:
            rendered_text = self.player_font.render(
                "Wins: {0:d}".format(self.players[player_num].score), True,
                (255, 255, 255)).convert_alpha()
        else:
            rendered_text = self.player_font.render(
                "Win: {0:d}".format(self.players[player_num].score), True,
                (255, 255, 255)).convert_alpha()
        self.center_text_on_surface(self.player_stats[player_num][2],
                                    rendered_text,
                                    (255, 255, 255, 255 * VIEW_TRANSPARENT))
        if update_now:
            self.update_table.emit()

    def update_all_players(self, role=False, wins=True):
        for i in range(4):
            if wins:
                self.update_player_wins(i, update_now=False)
            if role:
                self.update_players_role(i, update_now=False)
        self.update_table.emit()

    def display_current_player(self, current=-1):
        if current >= 0:
            print("Player {0:d}\n".format(current))
        for i in range(4):
            rendered_text = self.player_font.render(
                "Player {0:d}".format(i), True, (255, 0, 255)).convert_alpha()
            if i == current:
                self.center_text_on_surface(self.player_stats[i][0],
                                            rendered_text, (0, 64, 0, 255))
            else:
                self.center_text_on_surface(
                    self.player_stats[i][0], rendered_text,
                    (255, 255, 255, 255 * VIEW_TRANSPARENT))

        self.update_table.emit()

    def update_team_scores(self):
        if self.table_status['partner reveal']:
            msg = "Defender: {0:d}/{2:d}, Attacker: {1:d}/{3:d}\n".format(
                self.table_status['defender']['wins'],
                self.table_status['attacker']['wins'],
                self.table_status['defender']['target'],
                self.table_status['attacker']['target'])
            self.write_message(msg, line=2)
        else:
            msg = "Defender: {0:d}?/{1:d}, Attacker: ?/{2:d}\n".format(
                self.table_status['defender']['wins'],
                self.table_status['defender']['target'],
                self.table_status['attacker']['target'])
            self.write_message(msg, line=2)

    def get_pos(self):
        return self.x, self.y

    def continue_game(self):
        """
        This is where the FSM is. State transition should occur here.
        What takes place in the state should be in a function.
        :return: None
        """
        # TODO: Adjust the timing of sleep
        if self.game_state == GameState.DEALING:
            self.shuffle_and_deal()
            self.write_message("Shuffle Complete!")
            self.game_state = GameState.POINT_CHECK

        elif self.game_state == GameState.POINT_CHECK:
            if self.check_reshuffle():
                self.write_message('Reshuffle Initiated!', line=1)
                self.game_state = GameState.ENDING
            else:
                self.write_message('No Reshuffle needed!')
                self.game_state = GameState.BIDDING
                self.write_message("Start to Bid")
                self.prepare_bidding()
        elif self.game_state == GameState.BIDDING:
            bid_complete = self.start_bidding()
            if bid_complete:
                self.game_state = GameState.PLAYING
                self.update_all_players(role=True, wins=True)
                self.update_team_scores()

        elif self.game_state == GameState.PLAYING:
            self.play_a_round()
            if self.current_round == 13:
                self.write_message("Game Set! Press P to play again!")
                self.ongoing = False
                self.game_state = GameState.ENDING
        else:
            self.reset_game()
            self.game_state = GameState.DEALING

    def shuffle_and_deal(self):
        """
        Shuffle and deal the discard deck to the players, which should have 52 cards.
        :return: None
        """
        if self.discard_deck:
            for i in range(10):
                random.shuffle(self.discard_deck)
            for player in self.players:
                for i in range(STARTING_HAND):
                    player.add_card(self.discard_deck.pop())
            self.update_table.emit()

    def check_reshuffle(self):
        """
        Detect any possible reshuffle request within the players
        :return: True if reshuffle requested, else False
        """
        print("Player Point Count")
        for i, player in enumerate(self.players):
            print("Player {0:d}: {1:d}".format(i, player.get_card_points()))
            if player.get_card_points() < 4:
                self.write_message(
                    "Low points detected in Player {0:d}! ".format(i))
                return player.make_decision(self.game_state, 0)

    def prepare_bidding(self):
        # Randomly pick a starting player, whom also is the current bid winner
        self.current_player = random.randint(1, NUM_OF_PLAYERS) - 1
        print("Starting Player: {0:d}".format(self.current_player))
        self.passes = 0
        self.table_status["bid"] = 11  # Lowest Bid: 1 Club by default
        self.first_player = True  # Starting bidder "privilege" to raise the starting bid
        msg = "Current Bid: {0:d} {1:s}".format(
            self.table_status["bid"] // 10,
            cards.get_suit_string(self.table_status["bid"] % 10))
        self.write_message(msg, line=1, delay_time=0)
        self.display_current_player(self.current_player)
        msg = 'Bid Leader: Player {0:d}'.format(
            (self.current_player - self.passes - 1 *
             (not self.first_player)) % 4)
        self.write_message(msg, line=2, delay_time=1)

    def start_bidding(self):
        """
        The bidding procedure.
        :return: Whether bidding is completed
        """
        # Highest bid: 7 NoTrump. No further check required
        if self.passes < NUM_OF_PLAYERS - 1 and self.table_status["bid"] < 75:
            player_bid = self.players[self.current_player].make_decision(
                self.game_state, 0)
            if not player_bid:
                if not self.first_player:  # Starting bidder pass do not count at the start
                    self.passes += 1
            else:
                self.table_status["bid"] = player_bid
                self.passes = 0

            if self.table_status["bid"] < 75:
                self.current_player += 1
                self.current_player %= 4
            msg = "Current Bid: {0:d} {1:s}".format(
                self.table_status["bid"] // 10,
                cards.get_suit_string(self.table_status["bid"] % 10))
            self.write_message(msg, line=1, update_now=False)
            msg = 'Bid Leader: Player {0:d}'.format(
                (self.current_player - self.passes - 1 *
                 (not self.first_player)) % 4)
            self.write_message(msg, line=2, update_now=False)
            self.display_current_player(self.current_player)
            if self.first_player:
                self.first_player = False
            time.sleep(0.5)
            return False
        else:
            self.write_message("Player {0:d} is the bid winner!".format(
                self.current_player),
                               delay_time=1)
            msg = "Player {0:d} is calling a partner...".format(
                self.current_player)
            self.write_message(msg, delay_time=1)
            self.display_current_player(self.current_player)
            # Ask for the partner card
            self.table_status["partner"] = self.players[
                self.current_player].make_decision(self.game_state, 1)

            # Setup the table status before the play starts
            self.table_status['partner reveal'] = False
            self.table_status["trump suit"] = self.table_status["bid"] % 10
            self.table_status["trump broken"] = False
            self.table_status['played cards'] = [0, 0, 0, 0]
            if self.table_status['trump suit'] == 5:
                self.table_status["leading player"] = self.current_player
            else:
                self.table_status["leading player"] = (self.current_player +
                                                       1) % 4
            self.table_status['defender'][
                'target'] = self.table_status["bid"] // 10 + 6
            self.table_status['attacker'][
                'target'] = 14 - self.table_status['defender']['target']

            # Set the roles of the players
            self.players[self.current_player].role = PlayerRole.DEFENDER

            self.write_message('Bidding Complete', delay_time=0)
            msg = 'Trump: {1:s}, Partner: {0:s}'.format(
                cards.get_card_string(self.table_status["partner"]),
                cards.get_suit_string(self.table_status['trump suit']))
            self.write_message(msg, line=1, delay_time=1)
            return True

    def play_a_round(self):
        """
        Ask each player to play a valid card and determine the winner of the round
        :return: None
        """
        if not any(self.table_status["played cards"]):
            # Leading player starts with the leading card, which determines the leading suit
            self.current_player = self.table_status['leading player']
            self.display_current_player(self.current_player)
            card = self.players[self.current_player].make_decision(
                self.game_state, 0)
            self.table_status["played cards"][self.current_player] = card
            self.players_playzone[self.current_player].add_card(card)
        elif not all(self.table_status["played cards"]):
            # Subsequent player make their plays, following suit if possible
            self.display_current_player(self.current_player)
            print("Player {0:d}\n".format(self.current_player))
            card = self.players[self.current_player].make_decision(
                self.game_state, 1)
            self.players_playzone[self.current_player].add_card(card)
            self.table_status["played cards"][self.current_player] = card
        else:
            # Once all player played, find out who wins
            leading_card = self.table_status["played cards"][
                self.table_status['leading player']]
            card_suits = [
                card.suit() for card in self.table_status["played cards"]
            ]
            card_nums = [
                card.number() for card in self.table_status["played cards"]
            ]
            follow_suits = [suit == leading_card.suit() for suit in card_suits]
            trumps = [
                suit == self.table_status['trump suit'] for suit in card_suits
            ]
            trump_played = any(trumps)

            # Break trump if the trump suit is played
            if not self.table_status['trump broken']:
                if trump_played:
                    self.table_status['trump broken'] = True
                    self.write_message("Trump Broken!", delay_time=1)

            # Determine which players to check for winner, and determine winner
            valid_nums = [
                card_nums[i] *
                ((follow_suits[i] and not trump_played) or trumps[i])
                for i in range(4)
            ]
            winning_player = valid_nums.index(max(valid_nums))
            self.write_message("Player {0:d} wins!\n".format(winning_player),
                               delay_time=1)
            self.players[winning_player].score += 1
            self.update_player_wins(winning_player)

            # Clean up the cards, update score, set the next leading player, update round history
            for deck in self.players_playzone:
                self.discard_deck.append(deck.remove_card())

            if self.players[winning_player].role == PlayerRole.DEFENDER:
                self.table_status['defender']['wins'] += 1
            elif self.players[winning_player].role == PlayerRole.ATTACKER:
                self.table_status['attacker']['wins'] += 1

            self.table_status['leading player'] = winning_player
            self.table_status['round history'].append(
                copy.copy(self.table_status["played cards"]))
            self.update_team_scores()
            self.table_status["played cards"] = [0] * 4
            self.current_round += 1
            self.update_table.emit()
            return

        if not self.table_status['partner reveal']:
            if card.value == self.table_status['partner']:
                self.table_status['partner reveal'] = True
                self.write_message("Partner Revealed!", delay_time=1)
                self.reveal_all_roles(self.current_player)
                self.update_all_players(role=True, wins=False)

        self.current_player += 1
        self.current_player %= 4
        self.update_table.emit()
        time.sleep(0.5)

    def reveal_all_roles(self, partner):
        self.players[partner].role = PlayerRole.DEFENDER
        self.table_status['defender']['wins'] += self.players[partner].score
        for i in range(4):
            if self.players[i].role == PlayerRole.UNKNOWN:
                self.players[i].role = PlayerRole.ATTACKER
                self.table_status['attacker']['wins'] += self.players[i].score

    def reset_game(self):
        for player in self.players:
            while not player.is_empty():
                self.discard_deck.append(player.remove_card())
            player.score = 0
            player.role = PlayerRole.UNKNOWN

        for i in range(4):
            self.update_players_role(i)
            self.update_player_wins(i)
        self.table_status['defender']['wins'] = 0
        self.table_status['attacker']['wins'] = 0
        self.table_status["played cards"] = [0] * 4
        self.table_status['round history'] = []
        self.current_round = 0
        self.write_message("", line=1, update_now=False)
        self.write_message("", line=2)
        self.display_current_player()
        print(len(self.discard_deck))
        self.update_table.emit()
Example #48
0
class Table:
    """
    A Table is the place where all actions takes place. It is essentially a FSM, doing different
    routines at each state. It needs to keep track of the score, roles, the rules, etc. It needs
    to ask each player for decisions and respond to them accordingly. The table will also need
    to inform any decision to the Main Screen so that it can update the screen to reflect that
    change through the use of callbacks (Signal and Slot). This call should be minimised by making
    all the changes before calling to update the screen in one go.

    FSM cycles
    ---
    Preloop - Prepare the cards once
            - Initiate Players and connect them to the Table
    1.  Shuffle and Deal out cards to Players.
    2a. Detect weak hands and ask for reshuffle.
    2b. Return to (1) if any reshuffle occurs, otherwise proceed.
    3.  Bidding round. Randomly pick a starting player, in clockwise manner
        ask for a bid until it is valid.
    3b. Proceed only if 3 consecutive skips are detected.
    3c. Ask the winner of the bid a card not in their hand.
    3d. Set up the player roles, trump suit, rounds to win for both side
    3e.  Play the game. Start with bid winner if NO TRUMP, otherwise
        Starting next to the bid winner.
    4a.  With the first player, ask for any card, excluding trump suits if trump
        is not broken
    4b. With subsequent players, ask for cards that follow the suit of the first player
        , include trump suit if trump is broken. Ask for any card if the player cannot
        follow suit.
    4c. Once all 4 players has made valid plays, announce results, update scoring. Announce
        player roles if the partner card is played. Break trump if trump is played.
    4d. Repeat 4 until 13 rounds are made. Maybe add early win if confirmed one side wins
    5.  Ask for a new game. Go back to 1 if true.

    All played cards go into a hidden discard pile.

    """
    def __init__(self,
                 x,
                 y,
                 width,
                 height,
                 clear_colour,
                 autoplay=False,
                 view_all_cards=False,
                 terminal=False):
        # TODO: Reduce the amount of update_table call
        self.update_table = Signal()
        self.x = x
        self.y = y
        self.width = width
        self.height = height

        self.table_font = pygame.font.SysFont("None", 25)
        self.player_font = pygame.font.SysFont("None", 25)

        # For gameplay
        self.game_state = GameState.DEALING
        self.reshuffling_players = []
        self.current_round = 0
        self.passes = 0
        self.current_player = 0
        self.first_player = False  # This is for bidding purposes
        self.players = []
        self.players_playzone = []
        # Table status will be made known to the player by reference
        self.table_status = {
            'played cards': [0, 0, 0, 0],
            'leading player': 0,
            'trump suit': 1,
            'trump broken': False,
            'round history': [],
            'bid': 0,
            'partner': 0,
            'partner reveal': False,
            'defender': {
                'target': 0,
                'wins': 0
            },
            'attacker': {
                'target': 0,
                'wins': 0
            }
        }

        # Prepare the surfaces for displaying
        self.background = pygame.Surface((self.width, self.height))
        self.background.fill(clear_colour)
        self.background = self.background.convert()

        # TODO: Update the drawing of the table?
        # Prepare the card with dimensions
        w_deck = min(self.height, self.width) * 0.18
        l_deck = min(self.width, self.height) * 0.7
        # This is not a deck as it will never be drawn
        self.discard_deck = cards.prepare_playing_cards(
            int(w_deck * 0.6), int(w_deck * 0.6 * 97 / 71))
        game_margins = 5

        # Players' deck positioning
        playerx = ((self.width - l_deck) // 2, game_margins,
                   (self.width - l_deck) // 2,
                   self.width - w_deck - game_margins)
        playery = (self.height - w_deck - game_margins,
                   (self.height - l_deck) // 2, game_margins,
                   (self.height - l_deck) // 2)
        h_spacing = 20
        v_spacing = 25

        # Middle playfield for announcer and player playing deck positioning
        playfield_margins = 5
        margins_with_w_deck = w_deck + playfield_margins + game_margins
        playfield_x = margins_with_w_deck
        playfield_y = margins_with_w_deck
        playfield_width = self.width - margins_with_w_deck * 2
        playfield_height = self.height - margins_with_w_deck * 2

        playdeckx = (playfield_x + (playfield_width - w_deck) / 2, playfield_x,
                     playfield_x + (playfield_width - w_deck) / 2,
                     playfield_x + playfield_width - w_deck)
        playdecky = (playfield_y + playfield_height - w_deck,
                     playfield_y + (playfield_height - w_deck) / 2,
                     playfield_y,
                     playfield_y + (playfield_height - w_deck) / 2)

        # Player stats positioning
        stats_width = 100
        self.stats_height = 100
        stats_spacing = 10
        self.player_stats_x = (playdeckx[0] - stats_width - stats_spacing,
                               playdeckx[1],
                               playdeckx[2] + w_deck + stats_spacing,
                               playdeckx[3])
        self.player_stats_y = (playdecky[0] + w_deck - self.stats_height,
                               playdecky[1] - self.stats_height -
                               stats_spacing, playdecky[2],
                               playdecky[3] - w_deck - stats_spacing)

        self.player_stats = [[], [], [], []]

        # TODO: change surface to use colorkey, maybe, if the performance is tanked
        # Prepare all the player surfaces
        for i in range(NUM_OF_PLAYERS):
            vert = i % 2 == 1
            spacing = h_spacing
            if vert:
                spacing = v_spacing

            reveal_mode = cards.DeckReveal.HIDE_ALL
            if i == 0 or view_all_cards:
                reveal_mode = cards.DeckReveal.SHOW_ALL

            if i == 0:
                player_class = players.MainPlayer
                if terminal:
                    player_class = players.Player
                self.players.append(
                    player_class(playerx[i],
                                 playery[i],
                                 l_deck,
                                 w_deck,
                                 spacing,
                                 vert_orientation=vert,
                                 deck_reveal=reveal_mode))
            else:
                self.players.append(
                    players.Player(playerx[i],
                                   playery[i],
                                   l_deck,
                                   w_deck,
                                   spacing,
                                   vert_orientation=vert,
                                   deck_reveal=reveal_mode,
                                   flip=(i == 1 or i == 2),
                                   draw_from_last=(i == 2 or i == 3)))

            self.players[i].connect_to_table(self.table_status)
            if i > 0:
                self.players[i].add_ai(ai.VivianAI(self.table_status))

            self.players_playzone.append(
                cards.Deck(playdeckx[i], playdecky[i], w_deck, w_deck, 0))
            for j in range(3):
                surf = pygame.Surface((stats_width, self.stats_height / 3),
                                      pygame.SRCALPHA)
                rendered_text = self.player_font.render(
                    "Player {0:d}".format(i), True,
                    (255, 0, 255)).convert_alpha()
                self.center_text_on_surface(
                    surf, rendered_text,
                    (255, 255, 255, 255 * VIEW_TRANSPARENT))
                self.player_stats[i].append(surf)

        if autoplay:
            self.players[0].add_ai(ai.VivianAI(self.table_status))

        # Announcer positioning and surface creation
        announcer_margins = 5
        announcer_spacing = announcer_margins + w_deck
        self.announcer_x = playfield_x + announcer_spacing
        self.announcer_y = playfield_y + announcer_spacing
        self.announcer_width = playfield_width - 2 * announcer_spacing
        self.announcer_height = playfield_height - 2 * announcer_spacing
        self.announcer_line = []
        for i in range(3):
            surf = pygame.Surface(
                (self.announcer_width, self.announcer_height / 3),
                pygame.SRCALPHA)
            self.announcer_line.append(surf)

        self.update_all_players(role=True, wins=True, clear_wins=True)

        self.write_message("Press P to play!")

        self.ongoing = False
        self.require_player_input = False

        self.terminal_play = terminal
        self.calling_panel = UI.CallPanel(playdeckx[0] + w_deck + 5,
                                          playdecky[0] + w_deck - 100, 220,
                                          100)
        self.calling_panel.parent = self
        self.calling_panel.visible = False
        self.parent = None

        self.calling_panel.confirm_output.connect(self.emit_call)

        self.yes_button = UI.Button(playdeckx[0] + w_deck + 5,
                                    playdecky[0],
                                    50,
                                    25,
                                    text='yes')
        self.yes_button.visible = False
        self.yes_button.clicked.connect(lambda **z: pygame.event.post(
            pygame.event.Event(CALL_EVENT, call=True)))

        self.no_button = UI.Button(playdeckx[0] + w_deck + 5,
                                   playdecky[0] + 25 + 25,
                                   50,
                                   25,
                                   text='no')
        self.no_button.clicked.connect(lambda **z: pygame.event.post(
            pygame.event.Event(CALL_EVENT, call=False)))
        self.no_button.visible = False

        self.UI_elements = [
            self.calling_panel, self.yes_button, self.no_button
        ]

    def emit_call(self, output, **kwargs):
        pygame.event.post(pygame.event.Event(CALL_EVENT, call=output))

    def get_offset_pos(self):
        x, y = 0, 0
        if self.parent:
            x, y = self.parent.get_offset_pos()

        return x + self.x, y + self.y

    def center_text_on_surface(self, surf, rendered_text, clear_colour):
        """
        Blit the text centered in the surface rect box
        :param surf:
        :param rendered_text:
        :param clear_colour:
        :return:
        """
        line_center = surf.get_rect().center
        text_rect = rendered_text.get_rect(center=line_center)
        surf.fill(clear_colour)
        surf.blit(rendered_text, text_rect)

    def get_pos(self):
        return self.x, self.y

    def process_UI(self, event):
        draw_update = False
        #if event.type == pygame.KEYUP:
        #    if event.key == pygame.K_o:
        #        self.calling_panel.visible = not self.calling_panel.visible
        #    draw_update = True
        for element in self.UI_elements:
            if element.visible and \
                    element.process_events(event):
                draw_update = True

        if draw_update:
            self.update_table.emit()

    def continue_game(self, game_events):
        """
        This is where the FSM is. State transition should occur here.
        What takes place in the state should be in a function.
        :return: None
        """
        # TODO: Adjust the timing of sleep
        if self.game_state == GameState.DEALING:
            self.shuffle_and_deal()
            self.write_message("Shuffle Complete!")
            self.reshuffling_players = []
            for i, player in enumerate(self.players):
                if player.get_card_points() < 4:
                    self.write_message(
                        "Low points detected in Player {0:d}! ".format(i))
                    self.reshuffling_players.append(i)

            if not self.reshuffling_players:
                self.write_message('No Reshuffle needed!')
                self.game_state = GameState.BIDDING
                self.write_message("Start to Bid")
                self.prepare_bidding()
            else:
                self.current_player = self.reshuffling_players[0]
                self.game_state = GameState.POINT_CHECK

        elif self.game_state == GameState.POINT_CHECK:
            reshuffle = self.check_reshuffle(game_events)
            if reshuffle is None:
                return
            if reshuffle is False and not self.current_player == self.reshuffling_players[
                    -1]:
                return
            else:
                if reshuffle:
                    self.write_message('Reshuffle Initiated!', line=1)
                    self.game_state = GameState.ENDING
                else:
                    self.write_message('No Reshuffle needed!')
                    self.game_state = GameState.BIDDING
                    self.write_message("Start to Bid")
                    self.prepare_bidding()
        elif self.game_state == GameState.BIDDING:
            bid_complete = self.start_bidding(game_events)
            if bid_complete:
                self.game_state = GameState.PLAYING
                self.update_all_players(role=True, wins=True)
                self.update_team_scores()

        elif self.game_state == GameState.PLAYING:
            self.play_a_round(game_events)
            if self.current_round == 13:
                self.declare_winner()
                self.ongoing = False
                self.game_state = GameState.ENDING
        else:
            self.reset_game()
            self.game_state = GameState.DEALING

    def shuffle_and_deal(self):
        """
        Shuffle and deal the discard deck to the players, which should have 52 cards.
        :return: None
        """
        if self.discard_deck:
            for i in range(10):
                random.shuffle(self.discard_deck)
            for player in self.players:
                for i in range(STARTING_HAND):
                    player.add_card(self.discard_deck.pop())
            self.update_table.emit()

    def check_reshuffle(self, game_events):
        """
        Detect any possible reshuffle request within the players
        :return: True if reshuffle requested, else False
        """
        if not self.require_player_input:
            if not self.players[self.current_player].AI:
                self.require_player_input = True
                self.write_message("Do you want a reshuffle?",
                                   line=1,
                                   update_now=False)
                self.yes_button.visible = True
                self.no_button.visible = True
                self.update_table.emit()
                return
            else:
                reshuffle = self.players[self.current_player].make_decision(
                    self.game_state, 0)
        else:
            reshuffle = self.players[self.current_player].make_decision(
                self.game_state, 0, game_events)

            if reshuffle is None:
                return None
            self.require_player_input = False
            self.yes_button.visible = False
            self.no_button.visible = False
            self.update_table.emit()

        self.current_player = (self.current_player + 1) % NUM_OF_PLAYERS
        while self.current_player not in self.reshuffling_players:
            self.current_player = (self.current_player + 1) % NUM_OF_PLAYERS
        return reshuffle

    def prepare_bidding(self):
        # Randomly pick a starting player, whom also is the current bid winner
        self.current_player = random.randint(1, NUM_OF_PLAYERS) - 1
        print("Starting Player: {0:d}".format(self.current_player))
        self.passes = 0
        self.table_status["bid"] = 11  # Lowest Bid: 1 Club by default
        self.first_player = True  # Starting bidder "privilege" to raise the starting bid
        msg = "Current Bid: {0:d} {1:s}".format(
            self.table_status["bid"] // 10,
            cards.get_suit_string(self.table_status["bid"] % 10))
        self.write_message(msg, line=1, delay_time=0)
        self.display_current_player(self.current_player)
        self.update_player_bid(self.current_player, 11, update_now=False)
        msg = 'Bid Leader: Player {0:d}'.format(
            (self.current_player - self.passes - 1 *
             (not self.first_player)) % NUM_OF_PLAYERS)
        self.write_message(msg, line=2, delay_time=0.5)

        if not self.terminal_play:
            self.calling_panel.cancel_button.visible = True
            self.calling_panel.change_lists_elements(
                [str(i + 1) for i in range(7)],
                ['Clubs', 'Diamonds', 'Hearts', 'Spades', 'No Trump'])

    def start_bidding(self, game_events):
        """
        The bidding procedure. Flag up if player input required
        :return: Whether bidding is completed
        """
        # Highest bid: 7 NoTrump. No further check required
        if self.passes < NUM_OF_PLAYERS - 1 and self.table_status["bid"] < 75:
            if not self.require_player_input:
                if not self.players[self.current_player].AI:
                    self.require_player_input = True
                    if not self.terminal_play:
                        self.calling_panel.visible = True
                        self.update_table.emit()
                    return False
                else:
                    player_bid = self.players[
                        self.current_player].make_decision(self.game_state, 0)
            else:
                player_bid, msg = self.players[
                    self.current_player].make_decision(self.game_state, 0,
                                                       game_events)
                if msg:
                    self.write_message(msg, delay_time=1, update_now=True)
                if player_bid < 0:
                    return False
                self.require_player_input = False
                self.write_message("", delay_time=0, update_now=False)
                if not self.terminal_play:
                    self.calling_panel.visible = False
                    self.update_table.emit()
            if not player_bid:
                if not self.first_player:  # Starting bidder pass do not count at the start
                    self.passes += 1
            else:
                self.table_status["bid"] = player_bid
                self.passes = 0
                msg = "Current Bid: {0:d} {1:s}".format(
                    self.table_status["bid"] // 10,
                    cards.get_suit_string(self.table_status["bid"] % 10))
                self.write_message(msg, line=1, update_now=False)
                msg = 'Bid Leader: Player {0:d}'.format(self.current_player)
                self.write_message(msg, line=2, update_now=True)

            if self.first_player:
                self.first_player = False
                if player_bid:
                    self.update_player_bid(self.current_player,
                                           player_bid,
                                           update_now=False)
            else:
                self.update_player_bid(self.current_player,
                                       player_bid,
                                       update_now=False)

            if self.table_status["bid"] < 75:
                self.current_player += 1
                self.current_player %= NUM_OF_PLAYERS
            self.display_current_player(self.current_player)

            time.sleep(0.5)
            if self.passes == NUM_OF_PLAYERS - 1 or self.table_status[
                    "bid"] == 75:
                if not self.terminal_play:
                    self.calling_panel.cancel_button.visible = False
                    self.calling_panel.change_lists_elements([
                        '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q',
                        'K', 'A'
                    ], ['Clubs', 'Diamonds', 'Hearts', 'Spades'])
            return False
        else:
            if not self.require_player_input:
                self.write_message("Player {0:d} is the bid winner!".format(
                    self.current_player),
                                   delay_time=1)
                msg = "Player {0:d} is calling a partner...".format(
                    self.current_player)
                self.write_message(msg, delay_time=1)
                self.display_current_player(self.current_player)
                if not self.players[self.current_player].AI:
                    self.require_player_input = True
                    if not self.terminal_play:
                        self.calling_panel.visible = True
                        self.update_table.emit()
                    return False
                else:
                    # Ask for the partner card
                    self.table_status["partner"] = self.players[
                        self.current_player].make_decision(self.game_state, 1)
            else:
                partner, msg = self.players[self.current_player].make_decision(
                    self.game_state, 1, game_events)
                if msg:
                    self.write_message(msg, delay_time=0, update_now=True)

                if not partner:
                    return False

                self.table_status["partner"] = partner
                self.require_player_input = False
                if not self.terminal_play:
                    self.calling_panel.visible = False
                    self.update_table.emit()

            # Setup the table status before the play starts
            self.table_status['partner reveal'] = False
            self.table_status["trump suit"] = self.table_status["bid"] % 10
            self.table_status["trump broken"] = False
            self.table_status['played cards'] = [0, 0, 0, 0]
            if self.table_status['trump suit'] == 5:
                self.table_status["leading player"] = self.current_player
            else:
                self.table_status["leading player"] = (self.current_player +
                                                       1) % NUM_OF_PLAYERS
            self.table_status['defender'][
                'target'] = self.table_status["bid"] // 10 + 6
            self.table_status['attacker'][
                'target'] = 14 - self.table_status['defender']['target']

            # Set the roles of the players
            self.players[self.current_player].role = PlayerRole.DECLARER

            self.write_message('Bidding Complete', delay_time=0)
            msg = 'Trump: {1:s}, Partner: {0:s}'.format(
                cards.get_card_string(self.table_status["partner"]),
                cards.get_suit_string(self.table_status['trump suit']))
            self.write_message(msg, line=1, delay_time=1)
            return True

    def play_a_round(self, game_events):
        """
        Ask each player to play a valid card and determine the winner of the round
        This must work without pause if only bots are playing
        The function will exit after every player decision or if a user input is needed.
        If a user input is required, the function will continuously exit without proceeding to the next player
        until a valid input is received.

        :return: None
        """
        if not any(self.table_status["played cards"]):
            # Leading player starts with the leading card, which determines the leading suit
            if not self.require_player_input:
                if self.table_status['trump broken']:
                    self.write_message("Trump has been broken!", delay_time=0)
                else:
                    self.write_message("Trump is not broken", delay_time=0)
                self.current_player = self.table_status['leading player']
                self.display_current_player(self.current_player)
                if not self.players[self.current_player].AI:
                    self.require_player_input = True
                    return
                else:
                    card = self.players[self.current_player].make_decision(
                        self.game_state, 0)
            else:
                card, msg = self.players[self.current_player].make_decision(
                    self.game_state, 0, game_events)
                if msg:
                    self.write_message(msg, delay_time=0, update_now=True)
                if not type(card) is cards.Card:
                    if card:
                        self.update_table.emit()
                    return
                self.require_player_input = False

            self.table_status["played cards"][self.current_player] = card
            self.players_playzone[self.current_player].add_card(card)
        elif not all(self.table_status["played cards"]):
            # Subsequent player make their plays, following suit if possible
            if not self.require_player_input:
                self.display_current_player(self.current_player)
                if not self.players[self.current_player].AI:
                    self.require_player_input = True
                    return
                else:
                    card = self.players[self.current_player].make_decision(
                        self.game_state, 1)
            else:
                card, msg = self.players[self.current_player].make_decision(
                    self.game_state, 1, game_events)
                if msg:
                    self.write_message(msg, delay_time=0, update_now=False)
                if not type(card) is cards.Card:
                    if card:
                        self.update_table.emit()
                    return
                self.require_player_input = False

            self.players_playzone[self.current_player].add_card(card)
            self.table_status["played cards"][self.current_player] = card
        else:
            # Once all player played, find out who wins
            leading_card = self.table_status["played cards"][
                self.table_status['leading player']]
            card_suits = [
                card.suit() for card in self.table_status["played cards"]
            ]
            card_nums = [
                card.number() for card in self.table_status["played cards"]
            ]
            follow_suits = [suit == leading_card.suit() for suit in card_suits]
            trumps = [
                suit == self.table_status['trump suit'] for suit in card_suits
            ]

            # Determine which players to check for winner, and determine winner
            if any(trumps):
                valid_nums = [
                    card_nums[i] * trumps[i] for i in range(NUM_OF_PLAYERS)
                ]
            else:
                valid_nums = [
                    card_nums[i] * follow_suits[i]
                    for i in range(NUM_OF_PLAYERS)
                ]

            winning_player = valid_nums.index(max(valid_nums))
            self.write_message("Player {0:d} wins!\n".format(winning_player),
                               delay_time=1)
            self.players[winning_player].score += 1
            self.update_player_wins(winning_player)

            # Clean up the cards, update score, set the next leading player, update round history
            for deck in self.players_playzone:
                self.discard_deck.append(deck.remove_card())

            for player in self.players:
                if player.AI:
                    player.AI.update_memory()

            if self.players[winning_player].role == PlayerRole.DECLARER or\
               self.players[winning_player].role == PlayerRole.PARTNER:
                self.table_status['defender']['wins'] += 1
            elif self.players[winning_player].role == PlayerRole.ATTACKER:
                self.table_status['attacker']['wins'] += 1

            self.table_status['leading player'] = winning_player
            self.table_status['round history'].append(
                copy.copy(self.table_status["played cards"]))
            self.update_team_scores()
            self.table_status["played cards"] = [0] * NUM_OF_PLAYERS
            self.current_round += 1
            self.update_table.emit()

            return

        # Break trump if the trump suit is played
        if not self.table_status['trump broken']:
            self.table_status['trump broken'] = card.suit(
            ) == self.table_status['trump suit']
            if self.table_status['trump broken']:
                self.write_message("Trump broken!", delay_time=1)

        if not self.table_status['partner reveal']:
            if card.value == self.table_status['partner']:
                self.table_status['partner reveal'] = True
                self.write_message("Partner Revealed!", delay_time=1)
                self.reveal_all_roles(self.current_player)
                self.update_all_players(role=True, wins=False)

        self.current_player += 1
        self.current_player %= NUM_OF_PLAYERS
        self.update_table.emit()
        time.sleep(0.5)

    def write_message(self, text, delay_time=0.5, line=0, update_now=True):
        """
        Write a message into the center board surface (announcer)
        :param text: String to be displayed on the center board
        :param delay_time: How much delay to put once the string is display
        :param line: Which line of the announcer to write to
        :param update_now:
        :return: None
        """
        if 0 <= line < len(self.announcer_line):
            print(text)
            text = text.strip('\n')
            rendered_text = self.table_font.render(
                text, True, (255, 255, 255)).convert_alpha()
            self.center_text_on_surface(
                self.announcer_line[line], rendered_text,
                (255, 255, 255, 255 * VIEW_TRANSPARENT))
            if update_now:
                self.update_table.emit()
                time.sleep(delay_time)

    def update_players_role(self, player_num, update_now=True):
        """
        Update the display of the player roles. Blank if UNKNOWN
        :param player_num:
        :param update_now:
        :return:
        """
        self.player_stats[player_num][1].fill(
            (255, 255, 255, 255 * VIEW_TRANSPARENT))
        role_text = ''
        colour = (0, 239, 224)
        if self.players[player_num].role == PlayerRole.DECLARER:
            role_text = 'Declarer'
        elif self.players[player_num].role == PlayerRole.ATTACKER:
            role_text = 'Attacker'
            colour = (225, 0, 0)
        elif self.players[player_num].role == PlayerRole.PARTNER:
            role_text = 'Partner'
        rendered_text = self.player_font.render(role_text, True,
                                                colour).convert_alpha()
        self.center_text_on_surface(self.player_stats[player_num][1],
                                    rendered_text,
                                    (255, 255, 255, 255 * VIEW_TRANSPARENT))
        if update_now:
            self.update_table.emit()

    def update_player_wins(self, player_num, update_now=True, clear=False):
        """
        Update the display of player's number of wins.
        :param player_num:
        :param update_now:
        :param clear:
        :return:
        """
        self.player_stats[player_num][2].fill(
            (255, 255, 255, 255 * VIEW_TRANSPARENT))
        if not clear:
            if self.players[player_num].score > 1:
                rendered_text = self.player_font.render(
                    "Wins: {0:d}".format(self.players[player_num].score), True,
                    (255, 255, 255)).convert_alpha()
            else:
                rendered_text = self.player_font.render(
                    "Win: {0:d}".format(self.players[player_num].score), True,
                    (255, 255, 255)).convert_alpha()
            self.center_text_on_surface(
                self.player_stats[player_num][2], rendered_text,
                (255, 255, 255, 255 * VIEW_TRANSPARENT))
        if update_now:
            self.update_table.emit()

    def update_player_bid(self, player_num, bid, update_now=True):
        """
        Update the display of the player's last bid.
        :param player_num:
        :param update_now:
        :param clear:
        :return:
        """
        self.player_stats[player_num][2].fill(
            (255, 255, 255, 255 * VIEW_TRANSPARENT))
        if not bid:
            rendered_text = self.player_font.render(
                "Pass".format(self.players[player_num].score), True,
                (255, 255, 255)).convert_alpha()
        else:
            bid_text = str(bid // 10) + ' ' + cards.get_suit_string(bid % 10)
            rendered_text = self.player_font.render(
                bid_text.format(self.players[player_num].score), True,
                (255, 255, 255)).convert_alpha()
        self.center_text_on_surface(self.player_stats[player_num][2],
                                    rendered_text,
                                    (255, 255, 255, 255 * VIEW_TRANSPARENT))
        if update_now:
            self.update_table.emit()

    def update_all_players(self, role=False, wins=True, clear_wins=False):
        for i in range(NUM_OF_PLAYERS):
            if wins:
                self.update_player_wins(i, update_now=False, clear=clear_wins)
            if role:
                self.update_players_role(i, update_now=False)
        self.update_table.emit()

    def display_current_player(self, current=-1):
        if current >= 0:
            print("Player {0:d}\n".format(current))
        for i in range(NUM_OF_PLAYERS):
            rendered_text = self.player_font.render(
                "Player {0:d}".format(i), True, (255, 0, 255)).convert_alpha()
            if i == current:
                self.center_text_on_surface(self.player_stats[i][0],
                                            rendered_text, (0, 64, 0, 255))
            else:
                self.center_text_on_surface(
                    self.player_stats[i][0], rendered_text,
                    (255, 255, 255, 255 * VIEW_TRANSPARENT))

        self.update_table.emit()

    def update_team_scores(self):
        if self.table_status['partner reveal']:
            msg = "Declarer: {0:d}/{2:d}, Attacker: {1:d}/{3:d}\n".format(
                self.table_status['defender']['wins'],
                self.table_status['attacker']['wins'],
                self.table_status['defender']['target'],
                self.table_status['attacker']['target'])
            self.write_message(msg, line=2)
        else:
            msg = "Declarer: {0:d}?/{1:d}, Attacker: ?/{2:d}\n".format(
                self.table_status['defender']['wins'],
                self.table_status['defender']['target'],
                self.table_status['attacker']['target'])
            self.write_message(msg, line=2)

    def reveal_all_roles(self, partner):
        """
        Update all roles once the partner card is shown
        Also updates the partner to the player number
        :param partner:
        :return:
        """
        self.players[partner].role = PlayerRole.PARTNER
        self.table_status["partner"] = partner
        self.table_status['defender']['wins'] += self.players[partner].score
        for i in range(NUM_OF_PLAYERS):
            if self.players[i].role == PlayerRole.UNKNOWN:
                self.players[i].role = PlayerRole.ATTACKER
                self.table_status['attacker']['wins'] += self.players[i].score

    def declare_winner(self):
        if self.table_status['attacker']['wins'] >= self.table_status[
                'attacker']['target']:
            self.write_message("Attacker wins! Press P to play again!")
        if self.table_status['defender']['wins'] >= self.table_status[
                'defender']['target']:
            self.write_message("Declarer wins! Press P to play again!")

    def reset_game(self):
        """
        Reset all variables for the next game
        :return:
        """
        for player in self.players:
            while not player.is_empty():
                self.discard_deck.append(player.remove_card())
            player.score = 0
            player.role = PlayerRole.UNKNOWN
            if player.AI:
                player.AI.reset_memory()

        for i in range(NUM_OF_PLAYERS):
            self.update_players_role(i)
            self.update_player_wins(i, clear=True)
        self.table_status['defender']['wins'] = 0
        self.table_status['attacker']['wins'] = 0
        self.table_status["played cards"] = [0] * NUM_OF_PLAYERS
        self.table_status['round history'] = []
        self.current_round = 0
        self.write_message("", line=1, update_now=False)
        self.write_message("", line=2)
        self.display_current_player()
        self.update_table.emit()
Example #49
0
    def __init__(self,
                 x,
                 y,
                 width,
                 height,
                 clear_colour,
                 autoplay=False,
                 view_all_cards=False,
                 terminal=False):
        # TODO: Reduce the amount of update_table call
        self.update_table = Signal()
        self.x = x
        self.y = y
        self.width = width
        self.height = height

        self.table_font = pygame.font.SysFont("None", 25)
        self.player_font = pygame.font.SysFont("None", 25)

        # For gameplay
        self.game_state = GameState.DEALING
        self.reshuffling_players = []
        self.current_round = 0
        self.passes = 0
        self.current_player = 0
        self.first_player = False  # This is for bidding purposes
        self.players = []
        self.players_playzone = []
        # Table status will be made known to the player by reference
        self.table_status = {
            'played cards': [0, 0, 0, 0],
            'leading player': 0,
            'trump suit': 1,
            'trump broken': False,
            'round history': [],
            'bid': 0,
            'partner': 0,
            'partner reveal': False,
            'defender': {
                'target': 0,
                'wins': 0
            },
            'attacker': {
                'target': 0,
                'wins': 0
            }
        }

        # Prepare the surfaces for displaying
        self.background = pygame.Surface((self.width, self.height))
        self.background.fill(clear_colour)
        self.background = self.background.convert()

        # TODO: Update the drawing of the table?
        # Prepare the card with dimensions
        w_deck = min(self.height, self.width) * 0.18
        l_deck = min(self.width, self.height) * 0.7
        # This is not a deck as it will never be drawn
        self.discard_deck = cards.prepare_playing_cards(
            int(w_deck * 0.6), int(w_deck * 0.6 * 97 / 71))
        game_margins = 5

        # Players' deck positioning
        playerx = ((self.width - l_deck) // 2, game_margins,
                   (self.width - l_deck) // 2,
                   self.width - w_deck - game_margins)
        playery = (self.height - w_deck - game_margins,
                   (self.height - l_deck) // 2, game_margins,
                   (self.height - l_deck) // 2)
        h_spacing = 20
        v_spacing = 25

        # Middle playfield for announcer and player playing deck positioning
        playfield_margins = 5
        margins_with_w_deck = w_deck + playfield_margins + game_margins
        playfield_x = margins_with_w_deck
        playfield_y = margins_with_w_deck
        playfield_width = self.width - margins_with_w_deck * 2
        playfield_height = self.height - margins_with_w_deck * 2

        playdeckx = (playfield_x + (playfield_width - w_deck) / 2, playfield_x,
                     playfield_x + (playfield_width - w_deck) / 2,
                     playfield_x + playfield_width - w_deck)
        playdecky = (playfield_y + playfield_height - w_deck,
                     playfield_y + (playfield_height - w_deck) / 2,
                     playfield_y,
                     playfield_y + (playfield_height - w_deck) / 2)

        # Player stats positioning
        stats_width = 100
        self.stats_height = 100
        stats_spacing = 10
        self.player_stats_x = (playdeckx[0] - stats_width - stats_spacing,
                               playdeckx[1],
                               playdeckx[2] + w_deck + stats_spacing,
                               playdeckx[3])
        self.player_stats_y = (playdecky[0] + w_deck - self.stats_height,
                               playdecky[1] - self.stats_height -
                               stats_spacing, playdecky[2],
                               playdecky[3] - w_deck - stats_spacing)

        self.player_stats = [[], [], [], []]

        # TODO: change surface to use colorkey, maybe, if the performance is tanked
        # Prepare all the player surfaces
        for i in range(NUM_OF_PLAYERS):
            vert = i % 2 == 1
            spacing = h_spacing
            if vert:
                spacing = v_spacing

            reveal_mode = cards.DeckReveal.HIDE_ALL
            if i == 0 or view_all_cards:
                reveal_mode = cards.DeckReveal.SHOW_ALL

            if i == 0:
                player_class = players.MainPlayer
                if terminal:
                    player_class = players.Player
                self.players.append(
                    player_class(playerx[i],
                                 playery[i],
                                 l_deck,
                                 w_deck,
                                 spacing,
                                 vert_orientation=vert,
                                 deck_reveal=reveal_mode))
            else:
                self.players.append(
                    players.Player(playerx[i],
                                   playery[i],
                                   l_deck,
                                   w_deck,
                                   spacing,
                                   vert_orientation=vert,
                                   deck_reveal=reveal_mode,
                                   flip=(i == 1 or i == 2),
                                   draw_from_last=(i == 2 or i == 3)))

            self.players[i].connect_to_table(self.table_status)
            if i > 0:
                self.players[i].add_ai(ai.VivianAI(self.table_status))

            self.players_playzone.append(
                cards.Deck(playdeckx[i], playdecky[i], w_deck, w_deck, 0))
            for j in range(3):
                surf = pygame.Surface((stats_width, self.stats_height / 3),
                                      pygame.SRCALPHA)
                rendered_text = self.player_font.render(
                    "Player {0:d}".format(i), True,
                    (255, 0, 255)).convert_alpha()
                self.center_text_on_surface(
                    surf, rendered_text,
                    (255, 255, 255, 255 * VIEW_TRANSPARENT))
                self.player_stats[i].append(surf)

        if autoplay:
            self.players[0].add_ai(ai.VivianAI(self.table_status))

        # Announcer positioning and surface creation
        announcer_margins = 5
        announcer_spacing = announcer_margins + w_deck
        self.announcer_x = playfield_x + announcer_spacing
        self.announcer_y = playfield_y + announcer_spacing
        self.announcer_width = playfield_width - 2 * announcer_spacing
        self.announcer_height = playfield_height - 2 * announcer_spacing
        self.announcer_line = []
        for i in range(3):
            surf = pygame.Surface(
                (self.announcer_width, self.announcer_height / 3),
                pygame.SRCALPHA)
            self.announcer_line.append(surf)

        self.update_all_players(role=True, wins=True, clear_wins=True)

        self.write_message("Press P to play!")

        self.ongoing = False
        self.require_player_input = False

        self.terminal_play = terminal
        self.calling_panel = UI.CallPanel(playdeckx[0] + w_deck + 5,
                                          playdecky[0] + w_deck - 100, 220,
                                          100)
        self.calling_panel.parent = self
        self.calling_panel.visible = False
        self.parent = None

        self.calling_panel.confirm_output.connect(self.emit_call)

        self.yes_button = UI.Button(playdeckx[0] + w_deck + 5,
                                    playdecky[0],
                                    50,
                                    25,
                                    text='yes')
        self.yes_button.visible = False
        self.yes_button.clicked.connect(lambda **z: pygame.event.post(
            pygame.event.Event(CALL_EVENT, call=True)))

        self.no_button = UI.Button(playdeckx[0] + w_deck + 5,
                                   playdecky[0] + 25 + 25,
                                   50,
                                   25,
                                   text='no')
        self.no_button.clicked.connect(lambda **z: pygame.event.post(
            pygame.event.Event(CALL_EVENT, call=False)))
        self.no_button.visible = False

        self.UI_elements = [
            self.calling_panel, self.yes_button, self.no_button
        ]
Example #50
0
 def setup_method(self, method):
     self.signal = Signal()
Example #51
0
class DistantIO:
    def __init__(self):
        # Init logging facility
        # From : http://sametmax.com/ecrire-des-logs-en-python/
        logger = logging.getLogger()
        logger.setLevel(logging.DEBUG)
        formatter = logging.Formatter("%(asctime)s :: %(levelname)s :: %(message)s")
        file_handler = RotatingFileHandler("api_log.log", "a", 1000000, 1)
        file_handler.setLevel(logging.DEBUG)
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)

        stream_handler = logging.StreamHandler()
        stream_handler.setLevel(logging.DEBUG)
        logger.addHandler(stream_handler)

        # Signals
        self.signal_MCU_state_changed = Signal(args=["alive"])
        self.signal_received_descriptor = Signal(args=["var_id", "var_type", "var_name", "var_writeable", "group_id"])
        self.signal_received_group_descriptor = Signal(args=["group_id", "group_name"])
        self.signal_received_value = Signal(args=["var_id"])

        self.distantio = distantio_protocol()
        self.protocol = Protocol(self.unused)

        # Queue holding received characters to be processed by worker process
        self.input_queue = mp.Queue()
        # Queue holding decoded frames
        self.output_queue = mp.Queue()
        # Conditions for controlling run process
        self.condition_new_rx_data = mp.Event()
        self.condition_new_rx_data.clear()
        self.condition_run_process = mp.Event()
        self.condition_run_process.clear()

        # Worker process for decoding characters
        self.producer_conn, self.consumer_conn = mp.Pipe()
        self.worker = Worker(
            self.input_queue, self.producer_conn, self.condition_new_rx_data, self.condition_run_process
        )
        self.worker.start()

        # Array containing buffers with MCU variables values
        self.variables_values = dict()
        # max size of the buffers
        self.buffer_length = 128
        # Array containing last time each individual variable was updated
        self.last_variables_update = dict()
        # Min delay in seconds between two emit value received signal
        self.emit_signal_delay = 0.1
        self.time_start = time.time()

        # Timer for monitoring MCU alive
        self.mcu_died_delay = 2.0
        self.mcu_alive_timer = threading.Timer(self.mcu_died_delay, self.on_mcu_lost_connection)

        self.variable_list = dict()
        self.connected = False

        self.datalogger = Datalogger()

        # Start MCU timer
        self.mcu_alive_timer = threading.Timer(self.mcu_died_delay, self.on_mcu_lost_connection)
        self.mcu_alive_timer.start()

        logging.info("DistantIO API initialized successfully.")

    def decode_rx_data(self, data):
        self.input_queue.put(data)

    def export_data(self):

        self.signal_MCU_state_changed.emit(alive=False)

        self.connected = False
        logging.info("Disconnected successfully.")

        # Write emergency data to file
        self.datalogger.export()

    def terminate(self):

        self.mcu_alive_timer.cancel()
        if self.mcu_alive_timer.isAlive():
            self.mcu_alive_timer.join()

        logging.info("Sending terminate signal to all threads.")
        self.condition_new_rx_data.set()
        self.condition_run_process.set()
        self.worker.join()
        logging.info("Worker process joined.")
        logging.info("Active thread count :" + str(threading.active_count()))
        for t in threading.enumerate():
            logging.info("Thread :" + str(t))
        logging.info("API terminated successfully.")

    ### Distant IO calls to MCU
    # Ask the MCU to return all descriptors
    def request_descriptors(self):
        logging.info("requested all descriptors to MCU.")
        frame = self.distantio.get_descriptors_frame()
        frame = self.protocol.encode(frame)
        return frame

    # Ask the MCU to write a variable
    def request_write(self, variable_id, data):
        if not variable_id in self.variable_list:
            logging.error("variable id provided " + str(variable_id) + " not found.")
            return
        # Check data is number
        try:
            # Cast to float and see if that fails
            dummy = float(data)
        except ValueError:
            logging.error("value provided " + str(data) + " not correct.")
            return

        logging.info("requested MCU to write " + str(data) + " to var id " + str(variable_id) + ".")
        frame = self.distantio.get_write_value_frame(variable_id, self.variable_list[variable_id]["type"], data)
        frame = self.protocol.encode(frame)
        return frame

    # Ask the MCU to read all variables
    def request_read_all(self):
        for key in self.variable_list:
            logging.info("requested to receive readings of var id " + str(key) + ".")
            frame = self.distantio.get_start_reading_frame(key, self.variable_list[key]["type"])
            frame = self.protocol.encode(frame)
            yield frame

    def update(self):

        # Check new decoded data is available
        available = self.consumer_conn.poll()
        if not available:
            return None

        instruction = self.consumer_conn.recv(False)

        # If distantio received a alive signal
        if instruction["type"] == "alive-signal":
            # Restart the timer
            self.mcu_alive_timer.cancel()
            self.mcu_alive_timer.join()

            self.mcu_alive_timer = threading.Timer(self.mcu_died_delay, self.on_mcu_lost_connection)

            self.mcu_alive_timer.start()
            self.signal_MCU_state_changed.emit(alive=True)

        # if returned-value
        elif instruction["type"] == "returned-value":

            # Check var id is known, otherwise create a buffer for it
            if not instruction["var-id"] in self.variables_values:
                self.variables_values[instruction["var-id"]] = ValuesXY(self.buffer_length)

            # Store value and time in sbuffer
            self.variables_values[instruction["var-id"]].append(instruction["var-time"], instruction["var-value"])

            if not instruction["var-id"] in self.last_variables_update:
                self.last_variables_update[instruction["var-id"]] = 0

            current_time = time.time()
            elapsed_time = current_time - self.last_variables_update[instruction["var-id"]]

            # Make sure the received-value signal was not emitted too ofter
            if elapsed_time > self.emit_signal_delay:
                self.last_variables_update[instruction["var-id"]] = current_time
                self.signal_received_value.emit(var_id=instruction["var-id"])

        # if returned-descriptor
        elif instruction["type"] == "returned-descriptor":

            self.variable_list[instruction["var-id"]] = dict()
            self.variable_list[instruction["var-id"]]["type"] = instruction["var-type"]
            self.variable_list[instruction["var-id"]]["name"] = ["var-name"]
            self.variable_list[instruction["var-id"]]["writeable"] = ["var-writeable"]

            logging.info("Received MCU variable descriptor with id " + str(instruction["var-id"]))

            if not instruction["var-id"] in self.variables_values:
                self.variables_values[instruction["var-id"]] = ValuesXY(self.buffer_length)

            if not instruction["var-id"] in self.last_variables_update:
                self.last_variables_update[instruction["var-id"]] = 0

            self.signal_received_descriptor.emit(
                var_id=instruction["var-id"],
                var_type=instruction["var-type"],
                var_name=instruction["var-name"],
                var_writeable=instruction["var-writeable"],
                group_id=instruction["var-group"],
            )

        elif instruction["type"] == "returned-group-descriptor":
            self.signal_received_group_descriptor.emit(
                group_id=instruction["group-id"], group_name=instruction["group-name"]
            )

        elif instruction["type"] == "emergency-send":
            logging.warning("Received emergency data with user id " + str(instruction["data-id"]))

            self.datalogger.append(
                instruction["data-id"], instruction["data-time"], instruction["data-index"], instruction["data-value"]
            )

        else:
            logging.error("Unknown instruction :" + str(instruction))

        if count == maxamount and self.output_queue.qsize() > 200:
            logging.warning(
                "Instruction queue not processed fast enough. Current size :" + str(self.output_queue.qsize())
            )

    ## Callbacks
    def on_mcu_lost_connection(self):
        self.signal_MCU_state_changed.emit(alive=False)

    def unused(self, frame):
        logging.error("Local protocol decoded frame " + str(frame) + " instead of Worker")

    # Getters setters
    def get_last_value(self, var_id):
        if not var_id in self.variables_values:
            raise IndexError("Variable ID " + str(instruction["var-id"]) + " not found.")
        else:
            return self.variables_values[var_id].y[-1]

    def get_buffers_value(self, var_id):
        if not var_id in self.variables_values:
            raise IndexError("Variable ID " + str(instruction["var-id"]) + " not found.")
        else:
            return self.variables_values[var_id]
Example #52
0
 def __init__(self):
     self._loop = DummyLoop()
     self.bind_calls = []
     self.transmitted = []
     self.received_msg = Signal()
Example #53
0
class FileListModel(QAbstractItemModel, pytson.Translatable):
    """
    Itemmodel to abstract the files contained on a TS3 filepath.
    """

    def __init__(self, schid, cid, password, parent=None, *, readonly=False):
        super(QAbstractItemModel, self).__init__(parent)

        self.schid = schid
        self.cid = cid
        self.password = password

        self.readonly = readonly

        self.pathChanged = Signal()
        self.error = Signal()

        self._path = None
        self.newpath = None
        self.files = []
        self.newfiles = []

        self.retcode = None
        self.renretcode = None
        self.renfile = ()

        self.titles = [self._tr("Name"), self._tr("Size"), self._tr("Type"),
                       self._tr("Last Changed")]

        PluginHost.registerCallbackProxy(self)

    def __del__(self):
        PluginHost.unregisterCallbackProxy(self)

    @property
    def path(self):
        return self._path

    @path.setter
    def path(self, val):
        self._reload(val)

    @property
    def currentFiles(self):
        return self.files

    def _reload(self, path=None):
        if path:
            self.newpath = path
        else:
            self.newpath = self._path

        self.retcode = ts3lib.createReturnCode()
        err = ts3lib.requestFileList(self.schid, self.cid, self.password,
                                     self.newpath, self.retcode)
        if err != ERROR_ok:
            _errprint(self._tr("Error requesting filelist"), err, self.schid,
                      self.cid)

    def onFileListEvent(self, schid, channelID, path, name, size, date,
                        atype, incompletesize, returnCode):
        if (schid != self.schid or channelID != self.cid or
           returnCode != self.retcode):
            return

        self.newfiles.append(File(path, name, size, date, atype,
                                  incompletesize))

    def onFileListFinishedEvent(self, schid, channelID, path):
        if (schid != self.schid or channelID != self.cid or
           path != self.newpath):
            return
        # might be unneeded (event is catched in onServerErrorEvent)

    def onServerErrorEvent(self, schid, errorMessage, error, returnCode,
                           extraMessage):
        if schid != self.schid:
            return

        if returnCode == self.retcode:
            if error in [ERROR_ok, ERROR_database_empty_result]:
                self.beginResetModel()
                self.files = self.newfiles
                self.newfiles = []
                self.endResetModel()

                if self._path != self.newpath:
                    self._path = self.newpath
                    self.pathChanged.emit(self._path)
            else:
                self.error.emit(self._tr("Error requesting filelist"), error,
                                errorMessage)
        elif returnCode == self.renretcode:
            if error != ERROR_ok:
                self.renfile[0].name = self.renfile[1]

                self.error.emit(self._tr("Error renaming file"), error,
                                errorMessage)

            self.renfile = ()

    def onServerPermissionErrorEvent(self, schid, errorMessage, error,
                                     returnCode, failedPermissionID):
        if schid != self.schid or returnCode != self.retcode:
            return

        if returnCode == self.retcode:
            if error != ERROR_ok:
                self.error.emit(self._tr("Error requesting filelist"), error,
                                errorMessage)
        elif returnCode == self.renretcode:
            if error != ERROR_ok:
                self.renfile[0].name = self.renfile[1]

                self.error.emit(self._tr("Error renaming file"), error,
                                errorMessage)

            self.renfile = ()

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role == Qt.DisplayRole and orientation == Qt.Horizontal:
            return self.titles[section]

        return None

    def flags(self, idx):
        f = Qt.ItemIsEnabled | Qt.ItemIsSelectable

        if not self.readonly:
            return f | Qt.ItemIsEditable
        else:
            return f

    def index(self, row, column, parent=QModelIndex()):
        if parent.isValid():
            return QModelIndex()

        return self.createIndex(row, column)

    def parent(self, idx):
        return QModelIndex()

    def rowCount(self, parent=QModelIndex()):
        if parent.isValid():
            return 0

        return len(self.files)

    def columnCount(self, parent=QModelIndex()):
        return len(self.titles)

    def data(self, idx, role=Qt.DisplayRole):
        if not idx.isValid():
            return None

        f = self.files[idx.row()]
        if idx.column() == 0:
            if role == Qt.DisplayRole:
                return f.name
            elif role == Qt.DecorationRole:
                return f.icon
            elif role == Qt.EditRole and not self.readonly:
                return f.name
            elif role == Qt.UserRole:
                if f.isDirectory:
                    return "a%s" % f.name
                else:
                    return "b%s" % f.name
        elif role == Qt.DisplayRole:
            if idx.column() == 1 and not f.isDirectory:
                return bytesToStr(f.size)
            elif idx.column() == 2:
                if f.isDirectory:
                    return self._tr("Directory")
                else:
                    return self._tr("File")
            elif idx.column() == 3:
                return f.datetime.strftime(pytson.tr("filetransfer",
                                                     "%Y-%m-%d %H:%M:%S"))
        return None

    def setData(self, idx, value, role=Qt.EditRole):
        if not idx.isValid():
            return False

        f = self.fileByIndex(idx)
        if value == f.name:
            return

        self.renretcode = ts3lib.createReturnCode()
        self.renfile = (f, f.name)

        err = ts3lib.requestRenameFile(self.schid, self.cid, self.password,
                                       0, "", f.fullpath, joinpath(f.path,
                                                                   value),
                                       self.renretcode)

        if err == ERROR_ok:
            f.name = value
            return True

    def fileByIndex(self, idx):
        if idx.isValid():
            return self.files[idx.row()]
        return None