Highlighting portions of text in QPlainTextEdit - python

I have a list in QPlainTextEdit and want to highlight a specific line, line number 5 say.
I looked at various tutorials and examples, but the documentation of PyQt5 seems rather sparse.
Workable code, I would like to highlight lines.
For a suggestion of another widget I am also grateful. I don't want to edit this list, just show it and highlight lines.
import sys
from PyQt5.QtWidgets import *
app = QApplication(sys.argv)
main = QWidget()
main.resize(250, 150)
main.size
tbox = QPlainTextEdit(main)
for nr in range(1,5):
tbox.appendPlainText('%d'%nr)
## highlight line 2
## wait a while
## unhighlight line 2
## highlight line 4
main.show()
sys.exit(app.exec_())

If the highlighting depends on the text then the solution of #oetzi is the one indicated because although you delete lines the same text will remain highlighted, if instead the highlighting only depends on the position of the line then a possible solution is to use QSyntaxHighlighter.
In the following example you can enter whole numbers separated from spaces that indicate the position of the lines that will be highlighted (the position starts from 0):
import sys
from PyQt5.QtCore import pyqtSlot, QRegExp
from PyQt5.QtGui import QColor, QRegExpValidator, QSyntaxHighlighter, QTextCharFormat
from PyQt5.QtWidgets import (
QApplication,
QLineEdit,
QPlainTextEdit,
QVBoxLayout,
QWidget,
)
class SyntaxHighlighter(QSyntaxHighlighter):
def __init__(self, parent):
super(SyntaxHighlighter, self).__init__(parent)
self._highlight_lines = dict()
def highlight_line(self, line, fmt):
if isinstance(line, int) and line >= 0 and isinstance(fmt, QTextCharFormat):
self._highlight_lines[line] = fmt
tb = self.document().findBlockByLineNumber(line)
self.rehighlightBlock(tb)
def clear_highlight(self):
self._highlight_lines = dict()
self.rehighlight()
def highlightBlock(self, text):
line = self.currentBlock().blockNumber()
fmt = self._highlight_lines.get(line)
if fmt is not None:
self.setFormat(0, len(text), fmt)
class Widget(QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
self._lineedit = QLineEdit(textChanged=self.onTextChanged)
regex_validator = QRegExpValidator(QRegExp(r"[0-9 ]+"))
self._lineedit.setValidator(regex_validator)
self._plaintextedit = QPlainTextEdit()
self._highlighter = SyntaxHighlighter(self._plaintextedit.document())
lay = QVBoxLayout(self)
lay.addWidget(self._lineedit)
lay.addWidget(self._plaintextedit)
for i in range(10):
self._plaintextedit.appendPlainText("line %d" % i)
self.resize(320, 240)
#pyqtSlot(str)
def onTextChanged(self, text):
fmt = QTextCharFormat()
fmt.setBackground(QColor("yellow"))
self._highlighter.clear_highlight()
for e in text.split():
line = int(e)
self._highlighter.highlight_line(line, fmt)
if __name__ == "__main__":
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

import sys
from PyQt5.QtWidgets import (QPlainTextEdit, QApplication, QWidget)
app = QApplication(sys.argv)
main = QWidget()
main.resize(250, 150)
main.size
tbox = QPlainTextEdit(main)
condition = 0
for nr in range(1, 5):
if condition % 2 == 0:
tbox.appendHtml(f"<span style='background-color: yellow;'>{nr}</p>")
else:
tbox.appendHtml(f"<span style='background-color: white;'>{nr}</p>")
# tbox.appendPlainText('%d' % nr)
condition = condition + 1
main.show()
sys.exit(app.exec_())

Related

Issue in PyQt within Python - WebBrowser Project

The code below is a WebBrowser code.
The problem is when I want to switch tabs which happens in the function 'SwitchTab'. I've checked that in function 'SwitchTab', my 'Tab_Content' variable is empty-as it prints 'None'- and here is the problem. I couldn't figure out why would that be empty.(I've Bolded the partS of the code which stands for the Issue)
Thanks In Advance...
P.S: I'm using Python3.8 and PyCharm4.5.4 and Also PyQt5
import sys
import os
import json
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout,
QHBoxLayout, QPushButton, QLabel,
QLineEdit, QTabBar, QFrame, QStackedLayout, QTabWidget)
from PyQt5.QtGui import QIcon, QWindow, QImage
from PyQt5.QtCore import *
from PyQt5.QtWebEngineWidgets import *
class AddressBar(QLineEdit):
def __init__(self):
super().__init__()
def mousePressEvent(self, e):
self.selectAll()
class App(QFrame):
def __init__(self):
super().__init__()
self.CreateApp()
self.setWindowTitle("Web Browser")
self.setBaseSize(500 , 500)
def CreateApp(self):
self.layout = QVBoxLayout()
self.layout.setSpacing(0)
self.layout.setContentsMargins(0,0,0,0)
*#Creating Tabs*
self.TabBar = QTabBar(movable=True, tabsClosable=True)
self.TabBar.tabCloseRequested.connect(self.TabClose)
**self.TabBar.tabBarClicked.connect(self.SwitchTab)**
self.TabBar.setCurrentIndex(0)
*#Tabs List*
self.TabCount = 0
self.Tabs = []
*#Creating the AddressBar*
self.ToolBar = QWidget()
self.ToolBarLayout = QHBoxLayout()
self.AddressBar = AddressBar()
self.ToolBar.setLayout(self.ToolBarLayout)
self.ToolBarLayout.addWidget(self.AddressBar)
*#NewTab Button*
self.AddTabButton = QPushButton("+")
self.AddTabButton.clicked.connect(self.AddTab)
self.ToolBarLayout.addWidget(self.AddTabButton)
*#Main View*
self.Container = QWidget()
self.Container.layout = QStackedLayout()
self.Container.setLayout(self.Container.layout)
self.layout.addWidget(self.TabBar)
self.layout.addWidget(self.ToolBar)
self.layout.addWidget(self.Container)
self.setLayout(self.layout)
self.AddTab()
self.show()
def TabClose(self, i):
self.TabBar.removeTab(i)
print(self.TabCount)
print(self.Tabs)
def AddTab(self):
i = self.TabCount
self.Tabs.append(QWidget())
self.Tabs[i].layout = QVBoxLayout()
self.Tabs[i].setObjectName("Tab " + str(i))
*#Generate WebView*
self.Tabs[i].content = QWebEngineView()
self.Tabs[i].content.load(QUrl.fromUserInput("http://google.com"))
*#Add WebView to Tabs layout*
self.Tabs[i].layout.addWidget(self.Tabs[i].content)
*#Set top level tab [] to layout*
self.Tabs[i].setLayout(self.Tabs[i].layout)
*#Add tab to top level StackedWidget*
self.Container.layout.addWidget(self.Tabs[i])
self.Container.layout.setCurrentWidget(self.Tabs[i])
*#Set the tab at top of screen*
self.TabBar.addTab("New Tab - " + str(i))
self.TabBar.setTabData(i, "tab" + str(i))
self.TabBar.setCurrentIndex(i)
self.TabCount += 1
**def SwitchTab(self, i):
Tab_Data = self.TabBar.tabData(i)
print("tab: ", Tab_Data)
Tab_Content = self.findChild(QWidget, Tab_Data)
print(Tab_Content)
self.Container.layout.setCurrentWidget(Tab_Content)**
if __name__ == "__main__":
app = QApplication(sys.argv)
window = App()
sys.exit(app.exec_())
Because the findChild method takes the objectName into it, not the tabData you set. in this case, the solution is simple. Just set the objectName and the tabData with the same parameter.
self.TabBar.setTabData(i, "Tab " + str(i))
self.Tabs[i].setObjectName("Tab " + str(i))

Get color from variable number of buttons PyQt

I'm trying to get the player's names and the color that they choose for a next step in the project. Getting the names is easy enough but the colors is a bit of a pain.
I don't know how to get the selected color and apply it the button using the QColorDialog. The ultimate goal is to get a structure with player name linked to the color they chose.
Here is what i have:
from PyQt5.QtWidgets import (
QLineEdit,
QWidget,
QApplication,
QLabel,
QMainWindow,
QGridLayout,
QColorDialog,
QPushButton,
QVBoxLayout,
QHBoxLayout,
)
import sys
class NamesPlayers(QMainWindow):
""" name screen, ask the names of the players """
def __init__(self, nb_players):
super().__init__()
self.layout_widget = QWidget()
self.player_names = []
self.player_colors = []
main_layout = QVBoxLayout()
names_layout = QGridLayout()
button_layout = QHBoxLayout()
button_list = []
for i in range(nb_players):
label = QLabel("Name :")
player_name = QLineEdit()
color_button = QPushButton("Color")
color_button.setStyleSheet("background-color: white")
names_layout.addWidget(label, i, 0)
names_layout.addWidget(player_name, i, 1)
names_layout.addWidget(color_button, i, 2)
button_list.append(color_button)
self.player_names.append(player_name)
self.player_colors.append(color_button.styleSheet())
self.confirm_button = QPushButton("Confirm")
button_layout.addWidget(self.confirm_button)
main_layout.addLayout(names_layout)
main_layout.addLayout(button_layout)
for button in button_list:
button.clicked.connect(self.open_colordialog)
self.layout_widget.setLayout(main_layout)
self.setCentralWidget(self.layout_widget)
def open_colordialog(self):
color_dialog = QColorDialog()
color_dialog.exec_()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = NamesPlayers(4)
window.show()
app.exec_()
My main issue i guess is the fact that the number of buttons for the color can vary and when I click on those buttons, the address for the QColorDialog is always the same which is not what I want.
Any help is appreciated
One possible solution is to use the sender() method to get the button pressed:
import sys
from PyQt5 import QtCore, QtWidgets
class NamesPlayers(QtWidgets.QMainWindow):
""" name screen, ask the names of the players """
def __init__(self, nb_players, parent=None):
super().__init__(parent)
names_layout = QtWidgets.QGridLayout()
for i in range(nb_players):
label = QtWidgets.QLabel("Name :")
player_name = QtWidgets.QLineEdit()
color_button = QtWidgets.QPushButton("Color")
color_button.setStyleSheet("background-color: white")
color_button.clicked.connect(self.open_colordialog)
names_layout.addWidget(label, i, 0)
names_layout.addWidget(player_name, i, 1)
names_layout.addWidget(color_button, i, 2)
self.confirm_button = QtWidgets.QPushButton("Confirm")
central_widget = QtWidgets.QWidget()
main_layout = QtWidgets.QVBoxLayout(central_widget)
button_layout = QtWidgets.QHBoxLayout()
button_layout.addWidget(self.confirm_button)
main_layout.addLayout(names_layout)
main_layout.addLayout(button_layout)
self.setCentralWidget(central_widget)
#QtCore.pyqtSlot()
def open_colordialog(self):
button = self.sender()
color_dialog = QtWidgets.QColorDialog()
if color_dialog.exec_() == QtWidgets.QColorDialog.Accepted:
button.setStyleSheet(
"background-color: {}".format(color_dialog.selectedColor().name())
)
button.clearFocus()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
window = NamesPlayers(4)
window.show()
sys.exit(app.exec_())
Another possible solution is to send the button pressed (with the methods indicated in this answer):
from functools import partial
# ...
for i in range(nb_players):
# ...
color_button = QtWidgets.QPushButton("Color")
color_button.setStyleSheet("background-color: white")
color_button.clicked.connect(partial(self.open_colordialog, color_button))
# or
# color_button.clicked.connect(lambda *args, btn=color_button: self.open_colordialog(btn))
def open_colordialog(self, button):
color_dialog = QtWidgets.QColorDialog()
if color_dialog.exec_() == QtWidgets.QColorDialog.Accepted:
button.setStyleSheet(
"background-color: {}".format(color_dialog.selectedColor().name())
)
button.clearFocus()

How to use key press events in PyQt5

I want the "Add" function to run when I input a number into "LE1" and press the "Enter" key on the keyboard. I also want the line edit to clear its text when I select it for editing.
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QLineEdit, QLabel, QGridLayout, QWidget, QDialog
class MyWindow(QtWidgets.QMainWindow):
def __init__(self):
super(MyWindow, self).__init__()
centralWidget = QWidget()
self.setCentralWidget(centralWidget)
self.Glayout = QGridLayout(centralWidget)
self.LE1 = QLineEdit('Input Number',self)
self.LE1.keyPressEvent(self.KPE)
Label1 = QLabel('+ 1 =',self)
self.LE2 = QLineEdit(self)
self.Glayout.addWidget(self.LE1)
self.Glayout.addWidget(Label1)
self.Glayout.addWidget(self.LE2)
def Add(self):
Num = float(self.LE1.text())
math = Num + 1
ans = str(math)
self.LE2.setText(ans)
def KPE(self):
if event.key() == Qt.Key_Enter:
self.Add()
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
keyPressEvent is a method that if you override it that way you are losing the default behavior, besides it is unnecessary since QLineEdit has the returnPressed signal that notifies if Enter is pressed.
On the other hand, converting a string to floating can throw an exception so you should prevent that case, another better option is to use a widget that allows only numerical values with QSpinBox or QDoubleSpinBox, or at least restrict the values that are entered into the QLineEdit with a QValidator appropriate.
And finally do not use the word math as a variable name since that is the name of a library that could cause you problems in the future.
Considering the above, the solution is:
from PyQt5.QtWidgets import (
QApplication,
QGridLayout,
QLineEdit,
QLabel,
QMainWindow,
QWidget,
)
class MyWindow(QMainWindow):
def __init__(self, parent=None):
super(MyWindow, self).__init__(parent)
self.LE1 = QLineEdit("Input Number")
self.LE1.returnPressed.connect(self.add)
Label1 = QLabel("+ 1 =")
self.LE2 = QLineEdit()
centralWidget = QWidget()
self.setCentralWidget(centralWidget)
layout = QGridLayout(centralWidget)
layout.addWidget(self.LE1)
layout.addWidget(Label1)
layout.addWidget(self.LE2)
def add(self):
try:
num = float(self.LE1.text())
num += 1
self.LE2.setText(str(num))
except ValueError:
pass
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())

