Problem to display QLabels in GridLayout in PyQt5? - python

My Intention is to display five types of labels in two frames (left and right Frame).
In my case, the right frame, which contains two labels, is displayed or added as per my idea.
But in the Left frame, I face a problem. Added widgets display is not proper. The left frame is a GridLayout, In which we add three widgets. As of my idea, top widget is none or single line. Middle widget is a character and the bottim widget is either single, double or triple line.
import sys
from PyQt5.QtWidgets import QWidget,QFrame,QLabel,QHBoxLayout,QVBoxLayout,QGridLayout,QApplication
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
class Left_Right_FrameDesign(QWidget):
def __init__(self,color_left_lrcd,color_right_lrcd,
frame_left_lrcd_width,frame_right_lrcd_width,
frame_lrcd_height):
super().__init__()
self.color_left_lrcd = color_left_lrcd
self.color_right_lrcd = color_right_lrcd
self.frame_left_lrcd_width = frame_left_lrcd_width
self.frame_right_lrcd_width = frame_right_lrcd_width
self.frame_lrcd_height = frame_lrcd_height
self.frame_left_lrcd = QFrame()
self.frame_left_lrcd.setProperty("type","1")
self.frame_left_lrcd.setFixedSize(self.frame_left_lrcd_width,self.frame_lrcd_height)
self.frame_right_lrcd = QFrame()
self.frame_right_lrcd.setProperty("type","1")
self.frame_right_lrcd.setFixedHeight(self.frame_lrcd_height)
self.frame_right_lrcd.setFixedWidth(self.frame_right_lrcd_width)
self.principal_lrcd_layout = QHBoxLayout()
self.frame_left_lrcd_layout = QGridLayout(self.frame_left_lrcd)
self.frame_right_lrcd_layout = QVBoxLayout(self.frame_right_lrcd)
self.frame_left_lrcd_layout.setContentsMargins(0, 0, 0, 0)
self.frame_right_lrcd_layout.setContentsMargins(0,0,0,0)
self.frame_right_lrcd_layout.setSpacing(0)
self.frame_left_lrcd_layout.setAlignment(Qt.AlignHCenter| Qt.AlignVCenter)
self.principal_lrcd_layout.setSpacing(0)
self.principal_lrcd_layout.addWidget(self.frame_left_lrcd)
self.principal_lrcd_layout.addWidget(self.frame_right_lrcd)
self.principal_lrcd_layout.setContentsMargins(0, 0, 0, 0)
class example(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("My Widget Example")
self.setStyleSheet(style_sheet())
my_font_1 = QFont("Arial", 10, QFont.Bold)
my_font_1.setLetterSpacing(QFont.AbsoluteSpacing, -6)
my_font_en = QFont("Arial", 8, QFont.Bold)
my_font_hi = QFont("Mangal", 11, )
my_font_ta = QFont("Latha", 8,QFont.Bold)
lbl_sline = ('\u2015' * 1) # print single line
lbl_dline = '\u003D' * 4 # print double line
lbl_tline = ('\u2261' * 4) # print tripleline
text = {"lbl01": {"lan_1":"en_Accounts","lan_2":"ta_கணக்கியல்" ,"char":"ta_ழ்","top_line":"0","bot_line":"1"},
"lbl02": {"lan_1":"en_Manufacturing","lan_2":"hi_हिसाब किताब","char":"en_F8","top_line":"1","bot_line":"2"},
"lbl03": {"lan_1":"en_Inventory","lan_2":"ta_சரக்கியல்" ,"char":"hi_हि","top_line":"1","bot_line":"3"}}
lst_mainkeys = list(text.keys())
lst_subkeys = list(text[lst_mainkeys[0]].keys())
self.frame_design,self.lbl_lan1,self.lbl_lan2,self.lbl_tline,self.lbl_char,self.lbl_bline = {},{},{},{},{},{}
self.lay = QHBoxLayout()
self.lay.setSpacing(3)
self.lay.setContentsMargins(0, 0, 0, 0)
for i, j in enumerate(lst_mainkeys):
self.frame_design[i] = Left_Right_FrameDesign(color_left_lrcd="green", color_right_lrcd="blue",
frame_left_lrcd_width=50, frame_right_lrcd_width=200,
frame_lrcd_height= 40)
for m,n in enumerate(lst_subkeys):
name = j+"_"+str(m)
self.lbl_lan1[name] = QLabel()
self.lbl_lan2[name] = QLabel()
self.lbl_tline[name] = QLabel()
self.lbl_char[name] = QLabel()
self.lbl_bline[name] = QLabel()
self.temp_name =(text[j][n])
if self.temp_name[:2] == "en":
self.myfont = my_font_en
elif self.temp_name[:2] == "ta":
self.myfont = my_font_ta
elif self.temp_name[:2] == "hi":
self.myfont = my_font_hi
if n == "lan_1":
self.lbl_lan1[name].setText(text[j][n][3:])
self.lbl_lan1[name].setFont(self.myfont)
self.frame_design[i].frame_right_lrcd_layout.addWidget(self.lbl_lan1[name])
elif n == "lan_2":
self.lbl_lan2[name].setText(text[j][n][3:])
self.lbl_lan2[name].setFont(self.myfont)
self.frame_design[i].frame_right_lrcd_layout.addWidget(self.lbl_lan2[name])
elif n == "char":
self.lbl_char[name].setText(text[j][n][3:])
self.lbl_char[name].setFont(self.myfont)
self.frame_design[i].frame_left_lrcd_layout.addWidget(self.lbl_char[name],1,0)
if n == "top_line":
if self.temp_name == 0:
pass
elif self.temp_name == 1:
self.lbl_tline[name].setText(lbl_sline)
self.frame_design[i].frame_left_lrcd_layout.addWidget(self.lbl_tline[name],0,0)
if n == "bot_line":
if self.temp_name == 1:
self.lbl_bline[name].setText(lbl_sline)
elif self.temp_name == 2:
self.lbl_bline[name].setText(lbl_dline)
elif self.temp_name == 3:
self.lbl_bline[name].setText(lbl_tline)
self.lbl_bline[name].setText(text[j][n])
self.frame_design[i].frame_left_lrcd_layout.addWidget(self.lbl_bline[name],2,0)
self.lay.addLayout(self.frame_design[i].principal_lrcd_layout)
self.setLayout(self.lay)
def style_sheet():
return """
QFrame[type="1"] {background-color : rgb(188,212,114);}
QFrame[type="1"]::hover{background-color:rgb(255, 223, 0);color: Black;}}"""
if __name__ =="__main__":
app = QApplication(sys.argv)
mainwindow = example()
mainwindow.show()
sys.exit(app.exec_())

Related

FigureCanvas Fail To Appear When Receiving Signal From Popup

I'm hoping I can get some help with a problem I've been stuck on all day. I've looked through previous questions but nothing seems to quite match up with the issue I'm facing. Perhaps a fresh pair of eyes can guide me in the right direction. I'll include my code at the end of my question.
Background:
I'm working on a simple application that consists of the main window and a popup window. The main window contains only one button that opens the popup window when pressed. The popup window contains two checkbox options, an ok button, and a cancel button. When pressed, the popup's ok button returns a signal to the main window. This signal contains a list of 1s and 0s, depending on the status of the checkboxes. The purpose of the list is so that a function in the main window can determine which canvases to plot (either plot 1, plot 2, or both).
It should be noted that the main window is organized using a grid layout, and each canvas is meant to be displayed on a row in column 3 with a corresponding label in column 4. The desired outcome when creating both canvases is as shown in the figure:
Problem:
Everything works fine until the end when the signal returns to the slot at popup_input() within the main window. No errors occur but the canvases simply do not appear. Taking the code for creating the figures from popup_input() and placing it instead into open_options() does seem to work and gives the figure as shown above. However, my goal is to have the plots appear after the user makes their selection. Does anyone have any idea why the canvases are failing to appear?
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtWidgets import QDialog, QApplication, QMainWindow, QLabel, QPushButton
from PyQt5.QtGui import QFont
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.ticker import FormatStrFormatter
class Options(QMainWindow):
popup_response = QtCore.pyqtSignal(object)
def __init__(self):
super().__init__()
self._title = 'Plotting Options'
self.setWindowTitle(self._title)
self.selected_plots = [1, 1] # Always have both options selected by default
self.option1 = QtWidgets.QCheckBox('Option 1')
self.option1.setChecked(True)
self.option2 = QtWidgets.QCheckBox('Option 2')
self.option2.setChecked(True)
self.checkbox_layout = QtWidgets.QHBoxLayout()
self.checkbox_layout.addWidget(self.option1)
self.checkbox_layout.addWidget(self.option2)
self.ok_button = QtWidgets.QPushButton('OK', self)
self.ok_button.clicked.connect(lambda: self.clicked_ok())
self.cancel_button = QtWidgets.QPushButton('Cancel', self)
self.cancel_button.clicked.connect(lambda: self.clicked_cancel())
self.button_layout = QtWidgets.QHBoxLayout()
self.button_layout.addWidget(self.ok_button)
self.button_layout.addWidget(self.cancel_button)
self._popup = QtWidgets.QWidget()
self.setCentralWidget(self._popup)
self.layout = QtWidgets.QGridLayout(self._popup)
self.layout.addLayout(self.checkbox_layout, 0, 0)
self.layout.addLayout(self.button_layout, 1, 0)
def finalize_selected_plots(self):
if self.option1.isChecked() == False:
self.selected_plots[0] = 0
if self.option2.isChecked() == False:
self.selected_plots[1] = 0
return self.selected_plots
def clicked_ok(self):
self.plots = self.finalize_selected_plots()
# Send selection back to Results Window
main_window = MainWindow()
static_reply = self.plots
self.popup_response.connect(main_window.popup_input)
self.popup_response.emit(static_reply)
self.plots = [1,1]
self.close()
def clicked_cancel(self):
self.close()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.exPopup = Options()
self.data_to_plot = [[1, 5, 10], [2, 4, 6], [6, 4, 2]] # Dummy values
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
self.button_options = QtWidgets.QPushButton('Plot Options', self)
self.button_options.clicked.connect(self.open_options)
self.option_layout = QtWidgets.QHBoxLayout()
self.option_layout.addWidget(self.button_options)
self.layout = QtWidgets.QGridLayout(self._main)
self.layout.addLayout(self.option_layout, 0, 2)
def open_options(self):
self.exPopup.show()
#QtCore.pyqtSlot(object)
def popup_input(self, reply):
plot_title_font_size = 15
data = self.data_to_plot
if reply[0] != 0:
self.figure_xy1 = FigureCanvas(Figure(figsize=(5, 5)))
self._figure1 = self.figure_xy1.figure.subplots()
self._figure1.grid()
self.layout.addWidget(self.figure_xy1, 1, 3)
self.figure_label1 = QLabel('Test Plot 1', self)
self.figure_label1.setFont(QFont('Times', plot_title_font_size))
self.figure_label1.setAlignment(QtCore.Qt.AlignLeft)
self.layout.addWidget(self.figure_label1, 1, 4)
x = data[0]
y = data[1]
self._figure1.plot(x, y, '-')
self._figure1.set_xlabel('x')
self._figure1.set_ylabel('y1')
self._figure1.figure.canvas.draw()
if reply[1] != 0:
self.figure_xy2 = FigureCanvas(Figure(figsize=(5, 5)))
self._figure2 = self.figure_xy2.figure.subplots()
self._figure2.grid()
self.layout.addWidget(self.figure_xy2, 2, 3)
self.figure_label2 = QLabel('Test Plot 2', self)
self.figure_label2.setFont(QFont('Times', plot_title_font_size))
self.figure_label2.setAlignment(QtCore.Qt.AlignLeft)
self.layout.addWidget(self.figure_label2, 2, 4)
x = data[0]
y = data[2]
self._figure2.plot(x, y, '-')
self._figure2.set_xlabel('x')
self._figure2.set_ylabel('y2')
self._figure2.figure.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = QtWidgets.QStackedWidget()
main_window = MainWindow()
widget.addWidget(main_window)
widget.setWindowTitle("Main Window")
widget.show()
try:
sys.exit(app.exec_())
except:
print("Exiting")
I have found a solution to my issue. With the help of jmacey over on Reddit's pyqt page, here's what I figured out:
My popup should inherit QDialog vs QMainWindow
Simplified dialog button scheme by utilizing QDialogButtonBox
Include accepted and rejected signals for my ok and cancel buttons, respectively. I also included an extra signal for clicked_ok that would perform my desired function as the dialog closes.
popup dialog should be executed from my main window. If I click ok in my dialog, then it will run my code related to plotting.
Here are my updated classes:
class Options(QDialog):
popup_response = QtCore.pyqtSignal(object)
def __init__(self, parent=None):
super().__init__(parent)
self._title = 'Plotting Options'
self.setWindowTitle(self._title)
self.setGeometry(100, 100, 300, 200)
self.selected_plots = [1, 1] # Always have both options selected by default
self.option1 = QtWidgets.QCheckBox('Option 1')
self.option1.setChecked(True)
self.option2 = QtWidgets.QCheckBox('Option 2')
self.option2.setChecked(True)
self.checkbox_layout = QtWidgets.QHBoxLayout()
self.checkbox_layout.addWidget(self.option1)
self.checkbox_layout.addWidget(self.option2)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.button_box.accepted.connect(self.clicked_ok)
self.button_box.rejected.connect(self.reject)
self.popup_layout = QtWidgets.QVBoxLayout()
self.popup_layout.addLayout(self.checkbox_layout)
self.popup_layout.addWidget(self.button_box)
self.setLayout(self.popup_layout)
def finalize_selected_plots(self):
if self.option1.isChecked() == False:
self.selected_plots[0] = 0
if self.option2.isChecked() == False:
self.selected_plots[1] = 0
return self.selected_plots
def clicked_ok(self):
print("clicked ok")
self.plots = self.finalize_selected_plots()
# Send selection back to Results Window
# main_window = MainWindow()
# static_reply = self.plots
# self.popup_response.connect(main_window.popup_input)
# self.popup_response.emit(static_reply)
self.plots = [1,1]
self.close()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
#self.exPopup = Options()
self.data_to_plot = [[1, 5, 10], [2, 4, 6], [6, 4, 2]] # Dummy values
self._main = QtWidgets.QWidget()
self.setCentralWidget(self._main)
self.button_options = QtWidgets.QPushButton('Plot Options', self)
#self.button_options.clicked.connect(self.open_options)
self.button_options.clicked.connect(self.popup_input)
self.option_layout = QtWidgets.QHBoxLayout()
self.option_layout.addWidget(self.button_options)
self.layout = QtWidgets.QGridLayout(self._main)
self.layout.addLayout(self.option_layout, 0, 2)
# def open_options(self):
# self.exPopup.show()
##QtCore.pyqtSlot(object)
def popup_input(self):
plot_title_font_size = 15
data = self.data_to_plot
self.grab_popup = Options(self)
if self.grab_popup.exec():
print("Pressed ok")
if reply[0] != 0:
self.figure_xy1 = FigureCanvas(Figure(figsize=(5, 5)))
self._figure1 = self.figure_xy1.figure.subplots()
self._figure1.grid()
self.layout.addWidget(self.figure_xy1, 1, 3)
self.figure_label1 = QLabel('Test Plot 1', self)
self.figure_label1.setFont(QFont('Times', plot_title_font_size))
self.figure_label1.setAlignment(QtCore.Qt.AlignLeft)
self.layout.addWidget(self.figure_label1, 1, 4)
x = data[0]
y = data[1]
self._figure1.plot(x, y, '-')
self._figure1.set_xlabel('x')
self._figure1.set_ylabel('y1')
self._figure1.figure.canvas.draw()
if reply[1] != 0:
self.figure_xy2 = FigureCanvas(Figure(figsize=(5, 5)))
self._figure2 = self.figure_xy2.figure.subplots()
self._figure2.grid()
self.layout.addWidget(self.figure_xy2, 2, 3)
self.figure_label2 = QLabel('Test Plot 2', self)
self.figure_label2.setFont(QFont('Times', plot_title_font_size))
self.figure_label2.setAlignment(QtCore.Qt.AlignLeft)
self.layout.addWidget(self.figure_label2, 2, 4)
x = data[0]
y = data[2]
self._figure2.plot(x, y, '-')
self._figure2.set_xlabel('x')
self._figure2.set_ylabel('y2')
self._figure2.figure.canvas.draw()
else:
print("Pressed Cancel")

How do I make QtableWidget cell squared the size I want?

I'm trying to have a zoom in/zoom out effet on a QtableWidget.
to do so I have to variables
self.myfont which is a Qfont objet and self.table_size which is an int.
When I want zoom in/out I call those two functions :
def zoomin(self):
fontsize = self.myfont.pointSize() + 1
if fontsize < 1:
fontsize = 1
self.myfont.setPointSize(fontsize)
self.table_size += 5
if self.table_size < 1:
self.table_size = 1
print(self.table_size,fontsize)
self.redrawCMLayout()
def zoomout(self):
fontsize = self.myfont.pointSize() - 1
if fontsize < 1:
fontsize = 1
self.myfont.setPointSize(fontsize)
self.table_size -= 5
if self.table_size < 1:
self.table_size = 1
print(self.table_size,fontsize)
self.redrawCMLayout()
which change my two variables.
Then I redraw the QtableWidget with
self.tableWidget = QTableWidget()
self.tableWidget.verticalHeader().setDefaultSectionSize(self.table_size)
self.tableWidget.horizontalHeader().setDefaultSectionSize(self.table_size)
self.tableWidget.horizontalHeader().setFixedHeight(self.table_size)
self.tableWidget.verticalHeader().setFixedWidth(self.table_size)
self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed)
self.tableWidget.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
self.tableWidget.horizontalHeader().setFont(self.myfont)
self.tableWidget.verticalHeader().setFont(self.myfont)
When I'm zooming in it works fine
but when I'm zooming out, the horizontal header width does not match the vertical height anymore.
How can I force them to have the cells squared even for small width and height?
Here is a MRE
import sys
import os
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class ExampleWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.setMinimumSize(QSize(440, 240))
self.setWindowTitle("PyQt5 Textarea example")
self.myarray = [[4, 0, 2, 1],
[6, 0, 3, 7],
[4, 2, 2, 8],
[3, 2, 1, 0]]
self.myfont = QFont()
self.myfont.setPointSize(10)
self.table_size = 35
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.gridCM = QHBoxLayout()
self.update_CMLayout()
zoomin = QAction('zoomin', self)
zoomin.setShortcut('Ctrl+shift+Z')
zoomin.setToolTip('Redo')
zoomin.triggered.connect(self.zoomin)
zoomout = QAction( 'zoomout',self)
zoomout.setShortcut('Ctrl+shift+Z')
zoomout.setToolTip('Redo')
zoomout.triggered.connect(self.zoomout)
# toolbar
toolbar = self.addToolBar('')
toolbar.addAction(zoomin)
toolbar.addAction(zoomout)
self.centralWidget.setLayout(self.gridCM)
def update_CMLayout(self):
print('update_CMLayout')
#self.gridCM = QHBoxLayout()
self.grid3_layout = QGroupBox('Connectivity Matrix')
grid3 = QGridLayout()
self.grid3_layout.setLayout(grid3)
self.tableWidget = QTableWidget()
self.tableWidget.horizontalHeader().setFont(self.myfont)
self.tableWidget.verticalHeader().setFont(self.myfont)
self.tableWidget.verticalHeader().setDefaultSectionSize(self.table_size)
self.tableWidget.horizontalHeader().setDefaultSectionSize(self.table_size)
self.tableWidget.horizontalHeader().setFixedHeight(self.table_size)
self.tableWidget.verticalHeader().setFixedWidth(self.table_size)
self.tableWidget.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed)
self.tableWidget.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
line = len(self.myarray)
column = len(self.myarray[0])
self.tableWidget.setRowCount(line)
self.tableWidget.setColumnCount(column)
for c in range(column):
for l in range(line):
item = QTableWidgetItem(str(self.myarray[c][l]))
item.setFont(self.myfont)
item.setSizeHint(QSize(self.table_size, self.table_size))
self.tableWidget.setItem(c, l, item)
grid3.addWidget(self.tableWidget, 1, 1, 1, 1)
self.gridCM.insertWidget(0, self.grid3_layout)
def zoomin(self):
fontsize = self.myfont.pointSize() + 1
if fontsize < 1:
fontsize = 1
self.myfont.setPointSize(fontsize)
self.table_size += 5
if self.table_size < 1:
self.table_size = 1
print(self.table_size,fontsize)
self.redrawCMLayout()
def zoomout(self):
fontsize = self.myfont.pointSize() - 1
if fontsize < 1:
fontsize = 1
self.myfont.setPointSize(fontsize)
self.table_size -= 5
if self.table_size < 1:
self.table_size = 1
print(self.table_size,fontsize)
self.redrawCMLayout()
def redrawCMLayout(self):
self.gridCM.removeWidget(self.grid3_layout)
self.grid3_layout.deleteLater()
self.grid3_layout = None
self.tableWidget.deleteLater()
self.tableWidget = None
self.update_CMLayout()
# self.layout_Main.insertItem(2,self.gridCM)
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setStyle("Windows")
mainWin = ExampleWindow()
mainWin.show()
sys.exit( app.exec_() )
You have to set the setMinimumSectionSize to 0. On the other hand instead of deleting and creating the elements it is better to reuse:
class Delegate(QStyledItemDelegate):
def sizeHint(self, option, index):
s = QStyledItemDelegate.sizeHint(self, option, index)
return max(s.width(), s.height()) * QSize(1, 1)
class ExampleWindow(QMainWindow):
def __init__(self):
QMainWindow.__init__(self)
self.myfont = QFont()
self.myfont.setPointSize(10)
self.setMinimumSize(QSize(440, 240))
self.setWindowTitle("PyQt5 Textarea example")
self.myarray = [[4, 0, 2, 1], [6, 0, 3, 7], [4, 2, 2, 8], [3, 2, 1, 0]]
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
self.gridCM = QHBoxLayout(self.centralWidget)
zoomin = QAction("zoomin", self)
zoomin.setShortcut("Ctrl+shift+Z")
zoomin.setToolTip("Redo")
zoomin.triggered.connect(self.zoomin)
zoomout = QAction("zoomout", self)
zoomout.setShortcut("Ctrl+shift+Z")
zoomout.setToolTip("Redo")
zoomout.triggered.connect(self.zoomout)
# toolbar
toolbar = self.addToolBar("")
toolbar.addAction(zoomin)
toolbar.addAction(zoomout)
self.grid3_layout = QGroupBox("Connectivity Matrix")
grid3 = QGridLayout()
self.grid3_layout.setLayout(grid3)
self.gridCM.insertWidget(0, self.grid3_layout)
self.tableWidget = QTableWidget()
grid3.addWidget(self.tableWidget, 1, 1, 1, 1)
line = len(self.myarray)
column = len(self.myarray[0])
self.tableWidget.setRowCount(line)
self.tableWidget.setColumnCount(column)
self.delegate = Delegate()
self.tableWidget.setItemDelegate(self.delegate)
for c, row in enumerate(self.myarray):
for r, e in enumerate(row):
item = QTableWidgetItem(str(e))
self.tableWidget.setItem(r, c, item)
for header in (
self.tableWidget.horizontalHeader(),
self.tableWidget.verticalHeader(),
):
header.setSectionResizeMode(QHeaderView.ResizeToContents)
header.setMinimumSectionSize(0)
self.update_font()
def zoomin(self):
self.myfont.setPointSize(self.myfont.pointSize() + 1)
self.update_font()
def zoomout(self):
if self.myfont.pointSize() > 1:
self.myfont.setPointSize(self.myfont.pointSize() - 1)
self.update_font()
def update_font(self):
self.tableWidget.setFont(self.myfont)

