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
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
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()
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()
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.
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))