class LobbyScreen(Screen):
    def __init__(self, **kwargs):
        Screen.__init__(self, name="Lobby", **kwargs)
        client.add_listener(self.players_list_update_handler)
        client.add_listener(self.game_started_handler)

        _layout = BoxLayout(orientation='vertical')
        self.add_widget(_layout)

        self.players_stack = StackLayout(orientation='tb-lr', spacing=10)
        _layout.add_widget(self.players_stack)

        self.ready_btn = ToggleButton(text="Ready",
                                      font_size=42,
                                      size_hint=(1, 0.3))
        self.ready_btn.bind(state=self.on_ready)
        _layout.add_widget(self.ready_btn)

    def on_pre_enter(self, *args):
        client.add_listener(self.players_list_update_handler)
        client.add_listener(self.game_started_handler)

    def on_pre_leave(self, *args):
        client.remove_listener(self.players_list_update_handler)
        client.remove_listener(self.game_started_handler)

    def on_leave(self, *args):
        self.ready_btn.state = 'normal'

    def on_ready(self, instance, value):
        client.send({"op": 'SetReady', 'ready': value == 'down'})

    def players_list_update_handler(self, msg):
        if msg.get('op') == 'LobbyUpdated':
            self.players_stack.clear_widgets()
            for player in msg['players']:
                self.players_stack.add_widget(
                    Label(text=player['name'],
                          font_size=24,
                          size_hint=(0.5, 0.2),
                          color=(0, 0.7, 0, 1) if player['ready'] else
                          (1, 1, 1, 1)))
            return True

    def game_started_handler(self, msg):
        if msg.get('op') == 'PreparationStarted':
            prep_screen = self.manager.get_screen("Preparation")
            prep_screen.init(msg['#questions'], msg['#answers'])
            self.manager.current = "Preparation"
            return True
