class AreasBrowserDialog(BaseDialog): def __init__(self, parent, area_name, areas): super().__init__(parent, _("Select the area you mean"), _("&Select"), _("&Close")) self._areas = list(areas.items()) for id, (parent_name, data) in self._areas: self._areas_list.addItem( _("{area_name}, {parent_name}").format( area_name=area_name, parent_name=parent_name)) self._areas_list.setCurrentRow(0) def create_ui(self): areas_label = QLabel(_("Areas")) self.layout.addWidget(areas_label, 0, 0) self._areas_list = QListWidget(self) areas_label.setBuddy(self._areas_list) self._areas_list.currentRowChanged.connect(self.on_areas_list_listbox) self.layout.addWidget(self._areas_list, 1, 0) props_label = QLabel(_("Area properties")) self.layout.addWidget(props_label, 0, 1) self._area_props = QListWidget() props_label.setBuddy(self._area_props) self.layout.addWidget(self._area_props, 1, 1) def on_areas_list_listbox(self, index): self._area_props.clear() for key, value in self._areas[index][1][1].items(): self._area_props.addItem(f"{underscored_to_words(key)}: {value}") self._area_props.addItem( _("Area id: {}").format(self._areas[index][0])) @property def selected_area_id(self): return self._areas[self._areas_list.currentRow()][0]
class SimpleTodoPlus(QWidget): def __init__(self): QWidget.__init__(self) self.todo_list_widget = QListWidget(self) self.todo_list_widget.itemChanged.connect(self.onItemChanged) self.add_todo_btn = QPushButton("Add Todo", self) self.add_todo_btn.clicked.connect(self.add_todo_btn_clicked) self.remove_todo_btn = QPushButton("Remove Todo", self) self.remove_todo_btn.clicked.connect(self.remove_todo_btn_clicked) self.clear_todo_btn = QPushButton("Clear Todo", self) self.clear_todo_btn.clicked.connect(self.clear_todo_btn_clicked) vbox_layout = QVBoxLayout(self) vbox_layout.addWidget(self.todo_list_widget) hbox_layout = QHBoxLayout() hbox_layout.addWidget(self.add_todo_btn) hbox_layout.addWidget(self.remove_todo_btn) hbox_layout.addWidget(self.clear_todo_btn) vbox_layout.addLayout(hbox_layout) def add_todo_btn_clicked(self): item = QListWidgetItem(f"Todo {self.todo_list_widget.count() + 1}") item.setFlags(item.flags() | Qt.ItemIsUserCheckable | Qt.ItemIsEditable) item.setCheckState(Qt.Unchecked) self.todo_list_widget.addItem(item) self.todo_list_widget.edit(self.todo_list_widget.indexFromItem(item)) def remove_todo_btn_clicked(self): if self.todo_list_widget.count(): self.todo_list_widget.takeItem(self.todo_list_widget.currentRow()) def clear_todo_btn_clicked(self): self.todo_list_widget.clear() def onItemChanged(self, item): font = item.font() font.setStrikeOut(item.checkState() == Qt.Checked) item.setFont(font)
class LevelSelector(QDialog): def __init__(self, parent): super(LevelSelector, self).__init__(parent) self.setWindowTitle("Level Selector") self.setModal(True) self.selected_world = 1 self.selected_level = 1 self.object_set = 0 self.object_data_offset = 0x0 self.enemy_data_offset = 0x0 self.world_label = QLabel(parent=self, text="World") self.world_list = QListWidget(parent=self) self.world_list.addItems(WORLD_ITEMS) self.world_list.itemDoubleClicked.connect(self.on_ok) self.world_list.itemSelectionChanged.connect(self.on_world_click) self.level_label = QLabel(parent=self, text="Level") self.level_list = QListWidget(parent=self) self.level_list.itemDoubleClicked.connect(self.on_ok) self.level_list.itemSelectionChanged.connect(self.on_level_click) self.enemy_data_label = QLabel(parent=self, text="Enemy Data") self.enemy_data_spinner = Spinner(parent=self) self.object_data_label = QLabel(parent=self, text="Object Data") self.object_data_spinner = Spinner(self) self.object_set_label = QLabel(parent=self, text="Object Set") self.object_set_dropdown = QComboBox(self) self.object_set_dropdown.addItems(OBJECT_SET_ITEMS) self.button_ok = QPushButton("Ok", self) self.button_ok.clicked.connect(self.on_ok) self.button_cancel = QPushButton("Cancel", self) self.button_cancel.clicked.connect(self.close) self.window_layout = QGridLayout(self) self.window_layout.addWidget(self.world_label, 0, 0) self.window_layout.addWidget(self.level_label, 0, 1) self.window_layout.addWidget(self.world_list, 1, 0) self.window_layout.addWidget(self.level_list, 1, 1) self.window_layout.addWidget(self.enemy_data_label, 2, 0) self.window_layout.addWidget(self.object_data_label, 2, 1) self.window_layout.addWidget(self.enemy_data_spinner, 3, 0) self.window_layout.addWidget(self.object_data_spinner, 3, 1) self.window_layout.addWidget(self.object_set_label, 4, 0) self.window_layout.addWidget(self.object_set_dropdown, 4, 1) self.window_layout.addWidget(self.button_ok, 5, 0) self.window_layout.addWidget(self.button_cancel, 5, 1) self.setLayout(self.window_layout) self.world_list.setCurrentRow(1) # select Level 1-1 self.on_world_click() def keyPressEvent(self, key_event: QKeyEvent): if key_event.key() == Qt.Key_Escape: self.reject() def on_world_click(self): index = self.world_list.currentRow() assert index >= 0 self.level_list.clear() # skip first meaningless item for level in Level.offsets[1:]: if level.game_world == index: if level.name: self.level_list.addItem(level.name) if self.level_list.count(): self.level_list.setCurrentRow(0) self.on_level_click() def on_level_click(self): index = self.level_list.currentRow() assert index >= 0 self.selected_world = self.world_list.currentRow() self.selected_level = index + 1 level_is_overworld = self.selected_world == OVERWORLD_MAPS_INDEX if level_is_overworld: level_array_offset = self.selected_level else: level_array_offset = Level.world_indexes[self.selected_world] + self.selected_level object_data_for_lvl = Level.offsets[level_array_offset].rom_level_offset if not level_is_overworld: object_data_for_lvl -= Level.HEADER_LENGTH self.object_data_spinner.setValue(object_data_for_lvl) if not level_is_overworld: enemy_data_for_lvl = Level.offsets[level_array_offset].enemy_offset else: enemy_data_for_lvl = 0 if enemy_data_for_lvl > 0: # data in look up table is off by one, since workshop ignores the first byte enemy_data_for_lvl -= 1 self.enemy_data_spinner.setValue(enemy_data_for_lvl) self.enemy_data_spinner.setEnabled(not level_is_overworld) # if self.selected_world >= WORLD_1_INDEX: object_set_index = Level.offsets[level_array_offset].real_obj_set self.object_set_dropdown.setCurrentIndex(object_set_index) self.button_ok.setDisabled(self.selected_world == 0) def on_ok(self, _): if self.selected_world == 0: return self.object_set = self.object_set_dropdown.currentIndex() self.object_data_offset = self.object_data_spinner.value() # skip the first byte, because it seems useless self.enemy_data_offset = self.enemy_data_spinner.value() + 1 self.accept() def closeEvent(self, _close_event: QCloseEvent): self.reject()
class ObjectsBrowserWindow(QWidget): def __init__(self, parent, title, person, unsorted_objects, autoshow=True, progress_indicator=None): super().__init__(None) act = QAction("close", self) act.triggered.connect(self._do_close) act.setShortcut(QKeySequence("escape")) self.addAction(act) layout = QGridLayout() bar = QMenuBar(self) self._object_actions = bar.addMenu(_("Object actions")) property_actions = bar.addMenu(_("Property actions")) self._create_item(property_actions, _("Copy property value"), "ctrl+c", self.on_copypropvalue_selected) self._create_item(property_actions, _("Copy property name"), "alt+c", self.on_copypropname_selected) self._create_item(property_actions, _("Copy property name and value"), "ctrl+alt+c", self.on_copypropline_selected) objects_label = QLabel(_("Objects"), self) layout.addWidget(objects_label, 0, 0) self._objects_list = QListWidget(self) self._objects_list.setAccessibleName(objects_label.text()) self._objects_list.setContextMenuPolicy(Qt.CustomContextMenu) self._objects_list.currentRowChanged.connect(self.on_objects_listbox) self._objects_list.customContextMenuRequested.connect( self._on_objects_list_menu) objects_label.setBuddy(self._objects_list) layout.addWidget(self._objects_list, 1, 0) props_label = QLabel(_("Object properties"), self) layout.addWidget(props_label, 0, 1) self._props = QTreeWidget(self) self._props.setAccessibleName(props_label.text()) props_label.setBuddy(self._props) layout.addWidget(self._props, 1, 1) goto_button = QPushButton(_("Go to"), self) goto_button.setDefault(True) goto_button.clicked.connect(self.on_goto_clicked) self._objects_list.itemActivated.connect(goto_button.click) layout.addWidget(goto_button, 2, 0) close_button = QPushButton(_("Close"), self) close_button.clicked.connect(self.close) layout.addWidget(close_button, 2, 1) self.setLayout(layout) self.setWindowTitle(title + _(" ({num_objects} objects shown)").format( num_objects=len(unsorted_objects))) self._person = person self._autoshow = autoshow self._progress_indicator = progress_indicator self._all_actions = [] for member in object_actions.__dict__.values(): if inspect.isclass(member) and issubclass(member, ObjectAction): self._all_actions.append(member) self._objects_list.setCurrentRow(0) self._sorter = ObjectsSorter(unsorted_objects, person) self._sorter.objects_sorted.connect(self._objects_sorted) self._sorter.start() def _objects_sorted(self, data): objects, item_data = data self._objects = objects for (desc, dist, rel_bearing) in item_data: self._objects_list.addItem( _("{object}: distance {distance} meters, {rel_bearing}"). format(object=desc, distance=dist, rel_bearing=rel_bearing)) if self._progress_indicator: self._progress_indicator.hide() self._progress_indicator.deleteLater() if self._autoshow: self._objects_list.setCurrentRow(0) self.show() def _create_item(self, menu, label, shortcut, callback): action = menu.addAction(label) action.triggered.connect(callback) action.setShortcut(QKeySequence(shortcut)) def on_goto_clicked(self, evt): self._person.move_to(self.selected_object[2], force=True) def _do_close(self): self.close() self.destroy() windows = QApplication.topLevelWidgets() other_browsers = [ w for w in windows if w is not self and isinstance(w, self.__class__) and w.isVisible() ] if other_browsers: other_browsers[-1].activateWindow() else: menu_service().ensure_key_capturer_focus() def on_objects_listbox(self, current_index): selected = self._objects[current_index][1] self._props.clear() common_item = QTreeWidgetItem([_("Common properties")]) specific_item = QTreeWidgetItem([_("Specific properties")]) other_item = QTreeWidgetItem([ _("Other properties - they can not be searched and are not processed in any way" ) ]) common_fields = list( EntityMetadata.for_discriminator("OSMEntity").fields.keys()) selected_metadata = EntityMetadata.for_discriminator( selected.discriminator) known_fields = selected_metadata.all_fields formatted_values = {} for field_name in selected.defined_field_names(): raw_value = selected.value_of_field(field_name) if field_name not in known_fields: # By the mere fact that the other fields have no defined order, we can add them there without losing anything. other_item.addChild( QTreeWidgetItem([ "%s: %s" % (underscored_to_words(field_name), raw_value) ])) else: value_str = "%s: %s" % ( underscored_to_words(field_name), format_field_value(raw_value, known_fields[field_name].type_name)) formatted_values[field_name] = value_str for common in common_fields: del known_fields[common] common_item.addChild(QTreeWidgetItem([formatted_values[common]])) for specific in known_fields.keys( ): # Because we deleted the common ones in the loop before this, only the specific remain. if specific in formatted_values: specific_item.addChild( QTreeWidgetItem([formatted_values[specific]])) # We add the entity ID mainly for debugging purposes, and that's the reason why it is added the last and so special in the first place. common_item.addChild( QTreeWidgetItem([_("Object id: {}").format(selected.id)])) self._props.addTopLevelItem(common_item) if specific_item.childCount() > 0: self._props.addTopLevelItem(specific_item) self._props.expandItem(specific_item) #self._props.setCurrentItem(specific_item) # Breaks focus behavior slightly, but annoingly enough. if other_item.childCount() > 0: self._props.addTopLevelItem(other_item) self._object_actions.clear() for action in self._all_actions: if action.executable(selected): mi = self._object_actions.addAction(action.label) mi.triggered.connect( action_execution_handler_factory(action, selected, self)) @property def selected_object(self): return self._objects[self._objects_list.currentRow()] def on_copypropvalue_selected(self, evt): prop = self._props.currentItem().text(0) val = prop.split(": ", 1)[1] QApplication.clipboard().setText(val) def on_copypropname_selected(self, evt): prop = self._props.currentItem().text(0) name = prop.split(": ", 1)[0] QApplication.clipboard().setText(name) def on_copypropline_selected(self, evt): prop = self._props.currentItem().text(0) QApplication.clipboard().setText(prop) def _on_objects_list_menu(self, point): self._object_actions.exec_(point)
class Form(QWidget): """""" def __init__( self, parent: QApplication = None, *, title: str = "wooo", width: int = 400, height: int = 600, ) -> None: """Constructor""" super().__init__(parent) mixer.init() # initializethe pygame mixer self.assets_path: str = os.path.join( os.path.dirname(os.path.abspath(__file__)), 'assets') if parent is not None: self.parent = parent """ self.parent.iconbitmap() """ horiz_pos = 100 # from left of screen vertic_pos = 200 # from top of screen self.height = height self.width = width self.title = title self.setGeometry(horiz_pos, vertic_pos, self.width, self.height) # QtCore.QRect(x, y, w, h) self.setWindowTitle(self.title) self.setWindowIcon(QtGui.QIcon(self.assets_path + '/icons/melody.ico')) self.init_vol: int = 70 self.paused: bool = False self.muted: bool = False self.playing: bool = False self.current_song: Opt[str] = None self.selected_song_num: Opt[int] = None self.playlist: List[str] = [] self._init_ui() self.show() def _init_ui(self) -> None: self.layout = QVBoxLayout() self._menubar() self._create_rightframe() self._create_middleframe() self._create_leftframe() self._create_bottomframe() self.edit = QLineEdit("Write my name here") self.layout.addWidget(self.edit) self.greet_button = QPushButton("Show Greetings") self.greet_button.clicked.connect(self.greetings) self.layout.addWidget(self.greet_button) self._statusbar() self.setLayout(self.layout) def _menubar(self) -> None: menubar = QMenuBar() self.layout.addWidget(menubar) fileMenu = menubar.addMenu('File') fileMenu.addAction('Open', self.browse_file) fileMenu.addSeparator() fileMenu.addAction('Exit', self.close) helpMenu = menubar.addMenu('Help') helpMenu.addSeparator() helpMenu.addAction('About Us', self.about_us) # toolbar = self.addToolBar('Exit') # toolbar.addAction(self.play_music) def _statusbar(self) -> None: self.statusbar = QStatusBar() self.layout.addWidget(self.statusbar) self.statusbar.showMessage('Welcome', timeout=10_000) # status_bar = statusbar_parent.addPermanentWidget(statusbar_parent, stretch=True) def _create_rightframe(self) -> None: self.rightframe = QVBoxLayout() self.layout.addLayout(self.rightframe) self.filelabel = QLabel(text='Lets make some noise!') self.rightframe.addWidget(self.filelabel) self.lengthlabel = QLabel(text='Total Length : --:--') self.rightframe.addWidget(self.lengthlabel) self.currenttimelabel = QLabel(text='Current Time : --:--') self.rightframe.addWidget(self.currenttimelabel) def _create_leftframe(self) -> None: self.leftframe = QVBoxLayout() self.layout.addLayout(self.leftframe) self.playlistbox = QListWidget(self) self.playlistbox.setToolTip('''PlayListBox: Select song from list to play. Use browse or delete buttons to change playlist''' ) self.leftframe.addWidget(self.playlistbox) self.browse_button = QPushButton("Browse") self.browse_button.clicked.connect(self.browse_file) self.leftframe.addWidget(self.browse_button) self.delete_button = QPushButton("Delete") self.delete_button.clicked.connect(self.del_song) self.leftframe.addWidget(self.delete_button) def _create_middleframe(self) -> None: self.middleframe = QVBoxLayout() self.layout.addLayout(self.middleframe) self.play_button = QPushButton("Play") self.play_button.clicked.connect(self.play_music) play_icon = QtGui.QIcon( QtGui.QPixmap(self.assets_path + '/icons/play.png')) # play_icon.addPixmap(QtGui.QPixmap(self.assets_path + '/icons/play.png')) self.play_button.setIcon(play_icon) # self.play_button.setIconSize(QtCore.QSize(100, 100)) self.middleframe.addWidget(self.play_button) self.stop_button = QPushButton("Stop") self.stop_button.clicked.connect(self.stop_music) stop_icon = QtGui.QIcon( QtGui.QPixmap(self.assets_path + '/icons/stop.png')) self.stop_button.setIcon(stop_icon) # self.stop_button.setIconSize(QtCore.QSize(100, 100)) self.middleframe.addWidget(self.stop_button) self.pause_button = QPushButton("Pause") self.pause_button.clicked.connect(self.pause_music) pause_icon = QtGui.QIcon( QtGui.QPixmap(self.assets_path + '/icons/pause.png')) self.pause_button.setIcon(pause_icon) # self.pause_button.setIconSize(QtCore.QSize(100, 100)) self.middleframe.addWidget(self.pause_button) def _create_bottomframe(self) -> None: self.bottomframe = QVBoxLayout() self.layout.addLayout(self.bottomframe) self.volume_button = QPushButton("Mute") self.mute_icon = QtGui.QIcon( QtGui.QPixmap(self.assets_path + '/icons/mute.png')) self.volume_icon = QtGui.QIcon( QtGui.QPixmap(self.assets_path + '/icons/volume.png')) self.volume_button.setIcon(self.volume_icon) # self.volume_button.setIconSize(QtCore.QSize(100, 100)) self.volume_button.clicked.connect(self.mute_music) self.bottomframe.addWidget(self.volume_button) self.rewind_button = QPushButton("Rewind") rewind_icon = QtGui.QIcon( QtGui.QPixmap(self.assets_path + '/icons/rewind.png')) self.rewind_button.setIcon(rewind_icon) # self.volume_button.setIconSize(QtCore.QSize(100, 100)) self.play_button.clicked.connect(self.rewind_music) self.bottomframe.addWidget(self.rewind_button) self.vol_scale = QSlider(QtCore.Qt.Horizontal) self.vol_scale.setMinimum(0) self.vol_scale.setMaximum(100) # self.vol_scale.setTickPosition(QSlider.TicksBelow) # self.vol_scale.setTickInterval(5) self.vol_scale.setValue(self.init_vol) self.vol_scale.valueChanged.connect(self.set_vol) self.bottomframe.addWidget(self.vol_scale) mixer.music.set_volume(self.vol_scale.value()) # exitAction = QtGui.QAction('Exit', self) # exitAction.setShortcut('Ctrl+Q') # exitAction.setStatusTip('Exit application') # exitAction.triggered.connect(self.close) def set_vol(self) -> None: self.vol_from_slider: int = self.vol_scale.value() volume_percent: float = self.vol_from_slider / 100 mixer.music.set_volume(volume_percent) # from 0 to 1 def mute_music(self) -> None: if self.muted: # Unmute the music mixer.music.set_volume(self.vol_pre_mute / 100) self.volume_button.setIcon(self.volume_icon) self.vol_scale.setValue( self.vol_pre_mute) # (self.vol_from_slider) self.muted = False else: # mute the music self.vol_pre_mute: int = self.vol_scale.value() mixer.music.set_volume(0) self.volume_button.setIcon(self.mute_icon) self.vol_scale.setValue(0) self.muted = True def greetings(self) -> None: text = self.edit.text() print('Contents of QLineEdit widget: {}'.format(text)) self.statusbar.showMessage(text, timeout=2_000) def about_us(self) -> None: text = self.edit.text() print('Contents of QLineEdit widget: {}'.format(text)) def browse_file(self) -> None: get_filename_path: Tuple[str, str] = QFileDialog.getOpenFileName( self, # if cancelled, returns ("", "") "Open Sound File", self.assets_path, "Sound Files (*.wav *.mp3 *.ogg)") print(get_filename_path) filename_path = get_filename_path[0] if filename_path: self.add_to_playlist(filename_path) mixer.music.queue(filename_path) def add_to_playlist(self, filepath: str) -> None: filename = os.path.basename(filepath) index = 0 # print(self.playlist) # QListWidgetItem(self.playlistbox).setText(filename) # last_added = self.playlistbox.insertItem(index, filename) # .addItems([filenames]) self.playlist.insert(index, filepath) index += 1 def del_song(self) -> None: self.get_selected_song_num() # update self.selected_song_num if self.selected_song_num is not None: # if a song is selected print(self.selected_song_num) if self.playlist[ self. selected_song_num] == self.current_song: # if song is currently playing self.stop_music() # stop it self.playlistbox.takeItem( self.selected_song_num ) # remove the song from the box, note returns the song object self.playlist.pop(self.selected_song_num) # and playlist # self.selected_song_num remains same, so will play/del next song? self.reset_song() self.statusbar.showMessage("Song removed from playlist", timeout=1_000) self.selected_song_num = None # reset self.selected_song_num""" def get_selected_song_num(self) -> None: if self.playlistbox.count() > 0: selected_song_from_box = self.playlistbox.currentItem( ) # get current item if selected_song_from_box: self.selected_song: str = selected_song_from_box.text( ) # get items text self.selected_song_num = self.playlistbox.currentRow() else: self.statusbar.showMessage("Choose a file from the playlist", timeout=2_000) else: self.statusbar.showMessage("No files loaded to playlist", timeout=2_000) def stop_music(self) -> None: if self.playing: self.playing = False self.current_song = None mixer.music.stop() self.statusbar.showMessage("Music Stopped", timeout=5_000) def pause_music(self) -> None: if self.playing: self.paused = True mixer.music.pause() self.statusbar.showMessage("Music paused", timeout=0) def rewind_music(self) -> None: if self.playing: self.stop_music() time.sleep(0.5) self.play_music() self.statusbar.showMessage("Music Rewound to start", timeout=1_000) def reset_song(self) -> None: self.current_song = None self.filelabel.setText("") self.lengthlabel.setText('Total Length : --:00') self.currenttimelabel.setText("Current Time : --:--") # self.statusbar.showMessage("", timeout=0) def show_details(self, play_song: str) -> None: self.filelabel.setText("Playing" + ' - ' + os.path.basename(play_song)) file_data = os.path.splitext(play_song) if file_data[1] == '.mp3': audio = MP3(play_song) total_length = audio.info.length elif file_data[1] == '.wav': a = mixer.Sound(play_song) total_length = a.get_length() else: try: a = mixer.Sound(play_song) total_length = a.get_length() except Exception as e: print(e) self.current_song_lenth = total_length mins, secs = divmod(total_length, 60) # returns (time//60, remainder) mins = round(mins) secs = round(secs) timeformat = '{:02d}:{:02d}'.format(mins, secs) self.lengthlabel.setText("Total Length" + ' - ' + timeformat) self.t1 = threading.Thread(target=self.start_count, args=(total_length, )) self.t1.start() def start_count(self, total_time: int) -> None: """""" current_time = 0 while current_time <= total_time and mixer.music.get_busy( ): # music.get_busy() -> Returns False when stopped if self.paused: continue # if paused, infinite loop (don't count) else: mins, secs = divmod(current_time, 60) mins = round(mins) secs = round(secs) timeformat = '{:02d}:{:02d}'.format(mins, secs) self.currenttimelabel.setText("Current Time" + ' - ' + timeformat) time.sleep(1) current_time += 1 def play_music(self) -> None: '''if not playing: play, if playing and paused, unpause''' self.get_selected_song_num() # update self.selected_song_num if self.selected_song_num is not None and self.playlist: play_it: Opt[str] = self.playlist[self.selected_song_num] else: play_it = None if not self.playing and play_it: # if not yet playing, play selected song try: self.stop_music() time.sleep(0.5) mixer.music.load(play_it) mixer.music.play() except Exception as e: # messagebox.showerror('File not found, or unknown file type. Please check again.') if DEBUG: print(e) else: self.playing = True self.current_song = play_it self.show_details(play_it) self.statusbar.showMessage("Playing music" + ' - ' + os.path.basename(play_it)) elif self.playing and self.paused: # if paused, resume mixer.music.unpause() # self.statusbar.showMessage("Playing music" + ' - ' + os.path.basename(play_it)) self.statusbar.showMessage("Music Resumed", timeout=1_000) self.paused = False elif self.playing and not self.paused: if play_it == self.current_song and play_it is not None: # if already playing song, do nothing self.statusbar.showMessage( "Playing music" + ' - ' + os.path.basename(play_it), timeout=0) # TODO timout current song len else: # if different song selected, retry self.playing = False self.play_music() def close(self) -> None: try: self.stop_music() QApplication.closeAllWindows() except Exception as e: sys.exit(1) if DEBUG: print(e) else: print('App closed')
class ReportListComponent(QGroupBox): currentAnalysisChanged = Signal(object) def __init__(self): super().__init__() self.setTitle("Rapports disponibles") main_layout = QVBoxLayout(self) self._list = QListWidget() self._list.setContextMenuPolicy(Qt.CustomContextMenu) self._list.customContextMenuRequested.connect(self._showItemMenu) self._list.currentRowChanged.connect(self._currentAnalysisChanged) self._list.itemDoubleClicked.connect(lambda item: self._renameItem()) self._analysis = None main_layout.addWidget(self._list) def reset(self, analysis: Analysis): self._current_analysis_file = None self._analysis = analysis self._list.clear() for analysis in HistoryManager.analysisList(): item = QListWidgetItem("%s (%s)" % (analysis["name"], analysis["date"])) item.setData(Qt.UserRole, analysis["file"]) item.setData(Qt.UserRole + 1, analysis["name"]) self._list.addItem(item) self._list.setCurrentRow(0) @Slot(int) def _currentAnalysisChanged(self, row: int): if row < 0: return new_analysis_file = self._list.item(row).data(Qt.UserRole) if self._current_analysis_file == new_analysis_file: return if self._current_analysis_file is None: self._current_analysis_file = new_analysis_file return self._current_analysis_file = new_analysis_file self._analysis = HistoryManager.loadAnalysis(new_analysis_file) self.currentAnalysisChanged.emit(self._analysis) @Slot(QPoint) def _showItemMenu(self, pos: QPoint): globalPos = self._list.mapToGlobal(pos) actions_menu = QMenu() actions_menu.addAction("Renommer", self._renameItem) actions_menu.addAction("Supprimer", self._eraseItem) actions_menu.exec_(globalPos) @Slot() def _renameItem(self): item = self._list.currentItem() input_dialog = QInputDialog(self.parentWidget(), Qt.WindowSystemMenuHint | Qt.WindowTitleHint | Qt.WindowCloseButtonHint) font = input_dialog.font() font.setPixelSize(16) input_dialog.setFont(font) input_dialog.setMinimumWidth(300) input_dialog.setInputMode(QInputDialog.TextInput) input_dialog.setWindowTitle("Renommer l'analyse") input_dialog.setLabelText("Nouveau nom pour '%s' :" % item.data(Qt.UserRole + 1)) input_dialog.setTextValue(item.data(Qt.UserRole + 1)) input_dialog.setOkButtonText("OK") input_dialog.setCancelButtonText("Annuler") if not input_dialog.exec_(): return new_name = input_dialog.textValue() if self._analysis is None: return if new_name == self._analysis.parameters().name(): return regexp = QRegExp("^[a-zA-Z0-9_-#éèêëàîï ]{5,30}$") if not regexp.exactMatch(new_name): QMessageBox.warning(self, "Nouveau nom invalide", "Caractères autorisés : alphanumérique, espace, #, - et _ avec une longueur maximale de 30 caractères") return self._analysis.parameters().setName(new_name) HistoryManager.renameAnalysis(item.data(Qt.UserRole), self._analysis) current_row = self._list.currentRow() self.reset(self._analysis) self._list.setCurrentRow(current_row) self.currentAnalysisChanged.emit(self._analysis) @Slot() def _eraseItem(self): item = self._list.currentItem() message_box = QMessageBox() message_box.setIcon(QMessageBox.Warning) message_box.setWindowTitle("Supprimer une analyse ?") message_box.setText("Voulez vous vraiment supprimer l'analyse '%s' de façon définitive ?" % item.data(Qt.UserRole + 1)) message_box.setInformativeText("Assurez vous d'avoir exportez toutes les données dont vous avez besoin.") message_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) ret = message_box.exec_() if ret == QMessageBox.Yes: HistoryManager.deleteAnalysis(item.data(Qt.UserRole)) self._list.takeItem(self._list.currentRow()) if self._list.currentRow() == -1: self.currentAnalysisChanged.emit(None)
class AreaSelectionDialog(BaseDialog): def __init__(self, parent): super().__init__(parent, _("Select an area"), _("&Select"), _("&Exit"), cancel_button_column=2, buttons_to_new_row=False) self._initialize_areas() def _initialize_areas(self): if has_api_connectivity(): available = get_areas() cache_area_names(available) else: available = get_local_area_infos() self.request_button.setDisabled(True) collator = QCollator() available.sort(key=functools.cmp_to_key( lambda a, b: collator.compare(a["name"], b["name"]))) self._area_ids = [a["osm_id"] for a in available] self._area_names = [a["name"] for a in available] self._fill_areas(available) def create_ui(self): areas_label = QLabel(_("&Available areas")) self.layout.addWidget(areas_label, 0, 0, 1, 3) self._areas = QListWidget() self._areas.setAccessibleName(_("Available areas")) self.layout.addWidget(self._areas, 1, 0, 1, 3) areas_label.setBuddy(self._areas) self.request_button = QPushButton(_("&Request a new area")) self.layout.addWidget(self.request_button, 2, 1) self.request_button.clicked.connect(self.on_request_clicked) def _fill_areas(self, areas): for area in areas: area["created_at"] = rfc_3339_to_local_string(area["created_at"]) area["updated_at"] = rfc_3339_to_local_string(area["updated_at"]) area["db_size"] = format_size(area["db_size"]) self._areas.addItem( _("{name}: {state}, last updated {updated_at}, file size {db_size}, created {created_at}" ).format(**area)) @property def selected_map(self): return self._area_ids[self._areas.currentRow()] @property def selected_map_name(self): return self._area_names[self._areas.currentRow()] def on_request_clicked(self): name, ok = QInputDialog.getText( self, _("Enter the name of the requested area"), _("Area name requested")) if not ok or not name: return self._searched_name = name self._searcher = AreaCandidatesSearcher(name) self._searcher.results_ready.connect(self._on_area_candidates) self._searcher.rate_limited.connect(self._on_rate_limited) self._indicator = SearchIndicator() self._indicator.show() self._searcher.start() def _on_rate_limited(self): self._indicator.hide() QMessageBox.warning( self, _("Query limit reached"), _("You've reached the query limit for the overpass API, which is used for area searches. Try to request the area in a few minutes." )) def _on_area_candidates(self, candidates): self._indicator.hide() if not candidates: QMessageBox.warning( self, _("Area not found"), _("The area with name {name} does not correspond to any OSM areas." ).format(name=self._searched_name)) return if len(candidates) == 1: area_id = next(iter(candidates.keys())) log.info("Only one candidate with an admin level of %s and id %s.", next(iter(candidates.values()))[1]["admin_level"], area_id) else: dialog = AreasBrowserDialog(self, area_name=self._searched_name, areas=candidates) res = dialog.exec_() if res == QDialog.DialogCode.Accepted: area_id = dialog.selected_area_id else: return reply = request_area_creation(area_id, self._searched_name) if reply and isinstance( reply, dict) and "state" in reply and reply["state"] == "Creating": QMessageBox.information( self, _("Success"), _("The area creation request has been sent successfully. The area will become updated in a few minutes." )) self._areas.clear() self._initialize_areas() elif reply and isinstance( reply, dict) and "state" in reply and reply["state"] in { "Creating", "Updated", "ApplyingChanges", "GettingChanges" }: QMessageBox.information( self, _("Success"), _("The area creation request has already been sent.")) else: QMessageBox.warning( self, text= _("The area creation request failed. Response from server: {reply}" ).format(reply=reply), title=_("Failure"))
class appConfigScreen(QWidget): keybind_signal = Signal("QObject") update_signal = Signal("QObject") def __init__(self, appName, parms={}): super().__init__() self.dbg = False self.level = 'user' exePath = sys.argv[0] if os.path.islink(sys.argv[0]): exePath = os.path.realpath(sys.argv[0]) baseDir = os.path.abspath(os.path.dirname(exePath)) os.chdir(baseDir) self.rsrc = "%s/rsrc" % baseDir self.parms = parms self.modules = [] self.appName = appName self.textDomain = self.appName.lower().replace(" ", "_") gettext.textdomain('{}'.format(self.textDomain)) _ = gettext.gettext self.wikiPage = appName self.background = "%s/background.png" % self.rsrc self.banner = "%s/%s" % (self.rsrc, "banner.png") self.last_index = 0 self.stacks = {0: {'name': _("Options"), 'icon': 'icon'}} self.appConfig = appConfig() self.config = {} #def init def _debug(self, msg): if self.dbg: print("%s" % msg) #def _debug def setWiki(self, url): self.wikiPage = url #def setWiki def setTextDomain(self, textDomain): self.textDomain = textDomain #def setTextDomain def setRsrcPath(self, rsrc): if os.path.isdir(rsrc): self.rsrc = rsrc else: self._debug("%s doesn't exists") self._debug("Resources dir: %s" % self.rsrc) #def setRsrcPath def setIcon(self, icon): self._debug("Icon: %s" % icon) icn = icon if not os.path.isfile(icon): sw_ko = False self._debug("%s not found" % icon) if QtGui.QIcon.fromTheme(icon): icn = QtGui.QIcon.fromTheme(icon) if icn.name() == "": self._debug("%s not present at theme" % icon) sw_ko = True else: self._debug("%s found at theme" % icon) self._debug("Name: %s found at theme" % icn.name()) elif os.path.isfile("%s/%s" % (self.rsrc, icon)): icon = "%s/%s" % (self.rsrc, icon) self._debug("%s found at rsrc folder" % icon) icn = QtGui.QIcon(icon) else: icn = QtGui.QIcon.fromTheme("application-menu") self._debug("Icon not found at %s" % self.rsrc) if sw_ko: icn = QtGui.QIcon.fromTheme("application-menu") self._debug("Icon %s not found at theme" % icon) self.setWindowIcon(icn) #def setIcon def setBanner(self, banner): if not os.path.isfile(banner): if os.path.isfile("%s/%s" % (self.rsrc, banner)): banner = "%s/%s" % (self.rsrc, banner) else: banner = "" self._debug("Banner not found at %s" % self.rsrc) self.banner = banner #def setBanner def setBackgroundImage(self, background): if not os.path.isfile(background): if os.path.isfile("%s/%s" % (self.rsrc, background)): background = "%s/%s" % (self.rsrc, background) else: background = "" self._debug("Background not found at %s" % self.rsrc) self.background = background #def setBanner def setConfig(self, confDirs, confFile): self.appConfig.set_baseDirs(confDirs) self.appConfig.set_configFile(confFile) #def setConfig(self,confDirs,confFile): def _searchWiki(self): url = "" baseUrl = "https://wiki.edu.gva.es/lliurex/tiki-index.php?page=" if self.wikiPage.startswith("http"): url = self.wikiPage else: url = "%s%s" % (baseUrl, self.wikiPage) #try: # req=Request(url) # content=urlopen(req).read() #except: # self._debug("Wiki not found at %s"%url) # url="" return (url) #def _searchWiki def _get_default_config(self): data = {} data = self.appConfig.getConfig('system') self.level = data['system'].get('config', 'user') if self.level != 'system': data = self.appConfig.getConfig(self.level) level = data[self.level].get('config', 'n4d') if level != self.level: self.level = level data = self.appConfig.getConfig(level) data[self.level]['config'] = self.level self._debug("Read level from config: %s" % self.level) return (data) #def _get_default_config(self,level): def getConfig(self, level=None, exclude=[]): data = self._get_default_config() if not level: level = self.level if level != 'system': data = {} data = self.appConfig.getConfig(level, exclude) self.config = data.copy() self._debug("Read level from config: %s" % level) return (data) #def getConfig(self,level): def Show(self): if self.config == {}: self.getConfig() self.setStyleSheet(self._define_css()) if os.path.isdir("stacks"): for mod in os.listdir("stacks"): if mod.endswith(".py"): mod_name = mod.split(".")[0] mod_import = "from stacks.%s import *" % mod_name try: exec(mod_import) self.modules.append(mod_name) self._debug("Load stack %s" % mod_name) except Exception as e: # self._debug("Unable to load %s: %s"%(mod_name,e)) print("Unable to load %s: %s" % (mod_name, e)) idx = 1 for mod_name in self.modules: try: mod = eval("%s(self)" % mod_name) except Exception as e: # self._debug("Import failed for %s: %s"%(mod_name,e)) print("Import failed for %s: %s" % (mod_name, e)) continue if type(mod.index) == type(0): if mod.index > 0: idx = mod.index try: if mod.enabled == False: continue except: pass while idx in self.stacks.keys(): idx += 1 self._debug("New idx for %s: %s" % (mod_name, idx)) if 'parm' in mod.__dict__.keys(): try: if mod.parm: self._debug("Setting parms for %s" % mod_name) self._debug("self.parms['%s']" % mod.parm) mod.apply_parms(eval("self.parms['%s']" % mod.parm)) except Exception as e: self._debug("Failed to pass parm %s to %s: %s" % (mod.parm, mod_name, e)) try: mod.setTextDomain(self.textDomain) except Exception as e: print("Can't set textdomain for %s: %s" % (mod_name, e)) try: mod.setAppConfig(self.appConfig) except Exception as e: print("Can't set appConfig for %s: %s" % (mod_name, e)) try: if mod.visible == False: visible = False else: visible = True except: visible = True self.stacks[idx] = { 'name': mod.description, 'icon': mod.icon, 'tooltip': mod.tooltip, 'module': mod, 'visible': visible } try: mod.message.connect(self._show_message) except: pass self._render_gui() return (False) def _render_gui(self): self.getConfig() box = QGridLayout() img_banner = QLabel() if os.path.isfile(self.banner): img = QtGui.QPixmap(self.banner) img_banner.setPixmap(img) img_banner.setAlignment(Qt.AlignCenter) img_banner.setObjectName("banner") box.addWidget(img_banner, 0, 0, 1, 2) self.lst_options = QListWidget() self.stk_widget = QStackedWidget() idx = 0 if len(self.stacks) > 2: l_panel = self._left_panel() box.addWidget(l_panel, 1, 0, 1, 1, Qt.Alignment(1) | Qt.AlignTop) # self.stk_widget.setCurrentIndex(0) else: idx = 1 # self.stk_widget.setCurrentIndex(1) r_panel = self._right_panel() self.stk_widget.setCurrentIndex(idx) #self.gotoStack(idx,"") box.addWidget(r_panel, 1, 1, 1, 1) self.setLayout(box) self.show() #def _render_gui def _left_panel(self): panel = QWidget() box = QVBoxLayout() btn_menu = QPushButton() icn = QtGui.QIcon.fromTheme("application-menu") btn_menu.setIcon(icn) btn_menu.setIconSize(QSize(BTN_MENU_SIZE, BTN_MENU_SIZE)) btn_menu.setMaximumWidth(BTN_MENU_SIZE) btn_menu.setMaximumHeight(BTN_MENU_SIZE) btn_menu.setToolTip(_("Options")) btn_menu.setObjectName("menuButton") # box.addWidget(btn_menu,Qt.Alignment(1)) indexes = [] for index, option in self.stacks.items(): idx = index lst_widget = QListWidgetItem() lst_widget.setText(option['name']) mod = option.get('module', None) if mod: try: idx = mod.index except: pass if idx > 0: icn = QtGui.QIcon.fromTheme(option['icon']) lst_widget.setIcon(icn) if 'tooltip' in option.keys(): lst_widget.setToolTip(option['tooltip']) while idx in indexes: idx += 1 indexes.append(index) self.stacks[idx]['widget'] = lst_widget orderedStacks = {} orderedStacks[0] = self.stacks[0] #self.lst_options.addItem(orderedStacks[0]['widget']) cont = 0 indexes.sort() for index in indexes: if index: orderedStacks[cont] = self.stacks[index].copy() if self.stacks[index].get('visible', True) == True: self.lst_options.addItem(orderedStacks[cont]['widget']) cont += 1 self.stacks = orderedStacks.copy() box.addWidget(self.lst_options) self.lst_options.currentRowChanged.connect(self._show_stack) self.lst_options.setCurrentIndex(QModelIndex()) self.last_index = None panel.setLayout(box) self.resize(self.size().width() + box.sizeHint().width(), self.size().height() + box.sizeHint().height() / 2) self.lst_options.setFixedSize( self.lst_options.sizeHintForColumn(0) + 2 * (self.lst_options.frameWidth() + 15), self.height() ) #self.lst_options.sizeHintForRow(0) * self.lst_options.count() + 2 * (self.lst_options.frameWidth()+15)) return (panel) #def _left_panel def _right_panel(self): panel = QWidget() box = QVBoxLayout() idx = 0 text = [ _("Welcome to the configuration of ") + self.appName, _("From here you can:<br>") ] orderIdx = list(self.stacks.keys()) for idx in orderIdx: data = self.stacks[idx] stack = data.get('module', None) if stack: stack.setLevel(self.level) stack.setConfig(self.config) stack._load_screen() if self.stacks[idx].get('visible', True) == True: text.append( " * <a href=\"appconf://%s\"><span style=\"font-weight:bold;text-decoration:none\">%s</span></a>" % (idx + 1, stack.menu_description)) try: self.stk_widget.insertWidget(idx, stack) except: self.stk_widget.insertWidget(idx, stack.init_stack()) stack = QWidget() stack.setObjectName("panel") s_box = QVBoxLayout() lbl_txt = QLabel() lbl_txt.setTextFormat(Qt.RichText) lbl_txt.setText("<br>".join(text)) lbl_txt.linkActivated.connect(self._linkStack) lbl_txt.setObjectName("desc") lbl_txt.setAlignment(Qt.AlignTop) lbl_txt.setTextInteractionFlags(Qt.TextBrowserInteraction) s_box.addWidget(lbl_txt, 1) #Get wiki page url = self._searchWiki() if url: desc = _("Wiki help") lbl_wiki = QLabel( "<a href=\"%s\"><span style=\"text-align: right;\">%s</span></a>" % (url, desc)) lbl_wiki.setOpenExternalLinks(True) s_box.addWidget(lbl_wiki, 0, Qt.AlignRight) stack.setLayout(s_box) self.stk_widget.insertWidget(0, stack) #self.stacks[0]['module']=stack box.addWidget(self.stk_widget) panel.setLayout(box) return (panel) #def _right_panel def _linkStack(self, *args): stack = args[0].split('/')[-1] self.gotoStack(int(stack), '') def gotoStack(self, idx, parms): self._show_stack(idx=idx, parms=parms) def _show_stack(self, item=None, idx=None, parms=None): if (self.last_index == abs(self.lst_options.currentRow()) and (idx == self.last_index or isinstance(item, int))): # or self.last_index==None)): return if isinstance( self.stacks.get(self.last_index, {}).get('module', None), appConfigStack) == True: if self.stacks[self.last_index]['module'].getChanges(): if self._save_changes(self.stacks[self.last_index] ['module']) == QMessageBox.Cancel: self.lst_options.setCurrentRow(self.last_index) return else: self.stacks[self.last_index]['module'].setChanged(False) self.stacks[self.last_index]['module'].initScreen() if self.stacks[self.last_index]['module'].refresh: self._debug("Refresh config") self.getConfig() else: self._debug(self.stacks.get(self.last_index, {}).get('module')) self.last_index = 0 if isinstance(idx, int) == False: idx = self.lst_options.currentRow() + 1 self.last_index = idx - 1 try: self.stacks[idx]['module'].setConfig(self.config) except: pass self.stk_widget.setCurrentIndex(idx) # self.statusBar.hide() if parms: self.stacks[idx]['module'].setParms(parms) #def _show_stack def closeEvent(self, event): try: if self.stacks[self.last_index]['module'].getChanges(): if self._save_changes(self.stacks[self.last_index] ['module']) == QMessageBox.Cancel: event.ignore() except: pass def _show_message(self, msg, status=None): return # self.statusBar.setText(msg) # if status: # self.statusBar.show(status) # else: # self.statusBar.show(status) #def _show_message def _save_changes(self, module): dia = QMessageBox( QMessageBox.Question, _("Apply changes"), _("There're changes not saved at current screen.\nDiscard them and continue?" ), QMessageBox.Discard | QMessageBox.Cancel, self) return (dia.exec_()) def _define_css(self): css = """ QPushButton{ padding: 6px; margin:6px; font: 14px Roboto; } QPushButton#menu:active{ background:none; } QStatusBar{ background:red; color:white; font: 14px Roboto bold; } QLabel{ padding:6px; margin:6px; } #dlgLabel{ font:12px Roboto; margin:0px; border:0px; padding:3px; } QLineEdit{ border:0px; border-bottom:1px solid grey; padding:1px; font:14px Roboto; margin-right:6px; } #panel{ background-image:url("%s"); background-size:stretch; background-repeat:no-repeat; background-position:center; } #desc{ background-color:rgba(255,255,255,0.7); color:black; } #banner{ padding:1px; margin:1px; border:0px; } """ % self.background return (css)
class ForceActuatorWidget(QWidget): """ Abstract class for widget and graphics display of selected M1M3 values. Children classes must implement updateValues(data) method. Parameters ---------- m1m3 : `SALComm` SALComm instance to communicate with SAL. userWidget : `QWidget` Widget to be displayed on left from value selection. Its content shall be update in updateValues(data) method. Methods ------- updateValues(data) Must be defined in every child. This is called when selection is changed or when new data become available. If data parameter is None, then no data has been received for selected read topic. """ def __init__(self, m1m3, userWidget): super().__init__() self.m1m3 = m1m3 self.fieldDataIndex = None self.layout = QHBoxLayout() self.plotLayout = QVBoxLayout() self.selectionLayout = QVBoxLayout() self.detailsLayout = QFormLayout() self.filterLayout = QHBoxLayout() self.layout.addLayout(self.plotLayout) self.layout.addLayout(self.selectionLayout) self.selectionLayout.addLayout(self.detailsLayout) self.selectionLayout.addWidget(QLabel("Filter Data")) self.selectionLayout.addLayout(self.filterLayout) self.setLayout(self.layout) self.selectedActuatorIdLabel = QLabel("") self.selectedActuatorValueLabel = QLabel("") self.selectedActuatorWarningLabel = QLabel("") self.lastUpdatedLabel = TimeDeltaLabel() self.topicList = QListWidget() self.topicList.setFixedWidth(256) self.topicList.currentRowChanged.connect(self.currentTopicChanged) self.topics = Topics() for topic in self.topics.topics: self.topicList.addItem(topic.name) self.fieldList = QListWidget() self.fieldList.setFixedWidth(256) self.fieldList.currentRowChanged.connect(self.currentFieldChanged) self.plotLayout.addWidget(userWidget) self.detailsLayout.addRow(QLabel("Selected Actuator Details"), QLabel("")) self.detailsLayout.addRow(QLabel("Actuator Id"), self.selectedActuatorIdLabel) self.detailsLayout.addRow(QLabel("Actuator Value"), self.selectedActuatorValueLabel) self.detailsLayout.addRow(QLabel("Actuator Warning"), self.selectedActuatorWarningLabel) self.detailsLayout.addRow(QLabel("Last Updated"), self.lastUpdatedLabel) self.filterLayout.addWidget(self.topicList) self.filterLayout.addWidget(self.fieldList) self.topicList.setCurrentRow(0) @Slot(int) def currentTopicChanged(self, topicIndex): if topicIndex < 0: self._setUnknown() return self.fieldList.clear() for field in self.topics.topics[topicIndex].fields: self.fieldList.addItem(field[0]) fieldIndex = self.topics.topics[topicIndex].selectedField if fieldIndex < 0: self._setUnknown() return self.fieldList.setCurrentRow(fieldIndex) self._changeField(topicIndex, fieldIndex) @Slot(int) def currentFieldChanged(self, fieldIndex): topicIndex = self.topicList.currentRow() if topicIndex < 0 or fieldIndex < 0: self._setUnknown() return self._changeField(topicIndex, fieldIndex) self.topics.topics[topicIndex].selectedField = fieldIndex def _setUnknown(self): self.lastUpdatedLabel.setUnknown() def updateSelectedActuator(self, s): """ Called from childrens to update currently selected actuator display. Parameters ---------- s : `map` Contains id (selected actuator ID), data (selected actuator current value) and warning (boolean, true if value is in warning). """ if s is None: self.selectedActuatorIdLabel.setText("not selected") self.selectedActuatorValueLabel.setText("") self.selectedActuatorWarningLabel.setText("") return self.selectedActuatorIdLabel.setText(str(s.id)) self.selectedActuatorValueLabel.setText(str(s.data)) setWarningLabel(self.selectedActuatorWarningLabel, s.warning) def _changeField(self, topicIndex, fieldIndex): """ Redraw actuators with new values. """ topic = self.topics.topics[topicIndex] field = topic.fields[fieldIndex] self.fieldGetter = field[1] self.fieldDataIndex = field[2]() try: self.topics.changeTopic(topicIndex, self.dataChanged, self.m1m3) data = getattr(self.m1m3.remote, topic.getTopic()).get() self.dataChanged(data) except RuntimeError as err: print("ForceActuatorWidget._changeField", err) pass @Slot(map) def dataChanged(self, data): """ Called when selected data are updated. Parameters ---------- """ self.updateValues(data) if data is None: self._setUnknown() else: self.lastUpdatedLabel.setTime(data.timestamp)
class EventViewer(QDialog): def __init__(self, alid=0, db=0, mode=0): super().__init__() self.font = mainfont self.resize(1000, 600) layout = QGridLayout() self.layout = layout self.buttext = [] self.dictViewer = QWidget() self.linedits = {} self.last_eid = 0 self.photo_ids = 0 self.QLABELM_TYPE = type(QLabelM()) self.listWidget = QListWidget() self.listWidget.itemDoubleClicked.connect(self.doubleClick) self.listWidget.itemSelectionChanged.connect(self.itemChanged) self.setFont(mainfont) self.db = db self.alid = alid self.event_ids = db.get_all_event_ids(self.alid) self.photo_gallery = QLabel() #self.photo_gallery.setAlignment(Qt.AlignCenter) #self.photo_gallery.setPixmap(QPixmap('f.png').scaled(400,400,Qt.KeepAspectRatio)) print(self.event_ids) if self.event_ids: for eid in self.event_ids: event = db.get_event_data(self.alid, eid) print(event) if event: text = '' text = event['event_head'] if type(event) == type( {}) and 'event_head' in event.keys( ) and event['event_head'] != '' else text item = QListWidgetItem(text) if type(event) == type({}) and 'eid' in event.keys(): item.setWhatsThis(str(event['eid'])) self.listWidget.addItem(item) if self.listWidget.count(): self.listWidget.setCurrentRow(0) self.mode = mode def openMenu(position): # Создание PopupMenu menu = QMenu() if mode > 0: addAction = menu.addAction('Добавить событие') #menu.addSeparator() editAction = menu.addAction('Переименовать событие') #menu.addSeparator() delAction = menu.addAction('Удалить событие') delAllAction = menu.addAction('Удалить все события') menu.addSeparator() else: addAction, editAction, delAction, delAllAction = QAction( ), QAction(), QAction(), QAction() quitAction = menu.addAction('Выход') action = menu.exec_(self.mapToGlobal(position)) # Привязка событий к Actions if action == addAction: text, ok = QInputDialog().getText(self, "Название события", "Ввкдите название события:", QLineEdit.Normal, '') if ok: text = 'Новое событие' if text == '' else text res = self.db.add_event({ 'alid': self.alid, 'event_head': text }) if len(res) == 1: event = res[0] text = event['event_head'] if type(event) == type( {} ) and 'event_head' in event.keys( ) and event['event_head'] != '' else 'Новое событие' item = QListWidgetItem(text) item.setWhatsThis(str(event['eid'])) self.listWidget.addItem(item) db.events.save() #self.changed = True self.event_ids = db.get_all_event_ids(self.alid) if action == editAction: eid = self.listWidget.currentItem() if eid is not None: eid = self.listWidget.currentItem().whatsThis() last_name = self.db.get_event_data(self.alid, eid)['event_head'] text, ok = QInputDialog().getText( self, "Название события", "Ввкдите новое название события:", QLineEdit.Normal, str(last_name)) if ok: event = self.db.edit_event({ 'alid': self.alid, 'eid': eid, 'event_head': text }) self.listWidget.currentItem().setText(text) self.db.events.save() if event: event = self.db.get_event_data(alid, eid) #b = layout.takeAt(1) self.dictViewer.close() #b.widget().deleteLater() self.dictViewer = DictViewer( event, 1, self.db.events.invizible_fields, self.db.events.editable_fields) self.layout.addWidget(self.dictViewer, 0, 2) self.event_ids = db.get_all_event_ids(self.alid) if action == delAction: res = QMessageBox.question( self, 'ВНИМАНИЕ!!!', "Вы действительно хотите удалить событие?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if res == QMessageBox.Yes: eid = self.listWidget.currentItem() if eid is not None: eid = self.listWidget.currentItem().whatsThis() self.db.del_event({'alid': self.alid, 'eid': eid}) self.listWidget.takeItem(self.listWidget.currentRow()) self.db.events.save() #self.changed = True self.event_ids = db.get_all_event_ids(self.alid) if action == delAllAction: res = QMessageBox.question( self, 'ВНИМАНИЕ!!!', "Вы действительно хотите удалить все события?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if res == QMessageBox.Yes: if self.db.del_all_events(int(self.alid)): self.listWidget.clear() self.db.events.save() #self.changed = True self.event_ids = db.get_all_event_ids(self.alid) #db.photos.save() if action == quitAction: self.accept() self.listWidget.setContextMenuPolicy(Qt.CustomContextMenu) self.listWidget.customContextMenuRequested.connect(openMenu) layout.addWidget(self.listWidget, 0, 0, 1, 1) layout.addWidget(self.photo_gallery, 0, 1, 1, 1) self.setLayout(layout) def doubleClick(self, item): if item is not None: eid = item.whatsThis() text = self.db.get_event_path(self.alid, eid) print(eid) def closeEvent(self, event): if self.last_eid: self.check_and_save(self.last_eid) def itemChanged(self): if self.last_eid: self.check_and_save(self.last_eid) eid = self.listWidget.currentItem() if eid is not None: eid = self.listWidget.currentItem().whatsThis() event = self.db.get_event_data(self.alid, eid) if self.photo_gallery: self.photo_gallery.close() # Работаем с фотками self.photo_gallery = QLabelM(self.alid, self.db, 1, eid) self.layout.addWidget(self.photo_gallery, 0, 1, int(self.db.events.nkeys / 2), 1) # Заполняем данными self.fill_event_data(event) print(event) self.last_eid = eid self.setFocus() #print('item changed = ' + str(eid)) def keyPressEvent(self, event): if event.key() == Qt.Key_Left: #print('left pressed') if type(self.photo_gallery) == self.QLABELM_TYPE: self.photo_gallery.prev_show() if event.key() == Qt.Key_Right: #print('right pressed') if type(self.photo_gallery) == self.QLABELM_TYPE: self.photo_gallery.next_show() #super().keyPressEvent(event) def fill_event_data(self, event): if type(event) != type({}): return self.dictViewer.close() self.dictViewer = DictViewer(event, 1, self.db.events.invizible_fields, self.db.events.editable_fields) self.layout.addWidget(self.dictViewer, 0, 2, 1, 1) def check_and_save(self, eid): print('check_and_save' + str(eid)) changes = self.dictViewer.get_changes() event = self.db.get_event_data(self.alid, eid) if self.photo_gallery.changed: changes['photo_ids'] = self.photo_gallery.photo_ids print(changes) if len(changes) > 0 and type(event) == type({}): print('event changes saved') event.update(changes) self.db.edit_event(event) self.db.events.save()
class LevelSelector(QDialog): def __init__(self, parent): super(LevelSelector, self).__init__(parent) self.setWindowTitle("Level Selector") self.setModal(True) self.level_name = "" self.object_set = 0 self.object_data_offset = 0x0 self.enemy_data_offset = 0x0 self.world_label = QLabel(parent=self, text="World") self.world_list = QListWidget(parent=self) self.world_list.addItems(WORLD_ITEMS) self.world_list.itemDoubleClicked.connect(self.on_ok) self.world_list.itemSelectionChanged.connect(self.on_world_click) self.level_label = QLabel(parent=self, text="Level") self.level_list = QListWidget(parent=self) self.level_list.itemDoubleClicked.connect(self.on_ok) self.level_list.itemSelectionChanged.connect(self.on_level_click) self.enemy_data_label = QLabel(parent=self, text="Enemy Data") self.enemy_data_spinner = Spinner(parent=self) self.object_data_label = QLabel(parent=self, text="Object Data") self.object_data_spinner = Spinner(self) self.object_set_label = QLabel(parent=self, text="Object Set") self.object_set_dropdown = QComboBox(self) self.object_set_dropdown.addItems(OBJECT_SET_ITEMS) self.button_ok = QPushButton("Ok", self) self.button_ok.clicked.connect(self.on_ok) self.button_cancel = QPushButton("Cancel", self) self.button_cancel.clicked.connect(self.close) stock_level_widget = QWidget() stock_level_layout = QGridLayout(stock_level_widget) stock_level_layout.addWidget(self.world_label, 0, 0) stock_level_layout.addWidget(self.level_label, 0, 1) stock_level_layout.addWidget(self.world_list, 1, 0) stock_level_layout.addWidget(self.level_list, 1, 1) self.source_selector = QTabWidget() self.source_selector.addTab(stock_level_widget, "Stock Levels") for world_number in range(WORLD_COUNT): world_number += 1 world_map_select = WorldMapLevelSelect(world_number) world_map_select.level_selected.connect( self._on_level_selected_via_world_map) self.source_selector.addTab(world_map_select, f"World {world_number}") data_layout = QGridLayout() data_layout.addWidget(self.enemy_data_label, 0, 0) data_layout.addWidget(self.object_data_label, 0, 1) data_layout.addWidget(self.enemy_data_spinner, 1, 0) data_layout.addWidget(self.object_data_spinner, 1, 1) data_layout.addWidget(self.object_set_label, 2, 0) data_layout.addWidget(self.object_set_dropdown, 2, 1) data_layout.addWidget(self.button_ok, 3, 0) data_layout.addWidget(self.button_cancel, 3, 1) main_layout = QVBoxLayout() main_layout.addWidget(self.source_selector) main_layout.addLayout(data_layout) self.setLayout(main_layout) self.world_list.setCurrentRow(1) # select Level 1-1 self.on_world_click() def keyPressEvent(self, key_event: QKeyEvent): if key_event.key() == Qt.Key_Escape: self.reject() def on_world_click(self): index = self.world_list.currentRow() assert index >= 0 self.level_list.clear() # skip first meaningless item for level in Level.offsets[1:]: if level.game_world == index: if level.name: self.level_list.addItem(level.name) if self.level_list.count(): self.level_list.setCurrentRow(0) self.on_level_click() def on_level_click(self): index = self.level_list.currentRow() assert index >= 0 level_is_overworld = self.world_list.currentRow( ) == OVERWORLD_MAPS_INDEX if level_is_overworld: level_array_offset = index + 1 self.level_name = "" else: level_array_offset = Level.world_indexes[ self.world_list.currentRow()] + index + 1 self.level_name = f"World {self.world_list.currentRow()}, " self.level_name += f"{Level.offsets[level_array_offset].name}" object_data_for_lvl = Level.offsets[ level_array_offset].rom_level_offset if not level_is_overworld: object_data_for_lvl -= Level.HEADER_LENGTH if not level_is_overworld: enemy_data_for_lvl = Level.offsets[level_array_offset].enemy_offset else: enemy_data_for_lvl = 0 if enemy_data_for_lvl > 0: # data in look up table is off by one, since workshop ignores the first byte enemy_data_for_lvl -= 1 self.enemy_data_spinner.setEnabled(not level_is_overworld) # if self.world_list.currentRow() >= WORLD_1_INDEX: object_set_index = Level.offsets[level_array_offset].real_obj_set self.button_ok.setDisabled(level_is_overworld) self._fill_in_data(object_set_index, object_data_for_lvl, enemy_data_for_lvl) def _fill_in_data(self, object_set: int, layout_address: int, enemy_address: int): self.object_set_dropdown.setCurrentIndex(object_set) self.object_data_spinner.setValue(layout_address) self.enemy_data_spinner.setValue(enemy_address) def _on_level_selected_via_world_map(self, level_name: str, object_set: int, layout_address: int, enemy_address: int): self.level_name = level_name self._fill_in_data(object_set, layout_address, enemy_address) self.on_ok() def on_ok(self, _=None): self.object_set = self.object_set_dropdown.currentIndex() self.object_data_offset = self.object_data_spinner.value() # skip the first byte, because it seems useless self.enemy_data_offset = self.enemy_data_spinner.value() + 1 self.accept() def closeEvent(self, _close_event: QCloseEvent): self.reject()
class DiscoverUi(QWidget): def __init__(self): super().__init__() self.discover = Discover() self.devices = self.discover.devices self.listWidget = None self.treeWidget = None self.initUI() self.mac_addr = None def initDevices(self): for device in self.devices: newItem = QListWidgetItem() newItem.setText(device.name + " (" + device.address + ")") self.listWidget.addItem(newItem) def initUI(self): self.listWidget = QListWidget() self.initDevices() self.treeWidget = QTreeWidget() self.treeWidget.itemPressed.connect(self.onItemPressed) self.treeWidget.setColumnCount(4) self.treeWidget.setColumnWidth(0, 250) self.treeWidget.setColumnWidth(1, 300) self.treeWidget.setColumnWidth(2, 300) self.treeWidget.setColumnWidth(3, 150) self.treeWidget.setHeaderLabels(["Service", "Service UUID", "Characteristic UUID", "Characteristic Property"]) btn = QPushButton("Read Services") btn.clicked.connect(self.onPushButton) groupDevices = QGroupBox("Devices") groupDevices.setMaximumWidth(300) vbox = QVBoxLayout() vbox.addWidget(self.listWidget) vbox.addWidget(btn) groupDevices.setLayout(vbox) self.btnR = QPushButton("Read") self.btnR.clicked.connect(self.onReadButton) self.btnW = QPushButton("Write") self.btnW.clicked.connect(self.onWriteButton) self.lneI = QLineEdit() self.chkN = QCheckBox("Notify") self.chkN.toggled.connect(self.onNotifyCheck) hbox = QHBoxLayout() hbox.addWidget(self.btnR) hbox.addWidget(self.btnW) hbox.addWidget(self.lneI) hbox.addWidget(self.chkN) groupProperty = QGroupBox("Property") #groupProperty.setLayout(vbox) groupProperty.setLayout(hbox) groupServices = QGroupBox("Services") vbox = QVBoxLayout() vbox.addWidget(self.treeWidget) vbox.addWidget(groupProperty) groupServices.setLayout(vbox) hbox = QHBoxLayout() hbox.addWidget(groupDevices) hbox.addWidget(groupServices) self.setLayout(hbox) self.setGeometry(300, 300, 800, 600) self.setWindowTitle('BLE Discover') self.show() def onPushButton(self): try: self.mac_addr = self.devices[self.listWidget.currentRow()].address self.discover.getServices(self.mac_addr) except: print("Could not get GATT services.") else: svcs = self.discover.svcs for serviceKey, serviceValue in svcs.services.items(): item = QTreeWidgetItem(None, [serviceValue.description, serviceValue.uuid]) for characteristic in serviceValue.characteristics: for property in characteristic.properties: child = QTreeWidgetItem(["", "", characteristic.uuid, property]) item.addChild(child) self.treeWidget.addTopLevelItem(item) def onReadButton(self): byteArray = self.discover.readGattChar(self.mac_addr, self.chosenUuid) text = ''.join('{:02x}'.format(x) for x in byteArray) self.lneI.setText(text) def onWriteButton(self): text = self.lneI.text() print("onWriteButton") self.discover.writeGattChar(self.mac_addr, self.chosenUuid, bytes.fromhex(text)) def notifyCallback(self, sender, data): text = ''.join('{:02x}'.format(x) for x in data) self.lneI.textChanged.emit(text) def onNotifyCheck(self, checked): if checked: print("onNotifyCheck") self.discover.startNotify(self.mac_addr, self.chosenUuid, self.notifyCallback) else: print("onNotifyCheck else") def onItemPressed(self, item, column): if item.child(0) is None: print(item) print(item.text(2)) print(item.text(3)) self.chosenUuid = item.text(2) property = item.text(3) if property == "read": self.btnR.setEnabled(True) self.btnW.setEnabled(False) self.lneI.setEnabled(False) self.chkN.setEnabled(False) elif property == "write": self.btnR.setEnabled(True) self.btnW.setEnabled(True) self.lneI.setEnabled(True) self.chkN.setEnabled(False) elif property == "notify": self.btnR.setEnabled(False) self.btnW.setEnabled(False) self.lneI.setEnabled(False) self.chkN.setEnabled(True)
class AlbumViewer(QDialog): def __init__(self, pid=0, db=0, mode=0): super().__init__() self.font = mainfont self.resize(500, self.height()) layout = QGridLayout() self.layout = layout self.buttext = [] self.last_alid = 0 self.listWidget = QListWidget() self.listWidget.itemDoubleClicked.connect(self.doubleClick) self.listWidget.itemSelectionChanged.connect(self.itemChanged) self.dictAlbum = QWidget() self.DICTVIEWERTYPE = type(DictViewer()) self.setFont(mainfont) self.db = db self.pid = pid self.album_ids = db.get_all_albums_ids() print(self.album_ids) if self.album_ids: for alid in self.album_ids: album = db.get_album_data(alid) print(album) self.dictAlbum = DictViewer(album, 1, self.db.albums.invizible_fields, self.db.albums.editable_fields) if album: text = '' text = album['title'] if type(album) == type( {}) and 'title' in album.keys( ) and album['title'] != '' else text item = QListWidgetItem(text) if type(album) == type({}) and 'alid' in album.keys(): item.setWhatsThis(str(album['alid'])) self.listWidget.addItem(item) self.listWidget.setCurrentRow(0) self.mode = mode def openMenu(position): # Создание PopupMenu menu = QMenu() openAction = menu.addAction('Открыть альбом') openEAction = menu.addAction('Открыть события') menu.addSeparator() if mode > 0: #importAction = menu.addAction('Выгрузить альбом') exportAction = menu.addAction('Добавить альбом') renameAction = menu.addAction('Переименовать альбом') #menu.addSeparator() #editAction = menu.addAction('Заменить') menu.addSeparator() delAction = menu.addAction('Удалить альбом') delAllAction = menu.addAction('Удалить все альбомы') menu.addSeparator() else: exportAction, delAction, delAllAction = QAction(), QAction( ), QAction() quitAction = menu.addAction('Выход') action = menu.exec_(self.mapToGlobal(position)) # Привязка событий к Actions if action == openAction: self.doubleClick(self.listWidget.currentItem()) if action == openEAction: item = self.listWidget.currentItem() if item is not None: alid = item.whatsThis() self.events = EventViewer(int(alid), db, 1) self.events.show() if action == exportAction: text, ok = QInputDialog().getText(self, "Название альбома", "Ввкдите название альбома:", QLineEdit.Normal, 'Альбом') if ok: res = self.db.add_album({'imid': -1, 'title': text}) if len(res) == 1: album = res[0] item = QListWidgetItem(text) item.setWhatsThis(str(album['alid'])) self.listWidget.addItem(item) self.album_ids = db.get_all_albums_ids() if action == renameAction: item = self.listWidget.currentItem() if item is not None: alid = item.whatsThis() album = db.get_album_data(alid) text, ok = QInputDialog().getText( self, "Название альбома", "Ввкдите название альбома:", QLineEdit.Normal, album['title']) if ok: album = db.edit_album({ 'alid': int(alid), 'imid': -1, 'title': text }) item.setText(text) if album: album = self.db.get_album_data(alid) # b = layout.takeAt(1) # self.dictAlbum.close() # b.widget().deleteLater() self.dictAlbum = DictViewer( album, 1, self.db.albums.invizible_fields, self.db.albums.editable_fields) self.layout.addWidget(self.dictAlbum, 0, 1) self.album_ids = db.get_all_albums_ids() if action == delAction: res = QMessageBox.question( self, 'ВНИМАНИЕ!!!', "Вы действительно хотите удалить альбом?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if res == QMessageBox.Yes: self.delete_item() if action == delAllAction: res = QMessageBox.question( self, 'ВНИМАНИЕ!!!', "Вы действительно хотите удалить все альбомы?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if res == QMessageBox.Yes: item = self.listWidget.currentItem() while item is not None: self.delete_item() item = self.listWidget.currentItem() self.album_ids = db.get_all_albums_ids() if action == quitAction: self.accept() self.setContextMenuPolicy(Qt.CustomContextMenu) self.customContextMenuRequested.connect(openMenu) layout.addWidget(self.listWidget, 0, 0) layout.addWidget(self.dictAlbum, 0, 1) self.setLayout(layout) def delete_item(self): item = self.listWidget.currentItem() if item is not None: alid = item.whatsThis() self.db.del_album(int(alid)) self.listWidget.takeItem(self.listWidget.currentRow()) self.album_ids = self.db.get_all_albums_ids() def itemChanged(self): if self.last_alid: self.check_and_save(self.last_alid) item = self.listWidget.currentItem() if item is not None: alid = item.whatsThis() album = self.db.get_album_data(alid) self.dictAlbum = DictViewer(album, 1, self.db.albums.invizible_fields, self.db.albums.editable_fields) self.layout.addWidget(self.dictAlbum, 0, 1) self.last_alid = alid self.setFocus() def doubleClick(self, item): if item is not None: alid = item.whatsThis() print(alid) self.viewer = QLabelMA(alid, self.db, 1) self.viewer.show() def check_and_save(self, alid): changes = self.dictAlbum.get_changes() album = self.db.get_album_data(alid) print(changes) if len(changes) > 0 and type(album) == type({}): print('album changes saved') album.update(changes) self.db.edit_album(album)
class Repertoire(QWidget): def __init__(self, parent=None): super(Repertoire, self).__init__(parent) global filename self.monRepertoire = {} self.labelNom = QLabel("Nom") self.labelPrenom = QLabel("Prenom") self.labelTel = QLabel("Tel") self.leNom = QLineEdit() self.lePrenom = QLineEdit() self.leTel = QLineEdit() self.lwListeNoms = QListWidget() self.pbAjouter = QPushButton("Ajouter") self.pbModifier = QPushButton("Modifier") self.monRepertoire = self.lireJSON(filename) self.lwListeNoms.itemClicked.connect(self.userSelected) self.pbAjouter.clicked.connect(self.addUser) self.pbModifier.clicked.connect(self.modifyUser) layoutLabels = QVBoxLayout() layoutLabels.addWidget(self.labelNom) layoutLabels.addWidget(self.labelPrenom) layoutLabels.addWidget(self.labelTel) layoutLineEdit = QVBoxLayout() layoutLineEdit.addWidget(self.leNom) layoutLineEdit.addWidget(self.lePrenom) layoutLineEdit.addWidget(self.leTel) HLayout = QHBoxLayout() HLayout.addWidget(self.lwListeNoms) HLayout.addLayout(layoutLabels) HLayout.addLayout(layoutLineEdit) HLayoutButtons = QHBoxLayout() HLayoutButtons.addSpacerItem(QSpacerItem(100, 10)) HLayoutButtons.addWidget(self.pbModifier) HLayoutButtons.addWidget(self.pbAjouter) genLayout = QVBoxLayout() genLayout.addLayout(HLayout) genLayout.addLayout(HLayoutButtons) self.setLayout(genLayout) self.updateListw() def updateListw(self): self.lwListeNoms.clear() for fiche in self.monRepertoire["repertoire"]: self.lwListeNoms.addItem(fiche["nom"]) def sauveJSON(self, fileName): jsonClasse = json.dumps(self.monRepertoire, sort_keys=True, indent=4) f = open(fileName, 'w') f.write(jsonClasse) f.close() def addUser(self): retour = QInputDialog().getText(self, "Ajout Utilisateur", "Nom:") if retour[0] == "": return fiche = {} fiche["nom"] = retour[0] fiche["prenom"] = "" fiche["tel"] = "" self.monRepertoire["repertoire"].append(fiche) self.updateListw() self.sauveJSON(filename) def modifyUser(self): rowSelected = self.lwListeNoms.currentRow() self.monRepertoire["repertoire"][rowSelected]["nom"] = self.leNom.text( ) self.monRepertoire["repertoire"][rowSelected][ "prenom"] = self.lePrenom.text() self.monRepertoire["repertoire"][rowSelected]["tel"] = self.leTel.text( ) self.updateListw() self.sauveJSON(filename) def userSelected(self): rowSelected = self.lwListeNoms.currentRow() fiche = self.monRepertoire["repertoire"][rowSelected] self.leNom.setText(fiche["nom"]) self.lePrenom.setText(fiche["prenom"]) self.leTel.setText(fiche["tel"]) def lireJSON(self, fileName): with open(fileName) as json_file: dico = json.load(json_file) return dico return None
class Form(QDialog): def __init__(self, parent=None): self.player = vlc.MediaPlayer() super(Form, self).__init__(parent) self.setWindowTitle("Bassdrive Archive Player") layoutPlay = QHBoxLayout() self.pbPlay = QPushButton("Play") self.pbCast = QPushButton("Cast") layoutPlay.addWidget(self.pbPlay) layoutPlay.addWidget(self.pbCast) self.pbStop = QPushButton("Stop") self.pbRandom = QPushButton("Random") self.pbDownload = QPushButton("Download") self.pbPlay.clicked.connect(self.onPbPlay) self.pbDownload.clicked.connect(self.onPbDownload) self.pbRandom.clicked.connect(self.onPbRandom) self.pbStop.clicked.connect(self.onPbStop) self.pbPlay.setEnabled(False) self.xProgressBar = QProgressBar(self) self.listFiles = QListWidget() self.listFiles.itemClicked.connect(self.onItemSelected) # Create layout and add widgets layout = QVBoxLayout() layout.addWidget(self.xProgressBar) layout.addWidget(self.listFiles) layout.addLayout(layoutPlay) layout.addWidget(self.pbStop) layout.addWidget(self.pbRandom) layout.addWidget(self.pbDownload) self.xProgressBar.setRange(1, 100) # Set dialog layout self.setLayout(layout) # Read in what we've already scanned if os.path.isfile(DBFILE): with open(DBFILE, "r") as f: self.db = json.load(f) for file in self.db['files']: self.listFiles.addItem(file['filename']) else: self.db = {"last_scan": "2020-01-28T19:19:10.702353", "files": []} if datetime.now() >= datetime.fromisoformat( self.db['last_scan']) + timedelta(hours=12): self.startWebScraping(ROOTURL) self.scanChromecast() def scanChromecast(self): self.listener = pychromecast.CastListener(self.cc_added_callback, self.cc_removed_callback, self.cc_updated_callback) zconf = zeroconf.Zeroconf() self.browser = pychromecast.discovery.start_discovery( self.listener, zconf) def cc_added_callback(self, uuid, name): print("Found mDNS service for cast device {}".format(uuid)) self.list_devices() def cc_removed_callback(self, uuid, name, service): print("Lost mDNS service for cast device {} {}".format(uuid, service)) self.list_devices() def cc_updated_callback(self, uuid, name): print("Updated mDNS service for cast device {}".format(uuid)) self.list_devices() def list_devices(self): print("Currently known cast devices:") for uuid, service in self.listener.services.items(): print(" {} {}".format(uuid, service)) def onPbStop(self): self.player.stop() self.pbPlay.setText("Play") self.pbPlay.clicked.disconnect(self.onPause) self.pbPlay.clicked.connect(self.onPbPlay) self.pbPlay.setEnabled(True) def onPbRandom(self): randomNo = random.randint(0, len(self.db['files']) - 1) self.listFiles.setCurrentRow(randomNo) obj = self.db['files'][randomNo] self.playFile(obj) def playFile(self, obj): self.player.stop() self.player.set_mrl(obj['url']) self.player.play() self.pbPlay.setText("Pause") self.pbPlay.clicked.disconnect(self.onPbPlay) self.pbPlay.clicked.connect(self.onPause) self.pbPlay.setEnabled(True) def onItemSelected(self, listItem): self.pbPlay.setEnabled(True) print('Selected: {}({})'.format(listItem.text(), self.listFiles.currentRow())) def startWebScraping(self, rootUrl): print("Starting web scraping from {}...".format(rootUrl)) self.scrapethread = ScraperThread(rootUrl) self.scrapethread.scraper_update.connect(self.onScraperUpdate) self.scrapethread.scraper_complete.connect(self.onScraperComplete) self.scrapethread.start() def onScraperUpdate(self, obj): pass def onScraperComplete(self, newdb): for obj in newdb['files']: if obj not in self.db['files']: self.db['files'].append(obj) self.listFiles.addItem(obj['filename']) print("Adding new mix: {}".format(obj['filename'])) self.db['last_scan'] = datetime.now().isoformat() # Dump out the results with open(DBFILE, "w") as f: self.db json.dump(self.db, f, indent=4) def onBrowseClick(self): self.db['output'] = QFileDialog.getExistingDirectory() self.eOutputFolder.setText(self.db['output']) self.writeDb() def onPbDownload(self): self.pbDownload.setEnabled(False) selectedItem = self.listFiles.currentRow() fileObj = self.db['files'][selectedItem] self.download(fileObj) def onPbPlay(self): obj = self.db['files'][self.listFiles.currentRow()] self.playFile(obj) def onPause(self): self.player.pause() self.pbPlay.setText("Play") self.pbPlay.clicked.disconnect(self.onPause) self.pbPlay.clicked.connect(self.onPbPlay) def writeDb(self): self.db['output'] = self.eOutputFolder.text() with open(DBFILE, "w") as f: json.dump(self.db, f, indent=4) def setDownloaded(self, fullName, state): for obj in self.db['files']: if obj['filename'] == fullName: obj['downloaded'] = state self.writeDb() def onDownloadUpdate(self, data): self.xProgressBar.setValue(data) def onDownloadComplete(self, obj): #print( "Download complete on {}.".format(obj['fullName'])) self.setDownloaded(obj['filename'], True) def download(self, obj): if not os.path.isdir('cache'): os.mkdir('cache') filename = os.path.join('cache', obj['filename']) print("Downloasing {}...".format(filename)) self.downloadthread = DownloadThread('cache', obj) self.downloadthread.download_update.connect(self.onDownloadUpdate) self.downloadthread.download_complete.connect(self.onDownloadComplete) self.downloadthread.start()
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle('清单') self.setFixedSize(320, 480) # 布局 layout = QVBoxLayout() # 清单视图 self.items_view = QListWidget() self.items_view.setIconSize(QSize(14, 14)) self.items_view.doubleClicked.connect(self.toggle_complete) layout.addWidget(self.items_view) # 按钮布局 button_layout = QHBoxLayout() layout.addLayout(button_layout) # delete button self.delete_button = QPushButton('DELETE') self.delete_button.clicked.connect(self.delete) button_layout.addWidget(self.delete_button) # Complete button self.complete_button = QPushButton('COMPLETE') self.complete_button.clicked.connect(self.complete) button_layout.addWidget(self.complete_button) # add input self.add_input = QLineEdit() self.add_input.returnPressed.connect(self.add) layout.addWidget(self.add_input) # add button self.add_button = QPushButton('ADD') self.add_button.clicked.connect(self.add) layout.addWidget(self.add_button) # status bar self.setStatusBar(QStatusBar()) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.setStyleSheet(""" QListWidget { border: 1px solid #999; font-size: 13px; } QListWidget::item { color: #000; height: 30px; border-bottom: 1px solid #dedede; } QListWidget::item:selected { background-color: #fff9dd; } QPushButton { height: 24px; background-color: #fddb3a; font-weight: 900; } QLineEdit { padding: 5px; } """) self.items = [] self.load() self.list_items() @Slot() def add(self): name = self.add_input.text() if name: self.items_view.addItem(QListWidgetItem(name)) self.add_input.setText('') self.items.append({'name': name, 'done': False}) # self.save() db.save(name, False) @Slot() def delete(self): items = self.items_view.selectedItems() if items: self.items.pop(self.items_view.currentRow()) db.delete(self.items_view.currentRow() + 1) self.items_view.clear() self.list_items() # self.save() @Slot() def complete(self): items = self.items_view.selectedItems() if items: item_data = self.items[self.items_view.currentRow()] if not item_data['done']: icon = QIcon('done.svg') items[0].setIcon(icon) item_data['done'] = True # self.save() db.update(True, self.items_view.currentRow() + 1) @Slot() def toggle_complete(self): items = self.items_view.selectedItems() if items: item_data = self.items[self.items_view.currentRow()] if not item_data['done']: icon = QIcon('done.svg') items[0].setIcon(icon) item_data['done'] = True db.update(True, self.items_view.currentRow() + 1) else: icon = QIcon('') items[0].setIcon(icon) item_data['done'] = False db.update(False, self.items_view.currentRow() + 1) # self.save() def list_items(self): for item in self.items: list_item = QListWidgetItem(item['name']) if item['done']: icon = QIcon('done.svg') list_item.setIcon(icon) self.items_view.addItem(list_item) def load(self): # with open('data.json', 'r') as f: # self.items = json.load(f) self.items = db.select() def save(self): with open('data.json', 'w') as f: json.dump(self.items, f)
class SpecifySearchConditionsDialog(BaseDialog): def __init__(self, parent, entity): super().__init__(parent, _("Search criteria"), _("&Start search"), _("&Cancel")) self._entity = entity self._value_widget = None self._value_label = None self._search_expression_parts = [] self._added_condition = False self._populate_fields_tree(self._entity) def create_ui(self): fields_label = QLabel(_("Class fields"), self) self.layout.addWidget(fields_label, 0, 0) self._fields_tree = QTreeWidget(self) fields_label.setBuddy(self._fields_tree) self._fields_tree.currentItemChanged.connect( self.on_fields_tree_sel_changed) self.layout.addWidget(self._fields_tree, 1, 0) operator_label = QLabel(_("Operator"), self) self.layout.addWidget(operator_label, 0, 1) self._operator = QListWidget(self) operator_label.setBuddy(self._operator) self.layout.addWidget(self._operator, 1, 1) self._operator.currentRowChanged.connect(self.on_operator_choice) self._operator.setCurrentRow(0) add_button = QPushButton(_("&Add condition"), self) add_button.clicked.connect(self.on_add_clicked) self.layout.addWidget(add_button, 2, 0, 1, 3) criteria_label = QLabel(_("Current search criteria"), self) self.layout.addWidget(criteria_label, 3, 0, 1, 3) self._criteria_list = QListWidget(self) criteria_label.setBuddy(self._criteria_list) self.layout.addWidget(self._criteria_list, 4, 0, 1, 3) remove_button = QPushButton(_("&Remove condition"), self) remove_button.clicked.connect(self.on_remove_clicked) self.layout.addWidget(remove_button, 5, 0, 1, 3) distance_label = QLabel(_("Search objects to distance"), self) self.layout.addWidget(distance_label, 6, 0) self._distance_field = QSpinBox(self) self._distance_field.setMaximum(100000) self._distance_field.setSuffix(" " + _("meters")) self._distance_field.setSpecialValueText(_("No limit")) distance_label.setBuddy(self._distance_field) self.layout.addWidget(self._distance_field, 6, 1) def _populate_fields_tree(self, entity, parent=None): if parent is None: parent = self._fields_tree.invisibleRootItem() metadata = EntityMetadata.for_discriminator(entity) for field_name, field in sorted( metadata.all_fields.items(), key=lambda i: underscored_to_words(i[0])): child_metadata = None try: child_metadata = EntityMetadata.for_discriminator( field.type_name) except KeyError: pass if child_metadata: name = get_class_display_name(field.type_name) subparent = QTreeWidgetItem([name]) subparent.setData(0, Qt.UserRole, field_name) parent.addChild(subparent) self._populate_fields_tree(field.type_name, subparent) else: item = QTreeWidgetItem([underscored_to_words(field_name)]) item.setData(0, Qt.UserRole, (field_name, field)) parent.addChild(item) def on_fields_tree_sel_changed(self, item): data = item.data(0, Qt.UserRole) if data is not None and not isinstance(data, str): self._field_name = data[0] self._field = data[1] self._operators = operators_for_column_class(self._field.type_name) self._operator.clear() self._operator.addItems([o.label for o in self._operators]) self._added_condition = False def on_operator_choice(self, index): self._added_condition = False if self._value_widget: self.layout.removeWidget(self._value_widget) self._value_widget.deleteLater() if self._value_label: self.layout.removeWidget(self._value_label) self._value_label.deleteLater() self._value_label = None operator = self._operators[self._operator.currentRow()] value_label = self._create_value_label( operator.get_value_label(self._field)) self._value_widget = operator.get_value_widget(self, self._field) if not self._value_widget: return QWidget.setTabOrder(self._operator, self._value_widget) self._value_label = value_label if self._value_label: self._value_label.setBuddy(self._value_widget) self.layout.addWidget(self._value_label, 0, 2) self.layout.addWidget(self._value_widget, 1, 2) def _create_value_label(self, label): if not label: return label = QLabel(label, self) return label def on_add_clicked(self, evt): if not hasattr(self, "_field_name"): return self._added_condition = True json_path = [] parent_item = self._fields_tree.currentItem().parent() parent_data = parent_item.data(0, Qt.UserRole) if parent_item else None if isinstance(parent_data, str): json_path.append(parent_data) json_path.append(self._field_name) json_path = ".".join(json_path) operator_obj = self._operators[self._operator.currentRow()] expression = operator_obj.get_comparison_expression( self._field, FieldNamed(json_path), self._value_widget) self._search_expression_parts.append(expression) self._criteria_list.addItem( f"{underscored_to_words(self._field_name)} {operator_obj.label} {operator_obj.get_value_as_string(self._field, self._value_widget)}" ) @property def distance(self): return self._distance_field.value() def create_conditions(self): conditions = [] if self._search_expression_parts: for part in self._search_expression_parts: conditions.append(part) return conditions def on_remove_clicked(self, evt): selection = self._criteria_list.currentRow() if selection < 0: return del self._search_expression_parts[selection] self._criteria_list.removeItemWidget(self._criteria_list.currentItem()) def ok_clicked(self): if not self._added_condition: if QMessageBox.question( self, _("Question"), _("It appears that you forgot to add the current condition to the conditions list. Do you want to add it before starting the search?" )): self.on_add_clicked(None) super().ok_clicked()
class PersonaUI(QWidget): """ Widget for Persona creation view. :param MainFrame mainframe: application mainframe :param QWidget op: parent widget """ def __init__(self, mainframe, op): QWidget.__init__(self) self.mainframe = mainframe self.op = op self.grid = QGridLayout() self.setLayout(self.grid) self.listP = None self.listLS = None self.listEL1 = None self.listEL2 = None self.nameT = None self.levelT = None self.textT = None self.strT = None self.magT = None self.endT = None self.agiT = None self.luckT = None self.createFrame = None self.buttonFrame = None self.bfgrid = None # Actual create frame variables. self.cfgrid = None self.lsdic = None self.slashO = None self.strikeO = None self.pierceO = None self.fireO = None self.iceO = None self.windO = None self.elecO = None self.darkO = None self.lightO = None self.arcO = None self.iSpellOs = None self.lsSpellO = None self.lslevel = None self.initUI(True) def initUI(self, infoDump): """ Initializes the basic UI showing the list of Personas. Does a lot of stuff. :param dict infoDump: not sure lol """ self.mainframe.setWindowTitle("Persona Creator") if not infoDump: self.createFrameDraw() self.initButtonFrame(infoDump) self.listP = QListWidget(self) self.grid.addWidget(self.listP, 0, 3, 2, 1) temp = json_reader.readPerNames() self.listP.addItems(temp) def initButtonFrame(self, infoDump): """ Initializes the buttonframes that are present in all Persona creator views. :param dict infoDump: not sure lol """ self.buttonFrame = QWidget(self) self.bfgrid = QGridLayout() self.buttonFrame.setLayout(self.bfgrid) self.grid.addWidget(self.buttonFrame, 3, 0, 1, 4) new = QPushButton(self.buttonFrame, text="New") new.clicked.connect(self.new) self.bfgrid.addWidget(new, 4, 0) back = QPushButton(self.buttonFrame, text="Back") back.clicked.connect(self.back) self.bfgrid.addWidget(back, 4, 4) remove = QPushButton(self.buttonFrame, text="Remove") remove.clicked.connect(self.remove) self.bfgrid.addWidget(remove, 4, 3) edit = QPushButton(self.buttonFrame, text="Edit") edit.clicked.connect(self.edit) self.bfgrid.addWidget(edit, 4, 2) if not infoDump: save = QPushButton(self.buttonFrame, text="Save") save.clicked.connect(self.save) self.bfgrid.addWidget(save, 4, 1) def createFrameDraw(self): """ Initializes the GUI of the actual creation frame view. Does a LOT of stuff. """ self.createFrame = QWidget(self) self.cfgrid = QGridLayout() self.createFrame.setLayout(self.cfgrid) self.grid.addWidget(self.createFrame, 0, 0, 2, 2) self.lsdic = {} nameL = QLabel(self.createFrame, text="Name:") self.cfgrid.addWidget(nameL, 0, 0) self.nameT = QLineEdit(self.createFrame) self.nameT.setFixedSize(100, 20) self.cfgrid.addWidget(self.nameT, 0, 1) strL = QLabel(self.createFrame, text="Str") self.cfgrid.addWidget(strL, 0, 2) self.strT = QLineEdit(self.createFrame) self.strT.setFixedSize(20, 20) self.cfgrid.addWidget(self.strT, 0, 3) magL = QLabel(self.createFrame, text="Mag") self.cfgrid.addWidget(magL, 1, 2) self.magT = QLineEdit(self.createFrame) self.magT.setFixedSize(20, 20) self.cfgrid.addWidget(self.magT, 1, 3) endL = QLabel(self.createFrame, text="End") self.cfgrid.addWidget(endL, 2, 2) self.endT = QLineEdit(self.createFrame) self.endT.setFixedSize(20, 20) self.cfgrid.addWidget(self.endT, 2, 3) agiL = QLabel(self.createFrame, text="Agi") self.cfgrid.addWidget(agiL, 3, 2) self.agiT = QLineEdit(self.createFrame) self.agiT.setFixedSize(20, 20) self.cfgrid.addWidget(self.agiT, 3, 3) luckL = QLabel(self.createFrame, text="Luck") self.cfgrid.addWidget(luckL, 4, 2) self.luckT = QLineEdit(self.createFrame) self.luckT.setFixedSize(20, 20) self.cfgrid.addWidget(self.luckT, 4, 3) resList = json_reader.data_list("resistances") resL = QLabel(self.createFrame, text="Resistance:") self.cfgrid.addWidget(resL, 0, 5) slashL = QLabel(self.createFrame, text="Slash") self.cfgrid.addWidget(slashL, 1, 5) self.slashO = QComboBox(self.createFrame) self.slashO.addItems(resList) self.slashO.setCurrentIndex(1) self.cfgrid.addWidget(self.slashO, 1, 6) strikeL = QLabel(self.createFrame, text="Strike") self.cfgrid.addWidget(strikeL, 2, 5) self.strikeO = QComboBox(self.createFrame) self.strikeO.addItems(resList) self.strikeO.setCurrentIndex(1) self.cfgrid.addWidget(self.strikeO, 2, 6) pierceL = QLabel(self.createFrame, text="Pierce") self.cfgrid.addWidget(pierceL, 3, 5) self.pierceO = QComboBox(self.createFrame) self.pierceO.addItems(resList) self.pierceO.setCurrentIndex(1) self.cfgrid.addWidget(self.pierceO, 3, 6) fireL = QLabel(self.createFrame, text="Fire") self.cfgrid.addWidget(fireL, 4, 5) self.fireO = QComboBox(self.createFrame) self.fireO.addItems(resList) self.fireO.setCurrentIndex(1) self.cfgrid.addWidget(self.fireO, 4, 6) iceL = QLabel(self.createFrame, text="Ice") self.cfgrid.addWidget(iceL, 5, 5) self.iceO = QComboBox(self.createFrame) self.iceO.addItems(resList) self.iceO.setCurrentIndex(1) self.cfgrid.addWidget(self.iceO, 5, 6) elecL = QLabel(self.createFrame, text="Elec") self.cfgrid.addWidget(elecL, 6, 5) self.elecO = QComboBox(self.createFrame) self.elecO.addItems(resList) self.elecO.setCurrentIndex(1) self.cfgrid.addWidget(self.elecO, 6, 6) windL = QLabel(self.createFrame, text="Wind") self.cfgrid.addWidget(windL, 7, 5) self.windO = QComboBox(self.createFrame) self.windO.addItems(resList) self.windO.setCurrentIndex(1) self.cfgrid.addWidget(self.windO, 7, 6) lightL = QLabel(self.createFrame, text="Light") self.cfgrid.addWidget(lightL, 8, 5) self.lightO = QComboBox(self.createFrame) self.lightO.addItems(resList) self.lightO.setCurrentIndex(1) self.cfgrid.addWidget(self.lightO, 8, 6) darkL = QLabel(self.createFrame, text="Dark") self.cfgrid.addWidget(darkL, 9, 5) self.darkO = QComboBox(self.createFrame) self.darkO.addItems(resList) self.darkO.setCurrentIndex(1) self.cfgrid.addWidget(self.darkO, 9, 6) spellList = json_reader.data_list("spells") self.listLS = QListWidget(self.createFrame) self.listLS.setFixedSize(200, 300) self.cfgrid.addWidget(self.listLS, 3, 7, 8, 2) newLS = QPushButton(self.createFrame, text="+") newLS.clicked.connect(self.addLS) self.cfgrid.addWidget(newLS, 2, 7) delLS = QPushButton(self.createFrame, text="DEL") delLS.clicked.connect(self.delLS) self.cfgrid.addWidget(delLS, 2, 8) lsl = QLabel(self.createFrame, text="Learned Spells:") self.cfgrid.addWidget(lsl, 0, 7, 1, 2) arcanaL = QLabel(self.createFrame, text="Arcana:") self.cfgrid.addWidget(arcanaL, 1, 0) arc_list = json_reader.data_list("arcanas") self.arcO = QComboBox(self.createFrame) self.arcO.addItems(arc_list) self.arcO.setCurrentIndex(0) self.cfgrid.addWidget(self.arcO, 1, 1) levelL = QLabel(self.createFrame, text="Level:") self.cfgrid.addWidget(levelL, 2, 0) self.levelT = QLineEdit(self.createFrame) self.levelT.setFixedSize(20, 20) self.cfgrid.addWidget(self.levelT, 2, 1) heritageL = QLabel(self.createFrame, text="Inherits:") self.cfgrid.addWidget(heritageL, 3, 0, 1, 2) elements = json_reader.data_list("elements") elements.append("Support") self.listEL1 = QComboBox(self.createFrame) self.listEL1.addItems(elements) self.cfgrid.addWidget(self.listEL1, 4, 0) self.listEL2 = QComboBox(self.createFrame) self.listEL2.addItems(elements) self.cfgrid.addWidget(self.listEL2, 4, 1) iSpellL = QLabel(self.createFrame, text="Initial Spells:") self.cfgrid.addWidget(iSpellL, 5, 0, 1, 2) self.iSpellOs = [] for i in range(6, 9): temp = QComboBox(self.createFrame) temp.addItems(spellList) temp2 = QComboBox(self.createFrame) temp2.addItems(spellList) self.cfgrid.addWidget(temp, i, 0, 1, 2) self.cfgrid.addWidget(temp2, i, 2, 1, 2) self.iSpellOs.extend([temp, temp2]) textL = QLabel(self.createFrame, text="Info:") self.cfgrid.addWidget(textL, 10, 0) self.textT = QTextEdit(self.createFrame) self.textT.setFixedSize(300, 100) self.cfgrid.addWidget(self.textT, 10, 1, 1, 5) self.lslevel = QLineEdit(self.createFrame) self.lslevel.setFixedSize(40, 20) self.lsSpellO = QComboBox(self.createFrame) self.lsSpellO.addItems(spellList) self.cfgrid.addWidget(self.lsSpellO, 1, 7) self.cfgrid.addWidget(self.lslevel, 1, 8) def addLS(self): """ Add a learned spell to the list, based on what was entered. """ print("Adding learned spell") chosenSpell = self.lsSpellO.currentText() if (int)(self.lslevel.text()) <= (int)(self.levelT.text()): popup("You cannot add a spell at an earlier level than the Persona's base level", "Critical") return if chosenSpell != "": print("Ok") self.lsdic[chosenSpell] = self.lslevel.text() self.listLS.addItem(chosenSpell + " at level " + self.lslevel.text()) self.lslevel.setText("") self.lsSpellO.setCurrentIndex(0) return popup("You must choose a spell", "Critical") def delLS(self): """ Remove the selected learned spell from the list """ print("Deleting learned spell") key = "" i = 0 while len(self.listLS.currentItem().text()) > i: if self.listLS.currentItem().text()[i] == " " and \ self.listLS.currentItem().text()[i+1] == "a" and \ self.listLS.currentItem().text()[i+2] == "t": # TODO EWWWWWW break key += self.listLS.currentItem().text()[i] i = i + 1 print(key) print(self.lsdic.pop(key)) self.listLS.takeItem(self.listLS.currentRow()) def loadPer(self, name): """ Load a certain Persona from file. :param str name: name of Persona to load """ data = json_reader.readOne(name, 'pers') self.nameT.setText(data["name"]) self.textT.setText(data["desc"]) self.strT.setText(data["stats"][0]) self.magT.setText(data["stats"][1]) self.endT.setText(data["stats"][2]) self.agiT.setText(data["stats"][3]) self.luckT.setText(data["stats"][4]) self.levelT.setText(data["level"]) self.arcO.setCurrentIndex( [self.arcO.itemText(i) for i in range(self.arcO.count())].index(data["arcana"]) ) self.listEL1.setCurrentIndex( [self.listEL1.itemText(i) for i in range(self.listEL1.count())].index(data["heritage"][0]) ) self.listEL2.setCurrentIndex( [self.listEL2.itemText(i) for i in range(self.listEL2.count())].index(data["heritage"][1]) ) self.slashO.setCurrentIndex( [self.slashO.itemText(i) for i in range(self.slashO.count())].index(data["resistance"][0]) ) self.strikeO.setCurrentIndex( [self.strikeO.itemText(i) for i in range(self.strikeO.count())].index(data["resistance"][1]) ) self.pierceO.setCurrentIndex( [self.pierceO.itemText(i) for i in range(self.pierceO.count())].index(data["resistance"][2]) ) self.fireO.setCurrentIndex( [self.fireO.itemText(i) for i in range(self.fireO.count())].index(data["resistance"][3]) ) self.iceO.setCurrentIndex( [self.iceO.itemText(i) for i in range(self.iceO.count())].index(data["resistance"][4]) ) self.elecO.setCurrentIndex( [self.elecO.itemText(i) for i in range(self.elecO.count())].index(data["resistance"][5]) ) self.windO.setCurrentIndex( [self.windO.itemText(i) for i in range(self.windO.count())].index(data["resistance"][6]) ) self.lightO.setCurrentIndex( [self.lightO.itemText(i) for i in range(self.lightO.count())].index(data["resistance"][7]) ) self.darkO.setCurrentIndex( [self.darkO.itemText(i) for i in range(self.darkO.count())].index(data["resistance"][8]) ) i = 0 for combobox in self.iSpellOs: combobox.setCurrentIndex( [combobox.itemText(j) for j in range(combobox.count()-1)].index(data["spellDeck"][i]) ) i += 1 self.lsdic = data["spellLearn"] self.listLS.clear() for spell, level in self.lsdic.items(): self.listLS.addItem(spell + " at level " + level) print("Loaded " + data["name"]) def edit(self): """ Switch to edit view, also loads the selected Persona. """ try: if self.listP.currentItem().text() != "": if self.createFrame and not popup("Override any unsaved changes?", "Warning"): return self.loadPer(self.listP.currentItem().text()) except AttributeError: # To initialize createFrame UI before load if self.listP.currentItem().text() != "": temp = self.listP.currentItem().text() self.buttonFrame.close() self.initUI(False) self.loadPer(temp) else: return self.createFrame.show() self.mainframe.center() print("Changed to edit frame") def save(self): """ Validate all info and save to file on disk. """ if os.path.exists(json_reader.buildPath("data/pers/"+self.nameT.text()+".json")): if not popup("Override existing Persona "+self.nameT.text()+"?", "Question"): return print("Saving") spellDeck = [] for combobox in self.iSpellOs: spellDeck.append(combobox.currentText()) stats = [self.strT.text(), self.magT.text(), self.endT.text(), self.agiT.text(), self.luckT.text()] res = [self.slashO.currentText(), self.strikeO.currentText(), self.pierceO.currentText(), self.fireO.currentText(), self.iceO.currentText(), self.elecO.currentText(), self.windO.currentText(), self.lightO.currentText(), self.darkO.currentText()] try: (int)(self.levelT.text()) (int)(self.strT.text()) (int)(self.magT.text()) (int)(self.endT.text()) (int)(self.agiT.text()) (int)(self.luckT.text()) except ValueError: popup("There is a number entry that isn't valid.\nEntries requiring numbers are:\nLEVEL\nSTR" "\nMAG\nEND\nAGI\nLUCK", "Critical") print("Not Saved") return if not (self.nameT.text() and not self.nameT.text().isspace()): popup("No name entered for your Persona. Name is a required field.", "Critical") print("No Name, not saved") return toWrite = Persona( self.nameT.text(), self.arcO.currentText(), self.levelT.text(), self.textT.toPlainText(), spellDeck, self.lsdic, stats, res, [self.listEL1.currentText(), self.listEL2.currentText()] ) json_reader.writeOne(toWrite, 'pers') temp = self.nameT.text() if (temp not in [self.listP.item(i).text() for i in range(self.listP.count())]): self.listP.addItem(temp) self.loadPer(temp) print("Saved Persona") def remove(self): """ Remove a created Persona from the list and delete the file on disk. """ if self.listP.currentItem().text() == "": return if not popup( "Are you certain you want to completely remove this Persona?\n(Cannot be undone)", "Warning" ): return print("Removing Persona " + self.listP.currentItem().text()) json_reader.deletePer(self.listP.currentItem().text()) self.listP.takeItem( [self.listP.item(i).text() for i in range(self.listP.count())].index( self.listP.currentItem().text()) ) def new(self): """ Open an empty Persona edit view. """ if self.createFrame and not popup("Override any unsaved changes?", "Warning"): return if self.createFrame: self.createFrame.close() self.buttonFrame.close() self.initUI(False) self.createFrame.show() self.mainframe.center() print("Created") def back(self): """ Return to the parent widget. """ print("Returned to main screen") self.mainframe.changeState(self.op)
class EditCommentBankWindow(QMainWindow): def __init__(self, subject_name): QMainWindow.__init__(self) self.setWindowTitle("Edit Comment Bank: {} - {} {}".format( subject_name, config.APP_NAME, config.APP_VERSION)) self.setMinimumWidth(1200) self.setStyleSheet(config.STYLESHEET) self.subject = could_try_harder.load(subject_name) self.saved_list = could_try_harder.get_saved_list() # Widgets self.intro_comment_label = QLabel("Introductory Comment:") self.intro_comment_label.setProperty("styleClass", "heading") self.intro_comment_textedit = QTextEdit() self.comment_bank_label = QLabel("Comment Bank") self.comment_bank_label.setProperty("styleClass", "heading") self.comment_bank_listwidget = QListWidget() self.placeholder_instructions_label = QLabel( config.PLACEHOLDER_INSTRUCTIONS) self.add_comment_label = QLabel("Add Comment:") self.add_comment_entry = QLineEdit() self.add_comment_button = QPushButton("Add") self.update_comment_label = QLabel("Update Comment:") self.update_comment_entry = QLineEdit() self.update_comment_button = QPushButton("Update") self.delete_comment_button = QPushButton("Delete Comment") self.import_comments_combo = QComboBox() self.import_comments_button = QPushButton("Import...") self.cancel_button = QPushButton("Cancel") self.save_button = QPushButton("Save") # Layout self.layout = QVBoxLayout() self.top_layout = QHBoxLayout() self.intro_comment_layout = QVBoxLayout() self.intro_comment_layout.addWidget(self.intro_comment_label) self.intro_comment_layout.addWidget(self.intro_comment_textedit) self.top_layout.addLayout(self.intro_comment_layout) self.top_layout.addWidget(self.placeholder_instructions_label) self.layout.addLayout(self.top_layout) self.middle_layout = QVBoxLayout() self.middle_layout.addWidget(self.comment_bank_label) self.middle_layout.addWidget(self.comment_bank_listwidget) self.comment_actions_layout = QHBoxLayout() self.comment_actions_layout.addWidget(self.delete_comment_button, 0, Qt.AlignLeft) self.comment_actions_layout.addWidget(self.import_comments_combo, 1, Qt.AlignRight) self.comment_actions_layout.addWidget(self.import_comments_button, 0, Qt.AlignRight) self.middle_layout.addLayout(self.comment_actions_layout) self.update_comment_layout = QGridLayout() self.update_comment_layout.addWidget(self.update_comment_label, 0, 0) self.update_comment_layout.addWidget(self.update_comment_entry, 0, 1) self.update_comment_layout.addWidget(self.update_comment_button, 0, 2) self.update_comment_layout.addWidget(self.add_comment_label, 1, 0) self.update_comment_layout.addWidget(self.add_comment_entry, 1, 1) self.update_comment_layout.addWidget(self.add_comment_button, 1, 2) self.middle_layout.addLayout(self.update_comment_layout) self.layout.addLayout(self.middle_layout) self.bottom_layout = QHBoxLayout() self.bottom_layout.addWidget(self.cancel_button, 0, Qt.AlignLeft) self.bottom_layout.addWidget(self.save_button, 0, Qt.AlignRight) self.layout.addLayout(self.bottom_layout) # Slot connections self.comment_bank_listwidget.itemSelectionChanged.connect( self.do_update_comment_bank_selection) self.import_comments_button.clicked.connect(self.do_import_comments) self.update_comment_button.clicked.connect(self.do_update_comment) self.update_comment_entry.returnPressed.connect(self.do_update_comment) self.add_comment_button.clicked.connect(self.do_add_comment) self.add_comment_entry.returnPressed.connect(self.do_add_comment) self.delete_comment_button.clicked.connect(self.do_delete_comment) self.cancel_button.clicked.connect(self.do_cancel) self.save_button.clicked.connect(self.do_save) # Initial UI update self.update_ui() self.widget = QWidget() self.widget.setLayout(self.layout) self.setCentralWidget(self.widget) def update_ui(self): self.update_import_comments_list() self.update_intro_comment() self.update_comment_bank() def update_import_comments_list(self): self.import_comments_combo.clear() self.import_comments_combo.insertItems(0, self.saved_list) def update_intro_comment(self): self.intro_comment_textedit.clear() self.intro_comment_textedit.insertPlainText( self.subject['intro_comment']) def update_comment_bank(self): self.comment_bank_listwidget.clear() self.comment_bank_listwidget.addItems(self.subject['comment_bank']) self.do_update_comment_bank_selection() @Slot() def do_import_comments(self): # TODO confirm dialog first confirm_msg = QMessageBox(self) confirm_msg.setWindowTitle("Confirm") confirm_msg.setText("This will override current comments.") confirm_msg.setInformativeText("Do you want to continue?") confirm_msg.setStandardButtons(QMessageBox.No | QMessageBox.Yes) confirm_msg.setDefaultButton(QMessageBox.Yes) confirm = confirm_msg.exec() if confirm == QMessageBox.Yes: if self.import_comments_combo.count() > 0: new_subject = could_try_harder.load( self.import_comments_combo.currentText()) if new_subject: self.subject['intro_comment'] = new_subject[ 'intro_comment'] self.subject['comment_bank'] = new_subject['comment_bank'] self.update_ui() else: # TODO better error handling here print('Tried to import empty subject.') return return @Slot() def do_update_comment_bank_selection(self): if self.comment_bank_listwidget.selectedItems(): state = True else: state = False self.delete_comment_button.setEnabled(state) self.update_comment_button.setEnabled(state) # Update the text in the update comment line edit self.update_comment_entry.clear() if self.comment_bank_listwidget.currentItem(): self.update_comment_entry.insert( self.comment_bank_listwidget.currentItem().text()) @Slot() def do_update_comment(self): if self.update_comment_entry.text(): self.comment_bank_listwidget.currentItem().setText( could_try_harder.do_style( self.update_comment_entry.text().strip())) self.do_update_comment_bank_selection() @Slot() def do_add_comment(self): if self.add_comment_entry.text(): self.comment_bank_listwidget.addItem( could_try_harder.do_style( self.add_comment_entry.text().strip())) self.add_comment_entry.clear() self.do_update_comment_bank_selection() @Slot() def do_delete_comment(self): self.comment_bank_listwidget.takeItem( self.comment_bank_listwidget.currentRow()) self.do_update_comment_bank_selection() @Slot() def do_cancel(self): self.close() @Slot() def do_save(self): self.subject['intro_comment'] = could_try_harder.do_style( self.intro_comment_textedit.toPlainText().strip()) self.subject['comment_bank'] = [] for i in range(self.comment_bank_listwidget.count()): self.subject['comment_bank'].append( self.comment_bank_listwidget.item(i).text()) if could_try_harder.save(self.subject): self.close() else: # TODO better error handling here print("Save failed.")