Does PyQt5 have a transition effect like Kivy

Kivy Gui have a transition animation to switch between windows (going back and forth also) we can do it easily in kivy. But in PyQt5 I did't find out any way to transit between window (with animation) and going back and forth to a window again and again is also not working. So, is there any way to do like Kivy do transitions, going back and forth to a window easily in PyQt5.
Qt doesn't provide a similar effect on its own, but it still can be achieved using a subclass of a QStackedWidget (which behaves similarly to a QTabWidget, but without any QTabBar).
In the following example I'll show you how to implement a basic "swap" transition between two widgets that are added to a QStackedWidget, the next widget will scroll from right to left if the index is greater than the current, and vice versa.
class TransitionWidget(QtWidgets.QStackedWidget):
_nextIndex = _nextWidget = None
_orientation = QtCore.Qt.Horizontal
def __init__(self):
super().__init__()
self._animation = QtCore.QVariantAnimation(
startValue=0., endValue=1., duration=250)
self._animation.valueChanged.connect(self._aniUpdate)
self._animation.finished.connect(self._aniFinished)
self._animation.setEasingCurve(QtCore.QEasingCurve.InOutQuart)
def setDuration(self, duration):
self._animation.setDuration(duration)
def setCurve(self, curve):
if isinstance(curve, QtCore.QEasingCurve):
self._animation.setEasingCurve(curve)
def setOrientation(self, orientation):
self._orientation = orientation
def getRange(self, prevIndex, nextIndex):
rect = self.rect()
currentStart = nextEnd = QtCore.QPoint()
if self._orientation == QtCore.Qt.Horizontal:
if prevIndex < nextIndex:
currentEnd = QtCore.QPoint(-rect.width(), 0)
nextStart = QtCore.QPoint(rect.width(), 0)
else:
currentEnd = QtCore.QPoint(rect.width(), 0)
nextStart = QtCore.QPoint(-rect.width(), 0)
else:
if prevIndex < nextIndex:
currentEnd = QtCore.QPoint(0, -rect.width())
nextStart = QtCore.QPoint(0, rect.width())
else:
currentEnd = QtCore.QPoint(0, rect.width())
nextStart = QtCore.QPoint(0, -rect.width())
return currentStart, currentEnd, nextStart, nextEnd
def setCurrentIndex(self, index):
if index == self.currentIndex():
return
# prepare the next widget changes
if self._nextWidget is not None:
self._nextWidget.hide()
self._nextIndex = index
self._nextWidget = self.widget(index)
self._nextWidget.show()
rect = self.rect()
rect.translate(self.rect().topRight())
self._nextWidget.setGeometry(rect)
self._nextWidget.raise_()
self._animation.start()
def _aniFinished(self):
super().setCurrentIndex(self._nextIndex)
self._nextIndex = self._nextWidget = None
def _aniUpdate(self, value):
if not self._animation.state():
return
currentStart, currentEnd, nextStart, nextEnd = self.getRange(self.currentIndex(), self._nextIndex)
rect = self.rect()
self.currentWidget().setGeometry(rect.translated(QtCore.QLineF(currentStart, currentEnd).pointAt(value).toPoint()))
self._nextWidget.setGeometry(rect.translated(QtCore.QLineF(nextStart, nextEnd).pointAt(value).toPoint()))
self.update()
Example code:
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
mainWidget = QtWidgets.QWidget()
mainLayout = QtWidgets.QHBoxLayout(mainWidget)
transitionWidget = TransitionWidget()
mainLayout.addWidget(transitionWidget)
pageCount = 10
for page in range(pageCount):
widget = QtWidgets.QWidget()
layout = QtWidgets.QGridLayout(widget)
pageLabel = QtWidgets.QLabel('Page {}'.format(page + 1))
layout.addWidget(pageLabel, 0, 0, 1, 2)
prevBtn = QtWidgets.QPushButton('Previous')
if not page:
prevBtn.setEnabled(False)
layout.addWidget(prevBtn)
nextBtn = QtWidgets.QPushButton('Next')
layout.addWidget(nextBtn)
if page == pageCount - 1:
nextBtn.setEnabled(False)
transitionWidget.addWidget(widget)
prevBtn.clicked.connect(lambda _, page=page: transitionWidget.setCurrentIndex(page - 1))
nextBtn.clicked.connect(lambda _, page=page: transitionWidget.setCurrentIndex(page + 1))
sep = QtWidgets.QFrame(frameShape=QtWidgets.QFrame.VLine)
mainLayout.addWidget(sep)
orientationCombo = QtWidgets.QComboBox()
orientationLayout = QtWidgets.QFormLayout()
mainLayout.addLayout(orientationLayout)
orientationCombo.addItems(['Horizontal', 'Vertical'])
orientationCombo.currentIndexChanged.connect(lambda o: transitionWidget.setOrientation(o + 1))
orientationLayout.addRow('Orientation', orientationCombo)
durationSpin = QtWidgets.QSpinBox(minimum=50, maximum=1000, singleStep=50, suffix='ms')
orientationLayout.addRow('Duration', durationSpin)
durationSpin.setValue(transitionWidget._animation.duration())
durationSpin.valueChanged.connect(transitionWidget.setDuration)
mainWidget.show()
sys.exit(app.exec_())