Exemple #2
0
class MP3k(Widget):
    playlist_width = NumericProperty()

    def __init__(self, **kwargs):
        Globals.CONFIG = App.get_running_app().config
        Globals.TESTING = Globals.CONFIG.get('Development', 'test_mode')

        self.playlist_width = int(
            Globals.CONFIG.get('Playlist', 'playlist_width'))

        Globals.API = GoogleMusicApi()
        self.login_failed_popup = None
        self.google_music_api_login()

        self.formatter = Formatter()

        self.player = Player()
        self.player.set_streaming_quality(
            Globals.CONFIG.get('Google Play Music', 'quality').split(':')[0])

        self.playlist = Playlist()

        self.librarymanager = LibraryManager()
        # self.librarymanager.load_library()

        self.history = History()
        self.playlist.queue = self.history.playlist_history

        self.updating_progress = False
        self.playlist_hidden = False

        super().__init__(**kwargs)
        self.player.bind(playing=self.update_play_button_state)
        self.player.bind(progress_percent=self.update_progress_slider)

        # Add search result views
        # Songs
        self.songlist = SongViewer()

        # Stations
        # self.stationlist = StationViewer()
        self.stationscroll = ScrollView(size_hint=(1, 1))
        self.stationscroll.do_scroll_x = False
        self.stationlist = StackLayout(size_hint_y=None, spacing=10)
        self.stationlist.bind(minimum_height=self.stationlist.setter('height'))
        self.stationscroll.add_widget(self.stationlist)

        # Albums
        self.albumlist = AlbumViewer()
        # Create and init screen manager
        self.sm = ScreenManager()
        self.init_screens()

        # Listen for Keyboard events
        self._keyboard = Window.request_keyboard(None, self, 'text')
        self._keyboard.bind(on_key_down=self._pressed_key)
        self.searchbar.focus = True

    def google_music_api_login(self):
        if Globals.API.login(
                Globals.CONFIG.get('Google Play Music', 'login'),
                Globals.CONFIG.get('Google Play Music', 'password'),
                Globals.CONFIG.get('Google Play Music', 'device_id')):
            Logger.debug('Google Music: Login successful')
            if self.login_failed_popup:
                self.login_failed_popup.dismiss()
                self.login_failed_popup = None
        else:
            Logger.warning("Google Music: Login Failed")
            if not self.login_failed_popup:
                popup = Popup(title='Google Play Music™ login',
                              content=LoginCredentials(),
                              auto_dismiss=False,
                              size_hint=(1, 1))
                self.login_failed_popup = popup

            if Globals.STARTED:  # login failed after configuration change
                self.login_failed_popup.open(
                )  # open popup because MP3k is completely rendered
                App.get_running_app().close_settings()

            else:  # login failed on start
                pass  # wait with popup until MP3k is completely rendered (MP3kApp opens the popup in on_start())

    def try_login_step_1(self, instance, google_login, google_password):
        # Credentials login without device ID
        if Globals.API.login(google_login, google_password, ''):
            # login successful
            Globals.CONFIG.set('Google Play Music', 'login', google_login)
            Globals.CONFIG.set('Google Play Music', 'password',
                               google_password)
            Globals.CONFIG.write()

            self.show_device_login()

        else:
            instance.login_failed_label.color = (1, 0, 0, 1)

    def try_login_step_2(self, button_container):
        # look for selected device
        for button in button_container.children:
            if button.state == 'down':  # got it
                Globals.CONFIG.set('Google Play Music', 'device_id',
                                   button.device_id)
                Globals.CONFIG.write()  # set device id in config

                if Globals.API.relogin(
                        Globals.CONFIG.get(
                            'Google Play Music',
                            'login'),  # re-login with valid device_id
                        Globals.CONFIG.get('Google Play Music', 'password'),
                        Globals.CONFIG.get('Google Play Music', 'device_id')):
                    self.login_failed_popup.dismiss()
                    break
        else:
            Logger.error('LOGIN: You have to select a device!')

    def show_device_login(self):
        self.login_failed_popup.dismiss()
        self.login_failed_popup = Popup(title='Google Play Music™ login',
                                        content=self.build_devices_login(),
                                        auto_dismiss=False,
                                        size_hint=(1, 1))
        self.login_failed_popup.open()

    @staticmethod
    def build_devices_login():
        devices = Globals.API.get_registered_mobile_devices(
        )  # get registered mobile devices
        login_devices = LoginDevices()

        # add buttons for devices
        for device in devices:
            btn = DeviceButton(text='{} (ID: {})'.format(
                device['name'], device['id']),
                               device_id=device['id'],
                               size_hint_y=None,
                               height=30,
                               group='devices')
            login_devices.device_button_container.add_widget(btn)

        return login_devices

    def init_screens(self):

        # Create screens
        screen_songs = Screen(name='Songs', size_hint=(1, 1))
        screen_stations = Screen(name='Stations', size_hint=(1, 1))
        screen_albums = Screen(name='Albums', size_hint=(1, 1))

        # Add content to screens
        screen_songs.add_widget(self.songlist)
        # screen_stations.add_widget(self.stationlist)
        screen_stations.add_widget(self.stationscroll)
        screen_albums.add_widget(self.albumlist)

        # Add screens
        self.sm.add_widget(screen_songs)
        self.sm.add_widget(screen_stations)
        self.sm.add_widget(screen_albums)

        # Add screen manager and playlist
        self.screenmanagercontainer.add_widget(self.sm)

    def _pressed_key(self, keyboard, keycode, text, modifiers):
        if not self.searchbar.focus:
            if keycode[1] == 'spacebar' or keycode[0] == 1073742085:
                Logger.debug('Keyboard: Pressed spacebar/play-pause-media-key')
                self.playbutton_callback()
            elif keycode[0] == 1073742083:  # 'previous track' media key
                Logger.debug("Keyboard: Pressed 'previous track' media key")
                self.previousbutton_callback()
            elif keycode[0] == 1073742082:  # 'next track' media key
                Logger.debug("Keyboard: Pressed 'next track' media key")
                self.nextbutton_callback()
            #else:
            #    print(keycode)

    def update_play_button_state(self, instance, value):
        if value:
            self.playbutton.icon = '../res/icons/glyphicons-175-pause_white.png'
        else:
            self.playbutton.icon = '../res/icons/glyphicons-174-play_white.png'

    def update_progress_slider(self, instance, value):
        self.progress_slider.value = value

    def update_playlist_position(self, window, width, height):
        self.playlist_view.pos_hint = {'x': .3, 'y': 90.0 / height}

    def update_playlist_view(self, instance, value):
        print('Updating playlist..')
        print('data_queue before: ' + str(len(self.playlist_view.data_queue)))
        # self.playlist_view.data_queue = self.playlist.queue  # ListView should be updated if we do it like this..
        self.playlist_view.children[0].adapter.data.clear(
        )  # ..but it isn't, so we do this
        self.playlist_view.children[0].adapter.data.extend(
            self.playlist.queue)  # and then this
        # self.playlist_view.content.listview.adapter.data = self.playlist.queue  # never do this, it replaces the
        # ObservableList and breaks kivy functionality
        print('data_queue after: ' + str(len(self.playlist_view.data_queue)))

    def _update_progress_interval(self, delta_time):
        if not self.updating_progress:
            self.updating_progress = True

            if self.player.current_track and self.player.playback_started and self.player.playing:
                # time_played_ms = pygame.mixer.music.get_pos()
                # duration_ms = int(self.musicmanager.current_track['duration_ms'])
                # progress = time_played_ms / (duration_ms / 100)

                progress_percent = self.player.send_cmd_to_mplayer(
                    'get_percent_pos', 'ANS_PERCENT_POSITION')
                if progress_percent is not False and progress_percent is not None:
                    old_progress_percent = self.player.progress_percent
                    self.player.progress_percent = interpolate(
                        old_progress_percent, int(progress_percent))
                    Logger.trace('Progress: ' +
                                 str(self.player.progress_percent))
                elif progress_percent is False:
                    Logger.debug('_update_progress_interval: Received ' +
                                 str(progress_percent) + ' as progress')
                    self.player.playback_finished()
                    self.play_next_track()
                else:
                    Logger.debug('_update_progress_interval: Received ' +
                                 str(progress_percent) + ' as progress')

            # remove schedule if no track selected
            elif not self.player.playback_started and not self.player.playing:
                Logger.debug(
                    'No song playing, removing slider update interval..')
                self.updating_progress = False
                return False

            self.updating_progress = False

    def on_config_changed(self, section, key, value):
        Logger.debug('Config: Config changed')

        if key == 'playlist_width':
            self.playlist_width = int(value)

        elif section == 'Google Play Music':
            Globals.API.logout()
            self.google_music_api_login()

        elif section == 'Development':
            if key == 'test_mode':
                Globals.TESTING = True if value == '1' else False

    def fix_scrolling_workaround(self):
        self.playlist_view.listview._reset_spopulate()

    def playbutton_state(self):
        Logger.debug('Playbuttonstate: ' + self.playbutton.state)
        return 'down' if self.player.playing else 'normal'

    def mark_playing_track(self):
        # track_item = self.playlist_view.get_track(0)
        # track_item.update_image('../res/icons/equalizer.gif')
        playing_text = '{} - {}'.format(self.player.current_track['title'],
                                        self.player.current_track['artist'])

        self.playinglabel.text = playing_text
        App.get_running_app().title = playing_text

    def restart_track(self):
        Logger.info('Restarting track..')
        self.play_track(self.player.current_track)

    def play_previous_track(self):
        Logger.info('Playing previous track')
        track = self.playlist.get_previous_track()
        if track:
            self.play_track(track)

    def play_next_track(self):
        Logger.info('Playing next track')
        track = self.playlist.get_next_track()
        if track:
            self.play_track(track)
        else:
            App.get_running_app(
            ).title = 'MusicPlayer 3000 for Google Play Music™'

    def switch_screen_callback(self, screen_title):
        self.sm.current = screen_title

    def play_callback(self, track, index):
        Logger.debug('Playing from songlist (left): Index ' + str(index))
        self.playlist.add_track_and_set_current(track)
        self.fix_scrolling_workaround()
        self.play_track(track)

    def play_album_callback(self, album_id):
        index = len(self.playlist.queue)
        self.add_album_to_playlist_callback(album_id)
        idx, track = self.playlist.set_current_track(index)
        self.play_track(track)

    def play_from_playlist_callback(self, track, index):
        Logger.debug('Playing from playlist (right): Index ' + str(index))
        self.playlist.set_current_track(index)
        self.play_track(track)

    def play_track(self, track):
        Logger.info('Playing track: ' + track['title'])
        self.player.play_track_from_id(track)
        self.mark_playing_track()
        # self.set_playing_icon()
        # unschedule possible previous intervals
        Clock.unschedule(self._update_progress_interval)
        # start interval for updating the progress slider
        Clock.schedule_interval(self._update_progress_interval, .1)

    def set_playing_icon(self):
        index, current_track = self.playlist.get_current_track()
        # self.playlist_view.children[0].adapter.data[index]

    def playbutton_callback(self):
        if self.player.current_track:  # we have a track selected
            if self.player.playback_started and self.player.playing:  # pause track
                self.player.pause_current_track()
            elif self.player.playback_started and not self.player.playing:  # resume track
                self.player.resume_current_track()
            else:  # playback has finished, restart track
                self.restart_track()
        else:  # No track selected but maybe we have elements in the playlist
            Logger.debug('No current track set!')
            track = self.playlist.get_start()
            if track:
                self.play_track(track)
            else:  # do nothing if no track selected
                #self.librarymanager.synchronize_library()
                pass

    def nextbutton_callback(self):
        if self.player.current_track:  # we have a track selected
            self.play_next_track()
        else:  # No track selected but maybe we have some in the playlist
            Logger.debug('No current track set!')
            track = self.playlist.get_start()
            if track:
                self.play_track(track)
            else:  # do nothing if no track selected
                pass

    def previousbutton_callback(self):
        if self.player.current_track:  # we have a track selected
            self.play_previous_track()
        else:  # No track selected but maybe we have some in the playlist
            Logger.debug('No current track set!')
            track = self.playlist.get_start()
            if track:
                self.play_track(track)
            else:  # do nothing if no track selected
                pass

    def shufflebutton_callback(self):
        if self.playlist.shuffle:
            Logger.info("I won't shuffle anymore..")
            self.playlist.shuffle = False
            self.shufflebutton.source = self.shufflebutton.source_img_alt
        else:
            Logger.info("Everyday I'm shuffling..")
            self.playlist.shuffle = True
            self.shufflebutton.source = self.shufflebutton.source_img

    def skip_callback(self, touch_pos):
        width = self.progress_slider.width
        touch_pos_x = touch_pos[0]
        position = touch_pos_x / (width / 100)
        if self.player.current_track:  # we need a track to skip into
            if not self.player.playback_started:  # song is not playing, restart song
                self.restart_track()
            self.player.skip_track_to(position)  # skip to position
        else:
            # self.progress_slider.value = 0  # keep slider position at 0
            # TODO: Look into slider implementation to keep slider at 0
            pass

    def add_to_playlist_callback(self, track):
        self.playlist.add_track(QueryDict(track))
        self.fix_scrolling_workaround()

    def remove_from_playlist_callback(self, index):
        self.playlist.remove_track(index)

    def add_album_to_playlist_callback(self, album_id):
        album = Globals.API.get_album_info(album_id)
        album_tracks = album['tracks']
        if album_tracks:
            album_tracks = self.formatter.format_tracks_list(album_tracks)
            for track in album_tracks:
                self.playlist.add_track(QueryDict(track))
            self.fix_scrolling_workaround()

    def play_station_callback(self, title, seed):
        tracks = Globals.API.get_station_tracks(title, seed)
        if tracks:
            tracks = self.formatter.format_tracks_list(tracks)
            self.playlist.clear()
            for track in tracks:
                self.playlist.add_track(track)
            self.fix_scrolling_workaround()
            # self.playlist.set_current_track(0)
            #self.playbutton_callback()
            track = self.playlist.get_start()
            if track:
                self.play_track(track)
            else:  # do nothing if no track selected
                pass

    def playlist_button_callback(self):
        if self.playlist_hidden:
            self.playlist_container.width = self.playlist_width
            self.playlist_hidden = False
        else:
            self.playlist_container.width = 0
            self.playlist_hidden = True

    def clear_playlist_callback(self):
        Logger.info('Clearing playlist')
        self.playlist.clear()
        # self.playlist.set_current_track(0)

    def search(self, text):
        if len(text) >= 3:
            try:
                search_results = Globals.API.search(text)
                # with open('search_test.json', 'w') as outfile:
                #    json.dump(search_results, outfile)

                self.display_search_results(search_results)
            except CallFailure:
                Logger.warning("Search: No All Access for this account!")
                # TODO: Show login popup
                # TODO: Remove try..except block when gmusicapi 9.0.1 is stable
        else:
            with open('search_test.json') as outfile:
                search_results = json.load(outfile)
                self.display_search_results(search_results)

    def display_search_results(self, search_results):
        Logger.info("Displaying results..")

        Logger.debug("Displaying song results")
        tracks = []
        for entry in search_results['song_hits']:
            tracks.append(entry['track'])
        # songs_sorted = sorted(songs, key=self.get_song_key)
        tracks_formatted = self.formatter.format_tracks_list(tracks)
        # self.ids['list_songs'].data_songs = tracks_formatted
        self.songlist.data_songs = tracks_formatted

        Logger.debug("Displaying station results")
        stations = []
        for entry in search_results['station_hits']:
            stations.append(entry['station'])
        stations_formatted = self.formatter.format_stations_list(stations)

        # add station list items
        # self.stationlist.data_stations = stations_formatted

        # add station panels
        self.stationlist.clear_widgets()
        for station in stations_formatted:
            self.stationlist.add_widget(StationPanelItem(station))

        Logger.debug("Displaying album results")
        albums = []
        for entry in search_results['album_hits']:
            albums.append(entry['album'])
        albums_formatted = self.formatter.format_albums_list(albums)
        self.albumlist.data_albums = albums_formatted
