def set_enable_all_widgets(cls, parent: QWidget, widget_type, enabled: bool): """Set all widgets of a given type (or its subclasses) below given parent to given enabled state""" if isinstance(parent, widget_type): parent.setEnabled(enabled) for child in parent.children(): cls.set_enable_all_widgets(child, widget_type, enabled)
def walk(cls, w: W.QWidget, tree_path=None, parent=None, collect=None): collect = collect or cls.collect tree_path = tree_path or TreePath.root() children = w.children() info = collect(w, parent=parent, children=children, tree_path=tree_path) yield tree_path, info for idx, child in enumerate(children): yield from cls.walk( child, tree_path=tree_path.with_appended(idx), parent=w, collect=collect )
def clearWidget(widget: QWidget): num = 0 for d in widget.children(): #d.close() if isinstance(d, QLayout): clearLayout(d) else: d.close() d.deleteLater() num += 1 return num
def __clearMainWindow_(self, widget: QWidget) -> None: if isinstance(widget, QLineEdit): widget.clear() elif isinstance(widget, QSpinBox): widget.setValue(0) elif isinstance(widget, QPlainTextEdit): widget.setPlainText('') elif isinstance(widget, QCheckBox): widget.setChecked(False) elif isinstance(widget, QComboBox): widget.setCurrentIndex(0) elif isinstance(widget, QTableView): model = widget.model() model.clear() else: l = widget.children() for w in l: self.__clearMainWindow_(w)
class ChannelOptions(QWidget): """ Class for the channel loading window """ def __init__(self, data, parent): """ Constructor for channel loading. """ super().__init__() self.left = 10 self.top = 10 self.title = 'Select signals' self.width = parent.width / 5 self.height = parent.height / 2.5 # self.unprocessed_data = data_for_preds # if loading new data make copies in case user cancels loading channels self.new_load = 0 # if len(self.unprocessed_data) != 0: if data.edf_fn != parent.ci.edf_fn: self.pi = PredsInfo() self.new_load = 1 else: self.pi = parent.pi self.data = data self.parent = parent self.organize_win_open = 0 self.setup_ui() def setup_ui(self): """ Sets up UI for channel window. """ layout = QGridLayout() grid_lt = QGridLayout() grid_rt = QGridLayout() self.scroll = QScrollArea() self.scroll.setMinimumWidth(120) self.scroll.setMinimumHeight(200) # would be better if resizable self.scroll.setWidgetResizable(True) self.scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.chn_qlist = QListWidget() self.chn_qlist.setSelectionMode(QAbstractItemView.ExtendedSelection) self.scroll.setWidget(self.chn_qlist) self.populate_chn_list() self.data.converted_chn_names = [] self.data.convert_chn_names() self.ar1020 = self.data.can_do_bip_ar(1, 0) self.bip1020 = self.data.can_do_bip_ar(0, 0) self.ar1010 = self.data.can_do_bip_ar(1, 1) #self.bip1010 = self.data.can_do_bip_ar(0,1) self.data.total_nchns = len(self.data.chns2labels) self.setWindowTitle(self.title) self.setGeometry(self.parent.width / 3, self.parent.height / 3, self.width, self.height) lbl_info = QLabel("Select channels to plot: ") grid_lt.addWidget(lbl_info, 0, 0) self.scroll_chn_cbox = QScrollArea() self.scroll_chn_cbox.hide() self.scroll_chn_cbox.setWidgetResizable(True) self.scroll_chn_cbox.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.scroll_chn_cbox.setHorizontalScrollBarPolicy( Qt.ScrollBarAlwaysOff) if self.ar1020: self.cbox_ar = QCheckBox("Average reference (10-20)", self) self.cbox_ar.toggled.connect(self.ar_checked) grid_lt.addWidget(self.cbox_ar, 1, 0) self.cbox_bip = QCheckBox("Bipolar (10-20)", self) self.cbox_bip.toggled.connect(self.bip_checked) grid_lt.addWidget(self.cbox_bip, 2, 0) elif self.bip1020: self.cbox_bip = QCheckBox("Bipolar (10-20)", self) self.cbox_bip.toggled.connect(self.bip_checked) grid_lt.addWidget(self.cbox_bip, 1, 0) if self.ar1010: self.cbox_ar1010 = QCheckBox("Average reference (10-10)", self) self.cbox_ar1010.toggled.connect(self.ar_checked1010) grid_lt.addWidget(self.cbox_ar1010, 3, 0) # self.cbox_bip1010 = QCheckBox("Bipolar (10-10)",self) # self.cbox_bip1010.toggled.connect(self.bip_checked1010) # grid_lt.addWidget(self.cbox_bip1010,4,0) #elif self.bip1010: # self.cbox_bip1010 = QCheckBox("Bipolar (10-10)",self) # self.cbox_bip1010.toggled.connect(self.bip_checked1010) # grid_lt.addWidget(self.cbox_bip1010,3,0) self.chn_cbox_list = QWidget() self.scroll_chn_cbox.setWidget(self.chn_cbox_list) self.chn_cbox_layout = QVBoxLayout() self.chn_cbox_list.setLayout(self.chn_cbox_layout) self.cbox_list_items = [] for k in self.data.labels_from_txt_file.keys(): self.add_txt_file(k) self.uncheck_txt_files() grid_lt.addWidget(self.scroll_chn_cbox, 5, 0) #self.cbox_txtfile = QCheckBox("",self) #self.cbox_txtfile.toggled.connect(self.txtFileChecked) #grid_lt.addWidget(self.cbox_txtfile,6,0) self.btn_loadtxtfile = QPushButton("Load text file", self) self.btn_loadtxtfile.clicked.connect(self.load_txt_file) grid_lt.addWidget(self.btn_loadtxtfile, 6, 0) #self.btn_cleartxtfile = QPushButton("Clear text file",self) #self.btn_cleartxtfile.clicked.connect(self.clearTxtFile) #self.btn_cleartxtfile.setVisible(0) #grid_lt.addWidget(self.btn_cleartxtfile,6,0) if len(self.data.txt_file_fn) > 0: if self.data.use_loaded_txt_file: self.cbox_txtfile.setChecked(1) self.btn_loadtxtfile.setVisible(0) self.btn_cleartxtfile.setVisible(1) self.cbox_txtfile.setVisible(1) self.cbox_txtfile.setText(self.data.txt_file_fn) lbl = QLabel("") grid_lt.addWidget(lbl, 7, 0) btn_organize = QPushButton('Organize', self) btn_organize.clicked.connect(self.organize) grid_lt.addWidget(btn_organize, 8, 0) btn_colors = QPushButton('Change colors', self) btn_colors.clicked.connect(self.chg_colors) grid_lt.addWidget(btn_colors, 9, 0) btn_exit = QPushButton('Ok', self) btn_exit.clicked.connect(self.okay_pressed) grid_lt.addWidget(btn_exit, 10, 0) grid_rt.addWidget(self.scroll, 0, 1) layout.addLayout(grid_lt, 0, 0) layout.addLayout(grid_rt, 0, 1) self.setLayout(layout) if (not self.parent.argv.montage_file is None ) and self.parent.init == 0: self.load_txt_file(self.parent.argv.montage_file) self.okay_pressed() else: self.show() def populate_chn_list(self): """ Fills the list with all of the channels in the edf file. PREDICTION channels are ignored and saved into self.pi """ chns = self.data.chns2labels lbls = self.data.labels2chns self.data.pred_chn_data = [] edf_reader_obj = pyedflib.EdfReader(self.data.edf_fn) # if len(self.unprocessed_data) > 0: # reset predicted # self.parent.predicted = 0 if len(chns) == 0: self.parent.throw_alert("There are no named channels in the file.") self.close_window() else: self.chn_items = [] for i in range(len(chns)): if chns[i].find("PREDICTIONS") == -1: self.chn_items.append( QListWidgetItem(chns[i], self.chn_qlist)) self.chn_qlist.addItem(self.chn_items[i]) # elif len(self.unprocessed_data) > 0: # load in the prediction channels if they exist # if they do, then the file was saved which # means that there are a reasonable amount of channels elif self.new_load: # self.data.pred_chn_data.append(self.unprocessed_data[i]) self.data.pred_chn_data.append( edf_reader_obj.readSignal(i)) lbls.pop(chns[i]) chns.pop(i) # if len(self.unprocessed_data) > 0 and len(self.data.pred_chn_data) != 0: if self.new_load and len(self.data.pred_chn_data) != 0: self.data.pred_chn_data = np.array(self.data.pred_chn_data) self.data.pred_chn_data = self.data.pred_chn_data.T if self.data.pred_chn_data.shape[1] > 1: self.pi.pred_by_chn = 1 else: self.data.pred_chn_data = np.squeeze( self.data.pred_chn_data) if len(self.data.pred_chn_data) > 0: self.pi.preds = self.data.pred_chn_data self.pi.preds_to_plot = self.data.pred_chn_data self.pi.preds_loaded = 1 self.pi.plot_loaded_preds = 1 self.pi.preds_fn = "loaded from .edf file" self.pi.pred_width = ((self.data.fs * self.data.max_time) / self.pi.preds.shape[0]) self.parent.predicted = 1 # select the previously selected channels if they exist if len(self.data.list_of_chns) != 0 and not self.new_load: for k in range(len(self.data.list_of_chns)): self.chn_items[self.data.list_of_chns[k]].setSelected(True) self.scroll.show() def ar_checked(self): """ Called when average reference is checked. """ cbox = self.sender() chns = self.data.get_chns(self.data.labelsAR1020) if cbox.isChecked(): self.uncheck_txt_files() self.cbox_bip.setChecked(0) #if self.ar1010: # self.cbox_ar1010.setChecked(0) # self.cbox_bip1010.setChecked(0) #elif self.bip1010: # self.cbox_bip1010.setChecked(0) # select all AR channels, deselect all others self._select_chns(chns, 0) else: self._select_chns(chns, 1) def bip_checked(self): """ Called when bipolar is checked. """ cbox = self.sender() chns = self.data.get_chns(self.data.labelsBIP1020) if cbox.isChecked(): if self.ar1010: self.cbox_ar1010.setChecked(0) # self.cbox_bip1010.setChecked(0) # elif self.bip1010: # self.cbox_bip1010.setChecked(0) if self.ar1020: chns = self.data.get_chns(self.data.labelsAR1020) if cbox.isChecked(): self.cbox_ar.setChecked(0) self.uncheck_txt_files() self._select_chns(chns, 0) else: self._select_chns(chns, 1) elif cbox.isChecked(): self.uncheck_txt_files() # select all bipolar channels, deselect all others self._select_chns(chns, 0) else: self._select_chns(chns, 1) def ar_checked1010(self): """ Called when average reference 1010 is called. """ cbox = self.sender() chns = self.data.get_chns(self.data.labelsAR1010) if cbox.isChecked(): self.uncheck_txt_files() self.cbox_bip.setChecked(0) self.cbox_ar.setChecked(0) # self.cbox_bip1010.setChecked(0) # select all AR channels, deselect all others self._select_chns(chns, 0) else: self._select_chns(chns, 1) def bip_checked1010(self): """ Called when bipolar 1010 is called. """ cbox = self.sender() chns = self.data.get_chns(self.data.labelsBIP1010) if self.ar1020: chns = self.data.get_chns(self.data.labelsAR1010) if cbox.isChecked(): self.cbox_ar.setChecked(0) self.uncheck_txt_files() if self.ar1010: self.cbox_ar1010.setChecked(0) self.cbox_bip.setChecked(0) self._select_chns(chns, 0) else: self._select_chns(chns, 1) elif cbox.isChecked(): self.uncheck_txt_files() self.cbox_bip.setChecked(0) # select all bipolar channels, deselect all others self._select_chns(chns, 0) else: self._select_chns(chns, 1) def uncheck_txt_files(self): """ Deselect all text files. """ for child in self.chn_cbox_list.children(): for grand_child in child.children(): if isinstance(grand_child, QCheckBox): grand_child.setChecked(0) def txt_file_checked(self): # Called if a text file is selected. c = self.sender() name = c.text() chns = self.data.get_chns(self.data.labels_from_txt_file[name]) if c.isChecked(): if self.ar1020: self.cbox_ar.setChecked(0) self.cbox_bip.setChecked(0) if self.ar1010: self.cbox_ar1010.setChecked(0) # self.cbox_bip1010.setChecked(0) if self.bip1020: self.cbox_bip.setChecked(0) #if self.bip1010: # self.cbox_bip1010.setChecked(0) for child in self.chn_cbox_list.children(): for ch in child.children(): if isinstance(ch, QCheckBox): if ch.text() != name: ch.setChecked(0) self._select_chns(chns, 0) self.data.use_loaded_txt_file = 1 else: self._select_chns(chns, 1) self.data.use_loaded_txt_file = 0 def _select_chns(self, chns, deselect_only): """ Selects given channels. Args: chns - the channels to select / deselect deselect_only - whether to only deselect given channels """ if deselect_only: for k, val in enumerate(chns): if val: self.chn_items[k].setSelected(0) else: for k, val in enumerate(chns): if val: self.chn_items[k].setSelected(1) else: self.chn_items[k].setSelected(0) def add_txt_file(self, name): """ Called to load in the new text file and add to the list. """ # show the scroll area if it is hidden if self.scroll_chn_cbox.isHidden(): self.scroll_chn_cbox.show() # add text file to the list main_wid = QWidget() wid = QGridLayout() wid_name = QCheckBox(name) wid_name.toggled.connect(self.txt_file_checked) wid_name.setChecked(1) wid.addWidget(wid_name, 0, 0) main_wid.setLayout(wid) self.chn_cbox_layout.addWidget(main_wid) def load_txt_file(self, name=""): """ Called to load a text file. """ if self.parent.argv.montage_file is None or self.parent.init: name = QFileDialog.getOpenFileName(self, 'Open file', '.', 'Text files (*.txt)') name = name[0] if name is None or len(name) == 0: return short_name = name.split('/')[-1] if len(name.split('/')[-1]) > 15: short_name = name.split('/')[-1][0:15] + "..." if short_name in self.data.labels_from_txt_file.keys(): self.parent.throw_alert( "Each loaded text file must have a unique " + "name (first 14 characters). Please rename your file.") return if self._check_chns(name, short_name): self.add_txt_file(short_name) else: # throw error self.parent.throw_alert( "The channels in this file do not match" + " those of the .edf file you have loaded. Please check your file." ) def _check_chns(self, txt_fn, txt_fn_short): """ Function to check that this file has the appropriate channel names. Sets self.data.labels_from_txt_file if valid. Args: txt_fn: the file name to be loaded txt_fn_short: the name to be used in the dict Returns: 1 for sucess, 0 for at least one of the channels was not found in the .edf file """ try: text_file = open(txt_fn, "r") except: self.parent.throw_alert("The .txt file is invalid.") lines = text_file.readlines() l = 0 while l < len(lines): loc = lines[l].find("\n") if loc != -1: lines[l] = lines[l][0:loc] if len(lines[l]) == 0: lines.pop(l) else: l += 1 text_file.close() ret = 1 for l in lines: if not l in self.data.converted_chn_names and not l in self.data.labels2chns: ret = 0 if ret: self.data.labels_from_txt_file[txt_fn_short] = [] for i in range(len(lines)): if not lines[len(lines) - 1 - i] in self.data.converted_chn_names: lines[len(lines) - 1 - i] = convert_txt_chn_names( lines[len(lines) - 1 - i]) self.data.labels_from_txt_file[txt_fn_short].append( lines[len(lines) - 1 - i]) return ret def check_multi_chn_preds(self): """ Check if plotting predictions by channel. If so, check whether the number of channels match. Sets parent.predicted to 1 if correct, 0 if incorrect. """ if self.parent.pi.pred_by_chn and self.parent.predicted: if self.parent.ci.nchns_to_plot != self.parent.pi.preds_to_plot.shape[ 1]: self.parent.predicted = 0 def overwrite_temp_info(self): """ If temporary data was created in case the user cancels loading channels, it is now overwritten. Things to be overwritten: - parent.edf_info - parent.data - parent.fs - parent.max_time - parent.pi - parent.ci - parent.predicted - parent.count (set to 0 if new load) """ self.parent.edf_info = self.parent.edf_info_temp self.parent.max_time = self.parent.max_time_temp self.parent.pi.write_data(self.pi) self.parent.ci.write_data(self.data) self.data = self.parent.ci self.parent.sei.fn = self.parent.fn_full_temp if self.new_load: self.parent.count = 0 self.parent.lbl_fn.setText("Plotting: " + self.parent.fn_temp) def organize(self): """ Function to open the window to change signal order. """ if not self.parent.organize_win_open: ret = self.check() if ret == 0: self.parent.organize_win_open = 1 self.parent.chn_org = OrganizeChannels(self.data, self.parent) self.parent.chn_org.show() self.close_window() def chg_colors(self): """ Function to open the window to change signal color. """ if not self.parent.color_win_open: ret = self.check() if ret == 0: self.parent.color_win_open = 1 self.parent.color_ops = ColorOptions(self.data, self.parent, self) self.parent.color_ops.show() self.close_window() def check(self): """ Function to check the clicked channels and exit. Returns: -1 if there are no selected channels, 0 otherwise """ selected_list_items = self.chn_qlist.selectedItems() idxs = [] for k in range(len(self.chn_items)): if self.chn_items[k] in selected_list_items: idxs.append(self.data.labels2chns[self.data.chns2labels[k]]) if len(idxs) > self.parent.max_channels: self.parent.throw_alert("You may select at most " + str(self.parent.max_channels) + " to plot. " + "You have selected " + str(len(idxs)) + ".") return -1 if len(idxs) == 0: self.parent.throw_alert("Please select channels to plot.") return -1 # Overwrite if needed, and prepare to plot if self.new_load: self.overwrite_temp_info() if self.parent.si.plot_spec: self.parent.si.plot_spec = 0 self.parent.removeSpecPlot() plot_bip_from_ar = 0 if (self.ar1020 and self.cbox_bip.isChecked()): # or #self.ar1010 and self.cbox_bip1010.isChecked()): plot_bip_from_ar = 1 mont_type, txt_file_name = self._get_mont_type() self.data.prepare_to_plot(idxs, self.parent, mont_type, plot_bip_from_ar, txt_file_name) # check if multi-chn pred and number of chns match self.check_multi_chn_preds() return 0 def _get_mont_type(self): """ Gets the type of montage from the cboxes. Returns: 0 = ar 1020 1 = bip 1020 2 = ar 1010 3 = ar 1010 4 = text file 5 = selected channels don't match selection txt_file_name, the name of the text file """ mont_type = 5 txt_file_name = "" if self.ar1020 and self.cbox_ar.isChecked(): mont_type = 0 elif (self.ar1020 or self.bip1020) and self.cbox_bip.isChecked(): mont_type = 1 elif self.ar1010 and self.cbox_ar1010.isChecked(): mont_type = 2 #elif self.bip1010 and self.cbox_bip1010.isChecked(): # mont_type = 3 else: for child in self.chn_cbox_list.children(): for ch in child.children(): if isinstance(ch, QCheckBox) and ch.isChecked(): txt_file_name = ch.text() mont_type = 4 return mont_type, txt_file_name def okay_pressed(self): """ Called when okay is pressed. Calls check function and returns if channels were sucessfully selected. """ ret = self.check() if ret == 0: self.parent.call_initial_move_plot() self.close_window() def close_window(self): """ Closes the window. """ self.parent.chn_win_open = 0 self.close() def closeEvent(self, event): """ Called when the window is closed. """ self.parent.chn_win_open = 0 event.accept()
class AboutDialog(QDialog): def __init__(self, main_window): super().__init__(main_window) self.setWindowTitle(_("About UML .FRI")) main_layout = QVBoxLayout() desc_layout = QHBoxLayout() logo_pixmap = QPixmap() logo_pixmap.load(os.path.join(GRAPHICS, "logo", "logo_full.png")) logo = QLabel() logo.setPixmap(logo_pixmap) desc_layout.addWidget(logo, 0, Qt.AlignTop) desc_text_layout = QVBoxLayout() desc_text_layout.addWidget(QLabel("<h1>{0}</h1>".format(Application().about.name))) desc_text_layout.addWidget(QLabel("{0} {1}".format(_("Version"), Application().about.version))) desc_text_layout.addItem(QSpacerItem(0, 20)) desc_label = QLabel(Application().about.description) desc_label.setWordWrap(True) desc_text_layout.addWidget(desc_label) desc_text_layout.addStretch(1) for author, year in Application().about.author: desc_text_layout.addWidget(QLabel("<small>{0}</small>".format(self.__about_line(author, year)))) for url in Application().about.urls: desc_text_layout.addWidget(self.__create_link(url)) desc_layout.addLayout(desc_text_layout, 1) main_layout.addLayout(desc_layout) tabs = QTabWidget() tabs.setUsesScrollButtons(False) main_layout.addWidget(tabs, 1) self.__updates_tab = None tabs.addTab(self.__create_used_libraries_tab(), _("Used libraries")) if os.path.exists(LICENSE_FILE): tabs.addTab(self.__create_license_tab(), _("License")) tabs.addTab(self.__create_version_1_0_tab(), _("Version 1.0 contributions")) tabs.addTab(self.__create_updates_tab(), _("Updates")) if Application().about.is_debug_version: debug_layout = QHBoxLayout() debug_icon = QLabel() debug_icon.setPixmap(QIcon.fromTheme("dialog-warning").pixmap(QSize(16, 16))) debug_layout.addWidget(debug_icon) debug_text = QLabel(_("You are running UML .FRI in the debug mode")) debug_layout.addWidget(debug_text, 1) main_layout.addLayout(debug_layout) button_box = QDialogButtonBox(QDialogButtonBox.Ok) button_box.button(QDialogButtonBox.Ok).setText(_("Ok")) button_box.accepted.connect(self.accept) main_layout.addWidget(button_box) self.setLayout(main_layout) Application().event_dispatcher.subscribe(UpdateCheckStartedEvent, self.__update_started) Application().event_dispatcher.subscribe(UpdateCheckFinishedEvent, self.__update_finished) def __create_used_libraries_tab(self): versions_layout = QFormLayout() for depencency, version in Application().about.dependency_versions: versions_layout.addRow(_("{0} version").format(depencency), QLabel(version)) versions_widget = QWidget() versions_widget.setLayout(versions_layout) return versions_widget def __create_license_tab(self): license_text = QTextEdit() with open(LICENSE_FILE, 'rt') as license_file: license_text.setPlainText(license_file.read()) license_text.setReadOnly(True) license_text.setLineWrapMode(QTextEdit.NoWrap) return license_text def __create_version_1_0_tab(self): version_1_layout = QVBoxLayout() for author, year in Application().about.version_1_contributions: version_1_layout.addWidget(QLabel(self.__about_line(author, year))) version_1_widget = QWidget() version_1_widget.setLayout(version_1_layout) version_1_scroll = QScrollArea() version_1_scroll.setWidget(version_1_widget) return version_1_scroll def __create_updates_tab(self): if self.__updates_tab is None: self.__updates_tab = QWidget() else: self.__updates_tab.children()[0].deleteLater() updates_layout = QGridLayout() updates_widget = QWidget(self.__updates_tab) updates_widget.setLayout(updates_layout) self.__check_updates = QPushButton(_("Check for updates")) self.__check_updates.clicked.connect(lambda checked=False: Application().about.updates.recheck_update()) updates_layout.addWidget(self.__check_updates, 0, 0, 1, 3, Qt.AlignLeft) latest_version = Application().about.updates.latest_version updates_layout.addWidget(QLabel(_("Latest version available:")), 1, 0) if latest_version is None: updates_layout.addWidget(QLabel("-"), 1, 1) else: updates_layout.addWidget(QLabel(str(latest_version.version)), 1, 1) if Application().about.updates.latest_version.is_newer: updates_layout.addWidget(self.__create_download_link(latest_version.url), 1, 2) latest_prerelease = Application().about.updates.latest_prerelease if latest_prerelease is not None: updates_layout.addWidget(QLabel(_("Latest unstable version available:")), 2, 0) updates_layout.addWidget(QLabel(str(latest_prerelease.version)), 2, 1) if latest_prerelease.is_newer: updates_layout.addWidget(self.__create_download_link(latest_prerelease.url), 2, 2) if Application().about.updates.has_error: updates_layout.addWidget(QLabel("<b>{0}</b>".format(_("Error while checking update:"))), 3, 0) more_info = QPushButton(_("More info")) more_info.clicked.connect(self.__show_update_error) updates_layout.addWidget(more_info, 3, 1) updates_widget.setVisible(True) return self.__updates_tab def __show_update_error(self, checked=False): ExceptionDialog(Application().about.updates.error).exec() def __create_link(self, url): ret = QLabel("<a href=\"{0}\">{1}</a>".format(url, url)) ret.setOpenExternalLinks(True) return ret def __create_download_link(self, url): ret = QLabel("<a href=\"{0}\">{1}</a>".format(url, _("download update"))) ret.setToolTip(url) ret.setOpenExternalLinks(True) return ret def __about_line(self, author, year): line = "© " if isinstance(year, tuple): line += "{0}-{1} ".format(year[0], year[1]) else: line += "{0} ".format(author[1]) line += author return line def __update_started(self, event): self.__check_updates.setEnabled(False) def __update_finished(self, event): self.__create_updates_tab()
class App(QMainWindow): PATH = None def __init__(self): super().__init__() self.old_disk_data = reusables.load_json('database.json') self.window_title = 'Movies' self.left = 300 self.top = 300 self.width = 920 self.height = 600 self.data = {} tmdb2imdb = {} # self.getConfig() self.movies = {} # Contains the id -> title of movies self.popular_movies = [] self.threaded_search = ThreadedSearcher() self.threaded_search.search_result.connect(self.search_response) self.initUI() # Contains all the movies that fetched as popular movies form tmdb at intialization def getConfig(self): config = reusables.load_json('config.json') self.PATH = config['path'] @QtCore.pyqtSlot(dict) def updateData(self, movie): self.data[movie['id']] = movie self.statusBar.showMessage('Received info about: ' + movie['title']) # It is callback function from infoGetter, that tells to update UI btn = self.movieAreaContents.findChild(QtWidgets.QToolButton, str(movie['id'])) if btn: btn.setText('{} ({})'.format(movie['title'], movie['Year'])) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(movie['poster_value']), QtGui.QIcon.Normal, QtGui.QIcon.Off) btn.setIcon(icon) def infoGetterThreaded(self): # It starts the threaded info getter that obtains the images, plots and other info from tmbd (database.py) # self.updater = Updater(self.data, self.movies) self.updater = Updater() self.updater.update_signal.connect(self.updateData) self.updater.start() # self.insertSearchStack() def put_in_updater_stack(self, id): # if id not in if id in self.old_disk_data.keys( ): # If already avaiable disk data is avaiable, use that self.updateData(self.old_disk_data[id]) return threading.Thread(target=self.updater.putStack, args=(id, )).start() def updateAvailabeInfo(self): for iid, info in self.data.items(): btn = self.movieAreaContents.findChild(QtWidgets.QToolButton, iid) btn.setText('{} ({})'.format(info['title'], info['Year'])) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(info['poster_value']), QtGui.QIcon.Normal, QtGui.QIcon.Off) btn.setIcon(icon) def initUI(self): self.setWindowTitle(self.window_title) self.setGeometry(self.left, self.top, self.width, self.height) # Topmost widget in the program self.mainWigdet = QWidget() self.mainWigdet.setObjectName('main Widget') self.mainLayout = QVBoxLayout() # Gets the toolbar layout from the function self.toolbar = self.addToolbar() # Layout for the main area of the application ie movieArea & infoArea self.mainArea = QHBoxLayout() movieArea = self.movieArea() self.mainArea.addWidget(movieArea) spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) self.mainArea.addItem(spacerItem2) infoArea = self.infoArea() self.mainArea.addWidget(infoArea) # Finally setting of the application self.mainLayout.addLayout(self.toolbar) self.mainLayout.addLayout(self.mainArea) self.mainWigdet.setLayout(self.mainLayout) self.setCentralWidget(self.mainWigdet) self.insertMenuBar() self.insertStatusbar() # for a in ['title', 'tagline', 'Year', 'Rating', 'Director', 'Duration', 'Genre', 'Budget', 'Box Office', # 'cast','plot']: # self.contents.findChild(QLabel, a).setText(a) # Call the threaded function, to update the info about the movies simultaneously self.infoGetterThreaded() # Update the icons for the data that is already avaiable self.updateAvailabeInfo() # self.installEventFilter(self) # All the buttons in movieArea, used by search self.popular_movies_buttons = self.movieAreaContents.children()[1:] for id in self.movies: self.put_in_updater_stack(id) # print('done') # Set the inital infoArea to the first movie on the list # if self.movies: # self.showInfoArea(list(self.movies.keys())[0]) def create_movie_button(self, movie_name, id): icon_size = QtCore.QSize(156, 210) btn_size = QtCore.QSize(167, 240) icon = QtGui.QIcon() btn = QtWidgets.QToolButton() btn.setText(movie_name) btn.setObjectName(str(id)) # if iid in self.data.keys(): # Already searched movies uses database # btn.setText('{} ({})'.format(self.data[iid]['title'], self.data[iid]['Year'])) # btn.setObjectName(str(iid)) # else: ## Still unsearched movies uses pathname # btn.setText(movie.split(os.path.sep)[-1]) # btn.setObjectName(str(iid)) path = './media/poster/' + 'na' + '.jpg' # print(movie, path) icon.addPixmap(QtGui.QPixmap(path), QtGui.QIcon.Normal, QtGui.QIcon.Off) btn.setIcon(icon) btn.setAutoRaise(True) btn.setStyleSheet('''text-align:left;''') btn.setIconSize(icon_size) btn.setAutoExclusive(True) btn.setCheckable(True) btn.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon) btn.setFixedSize(btn_size) btn.installEventFilter(self) self.lyt.addWidget(btn) def movieArea(self): scrollArea = QScrollArea() # Main scroll Area self.movieAreaContents = QWidget( ) # Contents for inside the scroll area i.e. Buttons self.setObjectName('movieAreaContents') self.lyt = FlowLayout(hspacing=15, vspacing=10) # Layout for those contents/buttons self.lyt.setContentsMargins(0, 0, 0, 50) # Setup the properties of scrollbar scrollArea.setGeometry(QtCore.QRect(0, 0, 405, 748)) scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) scrollArea.setWidgetResizable(True) movies = self.getMovieNamesList() for iid, movie in movies.items(): self.create_movie_button(movie, iid) self.movieAreaContents.setLayout(self.lyt) scrollArea.setWidget(self.movieAreaContents) # Register buttons for events return scrollArea def infoArea(self): scrollArea = QScrollArea() # Main scroll Area self.contents = QWidget( ) # Contents for inside the scroll area i.e. Buttons lyt = QVBoxLayout() # Layout for those contents/buttons # Setup Properties for Content self.contents.setGeometry(QtCore.QRect(0, 0, 670, 806)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.contents.sizePolicy().hasHeightForWidth()) self.contents.setSizePolicy(sizePolicy) self.contents.setLayoutDirection(QtCore.Qt.LeftToRight) self.contents.setObjectName("info_scroll_area") # Setup properties for box layout lyt.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) lyt.setSpacing(10) lyt.setContentsMargins(10, -1, -1, -1) # Setup the properties of scrollarea scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) scrollArea.setWidgetResizable(True) scrollArea.setMinimumSize(QtCore.QSize(675, 0)) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( scrollArea.sizePolicy().hasHeightForWidth()) scrollArea.setSizePolicy(sizePolicy) # Basic icon size icon_size = QtCore.QSize(154, 210) btn_size = QtCore.QSize(154, 240) icon = QtGui.QIcon() # #Function to rapidly create common heading value rows # sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) # sizePolicy.setHorizontalStretch(0) # sizePolicy.setVerticalStretch(0) def infoRow(name): row = QHBoxLayout() row.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize) font = QtGui.QFont() font.setPointSize(9) font.setBold(True) font.setWeight(75) first = QLabel() first.setFont(font) first.setText(name) first.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTop | QtCore.Qt.AlignTrailing) font = QtGui.QFont() font.setPointSize(9) second = QLabel(text='') second.setFont(font) second.setObjectName(name) second.setOpenExternalLinks(True) second.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) row.addWidget(first) row.addWidget(second) return row #### Title self.title = QLabel('') font = QtGui.QFont() font.setFamily("Calibri") font.setPointSize(16) font.setBold(True) font.setWeight(75) self.title.setFont(font) self.title.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop) self.title.setWordWrap(True) self.title.setObjectName("title") lyt.addWidget(self.title) # Backdrop self.image = QtWidgets.QVBoxLayout() self.image.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) self.image.setSpacing(0) self.image.setObjectName("image") self.backdrop_value = QtWidgets.QLabel() sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Ignored) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.backdrop_value.sizePolicy().hasHeightForWidth()) self.backdrop_value.setSizePolicy(sizePolicy) self.backdrop_value.setMinimumSize(QtCore.QSize(624, 351)) self.backdrop_value.setMaximumSize(QtCore.QSize(624, 351)) self.backdrop_value.setPixmap(QtGui.QPixmap("./media/backdrop/na.jpg")) self.backdrop_value.setScaledContents(True) # self.setStyleSheet('''padding: 0px 0px 0px 0px;''') self.backdrop_value.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop) self.backdrop_value.setObjectName("backdrop_value") self.image.addWidget(self.backdrop_value) # Poster self.poster_value = QtWidgets.QLabel() sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Minimum) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth( self.poster_value.sizePolicy().hasHeightForWidth()) self.poster_value.setSizePolicy(sizePolicy) self.poster_value.setMinimumSize(QtCore.QSize(0, 52)) self.poster_value.setMaximumSize(QtCore.QSize(16777215, 240)) self.poster_value.setPixmap(QtGui.QPixmap('./media/poster/na.jpg')) self.poster_value.setScaledContents(False) self.poster_value.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop) self.poster_value.setIndent(0) self.poster_value.setStyleSheet('''padding: 0px 0px 0px 40px;''') self.poster_value.setObjectName("poster_value") self.image.addWidget(self.poster_value) lyt.addLayout(self.image) ## Tagline self.tagline = QLabel(text='') font = QtGui.QFont() font.setPointSize(10) font.setWeight(50) self.tagline.setFont(font) self.tagline.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop) self.tagline.setWordWrap(True) self.tagline.setObjectName('tagline') lyt.addWidget(self.tagline) spacer = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) lyt.addItem(spacer) lyt.addLayout(infoRow('Year')) lyt.addLayout(infoRow('Rating')) lyt.addLayout(infoRow('Director')) lyt.addLayout(infoRow('Duration')) lyt.addLayout(infoRow('Genre')) lyt.addLayout(infoRow('Budget')) lyt.addLayout(infoRow('Box Office')) # Cast self.cast_box = QtWidgets.QVBoxLayout() self.cast_box.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint) self.cast_box.setSpacing(4) self.cast = QtWidgets.QLabel(text='Cast') font = QtGui.QFont() font.setPointSize(9) font.setBold(True) font.setWeight(75) self.cast.setFont(font) self.cast.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop) self.cast_box.addWidget(self.cast) self.cast_value = QtWidgets.QLabel(text='') font = QtGui.QFont() font.setPointSize(9) self.cast_value.setFont(font) self.cast_value.setOpenExternalLinks(True) self.cast_value.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop) self.cast_value.setWordWrap(True) self.cast_value.setObjectName("cast") self.cast_box.addWidget(self.cast_value) lyt.addLayout(self.cast_box) # Spacer spacer = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) lyt.addItem(spacer) # Description self.plot_value = QtWidgets.QLabel(text='') font = QtGui.QFont() font.setPointSize(9) self.plot_value.setFont(font) self.plot_value.setTextFormat(QtCore.Qt.AutoText) self.plot_value.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop) self.plot_value.setWordWrap(True) self.plot_value.setObjectName("plot") lyt.addWidget(self.plot_value) lyt.addItem( QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)) # rating_box = QHBoxLayout() # # self.slider = QSlider(Qt.Horizontal) # self.slider.setMinimum(1) # self.slider.setMaximum(10) # self.slider.setTickInterval(1) # self.slider.setTickPosition(QSlider.TicksBelow) # self.rating_value = QLabel(text=' (0/5) ') # # self.slider.valueChanged[int].connect(self.rating_value_changed) # # rating_box.addWidget(QLabel(text='Your Rating')) # rating_box.addWidget(self.slider) # rating_box.addWidget(self.rating_value) # rating_box.setAlignment(Qt.AlignVCenter) # rating_box.setAlignment(Qt.AlignBottom) # lyt.addLayout(rating_box) self.contents.setLayout(lyt) # child = contents.findChild(QLabel, 'a') scrollArea.setWidget(self.contents) return scrollArea # def rating_value_changed(self, x): # my_ratings = reusables.load_json('my_ratings.json') # self.rating_value.setText('({:.1f}/5)'.format(x / 2)) # self.data[self.CURRENT_MOVIE]['my_rating'] = x / 2 # my_ratings[self.data[self.CURRENT_MOVIE]['imdb']] = x / 2 # # reusables.save_json(my_ratings, 'my_ratings.json', indent=2) # reusables.save_json(self.data, 'database_v2.json', indent=2) def insertStatusbar(self): self.statusBar = QStatusBar() self.setStatusBar(self.statusBar) self.statusBar.showMessage('Loaded {} movies'.format(len(self.movies))) def openSettings(self): config = reusables.load_json('config.json') dlg = Setting_UI(self) dlg.path_line_edit.setText(config['path']) if dlg.exec_(): # If okay is pressed new_path = dlg.path_line_edit.text() if os.path.exists(new_path): self.PATH = new_path print(self.PATH) self.initUI() else: # If cancel is pressed pass def insertMenuBar(self): # create menu menubar = QMenuBar() # layout.addWidget(menubar, 0, 0) actionFile = menubar.addMenu("File") actionSetting = actionFile.addAction("Settings") # actionFile.addAction("New") # actionFile.addAction("Open") # actionFile.addAction("Save") actionFile.addSeparator() actionQuit = actionFile.addAction("Quit") menubar.addMenu("Edit") menubar.addMenu("View") menubar.addMenu("Help") # Event Handlers actionSetting.triggered.connect(self.openSettings) actionQuit.triggered.connect(self.close) self.setMenuBar(menubar) def addToolbar(self): layout = QtWidgets.QHBoxLayout() # layout.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) # Add the widgets with size contrainsts self.search_field = QLineEdit() self.search_field.setMaximumSize(QtCore.QSize(500, 32)) self.search_field.setAlignment(Qt.AlignLeft) search_button = QPushButton('Search') search_button.setMaximumSize(QtCore.QSize(68, 34)) spacerItem = QtWidgets.QSpacerItem(10, 35, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) sort_label = QLabel('Sort By') sort_label.setOpenExternalLinks(True) sort_label.setMaximumSize(QtCore.QSize(72, 32)) comboBox = QComboBox() comboBox.setMaximumSize(QtCore.QSize(100, 32)) comboBox.addItems( ['Name A-Z', 'Name Z-A', 'Rating', 'Year', 'Runtime']) propertiesButton = QPushButton('Properties') propertiesButton.setMaximumSize(QtCore.QSize(95, 34)) spacerItem2 = QtWidgets.QSpacerItem(10, 35, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) ## Events search_button.clicked.connect(self.search_option) self.search_field.returnPressed.connect(self.search_option) self.search_field.textChanged.connect(self.search_option) comboBox.currentIndexChanged['QString'].connect( self.sort_option_changed) # Add the widgets to the layout layout.addWidget(self.search_field) layout.addWidget(search_button) layout.addItem(spacerItem) layout.addWidget(sort_label) layout.addWidget(comboBox) layout.addWidget(propertiesButton) layout.addItem(spacerItem2) layout.setAlignment(Qt.AlignLeft) layout.setAlignment(Qt.AlignHCenter) return layout # def search_option(self): # query = self.search_field.text() # # child_dict = defaultdict(str) # # accepted_list = [] # for child in self.childs: # if query == '': # If the query is None, the add all the widgets # self.lyt.addWidget(child) # elif query.lower() not in self.data[child.objectName()]['title'].lower(): # child.setParent(None) # else: # self.lyt.addWidget(child) # # self.statusBar.showMessage('{}'.format(self.movieAreaContents.children()[:1])) @QtCore.pyqtSlot(list) def search_response(self, list_of_results): print(len(list_of_results)) for child in self.movieAreaContents.children( )[1:]: # Remove all the button from movieArea child.setParent(None) for title, year, id in list_of_results: # Add newly searched movies if id not in self.popular_movies: self.create_movie_button('{} ({})'.format(title, year), str(id)) self.put_in_updater_stack(str(id)) for child in self.popular_movies_buttons: # Add the popular movies again self.lyt.addWidget(child) def threadedSearcherRequest(self, query): # print('here', query) self.threaded_search.putStack(query) def search_option(self): query = self.search_field.text() child_dict = defaultdict(str) self.threadedSearcherRequest(query) accepted_list = [] # for child in self.popular_movies_buttons: # if query == '': # If the query is None, the add all the widgets # self.lyt.addWidget(child) # elif query.lower() not in self.data[child.objectName()]['title'].lower(): # child.setParent(None) # else: # self.lyt.addWidget(child) # # self.statusBar.showMessage('{}'.format(self.movieAreaContents.children()[:1])) def sort_option_changed(self): sort_option = self.sender().currentText() childs = self.movieAreaContents.children()[1:] child_dict = defaultdict(str) child_dict = {child.objectName(): child for child in childs} for c in childs: c.setParent(None) dat = self.movies.keys() if sort_option == 'Name A-Z': dat = sorted(self.data, key=lambda x: self.data[x]['title'], reverse=False) elif sort_option == 'Name Z-A': dat = sorted(self.data, key=lambda x: self.data[x]['title'], reverse=True) elif sort_option == 'Rating': dat = sorted(self.data, key=lambda x: self.data[x]['Rating'], reverse=True) elif sort_option == 'Year': dat = sorted(self.data, key=lambda x: int(self.data[x]['Year']), reverse=True) elif sort_option == 'Runtime': dat = sorted(self.data, key=lambda x: int(self.data[x]['runtime_mins']), reverse=False) for k, v in self.movies.items(): if k not in dat: dat.append(k) for iid in dat: self.lyt.addWidget(child_dict[iid]) self.repaint() def showInfoArea(self, id): self.CURRENT_MOVIE = id linkgetter = lambda lst: ''.join([ '''<a style="text-decoration:none; color:black;" href="https://google.com/search?q={}">{}, </a> ''' .format(urllib.parse.quote(item), item) for item in lst ]) if id in self.data.keys(): info = self.data[id] for k in [ 'title', 'tagline', 'Year', 'Rating', 'Director', 'Duration', 'Genre', 'Budget', 'Box Office', 'cast', 'plot' ]: text = info[k] if type(text) == list: text = linkgetter(text) # print(text) self.contents.findChild(QLabel, k).setText(text) self.contents.findChild(QLabel, 'poster_value').setPixmap( QtGui.QPixmap(info['poster_value'])) self.contents.findChild(QLabel, 'backdrop_value').setPixmap( QtGui.QPixmap(info['backdrop_value'])) # Set the my_ratings # if 'my_rating' in self.data[self.CURRENT_MOVIE]: # if self.data[self.CURRENT_MOVIE]['my_rating']: # self.rating_value.setText('({:.1f}/5)'.format(self.data[self.CURRENT_MOVIE]['my_rating'])) # self.slider.setValue(int(self.data[self.CURRENT_MOVIE]['my_rating'] * 2)) def play_torrent(self, imdb_id): threading.Thread(target=player.play, args=(imdb_id, )).start() def eventFilter(self, obj, event): if type(obj) is QtWidgets.QToolButton: if event.type() == QtCore.QEvent.MouseButtonDblClick: # print("Double Click") print('Playing: ', self.data[obj.objectName()]['title']) self.play_torrent(self.data[obj.objectName()]['imdb_id']) # os.startfile(self.movies[obj.objectName()]) if event.type() == QtCore.QEvent.MouseButtonPress: if event.button() == QtCore.Qt.LeftButton: # print(self.movie_name_json[obj.objectName()], "Left click") # self.update_movie_info(movie_name=obj.objectName()) self.showInfoArea(obj.objectName()) elif event.button() == QtCore.Qt.RightButton: # print(obj.objectName(), "Right click", event.globalPos()) self.movieButtonContextMenu(obj.objectName()) # self.actionRight(obj) # print(self.db.database[obj.objectName()]['location']) # subprocess.Popen(r'explorer /select, ' + self.db.database[obj.objectName()]['location']) else: # For global keys if event.type() == QtCore.QEvent.KeyPress: key = event.key() # for a in self.movieAreaContents.children()[1:]: # a.hide() # if a.isChecked(): # print(self.movie_name_json[a.objectName()]) if key == Qt.Key_Return: print('Return Key pressed') # print(obj.childern()) elif key == Qt.Key_Delete: print('Delete Key Pressed') return QtCore.QObject.event(obj, event) def movieButtonContextMenu(self, id): contextMenu = QMenu(self) open_location = contextMenu.addAction("Open Folder Location") search = contextMenu.addAction("Search in Google") delete = contextMenu.addAction("Delete") # print(self.mapToGlobal(point)) action = contextMenu.exec_(QCursor.pos()) if action == search: webbrowser.open('https://google.com/search?q=' + urllib.parse.quote(self.data[id]['title'])) elif action == open_location: subprocess.Popen(r'explorer /select, ' + self.movies[id]) elif action == delete: pass def getMovieNamesList(self): movie = Movie() popular = movie.popular() # # lst = [(p.title, p.release_date[:4]) for p in popular] self.movies = {} for p in popular: # det = movie.details(p.id, append_to_response='append_to_response=videos') # print(p.id, p.title, p.release_date[:4], det.imdb_id) self.movies[str(p.id)] = p.title self.popular_movies.append(str(p.id)) return self.movies
class Maps(QWidget): def __init__(self, settings): # Settings self.settings = settings # Class Init self._active = False self.map_pairs = MapData(None).map_pairs # UI Init super(QWidget, self).__init__() self.setWindowTitle("Map") self.setWindowIcon(QIcon('ui/icon.png')) self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint | Qt.FramelessWindowHint | Qt.WindowMinMaxButtonsHint) self.setFocusPolicy(Qt.StrongFocus) if self.settings.get_value('maps', 'geometry') is not None: self.setGeometry( self.settings.get_value('maps', 'geometry') ) else: self.setGeometry(100, 100, 400, 400) self.grid = QGridLayout() self.grid.setSpacing(0) self.grid.setContentsMargins(0, 0, 0, 0) self.map_canvas = MapCanvas() self.grid.addWidget(self.map_canvas, 1, 0, 1, 1) self.setLayout(self.grid) self.setAttribute(Qt.WA_ShowWithoutActivating) with open('maps/style/qss.css') as css_file: self.setStyleSheet(css_file.read().replace('\n', '')) # self.setWindowOpacity(0.85) # Menu self.menu_container = QWidget() self.menu_container.setObjectName('menu_container') self.menu = QHBoxLayout() self.menu_container.setLayout(self.menu) self.menu_container.hide() # Start with menu hidden self.menu.setSpacing(5) self.menu.setContentsMargins(0, 0, 0, 0) # Map Buttons self.create_menu_buttons() # Add menu to window self.grid.addWidget(self.menu_container, 0, 0, 1, 1, Qt.AlignLeft) # Events self._filter = Filter(self) self.map_canvas.installEventFilter(self._filter) for child in self.menu_container.children(): child.installEventFilter(self._filter) # Testing self.toggle() self.map_canvas.load_map('The Overthere') def create_menu_buttons(self): # Center on Player Button button_center_player = QPushButton( QIcon('maps/graphics/map_menu_center_player.png'), '' ) button_center_player.setCheckable(True) # Make toggle button button_center_player.setToolTip('Center on Player') button_center_player.setObjectName('button_center_player') # Center on Last Point Button button_center_point = QPushButton( QIcon('maps/graphics/map_menu_center_map.png'), '' ) button_center_point.setCheckable(True) # Make toggle button button_center_point.setToolTip('Center Normally') button_center_point.setObjectName('button_center_point') # Create Button Group for Exclusive Toggling toggle_group = QButtonGroup(self) toggle_group.addButton(button_center_player, 1) toggle_group.addButton(button_center_point, 2) toggle_group.buttonClicked.connect(self.center_button_toggled) self.menu.addWidget(button_center_player) self.menu.addWidget(button_center_point) # Apply settings for current toggle if self.settings.get_value('maps', 'center_on') == 'point': button_center_point.setChecked(True) else: button_center_player.setChecked(True) # Fit to Window Button button_fit_window = QPushButton( QIcon('maps/graphics/map_menu_fit_window.png'), '' ) button_fit_window.setToolTip('Fit Map to Window') button_fit_window.clicked.connect(self.fit_to_window) self.menu.addWidget(button_fit_window, 0, Qt.AlignLeft) # Maps Combo Box self.combo_load_map = QComboBox(self) # Need to setView otherwise CSS doesn't work self.combo_load_map.setView(QListView()) self.combo_load_map.setToolTip('Manually Load Selected Map') for map_name in sorted(self.map_pairs.keys(), key=str.lower): self.combo_load_map.addItem(map_name) self.combo_load_map.currentIndexChanged.connect( self.load_map_from_combo ) self.menu.addWidget(self.combo_load_map, 0, Qt.AlignLeft) def load_map_from_combo(self, widget): self.map_canvas.load_map( self.combo_load_map.currentText().strip() ) self.fit_to_window(False) def fit_to_window(self, button): # Can't use QGraphicsView().fitInView because of scaling issues. # So calculate scale and use the lesser numeric scale to fit window # Use 0.9 against width/height to create a 10% border around map x_scale = self.map_canvas.geometry().width() * 0.9 \ / self.map_canvas.map_data.map_grid_geometry.width y_scale = self.map_canvas.geometry().height() * 0.9 \ / self.map_canvas.map_data.map_grid_geometry.height if x_scale <= y_scale: self.map_canvas.set_scale(x_scale) else: self.map_canvas.set_scale(y_scale) # Center Map on middle of map self.map_canvas.centerOn( self.map_canvas.map_data.map_grid_geometry.center_x, self.map_canvas.map_data.map_grid_geometry.center_y ) def center_button_toggled(self, button): if button.objectName() == 'button_center_point': self.settings.set_value('maps', 'center_on', 'point') else: self.settings.set_value('maps', 'center_on', 'player') if '__you__' in self.map_canvas.players.keys(): player = self.map_canvas.players['__you__'] self.map_canvas.centerOn( player.x, player.y ) def closeEvent(self, event): event.ignore() self.settings.set_value('maps', 'geometry', self.geometry()) self._active = False self.hide() def hide_menu(self): if not self.isActiveWindow(): self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) self.show() self.menu_container.hide() def show_menu(self): self.setWindowFlags(self.windowFlags() & ~Qt.FramelessWindowHint) self.show() self.menu_container.show() self.activateWindow() def is_active(self): return self._active def toggle(self): if self.is_active(): self._active = False self.hide() else: self._active = True self.show() def parse(self, item): if item[27:50] == "LOADING, PLEASE WAIT...": self.map_canvas.draw_loading_screen() if item[27:43] == 'You have entered': self.map_canvas.load_map(item[44:-2]) if item[27:43] == 'Your Location is': try: self.map_canvas.add_player('__you__', item[:27], item[43:]) self.map_canvas.update_players() except Exception as e: print(e)
class ParameterizedQueryDialog(QDialog): def __init__(self, name, parameters=None, connections=None): QDialog.__init__(self) self.result = [] self.setWindowTitle("Define query parameters") self.setWindowIcon(QIcon(":\plugins\GeODinQGIS\icons\transparent.png")) self.buttonBox = QDialogButtonBox(self) self.buttonBox.setOrientation(Qt.Horizontal) self.buttonBox.setStandardButtons(QDialogButtonBox.Cancel | QDialogButtonBox.NoButton | QDialogButtonBox.Ok) self.buttonBox.button(QDialogButtonBox.Ok).clicked.connect(self.ok) self.buttonBox.button(QDialogButtonBox.Cancel).clicked.connect( self.cancel) layout = QVBoxLayout(self) l1 = QLabel("Parameters in red are required entries.") l1.setStyleSheet('color: red') l2 = QLabel( "Parameters in blue are optional (empty entry fields are not evaluated in the query)." ) l2.setStyleSheet('color: blue') self.w = QWidget() self.w.setAutoFillBackground(True) p = self.w.palette() p.setColor(self.w.backgroundRole(), QColor(255, 221, 161)) self.w.setPalette(p) tableLayout = QGridLayout(self.w) j = 0 for i, parameter in enumerate(parameters): label = QLabel(parameter[0]) le1 = QLineEdit(parameter[1]) le1.setFixedSize(50, 25) le2 = QLineEdit() le2.setFixedSize(250, 25) tableLayout.addWidget(QLabel(connections[i]), i + j, 0) tableLayout.addWidget(label, i + j + 1, 1) tableLayout.addWidget(le1, i + j + 1, 2) tableLayout.addWidget(le2, i + j + 1, 3) j += 1 layout.addWidget( QLabel("Enter the required values of the query '" + name + "'")) layout.addWidget(QLabel("")) #layout.addWidget(l1) #layout.addWidget(l2) layout.addWidget( QLabel("This operators are available: < <= = >= > <> like")) layout.addWidget(self.w) layout.addWidget(self.buttonBox) self.exec_() def ok(self): for i in range(1, len(self.w.children()), 4): if not self.w.children()[i + 3].text().isdigit(): self.w.children()[i + 3].setText("'" + self.w.children()[i + 3].text() + "'") self.result.append([ self.w.children()[i + 1].text(), self.w.children()[i + 2].text(), self.w.children()[i + 3].text() ]) self.accept() def cancel(self): self.close()
class ConfigUI(QWidget): configFilePath = "" parseConfigSignal = pyqtSignal(etree._Element) selectResGroupSignal = pyqtSignal(list) def __init__(self, parent=None): super(ConfigUI, self).__init__(parent) grid = QGridLayout() self.setLayout(grid) ## grid.addWidget(QLabel("配置文件:"), 0, 0) grid.addWidget(QLabel("资源分组:"), 1, 0) grid.addWidget(QLabel("数据编码:"), 2, 0) ## self.configFileLE = QLineEdit() # self.configFileLE.setEnabled(False) self.configFileLE.setFocusPolicy(Qt.NoFocus) grid.addWidget(self.configFileLE, 0, 1) browsePB = QPushButton("浏览") browsePB.clicked.connect(self.browse_config_path) grid.addWidget(browsePB, 0, 2) ## self.resGroupWidget = QWidget() self.resGroupLayout = QHBoxLayout() self.resGroupWidget.setLayout(self.resGroupLayout) grid.addWidget(self.resGroupWidget, 1, 1) selectPB = QPushButton("选择") selectPB.clicked.connect(self.select_res_group) grid.addWidget(selectPB, 1, 2) # def create_config def browse_config_path(self): open = QFileDialog() # self.configFilePath = open.getOpenFileUrl(None, "选择转换列表文件") self.configFilePath = open.getOpenFileName(None, "选择转换列表文件", "./") self.configFileLE.setText(self.configFilePath[0]) if self.configFilePath[0] != "": list = etree.parse(self.configFilePath[0]) root = list.getroot() for item in root: if item.tag == "ConvTree": # 转换树 self.parseConfigSignal.emit(item) elif item.tag == "ResStyleList": # 资源分组 self.parse_res_group(item) pass def select_res_group(self): groups = self.resGroupWidget.children() if len(groups) > 0: resGroupArr = [] for item in groups: if isinstance(item, QCheckBox): if item.isChecked(): resGroupArr.append(int(item.text().split(" ")[1])) self.selectResGroupSignal.emit(resGroupArr) def parse_res_group(self, item): while self.resGroupLayout.count(): self.resGroupLayout.takeAt(0) for node in item: if node.tag != "ResStyle": continue print(node.attrib["Name"]) checkBox = QCheckBox(node.attrib["Name"] + " " + str(node.attrib["ID"])) self.resGroupLayout.addWidget(checkBox) self.resGroupLayout.addStretch()
class Visual(QMainWindow): def __init__(self, render_frequency: float, window_zoom: int = 0.7): super().__init__() self._screen_size = QApplication.primaryScreen().size() self._central_widget = QWidget(self) self._central_widget.setAttribute(Qt.WA_AcceptTouchEvents | Qt.WA_TranslucentBackground) self._central_widget.setWindowFlags(Qt.NoDropShadowWindowHint) self._central_widget.resize(self._screen_size) self._vtk_widget = QVTKRenderWindowInteractor(self._central_widget) self._vtk_widget.SetInteractorStyle( vtk.vtkInteractorStyleMultiTouchCamera()) self._vtk_widget.resize(self._screen_size) self.setCentralWidget(self._central_widget) sw, sh = self._screen_size.width(), self._screen_size.height() self.resize(sw * window_zoom, sh * window_zoom) self.move(sw * (1 - window_zoom) / 2, sh * (1 - window_zoom) / 2) self._central_widget.setFocus() # UI # self._ui = Ui_Form() # self._ui.setupUi(Form=self._central_widget) for widget in self._central_widget.children(): widget.setStyleSheet(f""" QGroupBox {{ background-color: rgba(200, 200, 200, 1); border-radius: 5; }}""") # QT_STYLE self.setWindowFlags(Qt.NoDropShadowWindowHint) self._render = vtk.vtkRenderer() self._ren_win = self._vtk_widget.GetRenderWindow() self._ren_win.AddRenderer(self._render) self._interactor = self._ren_win.GetInteractor() # TIMER self._timer = QTimer() self._timer.setInterval(int(1000 / render_frequency)) self._timer.timeout.connect(self._working) self._is_running = False self.set_render_ena(False) def _working(self): if self._is_running: self._interactor.Render() def set_render_ena(self, ena: bool): if ena != self._is_running: self._is_running = bool(ena) if ena: self._timer.start() else: self._timer.stop() def add_actor(self, actor: Union[vtk.vtkProp3D, vtk.vtkOpenGLTextActor, vtk.vtkAxesActor, vtk.vtkScalarBarActor, vtk.vtkOpenGLActor, list]): if actor.__class__ in [ vtk.vtkProp3D, vtk.vtkOpenGLTextActor, vtk.vtkAxesActor, vtk.vtkAssembly, vtk.vtkOpenGLActor ]: self._render.AddActor(actor) elif actor.__class__ in [vtk.vtkScalarBarActor]: self._render.AddActor2D(actor) elif actor.__class__ == list: for this_actor in actor: self._render.AddActor(this_actor) def del_actor(self, actor: vtk.vtkProp3D): self._render.RemoveActor(actor) def render_once(self): self._interactor.Render() def get_ui_form(self) -> Ui_Form: return self._ui def show(self): self._vtk_widget.GetRenderWindow().GetInteractor().Initialize() self._vtk_widget.GetRenderWindow().GetInteractor().Start() super().show() def exit(self): self._interactor.DestroyTimer() self._timer.stop() self.close()
class ChatBoxTemplate(QWidget): def __init__(self, servcon, chat_heading): super().__init__() PATH_ONE = r"./production/view/chat_box_template.ui" PATH_TWO = r"" try: file = check_for_file(PATH_ONE, PATH_TWO) uic.loadUi(file, self) except Exception as e: print(e) self.servcon = servcon self.chat_heading.setText(chat_heading) self.send.clicked.connect(self.send_message) # self.bt.clicked.connect(self.recieve_message) self.fbor = QWidget() self.vbox = QVBoxLayout() self.fbor.setLayout(self.vbox) self.vbox.addStretch() self.chat_message_list.setWidget(self.fbor) def resizeEvent(self, event): km = iter(self.fbor.children()) next(km) for i in km: if isinstance(i, MessageTextTemplate): wid = self.set_message_size(i) # i.self_evaluate() elif isinstance(i, MessageTextRecieveTemplate): wid = self.set_message_receive_size(i) super().resizeEvent(event) def set_message_size(self, msg_widget): horizontal_spacer = msg_widget.children()[0].itemAt(0) msg_text = msg_widget.message_text msg_widget_width = msg_text.fontMetrics().boundingRect(msg_text.text()).width() left_bound = self.width() * 0.5 if msg_widget_width < left_bound: msg_text.setWordWrap(False) horizontal_spacer.changeSize( 200, 20, QSizePolicy.MinimumExpanding, QSizePolicy.Minimum ) else: msg_text.setWordWrap(True) horizontal_spacer.changeSize( 200, 20, QSizePolicy.Fixed, QSizePolicy.Minimum ) if msg_widget.message_text.width() >= msg_widget_width: msg_text.setWordWrap(False) horizontal_spacer.changeSize( 200, 20, QSizePolicy.MinimumExpanding, QSizePolicy.Minimum ) if msg_widget.width() > self.width(): msg_text.setWordWrap(True) horizontal_spacer.changeSize( 200, 20, QSizePolicy.Fixed, QSizePolicy.Minimum ) horizontal_spacer.invalidate() msg_text.updateGeometry() def set_message_receive_size(self, msg_widget): horizontal_spacer = msg_widget.children()[0].itemAt(1) msg_text = msg_widget.message_text msg_widget_width = msg_text.fontMetrics().boundingRect(msg_text.text()).width() left_bound = self.width() * 0.5 if msg_widget_width < left_bound: msg_text.setWordWrap(False) horizontal_spacer.changeSize( 200, 20, QSizePolicy.MinimumExpanding, QSizePolicy.Minimum ) else: msg_text.setWordWrap(True) horizontal_spacer.changeSize( 200, 20, QSizePolicy.Fixed, QSizePolicy.Minimum ) if msg_widget.message_text.width() >= msg_widget_width: msg_text.setWordWrap(False) horizontal_spacer.changeSize( 200, 20, QSizePolicy.MinimumExpanding, QSizePolicy.Minimum ) if msg_widget.width() > self.width(): msg_text.setWordWrap(True) horizontal_spacer.changeSize( 200, 20, QSizePolicy.Fixed, QSizePolicy.Minimum ) horizontal_spacer.invalidate() msg_text.updateGeometry() def send_message(self): msg_text = self.input_field.text() self.load_send_message(msg_text) self.servcon.send_some(msg_text, self.chat_heading.text()) def load_send_message(self, msg_text): m = MessageTextTemplate() m.message_text.setText(msg_text) self.set_message_size(m) self.vbox.addWidget(m) self.scroll_to_last() def scroll_to_last(self): scroll_bar = self.chat_message_list.verticalScrollBar() scroll_bar.setMaximum(self.fbor.height()) scroll_bar.setValue(self.fbor.height()) def load_recieve_message(self, msg_text): m = MessageTextRecieveTemplate() m.message_text.setText(msg_text) self.set_message_receive_size(m) self.vbox.addWidget(m) self.scroll_to_last()
class ClientWindow(QMainWindow): """ This is the game's main window. It abstracts the single elements of the window into single methods one can call and use. """ return_pressed = pyqtSignal() def __init__(self): QMainWindow.__init__(self) self.command_stack = [] self.setObjectName("client_window") self.setWindowModality(Qt.NonModal) self.resize(800, 600) self.setDocumentMode(False) self.raw_title = Core.get_res_man().get_string("core.windowTitle") self.setWindowTitle(self.raw_title) central_widget = QWidget(self) central_widget.setObjectName("central_widget") self.setCentralWidget(central_widget) vertical_layout = QVBoxLayout(central_widget) vertical_layout.setObjectName("vertical_layout") central_widget.setLayout(vertical_layout) self.text_area = QTextEdit(central_widget) self.text_area.setReadOnly(True) self.text_area.setObjectName("text_area") vertical_layout.addWidget(self.text_area) self.command_row = QWidget(central_widget) self.command_row.setObjectName("command_row") vertical_layout.addWidget(self.command_row) command_row_layout = QHBoxLayout(self.command_row) command_row_layout.setObjectName("command_row_layout") command_row_layout.setContentsMargins(0, 0, 0, 0) command_row_layout.setSpacing(0) self.command_row.setLayout(command_row_layout) self.add_command_line() menu_bar = QMenuBar(self) menu_bar.setObjectName("menubar") self.setMenuBar(menu_bar) def get_raw_title(self): """ This constant method returns our raw window title. """ return self.raw_title def get_command_stack(self): """ This constant method returns a stack with all commands we ran. """ return self.command_stack def stack_command(self, command_text): """ This non-constant method puts another command on our command stack. """ self.command_stack.append(command_text) def get_text_area(self): """ This constant method returns our text area widget. """ return self.text_area def get_command_text(self, show_command=False, clear_prompt=False): """ This non-constant method Returns the text of the command prompt widget. If show_command is True, it will also show the entered text in the text area and if clear_prompt is True, it will also add the command to our command stack and clear the prompt. """ if self.command_line is not None: text = self.command_line.text() if show_command and len(text) > 0: self.show_command(text) if clear_prompt: self.stack_command(text) self.command_line.clear() return text else: return str() def scroll_text_area_down(self): """ This non-constant method scrolls our text area down until the end of our text is visible. """ self.text_area.ensureCursorVisible() def show_text(self, text, emplace_res_strings=True, add_html_tags=True): """ Thi non-constant method prints the given text in the text area as it's own paragraph. If emplace_res_strings is True (default), it will also decode resource string keys in it and if add_html_tags is True (default), it will add tags to the text that indicate that it should be handled as an HTML snippet. """ self.text_area.setTextColor(QColor(0, 0, 0)) if emplace_res_strings: text = Core.get_res_man().decode_string(text) if add_html_tags: text = '<html><body>' + text + '</body></html>' self.text_area.append(text) self.text_area.show() self.scroll_text_area_down() def show_command(self, text): """ This non-constant method shows the given text to the user, but displays it in a grey color and adds a "> " to show that the given text is a command or something else the user said or did. """ self.text_area.setTextColor(QColor(125, 125, 125)) self.text_area.append("> " + text) def clear_command_row(self): """ This non-constant method removes all widgets from our command row. """ while self.command_row.layout().count() > 0: self.command_row.layout().takeAt(0).widget().setParent(None) self.scroll_text_area_down() def add_command_line(self): """ This non-constant method adds a command line to our command row. """ self.command_line = CommandLine(self.command_row) child_number = len(self.command_row.children()) self.command_line.setObjectName("command_line_" + str(child_number)) self.command_row.layout().addWidget(self.command_line) self.command_line.returnPressed.connect(self.return_pressed) self.command_line.setFocus(Qt.ActiveWindowFocusReason) self.command_line.show() self.scroll_text_area_down() def add_option_button(self, text): """ This non-constant method adds an option button with the given text to the command row and returns it. The text may contain unresolved resource string keys as they will be resolved inside this method. """ text = Core.get_res_man().decode_string(text) button = QPushButton(text, self.command_row) child_number = len(self.command_row.children()) button.setObjectName("option_button_" + str(child_number)) self.command_row.layout().addWidget(button) self.scroll_text_area_down() return button def closeEvent(self, event): """ This overriden non-constant method gets called when the window is closed and saves the game. """ QCoreApplication.instance().save_world() event.accept()