├── .gitignore
├── .idea
├── .gitignore
├── inspectionProfiles
│ ├── Project_Default.xml
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
├── pyqt_line_number_widget.iml
└── vcs.xml
├── LICENSE
├── README.md
├── pyqt_line_number_widget
├── __init__.py
└── lineNumberWidget.py
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/pyqt_line_number_widget.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Jung Gyu Yoon
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pyqt-line-number-widget
2 | Show line numbers next to QTextBrowser or QTextEdit
3 |
4 | ## Requirements
5 | * PyQt5 >= 5.8
6 |
7 | ## Setup
8 | `python -m pip install pyqt-line-number-widget`
9 |
10 | ## Example
11 | Code Sample
12 | ```python
13 | from PyQt5.QtWidgets import QApplication, QHBoxLayout, QTextEdit, QWidget
14 | from pyqt_line_number_widget import LineNumberWidget
15 |
16 |
17 | class MainWindow(QWidget):
18 | def __init__(self):
19 | super().__init__()
20 | self.__initUi()
21 |
22 | def __initUi(self):
23 | self.__te = QTextEdit()
24 | self.__te.textChanged.connect(self.__line_widget_line_count_changed)
25 | self.__lineWidget = LineNumberWidget(self.__te)
26 |
27 | lay = QHBoxLayout()
28 | lay.addWidget(self.__lineWidget)
29 | lay.addWidget(self.__te)
30 |
31 | self.setLayout(lay)
32 |
33 | def __line_widget_line_count_changed(self):
34 | if self.__lineWidget:
35 | n = int(self.__te.document().lineCount())
36 | self.__lineWidget.changeLineCount(n)
37 |
38 |
39 | if __name__ == "__main__":
40 | import sys
41 |
42 | app = QApplication(sys.argv)
43 | mainWindow = MainWindow()
44 | mainWindow.show()
45 | app.exec()
46 | ```
47 |
48 | Result
49 |
50 | https://user-images.githubusercontent.com/55078043/174435483-d6e8b57c-5b26-48fd-a5a7-454841e11d69.mp4
51 |
52 |
53 |
--------------------------------------------------------------------------------
/pyqt_line_number_widget/__init__.py:
--------------------------------------------------------------------------------
1 | from .lineNumberWidget import *
--------------------------------------------------------------------------------
/pyqt_line_number_widget/lineNumberWidget.py:
--------------------------------------------------------------------------------
1 | from PyQt5.QtGui import QFont, QTextCursor
2 | from PyQt5.QtWidgets import QTextBrowser
3 | from PyQt5.QtCore import Qt
4 |
5 |
6 | class LineNumberWidget(QTextBrowser):
7 | def __init__(self, widget):
8 | super().__init__()
9 | self.__initUi(widget)
10 |
11 | def __initUi(self, widget):
12 | self.__lineCount = widget.document().lineCount()
13 | self.__size = int(widget.font().pointSizeF())
14 | self.__styleInit()
15 |
16 | self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
17 | self.setTextInteractionFlags(Qt.NoTextInteraction)
18 |
19 | self.verticalScrollBar().setEnabled(False)
20 |
21 | widget.verticalScrollBar().valueChanged.connect(self.__changeLineWidgetScrollAsTargetedWidgetScrollChanged)
22 |
23 | self.__initLineCount()
24 |
25 | def __changeLineWidgetScrollAsTargetedWidgetScrollChanged(self, v):
26 | self.verticalScrollBar().setValue(v)
27 |
28 | def __initLineCount(self):
29 | for n in range(1, self.__lineCount+1):
30 | self.append(str(n))
31 |
32 | def changeLineCount(self, n):
33 | max_one = max(self.__lineCount, n)
34 | diff = n-self.__lineCount
35 | if max_one == self.__lineCount:
36 | first_v = self.verticalScrollBar().value()
37 | for i in range(self.__lineCount, self.__lineCount + diff, -1):
38 | self.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor)
39 | self.moveCursor(QTextCursor.StartOfLine, QTextCursor.MoveAnchor)
40 | self.moveCursor(QTextCursor.End, QTextCursor.KeepAnchor)
41 | self.textCursor().removeSelectedText()
42 | self.textCursor().deletePreviousChar()
43 | last_v = self.verticalScrollBar().value()
44 | if abs(first_v-last_v) != 2:
45 | self.verticalScrollBar().setValue(first_v)
46 | else:
47 | for i in range(self.__lineCount, self.__lineCount + diff, 1):
48 | self.append(str(i + 1))
49 |
50 | self.__lineCount = n
51 |
52 | def setValue(self, v):
53 | self.verticalScrollBar().setValue(v)
54 |
55 | def setFontSize(self, s: float):
56 | self.__size = int(s)
57 | self.__styleInit()
58 |
59 | def __styleInit(self):
60 | self.__style = f'''
61 | QTextBrowser
62 | {{
63 | background: transparent;
64 | border: none;
65 | color: #AAA;
66 | font: {self.__size}pt;
67 | }}
68 | '''
69 | self.setStyleSheet(self.__style)
70 | self.setFixedWidth(self.__size*5)
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 | import codecs
3 | import os
4 |
5 | here = os.path.abspath(os.path.dirname(__file__))
6 |
7 | with codecs.open(os.path.join(here, "README.md"), encoding="utf-8") as fh:
8 | long_description = "\n" + fh.read()
9 |
10 | setup(
11 | name='pyqt-line-number-widget',
12 | version='0.0.1',
13 | author='Jung Gyu Yoon',
14 | author_email='yjg30737@gmail.com',
15 | license='MIT',
16 | packages=find_packages(),
17 | description='QWidget which shows line numbers of QTextBrowser or QTextEdit',
18 | url='https://github.com/yjg30737/pyqt-line-number-widget.git',
19 | long_description_content_type='text/markdown',
20 | long_description=long_description,
21 | install_requires=[
22 | 'PyQt5>=5.8'
23 | ]
24 | )
--------------------------------------------------------------------------------