Exemple #3
0
class Catalog(BoxLayout):
    def __init__(self,**kwargs):
        super(Catalog, self).__init__(**kwargs)       

        #self.orientation = 'vertical'

        self.search_bar = BoxLayout(size_hint=(1.0,0.05))        
        self.search_bar.add_widget(Label(text='Search',size_hint=(0.25,1.0)))
        self.search_text = (TextInput(multiline=False))
        self.search_bar.add_widget(self.search_text)

        self.filter_bar = BoxLayout(size_hint=(1.0,0.05))        
        self.AHSE = ToggleButton(text='AHSE',size_hint=(0.25,1.0))
        self.ENGR = ToggleButton(text='ENGR',size_hint=(0.25,1.0))
        self.MTH = ToggleButton(text='MTH',size_hint=(0.25,1.0))
        self.SCI = ToggleButton(text='SCI',size_hint=(0.25,1.0))        
        self.filter_bar.add_widget(self.AHSE)
        self.filter_bar.add_widget(self.ENGR)
        self.filter_bar.add_widget(self.MTH)
        self.filter_bar.add_widget(self.SCI)

        self.scrollview = ScrollView(size_hint=(1.0,0.9),size=(400,400))
        self.courses = StackLayout(spacing=5,size_hint_y=None)
        self.courses.bind(minimum_height=self.courses.setter('height'))
        for course_object in catalog:
            course_item = Course_Item(course=course_object,size_hint=(0.245,None),height=200)                             
            self.courses.add_widget(course_item)
        self.scrollview.add_widget(self.courses)
                        
        self.add_widget(self.search_bar)
        self.add_widget(self.filter_bar)
        self.add_widget(self.scrollview)

        Clock.schedule_interval(self.update_favorites,0.1)
        Clock.schedule_interval(self.search_function,0.1)


    def search_function(self,instance):
        query = self.search_text.text.lower()        
        searched_items = []
        filtered_items = []

        #fills up the temp list the first time it runs
        if len(search_temp_list) == 0:
            for course_item in self.courses.children:
                search_temp_list.append(course_item)       
        
        #if the query is not empty, do term search
        if query != "":                      
            for course_item in search_temp_list:                            
                if query == course_item.course.name.lower() or query == course_item.course.code or query == course_item.course.prof.lower():
                    searched_items.append(course_item)
                for keyword in course_item.course.keywords:
                    if query == keyword.lower():                        
                        searched_items.append(course_item)           
        else:
            searched_items = search_temp_list
        
        if self.AHSE.state == 'normal' and self.ENGR.state == 'normal' and self.MTH.state == 'normal' and self.SCI.state == 'normal':
            filtered_items = searched_items

        else:                                
            if self.AHSE.state == 'down':
                for course_item in searched_items:                   
                    if course_item.course.credits['AHSE'] > 0:                                                  
                        filtered_items.append(course_item)
            if self.ENGR.state == 'down': 
                for course_item in searched_items:                      
                    if course_item.course.credits['ENGR'] > 0 and course_item not in filtered_items:                                                  
                        filtered_items.append(course_item)
            if self.MTH.state == 'down':                          
                for course_item in searched_items:
                    if course_item.course.credits['MTH'] > 0 and course_item not in filtered_items:                                                 
                        filtered_items.append(course_item)
            if self.SCI.state == 'down':
                for course_item in searched_items:                   
                    if course_item.course.credits['SCI'] > 0 and course_item not in filtered_items:                                             
                        filtered_items.append(course_item)

        if len(self.courses.children) != len(filtered_items):
            self.courses.clear_widgets()
            for course_item in filtered_items:
                self.courses.add_widget(course_item)   

    def update_favorites(self,instance):        
        for course_item in self.courses.children:
            if course_item.favorite.state == 'normal' and course_item.course in favorite_courses:
                favorite_courses.remove(course_item.course)
            if course_item.favorite.state == 'down' and course_item.course not in favorite_courses:
                favorite_courses.append(course_item.course)                
