Exemplo n.º 1
0
class BaseListItem(ThemableBehavior, RectangularRippleBehavior, ButtonBehavior,
                   FloatLayout):
    """
    Base class to all ListItems. Not supposed to be instantiated on its own.
    """

    text = StringProperty()
    """
    Text shown in the first line.

    :attr:`text` is a :class:`~kivy.properties.StringProperty`
    and defaults to `''`.
    """

    text_color = ColorProperty(None)
    """
    Text color in ``rgba`` format used if :attr:`~theme_text_color` is set
    to `'Custom'`.

    :attr:`text_color` is a :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    font_style = StringProperty("Subtitle1")
    """
    Text font style. See ``kivymd.font_definitions.py``.

    :attr:`font_style` is a :class:`~kivy.properties.StringProperty`
    and defaults to `'Subtitle1'`.
    """

    theme_text_color = StringProperty("Primary", allownone=True)
    """
    Theme text color in ``rgba`` format for primary text.

    :attr:`theme_text_color` is a :class:`~kivy.properties.StringProperty`
    and defaults to `'Primary'`.
    """

    secondary_text = StringProperty()
    """
    Text shown in the second line.

    :attr:`secondary_text` is a :class:`~kivy.properties.StringProperty`
    and defaults to `''`.
    """

    tertiary_text = StringProperty()
    """
    The text is displayed on the third line.

    :attr:`tertiary_text` is a :class:`~kivy.properties.StringProperty`
    and defaults to `''`.
    """

    secondary_text_color = ColorProperty(None)
    """
    Text color in ``rgba`` format used for secondary text
    if :attr:`~secondary_theme_text_color` is set to `'Custom'`.

    :attr:`secondary_text_color` is a :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    tertiary_text_color = ColorProperty(None)
    """
    Text color in ``rgba`` format used for tertiary text
    if :attr:`~tertiary_theme_text_color` is set to 'Custom'.

    :attr:`tertiary_text_color` is a :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    secondary_theme_text_color = StringProperty("Secondary", allownone=True)
    """
    Theme text color for secondary text.

    :attr:`secondary_theme_text_color` is a :class:`~kivy.properties.StringProperty`
    and defaults to `'Secondary'`.
    """

    tertiary_theme_text_color = StringProperty("Secondary", allownone=True)
    """
    Theme text color for tertiary text.

    :attr:`tertiary_theme_text_color` is a :class:`~kivy.properties.StringProperty`
    and defaults to `'Secondary'`.
    """

    secondary_font_style = StringProperty("Body1")
    """
    Font style for secondary line. See ``kivymd.font_definitions.py``.

    :attr:`secondary_font_style` is a :class:`~kivy.properties.StringProperty`
    and defaults to `'Body1'`.
    """

    tertiary_font_style = StringProperty("Body1")
    """
    Font style for tertiary line. See ``kivymd.font_definitions.py``.

    :attr:`tertiary_font_style` is a :class:`~kivy.properties.StringProperty`
    and defaults to `'Body1'`.
    """

    divider = OptionProperty("Full",
                             options=["Full", "Inset", None],
                             allownone=True)
    """
    Divider mode. Available options are: `'Full'`, `'Inset'`
    and default to `'Full'`.

    :attr:`divider` is a :class:`~kivy.properties.OptionProperty`
    and defaults to `'Full'`.
    """

    bg_color = ColorProperty(None)
    """
    Background color for menu item.

    :attr:`bg_color` is a :class:`~kivy.properties.ColorProperty`
    and defaults to `None`.
    """

    _txt_left_pad = NumericProperty("16dp")
    _txt_top_pad = NumericProperty()
    _txt_bot_pad = NumericProperty()
    _txt_right_pad = NumericProperty(m_res.HORIZ_MARGINS)
    _num_lines = 3
    _no_ripple_effect = BooleanProperty(False)
class COGame(Widget):
    center = ObjectProperty(None)
    target = ObjectProperty(None)
    
    nudge_x = 0.
    # nudge_y = -2.

    # Time to wait after starting the video before getting to the center target display. 
    pre_start_vid_ts = 0.1

    ITI_mean = 1.
    ITI_std = .2
    center_target_rad = 1.5
    periph_target_rad = 1.5
    
    # SET THE POSITION OF THE EXIT BUTTONS AND THE PHOTODIODE INDICATOR LIGHT
    # positions are in CM measured from the center of the screen
    # if platform == 'darwin':
    
    exit_pos_x = (fixed_window_size_cm[0]/2)-1.5
    exit_pos_y = (fixed_window_size_cm[1]/2)-1.5
                 
    exit_pos = np.array([exit_pos_x, exit_pos_y])
    ind_pos_x = (fixed_window_size_cm[0]/2)-0.5
    ind_pos_y = (fixed_window_size_cm[1]/2)-0.5
                 
    indicator_pos = np.array([ind_pos_x, ind_pos_y])
    # elif platform == 'win32':
    #     exit_pos = np.array([7, 4])
    #     indicator_pos = np.array([8, 5])
    exit_rad = 1.
    exit_hold = 2 #seconds
    
    # SET THE HOLD AND TIMEOUT TIMES
    ch_timeout = 10. # ch timeout
    cht = .001 # center hold time

    target_timeout_time = 5.
    tht = .001

    cursor = {}
    cursor_start = {}
    cursor_ids = []

    anytouch_prev = False
    touch_error_timeout = 0.
    timeout_error_timeout = 0.
    hold_error_timeout = 0.
    drag_error_timeout = 0.

    ntargets = 4.
    target_distance = 4.
    touch = False

    center_target = ObjectProperty(None)
    periph_target1 = ObjectProperty(None)
    periph_target2 = ObjectProperty(None)

    done_init = False
    prev_exit_ts = np.array([0,0])

    # Number of trials: 
    trial_counter = NumericProperty(0)
    #indicator_txt = StringProperty('o')
    #indicator_txt_color = ListProperty([.5, .5, .5, 1.])

    t0 = time.time()

    cht_text = StringProperty('')
    tht_text = StringProperty('')
    generatorz_text = StringProperty('')
    targ_size_text = StringProperty('')
    big_rew_text = StringProperty('')
    cht_param = StringProperty('')
    tht_param = StringProperty('')
    targ_size_param = StringProperty('')
    big_rew_time_param = StringProperty('')
    generatorz_param = StringProperty('')
    nudge_text = StringProperty('')
    nudge_param = StringProperty('')
    def on_touch_down(self, touch):
        #handle many touchs:
        ud = touch.ud

        # Add new touch to ids: 
        self.cursor_ids.append(touch.uid)

        # Add cursor
        curs = pix2cm(np.array([touch.x, touch.y]))
        self.cursor[touch.uid] =  curs.copy()
        self.cursor_start[touch.uid] = curs.copy()

        # set self.touch to True
        self.touch = True

    def on_touch_move(self, touch):
        self.cursor[touch.uid] = pix2cm(np.array([touch.x, touch.y]))
        self.touch = True

    def on_touch_up(self, touch):
        try:
            self.cursor_ids.remove(touch.uid)
            _ = self.cursor.pop(touch.uid)
        except:
            print('removing touch from pre-game screen')
            
    def init(self, animal_names_dict=None, rew_in=None, task_in=None,
        test=None, hold=None, targ_structure=None,
        autoquit=None, rew_var=None, targ_timeout = None, nudge_x=None, screen_top=None):
        
        import os 
        path = os.getcwd()
        if 'BasalGangulia' in path:
            self.in_cage = True
        else:
            self.in_cage = False
        
        self.rew_cnt = 0
        self.small_rew_cnt = 0


        self.use_cap_sensor = False

        if self.use_cap_sensor:
            self.serial_port_cap = serial.Serial(port='COM5')

        self.rhtouch_sensor = 0.


        targ_timeout_opts = [15, 30, 45, 60]
        for i, val in enumerate(targ_timeout['tt']):
            if val:
                self.target_timeout_time = targ_timeout_opts[i]

        small_rew_opts = [0., .1, .3, .5]
        for i, val in enumerate(rew_in['small_rew']):
            if val:
                small_rew = small_rew_opts[i]

        big_rew_opts = [0., .3, .5, .7]
        for i, val in enumerate(rew_in['big_rew']):
            if val:
                big_rew = big_rew_opts[i]


        if rew_in['rew_anytouch']:
            self.reward_for_anytouch = [True, small_rew]
        else:
            self.reward_for_anytouch = [False, 0]

        if np.logical_or(rew_in['rew_targ'], rew_in['rew_center_pls_targ']):
            self.reward_for_targtouch = [True, big_rew]
        else:
            self.reward_for_targtouch = [False, 0]

        if rew_in['rew_center_pls_targ']:
            self.reward_for_center = [True, small_rew]
        else:
            self.reward_for_center = [False, 0]

        if rew_in['snd_only']:
            self.reward_for_targtouch = [True, 0.]
            self.skip_juice = True
        else:
            self.skip_juice = False
        
        
        # NUDGE X
        nudge_x_opts = [-6, -4, -2, 0, 2, 4, 6]    
        for i, val in enumerate(nudge_x['nudge_x']):
            if val:
                self.nudge_x = nudge_x_opts[i]
        
        # WHERE TO CONSIDER THE TOP OF THE SCREEN (HOW MUCH TO SHRINK IT DOWN BY)
        screen_top_opts = [-12, -10, -8, -6, -4, -2, 0]    
        for i, val in enumerate(screen_top['screen_top']):
            if val:
                self.screen_top = screen_top_opts[i]
                
        self.center_target_position = np.array([0., 0.])
        if self.in_cage:
            self.center_target_position[0] = self.center_target_position[0] - 4
        else:
            self.center_target_position[0] = self.center_target_position[0] + self.nudge_x
            
            # lower the center target by half of the total amount the screen height has been shrunk by
            self.center_target_position[1] = self.center_target_position[1] + self.screen_top/2
        
        # TARGET RADIUS
        target_rad_opts = [.5, .75, .82, .91, 1.0, 1.5, 2.25, 3.0]
        for i, val in enumerate(task_in['targ_rad']):
            if val:
                self.periph_target_rad = target_rad_opts[i]
                self.center_target_rad = target_rad_opts[i]
                
        # TARGET 1 POSITION
        target1_pos_opts = ['upper_left', 'lower_left', 'upper_right', 'lower_right']
        for i, val in enumerate(task_in['targ1_pos']):
            if val:
                self.periph_target_pos_str = target1_pos_opts[i]
        
        d_center2top = (fixed_window_size_cm[1]/2)+(self.screen_top/2)
        max_y_from_center = d_center2top-self.periph_target_rad
        
        if self.periph_target_pos_str == 'upper_right':
            # angle = 0.25*np.pi
            targ1_x = max_y_from_center+self.nudge_x
            targ1_y = self.center_target_position[1] + max_y_from_center
        elif self.periph_target_pos_str == 'lower_right':
            # angle = 1.75*np.pi
            targ1_x = max_y_from_center+self.nudge_x
            targ1_y = self.center_target_position[1] - max_y_from_center
        elif self.periph_target_pos_str == 'lower_left':
            # angle = 1.25*np.pi
            targ1_x = -max_y_from_center+self.nudge_x
            targ1_y = self.center_target_position[1] - max_y_from_center
        elif self.periph_target_pos_str == 'upper_left':
            # angle = 0.75*np.pi
            targ1_x = -max_y_from_center+self.nudge_x
            targ1_y = self.center_target_position[1] + max_y_from_center

        # target_distance = 6. # distance from center of screen to target
        
        # self.target1_position = np.array([np.cos(angle)*target_distance+self.nudge_x, np.sin(angle)*target_distance+self.nudge_y])
        self.target1_position = np.array([targ1_x, targ1_y])
        
        self.periph_target_position = self.target1_position
        self.target_index = 1
                
        # TARGET 2 POSITION
        target2_pos_opts = ['left', 'right', 'above', 'below']
        for i, val in enumerate(task_in['targ2_pos']):
            if val:
                self.target2_pos_str = target2_pos_opts[i]
                
        targ1_to_targ2_dist_opts = [0, 3, 6, 9, 12, 15]
        for i, val in enumerate(task_in['targ1_to_targ2_dist']):
            if val:
                self.targ1_to_targ2_dist = targ1_to_targ2_dist_opts[i]
        
        # self.target2_position = np.array([np.cos(angle)*target_distance+self.nudge_x, np.sin(angle)*target_distance+self.nudge_y])
        self.target2_position = np.array([targ1_x, targ1_y])
        if self.target2_pos_str == 'left':
            self.target2_position[0] = self.target2_position[0]-2*self.periph_target_rad-self.targ1_to_targ2_dist
        elif self.target2_pos_str == 'right':
            self.target2_position[0] = self.target2_position[0]+2*self.periph_target_rad+self.targ1_to_targ2_dist
        elif self.target2_pos_str == 'above':
            self.target2_position[1] = self.target2_position[1]+2*self.periph_target_rad+self.targ1_to_targ2_dist
        elif self.target2_pos_str == 'below':
            self.target2_position[1] = self.target2_position[1]-2*self.periph_target_rad-self.targ1_to_targ2_dist
        
        # HOW MUCH TIME TO WAIT UNTIL THE NEXT TARGET APPEARS
        self.time_to_next_targ = False
        
        # ANIMAL NAME
        for i, (nm, val) in enumerate(animal_names_dict.items()):
            if val:
                animal_name = nm

        self.use_center = False
        for i, (nm, val) in enumerate(targ_structure.items()):
            if val:
                generatorz = getattr(self, nm)
                self.generatorz_param2 = nm
                if 'co' in nm:
                    self.use_center = True

        holdz = [0.0, 0.1, 0.2, 0.3, 0.4, .5, .6, '.4-.6']
        
        self.cht_type = None
        self.tht_type = None

        for i, val in enumerate(hold['hold']):
            if val:
                if type(holdz[i]) is str:
                    mx, mn = holdz[i].split('-')
                    self.tht_type = holdz[i]
                    self.tht =  (float(mn)+float(mx))*.5
                else:
                    self.tht = holdz[i]

        for i, val in enumerate(hold['chold']):
            if val:
                if type(holdz[i]) is str:
                    mx, mn = holdz[i].split('-')
                    self.cht_type = holdz[i]
                    self.cht = (float(mn)+float(mx))*.5
                else:
                    self.cht = holdz[i]
        
        try:
            pygame.mixer.init()    
        except:
            pass

        # reward_delay_opts = [0., .4, .8, 1.2]
        # for i, val in enumerate(rew_del['rew_del']):
        #     if val:
        self.reward_delay_time = 0.0

        reward_var_opt = [1.0, .5, .33]
        for i, val in enumerate(rew_var['rew_var']):
            if val:
                self.percent_of_trials_rewarded = reward_var_opt[i]
                if self.percent_of_trials_rewarded == 0.33:
                    self.percent_of_trials_doubled = 0.1
                else:
                    self.percent_of_trials_doubled = 0.0
        
        self.reward_generator = self.gen_rewards(self.percent_of_trials_rewarded, self.percent_of_trials_doubled,
            self.reward_for_targtouch)


        # white_screen_opts = [True, False]
        # for i, val in enumerate(white_screen['white_screen']):
        #     if val:
        self.use_white_screen = False

        test_vals = [True, False, False]
        in_cage_vals = [False, False, True]
        for i, val in enumerate(test['test']):
            if val:
                self.testing = test_vals[i]
                #self.in_cage = in_cage_vals[i]

        autoquit_trls = [10, 25, 50, 100, 10**10]
        for i, val in enumerate(autoquit['autoquit']):
            if val: 
                self.max_trials = autoquit_trls[i]

        # drag_ok = [True, False]
        # for i, val in enumerate(drag['drag']):
        #     if val:
        #         self.drag_ok = drag_ok[i]
        self.drag_ok = False;

        # nudge_9am_dist = [0., .5, 1.]
        # for i, val in enumerate(nudge['nudge']):
        #     if val:
        self.nudge_dist = 0.

        # targ_pos = ['corners', None]
        # for i, val in enumerate(targ_pos['targ_pos']):
        #     if val:
        self.generator_kwarg = 'corners'


        # Preload sounds: 
        self.reward1 = SoundLoader.load('reward1.wav')
        self.reward2 = SoundLoader.load('reward2.wav')

        self.state = 'ITI'
        self.state_start = time.time()
        self.ITI = self.ITI_std + self.ITI_mean

        # Initialize targets: 
        self.center_target.set_size(2*self.center_target_rad)
        self.center_target.move(self.center_target_position)
        self.periph_target1.set_size(2*self.periph_target_rad)
        self.periph_target2.set_size(2*self.periph_target_rad)

        self.exit_target1.set_size(2*self.exit_rad)
        self.exit_target2.set_size(2*self.exit_rad)
        self.indicator_targ.set_size(self.exit_rad)
        self.indicator_targ.move(self.indicator_pos)
        self.indicator_targ.color = (0., 0., 0., 1.)

        self.exit_target1.move(self.exit_pos)
        self.exit_pos2 = np.array([self.exit_pos[0], -1*self.exit_pos[1]])
        self.exit_target2.move(self.exit_pos2)
        self.exit_target1.color = (.15, .15, .15, 1)
        self.exit_target2.color = (.15, .15, .15, 1)

        self.repeat = False

        self.FSM = dict()
        self.FSM['ITI'] = dict(end_ITI='vid_trig', stop=None)
        self.FSM['vid_trig'] = dict(rhtouch='target', stop=None)
        
        if self.use_center:
            self.FSM['vid_trig'] = dict(end_vid_trig='center', stop=None)
            self.FSM['center'] = dict(touch_center='center_hold', center_timeout='timeout_error', non_rhtouch='RH_touch',stop=None)
            self.FSM['center_hold'] = dict(finish_center_hold='target', early_leave_center_hold='hold_error', non_rhtouch='RH_touch', stop=None)

        self.FSM['target'] = dict(touch_target = 'targ_hold', target_timeout='timeout_error', stop=None,
            anytouch='rew_anytouch', non_rhtouch='RH_touch')#,touch_not_target='touch_error')
        self.FSM['targ_hold'] = dict(finish_targ2_hold='reward', finish_targ1_hold='target', early_leave_target_hold = 'hold_error',
         targ_drag_out = 'drag_error', stop=None, non_rhtouch='RH_touch')
        self.FSM['reward'] = dict(end_reward = 'ITI', stop=None, non_rhtouch='RH_touch')

        if self.use_center:
            return_ = 'center'
        else:
            return_ = 'target'

        self.FSM['touch_error'] = dict(end_touch_error=return_, stop=None, non_rhtouch='RH_touch')
        self.FSM['timeout_error'] = dict(end_timeout_error='ITI', stop=None, non_rhtouch='RH_touch')
        self.FSM['hold_error'] = dict(end_hold_error=return_, stop=None, non_rhtouch='RH_touch')
        self.FSM['drag_error'] = dict(end_drag_error=return_, stop=None, non_rhtouch='RH_touch')
        self.FSM['rew_anytouch'] = dict(end_rewanytouch='target', stop=None, non_rhtouch='RH_touch')
        self.FSM['idle_exit'] = dict(stop=None)

        try:
            self.reward_port = serial.Serial(port='COM4',
                baudrate=115200)
            self.reward_port.close()
        except:
            pass

        try:
            self.dio_port = serial.Serial(port='COM5', baudrate=115200)
            time.sleep(4.)
        except:
            pass

        try:
            self.cam_trig_port = serial.Serial(port='COM6', baudrate=9600)
            time.sleep(3.)
            # Say hello: 
            self.cam_trig_port.write('a'.encode())

            # Start cams @ 50 Hz
            self.cam_trig_port.write('1'.encode())
        except:
            pass
        
        # save parameters: 
        d = dict(animal_name=animal_name, center_target_rad=self.center_target_rad,
            periph_target_rad=self.periph_target_rad, target_structure = generatorz.__name__, 
            target1_position = self.target1_position, 
            target2_position = self.target2_position, 
            ITI_mean=self.ITI_mean, ITI_std = self.ITI_std, ch_timeout=self.ch_timeout, 
            cht=self.cht, reward_time_small=self.reward_for_center[1],
            reward_time_big=self.reward_for_targtouch[1],
            reward_for_anytouch=self.reward_for_anytouch[0],
            reward_for_center = self.reward_for_center[0],
            reward_for_targtouch=self.reward_for_targtouch[0], 
            touch_error_timeout = self.touch_error_timeout,
            timeout_error_timeout = self.timeout_error_timeout,
            hold_error_timeout = self.hold_error_timeout,
            drag_error_timeout = self.drag_error_timeout,
            ntargets = self.ntargets,
            target_distance = self.target_distance,
            start_time = datetime.datetime.now().strftime('%Y%m%d_%H%M'),
            testing=self.testing,
            rew_delay = self.reward_delay_time,
            use_cap_sensor = self.use_cap_sensor,
            drag_ok = self.drag_ok,
            )

        print(self.reward_for_center)
        print(self.reward_for_targtouch)
        print(self.reward_for_anytouch)

        #try:
        if self.testing or platform == 'darwin':
            pass

        else:
            import os
            path = os.getcwd()
            path = path.split('\\')
            path_data = [p for p in path if np.logical_and('Touch' not in p, 'Targ' not in p)]
            path_root = ''
            for ip in path_data:
                path_root += ip+'/'
            p = path_root + 'data/'
            print('Auto path : %s'%p)
            # Check if this directory exists: 
            if os.path.exists(p):
                pass
            else:
                p = path_root+ 'data_tmp_'+datetime.datetime.now().strftime('%Y%m%d')+'/'
                if os.path.exists(p):
                    pass
                else:
                    os.mkdir(p)
                    print('Making temp directory: ', p)

            print ('')
            print ('')
            print('Data saving PATH: ', p)
            print ('')
            print ('')
            self.filename = p+ animal_name+'_'+datetime.datetime.now().strftime('%Y%m%d_%H%M')
            
            if self.in_cage:
                self.filename = self.filename+'_cage'

            pickle.dump(d, open(self.filename+'_params.pkl', 'wb'))
            self.h5file = tables.open_file(self.filename + '_data.hdf', mode='w', title = 'NHP data')
            self.h5_table = self.h5file.create_table('/', 'task', Data, '')
            self.h5_table_row = self.h5_table.row
            self.h5_table_row_cnt = 0

            # Note in python 3 to open pkl files: 
            #with open('xxxx_params.pkl', 'rb') as f:
            #    data_params = pickle.load(f)
        # except:
        #     pass

    def gen_rewards(self, perc_trials_rew, perc_trials_2x, reward_for_grasp):
        mini_block = int(2*(np.round(1./self.percent_of_trials_rewarded)))
        rew = []
        trial_cnt_bonus = 0

        for i in range(500):
            mini_block_array = np.zeros((mini_block))
            ix = np.random.permutation(mini_block)
            mini_block_array[ix[:2]] = reward_for_grasp[1]

            trial_cnt_bonus += mini_block
            if perc_trials_2x > 0:
                if trial_cnt_bonus > int(1./(perc_trials_rew*perc_trials_2x)):
                    mini_block_array[ix[0]] = reward_for_grasp[1]*2.
                    trial_cnt_bonus = 0

            rew.append(mini_block_array)
        return np.hstack((rew))

    def close_app(self):
        # Save Data Eventually
         #Stop the video: 
        try:
            self.cam_trig_port.write('0'.encode())
        except:
            pass

        if self.use_cap_sensor:
            self.serial_port_cap.close()
        
        if self.idle:
            self.state = 'idle_exit'
            self.trial_counter = -1

            # Set relevant params text: 
            self.cht_text = 'Center Hold Time: '
            self.tht_text = 'Target Hold Time: '
            self.generatorz_text = 'Target Structure: '
            self.targ_size_text = 'Target Radius: '
            self.big_rew_text = 'Big Reward Time: '

            if type(self.cht_type) is str:
                self.cht_param = self.cht_type
            else:
                self.cht_param = 'Constant: ' + str(self.cht)

            if type(self.tht_type) is str:
                self.tht_param = self.tht_type
            else:
                self.tht_param = 'Constant: ' + str(self.tht)

            self.targ_size_param = str(self.center_target_rad)
            self.big_rew_time_param = str(self.reward_for_targtouch[1])
            self.generatorz_param = self.generatorz_param2

            self.nudge_text = 'Nudge 9oclock targ? '
            self.nudge_param = str(self.nudge_dist)
        else:
            App.get_running_app().stop()
            Window.close()

    def update(self, ts):
        self.state_length = time.time() - self.state_start
        self.rew_cnt += 1
        self.small_rew_cnt += 1
        
        # Run task update functions: 
        for f, (fcn_test_name, next_state) in enumerate(self.FSM[self.state].items()):
            kw = dict(ts=self.state_length)
            
            fcn_test = getattr(self, fcn_test_name)
            if fcn_test(**kw):
                # if stop: close the app
                if fcn_test_name == 'stop':
                    self.close_app()

                else:
                    # Run any 'end' fcns from prevoius state: 
                    end_state_fn_name = "_end_%s" % self.state
                    if hasattr(self, end_state_fn_name):
                        end_state_fn = getattr(self, end_state_fn_name)
                        end_state_fn()
                    self.prev_state = self.state
                    self.state = next_state
                    self.state_start = time.time()

                    # Run any starting functions: 
                    start_state_fn_name = "_start_%s" % self.state
                    if hasattr(self, start_state_fn_name):
                        start_state_fn = getattr(self, start_state_fn_name)
                        start_state_fn()
                
                break
            else:
                while_state_fn_name = "_while_%s" % self.state
                if hasattr(self, while_state_fn_name):
                    while_state_fn = getattr(self, while_state_fn_name)
                    while_state_fn()
            
        if self.use_cap_sensor:
            try:
                self.serial_port_cap.flushInput()
                port_read = self.serial_port_cap.read(4)
                if str(port_read[:2]) == "b'N1'":
                    self.rhtouch_sensor = False
                elif str(port_read[:2]) == "b'C1'":
                    self.rhtouch_sensor = True
                    print(self.rhtouch_sensor)
            except:
                print('passing state! ')
                pass     
        if self.testing or platform == 'darwin':
            pass
        else:
            if self.state == 'idle_exit':
                pass
            else:
                self.write_to_h5file()

    def write_to_h5file(self):
        self.h5_table_row['state']= self.state; 
        cursor = np.zeros((10, 2))
        cursor[:] = np.nan
        for ic, curs_id in enumerate(self.cursor_ids):
            cursor[ic, :] = self.cursor[curs_id]

        self.h5_table_row['cursor'] = cursor

        cursor_id = np.zeros((10, ))
        cursor_id[:] = np.nan
        cursor_id[:len(self.cursor_ids)] = self.cursor_ids
        self.h5_table_row['cursor_ids'] = cursor_id

        self.h5_table_row['target_pos'] = self.periph_target_position
        self.h5_table_row['time'] = time.time() - self.t0
        self.h5_table_row['cap_touch'] = self.rhtouch_sensor
        self.h5_table_row.append()

        # Write DIO 
        try:
            self.write_row_to_dio()
        except:
            pass
            
        # Upgrade table row: 
        self.h5_table_row_cnt += 1

    def write_row_to_dio(self):
        ### FROM TDT TABLE, 5 is GND, BYTE A ###
        row_to_write = self.h5_table_row_cnt % 256

        ### write to arduino: 
        word_str = b'd' + struct.pack('<H', int(row_to_write))
        self.dio_port.write(word_str)

    def stop(self, **kwargs):
        # If past number of max trials then auto-quit: 
        if np.logical_and(self.trial_counter >= self.max_trials, self.state == 'ITI'):
            self.idle = True
            return True
        else:
            e = [0, 0]
            e[0] = self.check_if_cursors_in_targ(self.exit_pos, self.exit_rad)
            e[1] = self.check_if_cursors_in_targ(self.exit_pos2, self.exit_rad)
            t = [0, 0]
            for i in range(2):
                if np.logical_and(self.prev_exit_ts[i] !=0, e[i] == True):
                    t[i] = time.time() - self.prev_exit_ts[i]
                elif np.logical_and(self.prev_exit_ts[i] == 0, e[i]==True):
                    self.prev_exit_ts[i] = time.time()
                else:
                    self.prev_exit_ts[i] = 0
                    
            if t[0] > self.exit_hold and t[1] > self.exit_hold:
                self.idle = False
                return True

            else:
                return False

    def _start_ITI(self, **kwargs):
        try:
            self.cam_trig_port.write('0'.encode())
        except:
            pass
        Window.clearcolor = (0., 0., 0., 1.)
        self.exit_target1.color = (.15, .15, .15, 1.)
        self.exit_target2.color = (.15, .15, .15, 1.)

        # Set ITI, CHT, THT
        self.ITI = np.random.random()*self.ITI_std + self.ITI_mean

        if type(self.cht_type) is str:
            cht_min, cht_max = self.cht_type.split('-')
            self.cht = ((float(cht_max) - float(cht_min)) * np.random.random()) + float(cht_min)

        if type(self.tht_type) is str:
            tht_min, tht_max = self.tht_type.split('-')
            self.tht = ((float(tht_max) - float(tht_min)) * np.random.random()) + float(tht_min)            
        
        self.center_target.color = (0., 0., 0., 0.)
        self.periph_target1.color = (0., 0., 0., 0.)
        self.periph_target2.color = (0., 0., 0., 0.)
        self.indicator_targ.color = (0., 0., 0., 0.)
        
    def end_ITI(self, **kwargs):
        return kwargs['ts'] > self.ITI

    def _start_vid_trig(self, **kwargs):
        if self.trial_counter == 0:
            time.sleep(1.)
        try:    
            self.cam_trig_port.write('1'.encode())
        except:
            pass
        self.first_target_attempt = True
        self.first_time_for_this_targ = True

        if np.logical_and(self.use_cap_sensor, not self.rhtouch_sensor):
            self.periph_target1.color = (1., 0., 0., 1.)
            self.periph_target2.color = (1., 0., 0., 1.)
            self.center_target.color = (1., 0., 0., 1.)
            Window.clearcolor = (1., 0., 0., 1.)

            # Turn exit buttons redish:
            self.exit_target1.color = (.9, 0, 0, 1.)
            self.exit_target2.color = (.9, 0, 0, 1.)

    def end_vid_trig(self, **kwargs):
        return kwargs['ts'] > self.pre_start_vid_ts


    def rhtouch(self, **kwargs):
        if self.use_cap_sensor:
            if self.rhtouch_sensor:
                return True
            else:
                return False
        else:
            return True

    def non_rhtouch(self, **kwargs):
        x = not self.rhtouch()
        # if x:
        #     self.repeat = True
        return x

    def _start_center(self, **kwargs):
        Window.clearcolor = (0., 0., 0., 1.)
        self.center_target.color = (1., 1., 0., 1.)
        self.exit_target1.color = (.15, .15, .15, 1)
        self.exit_target2.color = (.15, .15, .15, 1)
        self.periph_target1.color = (0., 0., 0., 0.) ### Make peripheral target alpha = 0 so doesn't obscure 
        self.indicator_targ.color = (.25, .25, .25, 1.)
        
        # Reset target index back to 1
        self.target_index = 1
        
        if self.first_time_for_this_targ:
            self.first_time_for_this_targ_t0 = time.time()
            self.periph_target2.color = (0., 0., 0., 0.)
            self.first_time_for_this_targ = False
            
    def _while_center(self, **kwargs):
        # check and see if it is time for the next target to appear
        if self.time_to_next_targ is not False:
            # import pdb; pdb.set_trace()
            if time.time() - self.first_time_for_this_targ_t0 > self.time_to_next_targ:
                # illuminate the next target
                self.periph_target2.move(self.target1_position)
                self.periph_target2.color = (1., 1., 0., 1.)        

    def _start_center_hold(self, **kwargs):
        self.center_target.color = (0., 1., 0., 1.)
        self.indicator_targ.color = (0.75, .75, .75, 1.)

    def _start_targ_hold(self, **kwargs):
        self.periph_target1.color = (0., 1., 0., 1.)
        self.indicator_targ.color = (0.75, .75, .75, 1.)

    def _end_center_hold(self, **kwargs):
        self.center_target.color = (0., 0., 0., 1.)
        self.first_time_for_this_targ = True

    def _end_target_hold(self, **kwargs):
        self.periph_target1.color = (0., 0., 0., 0.)
        self.first_time_for_this_targ = True

    def _start_touch_error(self, **kwargs):
        self.center_target.color = (0., 0., 0., 1.)
        self.periph_target1.color = (0., 0., 0., 1.)
        self.periph_target2.color = (0., 0., 0., 1.)
        self.repeat = True

    def _start_timeout_error(self, **kwargs):
        self.center_target.color = (0., 0., 0., 1.)
        self.periph_target1.color = (0., 0., 0., 1.)
        self.periph_target2.color = (0., 0., 0., 1.)
        #self.repeat = True

    def _start_hold_error(self, **kwargs):
        self.center_target.color = (0., 0., 0., 1.)
        self.periph_target1.color = (0., 0., 0., 1.)
        self.periph_target2.color = (0., 0., 0., 1.)
        self.repeat = True

    def _start_drag_error(self, **kwargs):
        self.center_target.color = (0., 0., 0., 1.)
        self.periph_target1.color = (0., 0., 0., 1.)
        self.periph_target2.color = (0., 0., 0., 1.)
        self.repeat = True

    def _start_target(self, **kwargs):
        Window.clearcolor = (0., 0., 0., 1.)
        self.center_target.color = (0., 0., 0., 0.)
        
        if self.target_index == 1:
            self.periph_target_position = self.target1_position
        elif self.target_index == 2:
            self.periph_target_position = self.target2_position

        self.periph_target1.move(self.periph_target_position)
        self.periph_target1.color = (1., 1., 0., 1.)
        self.repeat = False
        self.exit_target1.color = (.15, .15, .15, 1)
        self.exit_target2.color = (.15, .15, .15, 1)
        self.indicator_targ.color = (.25, .25, .25, 1.)
        if self.first_target_attempt:
            self.first_target_attempt_t0 = time.time();
            self.first_target_attempt = False
        
        if self.first_time_for_this_targ:
            self.first_time_for_this_targ_t0 = time.time()
            self.periph_target2.color = (0., 0., 0., 0.)
            self.first_time_for_this_targ = False
    
    def _while_target(self, **kwargs):
        # check and see if it is time for the next target to appear
        if self.time_to_next_targ is not False:
            # import pdb; pdb.set_trace()
            if time.time() - self.first_time_for_this_targ_t0 > self.time_to_next_targ and self.target_index == 1:
                # illuminate the next target
                self.periph_target2.move(self.target2_position)
                self.periph_target2.color = (1., 1., 0., 1.)

    def _start_reward(self, **kwargs):
        self.trial_counter += 1
        Window.clearcolor = (1., 1., 1., 1.)
        self.periph_target1.color = (1., 1., 1., 1.)
        self.periph_target2.color = (1., 1., 1., 1.)
        self.exit_target1.color = (1., 1., 1., 1.)
        self.exit_target2.color = (1., 1., 1., 1.)
        self.rew_cnt = 0
        self.cnts_in_rew = 0
        self.indicator_targ.color = (1., 1., 1., 1.)
        self.repeat = False

    def _while_reward(self, **kwargs):
        if self.rew_cnt == 1:
            self.run_big_rew()
            self.rew_cnt += 1

    def _start_rew_anytouch(self, **kwargs):
        #if self.small_rew_cnt == 1:
        if self.reward_for_anytouch[0]:
            self.run_small_rew()
        else:
            self.repeat = True
            #self.small_rew_cnt += 1

    def run_big_rew(self, **kwargs):
        try:
            print('in big reward:')
            self.repeat = False
            if self.reward_for_targtouch[0]:
                #winsound.PlaySound('beep1.wav', winsound.SND_ASYNC)
                #sound = SoundLoader.load('reward1.wav')
                print('in big reward 2')
                #print(str(self.reward_generator[self.trial_counter]))
                #print(self.trial_counter)
                #print(self.reward_generator[:100])
                self.reward1 = SoundLoader.load('reward1.wav')
                self.reward1.play()

                if not self.skip_juice:
                    if self.reward_generator[self.trial_counter] > 0:
                        self.reward_port.open()
                        #rew_str = [ord(r) for r in 'inf 50 ml/min '+str(self.reward_for_targtouch[1])+' sec\n']
                        rew_str = [ord(r) for r in 'inf 50 ml/min '+str(self.reward_generator[self.trial_counter])+' sec\n']
                        self.reward_port.write(rew_str)
                        time.sleep(.25 + self.reward_delay_time)
                        run_str = [ord(r) for r in 'run\n']
                        self.reward_port.write(run_str)
                        self.reward_port.close()
        except:
            pass
        
    def run_small_rew(self, **kwargs):
        try:
            if np.logical_or(self.reward_for_anytouch[0], self.reward_for_center[0]):
                #winsound.PlaySound('beep1.wav', winsound.SND_ASYNC)
                sound = SoundLoader.load('reward2.wav')
                sound.play()

                ### To trigger reward make sure reward is > 0:
                if np.logical_or(np.logical_and(self.reward_for_anytouch[0], self.reward_for_anytouch[1] > 0), 
                    np.logical_and(self.reward_for_center[0], self.reward_for_center[1] > 0)):

                    self.reward_port.open()
                    if self.reward_for_anytouch[0]:
                        rew_str = [ord(r) for r in 'inf 50 ml/min '+str(self.reward_for_anytouch[1])+' sec\n']
                    elif self.reward_for_center[0]:
                        rew_str = [ord(r) for r in 'inf 50 ml/min '+str(self.reward_for_center[1])+' sec\n']
                    self.reward_port.write(rew_str)
                    time.sleep(.25)
                    run_str = [ord(r) for r in 'run\n']
                    self.reward_port.write(run_str)
                    self.reward_port.close()
        except:
            pass

        #self.repeat = True

    def end_reward(self, **kwargs):
        self.indicator_txt_color = (1.,1., 1., 1.)
        if self.use_white_screen:
            if len(self.cursor_ids)== 0:
                return True
        else:
            if self.cnts_in_rew > 30:
                return True
            else:
                self.cnts_in_rew += 1
                return False

    def end_rewanytouch(self, **kwargs):
        if self.small_rew_cnt > 1:
            return True
        else:
            return False

    def end_touch_error(self, **kwargs):
        return kwargs['ts'] >= self.touch_error_timeout

    def end_timeout_error(self, **kwargs):
        return kwargs['ts'] >= self.timeout_error_timeout

    def end_hold_error(self, **kwargs):
        return kwargs['ts'] >= self.hold_error_timeout

    def end_drag_error(self, **kwargs):
        return kwargs['ts'] >= self.drag_error_timeout

    def touch_center(self, **kwargs):
        if self.drag_ok:
            return self.check_if_cursors_in_targ(self.center_target_position, self.center_target_rad)
        else:
            return np.logical_and(self.check_if_cursors_in_targ(self.center_target_position, self.center_target_rad),
                self.check_if_started_in_targ(self.center_target_position, self.center_target_rad))

    def center_timeout(self, **kwargs):
        return kwargs['ts'] > self.ch_timeout

    def finish_center_hold(self, **kwargs):
        if self.cht <= kwargs['ts']:
            if self.reward_for_center[0]:
                self.run_small_rew()
            return True
        else:
            return False

    def early_leave_center_hold(self, **kwargs):
        return not self.check_if_cursors_in_targ(self.center_target_position, self.center_target_rad)
        
    def center_drag_out(self, **kwargs):
        touch = self.touch
        self.touch = True
        stay_in = self.check_if_cursors_in_targ(self.center_target_position, self.center_target_rad)
        self.touch = touch
        return not stay_in

    def touch_target(self, **kwargs):
        if self.drag_ok:
            return self.check_if_cursors_in_targ(self.periph_target_position, self.periph_target_rad)
        else:
            return np.logical_and(self.check_if_cursors_in_targ(self.periph_target_position, self.periph_target_rad),
                self.check_if_started_in_targ(self.periph_target_position, self.periph_target_rad))

    def target_timeout(self, **kwargs):
        #return kwargs['ts'] > self.target_timeout_time
        if time.time() - self.first_target_attempt_t0 > self.target_timeout_time:
            self.repeat = False
            return True

    def finish_targ1_hold(self, **kwargs):
        if self.target_index == 1:
            if self.tht <= kwargs['ts']:
                # Play a small reward tone
                sound = SoundLoader.load('reward2.wav')
                sound.play()
                self.target_index = 2
                return True
            else:
                return False
        else:
            return False
        
    def finish_targ2_hold(self, **kwargs):
        if self.target_index == 2:
            return self.tht <= kwargs['ts']
        else:
            return False

    def early_leave_target_hold(self, **kwargs):
        return not self.check_if_cursors_in_targ(self.periph_target_position, self.periph_target_rad)

    def targ_drag_out(self, **kwargs):
        touch = self.touch
        self.touch = True
        stay_in = self.check_if_cursors_in_targ(self.periph_target_position, self.periph_target_rad)
        self.touch = touch
        return not stay_in

    def anytouch(self, **kwargs):
        if not self.touch_target():
            current_touch = len(self.cursor_ids) > 0
            rew = False
            if current_touch and not self.anytouch_prev:
                rew = True
            self.anytouch_prev = current_touch
            return rew
        else:
            return False

    def get_4targets(self, target_distance=4, nudge=0., gen_kwarg=None):
        return self.get_targets_co(target_distance=target_distance, nudge=0.)

    def get_targets_co(self, target_distance=4, nudge=0., gen_kwarg=None, ntargets=4):
        # Targets in CM: 
        if gen_kwarg ==  'corners':
            angle = np.linspace(0, 2*np.pi, 5)[:-1] + (np.pi/4.)
            target_distance = 6.
        else:
            angle = np.linspace(0, 2*np.pi, ntargets+1)[:-1]

        if self.in_cage:
            offset = np.array([-4., 0.])
            nudge_targ = np.array([0, 0, 0, 0])
            target_distance = 3.
        else:
            offset = np.array([0., 0.])
            nudge_targ = np.array([0, 0, 1., 0])
    
        x = np.cos(angle)*target_distance
        y = np.sin(angle)*target_distance
        tmp = np.hstack((x[:, np.newaxis], y[:, np.newaxis]))
        


        ### Add offset to the target positions 
        tmp = tmp + offset[np.newaxis, :]

        tgs = []
        nudges = []
        for blks in range(100):
            ix = np.random.permutation(tmp.shape[0])
            tgs.append(tmp[ix, :])
            nudges.append(nudge_targ[ix])

        tgs = np.vstack((tgs))
        nudges = np.hstack((nudges))
        nudge_ix = np.nonzero(nudges==1)[0]
        #print('Nudges: ')
        #print(len(nudge_ix))

        to_nudge = np.array([-1., 1.])*nudge
        tgs[nudge_ix, :] = tgs[nudge_ix, :] + to_nudge[np.newaxis, :]

        return tgs

    def get_targets_rand(self, target_distance=4):
        # Targets in CM: 
        angle = np.linspace(0, 2*np.pi, 1000)
        target_distance = np.linspace(target_distance/4., target_distance, 1000)

        ix_ang = np.random.permutation(1000)
        ix_dist = np.random.permutation(1000)

        x = np.cos(angle[ix_ang])*target_distance[ix_dist]
        y = np.sin(angle[ix_ang])*target_distance[ix_dist]
        return np.hstack((x[:, np.newaxis], y[:, np.newaxis]))

    def check_if_started_in_targ(self, targ_center, targ_rad):
        startedInTarg = False
        if self.touch:
            for id_ in self.cursor_ids:
                # If in target: 
                if np.linalg.norm(np.array(self.cursor[id_]) - targ_center) < targ_rad:
                    if np.linalg.norm(np.array(self.cursor_start[id_]) - targ_center) < targ_rad:
                        startedInTarg = True
        return startedInTarg

    def check_if_cursors_in_targ(self, targ_center, targ_rad):
        if self.touch:
            inTarg = False
            for id_ in self.cursor_ids:
                if np.linalg.norm(np.array(self.cursor[id_]) - targ_center) < targ_rad:
                    inTarg = True

            return inTarg
        else:
            return False
Exemplo n.º 3
0
class Effects(Screen):
    events_callback = ObjectProperty(None)
    sets = ObjectProperty(None)
    title_previous = StringProperty('')  # 액션바
    selectedImagePath = StringProperty('')

    def __init__(self, **kwargs):
        super(Effects, self).__init__(**kwargs)
        self.pos = (0, 0)
        self.size_hint = (1, 1)
        self.config = ConfigParser()

    # effect 효과의 타이틀명, 샘플이지미파일, 효과 프로그램명 정보 읽어오기
    def _get_effects(self):
        self.config.read(os.path.join(directory, 'Libs/mangopaint.ini'))
        _items = self.config.items('Effects')
        for key, _item in _items:
            img_sh = _item.split(',')
            _data = {
                'key': key,
                'image': img_sh[0],
                'pgm': img_sh[1],
                'order': img_sh[2]
            }
            self.effects_data.list.append(_data)

    # effects.kv 에서 호출하는 effects 화면 생성하기
    def create_effects_screen(self, imagePath):
        self.effects_data = Box()
        self.effects_data.pos = 0
        self.effects_data.list = []
        self._get_effects()
        self.effects_data.list.sort(key=lambda x: int(x['order']))
        self.effectsbar = EffectsBar(meta=self.effects_data)
        self.selectedImagePath = imagePath
        self.ids.myimage.source = self.selectedImagePath
        # self.container.add_widget(self.effectsbar.ret_scrollview)
        self.add_widget(self.effectsbar, index=0)

    # effectsbar 화면 랜더링
    # def update_effectsbar(self):
    #     self.effectsbar.update()

    # ###########################################
    # effect 효과를 적용하도록 화면 변경하가
    # ###########################################
    def transform_to_image(self, image_pos):
        self.effects_data.pos = image_pos
        _data = self.effects_data.list[image_pos]
        cmd = []

        # if 조건이 exclusive 하지 않아 혹 조건을 빠져나갈수 있는 위험이 있으니 주의할것!
        # 우선 뉴럴스타일이면, 다음으로 특정스타일을 물어보는 식이라...
        if _data.pgm == 'neural_style.py':
            args, output = self.neural_style_arguments(image_pos, _data.key)

        elif _data.key == 'edge_detect':
            args, output = self.edge_detect_arguments(image_pos)
        elif _data.key == 'water_color':
            args, output = self.water_color_arguments(image_pos)
        elif _data.key == 'oil_color':
            args, output = self.oil_color_arguments(image_pos)
        else:
            args, output = self.edge_detect_arguments(image_pos)

        cmd.append(sys.executable or 'python3')
        cmd.append(args)
        print(cmd[0] + cmd[1])
        process = Popen(cmd[0] + cmd[1],
                        shell=True,
                        stdout=PIPE,
                        stderr=STDOUT)
        out, err = process.communicate()
        self.ids.myimage.source = output
        print('effect function end: ' + _data.key + ' <==================')
        self.ids.myimage.reload()

    # 뉴럴스타일 방식의 패턴은 아래 함수로 집중
    def neural_style_arguments(self, image_pos, key):
        # python3 ./fast_neural_style/neural_style.py eval --content-image ../../../images/Colorful-Paint-with-Paper-Texture.jpg --model ./fast_neural_style/saved_models/mosaic.model --output-image ../Data/temp.png --cuda 0
        _pgm = os.path.join(directory, 'Effects/fast_neural_style',
                            self.effects_data.list[image_pos].pgm)
        output = os.path.join(directory, 'Data', 'temp.png')
        _cuda = 1
        _model = os.path.join(directory,
                              'Effects/fast_neural_style/saved_models',
                              key + '.model')
        args = " {} eval --content-image {} --model {} --output-image {} --cuda {}".format(
            _pgm, self.selectedImagePath, _model, output, _cuda)
        return args, output

    # 흑백 스케치 느낌 (아직은 칼라네요....)
    def edge_detect_arguments(self, image_pos):
        # python3 edge_detecting.py --content=$1 --output=$2 --blurred=$3
        # python3 edge_detecting.py --content "../../../images/mosaic.jpg" --output "../Data/mosaic_edge_result.png" --blurred 3
        _pgm = os.path.join(directory, 'Effects',
                            self.effects_data.list[image_pos].pgm)
        output = os.path.join(directory, 'Data', 'temp.png')
        args = " {} --content {} --output {} --blurred {}".format(
            _pgm, self.selectedImagePath, output, 3)
        return args, output

    # 유화 느낌
    def oil_color_arguments(self, image_pos):
        # python oil_painting.py --content=$1 --output=$2 --radius=$3 --intensity=$4
        # python3 oil_coloring.py --content "../../../images/mosaic.jpg" --output "../Data/mosaic_oil_result.png" --radius 5 --intensity 20
        _pgm = os.path.join(directory, 'Effects',
                            self.effects_data.list[image_pos].pgm)
        output = os.path.join(directory, 'Data', 'temp.png')
        args = " {} --content {} --output {} --radius {}  --intensity {}".format(
            _pgm, self.selectedImagePath, output, 5, 20)
        return args, output

    # 수채화 느낌
    def water_color_arguments(self, image_pos):
        # python3 Water_coloring.py --content=$1 --output=$2
        # python3 water_coloring.py --content "../../../images/mosaic.jpg" --output "../Data/mosaic_water_result.png"
        _pgm = os.path.join(directory, 'Effects',
                            self.effects_data.list[image_pos].pgm)
        output = os.path.join(directory, 'Data', 'temp.png')
        args = " {} --content {} --output {}".format(_pgm,
                                                     self.selectedImagePath,
                                                     output)
        return args, output

    # effects 화면에서 다시 갤러리 화면으로 돌아갈때.
    def remove_effects_widgets(self):
        print('destroyed')
        self.effects_data.clear()
Exemplo n.º 4
0
class ShowWidget(Widget):
    """Widget全体を管理するスクリプト"""

    #------ 共通プロパティの宣言 ---------------
    text = StringProperty()
    source = StringProperty(imagedir + 'lefton.png')
    color = ListProperty([1, 1, 1, 1])
    fileinputimage = StringProperty(imagedir + 'nomalimage.png')
    filepath = StringProperty()
    check1 = BooleanProperty(False)
    check2 = BooleanProperty(False)
    check3 = BooleanProperty(False)
    check_radio = BooleanProperty(True)

    loadfile = ObjectProperty(None)
    savefile = ObjectProperty(None)
    text_input = ObjectProperty(None)

    def dismiss_popup(self):
        self._popup.dismiss()

    def show_load(self):
        content = LoadDialog(load=self.load, cancel=self.dismiss_popup)
        self._popup = Popup(title="Load file",
                            content=content,
                            size_hint=(0.9, 0.9))
        self._popup.open()

    def show_save(self):
        content = SaveDialog(save=self.save, cancel=self.dismiss_popup)
        self._popup = Popup(title="Save file",
                            content=content,
                            size_hint=(0.9, 0.9))
        self._popup.open()

    def load(self, path, filename):
        with open(os.path.join(path, filename[0])) as stream:
            self.text_input.text = stream.read()

        self.dismiss_popup()

    def save(self, path, filename):
        print(path)
        print(filename)
        savedir = os.path.dirname(path)
        if os.path.splitext(filename)[1] == ".png":
            savepath = os.path.join(savedir, filename)
        else:
            savepath = os.path.join(savedir, filename + ".png")
        print(savepath)
        graphpanel = self.ids.graph_view
        graphpanel.save_graph(savepath)

        self.dismiss_popup()

    #def save_graph(self):
    #    """graphの保存"""
    #    #filechooser = Root()
    #    #filechooser.show_save()
    #    content = PopupChooseFile(select=self.select, cancel=self.cancel)
    #    self.popup = Popup(title="Select MP3", content=content)
    #    self.popup.open()

    #------ ShowWidgetの初期化 ---------------
    def __init__(self, **kwargs):
        super(ShowWidget, self).__init__(**kwargs)
        Window.bind(on_dropfile=self._on_file_drop)

    #------ ボタンイベント --------------------
    def buttonClickedroom(self):
        # 左のタブがクリックされた時
        # 表示画像の変更
        self.source = imagedir + 'lefton.png'
        print("buttonClickedroom!!!")

    def buttonClickedfile(self):
        # 右のタブがクリックされた時
        # 表示画像の変更
        self.source = imagedir + 'righton.png'
        print("buttonClickedfile!!!")

    def checkbox_check1(self, checkbox):
        # チェックボックス1が押された時
        self.check1 = checkbox.active
        return

    def checkbox_check2(self, checkbox):
        # チェックボックス2が押された時
        self.check2 = checkbox.active
        return

    def checkbox_check3(self, checkbox):
        # チェックボックス3が押された時
        self.check3 = checkbox.active
        return

    def checkbox_check(self, checkbox, funcname):
        global lists_id
        self.check_radio = checkbox.active
        graphpanel = self.ids.graph_view
        graphpanel.checkbox_check_test(funcname)
        configpanel = self.ids.conpanel
        configpanel.make_config_panel_list(funcname)
        # 表示グラフ指定チェックボックスが変化した時
        if len(lists_id) != 0:
            for index, item in lists_id.items():
                print(item.active)

    #------ ウィンドウイベント -----------------
    def _on_file_drop(self, window, file_path):
        # ウィンドウにドロップ&ドラッグされたファイルの名前を取得
        global loadfilepath
        # ファイル名を格納
        loadfilepath = file_path.decode()
        # 表示ファイル名を変更
        self.filepath = os.path.basename(file_path.decode())

    #------ 設定パネルの再描画 -----------------
    def conpanel(self):
        # 選択されたグラフの種類で設定パネルの中身を変更する
        graphpanel = self.ids.graph_view
        graphpanel.load_data()

        configpanel = self.ids.conpanel
        configpanel.make_config_panel(["tagai", "hamada"])
Exemplo n.º 5
0
class ShellWidget(Widget):
    place = StringProperty()
    other = ObjectProperty()
    default_height = NumericProperty()
    highest_index = None
    lowest_index = None

    def __init__(self, **kwargs):
        super(ShellWidget, self).__init__(**kwargs)
        self.size_hint_y, self.height = None, 0
        self.fake_children = {}

    def on_parent(self, _, parent):
        if self.place == 'top':
            self.other = parent._botwidget
        elif self.place == 'bot':
            self.other = parent._topwidget
        self.default_height = parent.default_height
        self.parent.bind(default_height=self.setter('default_height'))

    def add_widget(self, index=None, height=0):
        if index is None:
            if self.place == 'bot':
                len_other = len(self.other.fake_children)
                len_view = len(self.parent.view.children)
                index = len(self.fake_children) + len_other + len_view
            else:
                index = len(self.fake_children)

        if not self.lowest_index:
            self.lowest_index = index
        if not self.highest_index:
            self.highest_index = index

        if not height:
            if index in self.parent.height_cache:
                height = self.parent.height_cache[index]
            else:
                height = self.default_height

        if index > self.highest_index:
            self.highest_index = index
        elif index <= self.lowest_index:
            self.lowest_index = index

        self.fake_children[index] = {'index': index, 'height': height}
        self.height += height

    def remove_widget(self, index=None):
        if not index:
            if self.place == 'top':
                index = self.highest_index
            else:
                index = self.highest_index

        try:
            self.height -= self.fake_children[index]['height']
            del self.fake_children[index]
        except Exception as e:
            print('ERROR KEY', e)
            print('LOW', self.lowest_index, 'HIGH', self.highest_index)
            raise Exception('NAAV')

        if index == self.highest_index:
            for x in reversed(range(self.lowest_index - 1,
                                    self.highest_index)):
                if x in self.fake_children:
                    self.highest_index = x
                    break

        elif index == self.lowest_index:
            for x in range(self.lowest_index - 1, self.highest_index + 1):
                if x in self.fake_children:
                    self.lowest_index = x
                    break

    def remove_widgets_fast(self, start, stop):
        stop = stop + 1
        for index in range(start, stop):
            # print index
            self.height -= self.fake_children[index]['height']
            del self.fake_children[index]

    def clear_widgets(self):
        self.fake_children = {}
        self.height = 0

    def swap_height(self, pos):
        if not self.fake_children:
            return
##        print ('{}: swap_height: {}'.format(self.place.upper(), pos))

        view_count = self.parent.get_view_count()
        child_count = len(self.fake_children)
        pos = abs(pos)
        height_adjust = self.height - pos
        remlist = []
        if self.place == 'bot':
            view_count = -view_count
            view_index = -child_count
            iter_range = range(self.lowest_index, self.highest_index + 1)
        else:
            view_index = child_count
            iter_range = reversed(
                range(self.lowest_index, self.highest_index + 1))

        height = self.height
        time0 = time()
        for i in iter_range:
            if i not in self.fake_children:
                break
            index, child = i, self.fake_children[i]
            remlist.append(index)
            height -= child['height']
            self.other.add_widget(index=index + view_count,
                                  height=child['height'])
            if height <= height_adjust:
                break

        len_remlist = len(remlist)
        if len_remlist:
            time1 = time() - time0

            if self.place == 'top':
                self.remove_widgets_fast(remlist[-1], remlist[0])
                self.highest_index -= len_remlist
                self.parent.move_view_index(self.highest_index + 1)
            else:
                self.remove_widgets_fast(remlist[0], remlist[-1])
                self.lowest_index += len_remlist
                self.parent.move_view_index(self.other.highest_index + 1)
                self.highest_index = self.lowest_index + len(
                    self.fake_children) - 1

            time2 = time() - time0

    def swap_one(self):
        if not self.fake_children:
            return

        if self.place == 'top':
            rem_index, add_index = self.parent.move_view_top()
        else:
            rem_index, add_index = self.parent.move_view_bot()

        # print ('{}: swap_one: {} to {}'.format(
        #     self.place.upper(), rem_index, add_index))

        self.remove_widget(index=rem_index)
        self.other.add_widget(index=add_index)
Exemplo n.º 6
0
class MoveConfirmPopup(NormalPopup):
    """Popup that asks to confirm a file or folder move."""
    target = StringProperty()
    photos = ListProperty()
    origin = StringProperty()
Exemplo n.º 7
0
class RightPanelBtn(PanelBtn):
    pos_hint = DictProperty({'right': 1, 'bottom': 1})
    text = StringProperty('Ok')

    def _current_screen(self):
        return self.parent if hasattr(self.parent, 'manager') else None

    def on_press(self, *args, **kwargs):
        _screen = self._current_screen()
        menu_screen = ValidObject.menu_screen(_screen.manager.get_screen('menu'))
        bikes_screen = ValidObject.bikes_screen(_screen.manager.get_screen('bikes'))
        maps_screen = ValidObject.maps_screen(_screen.manager.get_screen('maps'))
        shop_screen = ValidObject.shop_screen(_screen.manager.get_screen('shop'))
        screens = [menu_screen, bikes_screen, maps_screen, shop_screen]

        if 'BikesScreen' == _screen.__class__.__name__:
            if not app_config('bike', 'title'):
                bike_model = get_bike_by_title(bikes_screen.ids['title'].text)
                rest_rm = calc_rest_rm(bike_model.price)

                if Bike.buy(bike_model):
                    RightPanelBtn.change_rm(screens, rest_rm)
                    RightPanelBtn.change_character_wrap(bikes_screen.ids['character_wrap_price'], bike_model.price)
                    RightPanelBtn.change_character_wrap(bikes_screen.ids['character_wrap_power'], bike_model.power)
                    RightPanelBtn.change_character_wrap(bikes_screen.ids['character_wrap_speed'], bike_model.speed)
                    RightPanelBtn.change_character_wrap(bikes_screen.ids['character_wrap_acceleration'], bike_model.acceleration)
                    RightPanelBtn.change_character_wrap(bikes_screen.ids['character_wrap_agility'], bike_model.agility)
                    RightPanelBtn.cancel_animation_button(screens, 'left_panel_menu_bikes')

                    self.init_item(menu_screen.init_bike)
                    RightPanelBtn.change_bottom_right_btn(menu_screen)
                    bikes_screen.ids['title'].color = UColor.hex(UColor.WHITE)
                else:
                    Clock.schedule_once(self._create_animation_fail, 0)
                    Clock.schedule_once(self._clear_animation, .5)

        elif 'MapsScreen' == _screen.__class__.__name__:
            if not app_config('map', 'title'):
                map_model = get_map_by_title(maps_screen.ids['title'].text)
                rest_rm = calc_rest_rm(map_model.price)

                if BaseLevel.buy(map_model):
                    RightPanelBtn.change_rm(screens, rest_rm)
                    RightPanelBtn.change_character_wrap(maps_screen.ids['character_wrap_price'], map_model.price)
                    RightPanelBtn.change_character_wrap(maps_screen.ids['character_wrap_record'], '/dev/')
                    RightPanelBtn.change_character_wrap(maps_screen.ids['character_wrap_level'], map_model.level)
                    RightPanelBtn.change_character_wrap(maps_screen.ids['character_wrap_map'], map_model.map)
                    RightPanelBtn.change_character_wrap(maps_screen.ids['character_wrap_total_way'], map_model.total_way)
                    RightPanelBtn.cancel_animation_button(screens, 'left_panel_menu_maps')

                    self.init_item(menu_screen.init_map)
                    RightPanelBtn.change_bottom_right_btn(menu_screen)
                    maps_screen.ids['title'].color = UColor.hex(UColor.WHITE)
                else:
                    Clock.schedule_once(self._create_animation_fail, 0)
                    Clock.schedule_once(self._clear_animation, .5)

    def init_item(self, cb_init):
        self.text = 'Ok'
        self.disabled = True
        cb_init()
        Clock.schedule_once(self._create_animation_success, 0)
        Clock.schedule_once(self._clear_animation, .4)

    def _create_animation_success(self, dt):
        bg = BgAnimation(widget=self._current_screen())
        bg.anim_color(bg.rgba_success)

    def _create_animation_fail(self, dt):
        bg = BgAnimation(widget=self._current_screen())
        bg.anim_color(bg.rgba_error)

    def _clear_animation(self, dt):
        bg = BgAnimation(widget=self._current_screen())
        bg.anim_color(bg.rgba_default)

    @staticmethod
    def cancel_animation_button(screens, sid):
        [Animation.cancel_all(s.ids[sid], 'background_color') for s in screens]

    @staticmethod
    def change_rm(screens, value):
        for s in screens:
            GetObject.get_child(s, 'RMLayout').ids['panel_rm'].text = "{}: {}".format(Currency.units, value)
            return

    @staticmethod
    def change_character_wrap(character_wrap, value, color=UColor.hex(UColor.WHITE)):
        if type(value) is int:
            progress_bar = ValidObject.progress_bar(character_wrap.children[0].children[0])
            character_wrap.value = value
            character_wrap.max = progress_bar.max = value

        if character_wrap.has_value:
            character_wrap.title = character_wrap.format_number()
        else:
            character_wrap.title = character_wrap.format_string()

        RightPanelBtn.prop_buttons_show(character_wrap)
        RightPanelBtn.change_color_labels_right_panel(character_wrap, color)

    @staticmethod
    def prop_buttons_show(character_wrap):
        btn = character_wrap.children[1].children[0]
        btn.disabled = not character_wrap.has_value
        btn.opacity = 1

    @staticmethod
    def change_bottom_right_btn(menu_screen):
        if app_config('bike', 'title') and app_config('map', 'title'):
            menu_screen.ids['right_panel_btn'].text = 'Go'
            menu_screen.ids['right_panel_btn'].disabled = False

    @staticmethod
    def change_color_labels_right_panel(character_wrap, color):
        RightPanelBtn._change_color_labels(character_wrap.children[0], color)
        RightPanelBtn._change_color_labels(character_wrap.children[1], color)

    @staticmethod
    def _change_color_labels(wrap_children, color):
        for lbl in wrap_children.children[:]:
            if type(lbl) is Label:
                lbl.color = color
Exemplo n.º 8
0
class FileViewItem(ToggleButtonBehavior, BoxLayout):
    name = StringProperty()
    source = StringProperty()
    is_all_folder = StringProperty(False)
    tmplWidget = ObjectProperty()

    def on_state(self, target, state):
        self.realise()
        if state == 'down':
            if self.name.lower().endswith('.kv'):
                f = Factory.get('FileItemOptionKV')()
            else:
                f = Factory.get('FileItemOption')()
            f.is_all_folder = self.is_all_folder
            self.add_widget(f)
            from kivy.animation import Animation
            anim = Animation(size_hint_y=0.2, duration=.1)
            anim.start(f)
        else:
            #remove the choice box
            self.remove_widget(self.children[0])

    def on_press(self):
        if self.last_touch.is_double_tap:  #directly add it to the pool
            if self.name.endswith('.bgp'):
                self.extract_package()
            elif self.name.endswith('.py'):
                print 'should I really execute this script ???'
            else:
                self.add_item("1", 'normal')

    def add_item(self, qt, verso):
        from os.path import relpath
        stack = App.get_running_app().root.ids['deck'].ids['stack']
        ##########################################################
        qt = int(qt)
        if self.is_all_folder:
            #print self, self.source, self.name, self.is_all_folder
            #It is a folder, add all the imge from folder
            for name in [
                    x for x in os.listdir(self.is_all_folder)
                    if x.endswith(FILE_FILTER)
            ]:
                ##for fiv in self.parent.children:
                ##if fiv.is_all_folder: continue
                if name.endswith(('.csv', '.xlsx')):
                    continue
                box = StackPart()
                #box.name = fiv.name
                box.name = name
                box.source = os.path.join(self.is_all_folder, name)
                box.qt = qt
                box.verso = verso
                if name.endswith('.kv'):
                    if self.is_all_folder.startswith(gamepath):
                        fold = relpath(self.is_all_folder, gamepath)
                    else:
                        fold = self.is_all_folder
                    box.template = "@%s" % os.path.join(fold, name)
                    box.realise()
                stack.add_widget(box)
                from kivy.base import EventLoop
                EventLoop.idle()
        elif self.name.endswith('.csv'):
            App.get_running_app().root.ids.deck.load_file_csv(self.name)
        elif self.name.endswith('.xlsx'):
            App.get_running_app().root.ids.deck.load_file(self.name)
        elif self.name.endswith('.bgp'):
            self.extract_package()
        elif self.name.endswith('.py'):
            print 'should be executing', self.name, self.source, self.is_all_folder
            from imp import load_source
            Logger.info('Executing PYScript file %s' % self.name)
            m = self.name
            load_source(m[:-3], m)

        else:
            box = StackPart()
            box.name = self.name
            box.source = self.source
            box.qt = qt
            box.verso = verso
            stack.add_widget(box)
            if self.name.endswith('.kv'):
                box.tmplWidget = self.tmplWidget
                if self.name.startswith(gamepath):
                    fold = relpath(self.name, gamepath)
                else:
                    fold = self.name
                box.template = "@%s" % fold
                box.realise()

    def realise(self, use_cache=False, *args):
        if not self.name.endswith('.kv'):
            return
        #Force the creation of an image from self.template, thourhg real display
        from kivy.clock import Clock
        #Force the creaiotn of the tmpl miniture for display
        from template import BGTemplate
        try:
            Logger.info('[SGM] Realise FileItemView calling From File')
            tmpl = BGTemplate.FromFile(self.name, use_cache)[-1]
        except IndexError:
            Logger.warn('Warning: template file %s contains no Template !!' %
                        self.name)
            from utils import alert
            alert('Error while loading %s template' % self.name)
            return
        #App.get_running_app().root.ids['realizer'].add_widget(tmpl) #force draw of the beast
        self.tmplWidget = tmpl

        def inner(*args):
            from kivy.base import EventLoop
            EventLoop.idle()
            cim = tmpl.toImage()
            cim.texture.flip_vertical()
            self.ids['img'].texture = cim.texture
            #App.get_running_app().root.ids['realizer'].remove_widget(tmpl)

        Clock.schedule_once(inner, -1)

    def extract_package(self):
        from zipfile import ZipFile
        import os
        import tempfile
        zip_name = self.name
        zip_path = os.path.abspath(zip_name)
        temp_dir = tempfile.mkdtemp(prefix='BGM_%s' %
                                    os.path.split(self.name)[-1])
        from kivy.resources import resource_add_path
        resource_add_path(temp_dir)

        with ZipFile(zip_path, 'r') as zip_file:
            # Build a list of only the members below ROOT_PATH
            members = zip_file.namelist()
            # Extract only those members to the temp directory
            zip_file.extractall(temp_dir, members)

        from kivy.app import App
        dm = App.get_running_app().root.ids.deck

        #Looping on all mebers, resolving differntly depending on file type
        for m in members:
            if m.endsiwth('.kv'):
                m = os.path.join(temp_dir, m)
                Logger.info('Registering Package Template %s' % m)
                from template import templateList
                templateList.register_file(os.path.join(temp_dir, m))
            #then deck
            elif m.endswith('.csv'):
                m = os.path.join(temp_dir, m)
                Logger.info('Loading CSV File %s' % m)
                dm.load_file_csv(os.path.join(temp_dir, m))
            elif m.endwith('.xlsx'):
                m = os.path.join(temp_dir, m)
                Logger.info('Loading XLSX File %s' % m)
                dm.load_file(os.path.join(temp_dir, m))
            elif m.endswith('.py'):
                #First python
                from imp import load_source
                m = os.path.join(temp_dir, m)
                Logger.info('Executing Package file %s' % m)
                load_source(m[:-3], m)

        from utils import start_file
        start_file(temp_dir)
        #Ensure the last CSV file is not saved, as it is parts of a package
        dm.record_last_file('')
Exemplo n.º 9
0
class ColorPicker(RelativeLayout):
    '''
    See module documentation.
    '''

    font_name = StringProperty('data/fonts/DroidSansMono.ttf')
    '''Specifies the font used used on the Color Picker

    :data:`font_name` is an :class:`~kivy.properties.StringProperty`
    defaults to 'data/fonts/DroidSansMono.ttf'
    '''

    color = ListProperty((1, 1, 1, 1))
    '''The :data:`color` holds the color currently selected in rgba format.

    :data:`color` is an :class:`~kivy.properties.ListProperty` defaults to
    (1, 1, 1, 1)
    '''

    hsv = ListProperty((1, 1, 1))
    '''The :data:`hsv` holds the color currently selected in hsv format.

    :data:`hsv` is an :class:`~kivy.properties.ListProperty` defaults to
    (1, 1, 1)
    '''
    def _get_hex(self):
        return get_hex_from_color(self.color)

    def _set_hex(self, value):
        self.color = get_color_from_hex(value)[:4]

    hex_color = AliasProperty(_get_hex, _set_hex, bind=('color', ))
    '''The :data:`hex_color` holds the currently selected color in hex.

    :data:`hex_color` is a :class:`~kivy.properties.AliasProperty` default to
    `#ffffffff`
    '''

    wheel = ObjectProperty(None)
    '''The :data:`wheel` holds the color wheel.

    :data:`wheel` is an :class:`~kivy.properties.ObjectProperty` defaults to
    None
    '''

    # now used only internally.
    foreground_color = ListProperty((1, 1, 1, 1))

    def on_color(self, instance, value):
        if not self._updating_clr:
            self._updating_clr = True
            self.hsv = rgb_to_hsv(*value[:3])
            self._updating_clr = False

    def on_hsv(self, instance, value):
        if not self._updating_clr:
            self._updating_clr = True
            self.color[:3] = hsv_to_rgb(*value)
            self._updating_clr = False

    def _trigger_update_clr(self, mode, clr_idx, text):
        self._upd_clr_list = mode, clr_idx, text
        Clock.unschedule(self._update_clr)
        Clock.schedule_once(self._update_clr)

    def _update_clr(self, dt):
        mode, clr_idx, text = self._upd_clr_list
        try:
            text = max(0, min(254, float(text)))
            if mode == 'rgb':
                self.color[clr_idx] = float(text) / 255.
            else:
                self.hsv[clr_idx] = float(text) / 255.
        except ValueError:
            Logger.warning('Color Picker: invalid value : {}'.format(text))

    def _update_hex(self, dt):
        if len(self._upd_hex_list) != 9:
            return
        self.hex_color = self._upd_hex_list

    def _trigger_update_hex(self, text):
        self._upd_hex_list = text
        Clock.unschedule(self._update_hex)
        Clock.schedule_once(self._update_hex)

    def __init__(self, **kwargs):
        self._updating_clr = False
        super(ColorPicker, self).__init__(**kwargs)
Exemplo n.º 10
0
class ArrowWidget(Widget):
    """A widget that points from one :class:`~LiSE.gui.board.Spot` to
    another.

    :class:`Arrow`s are the graphical representations of
    :class:`~LiSE.model.Portal`s. They point from the :class:`Spot`
    representing the :class:`Portal`'s origin, to the one representing
    its destination.

    """
    board = ObjectProperty()
    name = StringProperty()
    margin = NumericProperty(10)
    """When deciding whether a touch collides with me, how far away can
    the touch get before I should consider it a miss?"""
    w = NumericProperty(2)
    """The width of the inner, brighter portion of the :class:`Arrow`. The
    whole :class:`Arrow` will end up thicker."""
    pawns_here = ListProperty([])
    trunk_points = ListProperty([])
    head_points = ListProperty([])
    points = ReferenceListProperty(trunk_points, head_points)
    trunk_quad_vertices_bg = ListProperty([0] * 8)
    trunk_quad_vertices_fg = ListProperty([0] * 8)
    left_head_quad_vertices_bg = ListProperty([0] * 8)
    right_head_quad_vertices_bg = ListProperty([0] * 8)
    left_head_quad_vertices_fg = ListProperty([0] * 8)
    right_head_quad_vertices_fg = ListProperty([0] * 8)
    slope = NumericProperty(0.0, allownone=True)
    y_intercept = NumericProperty(0)
    origin = ObjectProperty()
    destination = ObjectProperty()
    repointed = BooleanProperty(True)
    bg_scale_unselected = NumericProperty(4)
    bg_scale_selected = NumericProperty(5)
    selected = BooleanProperty(False)
    hit = BooleanProperty(False)
    bg_color_unselected = ListProperty()
    bg_color_selected = ListProperty()
    fg_color_unselected = ListProperty()
    fg_color_selected = ListProperty()
    arrowhead_size = NumericProperty(10)
    collide_radius = NumericProperty(3)
    collider = ObjectProperty()
    portal = ObjectProperty()

    def on_portal(self, *args):
        """Set my ``name`` and instantiate my ``mirrormap`` as soon as I have
        the properties I need to do so.

        """
        if not (self.board and self.origin and self.destination
                and self.origin.name in self.board.character.portal
                and self.destination.name in self.board.character.portal):
            Clock.schedule_once(self.on_portal, 0)
            return
        self.name = '{}->{}'.format(self.portal['origin'],
                                    self.portal['destination'])

    def collide_point(self, x, y):
        """Delegate to my ``collider``, or return ``False`` if I don't have
        one.

        """
        if not self.collider:
            return False
        return (x, y) in self.collider

    def __init__(self, **kwargs):
        """Create trigger for my _repoint method. Delegate to parent for
        everything else.

        """
        self._trigger_repoint = Clock.create_trigger(self._repoint, timeout=-1)
        super().__init__(**kwargs)

    def on_origin(self, *args):
        """Make sure to redraw whenever the origin moves."""
        if self.origin is None:
            Clock.schedule_once(self.on_origin, 0)
            return
        self.origin.bind(pos=self._trigger_repoint, size=self._trigger_repoint)

    def on_destination(self, *args):
        """Make sure to redraw whenever the destination moves."""
        if self.destination is None:
            Clock.schedule_once(self.on_destination, 0)
            return
        self.destination.bind(pos=self._trigger_repoint,
                              size=self._trigger_repoint)

    def on_board(self, *args):
        """Draw myself for the first time as soon as I have the properties I
        need to do so.

        """
        if None in (self.board, self.origin, self.destination):
            Clock.schedule_once(self.on_board, 0)
            return
        self._trigger_repoint()

    def add_widget(self, wid, index=0, canvas=None):
        """Put the :class:`Pawn` at a point along my length proportionate to
        how close it is to finishing its travel through me.

        Only :class:`Pawn` should ever be added as a child of :class:`Arrow`.

        """
        super().add_widget(wid, index, canvas)
        if not hasattr(wid, 'group'):
            return
        wid._no_use_canvas = True
        mycanvas = (self.canvas.before if canvas == 'before' else
                    self.canvas.after if canvas == 'after' else self.canvas)
        mycanvas.remove(wid.canvas)
        pawncanvas = (self.board.pawnlayout.canvas.before if canvas == 'before'
                      else self.board.pawnlayout.canvas.after
                      if canvas == 'after' else self.board.pawnlayout.canvas)
        for child in self.children:
            if hasattr(child, 'group') and child.group in pawncanvas.children:
                pawncanvas.remove(child.group)
            pawncanvas.add(child.group)
        self.pospawn(wid)

    def remove_widget(self, wid):
        """Remove the special :class:`InstructionGroup` I was using to draw
        this :class:`Pawn`.

        """
        super().remove_widget(wid)
        wid._no_use_canvas = False

    def on_points(self, *args):
        """Reposition my children when I have new points."""
        for pawn in self.children:
            self.pospawn(pawn)

    def pos_along(self, pct):
        """Return coordinates for where a Pawn should be if it has travelled
        along ``pct`` of my length (between 0 and 1).

        Might get complex when I switch over to using beziers for
        arrows, but for now this is quite simple, using distance along
        a line segment.

        """
        if pct < 0 or pct > 1:
            raise ValueError("Invalid portion")
        (ox, oy) = self.origin.center
        (dx, dy) = self.destination.center
        xdist = (dx - ox) * pct
        ydist = (dy - oy) * pct
        return (ox + xdist, oy + ydist)

    def pospawn(self, pawn):
        """Position a :class:`Pawn` that is my child so as to reflect how far
        its :class:`Thing` has gone along my :class:`Portal`.

        """
        if self.board.tick < pawn.thing['arrival_time']:
            # It's weird that the pawn is getting placed in me, but
            # I'll do my best..
            pawn.pos = self.pos_along(0)
            return
        elif (pawn.thing['next_arrival_time']
              and self.board.tick >= pawn.thing['next_arrival_time']):
            pawn.pos = self.pos_along(1)
            return
        try:
            pawn.pos = self.pos_along(
                (self.board.tick - pawn.thing['arrival_time']) /
                (pawn.thing['next_arrival_time'] - pawn.thing['arrival_time']))
        except (TypeError, ZeroDivisionError):
            pawn.pos = self.pos_along(0)

    def _get_points(self):
        """Return the coordinates of the points that describe my shape."""
        orig = self.origin
        dest = self.destination
        (ox, oy) = orig.center
        ow = orig.width if hasattr(orig, 'width') else 0
        taillen = float(self.arrowhead_size)
        ory = ow / 2
        (dx, dy) = dest.center
        (dw, dh) = dest.size if hasattr(dest, 'size') else (0, 0)
        dry = dh / 2
        return get_points(ox, oy, ory, dx, dy, dry, taillen)

    def _get_slope(self):
        """Return a float of the increase in y divided by the increase in x,
        both from left to right."""
        orig = self.origin
        dest = self.destination
        ox = orig.x
        oy = orig.y
        dx = dest.x
        dy = dest.y
        if oy == dy:
            return 0
        elif ox == dx:
            return None
        else:
            rise = dy - oy
            run = dx - ox
            return rise / run

    def _get_b(self):
        """Return my Y-intercept.

        I probably don't really hit the left edge of the window, but
        this is where I would, if I were long enough.

        """
        orig = self.origin
        dest = self.destination
        (ox, oy) = orig.pos
        (dx, dy) = dest.pos
        denominator = dx - ox
        x_numerator = (dy - oy) * ox
        y_numerator = denominator * oy
        return ((y_numerator - x_numerator), denominator)

    def _repoint(self, *args):
        """Recalculate points, y-intercept, and slope"""
        if None in (self.origin, self.destination):
            Clock.schedule_once(self._repoint, 0)
            return
        (self.trunk_points, self.head_points) = self._get_points()
        (ox, oy, dx, dy) = self.trunk_points
        r = self.w / 2
        bgr = r * self.bg_scale_selected if self.selected \
            else self.bg_scale_unselected
        self.trunk_quad_vertices_bg = get_thin_rect_vertices(
            ox, oy, dx, dy, bgr)
        self.collider = Collide2DPoly(self.trunk_quad_vertices_bg)
        self.trunk_quad_vertices_fg = get_thin_rect_vertices(ox, oy, dx, dy, r)
        (x1, y1, endx, endy, x2, y2) = self.head_points
        self.left_head_quad_vertices_bg = get_thin_rect_vertices(
            x1, y1, endx, endy, bgr)
        self.right_head_quad_vertices_bg = get_thin_rect_vertices(
            x2, y2, endx, endy, bgr)
        self.left_head_quad_vertices_fg = get_thin_rect_vertices(
            x1, y1, endx, endy, r)
        self.right_head_quad_vertices_fg = get_thin_rect_vertices(
            x2, y2, endx, endy, r)
        self.slope = self._get_slope()
        self.y_intercept = self._get_b()
        self.repointed = True
Exemplo n.º 11
0
class MDCheckbox(CircularRippleBehavior, ToggleButtonBehavior, MDIcon):
    active = BooleanProperty(False)

    checkbox_icon_normal = StringProperty("checkbox-blank-outline")
    checkbox_icon_down = StringProperty("checkbox-marked-outline")
    radio_icon_normal = StringProperty("checkbox-blank-circle-outline")
    radio_icon_down = StringProperty("checkbox-marked-circle-outline")

    selected_color = ListProperty()
    unselected_color = ListProperty()
    disabled_color = ListProperty()
    _current_color = ListProperty([0.0, 0.0, 0.0, 0.0])

    def __init__(self, **kwargs):
        self.check_anim_out = Animation(font_size=0, duration=0.1, t="out_quad")
        self.check_anim_in = Animation(
            font_size=sp(24), duration=0.1, t="out_quad"
        )
        super().__init__(**kwargs)
        self.selected_color = self.theme_cls.primary_color
        self.unselected_color = self.theme_cls.secondary_text_color
        self.disabled_color = self.theme_cls.divider_color
        self._current_color = self.unselected_color
        self.check_anim_out.bind(
            on_complete=lambda *x: self.check_anim_in.start(self)
        )
        self.bind(
            checkbox_icon_normal=self.update_icon,
            checkbox_icon_down=self.update_icon,
            radio_icon_normal=self.update_icon,
            radio_icon_down=self.update_icon,
            group=self.update_icon,
            selected_color=self.update_color,
            unselected_color=self.update_color,
            disabled_color=self.update_color,
            disabled=self.update_color,
            state=self.update_color,
        )
        self.update_icon()
        self.update_color()

    def update_icon(self, *args):
        if self.state == "down":
            self.icon = (
                self.radio_icon_down if self.group else self.checkbox_icon_down
            )
        else:
            self.icon = (
                self.radio_icon_normal
                if self.group
                else self.checkbox_icon_normal
            )

    def update_color(self, *args):
        if self.disabled:
            self._current_color = self.disabled_color
        elif self.state == "down":
            self._current_color = self.selected_color
        else:
            self._current_color = self.unselected_color

    def on_state(self, *args):
        if self.state == "down":
            self.check_anim_in.cancel(self)
            self.check_anim_out.start(self)
            self.update_icon()
            self.active = True
        else:
            self.check_anim_in.cancel(self)
            self.check_anim_out.start(self)
            self.update_icon()
            self.active = False

    def on_active(self, *args):
        self.state = "down" if self.active else "normal"
Exemplo n.º 12
0
class ICDialog(ThemableBehavior, RectangularElevationBehavior, ModalView):
    """
    Dialog box with the ability to add action buttons
    """
    title = StringProperty('KivyIC Dialog Box')
    '''
    Title of the Dialog Box.

    :attr:`title` is an :class:`~kivy.properties.StringProperty` and
    defaults to 'KivyIC Dialog Box'.
    
    .. versionadded:: 0.1
    '''

    content = ObjectProperty(None)
    '''
    Container widget for what will be displayed in the Dialog Box.

    :attr:`content` is an :class:`~kivy.properties.ObjectProperty` and
    defaults to None.
    
    .. versionadded:: 0.1
    '''

    content_fit = OptionProperty('items', options=['window', 'items'])
    md_bg_color = ListProperty([0, 0, 0, .2])

    _container = ObjectProperty()
    _action_buttons = ListProperty([])
    _action_area = ObjectProperty()
    action_area_width = NumericProperty()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.bind(_action_buttons=self._update_action_buttons,
                  auto_dismiss=lambda *x: setattr(
                      self.shadow, 'on_release', self.shadow.dismiss
                      if self.auto_dismiss else None))

    def add_action_button(self, text, action=None):
        """Add an :class:`FlatButton` to the right of the action area.

        :param icon: Unicode character for the icon
        :type icon: str or None
        :param action: Function set to trigger when on_release fires
        :type action: function or None
        """
        button = MDFlatButton(text=text, size_hint=(None, None), height=dp(36))
        if action:
            button.bind(on_release=action)
        # FIX - fix color
        button.text_color = self.theme_cls.primary_color
        button.md_bg_color = self.theme_cls.bg_light
        self._action_buttons.append(button)

    def add_widget(self, widget):
        if self._container:
            if self.content:
                raise PopupException(
                    'Popup can have only one widget as content')
            self.content = widget
        else:
            super(ICDialog, self).add_widget(widget)

    def open(self, *largs):
        '''Show the view window from the :attr:`attach_to` widget. If set, it
        will attach to the nearest window. If the widget is not attached to any
        window, the view will attach to the global
        :class:`~kivy.core.window.Window`.
        '''
        if self._window is not None:
            Logger.warning('ModalView: you can only open once.')
            return self
        # search window
        self._window = self._search_window()
        if not self._window:
            Logger.warning('ModalView: cannot open view, no window found.')
            return self
        self._window.add_widget(self)
        self._window.bind(on_resize=self._align_center,
                          on_keyboard=self._handle_keyboard)
        self.center = self._window.center
        self.bind(size=self._align_center)
        a = Animation(_anim_alpha=1., d=self._anim_duration)
        a.bind(on_complete=lambda *x: self.dispatch('on_open'))
        a.start(self)
        return self

    def dismiss(self, *largs, **kwargs):
        '''Close the view if it is open. If you really want to close the
        view, whatever the on_dismiss event returns, you can use the *force*
        argument:
        ::

            view = ModalView(...)
            view.dismiss(force=True)

        When the view is dismissed, it will be faded out before being
        removed from the parent. If you don't want animation, use::

            view.dismiss(animation=False)

        '''
        if self._window is None:
            return self
        if self.dispatch('on_dismiss') is True:
            if kwargs.get('force', False) is not True:
                return self
        if kwargs.get('animation', True):
            Animation(_anim_alpha=0., d=self._anim_duration).start(self)
        else:
            self._anim_alpha = 0
            self._real_remove_widget()
        return self

    def on_content(self, instance, value):
        if self._container:
            self._container.clear_widgets()
            self._container.add_widget(value)

    def on__container(self, instance, value):
        if value is None or self.content is None:
            return
        self._container.clear_widgets()
        self._container.add_widget(self.content)

    def on_touch_down(self, touch):
        if self.disabled and self.collide_point(*touch.pos):
            return True
        return super(ICDialog, self).on_touch_down(touch)

    def _update_action_buttons(self, *args):
        self._action_area.clear_widgets()
        self.action_area_width = 0
        for btn in self._action_buttons:
            btn.content.texture_update()
            btn.width = btn.content.texture_size[0] + dp(16)
            self.action_area_width += btn.width
            self._action_area.add_widget(btn)
        spacing = sum(self._action_area.spacing) - self._action_area.spacing[0]
        self.action_area_width += spacing
Exemplo n.º 13
0
class DialogOKDismiss(ICDialog):
    """
    Ok - Dismiss Dialog with Input Text field

    Parameters:
        text: str: value of input text field
        helper_text: str: helper text to provide feedback to user
        hint_text: str: to provide feedback to user

    Usage:
        bind to response to determine user action, true if click OK false if Dismiss
        bind to text to get information typed
    """
    text = StringProperty()
    '''
    String in the Input Text Field. Can be used to set of retrieve data.

    :data:`text` is an :class:`~kivy.properties.StringProperty`,
    defaults to ''.
     
    .. version added:: 0.1
    '''

    hint_text = StringProperty()
    '''
    Text that will show in text input prior to receiving focus, setting mode
    to persistant will cause to raise above input line while user is typing.

    :data:`hint_text` is an :class:`~kivy.properties.StringProperty`,
    defaults to ''.
     
    .. version added:: 0.1
    '''

    helper_text = StringProperty()
    '''
    Text that will show below text input. Text will stay before and after focus

    :data:`helper_text` is an :class:`~kivy.properties.StringProperty`,
    defaults to ''.
     
    .. version added:: 0.1
    '''

    response = BooleanProperty()
    '''
    Stores action of user when exiting dialog.  
    True if user clicks ok.
    False if user clicks Dismiss.

    :data:`response` is an :class:`~kivy.properties.BooleanProperty`,
     
    .. version added:: 0.1
    '''
    def __init__(self, **kwargs):
        super(DialogOKDismiss, self).__init__(**kwargs)
        content = InputDialog(hint_text=self.hint_text,
                              helper_text=self.helper_text)
        self.bind(text=self.setter(content.text))
        self.content = content
        self.add_action_button("OK", action=lambda *x: self.click(True))
        self.add_action_button("Dismiss", action=lambda *x: self.click(False))
        self.content.focus = True

    def click(self, response):
        self.response = response
        self.dismiss()
Exemplo n.º 14
0
class FileExplorerDialog(ICDialog):
    title = StringProperty('File Explorer Dialog')
    '''
    Title of the dialog window.
    
    :data:`title` is an :class:`~kivy.properties.StringProperty`,
    defaults to 'File Explorer Dialog'.
    .. version added:: 0.1
    '''

    # FIX - works on windows, need to set to $Home on Linux
    initial_directory = StringProperty()
    '''
    Starting directory for file explorer.

    :data:`initial_directory` is an :class:`~kivy.properties.StringProperty`,
    defaults to 'C:/Users/<user name>/' for windows, $HOME for Linux.
    .. version added:: 0.1
    '''

    filter = ListProperty()
    '''
    Filter to apply to files shown in file view.

    :data:`filter` is an :class:`~kivy.properties.ListProperty`,
    defaults to '*.*'.
     
    .. version added:: 0.1
    '''

    # TODO -- v 0.2 - add ability to select multiple files
    file_name_s = StringProperty()
    '''
    Name of selected file(s).  If multiple files are selected, each file will
    be separated by a comma.

    :data:`file_name_s` is an :class:`~kivy.properties.StringProperty`,
    defaults to ''.
     
    .. version added:: 0.1
    '''
    def __init__(self, **kwargs):
        super(FileExplorerDialog, self).__init__(**kwargs)
        user_path = os.path.join(get_home_directory(), 'Documents')

        file_explorer = FileExplorer(select_string='Select',
                                     favorites=[(user_path, 'Documents')])
        file_explorer.bind(on_success=self._fbrowser_success,
                           on_canceled=self._fbrowser_canceled,
                           on_submit=self._fbrowser_submit)
        file_explorer.file_selection_container.clear_widgets()
        self.content = file_explorer

        self.add_action_button(
            "Dismiss", action=lambda *x: file_explorer.dispatch('on_canceled'))
        self.add_action_button(
            "OK", action=lambda *x: file_explorer.dispatch('on_success'))

        Clock.schedule_once(partial(self._post_init, file_explorer))

    def _post_init(self, file_explorer, *args):
        file_explorer.filter_button.width = self.action_area_width

    def _fbrowser_canceled(self, instance):
        self.dismiss()

    def _fbrowser_success(self, instance):
        # ERR - self.file_name_s = instance.selection[0] / IndexError: list index out of range
        self.file_name_s = instance.selection[0]
        self.dismiss()

    def _fbrowser_submit(self, instance):
        self.file_name_s = instance.selection[0]
        self.dismiss()

    def add_button(self, buttons):
        """
        add action butttons via a dict
        :param buttons: dict: {button name: action}
        :return: None
        """
        for text, action in buttons.items():
            self.add_action_button(text, action=lambda *x: action())
Exemplo n.º 15
0
class DraggableObjectBehavior(object):
    """A widget that inherits from this class can participate in a drag by
    someone dragging it with the mouse.
    """

    drag_controller = ObjectProperty(None)
    """A (potentially global) :class:`DraggableController` instance that manages
    the (potential) drag. If `None` during the first potential drag, a
    :class:`DraggableController` instance will be created and set.
    """

    drag_widget = ObjectProperty(None)
    """The widget, whose texture will be copied and previewed as the object is
    dragged. If `None`, it's this widget.
    """

    drag_cls = StringProperty('')
    """A name to determine where we can potentially drop the dragged widget.
    For each :class:`DraggableLayoutBehavior` that we hover over, if
    :attr:`drag_cls` is in that :attr:`DraggableLayoutBehavior.drag_classes`, we
    will preview and allow the widget to be dropped there.
    """

    _drag_touch = None

    def initiate_drag(self):
        """Called by the :class:`DraggableController`, when a drag is initiated
        on the widget (i.e. thw widget is actually being dragged once it
        exceeds the minimum drag distance).
        """
        pass

    def complete_drag(self):
        """Called by the :class:`DraggableController`, when a drag is completed.
        """
        pass

    def _touch_uid(self):
        return '{}.{}'.format(self.__class__.__name__, self.uid)

    def on_touch_down(self, touch):
        uid = self._touch_uid()
        if uid in touch.ud:
            return touch.ud[uid]

        if super(DraggableObjectBehavior, self).on_touch_down(touch):
            touch.ud[uid] = False
            return True

        x, y = touch.pos
        if not self.collide_point(x, y):
            touch.ud[uid] = False
            return False

        if self._drag_touch or ('button' in touch.profile
                                and touch.button.startswith('scroll')):
            touch.ud[uid] = False
            return False

        self._drag_touch = touch
        touch.grab(self)
        touch.ud[uid] = True

        if not self.drag_controller:
            self.drag_controller = DraggableController()

        return self.drag_controller.drag_down(self, touch)

    def on_touch_move(self, touch):
        uid = self._touch_uid()
        if uid not in touch.ud:
            touch.ud[uid] = False
            return super(DraggableObjectBehavior, self).on_touch_move(touch)

        if not touch.ud[uid]:
            return super(DraggableObjectBehavior, self).on_touch_move(touch)

        if touch.grab_current is not self:
            return False

        if not self.drag_controller:
            self.drag_controller = DraggableController()

        return self.drag_controller.drag_move(self, touch)

    def on_touch_up(self, touch):
        uid = self._touch_uid()
        if uid not in touch.ud:
            touch.ud[uid] = False
            return super(DraggableObjectBehavior, self).on_touch_up(touch)

        if not touch.ud[uid]:
            return super(DraggableObjectBehavior, self).on_touch_up(touch)

        if touch.grab_current is not self:
            return False

        touch.ungrab(self)
        self._drag_touch = None

        if not self.drag_controller:
            self.drag_controller = DraggableController()

        return self.drag_controller.drag_up(self, touch)
Exemplo n.º 16
0
class TemplateEditTree(TreeView):
    "Use in Template Edit Popup to display all possible fields"
    tmplPath = StringProperty(allownone=True)
    current_selection = ObjectProperty()
    values = DictProperty()  #values from template, if any
    tmplDict = DictProperty()  #link a tmplWidget to a node

    def update_tmpl(self, tmpl):
        if self.target:
            self.target.clear_widgets()
            self.target.add_widget(tmpl)
            tmpl.pos = self.target.pos
        self.current_selection = (tmpl, self.selected_node)

    def on_tmplPath(self, instance, value):
        self.current_selection = ()
        self.clear_widgets()
        self.get_root().nodes = list()
        if not value:
            return
        from template import BGTemplate
        #tmplPath is in the form [NAME][@PATH]. If path provided, load all tmpl from there. Without it, take name from library
        name, path = self.tmplPath.split('@')
        if not (name) and not (path):
            Logger.warn(
                'Warning: tmpl Path is empty. stopping template edition')
        if not path:
            from template import templateList
            tmpls = [templateList[name]]
        else:
            tmpls = BGTemplate.FromFile(self.tmplPath, use_cache=True)
        for tmpl in tmpls:
            tmpl.apply_values(self.values)
            node = self.load_tmpl(tmpl)
            self.select_node(node)  # will issue a template update

    def load_tmpl(self, tmpl):
        #Now add on load
        node = self.add_node(
            TreeViewLabel(text=tmpl.template_name,
                          color_selected=(.6, .6, .6, .8)))
        node.is_leaf = False  #add the thingy
        #point to the template
        node.template = tmpl
        self.tmplDict[tmpl] = node
        #Deal with Template Properties:
        for pname, editor in sorted(tmpl.vars.items()):
            self.add_node(TreeViewField(name=pname, editor=editor(tmpl)), node)
        #Deal with KV style elemebts
        for fname in sorted(tmpl.ids.keys()):
            if not isinstance(tmpl.ids[fname], BaseField):
                continue
            _wid = tmpl.ids[fname]
            if not _wid.editable:
                continue
            if _wid.default_attr:
                w = _wid.params[_wid.default_attr](_wid)
                if w is not None:  #None when not editable
                    self.add_node(
                        TreeViewField(pre_label=fname,
                                      name=_wid.default_attr,
                                      editor=w), node)
        self.toggle_node(node)
        return node
Exemplo n.º 17
0
class SelectionTool(BoxLayout):
    library_directory = f"{dirname(__file__)}/Example_Data"
    book_id = StringProperty()
    page = StringProperty()

    def Ss(self):
        timestr = time.strftime("%Y%m%d_%H%M%S")
        self.export_to_png(f"{dirname(__file__)}\word\IMG_" + timestr + ".png")
        # print(timestr)

    def __init__(self):
        super(SelectionTool, self).__init__()
        self.image_pane.bind(on_store_rectangles=self.store_rectangles)
        self.all_rectangles = {}
        self.rectangles_filename = 'rectangles.json'
        self.load_rectangles()

        book_pattern = os.path.join(self.library_directory, '[0-9]' * 4)
        self.book_selector.values = [
            os.path.basename(s) for s in glob.glob(book_pattern)
        ]
        self.book_selector.text = self.book_selector.values[
            0] if self.book_selector.values else 'No Books'

        self.on_book_id()

    def on_book_id(self, inst=None, value=None):
        image_pattern = os.path.join(self.library_directory, self.book_id,
                                     '*.jpg')
        self.page_selector.values = [
            os.path.basename(s)[:-4] for s in glob.glob(image_pattern)
        ]
        self.page_selector.text = self.page_selector.values[
            0] if self.page_selector.values else 'No Images'

        # self.word_list.clear_widgets()
        # with open(os.path.join(self.library_directory, self.book_id, 'word_list.txt')) as fp:
        #     for word in sorted(fp.readlines()):
        #         self.word_list.add_widget(Label(text=word.strip()))
        # self.color_word_list()

        self.on_page()

    def on_page(self, *_):
        page_filename = self.page + '.jpg'
        self.image_pane.source = os.path.join(self.library_directory,
                                              self.book_id, page_filename)
        self.image_pane.clear_rectangles()
        try:
            for rect in self.all_rectangles[self.book_id][self.page]:
                rect.compute_screen_coordinates()
                self.image_pane.add_new_rectangle(rect)
        except KeyError:
            pass

    def store_rectangles(self, sender=None, rectangles=[]):
        if self.book_id not in self.all_rectangles:
            self.all_rectangles[self.book_id] = {}
        self.all_rectangles[self.book_id].update(
            {self.page: [r for r in rectangles]})
        self.color_word_list()
        self.save_rectangles()

    def load_rectangles(self):
        self.all_rectangles = {}
        try:
            with open(self.rectangles_filename) as fd:
                all_rectangles_dict = json.load(fd)
                for book_id, book_rectangles in all_rectangles_dict.items():
                    self.all_rectangles[book_id] = {}
                    for page, rectangles in book_rectangles.items():
                        self.all_rectangles[book_id][page] = \
                            [SelectionBox(image_pane=self.image_pane, **rect) for rect in rectangles]
        except IOError:
            print("Can't find rectangles file!")

    def save_rectangles(self):
        rectangle_dict = {}
        for book_id, book_rectangles in self.all_rectangles.items():
            for page, rectangles in book_rectangles.items():
                page_dict = {page: [rect.to_dict() for rect in rectangles]}
                if rectangles:
                    rectangle_dict[book_id] = rectangle_dict.get(book_id, {})
                    rectangle_dict[book_id].update(page_dict)

        with open(self.rectangles_filename, 'w') as fd:
            json.dump(rectangle_dict,
                      fd,
                      sort_keys=True,
                      indent=4,
                      separators=(',', ': '))

    def color_word_list(self):
        if self.book_id in self.all_rectangles:
            rectangle_labels = [
                rect.label.text
                for page_rects in self.all_rectangles[self.book_id].values()
                for rect in page_rects
            ]
        else:
            rectangle_labels = []

        # for label in self.word_list.children:
        #     label.color = (0, 1, 0, 1) if label.text in rectangle_labels else (1, 1, 1, 1)

    def __init__(self):
        super(SelectionTool, self).__init__()
        self.image_pane.bind(on_store_rectangles=self.store_rectangles)
        self.all_rectangles = {}
        if kivy.platform == 'ios':  # added
            self.rectangles_filename = os.path.join(
                App.get_running_app().user_data_dir, 'rectangles.json')
        else:
            self.rectangles_filename = 'rectangles.json'
        self.load_rectangles()

        book_pattern = os.path.join(self.library_directory, '[0-9]' * 4)
        self.book_selector.values = [
            os.path.basename(s) for s in glob.glob(book_pattern)
        ]
        self.book_selector.text = self.book_selector.values[
            0] if self.book_selector.values else 'No Books'
Exemplo n.º 18
0
class Widget(KivyWidget):
    """MPF-MC Widget class.

    The :class:`Widget` class is the base class required for creating Widgets
    for use in the media controller.  It is based on the Kivy
    kivy.uix.widget.Widget class, but has some custom behavior for use in
    the MC.

    The most important detail is every widget is contained inside another
    specialized widget class (mpfmc.uix.widget.WidgetContainer).  This
    container class is always the parent of a MC Widget and provides the
    coordinate translations to allow MC widgets to use their anchor point
    coordinates instead of the bottom-left corner for all coordinate settings
    (x, y, pos).  The WidgetContainer is automatically created when a widget
    is created and should not be manipulated directly.  It is important to
    remember when walking the widget tree the WidgetContainer is the Widget's
    parent.

    """

    widget_type_name = ''  # Give this a name in your subclass, e.g. 'Image'

    # We loop through the keys in a widget's config dict and check to see if
    # the widget's base class has attributes for them, and if so, we set
    # them. This is how any attribute from the base class can be exposed via
    # our configs. However we use some config keys that Kivy also uses,
    # and we use them for different purposes, so there are some keys that we
    # use that we never want to set on widget base classes.
    _dont_send_to_kivy = ('x', 'y', 'key')

    merge_settings = tuple()

    animation_properties = list()
    """List of properties for this widget that may be animated using widget animations."""

    def __init__(self, mc: "MpfMc", config: Optional[dict]=None,
                 key: Optional[str]=None, **kwargs) -> None:
        del kwargs
        self._container = None
        self.size_hint = (None, None)

        # Needs to be deepcopy since configs can have nested dicts
        self.config = deepcopy(config)

        super().__init__(**self.pass_to_kivy_widget_init())

        self.mc = mc

        # Create a container widget as this widget's parent.  The container will adjust
        # the coordinate system for this widget so that all positional properties are
        # based on the widget's anchor rather than the lower left corner.
        self._container = WidgetContainer(self, z=self.config['z'])
        self._container.add_widget(self)
        self._container.fbind('parent', self.on_container_parent)

        self.animation = None
        self._animation_event_keys = set()
        # MPF event keys for event handlers that have been registered for
        # animation events. Used to remove the handlers when this widget is
        # removed.

        self._pre_animated_settings = dict()
        # dict of original values of settings that were animated so we can
        # restore them later

        self._percent_prop_dicts = dict()

        self._default_style = None
        self._set_default_style()
        self._apply_style()

        if 'color' in self.config and not isinstance(self.config['color'], RGBAColor):
            self.config['color'] = RGBAColor(self.config['color'])

        # Set initial attribute values from config
        for k, v in self.config.items():
            if k not in self._dont_send_to_kivy and hasattr(self, k):
                setattr(self, k, v)

        # Has to be after we set the attributes since it could be in the config
        self.key = key

        # Build animations
        if 'animations' in self.config and self.config['animations']:
            for k, v in self.config['animations'].items():
                if k == 'add_to_slide':
                    # needed because the initial properties of the widget
                    # aren't set yet
                    Clock.schedule_once(self.on_add_to_slide, -1)

                elif k not in magic_events:
                    self._register_animation_events(k)
        else:
            self.config['animations'] = dict()

        # why is this needed? Why is it not config validated by here? todo
        if 'reset_animations_events' in self.config:
            for event in [x for x in self.config['reset_animations_events'] if x not in magic_events]:
                self._animation_event_keys.add(self.mc.events.add_handler(
                    event=event, handler=self.reset_animations))

        # Set widget expiration (if configured)
        self.expire = config.get('expire', None)
        if self.expire:
            self.schedule_removal(self.expire)

    def __repr__(self) -> str:  # pragma: no cover
        return '<{} Widget id={}>'.format(self.widget_type_name, self.id)

    def pass_to_kivy_widget_init(self) -> dict:
        """Initializes the dictionary of settings to pass to Kivy."""
        return dict()

    def merge_asset_config(self, asset) -> None:
        for setting in [x for x in self.merge_settings if (
                        x not in self.config['_default_settings'] and
                        x in asset.config)]:
            self.config[setting] = asset.config[setting]

    def on_anchor_offset_pos(self, instance, pos):
        """Called whenever the anchor_offset_pos property value changes."""
        del instance
        if self.parent:
            self.parent.pos = pos

    def on_container_parent(self, instance, parent):
        del instance
        if parent:
            # some attributes can be expressed in percentages. This dict holds
            # those, key is attribute name, val is max value

            self._percent_prop_dicts = dict(x=parent.width,
                                            y=parent.height,
                                            width=parent.width,
                                            height=parent.height,
                                            opacity=1,
                                            line_height=1)

            self.pos = self.calculate_initial_position(parent.width,
                                                       parent.height,
                                                       self.config['x'],
                                                       self.config['y'])

    # pylint: disable-msg=too-many-arguments
    # pylint: disable-msg=too-many-statements
    @staticmethod
    def calculate_initial_position(parent_w: int, parent_h: int,
                                   x: Optional[Union[int, str]] = None,
                                   y: Optional[Union[int, str]] = None) -> tuple:
        """Returns the initial x,y position for the widget within a larger 
        parent frame based on several positioning parameters. This position will
        be combined with the widget anchor position to determine its actual 
        position on the screen.

        Args:
            parent_w: Width of the parent frame.
            parent_h: Height of the parent frame.
            x: (Optional) Specifies the x (horizontal) position of the widget from
                the left edge of the slide. Can be a numeric value which
                represents the actual x value, or can be a percentage (string with
                percent sign, like '20%') which is set taking into account the size
                of the parent width. (e.g. parent width of 800 with x='20%'
                results in x=160. Can also be negative to position the widget
                partially off the left of the slide. Default value of None will
                return the horizontal center (parent width / 2). Can also start
                with the strings "left", "center", or "right" which can be combined
                with values. (e.g right-2, left+4, center-1)
            y: (Optional) Specifies the y (vertical) position of the widget from
                the bottom edge of the slide. Can be a numeric value which
                represents the actual y value, or can be a percentage (string with
                percent sign, like '20%') which is set taking into account the size
                of the parent height. (e.g. parent height of 600 with y='20%'
                results in y=120. Can also be negative to position the widget
                partially off the bottom of the slide. Default value of None will
                return the vertical center (parent height / 2). Can also start
                with the strings "top", "middle", or "bottom" which can be combined
                with values. (e.g top-2, bottom+4, middle-1)

        Returns: Tuple of x, y coordinates for the lower-left corner of the
            widget you're placing.

        See the widgets documentation for examples.

        """
        # Set defaults
        if x is None:
            x = 'center'
        if y is None:
            y = 'middle'

        # ----------------------
        # X / width / horizontal
        # ----------------------

        # Set position
        if isinstance(x, str):

            x = str(x).replace(' ', '')
            start_x = 0

            if x.startswith('right'):
                x = x.strip('right')
                start_x = parent_w

            elif x.startswith('middle'):
                x = x.strip('middle')
                start_x = parent_w / 2

            elif x.startswith('center'):
                x = x.strip('center')
                start_x = parent_w / 2

            elif x.startswith('left'):
                x = x.strip('left')

            if not x:
                x = '0'

            x = percent_to_float(x, parent_w)
            x += start_x

        # --------------------
        # Y / height / vertical
        # --------------------

        # Set position
        if isinstance(y, str):

            y = str(y).replace(' ', '')
            start_y = 0

            if y.startswith('top'):
                y = y.strip('top')
                start_y = parent_h

            elif y.startswith('middle'):
                y = y.strip('middle')
                start_y = parent_h / 2

            elif y.startswith('center'):
                y = y.strip('center')
                start_y = parent_h / 2

            elif y.startswith('bottom'):
                y = y.strip('bottom')

            if not y:
                y = '0'

            y = percent_to_float(y, parent_h)
            y += start_y

        return x, y

    def _set_default_style(self) -> None:
        """Sets the default widget style name."""
        if ('{}_default'.format(self.widget_type_name.lower()) in
                self.mc.machine_config['widget_styles']):
            self._default_style = self.mc.machine_config['widget_styles'][
                '{}_default'.format(self.widget_type_name.lower())]

    def _apply_style(self, force_default: bool=False) -> None:
        """Apply any style to the widget that is specified in the config."""
        if not self.config['style'] or force_default:
            if self._default_style:
                style = self._default_style
            else:
                return
        else:
            try:
                style = self.mc.machine_config['widget_styles'][self.config['style'].lower()]
            except KeyError:
                raise ValueError("{} has an invalid style name: {}".format(
                    self, self.config['style'].lower()))

        found = False

        try:
            # This looks crazy but it's not too bad... The list comprehension
            # builds a list of attributes (settings) that are in the style
            # definition but that were not manually set in the widget.

            # Then it sets the attributes directly since the config was already
            # processed.
            for attr in [x for x in style if
                         x not in self.config['_default_settings']]:
                self.config[attr] = style[attr]

            found = True

        except (AttributeError, KeyError):
            pass

        if not found and not force_default:
            self._apply_style(force_default=True)

    def prepare_for_removal(self) -> None:
        """Prepare the widget to be removed."""
        self.mc.clock.unschedule(self.remove)
        self._remove_animation_events()

    def schedule_removal(self, secs: float) -> None:
        """Schedule the widget to be removed after the specified number
        of seconds have elapsed."""
        self.mc.clock.schedule_once(self.remove, secs)

    def remove(self, *dt) -> None:
        """Perform the actual removal of the widget."""
        del dt

        try:
            # This widget has a container parent that must be removed
            self._container.parent.remove_widget(self._container)
        except AttributeError:
            pass

        self.on_remove_from_slide()

    def _convert_animation_value_to_float(self, prop: str,
                                          val: Union[str, int, float]) -> Union[float, int]:
        """
        Convert an animation property value to a numeric value.
        Args:
            prop: The name of the property to animate
            val: The animation target value (may be a string that contains a % sign)

        Returns:
            Numeric value (float or int).
        """
        try:
            val = percent_to_float(val, self._percent_prop_dicts[prop])
        except KeyError:
            # because widget properties can include a % sign, they are
            # all strings, so even ones that aren't on the list to look
            # for percent signs have to be converted to numbers.
            if '.' in val:
                val = float(val)
            else:
                val = int(val)

        return val

    def build_animation_from_config(self, config_list: list) -> Animation:
        """Build animation object from config."""
        if not isinstance(config_list, list):
            raise TypeError('build_animation_from_config requires a list')

        # find any named animations and replace them with the real ones
        animation_list = list()

        for entry in config_list:
            if 'named_animation' in entry:
                for named_anim_settings in (
                        self.mc.animations[entry['named_animation']]):
                    animation_list.append(named_anim_settings)
            else:
                animation_list.append(entry)

        repeat = False
        animation_sequence_list = []

        for settings in animation_list:
            prop_dict = dict()
            values_needed = dict()
            values = settings['value'].copy()

            # Some properties that can be animated contain more than single values
            # (such as color). Need to ensure there are the correct number of
            # values for the properties to animate.
            values_needed_total = 0

            for prop in settings['property']:
                if isinstance(getattr(self, prop), list):
                    values_needed[prop] = len(getattr(self, prop))
                    values_needed_total += values_needed[prop]
                else:
                    values_needed[prop] = 1
                    values_needed_total += 1

            if len(settings['value']) != values_needed_total:
                self.mc.log.warning("There is a mismatch between the number of values "
                                    "available and the number of values required to animate "
                                    "the following properties in the %s widget: %s "
                                    "(animation will be ignored).",
                                    self.widget_type_name, settings['property'])
                continue

            # Create a dictionary of properties to animate along with their target values
            for prop in settings['property']:

                # Make sure target widget property can be animated
                if prop not in self.animation_properties:
                    self.mc.log.warning("%s widgets do not support animation "
                                        "for the %s property (animation will be ignored)",
                                        self.widget_type_name, prop)
                    continue

                # Convert target value(s) to numeric types
                if values_needed[prop] > 1:
                    val = [self._convert_animation_value_to_float(prop, x)
                           for x in values[:values_needed[prop]]]
                    del values[:values_needed[prop]]
                else:
                    val = self._convert_animation_value_to_float(prop, values[0])
                    del values[0]

                prop_dict[prop] = val

                # Save the pre-animated property value so it can later be restored
                if prop not in self._pre_animated_settings:
                    self._pre_animated_settings[prop] = getattr(self, prop)

            # TODO: Support custom easing functions
            # This can be done by replacing transition string with a function reference
            # when the string does not exist in the Kivy AnimationTransition class as
            # a method.

            # Create the animation object
            if settings['relative']:
                animation = RelativeAnimation(duration=settings['duration'],
                                              transition=settings['easing'],
                                              **prop_dict)
            else:
                animation = Animation(duration=settings['duration'],
                                      transition=settings['easing'],
                                      **prop_dict)

            # Determine if this animation should be performed in sequence or in parallel
            # with the previous animation.
            if settings['timing'] == 'with_previous' and animation_sequence_list:
                # Combine in parallel with previous animation
                animation_sequence_list[-1] &= animation
            else:
                # Add new sequential animation to the list
                animation_sequence_list.append(animation)

            if settings['repeat']:
                repeat = True

        # Combine all animations that should be performed in sequence into a single
        # animation object (add them all together)
        final_animation = reduce(lambda x, y: x + y, animation_sequence_list)

        if repeat:
            final_animation.repeat = True

        return final_animation

    def stop_animation(self) -> None:
        """Stop the current widget animation."""
        try:
            self.animation.stop(self)
        except AttributeError:
            pass

    def reset_animations(self, **kwargs) -> None:
        """Reset the widget properties back to their pre-animated values."""
        del kwargs
        for k, v in self._pre_animated_settings.items():
            setattr(self, k, v)

    def _register_animation_events(self, event_name: str) -> None:
        """Register handlers for the various events that trigger animation actions."""
        self._animation_event_keys.add(self.mc.events.add_handler(
            event=event_name, handler=self.start_animation_from_event,
            event_name=event_name))

    def start_animation_from_event(self, event_name: str, **kwargs) -> None:
        """Starts an animation based on an event name that has previously
        been registered."""
        del kwargs

        if event_name not in self.config['animations']:
            return

        self.stop_animation()
        self.animation = self.build_animation_from_config(
            self.config['animations'][event_name])
        self.animation.start(self)

    def _remove_animation_events(self) -> None:
        """Remove previously registered handlers for the various events that 
        trigger animation actions."""
        self.mc.events.remove_handlers_by_keys(self._animation_event_keys)
        self._animation_event_keys = set()

    def on_add_to_slide(self, dt) -> None:
        """Automatically called when this widget is added to a slide.

        If you subclass this method, be sure to call super(), as it's needed
        for widget animations.
        """
        del dt

        if 'add_to_slide' in self.config['reset_animations_events']:
            self.reset_animations()

        self.start_animation_from_event('add_to_slide')

    def on_remove_from_slide(self) -> None:
        """Automatically called when this widget is removed from a slide.

        If you subclass this method, be sure to call super(), as it's needed
        for widget animations.
        """
        if 'remove_from_slide' in self.config['reset_animations_events']:
            self.reset_animations()

    def on_pre_show_slide(self) -> None:
        """Automatically called when the slide this widget is part of is about
        to be shown. If there's an entrance transition, this method is called
        before the transition starts.

        If you subclass this method, be sure to call super(), as it's needed
        for widget animations.
        """
        if 'pre_show_slide' in self.config['reset_animations_events']:
            self.reset_animations()

        if 'pre_show_slide' in self.config['animations']:
            self.start_animation_from_event('pre_show_slide')

    def on_show_slide(self) -> None:
        """Automatically called when the slide this widget is part of has been
        shown. If there's an entrance transition, this method is called
        after the transition is complete.

        If you subclass this method, be sure to call super(), as it's needed
        for widget animations.
        """
        if 'show_slide' in self.config['reset_animations_events']:
            self.reset_animations()

        if 'show_slide' in self.config['animations']:
            self.start_animation_from_event('show_slide')

    def on_pre_slide_leave(self) -> None:
        """Automatically called when the slide this widget is part of is about
        to leave (e.g. when another slide is going to replace it). If
        there's an exit transition, this method is called before the
        transition starts.

        If you subclass this method, be sure to call super(), as it's needed
        for widget animations.
        """
        if 'pre_slide_leave' in self.config['reset_animations_events']:
            self.reset_animations()

        if 'pre_slide_leave' in self.config['animations']:
            self.start_animation_from_event('pre_slide_leave')

    def on_slide_leave(self) -> None:
        """Automatically called when the slide this widget is part of is about
        to leave (e.g. when another slide is going to replace it). If there's
        an exit transition, this method is called after the transition is
        complete.

        If you subclass this method, be sure to call super(), as it's needed
        for widget animations.
        """
        if 'slide_leave' in self.config['reset_animations_events']:
            self.reset_animations()

        if 'slide_leave' in self.config['animations']:
            self.start_animation_from_event('slide_leave')

    def on_slide_play(self) -> None:
        """Automatically called when the slide this widget is part of is played
        as part of a slide_player play command (either via a standalone slide
        player or as a show step).

        If you subclass this method, be sure to call super(), as it's needed
        for widget animations.
        """
        if 'slide_play' in self.config['reset_animations_events']:
            self.reset_animations()

        if 'slide_play' in self.config['animations']:
            self.start_animation_from_event('slide_play')

    def find_widgets_by_key(self, key: str) -> List["KivyWidget"]:
        """Return a list of widgets with the matching key value by searching
        the tree of children belonging to this widget."""
        return [x for x in self.walk(restrict=True, loopback=False) if hasattr(x, 'key') and x.key == key]

    #
    # Properties
    #

    def _get_container(self) -> KivyWidget:
        return self._container

    container = AliasProperty(_get_container, None)
    '''The widget container is a special container/parent widget that manages this widget.
    It has no graphical representation.'''

    key = StringProperty(None, allownone=True)
    '''Widget keys are used to uniquely identify instances of widgets which you can later 
    use to update or remove the widget.
    '''

    color = ListProperty([1.0, 1.0, 1.0, 1.0])
    '''The color of the widget, in the (r, g, b, a) format.

    :attr:`color` is a :class:`~kivy.properties.ListProperty` and
    defaults to [1.0, 1.0, 1.0, 1.0].
    '''

    anchor_x = StringProperty(None, allownone=True)
    '''Which edge of the widget will be used for positioning. ('left', 'center' 
    (or 'middle'), or 'right'. If None, 'center' will be used.
    '''

    anchor_y = StringProperty(None, allownone=True)
    '''Which edge of the widget will be used for positioning. ('top', 'middle' 
    (or 'center'), or 'bottom'. If None, 'center' will be used.
    '''

    anchor_pos = ReferenceListProperty(anchor_x, anchor_y)
    '''Which point of the widget will be used for positioning.

    :attr:`anchor_pos` is a :class:`~kivy.properties.ReferenceListProperty`
    of (:attr:`anchor_x`, :attr:`anchor_y`) properties.
    '''

    adjust_top = NumericProperty(0)
    '''Moves the "top" of this widget's anchor position down, meaning any
    positioning that includes calculations involving the top (anchor_y of 'top' 
    or 'middle') use the alternate top position. Positive values move the top 
    towards the center of the widget, negative values move it away. Negative 
    values can be used to give the widget "space" on the top, and positive 
    values can be used to remove unwanted space from the top of the widget. 
    Note that this setting does not actually crop or cut off the top of the 
    widget, rather, it just adjusts how the positioning is calculated.
    '''

    adjust_right = NumericProperty(0)
    '''Adjusts the anchor position calculations for the right side of the widget. 
    Positive values move the right position  towards the center, negative values 
    move it away from the center.
    '''

    adjust_bottom = NumericProperty(0)
    '''Adjusts the anchor position calculations for the bottom of the widget. 
    Positive values move the bottom position towards the center, negative values 
    move it away from the center.
    '''

    adjust_left = NumericProperty(0)
    '''Adjusts the anchor position calculations for the left side of the widget. 
    Positive values move the left position towards the center, negative values 
    move it away from the center.
    '''

    def _get_anchor_offset_pos(self):
        """Calculates the anchor offset position relative to the lower-left corner 
        of a widget based on several positioning parameters.
        """
        # Set defaults
        offset_x = 0
        offset_y = 0
        anchor_x = self.anchor_x
        anchor_y = self.anchor_y
        if not anchor_x:
            anchor_x = 'center'
        if not self.anchor_y:
            anchor_y = 'middle'

        # ----------------------
        # X / width / horizontal
        # ----------------------

        # Adjust for anchor_x & adjust_right/left
        if anchor_x in ('center', 'middle'):
            offset_x -= (self.width - self.adjust_right + self.adjust_left) / 2
        elif anchor_x == 'right':
            offset_x -= self.width - self.adjust_right
        else:  # left
            offset_x -= self.adjust_left

        # --------------------
        # Y / height / vertical
        # --------------------

        # Adjust for anchor_y & adjust_top/bottom
        if anchor_y in ('middle', 'center'):
            offset_y -= (self.height - self.adjust_top + self.adjust_bottom) / 2
        elif anchor_y == 'top':
            offset_y -= self.height - self.adjust_top
        else:  # bottom
            offset_y -= self.adjust_bottom

        return offset_x, offset_y

    anchor_offset_pos = AliasProperty(_get_anchor_offset_pos, None,
                                      bind=('size', 'anchor_x', 'anchor_y', 'adjust_top',
                                            'adjust_right', 'adjust_bottom', 'adjust_left'),
                                      cache=True)
    '''The anchor position of the widget (relative to the widget's lower left corner).
Exemplo n.º 19
0
class ListBSIconLeft(ILeftBody, MDLabel):
    icon = StringProperty()
Exemplo n.º 20
0
class RecycleDataBox(BoxLayout):
    data = ListProperty()
    '''The data used by DataBox. This is a list of dicts whose
    keys map to the corresponding property names of the viewclass'''

    viewclass = StringProperty()
    '''The viewclass that will be generated from each data dict'''

    use_recycling = False

    scroller = ObjectProperty()

    viewclass_class = None

    last_height = NumericProperty()
    default_height = NumericProperty(100)  # default hight of viewclasses
    max_viewclasses = 0  # scroller.height / default_height + margin
    indexed_widgets = False
    can_scroll = 1
    last_scroll = 0.0
    scheduled_scroll = False

    def __init__(self, data=None, **kwargs):
        super(RecycleDataBox, self).__init__(**kwargs)
        self._topwidget = ShellWidget(place='top')
        self._botwidget = ShellWidget(place='bot')
        self.view = BoxLayout(size_hint_y=None, orientation='vertical')
        self.view.bind(minimum_size=self.view.setter('size'))
        self.height_cache = {}
        for widget in self._topwidget, self.view, self._botwidget:
            self.add_widget(widget)

    def on_height(self, _, value):
        self.last_height = value

    def on_scroller(self, _, scroller):
        if scroller:
            if not self.view.children and self.data:
                self.on_data(None, self.data)
            scroller.bind(height=self.on_scroller_height)
            scroller.bind(scroll_y=self.on_scrolling)
            self.on_scroller_height(scroller, scroller.height)

    def on_scroller_height(self, scroller, value):
        self.max_viewclasses = int((value * 2) / self.default_height)

    def fin2(self, *args):
        if not self.scheduled_scroll:
            self.can_scroll = 1
            Clock.schedule_once(
                lambda *a: self.on_scrolling(None, -1, sc=True), 0.1)
            self.scheduled_scroll = True

    @mainthread
    def unlock_scroll(self):
        self.can_scroll = 1

    @mainthread
    def on_scrolling(self,
                     scroller,
                     scroll_y,
                     force=False,
                     move_one=False,
                     sc=False):
        if self.scheduled_scroll:
            self.scheduled_scroll = False

        if not self.use_recycling:
            return

        if not self.can_scroll and not force:
            return self.unlock_scroll()

        winheight = Window.system_size[1]
        maxpos, minpos = winheight * 1.2, winheight * -0.2

        center = self.view.children[int(len(self.view.children) / 2)]
        center_pos = center.to_window(*center.pos)[1]
        top = self.view.children[-1]
        top_pos = top.to_window(0, top.top)[1]
        bot = self.view.children[0]
        bot_pos = bot.to_window(0, bot.y)[1]

        if not move_one:

            if top_pos < minpos - winheight:
                self._topwidget.swap_height(top_pos - winheight)
            elif bot_pos > maxpos + winheight:
                self._botwidget.swap_height(top_pos - winheight)
            else:
                move_one = True

        if move_one:
            cnt = 0
            if top_pos <= maxpos:
                self._topwidget.swap_one()
                sc = False

            elif bot_pos >= minpos:
                self._botwidget.swap_one()
                sc = False

        self.can_scroll = 0
        self.unlock_scroll()
        if not sc:
            Clock.schedule_once(self.fin2, 0)

    def refresh_indexes(self, data):

        if not self._topwidget.fake_children:
            start_index = 0
        else:
            start_index = self._topwidget.highest_index + 1

        for i, child in enumerate(reversed(self.view.children)):
            i += start_index
            child.refresh_view_attrs(self, i, data[i])

    def move_view_index(self, index):
        for i, child in enumerate(reversed(self.view.children)):
            new_index = i + index
            child.refresh_view_attrs(self, new_index, self.data[new_index])

    def move_view_top(self):
        top = self.view.children[-1]
        bot = self.view.children[0]
        old_index = bot.index
        new_index = top.index - 1
        self.height_cache[bot.index] = bot.height
        self.view.remove_widget(bot)
        self.view.add_widget(bot, index=len(self.view.children))
        bot.refresh_view_attrs(self, new_index, self.data[new_index])
        return new_index, old_index

    def move_view_bot(self):
        top = self.view.children[-1]
        bot = self.view.children[0]
        old_index = top.index
        new_index = bot.index + 1
        self.height_cache[top.index] = top.height
        self.view.remove_widget(top)
        self.view.add_widget(top)
        top.refresh_view_attrs(self, new_index, self.data[new_index])
        return new_index, old_index

    def on_data(self, _, data):
        if self.viewclass_class and self.scroller and self.max_viewclasses:
            data_count = len(data)

            # Use normal behavior when data is short
            if data_count <= self.max_viewclasses:
                self._botwidget.height = 0
                self._topwidget.height = 0
                self.use_recycling = False
                return self.on_data_no_recycle(None, data)

            if not self.use_recycling:
                self.use_recycling = True
                for i in range(self.max_viewclasses):
                    instance = self.viewclass_class()
                    self.view.add_widget(instance)

            children_count = self.get_children_count()

            if children_count != data_count:
                # Add
                if children_count < data_count:
                    for count in range(data_count - children_count):
                        self._botwidget.add_widget()
                # Remove
                else:
                    counter = 0
                    while counter < children_count - data_count:
                        if self._botwidget.fake_children:
                            self._botwidget.remove_widget()
                        else:
                            self._topwidget.remove_widget()
                        counter += 1

            self.refresh_indexes(data)

    def on_data_no_recycle(self, _, data):
        if len(self.view.children) > self.max_viewclasses:
            self.clear_widgets()

        if self.viewclass_class:
            children_count = len(self.view.children)
            data_count = len(data)

            if children_count != data_count:
                if children_count < data_count:
                    for count in range(data_count - children_count):
                        self.view.add_widget(self.viewclass_class())
                else:
                    for count in range(children_count - data_count):
                        self.view.remove_widget(self.view.children[-1])

            if self.view.children:
                for i, child in enumerate(reversed(self.view.children)):
                    child.refresh_view_attrs(self, i, data[i])

    def get_view_count(self):
        return len(self.view.children)

    def get_children_count(self):
        return (len(self._topwidget.fake_children) + len(self.view.children) +
                len(self._botwidget.fake_children))

    def refresh_from_data(self, *args):
        self.on_data(None, self.data)

    def on_viewclass(self, instance, value):
        if isinstance(value, string_types):
            self.viewclass_class = getattr(Factory, value)
            self.on_data(None, self.data)
        else:
            Logger.error("{}: on_viewclass: {} is not an instance".format(
                self, e))
Exemplo n.º 21
0
class GridBSItem(ButtonBehavior, BoxLayout):
    source = StringProperty()

    caption = StringProperty()
Exemplo n.º 22
0
class SyncScreen(Screen):
    loggedInUser = StringProperty('')
    localPasswordList = ListProperty([])
    remotePasswordList = ListProperty([])

    def syncWithRemote(self):
        print "sync with remote"
        commandData = json.dumps({"action": "SYNC", "subaction": "PULL"})
        recvJsonData = self.parent.clientConnection.send_receive(commandData)

        self.parent.cursor.execute("delete from passwords where username='******'")
        pushPasswordData = recvJsonData['additional']['passwords']

        for password in pushPasswordData:
            self.parent.cursor.execute(
                "insert into passwords (username, account, password) values ('"
                + self.loggedInUser + "', '" + password['account'] + "', '" +
                password['password'] + "')")

        self.parent.db.commit()

        self.loadPasswordList_UI()

    def syncWithLocal(self):
        print "sync with local"
        commandData = json.dumps({
            "action": "SYNC",
            "subaction": "PUSH",
            "passwords": str(self.localPasswordList)
        })
        recvJsonData = self.parent.clientConnection.send_receive(commandData)

        self.loadPasswordList_UI()

    def onPasswordButtonClick(self, instance):

        for button in self.ids.local_password_list.children:
            button.background_color = (1, 1, 1, 1)
        for button in self.ids.remote_password_list.children:
            button.background_color = (1, 1, 1, 1)

        instance.background_color = (0.8, 0.8, 0.8, 1)

        self.currentAccount_pw = str(instance.pw_account)
        self.currentId_pw = str(instance.pw_id)
        self.currentPassword_pw = str(instance.pw_password)

        self.ids.password_info.text = "Account: {0}\nPassword: {1}".format(
            self.currentAccount_pw, self.currentPassword_pw)
        self.ids.password_location.text = "Location: {0}".format(
            instance.pw_location)

    def readPasswords(self):
        commandData = json.dumps({"action": "CRUD", "subaction": "READ"})
        recvJsonData = self.parent.clientConnection.send_receive(commandData)
        self.remotePasswordList = recvJsonData['additional']['passwords']

        passwordList = PasswordRead(self.parent.db, self.loggedInUser)
        self.localPasswordList = passwordList

    def loadPasswordList_UI(self):
        self.readPasswords()
        self.ids.remote_password_list.clear_widgets()
        self.ids.local_password_list.clear_widgets()

        self.ids.remote_password_list.add_widget(
            Label(text="Remote Password List"))
        self.ids.local_password_list.add_widget(
            Label(text="Local Password List"))
        cipher = AESCipher("nv93h50sk1zh508v")
        for entry in self.localPasswordList:

            passwordBtn = PasswordButton(text=entry['account'],
                                         background_color=(0.93, 0.93, 0.93,
                                                           1))

            passwordBtn.pw_username = entry['username']
            passwordBtn.pw_account = entry['account']
            try:
                passwordBtn.pw_password = cipher.decrypt(entry['password'])
            except Exception, e:
                print e
                passwordBtn.pw_password = "******"
            passwordBtn.pw_id = entry['id']
            passwordBtn.pw_id = entry['id']

            passwordBtn.pw_location = "Local"

            passwordBtn.bind(on_release=self.onPasswordButtonClick)

            self.ids.local_password_list.add_widget(passwordBtn)

        for entry in self.remotePasswordList:

            passwordBtn = PasswordButton(text=entry['account'],
                                         background_color=(0.93, 0.93, 0.93,
                                                           1))

            passwordBtn.pw_username = entry['username']
            passwordBtn.pw_account = entry['account']
            try:
                passwordBtn.pw_password = cipher.decrypt(entry['password'])
            except Exception, e:
                print e
                passwordBtn.pw_password = "******"
            passwordBtn.pw_id = entry['id']
            passwordBtn.pw_id = entry['id']

            passwordBtn.pw_location = "Remote"

            passwordBtn.bind(on_release=self.onPasswordButtonClick)

            self.ids.remote_password_list.add_widget(passwordBtn)
Exemplo n.º 23
0
class FullCodeInput(GridLayout):

    _do_cursor_scroll = BooleanProperty(True)

    code_input = ObjectProperty(None)

    tab = ObjectProperty(None)

    tab_type = StringProperty('code')

    filename = StringProperty('')
    ''' name current file in the input 
            :data:`filename` is a :class:`~kivy.properties.StringProperty`
    and defaults to ''
    '''

    saved = BooleanProperty(True)
    '''Indicates if the current file is saved or not
        :data:`saved` is a :class:`~kivy.properties.BooleanProperty`
    and defaults to True
    '''
    def __init__(self, **kwargs):
        super(FullCodeInput, self).__init__(**kwargs)
        Clock.schedule_once(self.first_number)
        self._former_line_lenght = 1
        self.first_time = True

    def first_number(self, dt):
        label = Numbers_(height=dp(self.ids.code_input.line_height),
                         text=str(1))
        Clock.schedule_once(lambda dt: setattr(label, 'state', 'down'))
        label.fbind('on_press', self._number_pressed)
        self.ids.numbering.add_widget(label)
        self._former_line_lenght = 1

    def _number_pressed(self, lb):
        self.ids.code_input.cursor = (self.ids.code_input.cursor[0],
                                      int(lb.text) - 1)

    def change_scroll_y(self, txt, scroll):
        if self._do_cursor_scroll:

            lines_lenght = len(txt._lines)
            line_pos = txt.cursor_row + 1

            norm_y = float(line_pos) / lines_lenght
            scroll.scroll_y = abs(norm_y - 1)
            if line_pos == 1:
                scroll.scroll_y = 1

        # scroll scroll numbers
        line_num = txt.cursor_row + 1
        children = self.ids.numbering.children[::-1]
        if children:
            child = children[line_num - 1]
            self.ids.number_scroll.scroll_to(child, dp(5))

            Clock.schedule_once(lambda dt: setattr(child, 'state', 'down'))

            def toggle(chd):
                if chd != child:
                    chd.state = 'normal'

            map(lambda child: toggle,
                ToggleButtonBehavior.get_widgets(child.group))

    def do_bar_scroll(self, txt, scroll):
        lines_lenght = len(txt._lines)
        line_pos = int(abs(scroll.scroll_y - 1) * lines_lenght)
        cursor_y = abs(line_pos)

        txt.cursor = (txt.cursor_col, cursor_y)

    def number_me(self, txt, scroll):
        lines_lenght = len(txt._lines)
        line_pos = txt.cursor_row + 2

        if not (lines_lenght <= 1) or not (self.first_time):

            if lines_lenght >= self._former_line_lenght:

                if line_pos == lines_lenght:
                    self.do_new_line(txt, scroll)
                else:
                    self.do_new_line(txt, scroll)

            else:
                self.remove_line_numbering(txt, scroll)

            self._former_line_lenght = lines_lenght
        self.first_time = False

    def do_new_line(self, txt, scroll):
        lines_lenght = len(txt._lines)

        for line_num in range(self._former_line_lenght + 1, lines_lenght + 1):

            label = Numbers_(height=dp(self.ids.code_input.line_height),
                             text=str(line_num))
            label.fbind('on_press', self._number_pressed)
            self.ids.numbering.add_widget(label)

    def remove_line_numbering(self, txt, scroll):
        lines_lenght = len(txt._lines)

        for line_num in range(lines_lenght + 1, self._former_line_lenght + 1):
            child = self.ids.numbering.children[0]
            self.ids.numbering.remove_widget(child)
Exemplo n.º 24
0
class PointRenderer(ScaledValues, SecondaryDataSource):
    color = ListProperty([1.] * 4)
    point_size = NumericProperty(10.0)
    shader = StringProperty('')
    #shader = OptionProperty('NO SECONDARY',['NO EFFECT'])

    def __init__(self, *args, **kwargs):
        glEnable(0x8642)  # equivalend to glEnable(GL_VERTEX_PROGRAM_POINT_SIZE)
        self.shader_fn = 'point_renderer.glsl'
        super(PointRenderer, self).__init__(**kwargs)        
        self.loadShaders()

        self.mesh = Mesh(mode='points', fmt=[(b'v_pos', 2, 'float'),
                                             (b'parm', 1, 'float')])  # ,pointsize=1000)
        self.render_context.add(self.mesh)

        self.updateModelViewMatrix()

    def registerConfigurableProperties(self):
        super(PointRenderer, self).registerConfigurableProperties()
        self.addConfigurableProperty(PointRenderer.point_size)

    def update(self):
        if self.enabled:
            pos_data = np.array(self.a, dtype=np.float32)
            pos_data = self.apply_preprocessing(pos_data)
            N = np.size(pos_data) / 2
            pos_data = pos_data.reshape(N, 2)

            if self.secondary_varname != '':
                parm_data = np.array(self.b, dtype=np.float32).reshape(N, 1)
                parm_data = self.apply_secondary_preprocessing(parm_data)
            else:
                parm_data = np.ones(N, dtype=np.float32).reshape(N, 1)
            data = np.hstack([pos_data, parm_data])
            if N > 0:
                self.mesh.indices = np.arange(N)  # ,dtype=np.float32)
                self.mesh.vertices = data.ravel()  # should be ravel for efficiency?
                self.render_context.ask_update()

    def setTarget(self):
        if self.target_object is not None and self.target_varname != '':
            s = 'self.a = self.target_object.%s' % (self.target_varname)
            exec(s)
            # if isinstance(self.a,np.ndarray) and len(np.shape(self.a))==2 :
            #     pass
            #     # self.colorfmt = ['ZERO_DEPTH_ARRAY?','luminance',
            #     #                  'luminance_alpha','rgb','rgba'][self.depth]
            # else :
            #     print(np.shape(self.a),self.target_object,self.target_varname)
            #     raise TypeError('Target of Array Renderer must be a 2D numpy.ndarray')

    def inspect(self):
        inspection_dump_file = self.createInspectionDumpFile()
        np.save(open(inspection_dump_file, 'wb'), self.a)
        self.launchInspector(inspection_dump_file)

    def loadShaders(self):
        self.shaders = loadShaders(self.shader_fn,
                                   {'point_size': 0.025 * self.point_size *
                                    min(Window.width, Window.height)})
        self.render_context.shader.vs = self.shaders['vs']
        self.render_context.shader.fs = self.shaders['fs']

    def on_point_size(self, obj, value):
        self.loadShaders()

    def on_size(self, inst, value):
        super(PointRenderer, self).on_size(inst, value)
        self.loadShaders()

    def on_pos(self, inst, value):
        super(PointRenderer, self).on_pos(inst, value)
        self.loadShaders()

    def on_color(self, obj, value):
        print('on_color')
        self.render_context['color'] = [float(v) for v in self.color]

    def on_shader(self, obj, glsl_fn):
        self.shader_fn = glsl_fn
        self.loadShaders()
Exemplo n.º 25
0
class InnerCodeInput(HoverBehavior, CodeExtraBehavior, CodeInput):

    path = StringProperty('')
    '''Path of the current file
        `path` is a :class:`~kivy.properties.StringProperty`
    and defaults to ''
    '''

    rightclick_dropdown = None
    ''' drop down menu that appears when the right click button is clicked
        `rightclick_dropdown` is an instance of .code_find.CodeInputFind
        '''
    code_finder = None
    '''  '''
    def __init__(self, **kwargs):
        super(InnerCodeInput, self).__init__(**kwargs)
        self.style_name = 'native'
        self.background_normal = ''
        self.background_active = ''

    def on_style_name(self, *args):
        self.style = NativeTweakStyle
        self.background_color = get_color_from_hex(self.style.background_color)
        self._trigger_refresh_text()

    def on_text(self, *args):
        if self.focus:
            self.parent.saved = False
            self.check_settings()

    def check_settings(self):
        from kivystudio.settings import settings_obj
        auto_save = settings_obj.auto_save
        auto_emulate = settings_obj.auto_emulate
        if auto_save:
            self.parent.parent.save_file(auto_save=True)
        if auto_emulate:
            from kivystudio.parser import emulate_file
            from kivystudio.components.emulator_area import get_emulator_area
            if get_emulator_area().emulation_file == self.parent.filename:
                emulate_file(self.parent.filename)

    def keyboard_on_textinput(self, window, text):
        'overiding the default textinput keyboard listener '
        if (text == '=' or text == '-') and 'ctrl' in Window.modifiers:
            return True
        super(InnerCodeInput, self).keyboard_on_textinput(window, text)

    def keyboard_on_key_down(self, keyboard, keycode, text, modifiers):
        'overiding the default keyboard listener '
        # print(keycode, modifiers)

        if keycode[
                1] == 'tab' and 'shift' in modifiers:  # unindentation [Shit-tab]
            self._do_reverse_indentation()

        elif keycode[
                1] == 'tab' and self.selection_text:  # multiple indentation [Tab]
            self.do_multiline_indent()

        elif keycode[
                1] == 'backspace' and 'ctrl' in modifiers:  # delete word left  [Ctrl-bsc]
            self.delete_word_left()

        elif keycode[1] == '/' and 'ctrl' in modifiers:  # a comment ctrl /
            self.do_comment()

        elif keycode[1] == 'enter':
            Clock.schedule_once(lambda dt: self.do_auto_indent())
            return super(CodeInput,
                         self).keyboard_on_key_down(keyboard, keycode, text,
                                                    modifiers)

        elif keycode[
                1] == 'f' and 'ctrl' in modifiers:  # ctrl f to open a search
            self.open_code_finder()

        elif keycode[0] == 27:  # on escape, do nothing
            return True

        else:
            # then return super for others
            return super(CodeInput,
                         self).keyboard_on_key_down(keyboard, keycode, text,
                                                    modifiers)

    def open_code_finder(self):
        ''' open the search finder 
            on the codeinput '''
        code_finder = InnerCodeInput.code_finder
        if code_finder:
            code_finder.open(self)
        else:
            InnerCodeInput.code_finder = CodeInputFind()
            InnerCodeInput.code_finder.open(self)

    def open_rightclick_dropdown(self):
        ''' open the dropdown right click 
            on the codeinput '''
        rightclick_dropdown = InnerCodeInput.rightclick_dropdown
        if rightclick_dropdown:
            rightclick_dropdown.open()
        else:
            InnerCodeInput.rightclick_dropdown = CodeInputDropDown()
            InnerCodeInput.rightclick_dropdown.open(self)

    def on_hover(self, *a):
        ''' changing the mouse cursor 
            on code input'''
        if self.hover:
            Window.set_system_cursor('ibeam')  # set cursor to ibeam
        else:
            Window.set_system_cursor('arrow')  # set cursor to arrow

    def on_touch_down(self, touch):
        if self.collide_point(*touch.pos):
            if touch.button == 'right':
                self.open_rightclick_dropdown()
                FocusBehavior.ignored_touch.append(touch)
                return True

        if touch.button == 'left':
            return super(InnerCodeInput, self).on_touch_down(touch)
Exemplo n.º 26
0
class FilmThicknessScreen(Screen):
    """properties bound to dropdown selection"""
    solvs = StringProperty('')
    solts = StringProperty('')
    """properties bound to inputs"""
    vol = StringProperty('')
    conc = StringProperty('')
    sden = StringProperty('')
    mden = StringProperty('')
    area = StringProperty('')
    """properties bound to output"""
    thickness = StringProperty('')

    def calculate(self):
        if self.verify() == True:
            conc = float(self.conc)
            vol = float(self.vol)
            sden = float(self.sden)
            mden = float(self.mden)
            area = float(self.area)
            """computing the mass in on ml of solution"""
            a = (1 - conc) / (conc * sden)
            b = 1 / (mden)
            mass = vol * (a + b)**(-1)
            """finding the volume of the film"""
            film_vol = mass / mden
            """using area guess to estimate film thickness"""
            self.thickness = str(round(film_vol / area * 10000,
                                       4)) + " Microns"
        else:
            _, message = self.verify()
            self.thickness = self.error_message(message)

    def verify(self, check_density=False):

        message = []
        """checking concentration field"""
        if self.conc == '':
            message.append("Concentration field is empty\n")
        else:
            try:
                if float(self.conc) > 1:
                    message.append("Concentration must be less than 1\n")
            except:
                message.append("Concentration must be greater than 0\n")
        """checking volume field"""
        if self.vol == '':
            message.append("Volume field is empty\n")
        else:
            try:
                if float(self.vol) < 0:
                    message.append("Volume must be greater than 0\n")
            except:
                pass
        """checking density fields"""
        if (self.sden == ''):
            message.append("Solvent density field is empty\n")
        if (self.mden == ''):
            message.append("Material density field is empty\n")
        """Here we return messages only if False, for use with error_message()"""
        if message == []:
            return True
        else:
            return False, message

    def error_message(self, messages):
        err = 'Error:\n'
        for message in messages:
            err += message
        return err
Exemplo n.º 27
0
class CupertinoAlertDialog(ModalView):
    """
    iOS style Alert Dialog

    .. image:: ../_static/alert_dialog/demo.gif
    """

    title = StringProperty(' ')
    """
    Text of title of :class:`CupertinoAlertDialog`
    
    .. image:: ../_static/alert_dialog/title.png
    
    **Python**
    
    .. code-block:: python
    
       CupertinoAlertDialog(title='Hello World')
   
    **KV**
    
    .. code-block::
    
       CupertinoAlertDialog:
           title: 'Hello World'
    """

    content = StringProperty(' ')
    """
    Text of content of :class:`CupertinoAlertDialog`
    
    .. image:: ../_static/alert_dialog/content.png
    
    **Python**
    
    .. code-block:: python
    
       CupertinoAlertDialog(content='Hello World')
   
    **KV**
    
    .. code-block::
    
       CupertinoAlertDialog:
           content: 'Hello World'
    """

    color = ColorProperty([1, 1, 1, 0.95])
    """
    Background color of :class:`CupertinoAlertDialog`
    
    .. image:: ../_static/alert_dialog/color.png
    
    **Python**
    
    .. code-block:: python
    
       CupertinoAlertDialog(color=(1, 0, 0, 1))
   
    **KV**
    
    .. code-block::
    
       CupertinoAlertDialog:
           color: 1, 0, 0, 1
    """

    color_down = ColorProperty([0.9, 0.9, 0.9, 1])
    """
    Background color of action of :class:`CupertinoAlertDialog` when pressed
    
    .. image:: ../_static/alert_dialog/color_down.gif
    
    **Python**
    
    .. code-block:: python
    
       CupertinoAlertDialog(color_down=(0.5, 0, 0, 1))
   
    **KV**
    
    .. code-block::
    
       CupertinoAlertDialog:
           color_down: 0.5, 0, 0, 1
    """

    curve = NumericProperty(15)
    """
    Curve of  :class:`CupertinoAlertDialog`
    
    .. image:: ../_static/alert_dialog/curve.png
    
    **Python**
    
    .. code-block:: python
    
       CupertinoAlertDialog(curve=20)
   
    **KV**
    
    .. code-block::
    
       CupertinoAlertDialog:
           curve: 20
    """
    def add_action(self, text, action):
        """
        Add an action to :class:`CupertinoAlertDialog`

        :param text: Text of action. ``markup`` is enabled
        :param action: Callback to be performed, bound to ``on_release`` of action
        """

        self.actions.add_widget(
            _CupertinoDialogButton(text=text,
                                   color_normal=self.color,
                                   color_down=self.color_down,
                                   on_release=action))

        if len(self.actions.children) > 2 or len(sub(r'\[.*?\]', '',
                                                     text)) > 10:
            self.actions.orientation = 'vertical'

        if self.actions.orientation == 'vertical':
            self.size_hint_y += 0.05
            self.actions.size_hint_y += 0.05

            self.actions.children[0].radii[-2:] = [self.curve, self.curve]
            for button in self.actions.children[1:]:
                button.radii = [0, 0, 0, 0]
        else:
            self.actions.children[-1].radii[-2:] = (0, self.curve)
            self.actions.children[0].radii[2] = self.curve
Exemplo n.º 28
0
class MDBanner(MDCard, FakeRectangularElevationBehavior):
    vertical_pad = NumericProperty(dp(68))
    """
    Indent the banner at the top of the screen.

    :attr:`vertical_pad` is an :class:`~kivy.properties.NumericProperty`
    and defaults to `dp(68)`.
    """

    opening_transition = StringProperty("in_quad")
    """
    The name of the animation transition.

    :attr:`opening_transition` is an :class:`~kivy.properties.StringProperty`
    and defaults to `'in_quad'`.
    """

    icon = StringProperty("data/logo/kivy-icon-128.png")
    """
    Icon banner.

    :attr:`icon` is an :class:`~kivy.properties.StringProperty`
    and defaults to `'data/logo/kivy-icon-128.png'`.
    """

    over_widget = ObjectProperty()
    """
    The widget that is under the banner.
    It will be shifted down to the height of the banner.

    :attr:`over_widget` is an :class:`~kivy.properties.ObjectProperty`
    and defaults to `None`.
    """

    text = ListProperty()
    """
    List of lines for banner text.
    Must contain no more than three lines for a
    `'one-line'`, `'two-line'` and `'three-line'` banner, respectively.

    :attr:`text` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[]`.
    """

    left_action = ListProperty()
    """
    The action of banner.

    To add one action, make a list [`'name_action'`, callback]
    where `'name_action'` is a string that corresponds to an action name and
    ``callback`` is the function called on a touch release event.

    :attr:`left_action` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[]`.
    """

    right_action = ListProperty()
    """
    Works the same way as :attr:`left_action`.

    :attr:`right_action` is an :class:`~kivy.properties.ListProperty`
    and defaults to `[]`.
    """

    type = OptionProperty(
        "one-line",
        options=[
            "one-line",
            "two-line",
            "three-line",
            "one-line-icon",
            "two-line-icon",
            "three-line-icon",
        ],
        allownone=True,
    )
    """
    Banner type. . Available options are: (`"one-line"`, `"two-line"`,
    `"three-line"`, `"one-line-icon"`, `"two-line-icon"`, `"three-line-icon"`).

    :attr:`type` is an :class:`~kivy.properties.OptionProperty`
    and defaults to `'one-line'`.
    """

    opening_timeout = BoundedNumericProperty(0.7, min=0.7)
    """
    Time interval after which the banner will be shown.

    .. versionadded:: 1.0.0

    :attr:`opening_timeout` is an :class:`~kivy.properties.BoundedNumericProperty`
    and defaults to `0.7`.
    """

    opening_time = NumericProperty(0.15)
    """
    The time taken for the banner to slide to the :attr:`state` `'open'`.

    .. versionadded:: 1.0.0

    :attr:`opening_time` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `0.15`.
    """

    closing_time = NumericProperty(0.15)
    """
    The time taken for the banner to slide to the :attr:`state` `'close'`.

    .. versionadded:: 1.0.0

    :attr:`closing_time` is a :class:`~kivy.properties.NumericProperty`
    and defaults to `0.15`.
    """

    _type_message = None
    _progress = False

    def add_actions_buttons(self, instance_box: MDBoxLayout,
                            data: list) -> NoReturn:
        """
        Adds buttons to the banner.

        :param data: ['NAME BUTTON', <function>];
        """

        if data:
            name_action_button, function_action_button = data
            action_button = MDFlatButton(
                text=f"[b]{name_action_button}[/b]",
                theme_text_color="Custom",
                text_color=self.theme_cls.primary_color,
                on_release=function_action_button,
            )
            action_button.markup = True
            instance_box.add_widget(action_button)

    def show(self) -> NoReturn:
        """Displays a banner on the screen."""
        def show(interval: Union[int, float]):
            self.set_type_banner()
            self.add_actions_buttons(self.ids.left_action_box,
                                     self.left_action)
            self.add_actions_buttons(self.ids.right_action_box,
                                     self.right_action)
            self._add_banner_to_container()
            Clock.schedule_once(self.animation_display_banner, 0.1)

        if not self._progress:
            self._progress = True
            if self.ids.container_message.children:
                self.hide()
            Clock.schedule_once(show, self.opening_timeout)

    def hide(self) -> NoReturn:
        """Hides the banner from the screen."""
        def hide(interval: Union[int, float]):
            anim = Animation(banner_y=0, d=self.closing_time)
            anim.bind(on_complete=self._remove_banner)
            anim.start(self)
            Animation(y=self.over_widget.y + self.height,
                      d=self.closing_time).start(self.over_widget)

        if not self._progress:
            self._progress = True
            Clock.schedule_once(hide, 0.5)

    def set_type_banner(self) -> NoReturn:
        self._type_message = {
            "three-line-icon": ThreeLineIconBanner,
            "two-line-icon": TwoLineIconBanner,
            "one-line-icon": OneLineIconBanner,
            "three-line": ThreeLineBanner,
            "two-line": TwoLineBanner,
            "one-line": OneLineBanner,
        }[self.type]

    def animation_display_banner(self, interval: Union[int,
                                                       float]) -> NoReturn:
        Animation(
            banner_y=self.height + self.vertical_pad,
            d=self.opening_time,
            t=self.opening_transition,
        ).start(self)
        anim = Animation(
            y=self.over_widget.y - self.height,
            d=self.opening_time,
            t=self.opening_transition,
        )
        anim.bind(on_complete=self._reset_progress)
        anim.start(self.over_widget)

    def _remove_banner(self, *args):
        self.ids.container_message.clear_widgets()
        self.ids.left_action_box.clear_widgets()
        self.ids.right_action_box.clear_widgets()
        self._reset_progress()

    def _reset_progress(self, *args):
        self._progress = False

    def _add_banner_to_container(self) -> NoReturn:
        self.ids.container_message.add_widget(
            self._type_message(text_message=self.text, icon=self.icon))
Exemplo n.º 29
0
class StackPart(ButtonBehavior, BoxLayout):
    selected = BooleanProperty(False)
    row = NumericProperty(0)
    template = StringProperty()
    tmplWidget = ObjectProperty()
    name = StringProperty()
    values = DictProperty()
    source = StringProperty()
    image = ObjectProperty(False)
    layout = ObjectProperty(None)

    def realise(self, withValue=False, use_cache=False):
        Logger.info('Calling realisze on %s (with Value %s / use_cache %s' %
                    (self, withValue, use_cache))
        #Force the creation of an image from self.template, thourhg real display
        #Skipt of computed image exists
        if self.image:
            return
        from kivy.clock import Clock
        #Force the creaiotn of the tmpl miniture for display
        from template import BGTemplate
        if not self.template:
            return
        try:
            if not use_cache:
                Logger.info('[SGM]Realize StackPart calling from file')
            tmpl = BGTemplate.FromFile(self.template, use_cache)[-1]
        except IndexError:
            Logger.warn('Warning: template file %s contains no Template !!' %
                        self.template)
            from utils import alert
            alert('Error while loading template %s ' % self.template)
            return
        #App.get_running_app().root.ids['realizer'].add_widget(tmpl) #force draw of the beast

        if withValue:
            tmpl.apply_values(self.values)
        self.tmplWidget = tmpl

        def inner(*args):
            #Here is hould loop on the template to apply them on values
            from kivy.base import EventLoop
            EventLoop.idle()
            cim = tmpl.toImage()
            cim.texture.flip_vertical()
            self.ids['img'].texture = cim.texture
            #App.get_running_app().root.ids['realizer'].remove_widget(tmpl)

        Clock.schedule_once(inner, -1)

    def on_press(self):
        self.selected = not (self.selected)
        if self.selected:  #update on dad selection
            if self.parent.last_selected and self.parent.last_selected != self:
                self.parent.last_selected.selected = False
            self.parent.last_selected = self

    def on_selected(self, instance, selected):
        if self.template:
            self.realise(True)
        if self.selected:
            from kivy.uix.boxlayout import BoxLayout
            BOX = BoxLayout(size_hint=(None, None),
                            orientation='vertical',
                            spacing=10)
            W = 90
            #Add Remove Button
            b = Factory.get('HiddenRemoveButton')(icon='trash', width=W)
            b.bind(on_press=lambda x: self.parent.remove_widget(self))
            #self.add_widget(b)
            be = Factory.get('HiddenRemoveButton')(icon='edit', width=W)
            if self.template:  #it is a template: add edit & export buttons

                def inner(*args):
                    p = Factory.get('TemplateEditPopup')()
                    p.name = self.template
                    options = p.ids['options']
                    #print 'editing options with ', self.values
                    options.values = self.values
                    options.tmplPath = self.template  #trigger options building on popup
                    p.stackpart = self
                    p.open()
                    p.do_layout()
                    p.content.do_layout()
                    from kivy.clock import Clock

                    def _inner(*args):
                        prev = p.ids['preview']
                        tmpl = prev.children[0]
                        TS = tmpl.size
                        PS = prev.parent.size
                        W_ratio = float(.9 * PS[0]) / TS[0]
                        H_ratio = float(.9 * PS[1]) / TS[1]
                        ratio = min(W_ratio, H_ratio)
                        x, y = p.ids['FL'].center
                        prev.center = ratio * x, ratio * y
                        #p.ids['preview'].scale = ratio
                        #forcing x to 0
                        prev.x = 5
                        prev.center_y = y
                        from kivy.metrics import cm
                        p.tsize = options.current_selection[0].size[0] / cm(
                            1), options.current_selection[0].size[1] / cm(1)
                        #print prev, prev.size, prev.pos
                        #print 'ratio', ratio
                        #print prev.parent, prev.parent.size, prev.parent.pos
                        #print prev.parent.parent

                    Clock.schedule_once(_inner, 0)
            else:  #edit button for pure image

                def inner(*args):
                    p = Factory.get('SizeEditPopup')()
                    p.name = self.source
                    _img = self.toPILImage()
                    p.ids.width.text = str(_img.size[0])
                    p.ids.height.text = str(_img.size[1])
                    p.open()

                    def _inner(*args):
                        w, h = float(p.ids.width.text), float(
                            p.ids.height.text)
                        if p.ids.w_metric.text == "cm":
                            from kivy.metrics import cm
                            w *= cm(1)
                        if p.ids.h_metric.text == "cm":
                            from kivy.metrics import cm
                            h *= cm(1)
                        self.setImageSize((w, h))

                    p.cb = _inner

            be.bind(on_press=inner)
            #self.add_widget(be)
            BOX.add_widget(be)
            #Img Export button
            bx = Factory.get('HiddenRemoveButton')(icon='export', width=W)
            bx.bind(on_press=lambda x: self.img_export())
            BOX.add_widget(bx)
            BOX.add_widget(b)
            self.add_widget(BOX)
            #Now Also Add a second box, with Up & Down !!!
            BOX = BoxLayout(size_hint=(None, None),
                            orientation='vertical',
                            spacing=0,
                            width=30)
            bx = Factory.get('HiddenRemoveButton')(icon='up-big')
            bx.bind(on_press=lambda x: self.parent.promote(self))
            BOX.add_widget(bx)
            bx = Factory.get('HiddenRemoveButton')(icon='down-big')
            bx.bind(on_press=lambda x: self.parent.demote(self))
            BOX.add_widget(bx)
            self.add_widget(BOX)
        else:
            self.remove_widget(self.children[0])
            self.remove_widget(self.children[0])

            #if self.template:
            #    self.remove_widget(self.children[0])

        self.focus = self.selected

    def Copy(self):
        blank = StackPart()
        for attr in [
                'template', 'name', 'values', "qt", "verso", 'source', 'image'
        ]:
            setattr(blank, attr, getattr(self, attr))
        blank.ids['img'].texture = self.ids['img'].texture
        return blank

    def img_export(self, dst='export.png'):
        pim = self.toPILImage()
        pim.save(dst)
        from utils import start_file
        start_file(dst)

    def setImageSize(self, size):
        size = [int(x) for x in list(size)]
        img = self.toPILImage()
        self.setImage(img.resize(size))

    def toPILImage(self):
        item = self
        if item.image:
            return item.image
        elif item.template:
            from PIL.Image import frombuffer
            if item.tmplWidget:  #it has been modified
                tmplWidget = item.tmplWidget
            else:
                from template import BGTemplate
                Logger.info('[SGM] toPILIMage calling fromfile ')
                tmplWidget = BGTemplate.FromFile(item.template)
                if tmplWidget:
                    #only taking the last one
                    tmplWidget = tmplWidget[-1]
                else:
                    raise NameError('No such template: ' + item.template)
                print 'here to be added: adding on realizer, exporting & then removing. more tricky'
                if item.values:
                    tmplWidget.apply_values(item.values)
            cim = tmplWidget.toImage(for_print=True)
            pim = frombuffer('RGBA', cim.size, cim._texture.pixels, 'raw',
                             'RGBA', 0, 1)
        else:
            from PIL import Image
            pim = Image.open(self.source)
        return pim

    def setImage(self, pilimage):
        from PIL.Image import FLIP_TOP_BOTTOM
        from kivy.graphics.texture import Texture
        #Standard mode: flip the
        flip = pilimage.transpose(FLIP_TOP_BOTTOM)
        from img_xfos import img_modes
        ktext = Texture.create(size=flip.size)
        ktext.blit_buffer(flip.tobytes(), colorfmt=img_modes[flip.mode])
        self.ids['img'].texture = ktext
        self.image = pilimage

    def getSize(self):
        from conf import FORCE_FIT_FORMAT, card_format
        if self.image:  #stack part with computed image
            return self.image.size
        elif self.tmplWidget:
            return self.tmplWidget.size
        elif self.template:
            self.realise(withValue=True, use_cache=True)
            return self.tmplWidget.size
        elif FORCE_FIT_FORMAT:
            return card_format.size
        else:
            if self.ids.img.texture:
                return self.ids.img.texture.size
            return card_format.size
Exemplo n.º 30
0
    def test_stringcheck(self):
        from kivy.properties import StringProperty

        a = StringProperty()
        a.link(wid, 'a')
        a.link_deps(wid, 'a')
        self.assertEqual(a.get(wid), '')
        a.set(wid, 'hello')
        self.assertEqual(a.get(wid), 'hello')

        try:
            a.set(wid, 88)  # number shouldn't be accepted
            self.fail('string accept number, fail.')
        except ValueError:
            pass
Exemplo n.º 31
0
class FileChooser(FileChooserController):
    '''Implementation of a :class:`FileChooserController` which supports
    switching between multiple, synced layout views.

    The FileChooser can be used as follows:

    .. code-block:: kv

        BoxLayout:
            orientation: 'vertical'

            BoxLayout:
                size_hint_y: None
                height: sp(52)

                Button:
                    text: 'Icon View'
                    on_press: fc.view_mode = 'icon'
                Button:
                    text: 'List View'
                    on_press: fc.view_mode = 'list'

            FileChooser:
                id: fc
                FileChooserIconLayout
                FileChooserListLayout

    .. versionadded:: 1.9.0
    '''

    manager = ObjectProperty()
    '''
    Reference to the :class:`~kivy.uix.screenmanager.ScreenManager` instance.

    manager is an :class:`~kivy.properties.ObjectProperty`.
    '''

    _view_list = ListProperty()

    def get_view_list(self):
        return self._view_list

    view_list = AliasProperty(get_view_list, bind=('_view_list', ))
    '''
    List of views added to this FileChooser.

    view_list is an :class:`~kivy.properties.AliasProperty` of type
    :class:`list`.
    '''

    _view_mode = StringProperty()

    def get_view_mode(self):
        return self._view_mode

    def set_view_mode(self, mode):
        if mode not in self._view_list:
            raise ValueError('unknown view mode %r' % mode)
        self._view_mode = mode

    view_mode = AliasProperty(get_view_mode,
                              set_view_mode,
                              bind=('_view_mode', ))
    '''
    Current layout view mode.

    view_mode is an :class:`~kivy.properties.AliasProperty` of type
    :class:`str`.
    '''

    @property
    def _views(self):
        return [screen.children[0] for screen in self.manager.screens]

    def __init__(self, **kwargs):
        super(FileChooser, self).__init__(**kwargs)

        self.manager = ScreenManager()
        super(FileChooser, self).add_widget(self.manager)

        self.trigger_update_view = Clock.create_trigger(self.update_view)

        self.fbind('view_mode', self.trigger_update_view)

    def add_widget(self, widget, **kwargs):
        if widget is self._progress:
            super(FileChooser, self).add_widget(widget, **kwargs)
        elif hasattr(widget, 'VIEWNAME'):
            name = widget.VIEWNAME + 'view'
            screen = Screen(name=name)
            widget.controller = self
            screen.add_widget(widget)
            self.manager.add_widget(screen)

            self.trigger_update_view()
        else:
            raise ValueError('widget must be a FileChooserLayout,'
                             ' not %s' % type(widget).__name__)

    def rebuild_views(self):
        views = [view.VIEWNAME for view in self._views]
        if views != self._view_list:
            self._view_list = views
            if self._view_mode not in self._view_list:
                self._view_mode = self._view_list[0]
            self._trigger_update()

    def update_view(self, *args):
        self.rebuild_views()

        sm = self.manager
        viewlist = self._view_list
        view = self.view_mode
        current = sm.current[:-4]

        viewindex = viewlist.index(view) if view in viewlist else 0
        currentindex = viewlist.index(current) if current in viewlist else 0

        direction = 'left' if currentindex < viewindex else 'right'

        sm.transition.direction = direction
        sm.current = view + 'view'

    def _create_entry_widget(self, ctx):
        return [
            Builder.template(view._ENTRY_TEMPLATE, **ctx)
            for view in self._views
        ]

    def _get_file_paths(self, items):
        if self._views:
            return [file[0].path for file in items]
        return []

    def _update_item_selection(self, *args):
        for viewitem in self._items:
            selected = viewitem[0].path in self.selection
            for item in viewitem:
                item.selected = selected

    def on_entry_added(self, node, parent=None):
        for index, view in enumerate(self._views):
            view.dispatch('on_entry_added', node[index],
                          parent[index] if parent else None)

    def on_entries_cleared(self):
        for view in self._views:
            view.dispatch('on_entries_cleared')

    def on_subentry_to_entry(self, subentry, entry):
        for index, view in enumerate(self._views):
            view.dispatch('on_subentry_to_entry', subentry[index], entry)

    def on_remove_subentry(self, subentry, entry):
        for index, view in enumerate(self._views):
            view.dispatch('on_remove_subentry', subentry[index], entry)

    def on_submit(self, selected, touch=None):
        view_mode = self.view_mode
        for view in self._views:
            if view_mode == view.VIEWNAME:
                view.dispatch('on_submit', selected, touch)
                return