def paintColorDots(self, painter, rect): pen = QtGui.QPen(Qt.gray) pen.setStyle(Qt.SolidLine) painter.setPen(pen) scaled_pts = [] h = rect.height() for point in self.points: x, y, r, g, b = point x = rect.left() + rect.width() * self.dataToX(x) y = rect.top() + rect.height() * (1.0 - self.alphaToY(y)) if scaled_pts: painter.drawLine(scaled_pts[-1][0], scaled_pts[-1][1], x, y) scaled_pts.append((x, y, r, g, b)) for x, y, r, g, b in scaled_pts: painter.setBrush(QtGui.QColor(255 * r, 255 * g, 255 * b)) painter.drawEllipse(x - DOT_RADIUS, y - DOT_RADIUS, 2 * DOT_RADIUS, 2 * DOT_RADIUS) if 0 <= self.hover_point < len(scaled_pts): # use larger radius for hover dot radius = DOT_RADIUS + 2 x, y, r, g, b = scaled_pts[self.hover_point] painter.setBrush(QtGui.QColor(255 * r, 255 * g, 255 * b)) painter.drawEllipse(x - radius, y - radius, 2 * radius, 2 * radius)
def set_starting_status_slot(self, component, size_download): """It responds to the established_connection signal emitted by the thread. The signal carries the file size, that will be displayed in the window.""" # gets the graphic object that displays the status, from a dictionary previously created status_item_object = self.items_status_dict[component.name]# [self.status_col_idx] status_item_object.setText('Available: '+str(int(size_download/1048576))+' MB') status_item_object.setForeground(QtGui.QBrush(QtGui.QColor(204, 255, 204)))
def paintHistogram(self, painter, rect): if self.path: vrange = self.original_vmax - self.original_vmin if vrange == 0.0: return norm_min = (self.vmin - self.original_vmin) / vrange norm_max = (self.vmax - self.original_vmin) / vrange h = rect.height() - 2 dnorm = norm_max - norm_min iwidth = 1.0 / (rect.width()) painter_path = QtGui.QPainterPath() for i in xrange(rect.width()): pos = (i * iwidth * dnorm + norm_min) * len(self.path) ipos = int(pos) if ipos < 0 or ipos >= len(self.path) - 1: continue y0 = self.path[ipos][1] y1 = self.path[ipos + 1][1] y = y0 + (y1 - y0) * (pos - int(pos)) # lerp x = rect.left() + i y = h - self.alphaToY(y) * h + 1 if painter_path.elementCount() == 0: painter_path.moveTo(x, y) else: painter_path.lineTo(x, y) pen = QtGui.QPen(Qt.red) pen.setStyle(Qt.SolidLine) painter.setPen(pen) painter.drawPath(painter_path)
def getIcons(self): self.icons = {} # use old Tk icons imgDir = os.path.join(os.environ['PYMOL_DATA'], "pmg_tk/bitmaps/builder") imgList = glob("%s/aro*.gif" % imgDir) + glob("%s/cyc*.gif" % imgDir) for imgFile in imgList: imgName = os.path.splitext(os.path.split(imgFile)[1])[0] if imgName not in list(self.icons.keys()): image = QtGui.QImage(imgFile) pixmap = QtGui.QPixmap.fromImage(image) image.invertPixels() inv_pixmap = QtGui.QPixmap.fromImage(image) self.icons[imgName] = (QtGui.QIcon(pixmap), QtGui.QIcon(inv_pixmap))
def textformat(color, style=''): fmt = QtGui.QTextCharFormat() fmt.setForeground(QtGui.QColor(color)) for word in style.split(): if word == 'bold': fmt.setFontWeight(QtGui.QFont.Bold) elif word == 'italic': fmt.setFontItalic(True) elif word.startswith('bg:'): fmt.setBackground(QtGui.QColor(word[3:])) else: print('unhandled style:', word) return fmt
def set_pmw_validator(self, validator): self.pmw_validator = validator if not self.pmw_validator: return None if self.pmw_validator["validator"] == "integer": self.setValidator( QtGui.QIntValidator(self.pmw_validator["min"], self.pmw_validator["max"])) elif self.pmw_validator["validator"] == "real": self.setValidator( QtGui.QDoubleValidator(self.pmw_validator["min"], self.pmw_validator["max"], 9)) else: raise KeyError("Unknown 'validator': %s" % self.pmw_validator["validator"])
def _fill_list(self): """ Create the list's data. """ self.items_dict = {} # Checks the database log in order to obtain the date when each database was last downloaded. download_log_dict = {} if os.path.isfile(self.installer_protocol.download_log_filepath): with open(self.installer_protocol.download_log_filepath, "r") as l_fh: download_log_dict = json.loads(l_fh.read()) # Configure the list of items is the 'all_components_list' from the PyMod Installer class. self.view.setRowCount(len(self.installer_protocol.components_list)) for row_counter, component in enumerate(self.installer_protocol.components_list): # Create an item and set the component name as text. item = QtWidgets.QTableWidgetItem(component.full_name) # add a checkbox to the name item.setFlags(QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled) item.setCheckState(QtCore.Qt.Unchecked) # place the items in columns self.view.setItem(row_counter, self.component_col_idx, item) self.items_dict.update({component: item}) # Set the names of the databases. self.view.setItem(row_counter, self.databases_col_idx, QtWidgets.QTableWidgetItem(component.databases_string)) # Create another item displaying the status. graphic_status = 'Wait...' status = QtWidgets.QTableWidgetItem(graphic_status) status.setForeground(QtGui.QBrush(QtGui.QColor(191, 191, 191))) self.view.setItem(row_counter, self.status_col_idx, status) # Set the source URL. self.view.setItem(row_counter, self.source_col_idx, QtWidgets.QTableWidgetItem(component.remote_source)) # Fill in the last downloaded column. if component.name in download_log_dict: last_downloaded_str = download_log_dict[component.name] else: last_downloaded_str = "Never" last_downloaded_item = QtWidgets.QTableWidgetItem(last_downloaded_str) self.view.setItem(row_counter, self.last_download_col_idx, last_downloaded_item) self.items_status_dict[component.name] = status self.items_last_download_dict[component.name] = last_downloaded_item
def setup_behavior(self): # init input fields self.form.input_model.addItems(get_object_names(self.cmd)) self.form.input_state.setValue(self.cmd.get_state()) # select pk1 atom if available self.update_from_pk1() # Install an eventfilter self.form.treeWidget.installEventFilter(self) # hook up events self.form.input_model.currentIndexChanged.connect( self.update_treewidget_model) self.form.input_state.valueChanged.connect( self.update_treewidget_state) self.form.input_index.valueChanged.connect(self.update_treewidget) self.form.button_refresh.clicked.connect(self.update_model_list) # themed icons only available by default on X11 if self.form.button_refresh.icon().isNull(): self.form.button_refresh.setIcon( QtGui.QIcon( os.path.expandvars( '$PYMOL_DATA/pmg_qt/icons/refresh.svg'))) # update and show self.update_treewidget_model() self.form.treeWidget.setColumnWidth(0, 200) self.form.treeWidget.itemChanged.connect(self.item_changed)
def __init__(self, parent, cmd): QtWidgets.QWidget.__init__(self, parent, Qt.Window) self.setMinimumSize(400, 500) self.cmd = cmd self.model = QtGui.QStandardItemModel(self) self.proxy_model = QtCoreModels.QSortFilterProxyModel(self) self.proxy_model.setSourceModel(self.model) self.setWindowTitle('PyMOL Advanced Settings') layout = QtWidgets.QVBoxLayout(self) self.setLayout(layout) self.filter_le = QtWidgets.QLineEdit(self) layout.addWidget(self.filter_le) self.filter_le.setPlaceholderText("Filter") self.filter_le.textChanged.connect(self.proxy_model.setFilterRegExp) self.populateData() self.table = QtWidgets.QTableView(self) self.table.setModel(self.proxy_model) layout.addWidget(self.table) self.formatTable() self.model.itemChanged.connect(self.itemChanged)
def __init__(self, parent): self.gui = parent self.fb_scale = 1.0 # OpenGL context setup if USE_QOPENGLWIDGET: f = QtGui.QSurfaceFormat() else: f = QtOpenGL.QGLFormat() from pymol.invocation import options # logic equivalent to layer5/main.cpp:launch if options.multisample: f.setSamples(4) if options.force_stereo != -1: # See layer1/Setting.h for stereo modes if options.stereo_mode in (1, 12) or ( options.stereo_mode == 0 and AUTO_DETECT_STEREO): f.setStereo(True) if options.stereo_mode in (11, 12) and not USE_QOPENGLWIDGET: f.setAccum(True) if USE_QOPENGLWIDGET: super(PyMOLGLWidget, self).__init__(parent=parent) self.setFormat(f) self.setUpdateBehavior(QtWidgets.QOpenGLWidget.PartialUpdate) else: super(PyMOLGLWidget, self).__init__(f, parent=parent) # pymol instance self.pymol = PyMOL() self.pymol.start() self.cmd = self.pymol.cmd # capture python output for feedback import pcatch pcatch._install() # for passive move drag self.setMouseTracking(True) # for accepting keyboard input (command line, shortcuts) self.setFocusPolicy(Qt.ClickFocus) # for idle rendering self._timer = QtCore.QTimer() self._timer.setSingleShot(True) self._timer.timeout.connect(self._pymolProcess) # drag n drop self.setAcceptDrops(True) # pinch-zoom self.grabGesture(Qt.PinchGesture)
def update_treeview(): model.clear() for key in manager.get_keys(): item = QtGui.QStandardItem() item.setText(key) model.blockSignals(True) model.appendRow(item) model.blockSignals(False)
def create_little_canvas(self, hit, domain_hit, default_width=300, default_height=11): # Builds the graphics view and scene. canvas_plot_scene = QtWidgets.QGraphicsScene() canvas_plot_view = QtWidgets.QGraphicsView(canvas_plot_scene) canvas_plot_view.setFixedHeight(default_height) canvas_plot_view.setFixedWidth(default_width) canvas_plot_view.setSizePolicy(self.preferred_size_policy) canvas_plot_view.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) canvas_plot_view.setVerticalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) canvas_plot_view.setFrameShape(QtWidgets.QFrame.NoFrame) canvas_plot_view.setStyleSheet("border: 0px; background: %s" % self.view_bg_color) # Get the coordinates of the various graphics elements. one_res_span = default_width / float( self.query_len ) # proporzione tra la lunghezza della seq e lo spazio grafico queryspan_start = int(domain_hit['start']) queryspan_end = int(domain_hit['end']) queryspan_start_graphic = int(queryspan_start * one_res_span) queryspan_end_graphic = int(queryspan_end * one_res_span) # canvas_true_width = int(queryspan_end_graphic-queryspan_start_graphic) # space_at_end = int(int(default_width)-(canvas_true_width+queryspan_start_graphic)) # Draws a gray rectangle representing the full protein sequence. canvas_plot_scene.addRect(0, 3, default_width, 5, self.full_seq_pen, self.full_seq_brush) # Draws a colored rectangle representing the domain. line_pen = QtGui.QPen(QtGui.QColor(0, 0, 0, 255), 1) qcolor = QtGui.QColor(0, 0, 0) qcolor.setNamedColor(hit['dom_color_hex']) brush = QtGui.QBrush(qcolor) canvas_plot_scene.addRect( queryspan_start_graphic, 0, queryspan_end_graphic - queryspan_start_graphic, default_height, line_pen, brush) return canvas_plot_view
def make_pymol_qicon(): icons_dir = os.path.expandvars('$PYMOL_DATA/pymol/icons') icon = QtGui.QIcon() for size in (16, 32, 128): icon.addFile(os.path.join(icons_dir, 'icon2_%dx%d.png' % (size, size)), QtCore.QSize(size, size)) return icon
def get_available_fonts(self): # Get the system fonts. font_family_list = [font_name.lower() for font_name in QtGui.QFontDatabase().families()] # Get the fonts available in PyMod. selected_fonts = [font_name.lower() for font_name in self.selected_fonts_dict.get(sys.platform, [])] # Filter the system fonts list. font_family_list = [font_name for font_name in font_family_list if font_name in selected_fonts] font_family_list.insert(0, "courier new") font_family_list.sort() return font_family_list
def _get_scene_png(self, scene_name): ''' Returns a QPixmap object that can be assigned to a label in order to display the scene thumbnail. This will be obsolete and removed in the future. ''' scene_pix_map = QtGui.QPixmap() png_buf = self.cmd.get_scene_thumbnail(scene_name) scene_pix_map.loadFromData(png_buf, "PNG") return scene_pix_map
def set_update_status_slot(self, component, text="new status", color="light_green"): status_item_object = self.items_status_dict[component.name] status_item_object.setText(text) if color == "light_green": status_item_object.setForeground(QtGui.QBrush(QtGui.QColor(204, 255, 204))) elif color == "green": status_item_object.setForeground(QtGui.QBrush(QtGui.QColor(10, 255, 10))) elif color == "gray": status_item_object.setForeground(QtGui.QBrush(QtGui.QColor(200, 200, 200))) elif color == "red": status_item_object.setForeground(QtGui.QBrush(QtGui.QColor(255, 0, 0))) else: raise KeyError(color)
def paintValueBox(self, painter, font_metrics, x, y, right_just, value, format="%.3f"): s = format % value sw = font_metrics.width(s) sh = font_metrics.height() if right_just: rect = QtCore.QRect(x - sw - 4, y - sh, sw + 4, sh + 2) else: rect = QtCore.QRect(x, y - sh, sw + 4, sh + 2) painter.fillRect(rect, QtGui.QColor(96, 96, 128) if self.line_color == Qt.lightGray else QtGui.QColor(0xFF, 0xFF, 0xFF)) painter.drawRect(rect) painter.drawText(rect.x() + 2, y - 2, s) return rect
def add_domain_representation(self, hit, domain_hit): queryspan_start = int(domain_hit['start']) queryspan_end = int(domain_hit['end']) domain_x = self.x_init + int( queryspan_start / float(self.query_len) * self.full_seq_rect_w) domain_y = self.domain_y_init domain_w = int((queryspan_end - queryspan_start) / float(self.query_len) * self.full_seq_rect_w) domain_h = 25 domain_pen = QtGui.QPen(QtGui.QColor(0, 0, 0, 255), 1) qcolor = QtGui.QColor(0, 0, 0) qcolor.setNamedColor(hit['dom_color_hex']) domain_brush = QtGui.QBrush(qcolor) domain_rect = self.canvas_plot_scene.addRect(domain_x, domain_y, domain_w, domain_h, domain_pen, domain_brush) domain_rect.setVisible(False) return domain_rect
def add_text_to_canvas(x, y, text, anchor=None, font_color="black", font_size=6, html_font_size=10): _text = str(text) text_item = self.canvas_plot_scene.addText(_text) w = text_item.boundingRect().width() h = text_item.boundingRect().height() text_item.setPos(int(x - w / 2.5), int(y - h / 2.5)) text_item.setFont(QtGui.QFont(text_item.font().family(), font_size))
def paintAxes(self, painter, rect): low = int(math.ceil(self.vmin)) hi = int(math.floor(self.vmax)) + 1 pen = painter.pen() pen.setStyle(Qt.SolidLine) pen.setColor(self.line_color) painter.setPen(pen) fm = QtGui.QFontMetrics(painter.font()) fw = fm.averageCharWidth() fh = fm.height() - 2 x0 = rect.left() x1 = rect.right() y0 = rect.bottom() y1 = y0 + 4 lastx = x0 # horizontal axis for tick in range(low, hi): s = str(tick) w = fw * len(s) x = x0 + w / 2 + rect.width() * ( tick - self.vmin) / float(self.vmax - self.vmin) if x - lastx > w + 2 * fw: painter.drawLine(x, y0, x, y1) painter.drawText(x - w / 2, y1 + fh - 2, s) lastx = x #vertical axis x1 = rect.left() lasty = y0 for tick in range(1, 10): t = tick / 10.0 y = y0 - self.alphaToY(t) * rect.height() if lasty - y > fh and y > 2 * fh: painter.drawLine(x1 - 5, y, x1, y) painter.drawText(x1 - 5 - 3 * fw, y - 2 + fh / 2, str(t)) lasty = y # text boxes self.text_boxes["vmin"] = self.paintValueBox(painter, fm, rect.left(), y1 + fh, False, self.vmin) self.text_boxes["vmax"] = self.paintValueBox(painter, fm, rect.right(), y1 + fh, True, self.vmax) self.text_boxes["amax"] = self.paintValueBox(painter, fm, x0 - 4 * fw, 2 + fh, False, self.amax, format="%.2f")
def __init__(self, parent=None, filename='', title='Text Editor'): super(TextEditor, self).__init__() self.highlight = None self.root = self self.root.setWindowTitle(title) menubar = self.root.menuBar() filemenu = menubar.addMenu("File") filemenu.addAction("Open", self.doOpen, QtGui.QKeySequence("Ctrl+O")) filemenu.addAction("Save", self.doSave, QtGui.QKeySequence("Ctrl+S")) filemenu.addAction("Save as ...", self.doSaveAs, QtGui.QKeySequence("Ctrl+Shift+S")) syntaxmenu = menubar.addMenu("Syntax") syntaxgroup = QtWidgets.QActionGroup(self) self.syntaxactions = {} for label in ['Python', 'PML', 'Plain Text']: key = label.split()[0].lower() action = syntaxmenu.addAction(label, lambda t=key: self.setSyntax(t)) syntaxgroup.addAction(action) action.setCheckable(True) self.syntaxactions[key] = action self.text = QtWidgets.QPlainTextEdit() self.text.setFont(getMonospaceFont()) self.root.setCentralWidget(self.text) connectFontContextMenu(self.text) self._open(filename) self.root.show() self.root.raise_()
def _copy_image(_self=pymol.cmd, quiet=1, dpi=-1): import tempfile fname = tempfile.mktemp('.png') if not _self.png(fname, prior=1, dpi=dpi): print("no prior image") return try: qim = QtGui.QImage(fname) QtWidgets.QApplication.clipboard().setImage(qim) finally: os.unlink(fname) if not quiet: print(" Image copied to clipboard")
def setPointColor(self, point, triple): """ Opens color picker and sets color of one or three points. """ self.color_point = point self.color_triple = triple _, _, r, g, b = self.points[self.color_point] if not self.color_dialog: self.color_dialog = QtWidgets.QColorDialog(self) self.color_dialog.currentColorChanged.connect( self.updatePointColor) self.color_dialog.finished.connect(self.colorDialogClosed) self.original_color = QtGui.QColor(255 * r, 255 * g, 255 * b) self.color_dialog.setCurrentColor(self.original_color) # open modal color dialog self.color_dialog.open()
def paintGrid(self, painter, rect): pen = QtGui.QPen(self.line_color) painter.setPen(pen) x0 = rect.x() x1 = rect.x() + rect.width() y0 = rect.y() y1 = rect.y() + rect.height() painter.drawLine(x0, y1, x1, y1) painter.drawLine(x0, y0, x0, y1) h = rect.height() num_lines = 10 pen.setStyle(Qt.DashLine) painter.setPen(pen) for line in xrange(1, num_lines): y = y0 + h * (1.0 - self.alphaToY(line / float(num_lines))) painter.drawLine(x0, y, x1, y)
def __init__(self, i, j, tags, mark_size, parent_window, pen=None, brush=None): # Build the polygon object representing a triangle. with warnings.catch_warnings(): warnings.simplefilter("ignore") polygon = QtGui.QPolygonF([ QtCore.QPoint(i, j - 2 * mark_size), QtCore.QPoint(i - 1.7 * mark_size, j + mark_size), QtCore.QPoint(i + 1.7 * mark_size, j + mark_size) ]) # Initialize. super(Ramachandran_plot_triangle, self).__init__(polygon) self.common_initialization(i, j, tags, parent_window, pen, brush)
def paintEvent(self, event): """ Paints the editor widget. """ painter = QtGui.QPainter() self.paint_rect = event.rect() self.paint_rect.adjust(self.left_margin, 0, 0, -self.bottom_margin) # tweak color depening on the panel floating state # disabled: always use default style is_floating = True # self.parent().parent().isFloating() self.line_color = Qt.darkGray if is_floating else Qt.lightGray painter.begin(self) painter.setRenderHint(QtGui.QPainter.Antialiasing) self.paintGrid(painter, self.paint_rect) self.paintAxes(painter, self.paint_rect) painter.setClipRect(self.paint_rect) self.paintHistogram(painter, self.paint_rect) self.paintZoomArea(painter, self.paint_rect) painter.setClipping(False) self.paintColorDots(painter, self.paint_rect) painter.end()
def __init__(self, data, parent, sortable=False, column_labels=None, row_labels=None, row_labels_height=25, *args): self.data = data self.column_labels = column_labels self.row_labels = row_labels self.row_labels_height = row_labels_height # Get a set with the length of each column. rows_number = len(self.data) columns_number = len(self.data[0]) QtWidgets.QTableWidget.__init__(self, parent=parent, columnCount=columns_number, rowCount=rows_number, sortingEnabled=sortable, *args) self.set_data() self.resizeColumnsToContents() # self.resizeRowsToContents() self.setStyleSheet( "QTableView::item {border: 0px; padding: 0px; margin: 0px;}") # self.itemDoubleClicked.connect(self.on_click) default_font = QtGui.QFont() default_font.setPointSize(default_font.pointSize() - 1) self.setFont(default_font)
def paintZoomArea(self, painter, rect): if self.init_pos and self.zoom_pos: rect.setLeft(self.init_pos.x()) rect.setRight(self.zoom_pos.x()) painter.fillRect(rect, QtGui.QBrush(QtGui.QColor(0, 64, 128, 128)))
def build_plotting_area(self, use_controls=True, use_all_controls_buttons=True, messagebar_initial_text=None, update_messagebar=False, messagebar_text_on_update="", messagebar_vars_on_update=(), on_click_action=None, x_label_text="x", y_label_text="y", label_size=None, use_save_to_csv=True, hide_x_ticks=False, hide_y_ticks=False, # hide_x_label=False, # hide_y_label=False, highlight_points=True, ): """ Configures the plotting area of the window. # Arguments use_controls: adds to the plotting window a right column with checkbuttons to show/hide the lines plots in the drawing area. use_all_controls_buttons: adds to the plotting control column 'Show All' and 'Hide All' buttons. messagebar_initial_text: initial text to be displayed in the messagebar of the plotting window. update_messagebar: if 'True', the messagebar will be updated when clicking some point of the scatterplots. messagebar_text_on_update: text to be shown when the messagebar is update. If it contains the "__plot_name__", "__x__", "__y__" string, they will be substituted with the name of the plot, the x value and y value of the point respectively. If some 'additional_data' is provided for a plot, its data will also be used to update the messagebar. Each element of an 'additional_data' list has to correspond to a data point of a plot and must be a dictionary in which the keys are strings representing the names of the additional data series. If, for example, these dictionaries have a key named 'info' and if the 'messagebar_text_on_update' contains the string "__info__", it will be substituted with the value associated to the 'info' key of that 'additional_data' point. on_click_action: function to be called when a point of a scatterplot is clicked. This function must have the following argument: point_data: a dictionary with additional data for the point or a 'None' value. x_label_text: text for the x-axis label. y_label_text: text for the y-axis label. label_size: font size for the axis labels. use_save_to_csv: if 'True' add to the plotting window menu a command to save data in the csv format. hide_x_ticks: if 'True', hide the ticks and numbers of the x-axis. hide_y_ticks: if 'True', hide the ticks and numbers of the y-axis. highlight_points: if 'True', the scatterplot points will be highlighted when they are clicked with the mouse. """ # Stores the arguments. self.use_controls = use_controls if self.use_controls: self.upper_frame_layout.addWidget(self.info_frame, 0, 0) self.middle_splitter.addWidget(self.plot_frame) self.middle_splitter.addWidget(self.controls_scrollarea) self.upper_frame_layout.addWidget(self.middle_splitter, 1, 0) self.controls_scrollarea.resize(230, self.controls_scrollarea.sizeHint().height()) self.controls_frame_layout.addWidget(self.labels_title, 0, 0, 1, 2) self.lower_frame_layout.addWidget(self.on_click_label, 0, 2) self.lower_frame_layout.addWidget(self.interact_button, 0, 3) self.lower_frame_layout.addWidget(self.no_interaction_button, 0, 4) else: self.upper_frame_layout.addWidget(self.plot_frame, 0, 0) self.use_all_controls_buttons = use_all_controls_buttons if self.use_controls and self.use_all_controls_buttons: self.lower_frame_layout.addWidget(self.show_label, 0, 5) self.lower_frame_layout.addWidget(self.show_all_button, 0, 6) self.lower_frame_layout.addWidget(self.hide_all_button, 0, 7) self.messagebar_initial_text = messagebar_initial_text if self.messagebar_initial_text is not None: self.info_label.setText(self.messagebar_initial_text) self.update_messagebar = update_messagebar self.on_click_action = on_click_action if self.on_click_action is not None: if not hasattr(self.on_click_action, "__call__"): raise TypeError("'on_click_action' must be a function.") self.messagebar_text_on_update = messagebar_text_on_update self.messagebar_vars_on_update = messagebar_vars_on_update self.use_save_to_csv = use_save_to_csv if self.use_save_to_csv: self.file_menu.addAction(self.save_to_csv_action) self.file_menu.addAction(self.save_to_png_action) self.highlight_points = highlight_points # Configure the PlotWidget. self.graphWidget.setMenuEnabled(False) if label_size is not None: kwargs = {"font-size": "%spx" % label_size} font = QtGui.QFont() font.setPixelSize(label_size) # curve_pen = pyqtgraph.mkPen(width=2, color="r") # Color and width of the curve. else: kwargs = {} self.graphWidget.setLabel("left", y_label_text, **kwargs) self.graphWidget.setLabel("bottom", x_label_text, **kwargs) if label_size is not None: self.graphWidget.getAxis("left").tickFont = font self.graphWidget.getAxis("bottom").tickFont = font # self.graphWidget.getAxis("left").setPen(curve_pen) if hide_x_ticks: self.graphWidget.getAxis("bottom").setStyle(showValues=False, tickLength=0) if hide_y_ticks: self.graphWidget.getAxis("left").setStyle(showValues=False, tickLength=0) if self.on_click_action is not None: self.graphWidget.getViewBox().scene().sigMouseClicked.connect(self.on_scene_click)
def __init__(self): # noqa QtWidgets.QMainWindow.__init__(self) self.setDockOptions(QtWidgets.QMainWindow.AllowTabbedDocks | QtWidgets.QMainWindow.AllowNestedDocks) # resize Window before it is shown options = pymol.invocation.options self.resize(options.win_x + (220 if options.internal_gui else 0), options.win_y + (246 if options.external_gui else 18)) # for thread-safe viewport command self.viewportsignal.connect(self.pymolviewport) # reusable dialogs self.dialog_png = None self.advanced_settings_dialog = None self.props_dialog = None self.builder = None # setting index -> callable self.setting_callbacks = defaultdict(list) # "session_file" setting in window title self.setting_callbacks[440].append(lambda v: self.setWindowTitle( "PyMOL (" + os.path.basename(v) + ")")) # "External" Command Line and Loggin Widget self._setup_history() self.lineedit = CommandLineEdit() self.lineedit.setObjectName("command_line") self.browser = QtWidgets.QPlainTextEdit() self.browser.setObjectName("feedback_browser") self.browser.setReadOnly(True) # convenience: clicking into feedback browser gives focus to command # line. Drawback: Copying with CTRL+C doesn't work in feedback # browser -> clear focus proxy while text selected self.browser.setFocusProxy(self.lineedit) @self.browser.copyAvailable.connect def _(yes): self.browser.setFocusProxy(None if yes else self.lineedit) self.browser.setFocus() # Font self.browser.setFont(getMonospaceFont()) connectFontContextMenu(self.browser) lineeditlayout = QtWidgets.QHBoxLayout() command_label = QtWidgets.QLabel("PyMOL>") command_label.setObjectName("command_label") lineeditlayout.addWidget(command_label) lineeditlayout.addWidget(self.lineedit) self.lineedit.setToolTip('''Command Input Area Get the list of commands by hitting <TAB> Get the list of arguments for one command with a question mark: PyMOL> color ? Read the online help for a command with "help": PyMOL> help color Get autocompletion for many arguments by hitting <TAB> PyMOL> color ye<TAB> (will autocomplete "yellow") ''') layout = QtWidgets.QVBoxLayout() layout.addWidget(self.browser) layout.addLayout(lineeditlayout) quickbuttonslayout = QtWidgets.QVBoxLayout() quickbuttonslayout.setSpacing(2) extguilayout = QtWidgets.QBoxLayout(QtWidgets.QBoxLayout.LeftToRight) extguilayout.setContentsMargins(2, 2, 2, 2) extguilayout.addLayout(layout) extguilayout.addLayout(quickbuttonslayout) class ExtGuiFrame(QtWidgets.QFrame): def mouseDoubleClickEvent(_, event): self.toggle_ext_window_dockable(True) _size_hint = QtCore.QSize(options.win_x, options.ext_y) def sizeHint(self): return self._size_hint dockWidgetContents = ExtGuiFrame(self) dockWidgetContents.setLayout(extguilayout) dockWidgetContents.setObjectName("extgui") self.ext_window = \ dockWidget = QtWidgets.QDockWidget(self) dockWidget.setWindowTitle("External GUI") dockWidget.setWidget(dockWidgetContents) if options.external_gui: dockWidget.setTitleBarWidget(QtWidgets.QWidget()) else: dockWidget.hide() self.addDockWidget(Qt.TopDockWidgetArea, dockWidget) # rearrange vertically if docking left or right @dockWidget.dockLocationChanged.connect def _(area): if area == Qt.LeftDockWidgetArea or area == Qt.RightDockWidgetArea: extguilayout.setDirection(QtWidgets.QBoxLayout.BottomToTop) quickbuttonslayout.takeAt(quickbuttons_stretch_index) else: extguilayout.setDirection(QtWidgets.QBoxLayout.LeftToRight) if quickbuttons_stretch_index >= quickbuttonslayout.count(): quickbuttonslayout.addStretch() # OpenGL Widget self.pymolwidget = PyMOLGLWidget(self) self.setCentralWidget(self.pymolwidget) cmd = self.cmd = self.pymolwidget.cmd ''' # command completion completer = QtWidgets.QCompleter(cmd.kwhash.keywords, self) self.lineedit.setCompleter(completer) ''' # overload <Tab> action self.lineedit.installEventFilter(self) self.pymolwidget.installEventFilter(self) # Quick Buttons for row in [ [ ('Reset', cmd.reset), ('Zoom', lambda: cmd.zoom(animate=1.0)), ('Orient', lambda: cmd.orient(animate=1.0)), # render dialog will be constructed when the menu is shown # for the first time. This way it's populated with the current # viewport and settings. Also defers parsing of the ui file. ('Draw/Ray', WidgetMenu(self).setSetupUi(self.render_dialog)), ], [ ('Unpick', cmd.unpick), ('Deselect', cmd.deselect), ('Rock', cmd.rock), ('Get View', self.get_view), ], [ ('|<', cmd.rewind), ('<', cmd.backward), ('Stop', cmd.mstop), ('Play', cmd.mplay), ('>', cmd.forward), ('>|', cmd.ending), ('MClear', cmd.mclear), ], [ ('Builder', self.open_builder_panel), ('Properties', self.open_props_dialog), ('Rebuild', cmd.rebuild), ], ]: hbox = QtWidgets.QHBoxLayout() hbox.setSpacing(2) for name, callback in row: btn = QtWidgets.QPushButton(name) btn.setProperty("quickbutton", True) btn.setAttribute(Qt.WA_LayoutUsesWidgetRect) # OS X workaround hbox.addWidget(btn) if callback is None: btn.setEnabled(False) elif isinstance(callback, QtWidgets.QMenu): btn.setMenu(callback) else: btn.released.connect(callback) quickbuttonslayout.addLayout(hbox) # progress bar hbox = QtWidgets.QHBoxLayout() self.progressbar = QtWidgets.QProgressBar() self.progressbar.setSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) hbox.addWidget(self.progressbar) self.abortbutton = QtWidgets.QPushButton('Abort') self.abortbutton.setStyleSheet("background: #FF0000; color: #FFFFFF") self.abortbutton.released.connect(cmd.interrupt) hbox.addWidget(self.abortbutton) quickbuttonslayout.addLayout(hbox) quickbuttonslayout.addStretch() quickbuttons_stretch_index = quickbuttonslayout.count() - 1 # menu top level self.menubar = menubar = self.menuBar() # action groups actiongroups = {} def _addmenu(data, menu): '''Fill a menu from "data"''' menu.setTearOffEnabled(True) menu.setWindowTitle(menu.title()) # needed for Windows for item in data: if item[0] == 'separator': menu.addSeparator() elif item[0] == 'menu': _addmenu(item[2], menu.addMenu(item[1].replace('&', '&&'))) elif item[0] == 'command': command = item[2] if command is None: print('warning: skipping', item) else: if isinstance(command, str): command = lambda c=command: cmd.do(c) menu.addAction(item[1], command) elif item[0] == 'check': if len(item) > 4: menu.addAction( SettingAction(self, cmd, item[2], item[1], item[3], item[4])) else: menu.addAction( SettingAction(self, cmd, item[2], item[1])) elif item[0] == 'radio': label, name, value = item[1:4] try: group, type_, values = actiongroups[item[2]] except KeyError: group = QtWidgets.QActionGroup(self) type_, values = cmd.get_setting_tuple(name) actiongroups[item[2]] = group, type_, values action = QtWidgets.QAction(label, self) action.triggered.connect(lambda _=0, args=(name, value): cmd.set(*args, log=1, quiet=0)) self.setting_callbacks[cmd.setting._get_index( name)].append( lambda v, V=value, a=action: a.setChecked(v == V)) group.addAction(action) menu.addAction(action) action.setCheckable(True) if values[0] == value: action.setChecked(True) elif item[0] == 'open_recent_menu': self.open_recent_menu = menu.addMenu('Open Recent...') else: print('error:', item) # recent files menu self.open_recent_menu = None # for plugins self.menudict = {'': menubar} # menu for _, label, data in self.get_menudata(cmd): assert _ == 'menu' menu = menubar.addMenu(label) self.menudict[label] = menu _addmenu(data, menu) # hack for macOS to hide "Edit > Start Dictation" # https://bugreports.qt.io/browse/QTBUG-43217 if pymol.IS_MACOS: self.menudict['Edit'].setTitle('Edit_') QtCore.QTimer.singleShot( 10, lambda: self.menudict['Edit'].setTitle('Edit')) # recent files menu if self.open_recent_menu: @self.open_recent_menu.aboutToShow.connect def _(): self.open_recent_menu.clear() for fname in self.recent_filenames: self.open_recent_menu.addAction( fname if len(fname) < 128 else '...' + fname[-120:], lambda fname=fname: self.load_dialog(fname)) # some experimental window control menu = self.menudict['Display'].addSeparator() menu = self.menudict['Display'].addMenu('External GUI') menu.addAction('Toggle floating', self.toggle_ext_window_dockable, QtGui.QKeySequence('Ctrl+E')) ext_vis_action = self.ext_window.toggleViewAction() ext_vis_action.setText('Visible') menu.addAction(ext_vis_action) # extra key mappings (MacPyMOL compatible) QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+O'), self).activated.connect(self.file_open) QtWidgets.QShortcut(QtGui.QKeySequence('Ctrl+S'), self).activated.connect(self.session_save) # feedback self.feedback_timer = QtCore.QTimer() self.feedback_timer.setSingleShot(True) self.feedback_timer.timeout.connect(self.update_feedback) self.feedback_timer.start(100) # legacy plugin system self.menudict['Plugin'].addAction('Initialize Plugin System', self.initializePlugins) # focus in command line if options.external_gui: self.lineedit.setFocus() else: self.pymolwidget.setFocus() # Apply PyMOL stylesheet try: with open( cmd.exp_path('$PYMOL_DATA/pmg_qt/styles/pymol.sty')) as f: style = f.read() except IOError: print('Could not read PyMOL stylesheet.') print('DEBUG: PYMOL_DATA=' + repr(os.getenv('PYMOL_DATA'))) style = "" if style: self.setStyleSheet(style)