class Peta(Screen):
    def __init__(self, **kwargs):
        super(Peta, self).__init__(**kwargs)

        self.Navigation = Image(source='peta/lib/Navigation.png',
                                pos_hint={
                                    'x': 0.74,
                                    'y': 0.80
                                },
                                size_hint=(.2, .1))
        self.legendBatasWil = Image(
            source='peta/lib/peta-batas-wilayah-keterangan.png',
            size_hint=(1, 1),
            pos_hint={
                'center_x': 0.5,
                'y': None
            })
        self.legendJalan = Image(source='peta/lib/peta-jalan-keterangan.png',
                                 size_hint=(1, 1),
                                 pos_hint={
                                     'center_x': 0.5,
                                     'y': None
                                 })
        self.legendAir = Image(
            source='peta/lib/peta-air-bersih-keterangan.png',
            size_hint=(1, 1),
            pos_hint={
                'center_x': 0.5,
                'y': None
            })
        self.legendGorong = Image(
            source='peta/lib/peta-gorong-gorong-keterangan.png',
            size_hint=(1, 1),
            pos_hint={
                'center_x': 0.5,
                'y': None
            })
        self.legendSarana = Image(
            source='peta/lib/peta-sarana-publik-keterangan.png',
            size_hint=(1, 2.5),
            pos_hint={
                'center_x': 0.5,
                'y': None
            })

        ## TOGGLE BUTTON FUNCTION ##
        self.jaringanJalanButton = ToggleButton(
            background_normal='peta/lib/jalan.png',
            background_down='peta/lib/jalan-hit.png',
            state='normal',
            size_hint=(.78, .25))
        self.jaringanAirBersihButton = ToggleButton(
            background_normal='peta/lib/air-bersih.png',
            background_down='peta/lib/air-bersih-hit.png',
            state='normal',
            size_hint=(.78, .25))
        self.jaringanGegorongButton = ToggleButton(
            background_normal='peta/lib/gorong-gorong.png',
            background_down='peta/lib/gorong-gorong-hit.png',
            state='normal',
            size_hint=(.78, .25))
        self.saranaPublik = ToggleButton(
            background_normal='peta/lib/sarana-publik.png',
            background_down='peta/lib/sarana-publik-hit.png',
            state='normal',
            size_hint=(.78, .25))

        ## BUTTON FUNCTION ##
        self.zoomInButton = Button(background_normal='peta/lib/zoom-in.png',
                                   background_down='peta/lib/zoom-in-hit.png',
                                   state='normal',
                                   size_hint=(.78, .25))
        self.zoomOutButton = Button(
            background_normal='peta/lib/zoom-out.png',
            background_down='peta/lib/zoom-out-hit.png',
            state='normal',
            size_hint=(.78, .25))

        self.jaringanJalanButton.bind(on_release=self.jalan)
        self.jaringanAirBersihButton.bind(on_release=self.air)
        self.jaringanGegorongButton.bind(on_release=self.gorong)
        self.saranaPublik.bind(on_release=self.sarana)

        self.zoomInButton.bind(on_release=self.zoomIn)
        self.zoomOutButton.bind(on_release=self.zoomOut)

        ## Button Container ##
        self.bLayout = BoxLayout(size_hint=(None, None),
                                 orientation='horizontal',
                                 width=900,
                                 height=650,
                                 pos_hint={
                                     'y': 0.05,
                                     'x': 0.05
                                 })
        self.bLayout.add_widget(self.jaringanJalanButton)
        self.bLayout.add_widget(self.jaringanAirBersihButton)
        self.bLayout.add_widget(self.jaringanGegorongButton)
        self.bLayout.add_widget(self.saranaPublik)
        self.bLayout.add_widget(self.zoomInButton)
        self.bLayout.add_widget(self.zoomOutButton)

        ## LEGEND CONTAINER ##
        self.legendContainer = StackLayout(orientation='lr-tb',
                                           size_hint=(None, None),
                                           minimum_width=366,
                                           minimum_height=672,
                                           width=366,
                                           height=105,
                                           pos_hint={
                                               'x': 0.75,
                                               'y': 0.70
                                           })
        self.legendContainer.add_widget(self.legendBatasWil)

        self.mapScreen = MapLayout()

        #self.add_widget(self.mapScreen)
        self.add_widget(self.mapScreen.mapBound)
        self.add_widget(self.bLayout)
        self.add_widget(self.Navigation)
        self.add_widget(self.legendContainer)
        self.zoomIdx = 0

    ## Filter Button function method ##
    def jalan(self, b, **kwargs):
        if b.state == 'down':
            self.mapScreen.addJalan()
            self.legendContainer.add_widget(self.legendJalan)

            # log
            gv.logger.log_button('view :' + 'jalan')
        elif b.state == 'normal':
            self.mapScreen.removeJalan()
            self.legendContainer.remove_widget(self.legendJalan)

            # log
            gv.logger.log_button('close :' + 'jalan')

    def air(self, b, **kwargs):
        if b.state == 'down':
            self.mapScreen.addAir()
            self.legendContainer.add_widget(self.legendAir)

            # log
            gv.logger.log_button('view :' + 'air bersih')
        elif b.state == 'normal':
            self.mapScreen.removeAir()
            self.legendContainer.remove_widget(self.legendAir)

            # log
            gv.logger.log_button('close :' + 'air bersih')

    def gorong(self, b, **kwargs):
        if b.state == 'down':
            self.mapScreen.addGorong()
            self.legendContainer.add_widget(self.legendGorong)

            # log
            gv.logger.log_button('view :' + 'gorong-gorong')
        elif b.state == 'normal':
            self.mapScreen.removeGorong()
            self.legendContainer.remove_widget(self.legendGorong)

            # log
            gv.logger.log_button('close :' + 'gorong-gorong')

    def sarana(self, b, **kwargs):
        if b.state == 'down':
            self.mapScreen.addSarana()
            self.legendContainer.add_widget(self.legendSarana)

            # log
            gv.logger.log_button('view :' + 'sarana')
        elif b.state == 'normal':
            self.mapScreen.removeSarana()
            self.legendContainer.remove_widget(self.legendSarana)

            # log
            gv.logger.log_button('close :' + 'sarana')

    def reset(self):
        self.mapScreen.resetMap()
        self.legendContainer.clear_widgets()
        self.legendContainer.add_widget(self.legendBatasWil)
        self.jaringanJalanButton.state = 'normal'
        self.jaringanAirBersihButton.state = 'normal'
        self.jaringanGegorongButton.state = 'normal'
        self.saranaPublik.state = 'normal'

    def zoomIn(self, button=None, **args):
        self.mapScreen.zoomingIn()

    def zoomOut(self, button=None, **args):
        self.mapScreen.zoomingOut()
Exemple #5
0
class EventListbox(StackLayout):

  def __init__(self, **kwargs):
    super(EventListbox, self).__init__(**kwargs)
    self.is_updating = False
    self.orientation = 'lr-tb'
    self.items = []
    self.bind(pos=self.draw, size=self.draw)
    self.data_bindings = dict()
    self.selected_view = None
    self.selected_item = None
    # content
    self.content = StackLayout(orientation = 'lr-tb')
    self.content.size_hint_y = None #for scrollviewer
    self.content.bind(minimum_height=self.content.setter('height'))
    self.scrollview = ScrollView(size_hint=[1, 1])
    self.scrollview.do_scroll_x = False
    self.scrollview.add_widget(self.content)
    self.add_widget(self.scrollview)
    
  def begin_update(self):
    self.is_updating = True
    
  def end_update(self):
    self.is_updating = False
    self.draw()
    
  def add_item(self, item):
    self.items.append(item)
    if not self.is_updating:
      self.draw()
    
  def clear_items(self):
    del self.items[:]
    self.clear_selection()
    self.draw()
    
  def clear_selection(self):
    self.selected_view = None
    self.selected_item = None
    
  def draw(self, *args):
    self.content.clear_widgets()
    self.data_bindings.clear()
    n = len(self.items)
    i = 0
    while i < n:
      item_wgt = EventWidget(self.items[i])
      item_wgt.height = self.height/4
      item_wgt.size_hint = [1, None] #for scrollviewer parent
      item_wgt.bind(on_touch_down=self.selection_change)
      self.content.add_widget(item_wgt)
      self.data_bindings[item_wgt] = self.items[i]
      i += 1
    # self.draw_background()
      
  def selection_change(self, instance, touch):
    for item_wgt in self.content.children:
      if item_wgt.collide_point(touch.x, touch.y):
        self.selected_view = item_wgt
        self.selected_item = self.data_bindings[item_wgt]
