class OrpheusLibrarySearch(QWidget): def __init__(self): super().__init__() self.initUI() self.populateList() def initUI(self): #palette = QPalette() #palette.setColor(QPalette.Background, QColor('#383C4A')) #palette.setColor(QPalette.WindowText, QColor('#C1C1C1')) #self.setPalette(palette) self.setMaximumSize(492, 653) self.setMinimumSize(492, 653) le = QLineEdit(self) le.textChanged[str].connect(self.onChanged) le.returnPressed.connect(self.onActivation) le.setClearButtonEnabled(True) le.setPlaceholderText('Start typing to search...') self.lw = QListWidget() self.visibleLw = QListWidget() #palette.setColor(QPalette.Base, QColor('#383C4A')) #self.visibleLw.setPalette(palette) self.visibleLw.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.visibleLw.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.visibleLw.itemActivated.connect(self.onActivation) self.scrollBar = QScrollBar() self.visibleLw.verticalScrollBar().valueChanged.connect(self.scrollBar.setValue) self.scrollBar.valueChanged.connect(self.visibleLw.verticalScrollBar().setValue) vbox = QVBoxLayout() vbox.setSpacing(3) #vbox.setContentsMargins(3, 3, 3, 3) vbox.setContentsMargins(0, 4, 0, 0) vbox.addWidget(le) hbox = QHBoxLayout() hbox.setSpacing(0) #hbox.setContentsMargins(0, 0, 0, 0) hbox.addWidget(self.visibleLw) hbox.addWidget(self.scrollBar) vbox.addLayout(hbox) self.setLayout(vbox) self.setWindowTitle('Music Library') icon = QIcon.fromTheme('musique') self.setWindowIcon(icon) def populateList(self): self.client = MPDClient() self.client.connect('localhost', 6600) self.playlistinfo = self.client.playlistinfo() self.client.disconnect() self.playlist = [] #backgroundColor = QColor('#383C4A') #foregroundColor = QColor('#C1C1C1') for i in self.playlistinfo: row = '' if 'album' in i: row = row + i['album'] + ' - ' if 'title' in i: if isinstance(i['title'], str): row = row + i['title'] else: row = row + i['title'][0] if 'artist' in i: row = row + ' - ' + i['artist'] self.playlist.append(row) #newRow = QListWidgetItem(row) #newRow.setBackground(backgroundColor) #newRow.setForeground(foregroundColor) #self.lw.addItem(newRow) self.visibleLw.addItems(self.playlist) self.visibleLw.setCurrentRow(0) def get_matches(self, pattern): self.visibleLw.clear() pattern = '.*' + pattern.replace(' ', '.*').lower() regexp = re.compile(pattern) for i in self.playlist: if regexp.match(i.lower()): self.visibleLw.addItem(i) def formatScrollBar(self): self.scrollBar.setMaximum(self.visibleLw.verticalScrollBar().maximum()) self.scrollBar.setPageStep(self.visibleLw.verticalScrollBar().pageStep()) def onChanged(self, text): self.get_matches(text) self.visibleLw.setCurrentRow(0) self.scrollBar.setMaximum(self.visibleLw.verticalScrollBar().maximum()) if self.visibleLw.verticalScrollBar().maximum() == 0: self.scrollBar.setVisible(False) else: self.scrollBar.setVisible(True) def onActivation(self): selected_song = self.visibleLw.currentItem().text() for i in range(0, len(self.playlist)): if selected_song == self.playlist[i]: self.client.connect('localhost', 6600) self.client.play(i) self.client.disconnect() def keyPressEvent(self, e): if e.key() == Qt.Key_Down: self.visibleLw.setFocus() elif e.key() == Qt.Key_Escape: self.close()
class PdfDrawWidget(QWidget): horizontalScrollbar = None verticalScrollbar = None pdfVis: Document = None pageNum: int = 0 zoom: float = 1 __controlIsPressed = False __pressStart: Tuple[float, float] = None def __init__(self, parent: QWidget = None, painter: QPainter = QPainter()): super().__init__(parent) self.painter = painter self.horizontalScrollbar = QScrollBar(Qt.Horizontal) self.verticalScrollbar = QScrollBar(Qt.Vertical) self.horizontalScrollbar.setVisible(False) self.verticalScrollbar.setVisible(False) self.horizontalScrollbar.valueChanged.connect(lambda: self.update()) self.verticalScrollbar.valueChanged.connect(lambda: self.update()) self.setMouseTracking(True) def paintEvent(self, event: QPaintEvent): """All Drawing Actions are activated here""" self.__drawPDF(event) __viewchanged = True __zoomed = True __pdfImages: List[Tuple[int, QImage]] = [ ] # List of Tuples where first index is the hightoffset __pageoffsets: List[int] = [] def __drawPDF(self, event: QPaintEvent): """Draws the Pdf centered onto the self Object as long as the self.pdfVis variable isn't None. self.verticalScrollbar & self.horizontalScrollbar will be manipulated in range and visibility""" if self.pdfVis is not None: if self.__viewchanged: if self.__zoomed: hightCount = 0 self.__pageoffsets = [0] for page in self.pdfVis.pages(): hightCount += page.rect.height * self.zoom self.__pageoffsets.append(hightCount) hightCount -= self.height() if self.height() < hightCount: self.verticalScrollbar.setRange(0, hightCount) self.verticalScrollbar.setVisible(True) else: self.verticalScrollbar.setVisible(False) self.verticalScrollbar.setRange(0, 0) mat = fitz.Matrix(self.zoom, self.zoom) self.__pdfImages = [] lowestVisablePage = next( i for i in range(len(self.__pageoffsets) - 1) if self.__pageoffsets[i + 1] >= self.verticalScrollbar.value()) try: highestVisablePage = next( i for i in range(len(self.__pageoffsets) - 1) if self.__pageoffsets[i + 1] >= self.verticalScrollbar.value() + self.height()) + 1 except StopIteration: highestVisablePage = self.pdfVis.pageCount for falseI, page in enumerate( self.pdfVis.pages(lowestVisablePage, highestVisablePage)): i = falseI + lowestVisablePage clipx0 = (page.rect.width * self.zoom / 2 - self.width() / 2 + self.horizontalScrollbar.value()) \ if ( page.rect.width * self.zoom / 2 - self.width() / 2 + self.horizontalScrollbar.value()) > 0 else 0 clipy0 = (self.verticalScrollbar.value() - self.__pageoffsets[i]) / self.zoom \ if self.verticalScrollbar.value() >= self.__pageoffsets[i] else 0 # clipx1 = # clipy1 = print(clipy0, clipx0) # print(self.verticalScrollbar.value()) pix: Pixmap = page.getPixmap(mat) # , clip=Rect(0,0,110,110)) fmt = QImage.Format_RGBA8888 if pix.alpha else QImage.Format_RGB888 self.__pdfImages.append((self.__pageoffsets[i], QImage(pix.samples, pix.width, pix.height, pix.stride, fmt))) if self.__pdfImages[0][1].width() > self.width(): self.horizontalScrollbar.setRange( self.width() - self.__pdfImages[0][1].width(), self.__pdfImages[0][1].width() - self.width()) self.horizontalScrollbar.setVisible(True) else: self.horizontalScrollbar.setVisible(False) self.horizontalScrollbar.setRange(0, 0) self.__viewchanged = False self.painter.begin(self) for i, (y, img) in enumerate(self.__pdfImages): self.painter.drawImage( self.painter.viewport().width() / 2 - img.width() / 2 - self.horizontalScrollbar.value(), y + 5 * i - self.verticalScrollbar.value(), img) self.painter.end() else: pass def resizeEvent(self, QResizeEvent): super().resizeEvent(QResizeEvent) def wheelEvent(self, event: QWheelEvent): if self.__controlIsPressed: if self.zoom + 0.005 * event.angleDelta().y() > 0: self.zoom += 0.005 * event.angleDelta().y() else: self.zoom = 0.1 self.__zoomed = True else: self.verticalScrollbar.setValue(self.verticalScrollbar.value() - event.angleDelta().y()) self.__viewchanged = True self.update() def keyPressEvent(self, event: QKeyEvent): if event.key() == Qt.Key_Control: self.__controlIsPressed = True def keyReleaseEvent(self, event: QKeyEvent): if event.key() == Qt.Key_Control: self.__controlIsPressed = False def mousePressEvent(self, event: QMouseEvent): self.__pressStart = (event.x(), event.y()) def mouseMoveEvent(self, event: QMouseEvent): if self.__pressStart is not None: pass # Todo Draw Rectangle around all Textboxes def loadDocument(self, path: str): self.pdfVis = fitz.Document(path) self.update()
class _s_CentralWidget(QWidget): ############################################################################### # CentralWidget SIGNALS ############################################################################### """ splitterCentralRotated() """ splitterCentralRotated = pyqtSignal() ############################################################################### def __init__(self, parent=None): super(_s_CentralWidget, self).__init__(parent) self.parent = parent #This variables are used to save the splitter sizes before hide self._splitterMainSizes = None self._splitterAreaSizes = None self.lateralPanel = None hbox = QHBoxLayout(self) hbox.setContentsMargins(0, 0, 0, 0) hbox.setSpacing(0) #Create Splitters to divide the UI in: MainPanel, Explorer, Misc self._splitterArea = QSplitter(Qt.Horizontal) self._splitterMain = QSplitter(Qt.Vertical) #Create scrollbar for follow mode self.scrollBar = QScrollBar(Qt.Vertical, self) self.scrollBar.setFixedWidth(20) self.scrollBar.setToolTip('Follow Mode: Scroll the Editors together') self.scrollBar.hide() self.scrollBar.valueChanged[int].connect(self.move_follow_scrolls) #Add to Main Layout hbox.addWidget(self.scrollBar) hbox.addWidget(self._splitterArea) def insert_central_container(self, container): self.mainContainer = container self._splitterMain.insertWidget(0, container) def insert_lateral_container(self, container): self.lateralPanel = LateralPanel(container) self._splitterArea.insertWidget(0, self.lateralPanel) def insert_bottom_container(self, container): self.misc = container self._splitterMain.insertWidget(1, container) def showEvent(self, event): #Show Event QWidget.showEvent(self, event) #Avoid recalculate the panel sizes if they are already loaded if self._splitterArea.count() == 2: return #Rearrange widgets on Window self._splitterArea.insertWidget(0, self._splitterMain) if not event.spontaneous(): self.change_misc_visibility() if bin(settings.UI_LAYOUT)[-1] == '1': self.splitter_central_rotate() if bin(settings.UI_LAYOUT >> 1)[-1] == '1': self.splitter_misc_rotate() if bin(settings.UI_LAYOUT >> 2)[-1] == '1': self.splitter_central_orientation() qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat) #Lists of sizes as list of QVariant- heightList = [QVariant, QVariant] heightList = list(qsettings.value("window/central/mainSize", [(self.height() / 3) * 2, self.height() / 3])) widthList = list(qsettings.value("window/central/areaSize", [(self.width() / 6) * 5, self.width() / 6])) self._splitterMainSizes = [int(heightList[0]), int(heightList[1])] self._splitterAreaSizes = [int(widthList[0]), int(widthList[1])] #Set the sizes to splitters #self._splitterMain.setSizes(self._splitterMainSizes) self._splitterMain.setSizes(self._splitterMainSizes) self._splitterArea.setSizes(self._splitterAreaSizes) self.misc.setVisible( qsettings.value("window/show_misc", False, type=bool)) def change_misc_visibility(self, on_start=False): if self.misc.isVisible(): self._splitterMainSizes = self._splitterMain.sizes() self.misc.hide() widget = self.mainContainer.get_actual_widget() if widget: widget.setFocus() else: self.misc.show() self.misc.gain_focus() def change_main_visibility(self): if self.mainContainer.isVisible(): self.mainContainer.hide() else: self.mainContainer.show() def change_explorer_visibility(self, force_hide=False): if self.lateralPanel.isVisible() or force_hide: self._splitterAreaSizes = self._splitterArea.sizes() self.lateralPanel.hide() else: self.lateralPanel.show() def splitter_central_rotate(self): w1, w2 = self._splitterArea.widget(0), self._splitterArea.widget(1) self._splitterArea.insertWidget(0, w2) self._splitterArea.insertWidget(1, w1) self.splitterCentralRotated.emit() def splitter_central_orientation(self): if self._splitterArea.orientation() == Qt.Horizontal: self._splitterArea.setOrientation(Qt.Vertical) else: self._splitterArea.setOrientation(Qt.Horizontal) def splitter_misc_rotate(self): w1, w2 = self._splitterMain.widget(0), self._splitterMain.widget(1) self._splitterMain.insertWidget(0, w2) self._splitterMain.insertWidget(1, w1) def splitter_misc_orientation(self): if self._splitterMain.orientation() == Qt.Horizontal: self._splitterMain.setOrientation(Qt.Vertical) else: self._splitterMain.setOrientation(Qt.Horizontal) def get_area_sizes(self): if self.lateralPanel.isVisible(): self._splitterAreaSizes = self._splitterArea.sizes() return self._splitterAreaSizes def get_main_sizes(self): if self.misc.isVisible(): self._splitterMainSizes = self._splitterMain.sizes() return self._splitterMainSizes def enable_follow_mode_scrollbar(self, val): if val: editorWidget = self.mainContainer.get_actual_editor() maxScroll = editorWidget.verticalScrollBar().maximum() position = editorWidget.verticalScrollBar().value() self.scrollBar.setMaximum(maxScroll) self.scrollBar.setValue(position) self.scrollBar.setVisible(val) def move_follow_scrolls(self, val): widget = self.mainContainer._tabMain.currentWidget() diff = widget._sidebarWidget.highest_line - val s1 = self.mainContainer._tabMain.currentWidget().verticalScrollBar() s2 = self.mainContainer._tabSecondary.\ currentWidget().verticalScrollBar() s1.setValue(val) s2.setValue(val + diff)
class TextArea(QWidget): """ 自定义文本展示区域(PS:不能键盘输入那种) 功能:能自动拉长文本区域并且带有滑动条 """ def __init__(self, parent): super().__init__(parent) self.setContentsMargins(0, 0, 0, 0) # 滚动条的宽度 # 对于水平方向的滚动条其实是高度 self.scrollBarWidth = 15 # 文本区 self.textContainer = TextView(self) self.textContainer.setContentsMargins(5, 0, 0, 0) # 设置居顶部显示 self.textContainer.setAlignment(Qt.AlignTop) # 设置文本区域根据文本进行自适应 self.textContainer.adjustSize() self.textContainer.setCallback(self.sizeChange) # 垂直滚动条 self.vBar = QScrollBar(Qt.Vertical, self) # self.vBar.sliderMoved.connect(self.dragV) self.vBar.valueChanged.connect(self.changeVertical) # 一开始隐藏 self.vBar.setVisible(False) # 在水平方向上文字的总长度是否超过容器的宽度标志位 # 只要出现一次超过,那么 self.overHorizontal就永远是True # 除非将超过容器宽度的文本都去除 self.overHorizontal = False # 水平方向上滑块上一次的值 self.hPreValue = 0 # 水平滚动条 self.hBar = QScrollBar(Qt.Horizontal, self) # self.hBar.sliderMoved.connect(self.dragH) self.hBar.valueChanged.connect(self.changeHorizontal) # 初始化的时候隐藏自身 self.hBar.setVisible(False) # 在垂直方向上文字的总高度是否超过容器的高度标志位 # 只要出现一次超过,那么 self.overVertical就永远是True # 除非将超过容器高度的文本都去除 self.overVertical = False # 垂直方向上滑块上一次的值 self.vPreValue = 0 def setSize(self, width, height): """ 设置TextArea的宽度和高度 :param width: 宽度 :param height: 高度 :return: no return """ self.resize(width, height) self.setMaximumSize(width, height) self.setMinimumSize(width, height) # 设置垂直方向ScrollBar的宽度和高度 # 垂直方向ScrollBar的高度等于TextArea的高度 self.vBar.resize(self.scrollBarWidth, height - self.scrollBarWidth) # 移动到指定位置 self.vBar.move(width - self.scrollBarWidth, 0) # 设置水平方向的ScrollBar的宽度和高度 # 水平方向ScrollBar的高度为self.scrollBarWidth self.hBar.resize(width, self.scrollBarWidth) self.hBar.move(0, height - self.scrollBarWidth) # 设置文本区域的高度和宽度 self.textContainer.resize(width, height) self.setStyleSheet("background-color: #088A68;") self.textContainer.setStyleSheet("border: 1px solid #FF0000; color: #FFFFFF;") def setMaximumSize(self, maxw: int, maxh: int) -> None: super().setMaximumSize(maxw, maxh) def setClassName(self, textAreaName=None, labelName=None, vBarName=None, hBarName=None): """ 设置ObjectName :param textAreaName: TextArea的ObjectName :param labelName: 文本区域的ObjectName :param vBarName: 垂直ScrollBar的ObjectName :param hBarName: 水平ScrollBar的ObjectName :return: """ self.setObjectName(textAreaName) self.textContainer.setObjectName(labelName) self.hBar.setObjectName(hBarName) self.vBar.setObjectName(vBarName) def append(self, text): self.textContainer.adjustSize() self.textContainer.append(text) margin = self.contentsMargins() # 如果文本长度超过了容器的宽度, 将标准位置为True # 并且显示水平滑动条 if self.textContainer.width() > (self.width() - margin.left() - margin.right()): self.overHorizontal = True self.hBar.setVisible(True) # 如果文本总高度度超过了容器的宽度, 将标准位置为True # 并且显示垂直滑动条 if self.textContainer.height() > (self.height() - margin.top() - margin.bottom()): self.overVertical = True self.vBar.setVisible(True) def dragHortizontal(self, value): """ 滑块水平方向上的拖拽事件 :argument value :return: """ print("H: " + str(value)) def dragVertical(self, value): """ 滑块垂直方向上的拖拽事件 :argument value :return: """ print("V: " + str(value)) def changeHorizontal(self, v): """ 垂直滑块的值发生改变时 :param v: :return: """ if v == 0: # 由于下列的操作会产生浮点型数据,所以在计算坐标的时候总有偏差 # 所以在 v = 0, 也就是滑动条回到最开始的地方, # self.textContainer对应方向上的坐标也归0,那样就不会出问题了 self.textContainer.move(0, self.textContainer.y()) # 将当前值作为下一次的preValue self.hPreValue = v return # print("change H: {}".format(v)) textW = self.textContainer.width() # 最大值(从0开始计算) maximum = self.hBar.maximum() + 1 # 计算每次改变值对应 self.textContainer在水平方向上需要移动的尺寸 stepW = textW / maximum # 当前值与上一次的值得差 change = v - self.hPreValue move = int(self.textContainer.x() - stepW * change) self.textContainer.move(move, self.textContainer.y()) # 将当前值作为下一次的preValue self.hPreValue = v def changeVertical(self, v): """ 水平滑块的值发生改变时 :param v: :return: """ if v == 0: # 由于下列的操作会产生浮点型数据,所以在计算坐标的时候总有偏差 # 所以在 v = 0, 也就是滑动条回到最开始的地方, # self.textContainer对应方向上的坐标也归0,那样就不会出问题了 self.textContainer.move(self.textContainer.x(), 0) self.vPreValue = v return # print("change V: {}".format(v)) textH = self.textContainer.width() # 最大值(从0开始计算) maximum = self.vBar.maximum() - self.vBar.minimum() + self.vBar.pageStep() # 计算每次改变值对应 self.textContainer在水平方向上需要移动的尺寸 stepH = float(textH) / maximum # 当前值与上一次的值得差 change = v - self.vPreValue move = int(self.textContainer.y() - stepH * change) self.textContainer.move(self.textContainer.x(), move) # 将当前值作为下一次的preValue self.vPreValue = v def sizeChange(self, obj: QLabel): print("size change:{},{} ".format(obj.width(), obj.height()))