Highlight Cell in a QTableWidget PyQt5

I have a pyqt5 QTableWidget and i want to highight a specific cell of the table
For example I give row 2 and column 2, and I want that cell to be highlighted to edit,i mean for the element in blue
My Table
def createTable(self):
# Create table
self.tableWidget = QTableWidget()
self.tableWidget.setRowCount(4096)
self.tableWidget.setColumnCount(16)
horizantalLabels = []
verticalLabels = []
for i in range(16):
horizantalLabels.append(funciones.tohex(i,8))
for i in range(4096):
verticalLabels.append(funciones.tohex(i*16,16))
self.tableWidget.setHorizontalHeaderLabels(horizantalLabels)
self.tableWidget.setVerticalHeaderLabels(verticalLabels)
# table selection change6
for i in range(4096):
for j in range(16):
self.tableWidget.setItem(i, j, QTableWidgetItem("00"))
self.tableWidget.setColumnWidth(j, 29)
self.tableWidget.setItemDelegate(HexDelegate())
self.tableWidget.cellChanged.connect(self.changed)
def goToPosition(self):
position = self.goTo.text()
self.tableWidget.scrollToBottom()
#print(position)
lenght = len(position)
if lenght > 3:
position_Row = position[:3]
position_Column = position[3:]
print(position_Row)
print(position_Column)
position_Row = int(position_Row, 16)
position_Column = int(position_Column,16)
print("row " + str(position_Row))
positionCell = self.tableWidget.item(position_Row, position_Column)
self.tableWidget.scrollToItem(positionCell)
in the method, go to position i want to highlight the cell
If you want to select items from a QTableWidget you must use the setRangeSelected method, this requires that the range of the selection, in your case would be the respective row and column, then I show an example:
class Widget(QWidget):
def __init__(self, *args, **kwargs):
QWidget.__init__(self, *args, **kwargs)
hlayout = QHBoxLayout()
self.rbox = QSpinBox(self)
self.cbox = QSpinBox(self)
hlayout.addWidget(self.rbox)
hlayout.addWidget(self.cbox)
vlayout = QVBoxLayout(self)
vlayout.addLayout(hlayout)
nrows = 5
ncols = 5
self.rbox.setMaximum(nrows-1)
self.cbox.setMaximum(ncols-1)
self.table = QTableWidget(nrows, ncols, self)
vlayout.addWidget(self.table)
for r in range(nrows):
for c in range(nrows):
it = QTableWidgetItem("{}-{}".format(r, c))
self.table.setItem(r, c, it)
self.rbox.valueChanged.connect(self.selectItem)
self.cbox.valueChanged.connect(self.selectItem)
self.selectItem()
def selectItem(self):
self.table.clearSelection()
x = self.rbox.value()
y = self.cbox.value()
self.table.setRangeSelected(QTableWidgetSelectionRange(x, y, x, y), True)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())
I found using the setItemSelected() method to be more convenient, if you need to highlight a single cell: all you have to do is pass in the item and set the boolean (no need for QTableWidgetSelectionRange and the coords).