PyQt5 : How to use progress bar in PyQt5?

See my code and tell me how do I set progress bar to the -dollar()- function and progress bar start with doing the function and finished with it. see my code at the continue:
from PyQt5.QtWidgets import (QWidget,QApplication,QTextEdit,
QInputDialog,QPushButton,QVBoxLayout,QProgressBar)
import sys
class Tbx(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.vbox = QVBoxLayout()
self.btn = QPushButton('ClickMe',self)
self.btn.clicked.connect(self.dollar)
self.te = QTextEdit(self)
self.prgb = QProgressBar(self)#How to set this to doing something?
self.vbox.addWidget(self.te)
self.vbox.addWidget(self.btn)
self.vbox.addWidget(self.prgb)
self.setLayout(self.vbox)
self.setGeometry(300,300,400,250)
self.setWindowTitle('Application')
self.show()
def dollar(self):#Like doing this set progress bar to this
text_1_int , ok = QInputDialog.getInt(self,'HowMany?','Enter How Many dollar do you want ?')
if not ok:
return
current_lines = self.te.toPlainText().split('\n')
new_lines = list()
for dollar_counter in range(1,text_1_int+1):
word = '$'*dollar_counter
new_lines += [word + text for text in current_lines]
self.te.setPlainText('\n'.join(new_lines))
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Tbx()
sys.exit(app.exec_())
You can set the maximum value of the progress control to the value entered in your input window and then simply use setValue to increase the progress bar, however, this will block the UI for very large calculations, so you might want to also move your method to a new thread and report progress to the UI using a signal, here is the full example:
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import (QWidget, QApplication, QTextEdit, QInputDialog, QPushButton, QVBoxLayout, QProgressBar)
import sys
class DollarCalculation(QThread):
reportProgress = pyqtSignal(int, list)
calculationFinished = pyqtSignal()
def __init__(self, numDollars, currentLines):
super().__init__()
self.numDollars = numDollars
self.currentLines = currentLines
def run(self) -> None:
for dollar_counter in range(1, self.numDollars + 1):
word = '$' * dollar_counter
self.reportProgress.emit(dollar_counter + 1, [word + text for text in self.currentLines])
self.calculationFinished.emit()
class Tbx(QWidget):
def __init__(self):
super().__init__()
self.initUI()
self.dollarCalculation = None
def initUI(self):
self.vbox = QVBoxLayout()
self.btn = QPushButton('ClickMe', self)
self.btn.clicked.connect(self.dollar)
self.te = QTextEdit(self)
self.prgb = QProgressBar(self)
self.vbox.addWidget(self.te)
self.vbox.addWidget(self.btn)
self.vbox.addWidget(self.prgb)
self.setLayout(self.vbox)
self.setGeometry(300, 300, 400, 250)
self.setWindowTitle('Application')
def dollar(self):
text_1_int, ok = QInputDialog.getInt(self, 'HowMany?', 'Enter How Many dollar do you want ?')
if not ok:
return
self.btn.setEnabled(False)
self.prgb.setMaximum(text_1_int + 1)
self.dollarCalculation = DollarCalculation(text_1_int, self.te.toPlainText().split('\n'))
self.dollarCalculation.reportProgress.connect(self.progress)
self.dollarCalculation.calculationFinished.connect(self.calculationFinished)
self.dollarCalculation.start()
def progress(self, value, newLines):
self.te.append('\n'.join(newLines))
self.prgb.setValue(value)
def calculationFinished(self):
self.btn.setEnabled(True)
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = Tbx()
ex.show()
sys.exit(app.exec_())

How do I print in multiple QLabel when I click the PushButton?

How do I print in multiple QLabel when I click the PushButton?, because it only works in Quantity I also want it in Item Name and Price. I tried putting multiple print_click(self) it wont work it say redefinition of unused 'print_clink'. Thanks in advance
My Code:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QPushButton, QLabel, QLineEdit
from PyQt5.QtCore import pyqtSlot`
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.ItemName = QLabel('Item Name:')
self.Item_Line = QLabel('')
self.Item_Name = QLineEdit('')
self.PriceName = QLabel('Price:')
self.Price_Line = QLabel('')
self.Price_Name = QLineEdit('')
self.QuantityName = QLabel('Quantity:')
self.Quantity_Line = QLabel('0')
self.Quantity_Name = QLineEdit()
self.Update_button = QPushButton("Update")
self.Update_button.clicked.connect(self.print_click)
self.Clear_button = QPushButton("Clear")
self.Clear_button.clicked.connect(self.clear_click)
hbox = QHBoxLayout(self)
hbox.addWidget(self.ItemName)
hbox.addWidget(self.Item_Name)
hbox.addWidget(self.PriceName)
hbox.addWidget(self.Price_Name)
hbox.addWidget(self.QuantityName)
hbox.addWidget(self.Quantity_Line)
hbox.addWidget(self.Quantity_Name)
hbox.addWidget(self.Update_button)
hbox.addWidget(self.Clear_button)
self.show()
self.Clear_button.clicked.connect(self.Item_Line.clear)
self.Clear_button.clicked.connect(self.Item_Name.clear)
self.Clear_button.clicked.connect(self.Price_Line.clear)
self.Clear_button.clicked.connect(self.Price_Name.clear)
self.Clear_button.clicked.connect(self.Quantity_Line.clear)
self.Clear_button.clicked.connect(self.Quantity_Name.clear)
#pyqtSlot()
def print_click(self):
self.Quantity_Line.setText(self.Quantity_Name.text())
def clear_click(self):
self.Quantity_Line(self.Quantity_Name.text(''))
return self.Quantity
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())
I'm not completely sure of the expected result but I guess there are some mistakes and redundancies in your code :
the Price_Line and Item_Line weren't added to the QHBoxLayout
the method print_click wasn't setting the text from Price_Name and Item_Name on the respective Price_Line and Item_Line.
the clear_click method wasn't really useful as you already connected the clear method of every other elements on that button.
The following code is adapted from yours, paying attention to the points mentioned above :
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QPushButton, QLabel, QLineEdit
from PyQt5.QtCore import pyqtSlot
class Window(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
self.ItemName = QLabel('Item Name:')
self.Item_Line = QLabel('')
self.Item_Name = QLineEdit()
self.PriceName = QLabel('Price:')
self.Price_Line = QLabel('')
self.Price_Name = QLineEdit()
self.QuantityName = QLabel('Quantity:')
self.Quantity_Line = QLabel('0')
self.Quantity_Name = QLineEdit()
self.Update_button = QPushButton("Update")
self.Update_button.clicked.connect(self.print_click)
self.Clear_button = QPushButton("Clear")
hbox = QHBoxLayout(self)
hbox.addWidget(self.ItemName)
hbox.addWidget(self.Item_Line)
hbox.addWidget(self.Item_Name)
hbox.addWidget(self.PriceName)
hbox.addWidget(self.Price_Line)
hbox.addWidget(self.Price_Name)
hbox.addWidget(self.QuantityName)
hbox.addWidget(self.Quantity_Line)
hbox.addWidget(self.Quantity_Name)
hbox.addWidget(self.Update_button)
hbox.addWidget(self.Clear_button)
self.show()
self.Clear_button.clicked.connect(self.Item_Line.clear)
self.Clear_button.clicked.connect(self.Item_Name.clear)
self.Clear_button.clicked.connect(self.Price_Line.clear)
self.Clear_button.clicked.connect(self.Price_Name.clear)
self.Clear_button.clicked.connect(self.Quantity_Line.clear)
self.Clear_button.clicked.connect(self.Quantity_Name.clear)
def print_click(self):
self.Price_Line.setText(self.Price_Name.text())
self.Item_Line.setText(self.Item_Name.text())
self.Quantity_Line.setText(self.Quantity_Name.text())
if __name__ == "__main__":
app = QApplication(sys.argv)
win = Window()
sys.exit(app.exec_())

Categories

Resources