Q. is it possible, dialog closes itself and return color name just when user clicked or double clicked color item
Below is working example (hopefully to demonstrate problem). clicking on canvas area will pop up color dialog. currently user has to select color and then hit 'OK' button, where as intent is complete when user click on color. Just wanted to save user time, one bit.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets, uic, QtMultimedia, QtMultimediaWidgets
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
colorsDict = { 'White': '0xFFFFFF','DarkGray': '0xA9A9A9','DarkSlateGray': '0x2F4F4F','LightSlateGray': '0x778899','DimGray': '0x696969','Gray': '0x808080','SlateGray': '0x708090','Black': '0x000000','DarkRed': '0x8B0000','Darkorange': '0xFF8C00','FireBrick': '0xB22222','Crimson': '0xDC143C','Salmon': '0xFA8072'}
def hexToQColor (h):
h = h.lstrip('#') # general usage safety
# h = h.lstrip('0x') # my use case
if h.find('0x') == 0:
h = h.split('0x')[1]
rgb = tuple(int(h[i:i+2], 16) for i in (0, 2 ,4))
return QColor(rgb[0],rgb[1],rgb[2])
class FfmpegColorDialog(QDialog):
"""
Custom FFMPEG Color Picker class
"""
resized = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(FfmpegColorDialog, self).__init__(parent)
# self.ui = uic.loadUi('ui/ffmpeg_colors.ui', self)
self.setWindowTitle("FFMPEG Color Picker")
self.listWidget = QListWidget()
self.readPrefs()
self.listWidget.setFlow(QListView.LeftToRight)
self.listWidget.setResizeMode(QListView.Adjust)
self.listWidget.setGridSize(QSize(32, 32))
self.listWidget.setSpacing(5)
self.listWidget.setViewMode(QListView.IconMode)
self.listWidget.itemClicked.connect(self.itemClicked)
self.listWidget.itemDoubleClicked.connect(self.itemDoubleClicked)
layout = QVBoxLayout(self)
layout.addWidget(self.listWidget)
# OK and Cancel buttons
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
buttons.accepted.connect(self.accept)
buttons.rejected.connect(self.reject)
layout.addWidget(buttons)
def readPrefs(self):
"""
reading preferences from module for Data in UI
"""
for each in colorsDict.keys():
item = colorsDict[each]
listItem = QListWidgetItem()
listItem.setToolTip(each)
listItem.setSizeHint(QSize(30, 30))
color = hexToQColor(item)
listItem.setBackground(QBrush(color))
self.listWidget.addItem(listItem)
def itemClicked(self,item):
self.listWidget.setCurrentItem(item)
# self.accept()
def itemDoubleClicked(self,item):
c = item.background().color()
self.listWidget.setCurrentItem(item)
result = self.exec_()
return(c,result==QDialog.Accepted)
def getResults(self):
if self.exec_() == QDialog.Accepted:
item = self.listWidget.currentItem()
# print (item.toolTip())
return ( item.toolTip())
else:
return None
def getUserColor(self):
return (self.listWidget.currentItem().toolTip())
#staticmethod
def getFinalColor(parent=None):
dialog = FfmpegColorDialog(parent)
result = dialog.exec_()
color = dialog.getUserColor()
return(color,result==QDialog.Accepted)
class MainWindow(QMainWindow):
central_widget = None
layout_container = None
def __init__(self):
super(MainWindow, self).__init__()
self.central_widget = QWidget()
self.layout_container = QVBoxLayout()
self.central_widget.setLayout(self.layout_container)
self.setCentralWidget(self.central_widget)
self.layout_container.addWidget(GraphicsView())
class GraphicsView(QGraphicsView):
def __init__(self):
super(GraphicsView, self).__init__()
self.scene = QGraphicsScene()
self.setScene(self.scene)
self.text = None
self.createText()
def createText(self):
self.text = QGraphicsTextItem()
font = QFont()
font.setPixelSize(40)
self.text.setFont(font)
self.text.setPlainText("Sample Text To Test")
self.scene.addItem(self.text)
def mousePressEvent(self, event):
r,ok = FfmpegColorDialog.getFinalColor()
hc = colorsDict[r]
rgb = hexToQColor(hc)
self.text.setDefaultTextColor(rgb)
if __name__ == '__main__':
app = QApplication(sys.argv)
# dia = FfmpegColorDialog()
# dia.show()
mw = MainWindow()
mw.show()
sys.exit(app.exec_())
Just connect the clicked signal of the QListWidget to the accept slot of FfmpegColorDialog:
class FfmpegColorDialog(QDialog):
"""
Custom FFMPEG Color Picker class
"""
resized = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(FfmpegColorDialog, self).__init__(parent)
# ...
self.listWidget = QListWidget()
self.listWidget.clicked.connect(self.accept) # <---
# ...
Related
i am new in PyQt but i have a problem i can't solve. I am trying to get text from second window and set it to field, so when i close second window i can print it form first main window, but my "AnotherWindow" button won't fire event and i really don't know why? Here is code. Can anyone guide me?
Thanks
class AnotherWindow(QMainWindow):
def __init__(self):
super().__init__()
self.resize(1200, 600)
self.text = "basetext"
self.layoutf = QFormLayout()
self.buttonf = QPushButton("get text")
self.buttonf.clicked.connect(lambda: self.getText)
self.line = QLineEdit()
self.layoutf.addRow(self.buttonf,self.line)
self.widgetf = QWidget()
self.widgetf.setLayout(self.layoutf)
self.setCentralWidget(self.widgetf)
def getText(self):
print(self.line.text)
self.text = self.line.text
self.close()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.w = None # No external window yet.
self.mainLayout = QGridLayout()
self.button = QPushButton("Push for Window")
self.button.clicked.connect(self.show_new_window)
self.button1 = QPushButton("Push ")
self.button1.clicked.connect(self.printFromSecondWindow)
self.mainLayout.addWidget(self.button,0,0)
self.mainLayout.addWidget(self.button1, 0, 1)
self.widget = QWidget()
self.widget.setLayout(self.mainLayout)
self.setCentralWidget(self.widget)
def show_new_window(self):
if self.w is None:
self.w = AnotherWindow()
self.w.show()
else:
self.w.close() # Close window.
self.w = None # Discard reference.
def printFromSecondWindow(self):
print(self.w.text)
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()
The problem is with self.buttonf.clicked.connect(...). This call attaches a function to the "clicked" action on the button. The function is called without parameters and the return is simply discarded. In your case, lambda: self.get_text is a function that does nothing but return the address of the self.get_text method. Since get_text doesn't need any additional parameters, you can bind it directly to this slot.
self.buttonf.clicked.connect(self.get_text)
You also have a bug later on where you need to call the text method. With these two changes, the working program is
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.Qt import *
class AnotherWindow(QMainWindow):
def __init__(self):
super().__init__()
self.resize(1200, 600)
self.text = "basetext"
self.layoutf = QFormLayout()
self.buttonf = QPushButton("get text")
self.buttonf.clicked.connect(self.getText)
self.line = QLineEdit()
self.layoutf.addRow(self.buttonf,self.line)
self.widgetf = QWidget()
self.widgetf.setLayout(self.layoutf)
self.setCentralWidget(self.widgetf)
def getText(self):
print("the info", self.line.text())
self.text = self.line.text()
self.close()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.w = None # No external window yet.
self.mainLayout = QGridLayout()
self.button = QPushButton("Push for Window")
self.button.clicked.connect(self.show_new_window)
self.button1 = QPushButton("Push ")
self.button1.clicked.connect(self.printFromSecondWindow)
self.mainLayout.addWidget(self.button,0,0)
self.mainLayout.addWidget(self.button1, 0, 1)
self.widget = QWidget()
self.widget.setLayout(self.mainLayout)
self.setCentralWidget(self.widget)
def show_new_window(self):
if self.w is None:
self.w = AnotherWindow()
self.w.show()
else:
self.w.close() # Close window.
self.w = None # Discard reference.
def printFromSecondWindow(self):
print(self.w.text)
app = QApplication(sys.argv)
w = MainWindow()
w.show()
app.exec_()
Instantiate the second window once in the main window constructor. self.w = AnotherWindow (self)
When creating an instance of the second window, self.w = AnotherWindow (self) - self is passed as a parent, so that when the main window is closed, the second window also closes.
To get text from a QLineEdit widget - apply QString text() const, more https://doc.qt.io/qt-5/qlineedit.html#text-prop
You did not show the method printFromSecondWindow in which, as I understand it, you wanted to show what you intended.
Try it:
import sys
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.Qt import *
class AnotherWindow(QMainWindow):
def __init__(self, parent=None):
super(AnotherWindow, self).__init__(parent)
self.widgetf = QWidget()
self.setCentralWidget(self.widgetf)
# self.resize(1200, 600)
self.text = "basetext"
self.layoutf = QFormLayout(self.widgetf)
self.buttonf = QPushButton("get text")
# self.buttonf.clicked.connect(lambda: self.getText) # ??? lambda
self.buttonf.clicked.connect(self.getText)
self.line = QLineEdit()
self.layoutf.addRow(self.buttonf,self.line)
def getText(self):
print(self.line.text()) # ! .text()
self.text = self.line.text() # ! .text()
self.close()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.widget = QWidget()
self.setCentralWidget(self.widget)
# ? self.w = None # No external window yet.
self.button = QPushButton("Push for Window")
self.button.clicked.connect(self.show_new_window)
self.button1 = QPushButton("Push ")
self.button1.clicked.connect(self.printFromSecondWindow)
self.mainLayout = QGridLayout(self.widget)
self.mainLayout.addWidget(self.button, 0, 0)
self.mainLayout.addWidget(self.button1, 0, 1)
self.w = AnotherWindow(self) # +++
def show_new_window(self):
# if self.w is None:
# self.w = AnotherWindow()
self.w.show() # !
# else:
# self.w.close() # Close window.
# self.w = None # Discard reference.
def printFromSecondWindow(self): # +++
print(self.w.text)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
This is a follow-up to this question of mine: Make QGroupBox selectable and clickable
My related question is how can I programmatically click on the first group upon startup?
I have tried to do this after implementing the solution provided in the original question using:
scrollLayout.itemAt(0).widget().click()
However, this did not work.
What am I missing?
Try it:
from PyQt5 import QtWidgets, QtGui, QtCore
class GroupBox(QtWidgets.QGroupBox):
clicked = QtCore.pyqtSignal(str, object)
def __init__(self, title):
super(GroupBox, self).__init__()
self.title = title
self.setTitle(self.title)
def mousePressEvent(self, event):
child = self.childAt(event.pos())
if not child:
child = self
self.clicked.emit(self.title, child)
class MainWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MainWindow, self).__init__()
w = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
w.setLayout(layout)
self.setCentralWidget(w)
my_tree = QtWidgets.QTreeWidget()
layout.addWidget(my_tree)
alpha = QtWidgets.QTreeWidgetItem(my_tree, ['Alpha'])
beta = QtWidgets.QTreeWidgetItem(my_tree, ['Beta'])
alpha.addChild(QtWidgets.QTreeWidgetItem(['one']))
alpha.addChild(QtWidgets.QTreeWidgetItem(['two']))
beta.addChild(QtWidgets.QTreeWidgetItem(['first']))
beta.addChild(QtWidgets.QTreeWidgetItem(['second']))
my_tree.expandAll()
alpha.child(0).setSelected(True)
scroll = QtWidgets.QScrollArea()
layout.addWidget(scroll)
scrollLayout = QtWidgets.QVBoxLayout()
scrollW = QtWidgets.QWidget()
scroll.setWidget(scrollW)
scrollW.setLayout(scrollLayout)
scrollLayout.setAlignment(QtCore.Qt.AlignTop)
scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
scroll.setWidgetResizable(True)
for _ in range(5):
fooGroup = GroupBox(f'GroupBox_{_}')
fooGroup.setObjectName(f'fooGroup {_}')
fooGroup.clicked.connect(self.onFooGroupClick)
fooLayout = QtWidgets.QVBoxLayout()
fooGroup.setLayout(fooLayout)
fooItem1 = QtWidgets.QLabel("fooItem1", objectName="fooItem1")
fooItem1.setStyleSheet('background: #44ffff')
fooItem2 = QtWidgets.QLabel("fooItem2", objectName="fooItem2")
fooItem2.setStyleSheet('background: #ffff56;')
fooItem3 = QtWidgets.QLabel("fooItem3", objectName="fooItem3")
fooItem3.setStyleSheet('background: #ff42ff;')
fooLayout.addWidget(fooItem1)
fooLayout.addWidget(fooItem2)
fooLayout.addWidget(fooItem3)
scrollLayout.addWidget(fooGroup)
# scrollLayout.itemAt(0).widget().click() # ---
_fooGroup = scrollLayout.itemAt(0).widget() # +++
_fooGroup.clicked.emit('GroupBox_0', _fooGroup) # +++
def onFooGroupClick(self, title, obj):
print(f"Group: {title}; objectName=`{obj.objectName()}`")
if __name__ == '__main__':
app = QtWidgets.QApplication([])
window = MainWindow()
window.show()
app.exec_()
I am trying to figure out a way to customize the scrollbars for QListWidget to have the scrollbars above and below the QListWidget instead of the normal vertical and horizontal scrollbars.
Please check out my example below if you don't understand what I mean.
In the example below I use QPushButtons with QTimers controlling the scrolling in place of the scrollbars but what I am looking for are scrollbars like the ones in QMenu when menu scrolling is enabled.
If that is not an option, I am wondering if there is a scrollbar signal or something that I could try to use to know when the scrollbars are normally activated? That way I can show/hide the buttons as needed. Thanks.
import sys
from PyQt5.QtCore import pyqtSignal, QTimer, Qt
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, \
QApplication, QStyle, QListWidget, QStyleOptionButton, QListWidgetItem
class UpBtn(QPushButton):
mouseHover = pyqtSignal()
def __init__(self):
QPushButton.__init__(self)
self.setMouseTracking(True)
self.timer = QTimer()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
opt = QStyleOptionButton()
self.initStyleOption(opt)
self.style().drawControl(QStyle.CE_ScrollBarSubLine, opt, painter, self)
painter.end()
def startScroll(self):
self.mouseHover.emit()
def enterEvent(self, event):
self.timer.timeout.connect(self.startScroll)
self.timer.start(120)
def leaveEvent(self, event):
self.timer.stop()
class DwnBtn(QPushButton):
mouseHover = pyqtSignal()
def __init__(self):
QPushButton.__init__(self)
self.setMouseTracking(True)
self.timer = QTimer()
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
opt = QStyleOptionButton()
self.initStyleOption(opt)
self.style().drawControl(QStyle.CE_ScrollBarAddLine, opt, painter, self)
painter.end()
def startScroll(self):
self.mouseHover.emit()
def enterEvent(self, event):
self.timer.timeout.connect(self.startScroll)
self.timer.start(120)
def leaveEvent(self, event):
self.timer.stop()
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.layout = QVBoxLayout()
self.layout.setContentsMargins(0, 0, 0, 0)
self.layout.setSpacing(0)
self.upBtn = UpBtn()
self.upBtn.setFixedWidth(230)
self.layout.addWidget(self.upBtn)
self.listWidget = QListWidget()
self.listWidget.setFixedWidth(230)
self.listWidget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.listWidget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.layout.addWidget(self.listWidget)
self.downBtn = DwnBtn()
self.downBtn.setFixedWidth(230)
self.layout.addWidget(self.downBtn)
self.setLayout(self.layout)
self.upBtn.clicked.connect(self.upBtnClicked)
self.upBtn.mouseHover.connect(self.upBtnClicked)
self.downBtn.clicked.connect(self.downBtnClicked)
self.downBtn.mouseHover.connect(self.downBtnClicked)
for i in range(100):
item = QListWidgetItem()
item.setText("list item " + str(i))
self.listWidget.addItem(item)
def upBtnClicked(self):
cur = self.listWidget.currentRow()
self.listWidget.setCurrentRow(cur - 1)
def downBtnClicked(self):
cur = self.listWidget.currentRow()
self.listWidget.setCurrentRow(cur + 1)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
EDIT:
Here is an example image for what I am talking about. This is a scrollable QMenu.
EDIT:
Scrollable QMenu code.
Uncomment the commented parts to get a fixed size like in the image. Normally Qmenu scrolling only works when the menu items exceed the screen height. I am just looking for the top and bottom hover style scrolling but to be used in QListWidget.
import sys
from PyQt5.QtCore import QPoint, QEvent
from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, \
QApplication, QAction, QMenu, QProxyStyle, QStyle
class MyMenu(QMenu):
def event(self, event):
if event.type() == QEvent.Show:
self.move(self.parent().mapToGlobal(QPoint(-108, 0)))
return super(MyMenu, self).event(event)
# class CustomStyle(QProxyStyle):
# def pixelMetric(self, QStyle_PixelMetric, option=None, widget=None):
# if QStyle_PixelMetric == QStyle.PM_MenuScrollerHeight:
# return 15
# if QStyle_PixelMetric == QStyle.PM_MenuDesktopFrameWidth:
# return 290
# else:
# return QProxyStyle.pixelMetric(self, QStyle_PixelMetric, option, widget)
class MainWindow(QWidget):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.layout = QVBoxLayout()
self.btn = QPushButton("Button")
self.btn.setFixedHeight(30)
self.btn.setFixedWidth(100)
self.myMenu = MyMenu("Menu", self.btn)
self.btn.setMenu(self.myMenu)
self.layout.addWidget(self.btn)
self.setLayout(self.layout)
menus = []
for _ in range(5):
myMenus = QMenu("Menu"+str(_+1), self.btn)
# myMenus.setFixedHeight(120)
myMenus.setStyleSheet("QMenu{menu-scrollable: 1; }")
menus.append(myMenus)
for i in menus:
self.btn.menu().addMenu(i)
for item in range(100):
action = QAction("item" + str(item), self)
i.addAction(action)
if __name__ == '__main__':
app = QApplication(sys.argv)
# app.setStyle(CustomStyle())
w = MainWindow()
w.show()
app.exec_()
The idea is to obtain the row of the upper and lower element that will decide whether the buttons are hidden or not, for that we use the method itemAt () that returns the item given the geometrical coordinates. On the other hand I have improved this calculation has to do every time they change the number of items in the QListView for that we use the signals of the internal model.
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
class Button(QtWidgets.QPushButton):
moveSignal = QtCore.pyqtSignal()
def __init__(self, *args, **kwargs):
super(Button, self).__init__(*args, **kwargs)
self.m_timer = QtCore.QTimer(self, interval=120)
self.m_timer.timeout.connect(self.moveSignal)
self.setMouseTracking(True)
self.setFixedHeight(20)
def mouseReleaseEvent(self, e):
super(Button, self).mousePressEvent(e)
self.setDown(True)
def enterEvent(self, e):
self.setDown(True)
self.m_timer.start()
super(Button, self).enterEvent(e)
def leaveEvent(self, e):
self.setDown(False)
self.m_timer.stop()
super(Button, self).leaveEvent(e)
class Window(QtWidgets.QWidget):
def __init__(self):
super(Window, self).__init__()
self.setFixedWidth(230)
icon = self.style().standardIcon(QtWidgets.QStyle.SP_ArrowUp)
self.upBtn = Button(icon=icon)
self.upBtn.moveSignal.connect(self.moveUp)
icon = self.style().standardIcon(QtWidgets.QStyle.SP_ArrowDown)
self.downBtn = Button(icon=icon)
self.downBtn.moveSignal.connect(self.moveDown)
self.listWidget = QtWidgets.QListWidget()
self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.listWidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
layout = QtWidgets.QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.addWidget(self.upBtn)
layout.addWidget(self.listWidget)
layout.addWidget(self.downBtn)
self.adjust_buttons()
self.create_connections()
def create_connections(self):
self.listWidget.currentItemChanged.connect(self.adjust_buttons)
model = self.listWidget.model()
model.rowsInserted.connect(self.adjust_buttons)
model.rowsRemoved.connect(self.adjust_buttons)
model.rowsMoved.connect(self.adjust_buttons)
model.modelReset.connect(self.adjust_buttons)
model.layoutChanged.connect(self.adjust_buttons)
#QtCore.pyqtSlot()
def adjust_buttons(self):
first = self.listWidget.itemAt(QtCore.QPoint())
r = self.listWidget.row(first)
self.upBtn.setVisible(r != 0 and r!= -1)
last = self.listWidget.itemAt(self.listWidget.viewport().rect().bottomRight())
r = self.listWidget.row(last)
self.downBtn.setVisible( r != (self.listWidget.count() -1) and r != -1)
#QtCore.pyqtSlot()
def moveUp(self):
ix = self.listWidget.moveCursor(QtWidgets.QAbstractItemView.MoveUp, QtCore.Qt.NoModifier)
self.listWidget.setCurrentIndex(ix)
#QtCore.pyqtSlot()
def moveDown(self):
ix = self.listWidget.moveCursor(QtWidgets.QAbstractItemView.MoveDown, QtCore.Qt.NoModifier)
self.listWidget.setCurrentIndex(ix)
#QtCore.pyqtSlot(str)
def add_item(self, text):
item = QtWidgets.QListWidgetItem(text)
self.listWidget.addItem(item)
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
window = Window()
for i in range(100):
window.add_item("item {}".format(i))
window.show()
sys.exit(app.exec_())
I'm making a program to specify a specific area using pyqt5.
If I click on the capture button, I will hide the MainWindow and display a screen to specify the area. I want to clear the screen and return to the mainwindow by pressing the esc key. But MainWindow does not show again. How should I work it?
form_class = uic.loadUiType("prototype.ui")[0]
class MainWindow(QtWidgets.QMainWindow, form_class) :
def __init__(self):
super().__init__()
self.setupUi(self)
self.Capture_Button.clicked.connect(self.Capture_Btn_clicked)
self.CaptureWindow = CaptureWidget()
self.CaptureWindow.hide()
def Capture_Btn_clicked(self) :
self.hide()
self.CaptureWindow.close()
self.CaptureWindow.__init__()
self.CaptureWindow.show()
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
def enterEvent(self, QEvent):
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
self.setWindowOpacity(1)
This is the class that specifies the area (some of the code is omitted).
class CaptureWidget(QtWidgets.QDialog):
def __init__(self):
super().__init__()
root = tk.Tk()
self.setupUi(self)
def setupUi(self) :
self.screen_width = root.winfo_screenwidth()
self.screen_height = root.winfo_screenheight()
self.setGeometry(0, 0, self.screen_width, self.screen_height)
self.setWindowTitle(' ')
self.begin = QtCore.QPoint()
self.end = QtCore.QPoint()
self.setWindowOpacity(0.3)
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
self.setWindowFlags(QtCore.Qt.FramelessWindowHint|QtCore.Qt.WindowStaysOnTopHint)
print('Capture the screen...')
self.is_set_region = False
self.is_mouse_click = False
self.show()
def keyPressEvent(self, event):
key = event.key()
if key == Qt.Key_Escape:
print('esc')
self.close()
elif key == Qt.Key_F1:
self.close()
self.__init__()
First of all, instead of overwriting the keyPressEvent method, it is easier to use QShortcut. On the other hand for this case it is better to create a signal that indicates when the escape key is pressed connecting it to the show method.
from PyQt5 import QtCore, QtGui, QtWidgets, uic
class CaptureWidget(QtWidgets.QDialog):
escape_pressed = QtCore.pyqtSignal()
def __init__(self):
super().__init__()
self.setupUi()
def setupUi(self):
self.setWindowOpacity(0.3)
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
shorcut_scaped = QtWidgets.QShortcut(QtCore.Qt.Key_Escape, self)
shorcut_scaped.activated.connect(self.escape_pressed)
shorcut_scaped.activated.connect(self.close)
shorcut = QtWidgets.QShortcut(QtCore.Qt.Key_F1, self)
shorcut.activated.connect(self.close)
form_class, _ = uic.loadUiType("prototype.ui")
class MainWindow(QtWidgets.QMainWindow, form_class):
def __init__(self):
super().__init__()
self.setupUi(self)
self.Capture_Button.clicked.connect(self.Capture_Btn_clicked)
self.CaptureWindow = CaptureWidget()
self.CaptureWindow.escape_pressed.connect(self.show)
#QtCore.pyqtSlot()
def Capture_Btn_clicked(self):
self.hide()
self.CaptureWindow.showFullScreen()
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CrossCursor))
def enterEvent(self, QEvent):
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
self.setWindowOpacity(1)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
I would like to ask how to make the text in QTextEdit scoll, to achieve an animational effect. The animational effect should be something like what in the video shows: https://www.youtube.com/watch?v=MyeuGdXv4XM
With PyQt I want to get this effect:
The text should be scolled automatically at a speed of 2 lines/second downwards, till it reaches the end and stops.
In my code below, when the button is clicked, the text is shown in QTextEdit-Widget. The text is very long, so that the scroll bar is shown.
My Problem:
I dont know how to make the animation effect. Thus I would like to ask your help to correct my code.
# -*- coding: utf-8 -*-
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys
import time
list_longText = [" long text 1 - auto scrolling " * 1000, " long text 2 - auto scrolling " * 2000]
class Worker(QObject):
finished = pyqtSignal()
strTxt = pyqtSignal(str)
def __init__(self, parent=None):
super(Worker, self).__init__(parent)
#pyqtSlot()
def onJob(self):
for i in range(2):
self.strTxt.emit(list_longText[i])
time.sleep(2)
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setFixedSize(600, 400)
self.setObjectName("window")
self.initUI()
def initUI(self):
self.txt = QTextEdit("", self)
self.btn = QPushButton("Button", self)
self.btn.clicked.connect(self.start)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.txt)
self.layout.addWidget(self.btn)
self.setLayout(self.layout)
self.show()
def start(self):
self.thread = QThread()
self.obj = Worker()
self.obj.strTxt.connect(self.showText)
self.obj.moveToThread(self.thread)
self.obj.finished.connect(self.thread.quit)
self.thread.started.connect(self.obj.onJob)
self.thread.start()
def showText(self, str):
self.txt.setText("{}".format(str))
self.autoScroll()
def autoScroll(self):
vsb = self.txt.verticalScrollBar()
if vsb.value() <= vsb.maximum():
vsb.setValue(vsb.value() + 2)
time.sleep(1)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
sys.exit(app.exec_())
Thanks very much for the help!
The task you want is not heavy, it is periodic so using a thread is inappropriate, for this task we can use QVariantAnimation.
The other part is to create a method that moves to a certain line of text for it we use QTextCursor next to findBlockByLineNumber() of QTextDocument.
And for the last one we must start moving to the last initial visible for it we use the cursorForPosition() method through the size of the viewport().
longText = "\n".join(["{}: long text - auto scrolling ".format(i) for i in range(100)])
class AnimationTextEdit(QTextEdit):
def __init__(self, *args, **kwargs):
QTextEdit.__init__(self, *args, **kwargs)
self.animation = QVariantAnimation(self)
self.animation.valueChanged.connect(self.move)
#pyqtSlot()
def startAnimation(self):
self.animation.stop()
lines_per_second = 2
self.moveToLine(0)
p = QPoint(self.viewport().width() - 1, self.viewport().height() - 1)
cursor = self.cursorForPosition(p)
self.animation.setStartValue(cursor.blockNumber())
self.animation.setEndValue(self.document().blockCount()-1)
self.animation.setDuration(self.animation.endValue()*1000/lines_per_second)
self.animation.start()
#pyqtSlot(QVariant)
def move(self, i):
cursor = QTextCursor(self.document().findBlockByLineNumber(i))
self.setTextCursor(cursor)
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setFixedSize(600, 400)
self.txt = AnimationTextEdit(self)
self.btn = QPushButton("Start", self)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.txt)
self.layout.addWidget(self.btn)
self.txt.append(longText)
self.txt.move(0)
self.btn.clicked.connect(self.txt.startAnimation)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
Update:
if you want a continuous movement you must use verticalScrollBar():
longText = "\n".join(["{}: long text - auto scrolling ".format(i) for i in range(100)])
class AnimationTextEdit(QTextEdit):
def __init__(self, *args, **kwargs):
QTextEdit.__init__(self, *args, **kwargs)
self.animation = QVariantAnimation(self)
self.animation.valueChanged.connect(self.moveToLine)
#pyqtSlot()
def startAnimation(self):
self.animation.stop()
self.animation.setStartValue(0)
self.animation.setEndValue(self.verticalScrollBar().maximum())
self.animation.setDuration(self.animation.endValue()*4)
self.animation.start()
#pyqtSlot(QVariant)
def moveToLine(self, i):
self.verticalScrollBar().setValue(i)
class MyApp(QWidget):
def __init__(self):
super(MyApp, self).__init__()
self.setFixedSize(600, 400)
self.txt = AnimationTextEdit(self)
self.btn = QPushButton("Start", self)
self.layout = QHBoxLayout(self)
self.layout.addWidget(self.txt)
self.layout.addWidget(self.btn)
self.txt.append(longText)
self.txt.moveToLine(0)
self.btn.clicked.connect(self.txt.startAnimation)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())