Create text area (textEdit) with line number in PyQt

I want to create textEdit with line number on the left side in PyQt like Notepad++. I tried this adding another textEdit but scrolling is stuck. I searched and found this question, but there is no good solution for it.
Is this what you are looking for CodeEditor example in pyqt based on the c++ http://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html
Putting it together for python3 (Im using PyQt4 not 5 but I guess it is similar) (and using QPlainTextEdit not QTextEdit see QTextEdit vs QPlainTextEdit ):
import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import numpy as np
class LineNumberArea(QWidget):
def __init__(self, editor):
super().__init__(editor)
self.myeditor = editor
def sizeHint(self):
return Qsize(self.editor.lineNumberAreaWidth(), 0)
def paintEvent(self, event):
self.myeditor.lineNumberAreaPaintEvent(event)
class CodeEditor(QPlainTextEdit):
def __init__(self):
super().__init__()
self.lineNumberArea = LineNumberArea(self)
self.connect(self, SIGNAL('blockCountChanged(int)'), self.updateLineNumberAreaWidth)
self.connect(self, SIGNAL('updateRequest(QRect,int)'), self.updateLineNumberArea)
self.connect(self, SIGNAL('cursorPositionChanged()'), self.highlightCurrentLine)
self.updateLineNumberAreaWidth(0)
def lineNumberAreaWidth(self):
digits = 1
count = max(1, self.blockCount())
while count >= 10:
count /= 10
digits += 1
space = 3 + self.fontMetrics().width('9') * digits
return space
def updateLineNumberAreaWidth(self, _):
self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0)
def updateLineNumberArea(self, rect, dy):
if dy:
self.lineNumberArea.scroll(0, dy)
else:
self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(),
rect.height())
if rect.contains(self.viewport().rect()):
self.updateLineNumberAreaWidth(0)
def resizeEvent(self, event):
super().resizeEvent(event)
cr = self.contentsRect();
self.lineNumberArea.setGeometry(QRect(cr.left(), cr.top(),
self.lineNumberAreaWidth(), cr.height()))
def lineNumberAreaPaintEvent(self, event):
mypainter = QPainter(self.lineNumberArea)
mypainter.fillRect(event.rect(), Qt.lightGray)
block = self.firstVisibleBlock()
blockNumber = block.blockNumber()
top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top()
bottom = top + self.blockBoundingRect(block).height()
# Just to make sure I use the right font
height = self.fontMetrics().height()
while block.isValid() and (top <= event.rect().bottom()):
if block.isVisible() and (bottom >= event.rect().top()):
number = str(blockNumber + 1)
mypainter.setPen(Qt.black)
mypainter.drawText(0, top, self.lineNumberArea.width(), height,
Qt.AlignRight, number)
block = block.next()
top = bottom
bottom = top + self.blockBoundingRect(block).height()
blockNumber += 1
def highlightCurrentLine(self):
extraSelections = []
if not self.isReadOnly():
selection = QTextEdit.ExtraSelection()
lineColor = QColor(Qt.yellow).lighter(160)
selection.format.setBackground(lineColor)
selection.format.setProperty(QTextFormat.FullWidthSelection, True)
selection.cursor = self.textCursor()
selection.cursor.clearSelection()
extraSelections.append(selection)
self.setExtraSelections(extraSelections)
if __name__ == "__main__":
app = QApplication(sys.argv)
txt = CodeEditor()
txt.show()
sys.exit(app.exec_())
It's my code of PyQt5 and python3 which combined #Dan-Dev and #Axel Schneider. You can directly run it, or simply import use code from QCodeEditor import QCodeEditor.
#!/usr/bin/python3
# QcodeEditor.py by acbetter.
# -*- coding: utf-8 -*-
from PyQt5.QtCore import Qt, QRect, QSize
from PyQt5.QtWidgets import QWidget, QPlainTextEdit, QTextEdit
from PyQt5.QtGui import QColor, QPainter, QTextFormat
class QLineNumberArea(QWidget):
def __init__(self, editor):
super().__init__(editor)
self.codeEditor = editor
def sizeHint(self):
return QSize(self.editor.lineNumberAreaWidth(), 0)
def paintEvent(self, event):
self.codeEditor.lineNumberAreaPaintEvent(event)
class QCodeEditor(QPlainTextEdit):
def __init__(self, parent=None):
super().__init__(parent)
self.lineNumberArea = QLineNumberArea(self)
self.blockCountChanged.connect(self.updateLineNumberAreaWidth)
self.updateRequest.connect(self.updateLineNumberArea)
self.cursorPositionChanged.connect(self.highlightCurrentLine)
self.updateLineNumberAreaWidth(0)
def lineNumberAreaWidth(self):
digits = 1
max_value = max(1, self.blockCount())
while max_value >= 10:
max_value /= 10
digits += 1
space = 3 + self.fontMetrics().width('9') * digits
return space
def updateLineNumberAreaWidth(self, _):
self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0)
def updateLineNumberArea(self, rect, dy):
if dy:
self.lineNumberArea.scroll(0, dy)
else:
self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height())
if rect.contains(self.viewport().rect()):
self.updateLineNumberAreaWidth(0)
def resizeEvent(self, event):
super().resizeEvent(event)
cr = self.contentsRect()
self.lineNumberArea.setGeometry(QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()))
def highlightCurrentLine(self):
extraSelections = []
if not self.isReadOnly():
selection = QTextEdit.ExtraSelection()
lineColor = QColor(Qt.yellow).lighter(160)
selection.format.setBackground(lineColor)
selection.format.setProperty(QTextFormat.FullWidthSelection, True)
selection.cursor = self.textCursor()
selection.cursor.clearSelection()
extraSelections.append(selection)
self.setExtraSelections(extraSelections)
def lineNumberAreaPaintEvent(self, event):
painter = QPainter(self.lineNumberArea)
painter.fillRect(event.rect(), Qt.lightGray)
block = self.firstVisibleBlock()
blockNumber = block.blockNumber()
top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top()
bottom = top + self.blockBoundingRect(block).height()
# Just to make sure I use the right font
height = self.fontMetrics().height()
while block.isValid() and (top <= event.rect().bottom()):
if block.isVisible() and (bottom >= event.rect().top()):
number = str(blockNumber + 1)
painter.setPen(Qt.black)
painter.drawText(0, top, self.lineNumberArea.width(), height, Qt.AlignRight, number)
block = block.next()
top = bottom
bottom = top + self.blockBoundingRect(block).height()
blockNumber += 1
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
codeEditor = QCodeEditor()
codeEditor.show()
sys.exit(app.exec_())
It runs like this.
By the way, if you want use it in your Qt Designer, you should do it like this and place the *.ui file in the same directory of your QCodeEditor.py file's path unless you set the environment variable. And, you need convert your *.ui file to *.py file by the command pyuic5 -x *.ui -o *.py. Hope Helpful~
here a version for PyQt5 (with Menu etc ...)
#!/usr/bin/python3
# -- coding: utf-8 --
from PyQt5.QtWidgets import QPlainTextEdit, QWidget, QVBoxLayout, QApplication, QFileDialog, QMessageBox, QHBoxLayout, \
QFrame, QTextEdit, QToolBar, QComboBox, QLabel, QAction, QLineEdit, QToolButton, QMenu, QMainWindow
from PyQt5.QtGui import QIcon, QPainter, QTextFormat, QColor, QTextCursor, QKeySequence, QClipboard, QTextCharFormat, QPalette
from PyQt5.QtCore import Qt, QVariant, QRect, QDir, QFile, QFileInfo, QTextStream, QRegExp, QSettings
import sys, os
lineBarColor = QColor("#ACDED5")
lineHighlightColor = QColor("#ACDED5")
class NumberBar(QWidget):
def __init__(self, parent = None):
super(NumberBar, self).__init__(parent)
self.editor = parent
layout = QVBoxLayout()
self.setLayout(layout)
self.editor.blockCountChanged.connect(self.update_width)
self.editor.updateRequest.connect(self.update_on_scroll)
self.update_width('1')
def update_on_scroll(self, rect, scroll):
if self.isVisible():
if scroll:
self.scroll(0, scroll)
else:
self.update()
def update_width(self, string):
width = self.fontMetrics().width(str(string)) + 10
if self.width() != width:
self.setFixedWidth(width)
def paintEvent(self, event):
if self.isVisible():
block = self.editor.firstVisibleBlock()
height = self.fontMetrics().height()
number = block.blockNumber()
painter = QPainter(self)
painter.fillRect(event.rect(), lineBarColor)
painter.drawRect(0, 0, event.rect().width() - 1, event.rect().height() - 1)
font = painter.font()
current_block = self.editor.textCursor().block().blockNumber() + 1
condition = True
while block.isValid() and condition:
block_geometry = self.editor.blockBoundingGeometry(block)
offset = self.editor.contentOffset()
block_top = block_geometry.translated(offset).top()
number += 1
rect = QRect(0, block_top, self.width() - 5, height)
if number == current_block:
font.setBold(True)
else:
font.setBold(False)
painter.setFont(font)
painter.drawText(rect, Qt.AlignRight, '%i'%number)
if block_top > event.rect().bottom():
condition = False
block = block.next()
painter.end()
class myEditor(QMainWindow):
def __init__(self, parent = None):
super(myEditor, self).__init__(parent)
self.MaxRecentFiles = 5
self.windowList = []
self.recentFileActs = []
self.setAttribute(Qt.WA_DeleteOnClose)
# Editor Widget ...
QIcon.setThemeName('Faenza-Dark')
self.editor = QPlainTextEdit()
self.editor.setStyleSheet(stylesheet2(self))
self.editor.setFrameStyle(QFrame.NoFrame)
self.editor.setTabStopWidth(14)
self.extra_selections = []
self.fname = ""
self.filename = ""
# Line Numbers ...
self.numbers = NumberBar(self.editor)
self.createActions()
# Laying out...
layoutH = QHBoxLayout()
layoutH.setSpacing(1.5)
layoutH.addWidget(self.numbers)
layoutH.addWidget(self.editor)
### begin toolbar
tb = QToolBar(self)
tb.setWindowTitle("File Toolbar")
self.newAct = QAction("&New", self, shortcut=QKeySequence.New,
statusTip="Create a new file", triggered=self.newFile)
self.newAct.setIcon(QIcon.fromTheme("document-new"))
self.openAct = QAction("&Open", self, shortcut=QKeySequence.Open,
statusTip="open file", triggered=self.openFile)
self.openAct.setIcon(QIcon.fromTheme("document-open"))
self.saveAct = QAction("&Save", self, shortcut=QKeySequence.Save,
statusTip="save file", triggered=self.fileSave)
self.saveAct.setIcon(QIcon.fromTheme("document-save"))
self.saveAsAct = QAction("&Save as ...", self, shortcut=QKeySequence.SaveAs,
statusTip="save file as ...", triggered=self.fileSaveAs)
self.saveAsAct.setIcon(QIcon.fromTheme("document-save-as"))
self.exitAct = QAction("Exit", self, shortcut=QKeySequence.Quit,
toolTip="Exit", triggered=self.handleQuit)
self.exitAct.setIcon(QIcon.fromTheme("application-exit"))
### find / replace toolbar
self.tbf = QToolBar(self)
self.tbf.setWindowTitle("Find Toolbar")
self.findfield = QLineEdit()
self.findfield.addAction(QIcon.fromTheme("edit-find"), QLineEdit.LeadingPosition)
self.findfield.setClearButtonEnabled(True)
self.findfield.setFixedWidth(150)
self.findfield.setPlaceholderText("find")
self.findfield.setToolTip("press RETURN to find")
self.findfield.setText("")
ft = self.findfield.text()
self.findfield.returnPressed.connect(self.findText)
self.tbf.addWidget(self.findfield)
self.replacefield = QLineEdit()
self.replacefield.addAction(QIcon.fromTheme("edit-find-and-replace"), QLineEdit.LeadingPosition)
self.replacefield.setClearButtonEnabled(True)
self.replacefield.setFixedWidth(150)
self.replacefield.setPlaceholderText("replace with")
self.replacefield.setToolTip("press RETURN to replace the first")
self.replacefield.returnPressed.connect(self.replaceOne)
self.tbf.addSeparator()
self.tbf.addWidget(self.replacefield)
self.tbf.addSeparator()
self.tbf.addAction("replace all", self.replaceAll)
self.tbf.addSeparator()
layoutV = QVBoxLayout()
bar=self.menuBar()
self.filemenu=bar.addMenu("File")
self.separatorAct = self.filemenu.addSeparator()
self.filemenu.addAction(self.newAct)
self.filemenu.addAction(self.openAct)
self.filemenu.addAction(self.saveAct)
self.filemenu.addAction(self.saveAsAct)
self.filemenu.addSeparator()
for i in range(self.MaxRecentFiles):
self.filemenu.addAction(self.recentFileActs[i])
self.updateRecentFileActions()
self.filemenu.addSeparator()
self.filemenu.addAction(self.exitAct)
bar.setStyleSheet(stylesheet2(self))
editmenu = bar.addMenu("Edit")
editmenu.addAction(QAction(QIcon.fromTheme('edit-copy'), "Copy", self, triggered = self.editor.copy, shortcut = QKeySequence.Copy))
editmenu.addAction(QAction(QIcon.fromTheme('edit-cut'), "Cut", self, triggered = self.editor.cut, shortcut = QKeySequence.Cut))
editmenu.addAction(QAction(QIcon.fromTheme('edit-paste'), "Paste", self, triggered = self.editor.paste, shortcut = QKeySequence.Paste))
editmenu.addAction(QAction(QIcon.fromTheme('edit-delete'), "Delete", self, triggered = self.editor.cut, shortcut = QKeySequence.Delete))
editmenu.addSeparator()
editmenu.addAction(QAction(QIcon.fromTheme('edit-select-all'), "Select All", self, triggered = self.editor.selectAll, shortcut = QKeySequence.SelectAll))
layoutV.addWidget(bar)
layoutV.addWidget(self.tbf)
layoutV.addLayout(layoutH)
### main window
mq = QWidget(self)
mq.setLayout(layoutV)
self.setCentralWidget(mq)
# Event Filter ...
self.installEventFilter(self)
self.editor.setFocus()
self.cursor = QTextCursor()
self.editor.setPlainText("hello")
self.editor.moveCursor(self.cursor.End)
self.editor.document().modificationChanged.connect(self.setWindowModified)
# Brackets ExtraSelection ...
self.left_selected_bracket = QTextEdit.ExtraSelection()
self.right_selected_bracket = QTextEdit.ExtraSelection()
def createActions(self):
for i in range(self.MaxRecentFiles):
self.recentFileActs.append(
QAction(self, visible=False,
triggered=self.openRecentFile))
def openRecentFile(self):
action = self.sender()
if action:
if (self.maybeSave()):
self.openFileOnStart(action.data())
### New File
def newFile(self):
if self.maybeSave():
self.editor.clear()
self.editor.setPlainText("")
self.filename = ""
self.setModified(False)
self.editor.moveCursor(self.cursor.End)
### open File
def openFileOnStart(self, path=None):
if path:
inFile = QFile(path)
if inFile.open(QFile.ReadWrite | QFile.Text):
text = inFile.readAll()
try:
# Python v3.
text = str(text, encoding = 'utf8')
except TypeError:
# Python v2.
text = str(text)
self.editor.setPlainText(text)
self.filename = path
self.setModified(False)
self.fname = QFileInfo(path).fileName()
self.setWindowTitle(self.fname + "[*]")
self.document = self.editor.document()
self.setCurrentFile(self.filename)
### open File
def openFile(self, path=None):
if self.maybeSave():
if not path:
path, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.homePath() + "/Documents/",
"Text Files (*.txt *.csv *.py);;All Files (*.*)")
if path:
inFile = QFile(path)
if inFile.open(QFile.ReadWrite | QFile.Text):
text = inFile.readAll()
try:
# Python v3.
text = str(text, encoding = 'utf8')
except TypeError:
# Python v2.
text = str(text)
self.editor.setPlainText(text)
self.filename = path
self.setModified(False)
self.fname = QFileInfo(path).fileName()
self.setWindowTitle(self.fname + "[*]")
self.document = self.editor.document()
self.setCurrentFile(self.filename)
def fileSave(self):
if (self.filename != ""):
file = QFile(self.filename)
print(self.filename)
if not file.open( QFile.WriteOnly | QFile.Text):
QMessageBox.warning(self, "Error",
"Cannot write file %s:\n%s." % (self.filename, file.errorString()))
return
outstr = QTextStream(file)
QApplication.setOverrideCursor(Qt.WaitCursor)
outstr << self.editor.toPlainText()
QApplication.restoreOverrideCursor()
self.setModified(False)
self.fname = QFileInfo(self.filename).fileName()
self.setWindowTitle(self.fname + "[*]")
self.setCurrentFile(self.filename)
else:
self.fileSaveAs()
### save File
def fileSaveAs(self):
fn, _ = QFileDialog.getSaveFileName(self, "Save as...", self.filename,
"Python files (*.py)")
if not fn:
print("Error saving")
return False
lfn = fn.lower()
if not lfn.endswith('.py'):
fn += '.py'
self.filename = fn
self.fname = os.path.splitext(str(fn))[0].split("/")[-1]
return self.fileSave()
def closeEvent(self, e):
if self.maybeSave():
e.accept()
else:
e.ignore()
### ask to save
def maybeSave(self):
if not self.isModified():
return True
if self.filename.startswith(':/'):
return True
ret = QMessageBox.question(self, "Message",
"<h4><p>The document was modified.</p>\n" \
"<p>Do you want to save changes?</p></h4>",
QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if ret == QMessageBox.Yes:
if self.filename == "":
self.fileSaveAs()
return False
else:
self.fileSave()
return True
if ret == QMessageBox.Cancel:
return False
return True
def findText(self):
ft = self.findfield.text()
if self.editor.find(ft):
return
else:
self.editor.moveCursor(1)
if self.editor.find(ft):
self.editor.moveCursor(QTextCursor.Start, QTextCursor.MoveAnchor)
def handleQuit(self):
print("Goodbye ...")
app.quit()
def set_numbers_visible(self, value = True):
self.numbers.setVisible(False)
def match_left(self, block, character, start, found):
map = {'{': '}', '(': ')', '[': ']'}
while block.isValid():
data = block.userData()
if data is not None:
braces = data.braces
N = len(braces)
for k in range(start, N):
if braces[k].character == character:
found += 1
if braces[k].character == map[character]:
if not found:
return braces[k].position + block.position()
else:
found -= 1
block = block.next()
start = 0
def match_right(self, block, character, start, found):
map = {'}': '{', ')': '(', ']': '['}
while block.isValid():
data = block.userData()
if data is not None:
braces = data.braces
if start is None:
start = len(braces)
for k in range(start - 1, -1, -1):
if braces[k].character == character:
found += 1
if braces[k].character == map[character]:
if found == 0:
return braces[k].position + block.position()
else:
found -= 1
block = block.previous()
start = None
# '''
cursor = self.editor.textCursor()
block = cursor.block()
data = block.userData()
previous, next = None, None
if data is not None:
position = cursor.position()
block_position = cursor.block().position()
braces = data.braces
N = len(braces)
for k in range(0, N):
if braces[k].position == position - block_position or braces[k].position == position - block_position - 1:
previous = braces[k].position + block_position
if braces[k].character in ['{', '(', '[']:
next = self.match_left(block,
braces[k].character,
k + 1, 0)
elif braces[k].character in ['}', ')', ']']:
next = self.match_right(block,
braces[k].character,
k, 0)
if next is None:
next = -1
if next is not None and next > 0:
if next == 0 and next >= 0:
format = QTextCharFormat()
cursor.setPosition(previous)
cursor.movePosition(QTextCursor.NextCharacter,
QTextCursor.KeepAnchor)
format.setBackground(QColor('white'))
self.left_selected_bracket.format = format
self.left_selected_bracket.cursor = cursor
cursor.setPosition(next)
cursor.movePosition(QTextCursor.NextCharacter,
QTextCursor.KeepAnchor)
format.setBackground(QColor('white'))
self.right_selected_bracket.format = format
self.right_selected_bracket.cursor = cursor
# '''
def paintEvent(self, event):
highlighted_line = QTextEdit.ExtraSelection()
highlighted_line.format.setBackground(lineHighlightColor)
highlighted_line.format.setProperty(QTextFormat
.FullWidthSelection,
QVariant(True))
highlighted_line.cursor = self.editor.textCursor()
highlighted_line.cursor.clearSelection()
self.editor.setExtraSelections([highlighted_line,
self.left_selected_bracket,
self.right_selected_bracket])
def document(self):
return self.editor.document
def isModified(self):
return self.editor.document().isModified()
def setModified(self, modified):
self.editor.document().setModified(modified)
def setLineWrapMode(self, mode):
self.editor.setLineWrapMode(mode)
def clear(self):
self.editor.clear()
def setPlainText(self, *args, **kwargs):
self.editor.setPlainText(*args, **kwargs)
def setDocumentTitle(self, *args, **kwargs):
self.editor.setDocumentTitle(*args, **kwargs)
def set_number_bar_visible(self, value):
self.numbers.setVisible(value)
def replaceAll(self):
print("replacing all")
oldtext = self.editor.document().toPlainText()
newtext = oldtext.replace(self.findfield.text(), self.replacefield.text())
self.editor.setPlainText(newtext)
self.setModified(True)
def replaceOne(self):
print("replacing all")
oldtext = self.editor.document().toPlainText()
newtext = oldtext.replace(self.findfield.text(), self.replacefield.text(), 1)
self.editor.setPlainText(newtext)
self.setModified(True)
def setCurrentFile(self, fileName):
self.curFile = fileName
if self.curFile:
self.setWindowTitle("%s - Recent Files" % self.strippedName(self.curFile))
else:
self.setWindowTitle("Recent Files")
settings = QSettings('Axel Schneider', 'PTEdit')
files = settings.value('recentFileList')
try:
files.remove(fileName)
except ValueError:
pass
files.insert(0, fileName)
del files[self.MaxRecentFiles:]
settings.setValue('recentFileList', files)
for widget in QApplication.topLevelWidgets():
if isinstance(widget, myEditor):
widget.updateRecentFileActions()
def updateRecentFileActions(self):
mytext = ""
settings = QSettings('Axel Schneider', 'PTEdit')
files = settings.value('recentFileList')
numRecentFiles = min(len(files), self.MaxRecentFiles)
for i in range(numRecentFiles):
text = "&%d %s" % (i + 1, self.strippedName(files[i]))
self.recentFileActs[i].setText(text)
self.recentFileActs[i].setData(files[i])
self.recentFileActs[i].setVisible(True)
for j in range(numRecentFiles, self.MaxRecentFiles):
self.recentFileActs[j].setVisible(False)
self.separatorAct.setVisible((numRecentFiles > 0))
def clearRecentFileList(self, fileName):
self.rmenu.clear()
def strippedName(self, fullFileName):
return QFileInfo(fullFileName).fileName()
def stylesheet2(self):
return """
QPlainTextEdit
{
background: #ECECEC;
color: #202020;
border: 1px solid #1EAE3D;
selection-background-color: #505050;
selection-color: #ACDED5;
}
QMenu
{
background: #F2F2F2;
color: #0E185F;
border: 1px solid #1EAE3D;
selection-background-color: #ACDED5;
}
"""
if __name__ == '__main__':
app = QApplication(sys.argv)
win = myEditor()
win.setWindowIcon(QIcon.fromTheme("application-text"))
win.setWindowTitle("Plain Text Edit" + "[*]")
win.setMinimumSize(640,250)
win.showMaximized()
if len(sys.argv) > 1:
print(sys.argv[1])
win.openFileOnStart(sys.argv[1])
app.exec_()

Categories

Resources