class HistToolApp(App):

    autosave_file_name = 'last_settings.hst'

    def __init__(self):
        super().__init__()
        self.models = []
        self.histLayout = None
        self.scrollView = None
        self.usedIDs = []
        self.all_settings = defaultdict(int)
        self.currID = self.loadSettings()
        self.initLayoutHeight = 10
        Window.bind(on_resize=self._on_resize)

    def saveSettings(self, fname=autosave_file_name):
        saved_settings = [
            self.all_settings[key] for key in self.all_settings
            if self.all_settings[key] != 0
        ]
        #print(saved_settings)
        with open(fname, 'w') as f:
            for item in saved_settings:
                f.write("%s\n" % item)

    def loadSettings(self, fname=autosave_file_name):
        idx = 0
        self.all_settings = defaultdict(int)
        with open(fname, 'r') as f:
            for line in f:
                self.all_settings[idx] = (eval(line))
                idx += 1
        return idx

    def build(self):

        if self.root is not None:
            self.root.clear_widgets()

        add_hist_button = Button(text='Add Histogram',
                                 font_size=14,
                                 on_press=self.addHistogram)
        create_hists_button = Button(text='Create Graphs',
                                     font_size=14,
                                     on_press=self.createHistograms)
        save_hists_button = Button(text='Save Histograms',
                                   font_size=14,
                                   on_press=self._save_button)
        load_hists_button = Button(text='Load Histograms',
                                   font_size=14,
                                   on_press=self._load_button)
        save_and_load = BoxLayout(orientation='vertical')
        save_and_load.add_widget(save_hists_button)
        save_and_load.add_widget(load_hists_button)

        btnLayout = BoxLayout(size_hint=(1, None), height=50)
        btnLayout.add_widget(add_hist_button)
        btnLayout.add_widget(create_hists_button)
        btnLayout.add_widget(save_and_load)

        self.scrollView = ScrollView(size_hint=(1, 1))
        self.scrollView.do_scroll_x = False
        self.histLayout = StackLayout(orientation='tb-lr')
        self.histLayout.size_hint = (1, None)
        self.histLayout.height = self.initLayoutHeight
        self.histLayout.bind(minimum_height=self.histLayout.setter('height'))
        self.scrollView.add_widget(self.histLayout)

        self.root = BoxLayout(orientation='vertical')
        self.root.add_widget(self.scrollView)
        self.root.add_widget(btnLayout)
        self.updateHistList()
        return self.root

    def _load_button(self, instance):
        filename = askopenfilename(
        )  # show an "Open" dialog box and return the path to the selected file
        if filename == '':
            return
        self.idx = self.loadSettings(filename)
        self.updateHistList()

    def _save_button(self, instance):
        filename = asksaveasfilename(
        )  # show an "Open" dialog box and return the path to the selected file
        if filename == '':
            return
        self.saveSettings(filename)
        self.updateHistList()

    def updateModels(self):
        self.models = []
        for key in self.all_settings:
            if self.all_settings[key] != 0:
                model = self.createModel(self.all_settings[key])
                self.models.append(model)

    def parseEvalString(self, string):
        varSt = 0
        varEnd = 0
        varReplacements = [
            'x', 'y', 'z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
            'k'
        ]
        vars = []
        remainingBrackets = True
        while remainingBrackets == True:
            remainingBrackets = False
            for idx, c in enumerate(string):
                if c == "{":
                    remainingBrackets = True
                    varSt = idx
                elif c == "}":
                    varEnd = idx
                    retVar = string[varSt + 1:varEnd]

                    if retVar in vars:
                        inserted = varReplacements[vars.index(retVar)]
                    else:
                        vars.append(retVar)
                        inserted = varReplacements[len(vars) - 1]

                    string = string[:varSt] + inserted + string[varEnd + 1:]
                    break

        if string == "":
            return None
        return (vars, "lambda " + ",".join(varReplacements[:len(vars)]) +
                " : " + string)

    def createModel(self, settings):
        eval_params1 = self.parseEvalString(settings[3])
        eval_params2 = self.parseEvalString(settings[5])
        if eval_params1 is None or eval_params2 is None:
            print("No evaluation parameters found. Histogram cancelled.")
            return

        #print(eval_params1)
        #print(eval_params2)

        model = hist_model()

        model.addTargetQuantity(settings[1])
        model.addParameter(0, eval_params1[0], eval(eval_params1[1]))
        model.addParameter(1, eval_params2[0], eval(eval_params2[1]))
        model.changeLog(settings[6])
        model.changeTitle(settings[0])
        model.changeLabels((settings[2], settings[4]))
        return model

    def addHistogram(self, instance):
        settings = histogram_screen()
        if settings == None:
            return
        elif None in settings or "" in settings:
            print("Error: at least one field was left blank.")
            return
        #['title', 'ID', 'label1', 'boolean1', 'label2', 'boolean2', True]
        #0           1       2           3           4       5           6

        self.all_settings[self.currID] = settings
        #displayed_settings[self.currID] = False
        self.currID += 1

        self.updateHistList()

    def createHistograms(self, instance):
        self.updateModels()
        hstats = hist_stats("results_with_data.csv")
        for model in self.models:
            hstats.addHistObject(model)

        hstats.execute()

    def editHist(self, key):
        setting = self.all_settings[key]
        new_setting = histogram_screen(*setting)
        if new_setting == None:
            new_setting = setting
        elif None in new_setting or "" in new_setting:
            print("Error: at least one field was left blank.")
            new_setting = setting
        self.all_settings[key] = new_setting
        #displayed_settings[key] = False
        self.updateHistList()

    def duplicateHist(self, key):
        print("CurrID: " + str(self.currID))
        self.all_settings[self.currID] = self.all_settings[key]
        self.currID += 1
        self.updateHistList()

    def updateHistList(self):
        #for widget in self.histLayout.children:
        #   print(widget.setting_id)
        #self.histLayout.remove_widget(widget)
        self.histLayout.clear_widgets()

        for key in range(self.currID + 1):
            if key in self.all_settings and self.all_settings[key] != 0:
                self.histLayout.add_widget(
                    HistDescriptor(self.all_settings[key][0],
                                   self,
                                   key,
                                   size_hint=(None, None)))

        self.histLayout.height = self.initLayoutHeight
        for child in self.histLayout.children:
            self.histLayout.height += child.height

        self.histLayout.bind(minimum_height=self.histLayout.setter('height'))

        self.saveSettings()
        print("HistLayout height: " + str(self.histLayout.height))

    def _on_resize(self, instance, w, h):
        self.root.size = (w, h)
        self.updateHistList()
