Related
My research for this issue returned answers that confirmed that FreeCAD GUI (or Qt) does not allow multiple activeDialog instances and that while multiple dialogs could be implemented I do not need that, I only want to load one activeDialog.
As I have tried to call from a new empty document, I believe that there should be no existing control (e.i. activeDialog) conflicting with the instance I am trying to create. I am not aware of multiple calls in the code and I don't know of a method to show an existing activeDialog in a document.
In this project I am learning to use FreeCAD and pySide so am not familiar with all conventions, anomalies etc. I am using FreeCAD, pySide and python (3.10 for freeCAD) Macos 10.14. I have created UI's both with code and in Qt Designer and consistently get the same behavior. I have cut and pasted multiple examples and have gotten the same behavior. I know it's something simple, I simply haven't found it yet.
The code is being developed so is not pretty, I can refactor as I learn and can go further.
Error occurs at :
FreeCADGui.Control.showDialog(panel)
with:
<class 'RuntimeError'>:Active task dialog found
from PySide import QtGui, QtCore
import Part, PartGui
import FreeCAD as App
class OffsetCalc(QtGui.QDialog):
def __init__(self):
super(OffsetCalc, self).__init__()
self.initUI()
def __str__(self):
return "OffsetCalc([])"
def __str__(self):
return "intUI([])"
def initUI(self):
self.result = userCancelled
# setting font and size
# create our window
# define window xLoc,yLoc,xDim,yDim
self.setGeometry( 850, 550, 250, 250)
self.setWindowTitle("Ship Offset Calculator")
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
# creating a label widget
# by default label will display at top left corner
# The beginning of the coordinate system is at the left top corner.
# The x values grow from left to right. The y values grow from top to bottom.
self.lSta = QtGui.QLabel("Station", self)
self.lSta.setFont('Ariel') # set to a non-proportional font
self.lSta.move(20, 20)
self.lSta = QtGui.QLabel("H.B/W.L.", self)
self.lSta.setFont('Ariel') # set to a non-proportional font
self.lSta.move(20, 50)
self.lSta = QtGui.QLabel("Feet", self)
self.lSta.setFont('Ariel') # set to a non-proportional font
self.lSta.move(20, 80)
self.lSta = QtGui.QLabel("Inches", self)
self.lSta.setFont('Ariel') # set to a non-proportional font
self.lSta.move(20, 110)
self.lSta = QtGui.QLabel("Eights", self)
self.lSta.setFont('Ariel') # set to a non-proportional font
self.lSta.move(20, 140)
# numeric input field
self.ista = QtGui.QLineEdit("Station", self)
self.ista.setInputMask("999")
#self.ista.setText("000")
self.ista.setFixedWidth(50)
self.ista.move(100, 20)
self.iwlht = QtGui.QLineEdit(self)
self.iwlht.setInputMask("999")
#self.iwlht.setText("000")
self.iwlht.setFixedWidth(50)
self.iwlht.move(100, 50)
self.ifeet = QtGui.QLineEdit(self)
self.ifeet.setInputMask("999")
#self.ifeet.setText("000")
self.ifeet.setFixedWidth(50)
self.ifeet.move(100, 80)
self.iinch = QtGui.QLineEdit(self)
self.iinch.setInputMask("999")
#self.iinch.setText("000")
self.iinch.setFixedWidth(50)
self.iinch.move(100, 110)
self.ieight = QtGui.QLineEdit(self)
self.ieight.setInputMask("999")
#self.ieight.setText("000")
self.ieight.setFixedWidth(50)
self.ieight.move(100, 140)
self.bok = QtGui.QPushButton("OK", self)
self.bok.clicked.connect(self.onbok)
self.bok.move(20, 200)
self.hbht = QtGui.QRadioButton("Calc H.B", self)
self.hbht.move(150, 205)
self.show()
def onbok(self):
sta = float(self.ista.text())
wlht = float(self.iwlht.text())
feet = float(self.ifeet.text())
inches = float(self.iinch.text())
eights = float(self.ieight.text())
inches8 = inches*8 # number of 1/8's in Inches column
dec_ft = (inches8 + eights)/96
total_ft = feet + dec_ft
# doc = App.activeDocument()
#p = Part.Point
p = App.ActiveDocument.addObject("Part::Vertex", "p1")
p.Y = sta # Station is always Y
if self.hbht.isChecked: #True =hb / False =ht
# use calced X
print("Using calc X")
p.X = total_ft
p.Z = wlht
else:
# use calced Z
print("Using calced Z")
p.X = wlht
p.Z = total_ft
App.ActiveDocument.recompute()
# print("Station = ", sta, "Height = ", wlht, "Feet = ", feet, "Inches = ", inches, "Eights + ", eights)
# print("Eights of Inches = ", inches8, "Dec Ft. = ", dec_ft, "Total = ", total_ft)
self.result = userOK
self.close()
doc=App.activeDocument()
p = Part.Point()
p.Y = sta # Station is always Y
if self.hbht.isChecked: #True =hb / False =ht
# use calced X
print("Using calc X")
p.X = total_ft
p.Z = wlht
else:
# use calced Z
print("Using calced Z")
p.X = wlht
p.Z = total_ft
doc.recompute()
def add_X_Point(self):
doc=App.activeDocument()
p = Part.Point
p.Y = Sta
p.X = hb
p.Z = total_ft
doc.recompute()
def onCancel(self):
self.result = userCancelled
self.close()
def onOk(self):
self.result = userOK
self.close()
def mousePressEvent(self, event):
# print mouse position, X & Y
print("X = ", event.pos().x())
print("Y = ", event.pos().y())
#
userCancelled = "Cancelled"
userOK = "OK"
form = OffsetCalc()
form.exec_()
I am trying to code the following strategy:
Buy:
(1) signal one being the 50 ema crossing over the 200 ema.
(2) signal two, wait for three red candles, with the final candle closing under the 50 ema.
(3) signal three, next candle is immediately engulfing.
Only buy when all three signals are true.
The strategy is the same for short positions expect with reversed logic.
I have managed to input (2) and (3) with the following but I am struggling with the code required for signal 1.
Any help/tips would be appreciated.
//#version=5
//Indicators
strategy("Enculfing Candles Long", overlay=true, initial_capital = 2000, default_qty_value = 100, default_qty_type = strategy.percent_of_equity)
fastma = ta.ema(close, 50)
slowma = ta.ema(close, 200)
atr = ta.atr(14)
plot(fastma, "fastma", color=color.blue)
plot(slowma, "slowma", color= color.yellow)
//Buy Conditions
lowerClose1 = (close[1]<close[2]) and (close[1]<fastma)
lowerClose2 = (close[2]<close[3]) and (close[2]>fastma)
lowerClose3 = (close[3]<close[4]) and close[3]>fastma
higherClose = close>open[1]
buysignal1 = (fastma>slowma)
buysignal2 = close>fastma
longcond1 = buysignal1 and buysignal2 and lowerClose1 and lowerClose2 and lowerClose3 and higherClose
//Strategy Execution
timePeriod = time>= timestamp(syminfo.timezone, 2021, 01, 01, 0, 0)
InTrade = strategy.position_size > 0
notInTrade = strategy.position_size <= 0
strategy.entry("buy", strategy.long, when= notInTrade and longcond1)
strategy.exit(id="buy", loss = (atr*200000), profit = (atr*300000))
I am encountering a strange issue! I am trying to plot using groupedbar but I facing this strange issue. here is the code to generate the data and plot it:
nam = string.(repeat(1:20, outer=2))
sx = repeat(["Pre-Polarization", "Post-Polarization"], inner = 20)
c = 1:40
groupedbar(nam, c, group = sx, xlabel = "Groups", ylabel = "Scores",
title = "Scores by group and category", bar_width = 0.9,
lw = 0, framestyle = :box)
And I get the following results:
Does anybody know the reason it's happening?
The reason the X axis values look strange is the Julia is sorting the numbers as strings, not as numbers. This means, for example, that "3" > "20" in your code for nam.
To fix this you should not stringify nam before it is plotted. So use
nam = repeat(1:20, outer=2)
in the above code.
If strings are required, you can exploit the fact that space comes before numbers in lexicographical sort, such that " 3" < "10"
For example:
nam = (repeat(1:20, outer=2))
# Space sorts before numbers
nam = [ n >= 20 ? "$n" :
n >= 10 ? " $n" :
" $n"
for n in nam]
sx = repeat(["Pre-Polarization", "Post-Polarization"], inner = 20)
c = 1:40
groupedbar(nam, c, group = sx, xlabel = "Groups", ylabel = "Scores",
title = "Scores by group and category", bar_width = 0.9,
lw = 0, framestyle = :box)
The keen-eyed observer might notice a slight misalignment of the numbers now. This can be fixed by using either U+200B Zero Width Space or an U+2063 Invisible Seperator in place of the regular space, though it would make the code harder to read.
In my game after pressing the start button I wanted to hide the button to stop the user from pressing the start button again and making more bubbles appear. I tried to make a variable equal to 0. Then every time the start button was clicked the value would go up by 1. If the value was 2 then the start button would disappear or become inactive. By trying to add this 'hiding button code' its only showing one bubble on my screen when the start button is clicked. Oh and the started button doesn't hide... ._.
from tkinter import *
import random
import tkinter as tk
# Message box for score to tell user if they have won or lost
from tkinter.messagebox import showinfo
class BubbleFrame:
def __init__(self, root, name):
self.name = name
root.title("Math Bubbles")
self.bubbles = {} # this will hold bubbles ids, positions and velocities
self.score = 0
self.buttonclick = 0 # Newly added in attempt to hide button
Button(root, text="Start", width=8, bg="Pink", command=self.make_bubbles).pack() # This button starts the game, making the bubbles move across the screen
Button(root, text="Quit", width=8, bg="Yellow",command=quit).pack()
self.canvas = Canvas(root, width=800, height=650, bg='#afeeee')
self.canvas.create_text(400, 30, fill="darkblue", font="Times 20 italic bold", text="Click the bubbles that are answers in the two times tables.")
#Shows score at beinging of the game
self.current_score = self.canvas.create_text(200, 60, fill="darkblue", font="Times 15 italic bold", text="Your score is: 0")
self.canvas.pack()
def make_bubbles(self):
for each_no in range(1, 21):
self.buttonclick += 1 #Newly added in attempt to hide button
xval = random.randint(5, 765)
yval = random.randint(5, 615)
COLOURS = ('#00ff7f', '#ffff00', '#ee82ee', '#ff69b4', '#fff0f5') # CAPS represents a constant variable
colour = random.choice(COLOURS) # This picks a colour randomly
oval_id = self.canvas.create_oval(xval, yval, xval + 60, yval + 60,fill=colour, outline="#000000", width=5, tags="bubble")
text_id = self.canvas.create_text(xval + 30, yval + 30, text=each_no, tags="bubble")
self.canvas.tag_bind("bubble", "<Button-1>", lambda x: self.click(x))
self.bubbles[oval_id] = (xval, yval, 0, 0, each_no, text_id) # add bubbles to dictionary
if buttonclick == 2: #Newly added in attempt to hide button
showinfo("Oh No", "Sorry %s, but the game is already started" % self.name)
def click(self, event):
if self.canvas.find_withtag(CURRENT):
item_uid = event.widget.find_closest(event.x, event.y)[0]
is_even = False
try: # clicked oval
self.bubbles[item_uid]
except KeyError: # clicked oval
for key, value in self.bubbles.iteritems():
if item_uid == value[5]: # comparing to text_id
if value[4] % 2 == 0:
is_even = True
self.canvas.delete(key) # deleting oval
self.canvas.delete(item_uid) # deleting text
else:
if self.bubbles[item_uid][4] % 2 == 0:
is_even = True
self.canvas.delete(item_uid) # deleting oval
self.canvas.delete(self.bubbles[item_uid][5]) # deleting text
if is_even:
self.score += 1
else:
self.score -= 1
showinfo("Oh no!", "%s! You clicked the wrong bubble, please start again." % self.name)
if self.score == 10:
#Tells user You won! if score is 10
showinfo("Winner", "You won %s!" % self.name)
self.canvas.delete(self.current_score)
#Shows updated score on canvas
self.current_score = self.canvas.create_text(200, 60, fill="darkblue", font="Times 15 italic bold", text="Your score is: %s"%self.score)
def bubble_move(self, root):
for oval_id, (x, y, dx, dy, each_no, text_id) in self.bubbles.items():
# update velocities and positions
dx += random.randint(-1, 1)
dy += random.randint(-1, 1)
# dx and dy should not be too large
dx, dy = max(-5, min(dx, 5)), max(-5, min(dy, 5))
# bounce off walls
if not 0 < x < 770:
dx = -dx
if not 0 < y < 620:
dy = -dy
# apply new velocities
self.canvas.move(oval_id, dx, dy)
self.canvas.move(text_id, dx, dy)
self.bubbles[oval_id] = (x + dx, y + dy, dx, dy, each_no, text_id)
# have mainloop repeat this after 100 ms
root.after(100, self.bubble_move, root)
if __name__ == "__main__":
root = Tk()
root.title("Welcome")
Label(root, text="Welcome to Math bubbles, what is your name?").pack()
name = Entry(root)
name.pack()
def submit(name, root):
root.destroy()
root = Tk()
Label(root, text="Hello %s, press the Start button to begin.\n" % name).pack()
BubbleFrame(root, name).bubble_move(root)
Button(root, text="Ok", command=lambda: submit(name.get(), root)).pack()
root.mainloop()
I faced IndentationError in line 103 and NameError in 39 (it is self.buttonclick not buttonclick). Next time check your code before posting.
Well, you invoke 20 buttonclicks each time you click on Start button and the value checking happens in the middle of the process.
I fixed the make_bubbles method for you abit.
def make_bubbles(self):
self.buttonclick += 1 #Newly added in attempt to hide button
if self.buttonclick == 2: #Newly added in attempt to hide button
self.startbutton.configure(state="disabled")
showinfo("Oh No", "Sorry %s, but the game is already started" % self.name)
return
for each_no in range(1, 21):
...
The logic to enable the button at the end of game is missing or perhaps no need at all. Maybe I would rather clear all bubbles and generate 20 new ones each time I click the Start button. Anyway, I recommend this site for tkinter programming.
There is some more things to fix here too. No more iteritems() in Python 3. Use self.bubbles.items() instead. Good luck!
I am wondering how to best truncate text in a QLabel based on it's maximum width/height.
The incoming text could be any length, but in order to keep a tidy layout I'd like to truncate long strings to fill a maximum amount of space (widget's maximum width/height).
E.g.:
'A very long string where there should only be a short one, but I can't control input to the widget as it's a user given value'
would become:
'A very long string where there should only be a short one, but ...'
based on the required space the current font needs.
How can I achieve this best?
Here is a simple example of what I'm after, though this is based on word count, not available space:
import sys
from PySide.QtGui import *
from PySide.QtCore import *
def truncateText(text):
maxWords = 10
words = text.split(' ')
return ' '.join(words[:maxWords]) + ' ...'
app = QApplication(sys.argv)
mainWindow = QWidget()
layout = QHBoxLayout()
mainWindow.setLayout(layout)
text = 'this is a very long string, '*10
label = QLabel(truncateText(text))
label.setWordWrap(True)
label.setFixedWidth(200)
layout.addWidget(label)
mainWindow.show()
sys.exit(app.exec_())
Even easier - use the QFontMetrics.elidedText method and overload the paintEvent, here's an example:
from PyQt4.QtCore import Qt
from PyQt4.QtGui import QApplication,\
QLabel,\
QFontMetrics,\
QPainter
class MyLabel(QLabel):
def paintEvent( self, event ):
painter = QPainter(self)
metrics = QFontMetrics(self.font())
elided = metrics.elidedText(self.text(), Qt.ElideRight, self.width())
painter.drawText(self.rect(), self.alignment(), elided)
if ( __name__ == '__main__' ):
app = None
if ( not QApplication.instance() ):
app = QApplication([])
label = MyLabel()
label.setText('This is a really, long and poorly formatted runon sentence used to illustrate a point')
label.setWindowFlags(Qt.Dialog)
label.show()
if ( app ):
app.exec_()
I found that #Eric Hulser's answer, while great, didn't work when the label was put into another widget.
I came up with this by hacking together Eric's response with the Qt Elided Label Example. It should behave just like a regular label, yet elide horizontally when the text width exceeds the widget width. It has an extra argument for different elide modes. I also wrote some tests for fun :)
If you want to use PyQt5...
Change "PySide2" to "PyQt5"
Change "Signal" to "pyqtSignal"
Enjoy!
Eliding Label
# eliding_label.py
from PySide2 import QtCore, QtWidgets, QtGui
class ElidingLabel(QtWidgets.QLabel):
"""Label with text elision.
QLabel which will elide text too long to fit the widget. Based on:
https://doc-snapshots.qt.io/qtforpython-5.15/overviews/qtwidgets-widgets-elidedlabel-example.html
Parameters
----------
text : str
Label text.
mode : QtCore.Qt.TextElideMode
Specify where ellipsis should appear when displaying texts that
don’t fit.
Default is QtCore.Qt.ElideMiddle.
Possible modes:
QtCore.Qt.ElideLeft
QtCore.Qt.ElideMiddle
QtCore.Qt.ElideRight
parent : QWidget
Parent widget. Default is None.
f : Qt.WindowFlags()
https://doc-snapshots.qt.io/qtforpython-5.15/PySide2/QtCore/Qt.html#PySide2.QtCore.PySide2.QtCore.Qt.WindowType
"""
elision_changed = QtCore.Signal(bool)
def __init__(self, text='', mode=QtCore.Qt.ElideMiddle, **kwargs):
super().__init__(**kwargs)
self._mode = mode
self.is_elided = False
self.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
self.setText(text)
def setText(self, text):
self._contents = text
# This line set for testing. Its value is the return value of
# QFontMetrics.elidedText, set in paintEvent. The variable
# must be initialized for testing. The value should always be
# the same as contents when not elided.
self._elided_line = text
self.update()
def text(self):
return self._contents
def paintEvent(self, event):
super().paintEvent(event)
did_elide = False
painter = QtGui.QPainter(self)
font_metrics = painter.fontMetrics()
text_width = font_metrics.horizontalAdvance(self.text())
# layout phase
text_layout = QtGui.QTextLayout(self._contents, painter.font())
text_layout.beginLayout()
while True:
line = text_layout.createLine()
if not line.isValid():
break
line.setLineWidth(self.width())
if text_width >= self.width():
self._elided_line = font_metrics.elidedText(self._contents, self._mode, self.width())
painter.drawText(QtCore.QPoint(0, font_metrics.ascent()), self._elided_line)
did_elide = line.isValid()
break
else:
line.draw(painter, QtCore.QPoint(0, 0))
text_layout.endLayout()
if did_elide != self.is_elided:
self.is_elided = did_elide
self.elision_changed.emit(did_elide)
if __name__ == '__main__':
app = QtWidgets.QApplication([])
long_text = "this is some long text, wouldn't you say?"
elabel = ElidingLabel(long_text)
elabel.show()
app.exec_()
Test Eliding Label
# test_eliding_label.py.py
#
# Run tests with
#
# python3 -m unittest test_eliding_label.py --failfast --quiet
import unittest
import unittest.mock
from PySide2 import QtCore, QtWidgets, QtGui, QtTest
import eliding_label
if not QtWidgets.QApplication.instance():
APP = QtWidgets.QApplication([]) # pragma: no cover
class TestElidingLabelArguments(unittest.TestCase):
def test_optional_text_argument(self):
elabel = eliding_label.ElidingLabel()
self.assertEqual(elabel.text(), "")
def test_text_argument_sets_label_text(self):
elabel = eliding_label.ElidingLabel(text="Test text")
self.assertEqual(elabel.text(), "Test text")
def test_optional_elision_mode_argument(self):
elabel = eliding_label.ElidingLabel()
self.assertEqual(elabel._mode, QtCore.Qt.ElideMiddle)
class TestElidingLabel(unittest.TestCase):
def setUp(self):
self.elabel = eliding_label.ElidingLabel()
def test_elabel_is_a_label(self):
self.assertIsInstance(self.elabel, QtWidgets.QLabel)
def test_has_elision_predicate(self):
self.assertEqual(self.elabel.is_elided, False)
def test_elision_predicate_changes_when_text_width_exceeds_widget_width(self):
# NOTE: This is a bit of a stretch, inducing a paint event
# when the event loop isn't running. Throws a bunch of C++
# sourced text which can't be (easily) caught.
self.elabel.setFixedWidth(25)
self.assertEqual(self.elabel.width(), 25)
long_text = "This is line is definely longer than 25 pixels."
painter = QtGui.QPainter()
font_metrics = painter.fontMetrics()
long_text_width = font_metrics.horizontalAdvance(long_text)
self.assertGreater(long_text_width, 25)
self.elabel.setText(long_text)
x = self.elabel.x()
y = self.elabel.y()
w = self.elabel.width()
h = self.elabel.height()
paint_event = QtGui.QPaintEvent(QtGui.QRegion(x, y, w, h))
self.elabel.paintEvent(paint_event)
self.assertEqual(self.elabel.is_elided, True)
def test_text_is_elided_when_text_width_exceeds_widget_width(self):
# NOTE: This is a bit of a stretch, inducing a paint event
# when the event loop isn't running. Throws a bunch of C++
# sourced text which can't be (easily) caught.
self.elabel.setFixedWidth(25)
self.assertEqual(self.elabel.width(), 25)
long_text = "This is line is definely longer than 25 pixels."
painter = QtGui.QPainter()
font_metrics = painter.fontMetrics()
long_text_width = font_metrics.horizontalAdvance(long_text)
self.assertGreater(long_text_width, 25)
self.elabel.setText(long_text)
x = self.elabel.x()
y = self.elabel.y()
w = self.elabel.width()
h = self.elabel.height()
paint_event = QtGui.QPaintEvent(QtGui.QRegion(x, y, w, h))
self.elabel.paintEvent(paint_event)
# PySide2.QtGui.QFontMetrics.elidedText states, "If the string
# text is wider than width , returns an elided version of the
# string (i.e., a string with '…' in it). Otherwise, returns
# the original string."
self.assertEqual(self.elabel._elided_line, '…')
def test_text_is_not_elided_when_text_width_is_less_than_widget_width(self):
# NOTE: This is a bit of a stretch, inducing a paint event
# when the event loop isn't running. Throws a bunch of C++
# sourced text which can't be (easily) caught.
self.elabel.setFixedWidth(500)
self.assertEqual(self.elabel.width(), 500)
short_text = "Less than 500"
painter = QtGui.QPainter()
font_metrics = painter.fontMetrics()
short_text_width = font_metrics.horizontalAdvance(short_text)
self.assertLess(short_text_width, 500)
self.elabel.setText(short_text)
x = self.elabel.x()
y = self.elabel.y()
w = self.elabel.width()
h = self.elabel.height()
paint_event = QtGui.QPaintEvent(QtGui.QRegion(x, y, w, h))
self.elabel.paintEvent(paint_event)
# PySide2.QtGui.QFontMetrics.elidedText states, "If the string
# text is wider than width , returns an elided version of the
# string (i.e., a string with '…' in it). Otherwise, returns
# the original string."
self.assertEqual(self.elabel._elided_line, short_text)
def test_stores_full_text_even_when_elided(self):
# NOTE: This is a bit of a stretch, inducing a paint event
# when the event loop isn't running. Throws a bunch of C++
# sourced text which can't be (easily) caught.
self.elabel.setFixedWidth(25)
self.assertEqual(self.elabel.width(), 25)
long_text = "This is line is definely longer than 25 pixels."
painter = QtGui.QPainter()
font_metrics = painter.fontMetrics()
long_text_width = font_metrics.horizontalAdvance(long_text)
self.assertGreater(long_text_width, 25)
self.elabel.setText(long_text)
x = self.elabel.x()
y = self.elabel.y()
w = self.elabel.width()
h = self.elabel.height()
paint_event = QtGui.QPaintEvent(QtGui.QRegion(x, y, w, h))
self.elabel.paintEvent(paint_event)
# PySide2.QtGui.QFontMetrics.elidedText states, "If the string
# text is wider than width , returns an elided version of the
# string (i.e., a string with '…' in it). Otherwise, returns
# the original string."
self.assertEqual(self.elabel._elided_line, '…')
self.assertEqual(self.elabel.text(), long_text)
def test_has_elision_changed_signal(self):
self.assertIsInstance(self.elabel.elision_changed, QtCore.Signal)
def test_elision_changed_signal_emits_on_change_to_is_elided_predicate(self):
mock = unittest.mock.Mock()
self.elabel.elision_changed.connect(mock.method)
# NOTE: This is a bit of a stretch, inducing a paint event
# when the event loop isn't running. Throws a bunch of C++
# sourced text which can't be (easily) caught.
# Induce elision
self.elabel.setFixedWidth(150)
self.assertEqual(self.elabel.width(), 150)
long_text = "This line is definitely going to be more than 150 pixels"
painter = QtGui.QPainter()
font_metrics = painter.fontMetrics()
long_text_width = font_metrics.horizontalAdvance(long_text)
self.assertGreater(long_text_width, 150)
self.elabel.setText(long_text)
self.assertEqual(self.elabel.is_elided, False) # no elide until painting
x = self.elabel.x()
y = self.elabel.y()
w = self.elabel.width()
h = self.elabel.height()
paint_event = QtGui.QPaintEvent(QtGui.QRegion(x, y, w, h))
self.elabel.paintEvent(paint_event)
self.assertEqual(self.elabel.is_elided, True)
mock.method.assert_called_once()
# Remove elision
short_text = "Less than 150"
painter = QtGui.QPainter()
font_metrics = painter.fontMetrics()
short_text_width = font_metrics.horizontalAdvance(short_text)
self.assertLess(short_text_width, 150)
self.elabel.setText(short_text)
self.assertEqual(self.elabel.is_elided, True) # still elided until painting
x = self.elabel.x()
y = self.elabel.y()
w = self.elabel.width()
h = self.elabel.height()
paint_event = QtGui.QPaintEvent(QtGui.QRegion(x, y, w, h))
self.elabel.paintEvent(paint_event)
self.assertEqual(self.elabel.is_elided, False)
self.assertEqual(mock.method.call_count, 2)
You can achieves this through determining the width with QFontMetrics, see this answer.
You would probably want to use or create some algorithm which finds the place to cut quickly, unless doing it in a simple for loop would be sufficient.
simpler solution if you want show QLabel in center in provided area
label.setAlignment(Qt.AlignmentFlag.AlignCenter)
label.minimumSizeHint = lambda self=label: QSize(0, QLabel.minimumSizeHint(self).height() )