def _get_checked_items(self, qlist: QListWidget): items = [] for i in range(qlist.count()): item = qlist.item(i) if item.checkState() == Qt.Checked: items.append(item) return items
def add_fields_to_component(component: Group, fields_widget: QListWidget, component_model: NexusTreeModel = None): """ Adds fields from a list widget to a component. :param component: Component to add the field to. :param fields_widget: The field list widget to extract field information such the name and value of each field. """ for i in range(fields_widget.count()): widget = fields_widget.itemWidget(fields_widget.item(i)) try: if not isinstance(widget.value, (Link, Dataset)): stream_module = deepcopy(widget.value) stream_module.parent_node = component component.children.append(stream_module) else: component[widget.name] = widget.value except ValueError as error: show_warning_dialog( f"Warning: field {widget.name} not added", title="Field invalid", additional_info=str(error), parent=fields_widget.parent().parent(), ) if component_model and component_model.current_nxs_obj[1]: row = component_model.rowCount(component_model.current_nxs_obj[1]) component_model.createIndex(row, 0, component_model.current_nxs_obj[1])
class SignalGrouper(QDialog): def __init__(self, chartData, parent=None): QDialog.__init__(self, parent) self.chartData = chartData self._create() def _create(self): self.mLayout = QVBoxLayout(self) self.gSelector = QListWidget() groups = self.chartData.getDataGroups() self.gSelector.addItems(groups) self.gSelector.currentTextChanged.connect(self._updateGroupList) self.chartData.dataGroupAdded.connect(self.gSelector.addItem) self.chartData.dataGroupAdded.connect(self.gSelector.addItem) self.mLayout.addWidget(self.gSelector) self.sSelector = QListWidget() self.mLayout.addWidget(self.sSelector) groupBtn = QPushButton('Create group from selected') groupBtn.clicked.connect(self.createNewGroup) self.mLayout.addWidget(groupBtn) def _updateGroupList(self): newGroup = self.gSelector.currentItem().text() sStruct = self.chartData[newGroup].getColStructure() self.sSelector.clear() for ws in sStruct: firstChannel = sStruct[ws][0] isOneSignal = self.chartData[newGroup][firstChannel][ws][0] if isOneSignal: item = QListWidgetItem(ws, self.sSelector) item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled) item.setCheckState(Qt.Unchecked) def createNewGroup(self): checkList = [] for i in range(self.sSelector.count()): sItem = self.sSelector.item(i) if sItem.checkState() == Qt.Checked: checkList.append(sItem.text()) if len(checkList) > 0: groupName, result = QInputDialog().getText(self, 'Input', 'Enter group name:') if result: ws = self.gSelector.currentItem().text() sStruct = self.chartData[ws].getColStructure(checkList) sKeys = list(sStruct.keys()) for s in range(len(sKeys)): if sStruct[sKeys[s]] != sStruct[sKeys[s-1]]: print('Signals have diffrent channel sets') return self.chartData.appendSignalGroup(ws, groupName, sStruct[sKeys[0]], checkList) else: return else: return
def gather_all_selected_track(list_widget: QListWidget, file_controller: FileController): user_selected_track = [] for index in range(list_widget.count()): item = list_widget.item(index) if item.checkState() == PySide2.QtCore.Qt.Checked: user_selected_track.append(file_controller.get_track(index)) return user_selected_track
def gather_all_selected_track(self, list_1: QListWidget, list_2: QListWidget): self._user_settings_1 = [] self._user_settings_2 = [] logger.debug("Gathering selected tracks") for index in range(list_1.count()): item = list_1.item(index) if item.checkState() == PySide2.QtCore.Qt.Checked: self._user_settings_1.append(index) logger.debug("Reference 1 Tracks : " + str(self._user_settings_1).strip('[]')) for index in range(list_2.count()): item = list_2.item(index) if item.checkState() == PySide2.QtCore.Qt.Checked: self._user_settings_2.append(index) logger.debug("Reference 2 Tracks : " + str(self._user_settings_2).strip('[]'))
class FieldAttrsDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setLayout(QGridLayout()) self.setWindowTitle("Edit Attributes") self.list_widget = QListWidget() self.list_widget.setMinimumSize(800, 600) self.add_button = QPushButton("Add attr") self.add_button.clicked.connect(self.__add_attr) self.remove_button = QPushButton("Remove attr") self.remove_button.clicked.connect(self._remove_attrs) self.layout().addWidget(self.list_widget, 0, 0, 2, 1) self.layout().addWidget(self.add_button, 0, 1) self.layout().addWidget(self.remove_button, 1, 1) def fill_existing_attrs(self, existing_dataset: h5py.Dataset): for name, value in existing_dataset.attrs.items(): if name not in ATTRS_BLACKLIST: frame = FieldAttrFrame(name, value) self._add_attr(existing_frame=frame) def __add_attr(self): """ Only used for button presses. Any additional arguments from the signal are ignored. """ self._add_attr() def _add_attr(self, existing_frame=None): item = QListWidgetItem() self.list_widget.addItem(item) frame = existing_frame if existing_frame is not None else FieldAttrFrame( ) item.setSizeHint(frame.sizeHint()) self.list_widget.setItemWidget(item, frame) def _remove_attrs(self): for index in self.list_widget.selectedIndexes(): self.list_widget.takeItem(index.row()) def get_attrs(self): attrs_dict = {} for index in range(self.list_widget.count()): item = self.list_widget.item(index) widget = self.list_widget.itemWidget(item) attrs_dict[widget.value[0]] = widget.value[1] return attrs_dict
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)
def add_fields_to_component(component: Component, fields_widget: QListWidget): """ Adds fields from a list widget to a component. :param component: Component to add the field to. :param fields_widget: The field list widget to extract field information such the name and value of each field. """ for i in range(fields_widget.count()): widget = fields_widget.itemWidget(fields_widget.item(i)) try: component.set_field( name=widget.name, value=widget.value, dtype=widget.dtype ) except ValueError as error: show_warning_dialog( f"Warning: field {widget.name} not added", title="Field invalid", additional_info=str(error), parent=fields_widget.parent().parent(), )
class AbstractMultiInputPage(EnableNextOnBackMixin, QWizardPage): def __init__(self, parent): QWizardPage.__init__(self, parent) self.parent_wizard = weakref.proxy(parent) self.label = QLabel() self.line_edit = QLineEdit() self.line_edit.returnPressed.connect(self.add_item) self.add_button = QPushButton() self.add_button.clicked.connect(self.add_item) self.list = QListWidget() self.delete_button = QPushButton('&Delete') self.delete_button.setDisabled(True) self.delete_button.clicked.connect(self.delete_item) grid = QGridLayout(self) grid.addWidget(self.label, 0, 0) grid.addWidget(self.line_edit, 0, 1) grid.addWidget(self.add_button, 0, 2) grid.addWidget(self.list, 1, 0, 1, 2) grid.addWidget(self.delete_button, 1, 2, 1, 1, Qt.AlignTop) self.setLayout(grid) def initializePage(self): if self.list.count() == 0: self.parent_wizard.next_button.setDisabled(True) def add_item(self): if not (name := self.line_edit.text()): return item = QListWidgetItem(name) self.list.addItem(item) self.matrix_add(name) self.line_edit.clear() self.line_edit.setFocus() self.parent_wizard.next_button.setEnabled(True) self.delete_button.setEnabled(True)
class ContinuousCriteriaPage(EnableNextOnBackMixin, QWizardPage): def __init__(self, parent): super().__init__(parent) self.parent_wizard = weakref.proxy(parent) self.setTitle('Continuous criteria') # Radio button, if yes then ask for inputs self.yes = QRadioButton( '&Yes, there are criteria that needs to be calculated') no = QRadioButton( 'N&o, I will manually give a rating for every choice and criteria') no.setChecked(True) self.registerField('yes', self.yes) self.yes.toggled.connect(self.toggled) group = QButtonGroup(self) group.addButton(self.yes) group.addButton(no) # Duplicated from AbstractMultiInputPage self.line_edit = QLineEdit() self.list_widget = QListWidget() self.add_button = QPushButton('&Add criterion') self.delete_button = QPushButton('&Delete') self.line_edit.setDisabled(True) self.list_widget.setDisabled(True) self.add_button.setDisabled(True) self.delete_button.setDisabled(True) self.line_edit.returnPressed.connect(self.add_item) self.add_button.clicked.connect(self.add_item) self.delete_button.clicked.connect(self.delete_item) grid = QGridLayout(self) grid.addWidget(self.yes, 0, 0) grid.addWidget(no, 1, 0) grid.addWidget(self.line_edit, 2, 0) grid.addWidget(self.add_button, 2, 1) grid.addWidget(self.list_widget, 3, 0) grid.addWidget(self.delete_button, 3, 1, Qt.AlignTop) self.setLayout(grid) def initializePage(self): for criterion in self.parent_wizard.main_parent.matrix.continuous_criteria: self.list_widget.addItem(QListWidgetItem(criterion)) if self.list_widget.count() != 0: self.yes.setChecked(True) self.parent_wizard.next_button.setEnabled(True) def toggled(self, checked: bool): if checked: self.line_edit.setEnabled(True) self.list_widget.setEnabled(True) self.add_button.setEnabled(True) self.parent_wizard.next_button.setDisabled(True) else: self.line_edit.setDisabled(True) self.list_widget.setDisabled(True) self.parent_wizard.next_button.setEnabled(True) def add_item(self): # Duplicated if not (name := self.line_edit.text()): return item = QListWidgetItem(name) self.list_widget.addItem(item) self.line_edit.clear() self.line_edit.setFocus() self.parent_wizard.next_button.setEnabled(True) self.delete_button.setEnabled(True) self.parent_wizard.main_parent.line_edit_cc_tab.setText(name) self.parent_wizard.main_parent.matrix.add_continuous_criterion( name, weight=float('nan')) self.parent_wizard.main_parent.add_continuous_criteria()
class PlaylistWidget(QWidget): itemPlayed = Signal(list) def __init__(self): super().__init__() self.layout = QVBoxLayout() self.open_playlist_btn = QPushButton("Open Playlist") self.playlist_list = QListWidget() self.setAcceptDrops(True) self.layout.addWidget(self.open_playlist_btn) self.layout.addWidget(self.playlist_list) self.open_playlist_btn.clicked.connect(self.open_playlist) self.playlist_list.itemDoubleClicked.connect( self.on_playlist_list_doubleclick) self.setLayout(self.layout) def open_playlist(self): filename = QFileDialog.getOpenFileName(self, "Select a playlist") if filename[0]: self.load_playlist(filename[0]) def load_playlist(self, playlist_filepath): self.clear_playlist() p = Playlist.load_from_file(playlist_filepath) playlist_item_widget_list = [ PlaylistItemWidget(playlist_item) for playlist_item in p.playlist_content ] for playlist_item in playlist_item_widget_list: self.playlist_list.addItem(playlist_item) def load_playlist_item(self, playlist_item_widget): self.itemPlayed.emit(playlist_item_widget.playlist_item.media_list) def clear_playlist(self): while self.playlist_list.count() > 0: item = self.playlist_list.takeItem(0) def on_playlist_list_doubleclick(self, playlist_item): self.load_playlist_item(playlist_item) def dragEnterEvent(self, event: PySide2.QtGui.QDragEnterEvent) -> None: if event.mimeData().hasUrls: event.accept() else: event.ignore() def dragMoveEvent(self, event): if event.mimeData().hasUrls: event.setDropAction(Qt.CopyAction) event.accept() else: event.ignore() def dropEvent(self, event: PySide2.QtGui.QDropEvent) -> None: if event.mimeData().hasUrls: event.setDropAction(Qt.CopyAction) event.accept() if len(event.mimeData().urls()) > 0: url = event.mimeData().urls()[0] self.load_playlist(url.toLocalFile()) else: event.ignore()
from PySide2 import QtCore from PySide2.QtWidgets import QApplication, QListWidget, QComboBox if __name__ == '__main__': """ Two views using each one a instance of our data When the data is change in QListWidget(), the data in QComboBox will not be changed """ app = QApplication(sys.argv) # Let's make the QListWidget show this data data = ["ONE", "TWO", "THREE", "FOUR", "FIVE"] list_widget = QListWidget() list_widget.show() list_widget.addItems(data) # Let's make elements on QListWidget editable for index in range(list_widget.count()): item = list_widget.item(index) item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable) # A comboBox showing the same data comboBox = QComboBox() comboBox.show() comboBox.addItems(data) sys.exit(app.exec_())
class Widget(QWidget): def __init__(self): QWidget.__init__(self) # Data for plotting and marker data storage self.data = {} self.slice_index = { 'ind1': 0, 'ind2': 0 } self.marker_id = 0 self.marker_ind = [] self.marker_setpoint = { 'Marker1': 0, 'Marker2': 0, } # Error message dialog widget self.error_popup = QErrorMessage() self.error_popup.setWindowTitle('Snap file error') # Left (List of Checkboxes) self.list_widget = QListWidget() #Resize width and height self.list_widget.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.list_widget.setMinimumWidth(200) self.list_widget.setMaximumWidth(400) # self.list_widget.setMinimumHeight(300) self.list_widget.setMaximumHeight(500) # Signal groupbox self.signal_groupbox = QGroupBox('Available Signals') self.signal_groupbox.setMinimumWidth(200) self.signal_groupbox.setMaximumWidth(350) self.signal_groupbox.setMinimumHeight(100) self.sig_group_layout = QVBoxLayout() self.signal_groupbox.setLayout(self.sig_group_layout) # Statistics groupbox self.stats_groupbox = QGroupBox('Statistics') self.stats_groupbox.setMinimumWidth(200) self.stats_groupbox.setMaximumWidth(350) self.stats_groupbox.setMinimumHeight(240) self.stats_group_layout = QFormLayout() # Label initiation # Marker Time 1 self.mark_one_time_label = QLabel('Marker1_time: ') self.mark_one_time_value = QLabel() # Marker Time 2 self.mark_two_time_label = QLabel('Marker2_time: ') self.mark_two_time_value = QLabel() # On/Off labels for 0/1 signals counter self.on_off_label = QLabel('On/Off: ') self.on_off_value = QLabel() # Mean value self.mean_label = QLabel('Mean: ') self.mean_value = QLabel() # Standard deviation self.std_label = QLabel('Sigma(STD): ') self.std_value = QLabel() # Minimal value self.min_label = QLabel('Min: ') self.min_value = QLabel() # Maximual value self.max_label = QLabel('Max: ') self.max_value = QLabel() # Max - Min value self.val_diff_label = QLabel('Max-Min: ') self.val_diff_value = QLabel() # Time difference (X-axis) self.time_diff_label = QLabel('Time_diff: ') self.time_diff_value = QLabel('') # Row addition of labels self.stats_group_layout.addRow(self.mark_one_time_label, self.mark_one_time_value) self.stats_group_layout.addRow(self.mark_two_time_label, self.mark_two_time_value) self.stats_group_layout.addRow(self.time_diff_label, self.time_diff_value) self.stats_group_layout.addRow(self.on_off_label, self.on_off_value) self.stats_group_layout.addRow(self.mean_label, self.mean_value) self.stats_group_layout.addRow(self.std_label, self.std_value) self.stats_group_layout.addRow(self.min_label, self.min_value) self.stats_group_layout.addRow(self.max_label, self.max_value) self.stats_group_layout.addRow(self.val_diff_label, self.val_diff_value) self.stats_groupbox.setLayout(self.stats_group_layout) # Set markers section of the application (bottom left) self.marker_grid = QGridLayout() self.marker_one_notice = QLabel() self.marker_two_notice = QLabel() self.set_marker_one_label = QLabel('Set Marker1:') self.set_marker_two_label = QLabel('Set Marker2:') self.set_marker_one_value = QLineEdit() self.set_marker_one_value.setMaximumWidth(100) self.set_marker_two_value = QLineEdit() self.set_marker_two_value.setMaximumWidth(100) self.marker_grid.addWidget(self.set_marker_one_label) self.marker_grid.addWidget(self.set_marker_one_value) self.marker_grid.addWidget(self.marker_one_notice) self.marker_grid.addWidget(self.set_marker_two_label) self.marker_grid.addWidget(self.set_marker_two_value) self.marker_grid.addWidget(self.marker_two_notice) # Leftside app layout self.v_layout = QVBoxLayout() self.v_layout.addWidget(self.list_widget) self.v_layout.addWidget(self.signal_groupbox) self.v_layout.addWidget(self.stats_groupbox) self.v_layout.addLayout(self.marker_grid) # Matplotlib figure self.fig = Figure(figsize=(5, 3)) self.canvas = FigureCanvas(self.fig) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.ax = self.canvas.figure.subplots() self.ax.grid() self.ax.set_xlabel('Time[s]') self.fig.suptitle('Parameter Plot') # QWidget Layout self.h_layout = QHBoxLayout() self.h_layout.addLayout(self.v_layout) self.h_layout.addWidget(self.canvas) # Set the layout to the QWidget self.setLayout(self.h_layout) # ListWidget and plot connections self.list_widget.itemChanged.connect(self.item_changed) self.click_event = self.fig.canvas.mpl_connect('button_press_event', self.on_click) self.set_marker_one_value.returnPressed.connect( lambda: self.add_marker('one')) self.set_marker_two_value.returnPressed.connect( lambda: self.add_marker('two')) # Add radio button when signal is checked for plotting def add_radio_button(self, name): self.rad_btn = QRadioButton(name) self.rad_btn.toggled.connect(self.calculate_signal_stats) self.sig_group_layout.addWidget(self.rad_btn) # Remove radio button when signal is unchecked for plotting def remove_radio_button(self, name): for item in self.signal_groupbox.children(): try: if item.text() == name: item.setParent(None) except AttributeError: pass # Remove all radiobuttons on new data load def clear_signals(self): count = 0 for item in self.signal_groupbox.children(): if count == 0: count = 1 continue else: item.setParent(None) # Check state of all radiobuttons, if none is checked remove stats values def check_signals(self): count = 0 num_of_check = 0 for item in self.signal_groupbox.children(): if count == 0: count = 1 continue else: if item.isChecked(): num_of_check += 1 # If no radiobuttons are checked, remove stats if num_of_check == 0: self.mean_value.setText('') self.std_value.setText('') self.max_value.setText('') self.min_value.setText('') self.val_diff_value.setText('') self.on_off_value.setText('') # Item additon of listWidget def fill_list(self, list_items): self.list_widget.clear() for column in list_items: item = QListWidgetItem(column) item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) item.setCheckState(Qt.Unchecked) self.list_widget.addItem(item) self.show() # If new data is loaded, replace the old one def replace_data(self, temp): if not temp == self.data: self.data = temp self.clear_signals() @Slot() # Item state changed in listWidget event handler def item_changed(self, item): if item.checkState() == Qt.Unchecked: self.remove_plot(item.text()) self.remove_radio_button(item.text()) self.check_signals() else: self.add_plot(self.data['Time'], self.data[item.text()], item.text()) self.add_radio_button(item.text()) # Method for plotting data def add_plot(self, x_data, y_data, name): self.ax.plot(x_data, y_data, label=name, picker=3) self.ax.grid(True) self.ax.relim() self.ax.set_xlabel('Time[s]') self.ax.autoscale_view() self.ax.legend(loc='upper right') self.canvas.draw() # Method for marker addition via QLineEdit def add_marker(self, label): # Check if any signal is plotted sig_count = 0 for i in range(self.list_widget.count()): if self.list_widget.item(i).checkState() == Qt.Checked: sig_count += 1 if sig_count == 0: self.marker_one_notice.setText('No active signal!') self.marker_two_notice.setText('No active signal!') return try: max_time = self.data['Time'][-1] min_time = self.data['Time'][0] except KeyError: self.marker_one_notice.setText('Signal data not loaded!') self.marker_two_notice.setText('Signal data not loaded!') if label == 'one': try: mark1_value = float(self.set_marker_one_value.text()) if mark1_value < max_time and mark1_value > min_time: if self.marker_id == 0: self.marker_id += 1 label_id = self.marker_id elif self.marker_id == 1: self.marker_id += 1 label_id = self.marker_id self.remove_marker('first') else: self.remove_marker('first') label_id = 1 self.marker_one_notice.setText('') self.marker_setpoint['Marker1'] = mark1_value self.mark_one_time_value.setText( self.set_marker_one_value.text()) self.calculate_marker_stats() self.calculate_signal_stats() # Draw the marker L = self.ax.axvline( x=float(self.set_marker_one_value.text()), linestyle='dashed', color='red', label='_Marker' + str(label_id)) self.fig.canvas.draw() else: self.marker_one_notice.setText('Marker1 out of bounds') except ValueError: self.marker_one_notice.setText('Non-Valid value entered!') else: try: mark2_value = float(self.set_marker_two_value.text()) if mark2_value < max_time and mark2_value > min_time: if self.marker_id == 1: self.marker_id += 1 label_id = self.marker_id elif self.marker_id == 2: label_id = 2 self.remove_marker('second') else: self.marker_two_notice.setText('Marker1 not placed') self.marker_two_notice.setText('') self.marker_setpoint['Marker2'] = mark2_value self.mark_two_time_value.setText( self.set_marker_two_value.text()) self.calculate_marker_stats() self.calculate_signal_stats() # Draw the marker L = self.ax.axvline( x=float(self.set_marker_two_value.text()), linestyle='dashed', color='red', label='_Marker' + str(label_id)) self.fig.canvas.draw() else: self.marker_two_notice.setText('Marker2 out of bounds') except: self.marker_two_notice.setText('Non-Valid value entered!') # Marker removal method def remove_marker(self, label): for item in self.ax.lines: if 'Marker' in item.get_label(): self.marker_ind.append(item) # If there are two markers remove them from plot and adjust marker # time labels if label == 'both': self.marker_ind[0].remove() self.marker_ind[1].remove() self.marker_ind = [] self.marker_setpoint['Marker1'] = 0 self.marker_setpoint['Marker2'] = 0 self.mark_one_time_value.setText('') self.mark_two_time_value.setText('') self.time_diff_value.setText('') self.marker_id = 0 # Remove only marker1 elif label == 'first': self.marker_ind[0].remove() self.marker_ind = [] self.marker_setpoint['Marker1'] = 0 self.mark_one_time_value.setText('') self.marker_id -= 1 elif label == 'second': self.marker_ind[1].remove() self.marker_ind = [] self.marker_setpoint['Marker2'] = 0 self.mark_two_time_value.setText('') self.marker_id -= 1 self.ax.set_xlabel('Time[s]') self.canvas.draw() # Method for plot removal def remove_plot(self, name): cnt = 0 for item in self.ax.lines: if item.get_label() == name: self.ax.lines[cnt].remove() cnt += 1 self.ax.relim() self.ax.autoscale_view() self.ax.legend(loc='upper right') self.ax.set_xlabel('Time[s]') self.canvas.draw() # Check if all elements are unticked counter = 0 for i in range(self.list_widget.count()): if self.list_widget.item(i).checkState() == Qt.Checked: counter +=1 if counter == 0: self.remove_marker('both') # On click event for plot, only two markers can be active at the time def on_click(self, event): try: # Catch left click event if event.button == 1: x = event.xdata if self.marker_id < 2: if self.marker_id == 0: self.marker_setpoint['Marker1'] = round(x) self.mark_one_time_value.setText( str(self.marker_setpoint['Marker1'])) self.calculate_marker_stats() self.calculate_signal_stats() else: self.marker_setpoint['Marker2'] = round(x) self.mark_two_time_value.setText( str(self.marker_setpoint['Marker2'])) self.calculate_marker_stats() self.calculate_signal_stats() self.marker_id += 1 L = self.ax.axvline(x=x, linestyle='dashed', color='red', label='_Marker' + str(self.marker_id)) self.fig.canvas.draw() # Catch right click event elif event.button == 3: self.remove_marker('both') except TypeError: pass # Marker analysis method def calculate_marker_stats(self): if self.marker_setpoint['Marker2'] == 0: diff = self.data['Time'][-1] - self.marker_setpoint['Marker1'] self.time_diff_value.setText(str(diff)) else: diff = self.marker_setpoint['Marker2'] - \ self.marker_setpoint['Marker1'] self.time_diff_value.setText(convert_seconds(diff)) # Signal analysis method def calculate_signal_stats(self): self.check_signals() selected_signal = '' signal_data = [] num_off, num_on = 0, 0 for item in self.signal_groupbox.children(): try: if item.isChecked(): # Signal extraction block selected_signal = item.text() # If only one marker, Marker1 is placed on graph if self.marker_setpoint['Marker2'] == 0 and self.marker_setpoint['Marker1'] != 0: for i in range(len(self.data['Time'])): if self.data['Time'][i] > self.marker_setpoint['Marker1']: self.slice_index['ind1'] = i break signal_data = np.asarray( self.data[selected_signal][self.slice_index['ind1']:], dtype=np.float32) # Both markers, Marker1 and Marker2 are present on graph elif self.marker_setpoint['Marker1'] != 0 and self.marker_setpoint['Marker2'] != 0: for i in range(len(self.data['Time'])): if self.data['Time'][i] > self.marker_setpoint['Marker1']: self.slice_index['ind1'] = i break for i in range(len(self.data['Time'])): if self.data['Time'][len(self.data['Time']) - i - 1] < self.marker_setpoint['Marker2']: self.slice_index['ind2'] = len(self.data['Time']) - i break signal_data = np.asarray( self.data[selected_signal][self.slice_index['ind1']:self.slice_index['ind2'] - 1], dtype=np.float32) # No markers present, whole signal stats are showed else: signal_data = np.asarray(self.data[selected_signal], dtype=np.float32) try: # Signal mean calculation self.mean_value.setText(str(np.mean(signal_data))) # Standard deviation self.std_value.setText(str(np.std(signal_data))) # Maximum value self.max_value.setText(str(np.max(signal_data))) # Minimum value self.min_value.setText(str(np.min(signal_data))) # Max - Min self.val_diff_value.setText(str(np.max(signal_data) - (np.min(signal_data)))) if np.max(signal_data) == 1.0: for i in range(len(signal_data)): if i != len(signal_data) - 1: temp = signal_data[i] - signal_data[i+1] if temp == 1: num_off += 1 elif temp == -1: num_on += 1 self.on_off_value.setText(str(num_on) + '\\' + str(num_off)) except ValueError: err_line1 = 'Missing data, result is empty array!' err_line2 = '\n Please check snap file.' self.error_popup.showMessage(err_line1 + err_line2) except AttributeError: pass
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 PiecesPlayer(QWidget): """ main widget of application (used as widget inside PiecesMainWindow) """ def __init__(self, parent): """ standard constructor: set up class variables, ui elements and layout """ # TODO: split current piece info into separate lineedits for title, album name and length # TODO: make time changeable by clicking next to the slider (not only # by dragging the slider) # TODO: add "about" action to open info dialog in new "help" menu # TODO: add option to loop current piece (?) # TODO: more documentation # TODO: add some "whole piece time remaining" indicator? (complicated) # TODO: implement a playlist of pieces that can be edited and enable # going back to the previous piece (also un- and re-shuffling?) # TODO: implement debug dialog as menu action (if needed) if not isinstance(parent, PiecesMainWindow): raise ValueError('Parent widget must be a PiecesMainWindow') super(PiecesPlayer, self).__init__(parent=parent) # -- declare and setup variables for storing information -- # various data self._set_str = '' # string of currently loaded directory sets self._pieces = {} # {<piece1>: [<files piece1 consists of>], ...} self._playlist = [] # list of keys of self._pieces (determines order) self._shuffled = True # needed for (maybe) reshuffling when looping # doc for self._history: # key: timestamp ('HH:MM:SS'), # value: info_str of piece that started playing at that time self._history = {} self._status = 'Paused' self._current_piece = {'title': '', 'files': [], 'play_next': 0} self._default_volume = 60 # in percent from 0 - 100 self._volume_before_muted = self._default_volume # set to true by self.__event_movement_ended and used by self.__update self._skip_to_next = False # vlc-related variables self._vlc_instance = VLCInstance() self._vlc_mediaplayer = self._vlc_instance.media_player_new() self._vlc_mediaplayer.audio_set_volume(self._default_volume) self._vlc_medium = None self._vlc_events = self._vlc_mediaplayer.event_manager() # -- create and setup ui elements -- # buttons self._btn_play_pause = QPushButton(QIcon(get_icon_path('play')), '') self._btn_previous = QPushButton(QIcon(get_icon_path('previous')), '') self._btn_next = QPushButton(QIcon(get_icon_path('next')), '') self._btn_volume = QPushButton(QIcon(get_icon_path('volume-high')), '') self._btn_loop = QPushButton(QIcon(get_icon_path('loop')), '') self._btn_loop.setCheckable(True) self._btn_play_pause.clicked.connect(self.__action_play_pause) self._btn_previous.clicked.connect(self.__action_previous) self._btn_next.clicked.connect(self.__action_next) self._btn_volume.clicked.connect(self.__action_volume_clicked) # labels self._lbl_current_piece = QLabel('Current piece:') self._lbl_movements = QLabel('Movements:') self._lbl_time_played = QLabel('00:00') self._lbl_time_left = QLabel('-00:00') self._lbl_volume = QLabel('100%') # needed so that everything has the same position # independent of the number of digits of volume self._lbl_volume.setMinimumWidth(55) # sliders self._slider_time = QSlider(Qt.Horizontal) self._slider_volume = QSlider(Qt.Horizontal) self._slider_time.sliderReleased.connect( self.__event_time_changed_by_user) self._slider_volume.valueChanged.connect(self.__event_volume_changed) self._slider_time.setRange(0, 100) self._slider_volume.setRange(0, 100) self._slider_volume.setValue(self._default_volume) self._slider_volume.setMinimumWidth(100) # other elements self._checkbox_loop_playlist = QCheckBox('Loop playlist') self._lineedit_current_piece = QLineEdit() self._lineedit_current_piece.setReadOnly(True) self._lineedit_current_piece.textChanged.connect( self.__event_piece_text_changed) self._listwidget_movements = QListWidget() self._listwidget_movements.itemClicked.connect( self.__event_movement_selected) # -- create layout and insert ui elements-- self._layout = QVBoxLayout(self) # row 0 (name of current piece) self._layout_piece_name = QHBoxLayout() self._layout_piece_name.addWidget(self._lbl_current_piece) self._layout_piece_name.addWidget(self._lineedit_current_piece) self._layout.addLayout(self._layout_piece_name) # rows 1 - 5 (movements of current piece) self._layout.addWidget(self._lbl_movements) self._layout.addWidget(self._listwidget_movements) # row 6 (time) self._layout_time = QHBoxLayout() self._layout_time.addWidget(self._lbl_time_played) self._layout_time.addWidget(self._slider_time) self._layout_time.addWidget(self._lbl_time_left) self._layout.addLayout(self._layout_time) # row 7 (buttons and volume) self._layout_buttons_and_volume = QHBoxLayout() self._layout_buttons_and_volume.addWidget(self._btn_play_pause) self._layout_buttons_and_volume.addWidget(self._btn_previous) self._layout_buttons_and_volume.addWidget(self._btn_next) self._layout_buttons_and_volume.addWidget(self._btn_loop) self._layout_buttons_and_volume.addSpacing(40) # distance between loop and volume buttons: min. 40, but stretchable self._layout_buttons_and_volume.addStretch() self._layout_buttons_and_volume.addWidget(self._btn_volume) self._layout_buttons_and_volume.addWidget(self._slider_volume) self._layout_buttons_and_volume.addWidget(self._lbl_volume) self._layout.addLayout(self._layout_buttons_and_volume) # -- setup hotkeys -- self._KEY_CODES_PLAY_PAUSE = [269025044] self._KEY_CODES_NEXT = [269025047] self._KEY_CODES_PREVIOUS = [269025046] self._keyboard_listener = keyboard.Listener(on_press=self.__on_press) self._keyboard_listener.start() QShortcut(QKeySequence('Space'), self, self.__action_play_pause) # -- various setup -- self._timer = QTimer(self) self._timer.timeout.connect(self.__update) self._timer.start(100) # update every 100ms self.setMinimumWidth(900) self.setMinimumHeight(400) # get directory set(s) input and set up self._pieces # (exec_ means we'll wait for the user input before continuing) DirectorySetChooseDialog(self, self.set_pieces_and_playlist).exec_() # skip to next movement / next piece when current one has ended self._vlc_events.event_attach(VLCEventType.MediaPlayerEndReached, self.__event_movement_ended) def __action_next(self): """ switches to next file in self._current_piece['files'] or to the next piece, if the current piece has ended """ reset_pause_after_current = False # current movement is last of the current piece if self._current_piece['play_next'] == -1: if len(self._playlist) == 0: # reached end of playlist if self._btn_loop.isChecked(): self._playlist = list(self._pieces.keys()) if self._shuffled: shuffle(self._playlist) return if self._status == 'Playing': self.__action_play_pause() self._current_piece['title'] = '' self._current_piece['files'] = [] self._current_piece['play_next'] = -1 self._lineedit_current_piece.setText('') self.__update_movement_list() self.parentWidget().update_status_bar( self._status, 'End of playlist reached.') return else: if self.parentWidget().get_exit_after_current(): self.parentWidget().exit() if self.parentWidget().get_pause_after_current(): self.__action_play_pause() reset_pause_after_current = True # reset of the menu action will be at the end of this # function, or else we won't stay paused self._current_piece['title'] = self._playlist.pop(0) self._current_piece['files'] = [ p[1:-1] for p in self._pieces[self._current_piece['title']] ] # some pieces only have one movement self._current_piece['play_next'] = \ 1 if len(self._current_piece['files']) > 1 else -1 self.__update_vlc_medium(0) self._lineedit_current_piece.setText( create_info_str(self._current_piece['title'], self._current_piece['files'])) self.__update_movement_list() self._history[datetime.now().strftime('%H:%M:%S')] = \ self._lineedit_current_piece.text() else: self.__update_vlc_medium(self._current_piece['play_next']) # next is last movement if self._current_piece['play_next'] == \ len(self._current_piece['files']) - 1: self._current_piece['play_next'] = -1 else: # there are at least two movements of current piece left self._current_piece['play_next'] += 1 if self._status == 'Paused' and not reset_pause_after_current: self.__action_play_pause() elif reset_pause_after_current: self.parentWidget().set_pause_after_current(False) else: self._vlc_mediaplayer.play() self.parentWidget().update_status_bar( self._status, f'{len(self._pieces) - len(self._playlist)}/{len(self._pieces)}') def __action_play_pause(self): """ (gets called when self._btn_play_pause is clicked) toggles playing/pausing music and updates everything as needed """ # don't do anything now (maybe end of playlist reached?) if self._current_piece['title'] == '': return if self._status == 'Paused': if not self._vlc_medium: self.__action_next() self._vlc_mediaplayer.play() self._btn_play_pause.setIcon(QIcon(get_icon_path('pause'))) self._status = 'Playing' else: self._vlc_mediaplayer.pause() self._btn_play_pause.setIcon(QIcon(get_icon_path('play'))) self._status = 'Paused' self.parentWidget().update_status_bar( self._status, f'{len(self._pieces) - len(self._playlist)}/{len(self._pieces)}') def __action_previous(self): """ (called when self._btn_previous ist clicked) goes back one movement of the current piece, if possible (cannot go back to previous piece) """ # can't go back to previous piece, but current one has no or one movement if len(self._current_piece['files']) <= 1: pass # currently playing first movement, so nothing to do as well elif self._current_piece['play_next'] == 1: pass else: # we can go back one movement # currently at last movement if self._current_piece['play_next'] == -1: # set play_next to last movement self._current_piece['play_next'] = \ len(self._current_piece['files']) - 1 else: # currently before last movement # set play_next to current movement self._current_piece['play_next'] -= 1 self._vlc_mediaplayer.stop() self.__update_vlc_medium(self._current_piece['play_next'] - 1) self._vlc_mediaplayer.play() def __action_volume_clicked(self): """ (called when self._btn_volume is clicked) (un)mutes volume """ if self._slider_volume.value() == 0: # unmute volume self._slider_volume.setValue(self._volume_before_muted) else: # mute volume self._volume_before_muted = self._slider_volume.value() self._slider_volume.setValue(0) def __event_movement_ended(self, event): """ (called when self._vlc_media_player emits a MediaPlayerEndReached event) sets self._skip_to_next to True so the next self.__update call will trigger self.__action_next """ self._skip_to_next = True def __event_movement_selected(self): """ (called when self._listwidget_movements emits itemClicked) skips to the newly selected movement """ index = self._listwidget_movements.indexFromItem( self._listwidget_movements.currentItem()).row() # user selected a movement different from the current one if index != self.__get_current_movement_index(): self._current_piece['play_next'] = index self.__action_next() def __event_piece_text_changed(self): """ (called when self._lineedit_current_piece emits textChanged) ensures that the user sees the beginning of the text in self._lineedit_current_piece (if text is too long, the end will be cut off and the user must scroll manually to see it) """ self._lineedit_current_piece.setCursorPosition(0) def __event_volume_changed(self): """ (called when value of self._slider_volume changes) updates text of self._lbl_volume to new value of self._slider_value and sets icon of self._btn_volume to a fitting one """ volume = self._slider_volume.value() self._lbl_volume.setText(f'{volume}%') if volume == 0: self._btn_volume.setIcon(QIcon(get_icon_path('volume-muted'))) elif volume < 34: self._btn_volume.setIcon(QIcon(get_icon_path('volume-low'))) elif volume < 67: self._btn_volume.setIcon(QIcon(get_icon_path('volume-medium'))) else: self._btn_volume.setIcon(QIcon(get_icon_path('volume-high'))) self._vlc_mediaplayer.audio_set_volume(volume) def __event_time_changed_by_user(self): """ (called when user releases self._slider_time) synchronizes self._vlc_mediaplayer's position to the new value of self._slider_time """ self._vlc_mediaplayer.set_position(self._slider_time.value() / 100) def __get_current_movement_index(self): """ returns the index of the current movement in self._current_piece['files'] """ play_next = self._current_piece['play_next'] if play_next == -1: return len(self._current_piece['files']) - 1 else: return play_next - 1 def __on_press(self, key): """ (called by self._keyboard_listener when a key is pressed) looks up key code corresponding to key and calls the appropriate action function """ try: # key is not always of the same type (why would it be?!) key_code = key.vk except AttributeError: key_code = key.value.vk if key_code in self._KEY_CODES_PLAY_PAUSE: self.__action_play_pause() elif key_code in self._KEY_CODES_NEXT: self.__action_next() elif key_code in self._KEY_CODES_PREVIOUS: self.__action_previous() def __update_movement_list(self): """ removes all items currently in self._listwidget_movements and adds everything in self._current_piece['files] """ # TODO: use ID3 title instead of filename while self._listwidget_movements.count() > 0: self._listwidget_movements.takeItem(0) files = self._current_piece['files'] if os_name == 'nt': # windows paths look different than posix paths # remove path to file, title number and .mp3 ending files = [i[i.rfind('\\') + 3:-4] for i in files] else: files = [i[i.rfind('/') + 4:-4] for i in files] self._listwidget_movements.addItems(files) def __update(self): """ (periodically called when self._timer emits timeout signal) updates various ui elements""" # -- select currently playing movement in self._listwidget_movements -- if self._listwidget_movements.count() > 0: self._listwidget_movements.item( self.__get_current_movement_index()).setSelected(True) # -- update text of self._lbl_time_played and self._lbl_time_left, # if necessary -- if self._vlc_medium: try: time_played = self._vlc_mediaplayer.get_time() medium_duration = self._vlc_medium.get_duration() # other values don't make sense (but do occur) if (time_played >= 0) and (time_played <= medium_duration): self._lbl_time_played.setText( get_time_str_from_ms(time_played)) else: self._lbl_time_played.setText(get_time_str_from_ms(0)) self._lbl_time_left.setText( f'-{get_time_str_from_ms(medium_duration - time_played)}') except OSError: # don't know why that occurs sometimes pass # -- update value of self._slider_time -- # don't reset slider to current position if user is dragging it if not self._slider_time.isSliderDown(): try: self._slider_time.setValue( self._vlc_mediaplayer.get_position() * 100) except OSError: # don't know why that occurs sometimes pass if self._skip_to_next: self._skip_to_next = False self.__action_next() def __update_vlc_medium(self, files_index): old_medium = self._vlc_medium self._vlc_medium = self._vlc_instance.media_new( self._current_piece['files'][files_index]) self._vlc_medium.parse() self._vlc_mediaplayer.set_media(self._vlc_medium) if old_medium: # only release if not None old_medium.release() def get_history(self): """ getter function for parent widget """ return self._history def get_set_str(self): """ getter function for parent widget """ return self._set_str if self._set_str != '' \ else 'No directory set loaded.' def set_pieces_and_playlist(self, pieces, playlist, set_str, shuffled): """ needed so that DirectorySetChooseDialog can set our self._pieces and self._playlist """ # just to be sure if isinstance(pieces, dict) and isinstance(playlist, list): self._vlc_mediaplayer.stop() self._set_str = set_str self._pieces = pieces self._playlist = playlist self._shuffled = shuffled self._current_piece['title'] = self._playlist.pop(0) self._current_piece['files'] = [ p.replace('"', '') for p in self._pieces[self._current_piece['title']] ] self._current_piece['play_next'] = \ 1 if len(self._current_piece['files']) > 1 else -1 self._lineedit_current_piece.setText( create_info_str(self._current_piece['title'], self._current_piece['files'])) self.__update_movement_list() self.__update_vlc_medium(0) self._history[datetime.now().strftime('%H:%M:%S')] = \ self._lineedit_current_piece.text() def exit(self): """ exits cleanly """ try: # don't know why that occurs sometimes self._vlc_mediaplayer.stop() self._vlc_mediaplayer.release() self._vlc_instance.release() except OSError: pass self._keyboard_listener.stop()
class ClientWindow(QDialog): def __init__(self): super(ClientWindow, self).__init__() self.resize(823,511) self.setWindowTitle('用户客户端') self.friendList = QListWidget() self.friendList.doubleClicked.connect(self.changeFriendText) self.friendList.clicked.connect(self.changeFriendText) self.portLine = QLineEdit() self.portLine.setText('12345') self.connectBtn = QPushButton('连接') self.connectBtn.clicked.connect(self.setupFun) self.messageText = QTextEdit() self.messageLine = QLineEdit() self.sendBtn = QPushButton('发送') self.sendBtn.clicked.connect(self.sendFun) self.text = '' self.userText = {} self.initUI() self.palette = QPalette() self.palette.setBrush(QPalette.Background, QBrush(QPixmap('./image/background.jpg'))) self.setPalette(self.palette) self.client = Client() self.client.trigger.connect(self.updateTextFun) self.ininData() def changeFriendText(self): currentItem = self.friendList.currentItem().text() self.text = self.userText[currentItem] self.messageText.setText(self.text) def ininData(self): self.friendList.addItem('broadcast') self.userText['broadcast'] = '' self.friendList.setCurrentItem(self.friendList.item(0)) def initUI(self): mainLayout = QVBoxLayout() mainWidget = QWidget() widget1 = QWidget() layout1 = QHBoxLayout() layout1.addWidget(self.portLine) layout1.addWidget(self.connectBtn) widget1.setLayout(layout1) mainLayout.addWidget(widget1) widget2 = QWidget() layout2 = QHBoxLayout() layout2.addWidget(self.messageText) layout2.addWidget(self.friendList) layout2.setStretch(0,2) layout2.setStretch(0,1) widget2.setLayout(layout2) mainLayout.addWidget(widget2) # mainLayout.addStretch(1) widget3 = QWidget() layout3 = QHBoxLayout() layout3.addWidget(self.messageLine) layout3.addWidget(self.sendBtn) widget3.setLayout(layout3) mainLayout.addWidget(widget3) self.setLayout(mainLayout) def setupFun(self): port = int(self.portLine.text()) self.client.setupFun(port) self.client.start() def sendFun(self): addr = self.friendList.currentItem().text() message = self.messageLine.text() if message != 'add' and message != 'del': self.userText[addr] += '我:' + message + '\n' self.changeFriendText() if addr != 'broadcast': addr = ('localhost',int(addr)) self.client.sendFun(message,addr) self.messageLine.setText('') def updateTextFun(self,data): currentItem = self.friendList.currentItem().text() data = json.loads(data) if 'cmd' in data.keys(): if data['cmd'] == 'add': fromUser = str(data['from']) addr = ('127.0.0.1',int(fromUser)) item = self.friendList.findItems(fromUser,Qt.MatchExactly) if fromUser not in self.userText.keys(): self.userText[fromUser] = '' if len(item) == 0: self.friendList.addItem(fromUser) self.client.sendFun('add',addr) # 已添加 elif data['cmd'] == 'del': fromUser = str(data['from']) rows = self.friendList.count() for row in range(rows): if fromUser == self.friendList.item(row).text(): self.friendList.takeItem(row) else: message = data['message'] fromUser = str(data['from']) if fromUser != 'broadcast': addr = ('127.0.0.1', int(fromUser)) item = self.friendList.findItems(fromUser, Qt.MatchExactly) if fromUser not in self.userText.keys(): self.userText[fromUser] = '' if len(item) == 0: self.friendList.addItem(fromUser) self.client.sendFun('add', addr) # 已添加 if message != 'add' and message != 'del': self.userText[fromUser] += fromUser + ':' + message + '\n' if fromUser == currentItem: self.text = self.userText[fromUser] self.messageText.setText(self.text) def closeEvent(self, event:PySide2.QtGui.QCloseEvent): self.client.closeFun()
class Window(QMainWindow): def __init__(self): super(Window, self).__init__() self.lastProjectDirectory = None self.view = None self.items = dict() self.loadSettings() self.setWindowTitle("2D Tagger") self.createActions() self.createMenus() self.createFileList() self.createContainer() self.createStatusBar() def loadSettings(self): try: with io.open(Path(__file__).parent / 'app.json', 'r') as f: data = json.load(f) if 'lastProjectDirectory' in data: self.lastProjectDirectory = data['lastProjectDirectory'] except: pass def saveSettings(self): data = dict() data['lastProjectDirectory'] = self.lastProjectDirectory with io.open(Path(__file__).parent / 'app.json', 'w') as f: json.dump(data, f, indent=' ') def createActions(self): # root = QFileInfo(__file__).absolutePath() self.openAct = QAction( # QIcon(root + '/icons/open.png'), "&Open", self, shortcut=QKeySequence.Open, statusTip="Open project", triggered=self.openProject) self.closeAct = QAction( # QIcon(root + '/icons/close.png'), "&Close", self, shortcut=QKeySequence.Close, statusTip="Close project", triggered=self.closeProject, enabled=False) self.exitAct = QAction( # QIcon(root + '/icons/quit.png'), "&Quit", self, shortcut=QKeySequence.Quit, statusTip="Close the application", triggered=self.close) self.aboutAct = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) def createMenus(self): fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(self.openAct) fileMenu.addAction(self.closeAct) fileMenu.addSeparator() fileMenu.addAction(self.exitAct) self.menuBar().addSeparator() helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction(self.aboutAct) def createFileList(self): self.files = QListWidget() self.files.setSelectionMode(QAbstractItemView.SingleSelection) self.files.setSortingEnabled(False) self.files.itemSelectionChanged.connect(self.onSelect) self.files.itemDoubleClicked.connect(self.onToggle) self.files.setEnabled(False) dock = QDockWidget("Images", self) dock.setAllowedAreas(Qt.LeftDockWidgetArea) dock.setFixedWidth(320) dock.setFeatures(QDockWidget.NoDockWidgetFeatures) dock.setWidget(self.files) self.addDockWidget(Qt.LeftDockWidgetArea, dock) def createContainer(self): self.container = QWidget(self) self.container.setStyleSheet("View { background: black; }") self.container.setLayout(QVBoxLayout()) self.setCentralWidget(self.container) def createStatusBar(self): self.statusBar().showMessage("Initialized") def enableProjectActions(self, enabled): actions = [self.closeAct, self.files] for action in actions: action.setEnabled(enabled) def closeEvent(self, event): event.accept() def openProject(self, path=None): if not path: path = QFileDialog.getExistingDirectory( self, "Choose image directory", dir=self.lastProjectDirectory) if path: self.closeProject() self.view = View(self) self.view.previous.connect(self.onPrevious) self.view.next.connect(self.onNext) self.container.layout().addWidget(self.view) filename = Path(path) / "items.json" if filename.exists(): with io.open(filename, 'r') as f: self.items = json.load(f) else: self.items = dict() for filename in find_images(Path(path)): if filename in self.items: continue self.items[filename] = dict({"active": True}) self.refreshItems() self.files.setItemSelected(self.files.item(0), True) self.enableProjectActions(True) self.lastProjectDirectory = path self.saveProject() self.saveSettings() def saveProject(self): if not self.view: return with io.open(Path(self.lastProjectDirectory) / "items.json", 'w') as f: json.dump(self.items, f, indent=' ') def closeProject(self): self.enableProjectActions(False) if self.view: self.container.layout().removeWidget(self.view) self.view.close() self.view = None self.items = dict() self.refreshItems() def selection(self): selection = self.files.selectedItems() if len(selection) > 0: return selection[0].text() return None def refreshItems(self): filenames = sorted(self.items.keys()) for i in range(len(filenames)): filename = filenames[i] item = self.items[filename] file = self.files.item(i) if not file: file = QListWidgetItem(filenames[i]) self.files.insertItem(i, file) if item['active']: file.setTextColor(QColor.fromRgbF(0.0, 0.5, 0.0)) else: file.setTextColor(QColor.fromRgbF(0.5, 0.0, 0.0)) while self.files.count() > len(filenames): self.files.takeItem(len(filenames)) @Slot() def onToggle(self, item): self.items[ item.text()]['active'] = not self.items[item.text()]['active'] self.refreshItems() self.saveProject() @Slot() def onSelect(self): selection = self.selection() if self.view: self.view.save() if selection: self.view.load(Path(self.lastProjectDirectory) / selection) @Slot() def onPrevious(self): if self.files.count() == 0: return index = 0 selection = self.selection() if selection: for i in range(self.files.count()): item = self.files.item(i) if item.text() == selection: index = i - 1 break if index < 0: index = self.files.count() - 1 self.files.setItemSelected(self.files.item(index), True) @Slot() def onNext(self): if self.files.count() == 0: return index = 0 selection = self.selection() if selection: for i in range(self.files.count()): item = self.files.item(i) if item.text() == selection: index = i + 1 break if index >= self.files.count(): index = 0 self.files.setItemSelected(self.files.item(index), True) @Slot(str) def showMessage(self, message): self.statusBar().showMessage(message) def about(self): QMessageBox.about( self, "About Application", "The <b>2D Tagger</b> can load images, manually tag/label images and export result." )
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()
def _remove_addr(self, qlist: QListWidget, addr): for i in range(qlist.count()): qitem = qlist.item(i) if int(qitem.text(), 16) == addr: qlist.takeItem(i) return
class MainFrame(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): # Define buttons # Button to calculate lunar dates self.calculate_lunar = QPushButton('음력날짜 계산', self) self.calculate_lunar.clicked.connect(self.calculate_lunar_date) self.calculate_lunar.setGeometry(20, 260, 130, 35) self.calculate_lunar.setStatusTip('음력 날짜를 계산합니다.') self.calculate_lunar.setShortcut('Return') # Button to save ICS file to local directory self.ICS_save_button = QPushButton('ICS 파일 저장', self) self.ICS_save_button.clicked.connect(self.create_ics_file) self.ICS_save_button.setGeometry(175, 260, 130, 35) self.ICS_save_button.setStatusTip('ICS 파일을 컴퓨터에 저장합니다.') # Define labels # Define label for lunar date to convert lunar_date_text = QLabel(self) lunar_date_text.setText('계산할 음력 날짜') lunar_date_text.setGeometry(25, 15, 240, 25) # Define label for name of event event_name_text = QLabel(self) event_name_text.setText('이벤트 이름 입력') event_name_text.setGeometry(25, 50, 240, 25) # Define label for repetition event_name_text = QLabel(self) event_name_text.setText('몇년치나 할까요?') event_name_text.setGeometry(25, 85, 240, 25) # Define ListWidget to display results self.result_console = QListWidget(self) self.result_console.setGeometry(25, 118, 275, 133) self.result_console.setStatusTip('올해부터 매년 양력날짜가 출력됩니다.') # Define user input areas # Define lunar date to convert to solar date self.user_date_input = QLineEdit(self) self.user_date_input.setGeometry(180, 15, 120, 27) self.user_date_input.setAlignment(Qt.AlignCenter) self.user_date_input.setPlaceholderText('YYYYMMDD') self.user_date_input.setStatusTip('날짜를 YYYYMMDD로 입력하세요.') # Define name of event to use in ICS file self.user_event_name = QLineEdit(self) self.user_event_name.setGeometry(180, 50, 120, 27) self.user_event_name.setAlignment(Qt.AlignCenter) self.user_event_name.setPlaceholderText('이벤트 이름') self.user_event_name.setStatusTip('이벤트 이름을 입력하세요.') # Define number of years to convert self.user_repetition = QLineEdit(self) self.user_repetition.setGeometry(180, 84, 120, 27) self.user_repetition.setAlignment(Qt.AlignCenter) self.user_repetition.setText('10') self.user_repetition.setStatusTip('생성할 햇수를 입력하세요.') # Define size of main window self.setGeometry(200, 320, 325, 330) self.setWindowTitle('날짜') self.statusBar() self.show() # Define functions # Function to parse lunar date from openAPI website using user input def calculate_lunar_date(self): if len(self.user_date_input.text()) != 8: reply = QMessageBox.question(self, 'Error', "YYYYMMDD로 정확한 날짜를 입력해 주십시오.", QMessageBox.Close) return url = 'http://apis.data.go.kr/B090041/openapi/service/LrsrCldInfoService/getSolCalInfo' personal_key = 'UrEdHGIOCJr600RsRQ%2BnoL2wma3HT9JXmYn0uhHwmxImsnE2GHKYfn1RUfiEK9RIHXPJ3FBRnONxXMjplU3%2F7g%3D%3D' lunar_input = self.user_date_input.text() lunar_month = lunar_input[4:6] lunar_day = lunar_input[6:] repeat = int(self.user_repetition.text()) + 1 for i in range(0, repeat): lunar_year = int(lunar_input[:4]) + int(i) # Limit search at lunar year 2050. if lunar_year > 2050: break query_params = '?lunYear={}&lunMonth={}&lunDay={}&ServiceKey={}'.format( lunar_year, lunar_month, lunar_day, personal_key) session = XMLSession() r = session.get(url + query_params) solar_day = r.xml.xpath('//solDay', first=True).text solar_month = r.xml.xpath('//solMonth', first=True).text solar_year = r.xml.xpath('//solYear', first=True).text solar_week = r.xml.xpath('//solWeek', first=True).text solar_date_text = ' {}년 {}월 {}일 {}요일 '.format( solar_year, solar_month, solar_day, solar_week) QListWidgetItem(solar_date_text, self.result_console) def create_ics_file(self): event_name = self.user_event_name.text() ics_content = 'BEGIN:VCALENDAR\nVERSION:2.0\nCALSCALE:GREGORIAN\nBEGIN:VTIMEZONE\nTZID:Asia/Tokyo\n' \ 'TZURL:http://tzurl.org/zoneinfo-outlook/Asia/Tokyo\nX-LIC-LOCATION:Asia/Tokyo\n' \ 'BEGIN:STANDARD\nTZOFFSETFROM:+0900\nTZOFFSETTO:+0900\nTZNAME:JST\n' \ 'DTSTART:19700101T000000\nEND:STANDARD\nEND:VTIMEZONE\n' if len(self.user_event_name.text()) == 0: reply = QMessageBox.question(self, 'Error', "이벤트 이름을 입력해 주십시오.", QMessageBox.Close) return elif self.result_console.count() == 0: reply = QMessageBox.question(self, 'Error', "날짜 계산을 먼저 해주십시오.", QMessageBox.Close) return # Iterate through QListWidget items: https://stackoverflow.com/a/34479509 converted_dates = [ str(self.result_console.item(i).text()) for i in range(self.result_console.count()) ] for i in converted_dates: # Extract digits from string: https://stackoverflow.com/a/26825833 date_string = (''.join(filter(str.isdigit, i))) event_string = 'BEGIN:VEVENT\nDTSTAMP:20180802T115053z\nDTSTART;TZID="Asia/Tokyo":{0}T120000' \ '\nDTEND;TZID="Asia/Tokyo":{0}T120000\nSUMMARY:{1}\nBEGIN:VALARM\n' \ 'TRIGGER:-PT14H\nREPEAT:1\nACTION:DISPLAY\nDESCRIPTION:Reminder\nEND:VALARM\nEND:VEVENT\n' ics_content += event_string.format(date_string, event_name) ics_content += 'END:VCALENDAR' # This program cannot read existing .ics files, so if user creates a new ics event with existing file name, # the program would crash. new_ics = open(event_name.replace(' ', '') + '.ics', 'w') new_ics.write(ics_content) new_ics.close() success = QMessageBox.question(self, 'Success', "ICS 파일 생성 완료!", QMessageBox.Close)
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 AddMovieDialog(QDialog): client = Client() def __init__(self, parent): super(AddMovieDialog, self).__init__(parent) self.setWindowTitle("Add Movie") main_layout = QVBoxLayout(self) self.selected_movies = None search_layout = QHBoxLayout() main_layout.addLayout(search_layout) self.search_field = QLineEdit() self.search_field.returnPressed.connect(self.find_action) self.search_field.setPlaceholderText("Search movies...") search_layout.addWidget(self.search_field) self.find_all_checkbx = QCheckBox("Find All") search_layout.addWidget(self.find_all_checkbx) self.result_list = QListWidget() self.result_list.setSelectionMode(QListWidget.ExtendedSelection) main_layout.addWidget(self.result_list) button_layout = QHBoxLayout() main_layout.addLayout(button_layout) add_bttn = QPushButton("Add Movie") add_bttn.clicked.connect(self.accept) add_all_bttn = QPushButton("Add All Movies") add_all_bttn.clicked.connect(self.add_all_action) cancel_bttn = QPushButton("Cancel") cancel_bttn.clicked.connect(self.reject) button_layout.addWidget(add_bttn) button_layout.addWidget(add_all_bttn) button_layout.addWidget(cancel_bttn) def keyPressEvent(self, event): event.ignore() def add_all_action(self): for i in range(self.result_list.count()): self.result_list.setItemSelected(self.result_list.item(i), True) self.accept() def accept(self): selected_items = self.result_list.selectedItems() if not selected_items: return self.selected_movies = [ i.movie_data for i in selected_items if not self.client.find_movie(i.movie_data["id"]) ] super(AddMovieDialog, self).accept() def find_action(self): movie_title = self.search_field.text() if len(movie_title): self.result_list.clear() for item in find_movie( movie_title, all_pages=self.find_all_checkbx.isChecked()): MovieItem(self.result_list, item)
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 QtEpsInfo(QtWidgets.QWidget, Ui_EpsInfo): def __init__(self, owner): super(self.__class__, self).__init__() Ui_EpsInfo.__init__(self) self.setupUi(self) self.owner = weakref.ref(owner) self.epsListWidget = QListWidget() self.epsListWidget.setFlow(self.epsListWidget.LeftToRight) self.epsListWidget.setWrapping(True) self.epsListWidget.setFrameShape(self.epsListWidget.NoFrame) self.epsListWidget.setResizeMode(self.epsListWidget.Adjust) self.epsListWidget.itemClicked.connect(self.SelectEps) self.gridLayout_2.addWidget(self.epsListWidget, 1, 0) self.closeFlag = self.__class__.__name__ self.bookId = "" self.loadingForm = QtLoading(self) self.setWindowTitle("章节列表") self.greed = QColor(18, 161, 130) self.blue = QColor(97, 154, 195) self.white = QColor(0, 0, 0, 0) def OpenEpsInfo(self, bookId): self.show() self.loadingForm.show() self.bookId = bookId self.epsListWidget.clear() if bookId not in BookMgr().books: self.owner().qtTask.AddHttpTask(lambda x: BookMgr().AddBookById(self.bookId, x), self.OpenBookInfoBack, cleanFlag=self.closeFlag) else: self.owner().qtTask.AddHttpTask(lambda x: BookMgr().AddBookEpsInfo(self.bookId, x), self.OpenEpsInfoBack, cleanFlag=self.closeFlag) def OpenBookInfoBack(self, msg): if msg == Status.Ok: self.owner().qtTask.AddHttpTask(lambda x: BookMgr().AddBookEpsInfo(self.bookId, x), self.OpenEpsInfoBack, cleanFlag=self.closeFlag) else: self.loadingForm.close() def OpenEpsInfoBack(self, msg): self.loadingForm.close() self.epsListWidget.clear() if msg == Status.Ok: self.UpdateEpsInfo() return def UpdateEpsInfo(self): self.epsListWidget.clear() info = BookMgr().books.get(self.bookId) if not info: return downloadEpsId = self.owner().downloadForm.GetDownloadEpsId(self.bookId) for index, epsInfo in enumerate(info.eps): label = QLabel(epsInfo.title) label.setContentsMargins(20, 10, 20, 10) item = QListWidgetItem(self.epsListWidget) item.setSizeHint(label.sizeHint()) if index in downloadEpsId: item.setBackground(self.greed) else: item.setBackground(self.white) self.epsListWidget.setItemWidget(item, label) def SelectEps(self, item): if item.background().color() == self.greed: return elif item.background().color() == self.blue: item.setBackground(self.white) else: item.setBackground(self.blue) return def SelectAll(self): for i in range(self.epsListWidget.count()): item = self.epsListWidget.item(i) if item.background().color() == self.greed: continue item.setBackground(self.blue) return def CancleSelect(self): for i in range(self.epsListWidget.count()): item = self.epsListWidget.item(i) if item.background().color() == self.greed: continue item.setBackground(self.white) return def StartDownload(self): downloadIds = [] for i in range(self.epsListWidget.count()): item = self.epsListWidget.item(i) if item.background().color() == self.blue: downloadIds.append(i) if not downloadIds: return self.owner().downloadForm.AddDownload(self.bookId, downloadIds) self.UpdateEpsInfo() return
class CreateSequenceWindow(QDialog): """ Window for creating a new sequence """ def __init__(self, motors={}, listOfSequenceHandler=None, sequence=None, modifySequence=False): """ Initializtion of the window for creating a new sequence :param motors: The dictionary of all the motors :param listOfSequenceHandler: The handler of the list of sequence """ QDialog.__init__(self) # Set the window icon appIcon = QIcon(icon) self.setWindowIcon(appIcon) ## Flag if the sequence is a modified one or a new one self.__modifySequence = modifySequence ## The handler of the list of sequence self.__listOfSequenceHandler = listOfSequenceHandler ## The dictionary of all the motors self.__motors = motors ## The new sequence self.__sequence = sequence ## A dictionary of the positions of the new sequence self.__wantedPositions = {} ## The layout of the create sequence window self.__layout = QVBoxLayout(self) ## The widget for the name of the sequenc self.nameEntry = QLineEdit() self.nameEntry.setText(self.__sequence.getName()) ## The label for the widget in which the name of the sequence is written self.__nameLabel = QLabel("Sequence Name") ## The list of the different moves that forms the sequence self.__listOfMoveLabels = QListWidget() moveNumber = 1 for move in self.__sequence.getMoves(): # Set text for the move label labelText = "move " + str(moveNumber) + ": " moveNumber += 1 for motor in self.__motors: labelText += self.__motors[motor].getName() + " " + \ str(move.getMotorPosition(self.__motors[motor].getName())) + ", " label = moveLabel(move, labelText, self.__motors) # insert label to the head of the list self.__listOfMoveLabels.insertItem(0, label) # Put the sliders of the create sequence window in a list ## List of sliders in the create sequence window self.listOfSliders = [] dictOfSlider = dict() for motor in self.__motors: dictOfSlider[motor] = QSlider(Qt.Horizontal) dictOfSlider[motor].setMaximum(4095) dictOfSlider[motor].setValue( self.__motors[motor].getCurrentPosition()) dictOfSlider[motor].sliderMoved.connect( self.__motors[motor].setGoalPosition) self.listOfSliders.append(dictOfSlider[motor]) ## Message to make the user put a name to the sequence self.__noNameMessage = QMessageBox() self.__noNameMessage.setIcon(QMessageBox.Warning) self.__noNameMessage.setWindowIcon(appIcon) self.__noNameMessage.setText( "Please name your sequence before saving it") self.__noNameMessage.setStandardButtons(QMessageBox.Ok) # Renable the create sequence window and closes the message self.__noNameMessage.accepted.connect(self.enableWindow) ## Warning message to make sure the user doen't want to save the sequence self.__warningMessage = QMessageBox() self.__warningMessage.setIcon(QMessageBox.Warning) self.__warningMessage.setWindowIcon(appIcon) self.__warningMessage.setText( "Are you sure you want to close this window? Your sequence will not be saved" ) self.__warningMessage.setStandardButtons(QMessageBox.Ok | QMessageBox.Cancel) # Close the create sequence window and the message self.__warningMessage.accepted.connect(self.reject) # Renable the create sequence window and closes the message self.__warningMessage.rejected.connect(self.enableWindow) # Set the text for the labels ## Labels for the motors in the UI self.__motorLabels = [] for motorNumber in range(0, len(motors)): self.__motorLabels.append( QLabel("Motor " + str(motorNumber + 1) + " position")) ## Button to add a move to the sequence and procede to the next move self.nextMoveButton = QPushButton("Save Move") ## Buttons to accept or cancel the creation of a sequence self.buttonBox = QDialogButtonBox() self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.Ok) # If ok pressed add the sequence to the list self.buttonBox.accepted.connect( lambda: self.addSequenceToList(self.__modifySequence)) # If cancel pressed close the create sequence window self.buttonBox.rejected.connect(self.__warningMessage.exec) # Renable the main window when the create sequence closes self.rejected.connect(self.__listOfSequenceHandler.enableUi) self.accepted.connect(self.__listOfSequenceHandler.enableUi) self.nextMoveButton.clicked.connect(self.addMovetoSequence) self.__listOfMoveLabels.itemDoubleClicked.connect( self.moveDoubleClicked) # Build the vertical layout with the different widgets self.__layout.addWidget(self.__nameLabel) self.__layout.addWidget(self.nameEntry) self.__layout.addWidget(self.__listOfMoveLabels) for motorNumber in range(len(self.__motors)): self.__layout.addWidget(self.__motorLabels[motorNumber]) self.__layout.addWidget(self.listOfSliders[motorNumber]) self.__layout.addWidget(self.nextMoveButton) self.__layout.addWidget(self.buttonBox) # Connect the qwidgetlist to the custom right click menu self.__listOfMoveLabels.setContextMenuPolicy(Qt.CustomContextMenu) self.__listOfMoveLabels.customContextMenuRequested.connect( self.rightClickMenu) def setName(self, name): """ Sets the name of the sequence with the user input :param name: The name of the sequence :return: No return """ self.__sequence.setName(name) def getSequence(self): """ Accessor of the sequence (for tests) :return: the sequence """ return self.__sequence def getListofMoveLabels(self): """ Accessor of the list of move labels (for tests) :return: the list of move labels """ return self.__listOfMoveLabels def addSequenceToList(self, modifySequence=False): """ Add the sequence to the list of sequence :param modifySequence: bool, if true there's a selected sequence that needs to be modified if false it's a new sequence :return: No return """ # TODO: look to move this method to the list of sequence handler # TODO: don't let the user enter a sequence that has the same name as an old one self.setName(self.nameEntry.text()) if self.__sequence.getName() != "": # Load previously saved sequences try: with open('SaveSequence.json') as save: savedListOfSequences = json.load(save) except FileNotFoundError: savedListOfSequences = [] if modifySequence: # Get the item that needs to be modified # selectedSequence = self.__listOfSequenceHandler.getSelectedItems() # Find the selected sequence in the list of saved ones for sequence in savedListOfSequences: if self.__sequence.getName() in sequence: indexOfTheSequence = savedListOfSequences.index( sequence) # remove the unmodified sequence to insert the modified sequence savedListOfSequences.remove(sequence) self.__listOfSequenceHandler.addItem(self.__sequence) # Make the sequence json seriable newSequence = dict() newSequence[self.__sequence.getName()] = [] for moveNumber in range(len( self.__sequence.getMoves())): newSequence[self.__sequence.getName()].append( self.__sequence.getMoves() [moveNumber].getMovePositions()) # Append new sequence to the old ones savedListOfSequences.insert(indexOfTheSequence, newSequence) # Write the sequences to the file with open('SaveSequence.json', 'w') as outfile: json.dump(savedListOfSequences, outfile) self.accept() else: self.__listOfSequenceHandler.addItem(self.__sequence) # Make the sequence json seriable newSequence = dict() newSequence[self.__sequence.getName()] = [] for moveNumber in range(len(self.__sequence.getMoves())): newSequence[self.__sequence.getName()].append( self.__sequence.getMoves() [moveNumber].getMovePositions()) # Append new sequence to the old ones savedListOfSequences.append(newSequence) # Write the sequences to the file with open('SaveSequence.json', 'w') as outfile: json.dump(savedListOfSequences, outfile) self.accept() else: self.setEnabled(False) self.__noNameMessage.exec_() def addMovetoSequence(self): """ Add the last move to the sequence :return: No return """ move = None labelToModify = None # Check if there's a move in modifying state for row in range(self.__listOfMoveLabels.count()): if not self.__listOfMoveLabels.item(row).getMove().isNew: move = self.__listOfMoveLabels.item(row).getMove() labelToModify = self.__listOfMoveLabels.item(row) break # verify if the move is a new one if move is None: # Create the new move and set his positions move = Move(self.__motors) i = 0 for motorName in self.__motors: move.setMotorPosition(motorName, self.listOfSliders[i].value()) i += 1 self.__sequence.addMove(move) # Set text for the move label labelText = "move " + str( self.__sequence.getNumberofMoves()) + ": " i = 0 for motor in self.__motors: labelText += self.__motors[motor].getName() + " " +\ str(self.listOfSliders[i].value()) + ", " i += 1 label = moveLabel(move, labelText, self.__motors) # insert label to the head of the list self.__listOfMoveLabels.insertItem(0, label) else: # modify the move i = 0 for motorName in self.__motors: move.setMotorPosition(motorName, self.listOfSliders[i].value()) i += 1 # modify the label of the move textToEdit = labelToModify.text() listOfTextToEdit = textToEdit.split(' ') labelText = listOfTextToEdit[0] + " " + listOfTextToEdit[1] + " " i = 0 for motor in self.__motors: labelText += self.__motors[motor].getName() + " " + \ str(self.listOfSliders[i].value()) + ", " i += 1 labelToModify.setText(labelText) labelToModify.setSelected(False) labelToModify.setBackground(Qt.white) # reset the state of the move move.isNew = True # Access the move positions when double clicked on def moveDoubleClicked(self, moveItem): """ Called when a move in the sequence is double clicked :param moveItem: the move that was double clicked :return: No return """ moveItem.goToMoveOfTheLabel() self.updateSlidersPositions() def updateSlidersPositions(self): counterMotors = 0 for motor in self.__motors: self.listOfSliders[counterMotors].setValue( self.__motors[motor].getGoalPosition()) counterMotors += 1 def enableWindow(self): """ Enable the create sequence window :return: """ self.setEnabled(True) def rightClickMenu(self, event): """ The right click menu of the move list :param event: The event (here right click) that makes the menu come up :return: No return """ menu = QMenu() # Add a button in the menu that when clicked, it puts a move in modifying state menu.addAction( "Modify Move", lambda: self.modifyMove(self.__listOfMoveLabels. selectedItems()[0])) # Add a button in the menu that when clicked, it deletes a move in the list menu.addAction( "Delete Move", lambda: self.deleteMove(self.__listOfMoveLabels. selectedItems()[0])) menu.exec_(self.__listOfMoveLabels.mapToGlobal(event)) def deleteMove(self, label): """ Delete a move and its label of the sequence :param label: label of the move :return: No return """ # remove the label from the list self.__listOfMoveLabels.takeItem(self.__listOfMoveLabels.row(label)) # remove the move from the sequence self.__sequence.deleteMove(label.getMove()) # rename the labels in the list of moves for index in range(self.__sequence.getNumberofMoves() - 1, -1, -1): labelToModify = self.__listOfMoveLabels.item(index) textToEdit = labelToModify.text() listOfTextToEdit = textToEdit.split(' ') listOfTextToEdit[1] = str(self.__sequence.getNumberofMoves() - index) + ':' textToEdit = ' '.join(listOfTextToEdit) self.__listOfMoveLabels.item(index).setText(textToEdit) def modifyMove(self, label): """ Put a move to a modified state :param label: label of the move :return: No return """ # Check if there's a move in modifying state for row in range(self.__listOfMoveLabels.count()): if not self.__listOfMoveLabels.item(row).getMove().isNew: self.__listOfMoveLabels.item(row).getMove().isNew = True self.__listOfMoveLabels.item(row).setBackground( QBrush(Qt.white)) moveToModify = label.getMove() moveToModify.isNew = False label.setBackground(QBrush(Qt.darkCyan)) label.goToMoveOfTheLabel() self.updateSlidersPositions()
class Window(QWidget): def __init__(self): super().__init__() self.setWindowTitle("Network Designer") self.setGeometry(300, 200, 840, 720) self.current_id = 1 self.createLayout() self.couple = [] self.show() def makeButtonsLayout(self): self.button = QPushButton("Add Node", self) self.button.clicked.connect(self.addNode) self.btn_connection = QPushButton("Add Connection:", self) self.btn_connection.clicked.connect(self.addConnection) self.button3 = QPushButton("Export Network", self) self.button3.clicked.connect(self.file_save) self.lbl_connection = QLabel("Connection") self.l_btns = QHBoxLayout() self.l_btns.addWidget(self.button3) self.l_btns.addStretch() self.l_btns.addWidget(self.btn_connection) self.l_btns.addWidget(self.lbl_connection) self.l_btns.addStretch() self.l_btns.addWidget(self.button) self.btns = QWidget() self.btns.setLayout(self.l_btns) self.btns.setFixedHeight(40) def initializeGview(self): self.scene = GraphicsScene() self.scene.setSceneRect(0, 0, 480, 480) self.scene.click.connect(self.keepNode) self.addNode() self.view = QGraphicsView(self.scene) self.view.setGeometry(0, 0, 500, 500) self.view.scale(1, 1) def createOrderList(self): self.orderList = QListWidget() # Enable drag & drop ordering of items. self.orderList.setDragDropMode(QAbstractItemView.InternalMove) def createLayout(self): self.makeButtonsLayout() self.initializeGview() self.createOrderList() l_network = QHBoxLayout() l_network.addWidget(self.view) l_network.addStretch() l_network.addWidget(self.orderList) self.l_root = QVBoxLayout() self.l_root.addLayout(l_network) self.l_root.addStretch() self.l_root.addWidget(self.btns) # self.l_root.addWidget(self.view) self.setLayout(self.l_root) def addNode(self): greenBrush = QBrush(Qt.green) blackPen = QPen(Qt.black) # blueBrush = QBrush(Qt.blue) blackPen.setWidth(5) ellipse = GraphicsEllipse(str(self.current_id), blackPen, greenBrush, 100, 100, NODE_D, NODE_D) self.scene.addItem(ellipse) self.current_id += 1 def keepNode(self, node): if len(self.couple) < 2: self.couple.append(node) else: self.couple.pop(0) self.couple.append(node) if len(self.couple) == 2: self.lbl_connection.setText(self.couple[0].label + " --> " + self.couple[1].label) def addConnection(self): line = GraphicsLine(self.couple[0], self.couple[1], NODE_R) self.scene.addItem(line) for v in self.couple: v.add_connection(line) self.orderList.addItem(self.lbl_connection.text()) def getNodes(self): positions = {} for i in self.scene.items(): if type(i) is GraphicsEllipse: positions[int(i.label)] = i.getPosition() return positions def getConnections(self): connections = [] for i in range(self.orderList.count()): conn = self.orderList.item(i).text().split(" --> ") connections.append((int(conn[0]), int(conn[1]), {"label": str(i + 1)})) return connections def file_save(self): positions = self.getNodes() connections = self.getConnections() network = {} network["labels"] = {i: i for i in sorted(list(positions.keys()))} network["edges"] = connections network["pos"] = positions text = json.dumps(network) name = QFileDialog.getSaveFileName(self, 'Save File') file = open(name[0], 'w') # text = "something" file.write(text) file.close()
class SynthPdfWidget(QWidget): def __init__(self, status_bar): super(SynthPdfWidget, self).__init__() self.status_bar = status_bar layout = QHBoxLayout() self.list_view = QListWidget() self.list_view.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_view.setAlternatingRowColors(True) self.list_view.itemClicked.connect(self.click_item_signal) self.list_view.itemEntered.connect(self.click_item_signal) self.list_view.itemSelectionChanged.connect( self.change_selection_signal) controls_layout = QVBoxLayout() controls_layout.setAlignment(Qt.AlignTop) select_zone = SelectWidget(self.click_move_up, self.click_move_down, self.click_invert, self.click_delete) select_zone.setMinimumWidth(250) add_pages_button = QPushButton("Add Pages From PDF") add_pages_button.clicked.connect(self.add_pdf) controls_layout.addWidget(select_zone) controls_layout.addWidget(add_pages_button) layout.addWidget(self.list_view) layout.addLayout(controls_layout) self.setLayout(layout) def _add_pages_from_pdf(self, pdf_path): with open(pdf_path, "rb") as file: reader = PdfFileReader(file) file_name = os.path.basename(pdf_path) pages_count = reader.getNumPages() for i in range(pages_count): new_item = ImageListItem( "page " + str(i + 1) + " (" + file_name + ")", (pdf_path, i)) self.list_view.addItem(new_item) self.status_bar.showMessage("Add " + str(pages_count) + " pages") # ----------external methods from create command def extern_get_files_and_pages( self ): # return selected pages in the form {file1: [p1, p2, ...], file2: [p1, p2, ...], ...} to_return = {} if len(self.list_view.selectedItems() ) == 0: # nothing selected, add all pages items_count = self.list_view.count() for item_index in range(items_count): item = self.list_view.item(item_index) item_data = item.get_data() file_name = item_data[0] page_index = item_data[1] if file_name in to_return: to_return[file_name].append(page_index) else: to_return[file_name] = [page_index] else: for s in self.list_view.selectedItems(): s_data = s.get_data() file_name = s_data[0] page_index = s_data[1] if file_name in to_return: to_return[file_name].append(page_index) else: to_return[file_name] = [page_index] return to_return # ----------add button----------------------- def add_pdf(self): files_dialog = QFileDialog() files_dialog.setNameFilter("PDF (*.pdf)") files_dialog.setFileMode(QFileDialog.ExistingFiles) if files_dialog.exec_(): files = files_dialog.selectedFiles() for f_path in files: self._add_pages_from_pdf(f_path) # ----------List_view signals---------------- def click_item_signal(self, item): self.status_bar.showMessage("Select " + str(item.text())) def change_selection_signal(self): if len(self.list_view.selectedItems()) == 0: # self._set_preview(None) # nothing selected pass # ----------list_view commands--------------- def click_move_up(self): selected = self.list_view.selectedItems() selected_indexes = [ self.list_view.indexFromItem(sel).row() for sel in selected ] selected_indexes.sort() if len(selected_indexes) > 0 and selected_indexes[0] > 0: for index in selected_indexes: prev_item = self.list_view.takeItem(index - 1) self.list_view.insertItem(index, prev_item) self.status_bar.showMessage("Move " + str(len(selected_indexes)) + " items") else: self.status_bar.showMessage("Nothing to move") def click_move_down(self): selected = self.list_view.selectedItems() selected_indexes = [ self.list_view.indexFromItem(sel).row() for sel in selected ] selected_indexes.sort() sel_count = len(selected_indexes) if len(selected_indexes) > 0 and selected_indexes[ sel_count - 1] < self.list_view.count() - 1: for i_index in range(sel_count): next_item = self.list_view.takeItem( selected_indexes[sel_count - i_index - 1] + 1) self.list_view.insertItem( selected_indexes[sel_count - i_index - 1], next_item) self.status_bar.showMessage("Move " + str(len(selected_indexes)) + " items") else: self.status_bar.showMessage("Nothing to move") def click_invert(self): selected = self.list_view.selectedItems() selected_indexes = [] for sel in selected: selected_indexes.append(self.list_view.indexFromItem(sel).row()) total_indexes = [i for i in range(self.list_view.count())] new_indexes = [] for i in total_indexes: if i not in selected_indexes: new_indexes.append(i) self.list_view.clearSelection() for i in new_indexes: self.list_view.item(i).setSelected(True) self.status_bar.showMessage("Invert selection: " + str(new_indexes)) def click_delete(self): selected = self.list_view.selectedItems() delete_names = [] for s in selected: s_index = self.list_view.indexFromItem(s).row() del_item = self.list_view.takeItem(s_index) delete_names.append(del_item.text()) if len(delete_names) == 0: self.status_bar.showMessage("Nothing to delete") else: self.status_bar.showMessage("Delete items: " + ", ".join(delete_names))
class ImageToPdfWidget(QWidget): def __init__(self, status_link): super(ImageToPdfWidget, self).__init__() LABEL_WIDTH = 80 self.last_selected_items = [] self.options_mode = 0 self.update_status_combobox = False layout = QHBoxLayout() self.status_bar = status_link self.list_view = QListWidget() self.list_view.setSelectionMode(QAbstractItemView.ExtendedSelection) self.list_view.setAlternatingRowColors(True) self.list_view.itemClicked.connect(self.click_item_signal) self.list_view.itemEntered.connect(self.click_item_signal) self.list_view.itemSelectionChanged.connect( self.change_selection_signal) controls_layout = QVBoxLayout() controls_layout.setAlignment(Qt.AlignTop) select_zone = SelectWidget(self.click_move_up, self.click_move_down, self.click_invert, self.click_delete) # options zone ------------------------------------- options_zone = QGroupBox("Options") self.options_zone_layout = QVBoxLayout() self.options_mode_combobox = QComboBox() self._add_items_to_mode_combobox(self.options_mode_combobox) options_mode_label = QLabel("Mode") options_mode_label.setMaximumWidth(LABEL_WIDTH) options_mode_layout = QHBoxLayout() options_mode_layout.addWidget(options_mode_label) options_mode_layout.addWidget(self.options_mode_combobox) self.options_zone_layout.addLayout(options_mode_layout) self.option_source_widget = OptionsFromSourceWidget( label_width=LABEL_WIDTH, status_bar=self.status_bar) self.options_a_widget = OptionsAWidget(label_width=LABEL_WIDTH, status_bar=self.status_bar) self.options_mode_combobox.currentIndexChanged.connect( self.change_options_mode_signal) self.change_options_mode_signal(self.options_mode) options_zone.setLayout(self.options_zone_layout) # Add files button and final structures --------------------------- add_file_button = QPushButton("Add Files") add_file_button.clicked.connect(self.click_add_files) controls_layout.addWidget(select_zone) controls_layout.addWidget(options_zone) controls_layout.addWidget(add_file_button) # image preview --------------------------------------------------- image_prev_layout = QVBoxLayout() image_prev_layout.setContentsMargins(0, 0, 4, 0) self.IMG_PREVIEW_WIDTH = 256 self.img_label = QLabel() self.img_label.setAlignment(Qt.AlignCenter) self.select_text = "Click item to preview" self.last_created_pix_path = "" self.img_label.setText(self.select_text) image_prev_layout.addWidget(self.img_label) # slider for the preview scale self.img_scale_slider = QSlider() self.img_scale_slider.setMinimum(6) self.img_scale_slider.setMaximum(2048) self.img_scale_slider.setValue(self.IMG_PREVIEW_WIDTH) self.img_scale_slider.setOrientation(Qt.Horizontal) self.img_scale_slider.valueChanged.connect( self.change_scale_slider_signal) image_prev_layout.addWidget(self.img_scale_slider) self._update_preview() layout.addLayout(image_prev_layout) layout.addWidget(self.list_view) layout.addLayout(controls_layout) self.setLayout(layout) self.update_status_combobox = True def _pillow_to_pixmap(self, img): if img.mode == "RGB": r, g, b = img.split() img = Image.merge("RGB", (b, g, r)) elif img.mode == "RGBA": r, g, b, a = img.split() img = Image.merge("RGBA", (b, g, r, a)) elif img.mode == "L": img = img.convert("RGBA") img2 = img.convert("RGBA") data = img2.tobytes("raw", "RGBA") qim = QImage(data, img.size[0], img.size[1], QImage.Format_ARGB32) pixmap = QPixmap.fromImage(qim) return pixmap def _update_preview(self): self.img_label.setMinimumWidth(self.IMG_PREVIEW_WIDTH) self.img_label.setMaximumWidth( max(self.IMG_PREVIEW_WIDTH, self.img_scale_slider.width())) if len(self.last_created_pix_path) > 0: img = Image.open(self.last_created_pix_path) img_pix = self._pillow_to_pixmap(img) img_pix = img_pix.scaled( QSize(self.IMG_PREVIEW_WIDTH, self.IMG_PREVIEW_WIDTH), Qt.KeepAspectRatio) self.img_label.setPixmap(img_pix) def _set_preview(self, img_path): if img_path is None: self.last_created_pix_path = "" self.img_label.setText(self.select_text) else: if img_path != self.last_created_pix_path: self.last_created_pix_path = img_path self._update_preview() def _add_items_to_mode_combobox(self, combobox): combobox.addItem("From Source") combobox.addItem("A4") # combobox.addItem("A5") # combobox.addItem("A6") # combobox.addItem("Letter") def _get_filtered_string(self, string): to_return_array = [] for s in string: if s.isdigit(): to_return_array.append(s) return "".join(to_return_array) def get_images_to_save(self): path_array = [] for i in range(self.list_view.count()): path_array.append(self.list_view.item(i).get_data()) return path_array def get_image_parameters(self): # return as dictionary if self.options_mode == 0: return { "mode": 0, "pixels": self.option_source_widget.get_pixel_value(), "margin": self.option_source_widget.get_margin_value(), "background": self.option_source_widget.get_background_value() } else: return { "mode": self.options_mode, "align": self.options_a_widget.get_align_value(), "margin": self.options_a_widget.get_margin_value(), "background": self.options_a_widget.get_background_value() } def add_items(self, array): added_names = [] for a in array: new_name = os.path.basename(a) new_item = ImageListItem(new_name, a) added_names.append(new_name) self.list_view.addItem(new_item) self.status_bar.showMessage("Add items: " + ", ".join(added_names)) def change_scale_slider_signal(self, value): self.IMG_PREVIEW_WIDTH = value self._update_preview() self.status_bar.showMessage("Set preview scale to " + str(value)) def click_item_signal(self, item): pass # self._set_preview(item.get_data()) # self.status_bar.showMessage("Select " + str(item.text())) def _get_first_new_index(self, current, last): for v in current: if v not in last: return v return current[0] def change_selection_signal(self): if len(self.list_view.selectedItems()) == 0: self._set_preview(None) # nothing selected else: selected_indexes = [ self.list_view.indexFromItem(sel).row() for sel in self.list_view.selectedItems() ] item = self.list_view.item( self._get_first_new_index(selected_indexes, self.last_selected_items)) self._set_preview(item.get_data()) self.status_bar.showMessage("Select " + str(item.text())) self.last_selected_items = selected_indexes def change_options_mode_signal(self, index): self.options_mode = index if self.options_mode == 0: self.options_zone_layout.removeWidget(self.options_a_widget) self.options_a_widget.setParent(None) self.options_zone_layout.addWidget(self.option_source_widget) else: self.options_zone_layout.removeWidget(self.option_source_widget) self.option_source_widget.setParent(None) self.options_zone_layout.addWidget(self.options_a_widget) if self.update_status_combobox: self.status_bar.showMessage( "Set combine mode to \"" + self.options_mode_combobox.itemText(index) + "\"") def resizeEvent(self, size): # self._update_preview() pass def click_move_up(self): selected = self.list_view.selectedItems() selected_indexes = [ self.list_view.indexFromItem(sel).row() for sel in selected ] selected_indexes.sort() if len(selected_indexes) > 0 and selected_indexes[0] > 0: for index in selected_indexes: prev_item = self.list_view.takeItem(index - 1) self.list_view.insertItem(index, prev_item) self.status_bar.showMessage("Move " + str(len(selected_indexes)) + " items") else: self.status_bar.showMessage("Nothing to move") def click_move_down(self): selected = self.list_view.selectedItems() selected_indexes = [ self.list_view.indexFromItem(sel).row() for sel in selected ] selected_indexes.sort() sel_count = len(selected_indexes) if len(selected_indexes) > 0 and selected_indexes[ sel_count - 1] < self.list_view.count() - 1: for i_index in range(sel_count): next_item = self.list_view.takeItem( selected_indexes[sel_count - i_index - 1] + 1) self.list_view.insertItem( selected_indexes[sel_count - i_index - 1], next_item) self.status_bar.showMessage("Move " + str(len(selected_indexes)) + " items") else: self.status_bar.showMessage("Nothing to move") def click_invert(self): selected = self.list_view.selectedItems() selected_indexes = [] for sel in selected: selected_indexes.append(self.list_view.indexFromItem(sel).row()) total_indexes = [i for i in range(self.list_view.count())] new_indexes = [] for i in total_indexes: if i not in selected_indexes: new_indexes.append(i) self.list_view.clearSelection() for i in new_indexes: self.list_view.item(i).setSelected(True) self.status_bar.showMessage("Invert selection: " + str(new_indexes)) def click_delete(self): selected = self.list_view.selectedItems() delete_names = [] for s in selected: s_index = self.list_view.indexFromItem(s).row() del_item = self.list_view.takeItem(s_index) delete_names.append(del_item.text()) if len(delete_names) == 0: self.status_bar.showMessage("Nothing to delete") else: self.status_bar.showMessage("Delete items: " + ", ".join(delete_names)) def click_add_files(self): files_dialog = QFileDialog() files_dialog.setNameFilter("Images (*.jpg *.jpeg *.bmp *.png *.tiff)") files_dialog.setFileMode(QFileDialog.ExistingFiles) if files_dialog.exec_(): files = files_dialog.selectedFiles() self.add_items(files)
class FieldAttrsDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setLayout(QGridLayout()) self.setWindowTitle("Edit Attributes") self.list_widget = QListWidget() self.list_widget.setMinimumSize(800, 600) self.add_button = QPushButton("Add attr") self.add_button.clicked.connect(self.__add_attr) self.remove_button = QPushButton("Remove attr") self.remove_button.clicked.connect(self._remove_attrs) self.layout().addWidget(self.list_widget, 0, 0, 2, 1) self.layout().addWidget(self.add_button, 0, 1) self.layout().addWidget(self.remove_button, 1, 1) def fill_existing_attrs(self, existing_dataset: Dataset): for attr in existing_dataset.attributes: if attr.name not in ATTRS_EXCLUDELIST: frame = FieldAttrFrame(attr) self._add_attr(existing_frame=frame) def __add_attr(self): """ Only used for button presses. Any additional arguments from the signal are ignored. """ self._add_attr() def _add_attr(self, existing_frame=None): item = QListWidgetItem() self.list_widget.addItem(item) frame = existing_frame if existing_frame is not None else FieldAttrFrame( ) item.setSizeHint(frame.sizeHint()) self._setup_attribute_name_validator(frame) self.list_widget.setItemWidget(item, frame) def _remove_attrs(self): for index in self.list_widget.selectedIndexes(): self.list_widget.takeItem(index.row()) def get_attrs(self): attrs_list = [] for index in range(self.list_widget.count()): item = self.list_widget.item(index) widget = self.list_widget.itemWidget(item) if widget: attrs_list.append((widget.name, widget.value, widget.dtype)) return attrs_list def get_attr_names(self): return [item[0] for item in self.get_attrs()] def _setup_attribute_name_validator(self, frame): frame.attr_name_lineedit.setValidator( AttributeNameValidator(self.get_attr_names)) frame.attr_name_lineedit.validator().is_valid.connect( partial( validate_line_edit, frame.attr_name_lineedit, tooltip_on_accept="Attribute name is valid.", tooltip_on_reject="Attribute name is not valid", ))
class VODCutter(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.twitch_client_id = config.TWITCH_API_CLIENT_ID self.twitch_oauth_token = config.TWITCH_API_OAUTH_TOKEN self.twitch_interface = TwitchInterface( api_client_id=config.TWITCH_API_CLIENT_ID, api_oauth_token=config.TWITCH_API_OAUTH_TOKEN, browser_client_id=config.TWITCH_BROWSER_OAUTH_TOKEN, browser_oauth_token=config.TWITCH_BROWSER_OAUTH_TOKEN) self.vlc_interface = VLCInterface(config.VLC_PATH) self.loaded_video = None self.main_layout = QVBoxLayout() self.launch_vlc_btn = QPushButton("Launch VLC") self.info_layout = QGridLayout() self.file_picker_layout = QHBoxLayout() self.file_path_field = QLineEdit() self.file_browser_btn = QPushButton(text="...") self.file_picker_layout.addWidget(self.file_path_field) self.file_picker_layout.addWidget(self.file_browser_btn) vod_filepath_label = QLabel("VOD Filepath") id_twitch_label = QLabel("ID Twitch") created_at_label = QLabel("Created at") duration_label = QLabel("Duration") title_label = QLabel("Title") streamer_label = QLabel("Streamer") self.id_twitch_field = QLineEdit() self.created_at_field = QLineEdit() self.duration_field = QLineEdit() self.title_field = QLineEdit() self.streamer_field = QLineEdit() self.id_twitch_field.setEnabled(False) self.created_at_field.setEnabled(False) self.duration_field.setEnabled(False) self.title_field.setEnabled(False) self.streamer_field.setEnabled(False) self.info_layout.addWidget(vod_filepath_label, 0, 0) self.info_layout.addWidget(id_twitch_label, 1, 0) self.info_layout.addWidget(created_at_label, 2, 0) self.info_layout.addWidget(duration_label, 3, 0) self.info_layout.addWidget(title_label, 4, 0) self.info_layout.addWidget(streamer_label, 5, 0) self.info_layout.addLayout(self.file_picker_layout, 0, 1) self.info_layout.addWidget(self.id_twitch_field, 1, 1) self.info_layout.addWidget(self.created_at_field, 2, 1) self.info_layout.addWidget(self.duration_field, 3, 1) self.info_layout.addWidget(self.title_field, 4, 1) self.info_layout.addWidget(self.streamer_field, 5, 1) self.segments_create_btn = QPushButton("Import Chapters") self.download_thumbnails_btn = QPushButton("Download Thumbnails") self.download_chatlog_btn = QPushButton("Download Chat Log") self.segments_list = QListWidget() self.segments_add_btn = QPushButton(text="+") self.segments_delete_btn = QPushButton(text="-") self.jump_start_btn = QPushButton(text="Jump To Start") self.jump_end_btn = QPushButton(text="Jump To End") self.set_start_btn = QPushButton(text="Set Start") self.set_end_btn = QPushButton(text="Set End") self.split_btn = QPushButton(text="Split") self.process_selected_btn = QPushButton( text="Process Selected Segment") self.process_all_btn = QPushButton(text="Process All Segments") self.jump_layout = QHBoxLayout() self.jump_layout.addWidget(self.jump_start_btn) self.jump_layout.addWidget(self.jump_end_btn) self.set_layout = QHBoxLayout() self.set_layout.addWidget(self.set_start_btn) self.set_layout.addWidget(self.set_end_btn) self.main_layout.addWidget(self.launch_vlc_btn) self.main_layout.addLayout(self.file_picker_layout) self.main_layout.addLayout(self.info_layout) self.main_layout.addWidget(self.segments_create_btn) self.main_layout.addWidget(self.download_thumbnails_btn) self.main_layout.addWidget(self.download_chatlog_btn) self.main_layout.addWidget(self.segments_list) self.main_layout.addWidget(self.segments_add_btn) self.main_layout.addWidget(self.segments_delete_btn) self.main_layout.addLayout(self.jump_layout) self.main_layout.addLayout(self.set_layout) self.main_layout.addWidget(self.split_btn) self.main_layout.addWidget(self.process_selected_btn) self.main_layout.addWidget(self.process_all_btn) self.main_widget = QWidget() self.main_widget.setLayout(self.main_layout) self.setCentralWidget(self.main_widget) self.segments_list.itemDoubleClicked.connect( self.on_segments_list_doubleclick) self.jump_start_btn.clicked.connect(self.jump_to_segment_start) self.jump_end_btn.clicked.connect(self.jump_to_segment_end) self.set_start_btn.clicked.connect(self.set_segment_start) self.set_end_btn.clicked.connect(self.set_segment_end) self.download_thumbnails_btn.clicked.connect(self.download_thumbnails) self.segments_add_btn.clicked.connect(self.create_segment) self.segments_delete_btn.clicked.connect(self.delete_segment) self.split_btn.clicked.connect(self.split_selected_segment) self.launch_vlc_btn.clicked.connect(self.on_launch_vlc) self.file_path_field.returnPressed.connect(self.on_video_url_changed) self.file_browser_btn.clicked.connect(self.on_filebrowse_btn_click) self.process_selected_btn.clicked.connect( self.process_selected_segment) self.process_all_btn.clicked.connect(self.process_all_segments) def on_launch_vlc(self): self.vlc_interface.launch() def on_filebrowse_btn_click(self): filename = QFileDialog.getOpenFileName(self, "Select a video file") if filename[0]: self.set_video_file(filename[0]) def on_video_url_changed(self): self.set_video_file(self.file_path_field.text()) def on_segments_list_doubleclick(self, item): current_segment = item.get_segment() if current_segment: self.vlc_interface.set_current_time(int( current_segment.start_time)) def set_video_file(self, filepath=None): self.file_path_field.setText("" if filepath is None else filepath) if filepath: self.loaded_video = InputVideo() if re.search(r"^(?:/|[a-z]:[\\/])", filepath, re.I): file_url = "file://" + filepath self.loaded_video.is_local = True else: file_url = filepath if not self.loaded_video.is_local: streams = streamlink.streams(file_url) if streams: self.loaded_video.filepath = streams["best"].url else: self.loaded_video.filepath = file_url else: self.loaded_video.filepath = file_url try: self.update_twitch_metadatas() except requests.exceptions.ConnectionError: print("<!!> Can't connect to Twitch API.") try: self.vlc_interface.open_url(self.loaded_video.filepath) except requests.exceptions.ConnectionError: print("<!!> Can't connect to local VLC instance.") def get_twitch_id_from_filepath(self): filename = self.file_path_field.text() parsed_filename = re.search("([0-9]+)\.mp4$", filename, re.I) if parsed_filename: video_id = parsed_filename.group(1) return int(video_id) else: parsed_url = re.search("videos/([0-9]+)", filename, re.I) if parsed_url: video_id = parsed_url.group(1) return int(video_id) else: raise Exception( f"<!!> Can't find video Twitch id in video filename ({filename})" ) def create_segment_before(self, segment_obj): pass def create_segment_after(self, segment_obj): pass def update_twitch_metadatas(self): twitch_video_id = self.get_twitch_id_from_filepath() metadatas = self.twitch_interface.get_twitch_metadatas(twitch_video_id) self.loaded_video.metadatas = metadatas duration = parse_duration(metadatas["duration"]) self.id_twitch_field.setText(metadatas["id"]) self.created_at_field.setText(str(metadatas["created_at"])) self.duration_field.setText(format_time(duration.seconds)) self.title_field.setText(metadatas["title"]) self.streamer_field.setText(metadatas["user_login"]) for moment in self.twitch_interface.get_video_games_list( metadatas["id"]): s = Segment() s.name = f"{moment['description']} ({moment['type']})" s.start_time = moment['positionMilliseconds'] / 1000 s.end_time = (moment['positionMilliseconds'] + moment['durationMilliseconds']) / 1000 self.segments_list.addItem(SegmentListItem(s)) def create_segment(self): s = Segment() s.name = f"Segment {self.segments_list.count()}" s.start_time = 0 s.end_time = self.vlc_interface.get_duration() self.segments_list.addItem(SegmentListItem(s)) def delete_segment(self): for item in self.segments_list.selectedItems(): idx = self.segments_list.indexFromItem(item) item = self.segments_list.takeItem(idx.row()) del item def split_selected_segment(self): current_time = self.vlc_interface.get_current_time() for segment_item in self.segments_list.selectedItems(): current_segment = segment_item.get_segment() if current_segment: new_segment = segment_item.split( current_time, name="Splitted " + current_segment.name, split_mode=SPLIT_MODE.ABSOLUTE) self.segments_list.addItem(SegmentListItem(new_segment)) def get_selected_segments(self): return list( map(lambda item: item.get_segment(), self.segments_list.selectedItems())) def jump_to_segment_start(self): selected_segments = self.get_selected_segments() if selected_segments: self.vlc_interface.set_current_time( math.floor(selected_segments[0].start_time)) def jump_to_segment_end(self): selected_segments = self.get_selected_segments() if selected_segments: self.vlc_interface.set_current_time( math.floor(selected_segments[0].end_time)) def set_segment_start(self): current_time = self.vlc_interface.get_current_time() selected_segments = self.segments_list.selectedItems() if selected_segments: selected_segments[0].get_segment().start_time = current_time selected_segments[0].update() def set_segment_end(self): current_time = self.vlc_interface.get_current_time() selected_segments = self.segments_list.selectedItems() if selected_segments: selected_segments[0].get_segment().end_time = current_time selected_segments[0].update() def process_selected_segment(self): for segment in self.get_selected_segments(): self.process_segment(segment) def process_all_segments(self): for idx in range(self.segments_list.count()): segment_item = self.segments_list.item(idx) self.process_segment(segment_item.get_segment()) def process_segment(self, segment_obj): if not self.loaded_video: raise Exception("<!!> No video loaded") video_id = self.loaded_video.metadatas.get("id", None) created_at = self.loaded_video.metadatas.get("created_at", None) user_login = self.loaded_video.metadatas.get("user_login", None) if not (video_id and created_at and user_login): raise Exception("<!!> Missing video metadatas") created_at_timestamp = int(datetime.datetime.timestamp(created_at)) if self.loaded_video.is_local: cmd = f'ffmpeg -i "{self.loaded_video.filepath}" -ss {segment_obj.start_time} -to {segment_obj.end_time} -c:v copy -c:a copy "{user_login}_{created_at_timestamp}_{video_id}.mp4"' else: cmd = f'streamlink -f --hls-start-offset {format_time(segment_obj.start_time)} --hls-duration {format_time(segment_obj.end_time - segment_obj.start_time)} --player-passthrough hls "{self.loaded_video.filepath}" best -o "{user_login}_{created_at_timestamp}_{video_id}.mp4"' print(cmd) os.system(cmd) def download_thumbnails(self): twitch_video_id_str = self.id_twitch_field.text() if twitch_video_id_str: thumbnails_manifest_url = self.twitch_interface.get_video_thumbnails_manifest_url( int(twitch_video_id_str)) thumbnails_manifest, images_url_list = self.twitch_interface.get_thumbnails_url_from_manifest( thumbnails_manifest_url) for img in images_url_list: r = requests.get(images_url_list[img]) fp = open(img, "wb") fp.write(r.content) fp.close()