Exemple #7
0
class FileBrowser(BoxLayout):
    def __init__(self, name, **kwargs):
        super().__init__(**kwargs)
        self.orientation = "vertical"
        # the below is now handled
        #self.size_hint = (None, None) #it would be nice if this could be optionally passed in with kwargs
        #self.size = (width, height)
        self.source = ""
        self.show_file_names = True
        self.selected_image = SpecialImage(None)
        self.image_width_hint = 1  # big one
        self.bind(
            size=partial(self.set_image_width_hint)
        )  # will need size or just height, depending on what's being used in the function

        Window.bind(mouse_pos=self._mouse_move)
        self.hover_count = None  # set this during populate

        self.menu_bar = MenuBar(name, almost_black, light_grey, light_blue)
        self.add_widget(self.menu_bar)

        self.scroll_body = ScrollView(bar_width='12dp',
                                      scroll_wheel_distance='20dp',
                                      scroll_type=['bars', 'content'],
                                      bar_inactive_color=[.7, .7, .7, .2
                                                          ])  ### add settings
        self.add_widget(self.scroll_body)
        self.scroll_body.bind(on_scroll_stop=self.on_scroll_stop_function)

        self.body = StackLayout(
            orientation="lr-tb",
            size_hint_x=0.99,
            size_hint_y=None,
            spacing=[1, 5]
        )  # the horizontal spacing isn't like the website, but they didn't have background colours like I do
        self.scroll_body.add_widget(self.body)
        self.body.bind(minimum_height=self.body.setter('height'))
        self.body.bind(height=self.scroll_test_function)

        self.bottom_bar = BottomBar(size_hint=(1, None),
                                    height='17dp',
                                    font_size='13dp',
                                    bold=True)
        self.add_widget(self.bottom_bar)

        self.draw()
        self.bind(pos=self.update_rectangles, size=self.update_rectangles)

        self.bind(size=self.test_function)

        self.size = self.size

        self.test_function()

    def scroll_test_function(self, *args):
        self.scroll_body.scroll_wheel_distance = kivy.metrics.dp(20) * math.log(
            self.body.height
        ) / 4  #< I think I stumbled across a combination here hat's quite effective (it goes between 35-45 depending on number of items, maybe more maybe less)

    def test_function(self, *args):
        self.body.padding = [3, 5, kivy.metrics.dp(12), 0]
        #self.body.padding = [(self.width - (self.width * 0.99)), 5, kivy.metrics.dp(12), 0] #

    def draw(self):
        self.canvas.before.clear()
        with self.canvas.before:
            Color(1, 1, 1, 1)
            self.rectangle = Rectangle(pos=self.pos, size=self.size)

    def update_rectangles(self, *args):
        self.rectangle.pos = self.pos
        self.rectangle.size = self.size

    def populate(self, source):  # a means of setting source
        self.source = source
        if self.body.children != []:
            self.body.clear_widgets()

        if source != "":
            direntry_iterator = os.scandir(self.source)
            for direntry in direntry_iterator:
                if direntry.is_file(
                ) == True:  # Could also check if they are images (.png, .jpg)
                    a_special_image = SpecialImage(direntry)
                    self.body.add_widget(a_special_image)

                    a_special_image.be_within = self
                    a_special_image.be_not_within = self.menu_bar
                    a_special_image.be_not_within_two = self.bottom_bar

                    # set sizes of specialimage. This might be a dumb way to do it, they only need to be called once, you see.
                    a_special_image.update_height()
                    a_special_image.update_image_height()
                    a_special_image.update_boxlayout()
                    a_special_image.update_text_textboxes()

    def set_selected(self, a_special_image):
        if a_special_image != self.selected_image:

            if self.selected_image != None:
                self.selected_image.selected = False
                self.selected_image.set_deselected()

            self.selected_image = a_special_image
            a_special_image.set_selected()
            self.bottom_bar.update_text(self.selected_image.file_name +
                                        " selected")

    def set_image_width_hint(self, *args):
        pass

    def _mouse_move(self, *args):
        if not self.get_root_window():
            return

        is_collide = self.collide_point(*self.to_widget(*args[1]))

        hovered = False
        for child in self.body.children:
            if child.hover == True and child.selected == False:  # since until you on_exit a special image, and it's selected, hover will be true (and we don't want to have a hand cursor)
                hovered = True
                break

        if is_collide == True and hovered == True:
            Window.set_system_cursor("hand")
        elif is_collide == False:
            pass
        else:
            Window.set_system_cursor("arrow")

    def on_scroll_stop_function(self, *args):
        for child in self.body.children:  # i.e. when on_scroll_stop is called for scroll_body, iterate through the special images and get them to check if they should be on_hovered or exits (this wouldn't be done otherwise: because a scroll isn't a mouse move)
            child._mouse_move(
                "hifdidl", Window.mouse_pos
            )  # THANK F**K THAT THIS WORKS. My doubt was that it would pass some bull shit Window.mouse_pos.
Exemple #8
0
class SentenceBuildMode(BaseMode):
    name = 'Sentence building test'

    def build_settings_page(self):
        bx = BoxLayout(orientation='vertical', padding=(20, 40), spacing=60)
        bt_home = Button(text='Home', size_hint=(1, 0.23), font_size=self._fs)
        bt_home.bind(on_release=self.go_to_home_page)
        lb = Label(text=self.name, font_size=self._fs)
        self.spiner_level = Spinner(text='Select difficulty level',
                                    values=('Low', 'Middle', 'High', 'All'),
                                    size_hint=(1, 0.4),
                                    font_size=self._fs)
        self.spiner_level.bind(text=self.set_level)
        bt_start = Button(text='Start', font_size=self._fs)
        bt_start.bind(on_release=self.go_to_main_page)
        return bx, zip((bx, ), ((bt_home, lb, self.spiner_level, bt_start), ))

    def build_main_page(self):
        bx = BoxLayout(orientation='vertical', padding=(20, 40), spacing=10)
        bx_switch_screens = BoxLayout(orientation='horizontal',
                                      size_hint=(1, 0.075),
                                      spacing=10)
        bx_main = BoxLayout(orientation='vertical',
                            padding=(0, 0, 0, 0),
                            spacing=30)
        bt_home = Button(text='Home', font_size=self._fs)
        bt_home.bind(on_release=self.go_to_home_page)
        bt_settings = Button(text='Back to settings', font_size=self._fs)
        bt_settings.bind(on_release=self.go_to_settings_page)
        self.label_counter = Label(font_size=self._fs // 2,
                                   halign="center",
                                   size_hint=(1, 0.1),
                                   markup=True)
        self.label_main = Label(font_size=self._fs,
                                halign="center",
                                size_hint=(1, 0.8))
        self.sl_answer = StackLayout(spacing=10)
        self.sl_build = StackLayout(spacing=10)
        self.bt_check = Button(text='Check', font_size=self._fs)
        self.bt_check.bind(on_release=self.next_command)
        return bx, zip(
            (bx_switch_screens, bx_main, bx),
            ((bt_home, bt_settings),
             (self.label_counter, self.label_main, self.sl_answer,
              self.sl_build, self.bt_check), (bx_switch_screens, bx_main)))

    def next_command(self, instance):
        command = instance.text
        if command == 'Check':
            if self.eng[:-1] == ' '.join(self.answer):
                self.answer_right_counter += 1
                self.label_main.text = 'Right :)'
            else:
                self.answer_wrong_counter += 1
                self.label_main.text = f'Wrong :(\nRight: {self.eng.capitalize()}'
            self.update_label_counter()
            instance.text = 'Next'
        else:
            self.update_main_page()
            instance.text = 'Check'

    def update_buttons(self, instance):
        if self.bt_check.text == 'Check':
            if instance.parent is self.sl_build:
                self.build_buttons(self.sl_answer, [instance.text])
                self.answer.append(instance.text)
                self.sl_build.remove_widget(instance)
            else:
                self.build_buttons(self.sl_build, [instance.text])
                self.answer.remove(instance.text)
                self.sl_answer.remove_widget(instance)

    def build_buttons(self, layout, variables):
        for variable in variables:
            button = Button(text=variable,
                            width=self._fs * 1.1 +
                            len(variable) * self._fs * 0.4,
                            size_hint=(None, 0.3),
                            font_size=self._fs)
            button.bind(on_release=self.update_buttons)
            layout.add_widget(button)

    def update_main_page(self):
        self.sl_answer.clear_widgets()
        self.sl_build.clear_widgets()
        self.answer = []
        detail = self.sentence_builder.build_sentence()
        self.eng = detail['eng']
        self.label_main.text = detail['rus']
        self.build_buttons(self.sl_build, detail['world_variants'])

    def update_label_counter(self):
        text = f'Right: [color=0000ff]{self.answer_right_counter}[/color]   Wrong: [color=0000ff]{self.answer_wrong_counter}[/color]'
        self.label_counter.text = text

    def init_mode(self):
        self.sentence_builder = SentenceBuilder(self.level)
        self.answer_right_counter = 0
        self.answer_wrong_counter = 0
        self.update_label_counter()
        self.update_main_page()
