Qt wrapping text in boundingRect - qt

I am writing an application in PyQt and I need to wrap the text that I am painting. I use boundingRect method of QFontMetrics class to define the size and then QPainter.drawText to draw. I need the text to fit in a given rectangle. This is the line I use:
rect = metrics.boundingRect(rect, Qt.TextWordWrap, text)
However, due to the flag Qt.TextWordWrap, if the text doesn't have any spaces it doesn't wrap and will not fit in the rectangle. I tried Qt.TextWrapAnywhere but this breaks the words apart even when there are spaces. The Qt.TextFlag doesn't seem to have any flag, that prioritize wrapping words and only if it is impossible, breaks the words apart, like QTextOption.WrapAtWordBoundaryOrAnywhere.
Is there a way to wrap text like this using boundingRect and drawText?

For situations like these, it's usually better to use a QTextDocument, which allows using more advanced options.
class WrapAnywhereLabel(QtWidgets.QWidget):
def __init__(self, text=''):
super().__init__()
self.doc = QtGui.QTextDocument(text)
self.doc.setDocumentMargin(0)
opt = QtGui.QTextOption()
opt.setWrapMode(opt.WrapAtWordBoundaryOrAnywhere)
self.doc.setDefaultTextOption(opt)
def hasHeightForWidth(self):
return True
def heightForWidth(self, width):
self.doc.setTextWidth(width)
return self.doc.size().height()
def sizeHint(self):
return self.doc.size().toSize()
def resizeEvent(self, event):
self.doc.setTextWidth(self.width())
def paintEvent(self, event):
qp = QtGui.QPainter(self)
self.doc.drawContents(qp, QtCore.QRectF(self.rect()))
import sys
app = QtWidgets.QApplication(sys.argv)
w = WrapAnywhereLabel('hellooooo hello hellooooooooooooooo')
w.show()
sys.exit(app.exec_())

Such function can be easily implemented with QFontMetricsF
from PyQt5 import QtCore, QtGui, QtWidgets
import math
def drawText(painter, rect, text):
metrics = QtGui.QFontMetricsF(painter.font())
space = metrics.horizontalAdvance(" ")
width = rect.width()
def lineWidth(line):
return sum([word[1] for word in line]) + space * (len(line) - 1)
def canFit(line, word):
return lineWidth(line + [word]) < width
def forceSplit(word):
charSize = [metrics.horizontalAdvance(c) for c in word[0]]
for i in reversed(range(1,len(charSize))):
if sum(charSize[:i]) < width:
return [(word, metrics.horizontalAdvance(word)) for word in [word[0][:i], word[0][i:]]]
queue = [(word, metrics.horizontalAdvance(word)) for word in text.split(" ")]
lines = []
line = []
while len(queue) > 0:
word = queue.pop(0)
if canFit(line, word):
line.append(word)
else:
if len(line) == 0:
word1, word2 = forceSplit(word)
line.append(word1)
lines.append(line)
line = []
queue.insert(0, word2)
else:
lines.append(line)
line = []
queue.insert(0, word)
if len(line) > 0:
lines.append(line)
line = []
painter.save()
painter.setClipRect(rect)
x = rect.x()
y = rect.y() + metrics.height()
for line in lines:
text = " ".join([word[0] for word in line])
painter.drawText(int(x), int(y), text)
y += metrics.leading() + metrics.height()
painter.restore()
def replaceSomeSpaces(text, n):
res = []
for i,word in enumerate(text.split(" ")):
res.append(word)
if (i % n) == 0:
res.append(" ")
else:
res.append("_")
return "".join(res)
class Widget(QtWidgets.QWidget):
def __init__(self, parent = None):
super().__init__(parent)
self._text = None
def setText(self, text):
self._text = text
def paintEvent(self, event):
if self._text is None:
return
painter = QtGui.QPainter(self)
rect = self.rect()
# test clipping
# rect.setHeight(rect.height() / 2)
drawText(painter, rect, self._text)
def sizeHint(self):
return QtCore.QSize(200,200)
if __name__ == "__main__":
app = QtWidgets.QApplication([])
lorem = "Lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua"
widget = Widget()
widget.setFont(QtGui.QFont("Arial", 12))
widget.setText(replaceSomeSpaces(lorem, 3))
widget.show()
app.exec()