class Peta(Screen):
    def __init__(self, **kwargs):
        super(Peta, self).__init__(**kwargs)

        self.Navigation = Image(source="peta/lib/Navigation.png", pos_hint={"x": 0.74, "y": 0.80}, size_hint=(0.2, 0.1))
        self.legendBatasWil = Image(
            source="peta/lib/peta-batas-wilayah-keterangan.png", size_hint=(1, 1), pos_hint={"center_x": 0.5, "y": None}
        )
        self.legendJalan = Image(
            source="peta/lib/peta-jalan-keterangan.png", size_hint=(1, 1), pos_hint={"center_x": 0.5, "y": None}
        )
        self.legendAir = Image(
            source="peta/lib/peta-air-bersih-keterangan.png", size_hint=(1, 1), pos_hint={"center_x": 0.5, "y": None}
        )
        self.legendGorong = Image(
            source="peta/lib/peta-gorong-gorong-keterangan.png", size_hint=(1, 1), pos_hint={"center_x": 0.5, "y": None}
        )
        self.legendSarana = Image(
            source="peta/lib/peta-sarana-publik-keterangan.png",
            size_hint=(1, 2.5),
            pos_hint={"center_x": 0.5, "y": None},
        )

        ## TOGGLE BUTTON FUNCTION ##
        self.jaringanJalanButton = ToggleButton(
            background_normal="peta/lib/jalan.png",
            background_down="peta/lib/jalan-hit.png",
            state="normal",
            size_hint=(0.78, 0.25),
        )
        self.jaringanAirBersihButton = ToggleButton(
            background_normal="peta/lib/air-bersih.png",
            background_down="peta/lib/air-bersih-hit.png",
            state="normal",
            size_hint=(0.78, 0.25),
        )
        self.jaringanGegorongButton = ToggleButton(
            background_normal="peta/lib/gorong-gorong.png",
            background_down="peta/lib/gorong-gorong-hit.png",
            state="normal",
            size_hint=(0.78, 0.25),
        )
        self.saranaPublik = ToggleButton(
            background_normal="peta/lib/sarana-publik.png",
            background_down="peta/lib/sarana-publik-hit.png",
            state="normal",
            size_hint=(0.78, 0.25),
        )

        ## BUTTON FUNCTION ##
        self.zoomInButton = Button(
            background_normal="peta/lib/zoom-in.png",
            background_down="peta/lib/zoom-in-hit.png",
            state="normal",
            size_hint=(0.78, 0.25),
        )
        self.zoomOutButton = Button(
            background_normal="peta/lib/zoom-out.png",
            background_down="peta/lib/zoom-out-hit.png",
            state="normal",
            size_hint=(0.78, 0.25),
        )

        self.jaringanJalanButton.bind(on_release=self.jalan)
        self.jaringanAirBersihButton.bind(on_release=self.air)
        self.jaringanGegorongButton.bind(on_release=self.gorong)
        self.saranaPublik.bind(on_release=self.sarana)

        self.zoomInButton.bind(on_release=self.zoomIn)
        self.zoomOutButton.bind(on_release=self.zoomOut)

        ## Button Container ##
        self.bLayout = BoxLayout(
            size_hint=(None, None), orientation="horizontal", width=900, height=650, pos_hint={"y": 0.05, "x": 0.05}
        )
        self.bLayout.add_widget(self.jaringanJalanButton)
        self.bLayout.add_widget(self.jaringanAirBersihButton)
        self.bLayout.add_widget(self.jaringanGegorongButton)
        self.bLayout.add_widget(self.saranaPublik)
        self.bLayout.add_widget(self.zoomInButton)
        self.bLayout.add_widget(self.zoomOutButton)

        ## LEGEND CONTAINER ##
        self.legendContainer = StackLayout(
            orientation="lr-tb",
            size_hint=(None, None),
            minimum_width=366,
            minimum_height=672,
            width=366,
            height=105,
            pos_hint={"x": 0.75, "y": 0.70},
        )
        self.legendContainer.add_widget(self.legendBatasWil)

        self.mapScreen = MapLayout()

        # self.add_widget(self.mapScreen)
        self.add_widget(self.mapScreen.mapBound)
        self.add_widget(self.bLayout)
        self.add_widget(self.Navigation)
        self.add_widget(self.legendContainer)
        self.zoomIdx = 0

    ## Filter Button function method ##
    def jalan(self, b, **kwargs):
        if b.state == "down":
            self.mapScreen.addJalan()
            self.legendContainer.add_widget(self.legendJalan)

            # log
            gv.logger.log_button("view :" + "jalan")
        elif b.state == "normal":
            self.mapScreen.removeJalan()
            self.legendContainer.remove_widget(self.legendJalan)

            # log
            gv.logger.log_button("close :" + "jalan")

    def air(self, b, **kwargs):
        if b.state == "down":
            self.mapScreen.addAir()
            self.legendContainer.add_widget(self.legendAir)

            # log
            gv.logger.log_button("view :" + "air bersih")
        elif b.state == "normal":
            self.mapScreen.removeAir()
            self.legendContainer.remove_widget(self.legendAir)

            # log
            gv.logger.log_button("close :" + "air bersih")

    def gorong(self, b, **kwargs):
        if b.state == "down":
            self.mapScreen.addGorong()
            self.legendContainer.add_widget(self.legendGorong)

            # log
            gv.logger.log_button("view :" + "gorong-gorong")
        elif b.state == "normal":
            self.mapScreen.removeGorong()
            self.legendContainer.remove_widget(self.legendGorong)

            # log
            gv.logger.log_button("close :" + "gorong-gorong")

    def sarana(self, b, **kwargs):
        if b.state == "down":
            self.mapScreen.addSarana()
            self.legendContainer.add_widget(self.legendSarana)

            # log
            gv.logger.log_button("view :" + "sarana")
        elif b.state == "normal":
            self.mapScreen.removeSarana()
            self.legendContainer.remove_widget(self.legendSarana)

            # log
            gv.logger.log_button("close :" + "sarana")

    def reset(self):
        self.mapScreen.resetMap()
        self.legendContainer.clear_widgets()
        self.legendContainer.add_widget(self.legendBatasWil)
        self.jaringanJalanButton.state = "normal"
        self.jaringanAirBersihButton.state = "normal"
        self.jaringanGegorongButton.state = "normal"
        self.saranaPublik.state = "normal"

    def zoomIn(self, button=None, **args):
        self.mapScreen.zoomingIn()

    def zoomOut(self, button=None, **args):
        self.mapScreen.zoomingOut()