Related

How to swap the position of the 2 buttons by dragging in PyQt5 [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I am trying to make small program in Python,using PyQt5.The program will as have many button has arranged neatly.When dragging one of the buttons to another button, you can swap their positions while the buttons are still arranged neatly.I tried many ways, but in the end the buttons couldn’t be arranged neatly.What should I do to achieve this effect
Sounds like a job for QEventFilter.
Simple version:
from PyQt5.QtCore import QEvent, QObject
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout, QPushButton
class Filter(QObject):
def __init__(self, parent = None):
super().__init__(parent)
self._pressed = None
self._drag = False
def setDragMode(self, active):
""" When drag mode activated buttons are not clickable (mouse events are filtered) """
self._drag = active
def setLayout(self, layout):
self._layout = layout
for i in range(layout.count()):
item = layout.itemAt(i)
if item.widget():
item.widget().installEventFilter(self)
def eventFilter(self, obj, event):
if not self._drag:
return False
if event.type() == QEvent.MouseButtonPress:
if isinstance(obj, QPushButton):
self._pressed = obj
return True
elif event.type() == QEvent.MouseMove:
button = self._pressed
layout = self._layout
if button is None:
return True
pos = obj.mapToParent(event.pos())
index = layout.indexOf(button)
geometry = button.geometry()
if pos.x() < geometry.topLeft().x():
if index - 1 >= 0:
prev = layout.itemAt(index - 1).widget().geometry()
if prev.topRight().x() > pos.x():
layout.insertWidget(index - 1, button)
elif pos.x() > geometry.topRight().x():
if index + 1 < layout.count():
next_ = layout.itemAt(index + 1).widget().geometry()
if next_.topLeft().x() < pos.x():
layout.insertWidget(index + 1, button)
return True
elif event.type() == QEvent.MouseButtonRelease:
self._pressed = None
return True
return False
if __name__ == "__main__":
app = QApplication([])
widget = QWidget()
layout = QHBoxLayout()
buttons = [QPushButton("button {}".format(i)) for i in range(5)]
for button in buttons:
layout.addWidget(button)
widget.setLayout(layout)
widget.show()
filter_ = Filter()
filter_.setLayout(layout)
filter_.setDragMode(True)
app.exec_()
More intuitive and interactive version:
from PyQt5.QtCore import QEvent, QObject
from PyQt5.QtGui import QPainter
from PyQt5.QtWidgets import QWidget, QApplication, QHBoxLayout, QPushButton
class Overlay(QWidget):
def __init__(self, parent = None):
super().__init__(parent)
self._widget = None
self._pressPos = None
self._topLeftPos = None
self._movePos = None
self._pixmap = None
def setDragWidget(self, widget, pressPos, topLeftPos):
self._widget = widget
self._pressPos = pressPos
self._movePos = pressPos
self._topLeftPos = topLeftPos
self._pixmap = widget.grab()
def mouseMoved(self, pos):
self._movePos = pos
self.update()
def mouseReleased(self):
self._pixmap = None
self.update()
def paintEvent(self, event):
if self._pixmap is None:
return
painter = QPainter(self)
painter.drawPixmap(self._topLeftPos + self._movePos - self._pressPos, self._pixmap)
def swap_widgets(layout, index1, index2):
if index1 == index2:
return
widget1 = layout.itemAt(index1).widget()
widget2 = layout.itemAt(index2).widget()
layout.insertWidget(index1, widget2)
layout.insertWidget(index2, widget1)
class Filter(QObject):
def __init__(self, parent = None):
super().__init__(parent)
self._pressed = None
self._drag = False
self._overlay = None
def setDragMode(self, active):
""" When drag mode activated buttons are not clickable (mouse events are filtered) """
self._drag = active
def setLayout(self, layout):
self._layout = layout
for i in range(layout.count()):
item = layout.itemAt(i)
if item.widget():
item.widget().installEventFilter(self)
def eventFilter(self, obj, event):
if not self._drag:
return False
if event.type() == QEvent.MouseButtonPress:
if isinstance(obj, QPushButton):
self._pressed = obj
parent = obj.parent()
overlay = Overlay(parent)
overlay.setGeometry(parent.rect())
pressPos = obj.mapToParent(event.pos())
overlay.setDragWidget(obj, pressPos, obj.geometry().topLeft())
overlay.show()
self._overlay = overlay
return True
elif event.type() == QEvent.MouseMove:
button = self._pressed
layout = self._layout
if button is None:
return True
pos = obj.mapToParent(event.pos())
overlay = self._overlay
if overlay is not None:
overlay.mouseMoved(pos)
return True
elif event.type() == QEvent.MouseButtonRelease:
overlay = self._overlay
button = self._pressed
layout = self._layout
if overlay is not None:
overlay.mouseReleased()
overlay.hide()
pos = obj.mapToParent(event.pos())
index1 = layout.indexOf(button)
for index2 in range(layout.count()):
widget = layout.itemAt(index2).widget()
if widget is None:
continue
if widget.geometry().contains(pos):
swap_widgets(layout, index1, index2)
break
self._pressed = None
return True
return False
if __name__ == "__main__":
app = QApplication([])
widget = QWidget()
layout = QHBoxLayout()
buttons = [QPushButton("button {}".format(i)) for i in range(5)]
for button in buttons:
layout.addWidget(button)
widget.setLayout(layout)
widget.show()
filter_ = Filter()
filter_.setLayout(layout)
filter_.setDragMode(True)
app.exec_()

How do I connect WindowContextHelpButtonHint to a widget click?

I can add self.setWindowFlags(Qt.Window | Qt.WindowContextHelpButtonHint | Qt.WindowCloseButtonHint) to the constructor of my QMainWindow which adds the '?' button. Now I want to add contextual help information to virtually all UI components. How do I connect a click on the '?' button and then a click on some ui element (say a qlistview) such that a help message pops up?
Below is part of my program stripped to make it easy. How do I make it such that when the user clicks the '?' and then either a QDoubleSpinBox or the QListView or any of the QPushButtons that a help message displays?
I have searched for this but it seems the vast amount of questions and answers are about how to remove the '?' button or how to add it. None actually deal with how to alter the button's behaviour.
I've also tried using setInputMethodHints() to assign hints to ui components but this method doesn't accept custom strings as I assumed it would.
Btw: I also would like the '?' button to not replace the minimization button. I have yet to figure out how to achieve this either.
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
APPNAME = 'Mesoscale Brain Explorer'
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.setWindowTitle(APPNAME)
self.sidebar = Sidebar()
self.setWindowFlags(Qt.Window | Qt.WindowContextHelpButtonHint | Qt.WindowCloseButtonHint)
self.setup_ui()
def setup_ui(self):
self.pl_frame = QFrame()
splitter = QSplitter(self)
self.enable = lambda yes: splitter.setEnabled(yes)
splitter.setHandleWidth(3)
splitter.setStyleSheet('QSplitter::handle {background: #cccccc;}')
splitter.addWidget(self.sidebar)
splitter.addWidget(self.pl_frame)
self.setCentralWidget(splitter)
self.menu = self.menuBar()
m = self.menu.addMenu('&File')
a = QAction('&New project', self)
a.setShortcut('Ctrl+N')
a.setStatusTip('Create new project')
m.addAction(a)
a = QAction('&Open project', self)
a.setShortcut('Ctrl+O')
a.setStatusTip('Open project')
m.addAction(a)
a = QAction("&Quit", self)
a.setShortcut("Ctrl+Q")
a.setStatusTip('Leave The App')
a.setIcon(QIcon('pics/quit.png'))
m.addAction(a)
about_action = QAction('&About ' + APPNAME, self)
about_action.setStatusTip('About ' + APPNAME)
about_action.setShortcut('F1')
m = self.menu.addMenu('&Project')
m.setEnabled(False)
a = QAction("&Close", self)
a.setStatusTip('Close project')
m.addAction(a)
self.project_menu = m
help_menu = self.menu.addMenu('&Help')
help_menu.addAction(about_action)
class Sidebar(QWidget):
open_pipeconf_requested = pyqtSignal()
open_datadialog_requested = pyqtSignal()
automate_pipeline_requested = pyqtSignal()
x_origin_changed = pyqtSignal(float)
y_origin_changed = pyqtSignal(float)
units_per_pixel_changed = pyqtSignal(float)
def __init__(self, parent=None):
super(Sidebar, self).__init__(parent)
self.x_origin = QDoubleSpinBox()
self.y_origin = QDoubleSpinBox()
self.units_per_pixel = QDoubleSpinBox()
self.setup_ui()
def setup_ui(self):
self.setContentsMargins(4, 6, 5, 0)
vbox = QVBoxLayout()
hbox = QHBoxLayout()
hbox.addWidget(QLabel('Origin:'))
hbox.addWidget(QLabel('Units per pixel:'))
vbox.addLayout(hbox)
hbox2 = QHBoxLayout()
hhbox = QHBoxLayout()
hhbox2 = QHBoxLayout()
hhbox.addWidget(QLabel("X:"))
hhbox.addWidget(self.x_origin)
hhbox.addWidget(QLabel("Y:"))
hhbox.addWidget(self.y_origin)
hhbox2.addWidget(self.units_per_pixel)
hbox2.addLayout(hhbox)
hbox2.addLayout(hhbox2)
vbox.addLayout(hbox2)
self.units_per_pixel.setDecimals(5)
self.units_per_pixel.setMaximum(100000)
self.x_origin.setMaximum(100000)
self.y_origin.setMaximum(100000)
self.pl_list = QListView()
self.pl_list.setIconSize(QSize(18, 18))
self.pl_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.pl_list.setEditTriggers(QAbstractItemView.NoEditTriggers)
vbox.addWidget(QLabel('Pipeline:'))
vbox.addWidget(self.pl_list)
pb = QPushButton('&Automation')
pb.clicked.connect(self.automate_pipeline_requested)
vbox.addWidget(pb)
vbox.addSpacerItem(QSpacerItem(0, 1, QSizePolicy.Minimum, QSizePolicy.Expanding))
pb = QPushButton('&Configure Pipeline')
pb.clicked.connect(self.open_pipeconf_requested)
vbox.addWidget(pb)
pb = QPushButton('&Manage Data')
pb.clicked.connect(self.open_datadialog_requested)
vbox.addWidget(pb)
vbox.setStretch(0, 0)
vbox.setStretch(1, 0)
vbox.setStretch(2, 0)
self.setLayout(vbox)
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setApplicationName(APPNAME)
app.setOrganizationName('University of British Columbia')
w = MainWindow()
w.resize(1060, 660)
w.show()
app.exec_()
app.deleteLater()
del w
sys.exit()

how to override qscrollbar onclick default behaviour

When you click on QScrollBar's "page control area" ('c' on the image), it will scroll one page. What I want is to make it scroll to the full, just like when you choose "Scroll here" context menu item.
That's quite an interesting question. To find out an answer, we need to take a look at QScrollBar source and find out two things:
How to determine which part of the scroll bar has been clicked;
How to trigger "Scroll Here" behavior.
The answer to the first question lies in QScrollBar::mousePressEvent implementation. It turns out that QStyle::hitTestComplexControl does just what we need. What for the second question, just search "Scroll here" and you'll see that QScrollBarPrivate::pixelPosToRangeValue is used to convert event position to slider value. Unfortunately, we don't have access to functions of this private class, so we're forced to reimplement it. Now let's apply gained knowledge and implement new behavior in a subclass:
import sys
from PyQt4 import QtCore, QtGui
class ModifiedScrollBar(QtGui.QScrollBar):
def __init__(self, parent = None):
super(ModifiedScrollBar, self).__init__(parent)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.LeftButton:
opt = QtGui.QStyleOptionSlider()
self.initStyleOption(opt)
control = self.style().hitTestComplexControl(QtGui.QStyle.CC_ScrollBar, opt,
event.pos(), self)
if (control == QtGui.QStyle.SC_ScrollBarAddPage or
control == QtGui.QStyle.SC_ScrollBarSubPage):
# scroll here
gr = self.style().subControlRect(QtGui.QStyle.CC_ScrollBar, opt,
QtGui.QStyle.SC_ScrollBarGroove, self)
sr = self.style().subControlRect(QtGui.QStyle.CC_ScrollBar, opt,
QtGui.QStyle.SC_ScrollBarSlider, self)
if self.orientation() == QtCore.Qt.Horizontal:
pos = event.pos().x()
sliderLength = sr.width()
sliderMin = gr.x()
sliderMax = gr.right() - sliderLength + 1
if (self.layoutDirection() == QtCore.Qt.RightToLeft):
opt.upsideDown = not opt.upsideDown
else:
pos = event.pos().y()
sliderLength = sr.height()
sliderMin = gr.y()
sliderMax = gr.bottom() - sliderLength + 1
self.setValue(QtGui.QStyle.sliderValueFromPosition(
self.minimum(), self.maximum(), pos - sliderMin,
sliderMax - sliderMin, opt.upsideDown))
return
return super(ModifiedScrollBar, self).mousePressEvent(event)
def main():
app = QtGui.QApplication(sys.argv)
edit = QtGui.QTextEdit()
#uncomment for testing horizontal scrollbar
#edit.setLineWrapMode(QtGui.QTextEdit.NoWrap)
edit.setPlainText("Lorem ipsum...")
edit.setVerticalScrollBar(ModifiedScrollBar())
edit.setHorizontalScrollBar(ModifiedScrollBar())
edit.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Works for me, python 3.4 pyqt 5.4.
pixelPosToRangeValue is taken from qt source
def wrapEF(ef):
w = QObject()
w.eventFilter = ef
return w
def sbEventFilter(s, e):
q = s
if (e.type() == QEvent.MouseButtonPress and e.button() == Qt.LeftButton
or e.type() == QEvent.MouseButtonDblClick):
#pixelPosToRangeValue(pos)
opt = QStyleOptionSlider()
q.initStyleOption(opt)
gr = q.style().subControlRect(QStyle.CC_ScrollBar, opt,
QStyle.SC_ScrollBarGroove, q)
sr = q.style().subControlRect(QStyle.CC_ScrollBar, opt,
QStyle.SC_ScrollBarSlider, q)
if q.orientation() == Qt.Horizontal:
sliderLength = sr.width()
sliderMin = gr.x()
sliderMax = gr.right() - sliderLength + 1
if q.layoutDirection() == Qt.RightToLeft:
opt.upsideDown = not opt.upsideDown
dt = sr.width()/2
pos = e.pos().x()
else:
sliderLength = sr.height()
sliderMin = gr.y()
sliderMax = gr.bottom() - sliderLength + 1
dt = sr.height()/2
pos = e.pos().y()
r = QStyle.sliderValueFromPosition(q.minimum(), q.maximum(),
pos - sliderMin - dt,
sliderMax - sliderMin, opt.upsideDown)
#pixelPosToRangeValue,
q.setValue(r)
return q.eventFilter(s, e)
self.scrollBarEF = wrapEF(sbEventFilter)
self.hscrollbar.installEventFilter(self.scrollBarEF)
self.vscrollbar.installEventFilter(self.scrollBarEF)

Custom Qt QMenu

Is there a way in Qt to add a layout or widgets to a QMenu to create a custom menu?
The example below (left) is what I have, and I'd like to aim for something similar to the mock-up on the right, by adding non-menu widgets. If it can't be done by QMenu, are there guides to produce similar results (perhaps by having a more standard widget act as a context menu) anywhere?
Sure there is! In Qt, if there is a a will there is a way.
You will probably need to make your own class that uses QMenu and uses a member QListWidget.
You will then need to generate the layout and overload all the correct QLayout functions for size recalculation.
And then you'll need to use this layout (think QHBoxLayout) to display both a QMenu and a QListWidget side by side.
That should be enough to point you in the right direction.
EDIT:
As a commenter pointed out, you can't inherit two QObject things so I updated the
answer accordingly.
To customize menu items you can use QWidgetAction class. But you want to customize menu to look like popup widget. So you may subclass QMenu and try to improve layout of menu for your needs (QMenu is QWidget). You questoin is not clear.
I wrote a script, you can try it.
but I am not subclass QMenu.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from PySide.QtGui import *
from PySide.QtCore import *
class MenuItem(QWidget):
"""docstring for MenuItem"""
def __init__(self, text='test', icon=None, parent=None):
super(MenuItem, self).__init__(parent)
hbox = QHBoxLayout(self)
# hbox.setContentsMargins(0, 0, 0, 0)
label = QLabel(text)
btn = QPushButton()
if icon:
btn.setIcon(icon)
hbox.addWidget(label)
hbox.addStretch()
hbox.addWidget(btn)
self.setMinimumWidth(parent.width())
class MyMenu(QWidget):
"""docstring for MyMenu"""
def __init__(self, parent=None):
super(MyMenu, self).__init__(parent)
self.main_width = 200
self.main_height = 150
self.close_menu = False
self.parent = parent
self.setGeometry(0, 0, 200, 150)
self.initUI()
self.setWindowFlags(Qt.Popup)
# self.setWindowModality(Qt.WindowModal)
def initUI(self):
main_frame = QWidget(self)
main_v_layout = QVBoxLayout(main_frame)
main_v_layout.setContentsMargins(0, 0, 0, 0)
item_1 = MenuItem('item 1', parent=self)
item_2 = MenuItem('item 2', parent=self)
item_3 = MenuItem('item 3', parent=self)
main_v_layout.addWidget(item_1)
main_v_layout.addWidget(item_2)
main_v_layout.addWidget(item_3)
def animationShow(self):
self.close_menu = False
self.start_close_menu = True
self.show()
# PyQt4.QtCore.QRect(0, 0, 400, 23)
rect = self.parent.rect()
# PyQt4.QtCore.QPoint(199, 11)
center_pos = rect.center()
# PyQt4.QtCore.QPoint(654, 465)
global_center_pos = self.parent.mapToGlobal(center_pos)
height = rect.height()
show_pos = QPoint(
global_center_pos.x() - (self.width() / 2),
global_center_pos.y() + height)
# print show_pos
self.move(show_pos)
self.inAnimation(show_pos)
def inAnimation(self, show_pos=None):
start_height = QSize(self.main_width, 0)
end_height = QSize(self.main_width, self.main_height)
size_anim = QPropertyAnimation(self, 'size')
size_anim.setStartValue(start_height)
size_anim.setEndValue(end_height)
size_anim.setDuration(160)
size_anim.setEasingCurve(QEasingCurve.OutQuad)
opacity_anim = QPropertyAnimation(self, 'windowOpacity')
opacity_anim.setStartValue(0.0)
opacity_anim.setEndValue(1.0)
opacity_anim.setDuration(260)
opacity_anim.setEasingCurve(QEasingCurve.OutQuad)
self.in_anim_group = QParallelAnimationGroup()
self.in_anim_group.addAnimation(size_anim)
self.in_anim_group.addAnimation(opacity_anim)
self.in_anim_group.start()
def outAnimation(self):
try:
end_size = QSize(self.size().width(), 0)
pos_anim = QPropertyAnimation(self, 'size')
pos_anim.setEndValue(end_size)
pos_anim.setDuration(200)
pos_anim.setEasingCurve(QEasingCurve.InQuad)
opacity_anim = QPropertyAnimation(self, 'windowOpacity')
opacity_anim.setStartValue(1.0)
opacity_anim.setEndValue(0.0)
opacity_anim.setDuration(200)
opacity_anim.setEasingCurve(QEasingCurve.InQuad)
self.out_anim_group = QParallelAnimationGroup()
self.out_anim_group.addAnimation(pos_anim)
self.out_anim_group.addAnimation(opacity_anim)
self.out_anim_group.finished.connect(self.closeMenu)
self.out_anim_group.start()
except RuntimeError as e:
pass
except Exception as e:
print e
def closeMenu(self):
self.close_menu = True
self.setVisible(False)
def closeEvent(self, event):
# super(MyMenu, self).closeEvent(event)
if self.start_close_menu:
self.outAnimation()
self.start_close_menu = False
def hideEvent(self, event):
# print 'hideEvent', event
super(MyMenu, self).hideEvent(event)
def setVisible(self, visible):
if self.close_menu:
visible = False
elif not visible:
visible = True
super(MyMenu, self).setVisible(visible)
class Win(QWidget):
"""docstring for Win"""
def __init__(self):
super(Win, self).__init__()
vbox = QVBoxLayout(self)
btn = QPushButton('call menu')
vbox.addWidget(btn)
self.menu = MyMenu(btn)
btn.clicked.connect(self.menu.animationShow)
if __name__ == '__main__':
app = QApplication(sys.argv)
win = Win()
win.show()
sys.exit(app.exec_())

AttributeError: 'QTextEdit' object has no attribute 'text'

Sometimes I need to make multiple copies of code with incrementing numbers.
In a form I'm coding, I need to make upwards of 12 checkboxes, each of which requires the following code:
self.checkBox1 = QtGui.QCheckBox()
self.checkBox1.setGeometry(QtCore.QRect(20, 20, 70, 17))
self.checkBox1.setObjectName(_fromUtf8("checkBox1"))
The script below enables me to avoid the boring task of manually changing the numbers for each checkbox.
I just copy the 3 lines above into the windows clipboard, and then...
I insert "checkBox" into the first field in the form, "1" into the second field, and 12 into the third field.
When I hit the "ok" button, the 12 sequentially numbered copies of the 3 lines appear in the 4th field in the form.
I hope this provides some help to other people.
Marc
Here's my code:
# -*- coding: latin-1 -*-
"""
duplicate_text_with_incrementing_nos_for_programming_and_paste_to_clipboard.py
Harvest text from clipboard and run functions below, and then paste back to clipboard
"""
import sys
from PyQt4 import QtCore
from PyQt4 import QtGui
from PyQt4.QtCore import (Qt, SIGNAL)
from PyQt4.QtGui import (QApplication, QDialog, QHBoxLayout, QLabel,
QPushButton)
class Form(QDialog):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
self.initUI()
def initUI(self):
okButton01 = QtGui.QPushButton("OK")
cancelButton01 = QtGui.QPushButton("Cancel")
okButton01.clicked.connect(self.fn_okButton01_clicked)
cancelButton01.clicked.connect(QtCore.QCoreApplication.instance().quit)
self.cancelButton01 = cancelButton01
prefix_label = QtGui.QLabel('Prefix')
digit_label = QtGui.QLabel('Digit')
iterations_label = QtGui.QLabel('Iterations')
clip_label = QtGui.QLabel('Clip')
prefixEdit = QtGui.QLineEdit()
digitEdit = QtGui.QLineEdit()
iterationsEdit = QtGui.QLineEdit()
reviewEdit = QtGui.QTextEdit()
self.prefix_label = prefix_label
self.digit_label = digit_label
self.iterations_label = iterations_label
self.clip_label = clip_label
self.prefixEdit = prefixEdit
self.digitEdit = digitEdit
self.iterationsEdit = iterationsEdit
self.reviewEdit = reviewEdit
hbox01 = QtGui.QHBoxLayout()
hbox01.addWidget(prefix_label)
hbox01.addWidget(prefixEdit)
hbox01.addWidget(digit_label)
hbox01.addWidget(digitEdit)
hbox01.addWidget(iterations_label)
hbox01.addWidget(iterationsEdit)
hbox03 = QtGui.QHBoxLayout()
hbox03.addWidget(clip_label)
hbox03.addWidget(reviewEdit)
self.reviewEdit.setText(fn_getText())
hbox00 = QtGui.QHBoxLayout()
hbox00.addStretch(1)
hbox00.addWidget(okButton01)
hbox00.addWidget(cancelButton01)
vbox0 = QtGui.QVBoxLayout()
vbox0.addLayout(hbox01)
vbox0.addStretch(1)
vbox0.addLayout(hbox03)
vbox0.addStretch(1)
vbox0.addLayout(hbox00)
self.setLayout(vbox0)
self.setGeometry(300, 300, 600, 300) #class PySide.QtCore.QRectF(left, top, width, height) http://srinikom.github.com/pyside-docs/PySide/QtCore/QRectF.html#PySide.QtCore.QRectF
self.setWindowTitle('Duplicate Code Strings W/Increasing Numbers')
self.show()
def fn_okButton01_clicked(self):
prefixEditText = str(self.prefixEdit.text())
digitEditText = str(self.digitEdit.text())
iterationsEditText = str(self.iterationsEdit.text())
nutext = prefixEditText + ' ' + digitEditText + ' ' + iterationsEditText
print 'Line 89: nutext = ' + str(nutext)
original_clip = self.reviewEdit.toPlainText() # PySide.QtGui.QLineEdit.text(), http://srinikom.github.com/pyside-docs/PySide/QtGui/QLineEdit.html
txt2paste2clipbd = fn_duplicate_code_with_increments(texte=str(original_clip), no_of_copies=str(iterationsEditText), string_b4_digits=str(prefixEditText), digits=str(digitEditText))
self.reviewEdit.setPlainText(txt2paste2clipbd)
setWinClipText(txt2paste2clipbd)
#self.deleteLater()
#event.accept() #http://www.qtcentre.org/threads/20895-PyQt4-Want-to-connect-a-window-s-close-button
#self.destroy()
def formm():
app = QApplication(sys.argv)
form = Form()
form.show()
app.exec_()
def fn_getText():
# get text from clipboard
win32clipboard.OpenClipboard()
text = win32clipboard.GetClipboardData(win32clipboard.CF_TEXT)
win32clipboard.CloseClipboard()
return text
def setWinClipText(aString):
# Send text to clipboard
import win32clipboard
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardText(aString)
win32clipboard.CloseClipboard()
def fn_duplicate_code_with_increments(texte, no_of_copies, string_b4_digits, digits):
"""
to do: combine args 2 and 3, and use re module to determine with chars are the digits to increment
"""
import re
import string
i = 0
tempclipDup = texte[:]
temp_instance = ''
accumulator = ''
while i <= int(no_of_copies) - 1:
i +=1
orig_str = string_b4_digits + str(digits)
replact_st = string_b4_digits + str(i)
temp_instance = tempclipDup.replace(orig_str, replact_st)
if len(accumulator) > 2:
accumulator = accumulator + '\n' + temp_instance
else:
accumulator = temp_instance
return accumulator
if 1 == 1:
import os
import sys
import subprocess
import win32clipboard
import win32con
fn_operation_log = ''
arg_sent_2this_script = ''
alternative = 1
if alternative == 1:
formm()
elif alternative == 2:
txt2paste2clipbd = fn_duplicate_code_with_increments(texte=fn_getText(), no_of_copies=3, string_b4_digits='hbox', digits=1)
setWinClipText(txt2paste2clipbd)
The property is called plainText for QTextEdit.
(Contrary to the single line QLineEdit, which has a text property)

Resources