class Catalog(BoxLayout):
    """Tab that displays available courses and allows user to search for, see details about, and add courses to planner tab"""
    def __init__(self,sm,**kwargs):
        super(Catalog,self).__init__(**kwargs)
        self.orientation = 'vertical'
        self.sm = sm

        ## Search Bar ##
        self.search_bar = BoxLayout(size_hint=(1.0,0.05))
        self.search_text = TextInput(multiline=False,size_hint =(0.6,1.0))
        self.create_course_popup = Build_Course(self.sm)
        self.create_course_button = Button(text='Create a Course',size_hint=(0.2,1.0),on_press=self.create_course_popup.open_pop_up)        
        self.search_bar.add_widget(Label(text='Search',size_hint=(0.2,1.0)))
        self.search_bar.add_widget(self.search_text)
        self.search_bar.add_widget(self.create_course_button)

        ## Filter Buttons ##
        self.filter_bar = BoxLayout(size_hint=(1.0,0.05))        
        self.AHSE = ToggleButton(text='AHSE',size_hint=(0.25,1.0))
        self.ENGR = ToggleButton(text='ENGR',size_hint=(0.25,1.0))
        self.MTH = ToggleButton(text='MTH',size_hint=(0.25,1.0))
        self.SCI = ToggleButton(text='SCI',size_hint=(0.25,1.0))        
        self.filter_bar.add_widget(self.AHSE)
        self.filter_bar.add_widget(self.ENGR)
        self.filter_bar.add_widget(self.MTH)
        self.filter_bar.add_widget(self.SCI)

        ## Scrollview of Courses ##
        self.scrollview = ScrollView(size_hint=(1.0,0.9),size=(400,400),scroll_timeout=5)
        self.courses = StackLayout(spacing=5,size_hint_y=None)
        self.courses.bind(minimum_height=self.courses.setter('height'))
        self.scrollview.add_widget(self.courses)
        
        ## Add Widgets to Tab ##
        self.add_widget(self.search_bar)
        self.add_widget(self.filter_bar)
        self.add_widget(self.scrollview)

        Clock.schedule_interval(self.search_function,0.1)    

    def search_function(self,instance):
        """Allows user to search for courses by name, keyword, professor, or course code and filter courses by type"""
        query = self.search_text.text.lower()        
        searched_items = []
        filtered_items = []

        #fills up the temp list the first time the function is called (copy of list of all courses)
        if len(search_temp_list) == 0:
            for course_item in self.courses.children:
                search_temp_list.append(course_item)       
        
        #if the query is not empty, does term search first
        if query != "":                      
            for course_item in search_temp_list:                            
                if query == course_item.course.name.lower() or query == course_item.course.code or query == course_item.course.prof.lower():
                    searched_items.append(course_item)
                for keyword in course_item.course.keywords:
                    if query == keyword.lower():                        
                        searched_items.append(course_item)

        #if the query is empty, searched courses = all courses
        else:
            searched_items = search_temp_list
        
        #if none of the buttons are down, keep all searched courses
        if self.AHSE.state == 'normal' and self.ENGR.state == 'normal' and self.MTH.state == 'normal' and self.SCI.state == 'normal':
            filtered_items = searched_items

        #if a button is down, shows only courses in that category (holding multiple buttons shows more courses)
        else:                                
            if self.AHSE.state == 'down':
                for course_item in searched_items:                   
                    if course_item.course.credits['AHSE'] > 0:                                                  
                        filtered_items.append(course_item)
            if self.ENGR.state == 'down': 
                for course_item in searched_items:                      
                    if course_item.course.credits['ENGR'] > 0 and course_item not in filtered_items:                                                  
                        filtered_items.append(course_item)
            if self.MTH.state == 'down':                          
                for course_item in searched_items:
                    if course_item.course.credits['MTH'] > 0 and course_item not in filtered_items:                                                 
                        filtered_items.append(course_item)
            if self.SCI.state == 'down':
                for course_item in searched_items:                   
                    if course_item.course.credits['SCI'] > 0 and course_item not in filtered_items:                                             
                        filtered_items.append(course_item)

        if len(self.courses.children) != len(filtered_items):
            self.courses.clear_widgets()
            for course_item in filtered_items:
                self.courses.add_widget(course_item) 
class MainController(Widget):
	def __init__(self, window_size, **kwargs):
		super(MainController, self).__init__(**kwargs)
		self.name = "Main Controller"
		self.timer_label = TimerLabel(parent_size=window_size)
		self.current_day = date.today()
		self.current_day_display = Label(text=self.update_current_day_display(), markup=True)
		self.db = self.get_db_file()
		self.stack_current_limit = 0

		self.stack_src = []
		self.submit_button = Button()
		self.button_previous_day = Button()
		self.button_next_day = Button()
		self.button_up = Button()
		self.button_down = Button()
		self.row_form = TimeRowForm()
		self.button_report = Button()
		self.stack_layout = StackLayout()

		self.on_load()

	def get_db_file(self, today=date.today()):
		# Create today file if it doesn't exists
		self.current_day = today
		self.current_day_display.text = self.update_current_day_display()
		db_filename = "./db/" + str(self.current_day) + "_timelog.db"
		if not os.path.isfile(db_filename):
			timelog_file = open(db_filename, 'a')
			timelog_file.write("{}")
			timelog_file.close()

		return pickledb(db_filename, False)

	def update_current_day_display(self):
		return '[color=b2b2b2]' + str(self.current_day.strftime('%a %d %b %Y')) + '[/color]'

	def on_load(self):
		self.build_stack_src(self.db).bind_buttons().update_stack_view(len(self.stack_src))

	def build_stack_src(self, db):
		# sort db rows by key/timestamp
		row_set = set(db.getall())
		row_list = list(row_set)
		row_list.sort()
		for key in row_list:
			self.stack_src.append(db.get(key))
		return self

	def bind_buttons(self):
		self.submit_button.bind(on_press=self.add_new_row)
		self.button_previous_day.bind(on_press=self.load_previous_day)
		self.button_next_day.bind(on_press=self.load_next_day)
		self.button_up.bind(on_press=self.go_up)
		self.button_down.bind(on_press=self.go_down)
		self.button_report.bind(on_press=self.run_report)
		return self

	def load_previous_day(self, button):
		self.db = self.get_db_file(self.previous_day(self.current_day))
		self.clear_stack_src().build_stack_src(self.db).update_stack_view(len(self.stack_src))

	def load_next_day(self, button):
		if not self.current_day == date.today():
			self.db = self.get_db_file(self.next_day(self.current_day))
			self.clear_stack_src().build_stack_src(self.db).update_stack_view(len(self.stack_src))

	@staticmethod
	def previous_day(current_day=date.today()):
		return current_day - timedelta(days=1)

	@staticmethod
	def next_day(current_day=date.today()):
		return current_day + timedelta(days=1)

	def go_up(self, button):
		limit = self.stack_current_limit - 1 if self.stack_current_limit > ROWS_IN_STACK else self.stack_current_limit
		self.update_stack_view(limit)

	def go_down(self, button):
		limit = min(self.stack_current_limit + 1, len(self.stack_src))
		self.update_stack_view(limit)

	def run_report(self, button):
		TimeParser(self.db).parse_rows()

	def update_stack_src(self, project_title, project_description):
		row = self.build_row_dict(self.timer_label.time_label.text, self.timer_label.time_label.time, project_title,
			project_description)
		self.stack_src.append(row)
		return self

	def clear_stack_src(self):
		self.stack_src = []
		return self

	def insert_row(self, row):
		self.db.set(str(datetime.now()), row)
		self.db.dump()
		return self

	@staticmethod
	def build_row_dict(time_text, time, project_title, project_description):
		return {
			'time_text': time_text,
			'time': time,
			'project_title': project_title,
			'project_description': project_description
		}

	def add_new_row(self, button):
		project_title = self.row_form.text_project.text
		project_description = self.row_form.text_description.text
		self.insert_row(self.build_row_dict(self.timer_label.time_label.text, self.timer_label.time_label.time,
			project_title, project_description))
		self.update_stack_src(project_title, project_description)
		self.update_stack_view(len(self.stack_src))
		self.row_form.text_description.text = ''

	def get_style_stack_row(self, row):
		stack_row = self.display_row_label(self.stack_src[row])
		stack_row.size = 300, 30
		stack_row.pos_hint = {'right': 1, 'center_y': 0.5}
		return stack_row

	def update_stack_view(self, limit):
		self.stack_current_limit = limit
		stack_range = self.get_stack_range(limit)
		self.stack_layout.clear_widgets()
		for row in range(stack_range['start'], stack_range['limit']):
			stack_row = self.get_style_stack_row(row)
			self.stack_layout.add_widget(stack_row)

		return self

	@staticmethod
	def get_stack_range(limit):
		start = 0 if limit - ROWS_IN_STACK <= 0 else limit - ROWS_IN_STACK
		return {'start': start, 'limit': limit}

	@staticmethod
	def display_row_label(row_dict):
		return Label(text=row_dict['time_text'] + " - \n" + row_dict['project_title'] + " "
		+ row_dict['project_description'], size_hint=(1., .1), pos=(0, 0), markup=True)
		# return StackRowLabel(row_dict['time_text'], row_dict['project_title'] + row_dict['project_description'])

	@staticmethod
	def rgba2float(r, g, b, a=1.0):
		return float("{0:.2f}".format(r/256.0)), float("{0:.2f}".format(g/256.0)), float("{0:.2f}".format(b/256.0)), a

	@staticmethod
	def log_str(value):
		Logger.info(str(value))