Skip to content

Commit e88549d

Browse files
committed
Bugfixes, progressbar.
1 parent 1e83f20 commit e88549d

10 files changed

Lines changed: 262 additions & 30 deletions

File tree

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
# DiffCast
1+
<p style="text-align:center">
2+
<img src="diffcast/images/icon.png" />
3+
</p>
24

3-
Auto-generate coding demos & screencasts from Python files.
5+
6+
**Auto-generate coding demos & screencasts from Python files.**
47

58
Provided with a series of Python files, DiffCast automatically generates coding
69
demos/screencasts using human-like edits. Designed for creating reproducible
@@ -10,6 +13,7 @@ tutoring examples, library demos or demonstrating code to a class.
1013

1114
## What?
1215

16+
If you make programming screencasts
1317

1418

1519

@@ -29,10 +33,10 @@ tutoring examples, library demos or demonstrating code to a class.
2933

3034
Below are some examples of screencasts created using DiffCast. These are short examples, to keep things readable but there is no limit to the number of transition files you can use, or how long the resulting DiffCast can be.
3135

32-
_Short demo, using the 4 demoN.py files in /demos. Demonstrates the block indenting behavior_
36+
_Short demo, using the 4 demoN.py files in /demos. Demonstrates the block indenting behavior_
3337
<video src="https://user-images.githubusercontent.com/126239/151127893-5c98ba8d-c431-4a25-bb1f-e0b33645a2b6.mp4"></video>
3438

35-
_Longer demo, using the 6 windows_N.py files in /demos. Demonstrates more complex edits, whitespace padding._
39+
_Longer demo, using the 6 windows_N.py files in /demos. Demonstrates more complex edits, whitespace padding._
3640
<video src="https://user-images.githubusercontent.com/126239/151128026-531c46db-30cb-466a-a836-8818718a2b13.mp4"></video>
3741

3842

@@ -75,7 +79,7 @@ When you edit a file you *generally* go from top to bottom, but you don't enter
7579

7680
* If you're adding a block of code which is followed by a blank line, a trailing blank line will be added first (adding space around the new code).
7781
* Changes to the middle of lines will be edited in the middle of the line, leaving leading and trailing parts intact during editing.
78-
* Whitespace is added using in blocks of 4 (emulating tab indent) where possible.
82+
* Whitespace is added in blocks of 4 (emulating tab indent) where possible.
7983
* Blocks are indented as a whole, where possible.
8084

8185
This is a work in progress and new edits may be added.

demos/windows_1.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import sys
2+
3+
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget
4+
5+
6+
class AnotherWindow(QWidget):
7+
"""
8+
This "window" is a QWidget. If it has no parent, it
9+
will appear as a free-floating window.
10+
"""
11+
12+
def __init__(self):
13+
super().__init__()
14+
layout = QVBoxLayout()
15+
self.label = QLabel("Another Window")
16+
layout.addWidget(self.label)
17+
self.setLayout(layout)
18+
19+
20+
class MainWindow(QMainWindow):
21+
def __init__(self):
22+
super().__init__()
23+
self.button = QPushButton("Push for Window")
24+
self.button.clicked.connect(self.show_new_window)
25+
self.setCentralWidget(self.button)
26+
27+
def show_new_window(self, checked):
28+
w = AnotherWindow()
29+
w.show()
30+
31+
32+
app = QApplication(sys.argv)
33+
w = MainWindow()
34+
w.show()
35+
app.exec_()

demos/windows_2.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import sys
2+
from random import randint
3+
4+
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget
5+
6+
7+
class AnotherWindow(QWidget):
8+
"""
9+
This "window" is a QWidget. If it has no parent, it
10+
will appear as a free-floating window.
11+
"""
12+
13+
def __init__(self):
14+
super().__init__()
15+
layout = QVBoxLayout()
16+
self.label = QLabel("Another Window % d" % randint(0, 100))
17+
layout.addWidget(self.label)
18+
self.setLayout(layout)
19+
20+
21+
class MainWindow(QMainWindow):
22+
def __init__(self):
23+
super().__init__()
24+
self.button = QPushButton("Push for Window")
25+
self.button.clicked.connect(self.show_new_window)
26+
self.setCentralWidget(self.button)
27+
28+
def show_new_window(self, checked):
29+
self.w = AnotherWindow()
30+
self.w.show()
31+
32+
33+
app = QApplication(sys.argv)
34+
w = MainWindow()
35+
w.show()
36+
app.exec_()

demos/windows_3.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import sys
2+
from random import randint
3+
4+
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget
5+
6+
7+
class AnotherWindow(QWidget):
8+
"""
9+
This "window" is a QWidget. If it has no parent, it
10+
will appear as a free-floating window.
11+
"""
12+
13+
def __init__(self):
14+
super().__init__()
15+
layout = QVBoxLayout()
16+
self.label = QLabel("Another Window % d" % randint(0, 100))
17+
layout.addWidget(self.label)
18+
self.setLayout(layout)
19+
20+
21+
class MainWindow(QMainWindow):
22+
def __init__(self):
23+
super().__init__()
24+
self.w = None # No external window yet.
25+
self.button = QPushButton("Push for Window")
26+
self.button.clicked.connect(self.show_new_window)
27+
self.setCentralWidget(self.button)
28+
29+
def show_new_window(self, checked):
30+
if self.w is None:
31+
self.w = AnotherWindow()
32+
self.w.show()
33+
34+
35+
app = QApplication(sys.argv)
36+
w = MainWindow()
37+
w.show()
38+
app.exec_()

demos/windows_4.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import sys
2+
from random import randint
3+
4+
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget
5+
6+
7+
class AnotherWindow(QWidget):
8+
"""
9+
This "window" is a QWidget. If it has no parent, it
10+
will appear as a free-floating window.
11+
"""
12+
13+
def __init__(self):
14+
super().__init__()
15+
layout = QVBoxLayout()
16+
self.label = QLabel("Another Window % d" % randint(0, 100))
17+
layout.addWidget(self.label)
18+
self.setLayout(layout)
19+
20+
21+
class MainWindow(QMainWindow):
22+
def __init__(self):
23+
super().__init__()
24+
self.w = None # No external window yet.
25+
self.button = QPushButton("Push for Window")
26+
self.button.clicked.connect(self.show_new_window)
27+
self.setCentralWidget(self.button)
28+
29+
def show_new_window(self, checked):
30+
if self.w is None:
31+
self.w = AnotherWindow()
32+
self.w.show()
33+
34+
else:
35+
self.w = None # Discard reference, close window.
36+
37+
38+
app = QApplication(sys.argv)
39+
w = MainWindow()
40+
w.show()
41+
app.exec_()

demos/windows_5.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import sys
2+
from random import randint
3+
4+
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget
5+
6+
7+
class AnotherWindow(QWidget):
8+
"""
9+
This "window" is a QWidget. If it has no parent, it
10+
will appear as a free-floating window.
11+
"""
12+
13+
def __init__(self):
14+
super().__init__()
15+
layout = QVBoxLayout()
16+
self.label = QLabel("Another Window % d" % randint(0, 100))
17+
layout.addWidget(self.label)
18+
self.setLayout(layout)
19+
20+
21+
class MainWindow(QMainWindow):
22+
def __init__(self):
23+
super().__init__()
24+
self.w = AnotherWindow()
25+
self.button = QPushButton("Push for Window")
26+
self.button.clicked.connect(self.show_new_window)
27+
self.setCentralWidget(self.button)
28+
29+
def show_new_window(self, checked):
30+
self.w.show()
31+
32+
33+
app = QApplication(sys.argv)
34+
w = MainWindow()
35+
w.show()
36+
app.exec_()

demos/windows_6.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import sys
2+
from random import randint
3+
4+
from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget
5+
6+
7+
class AnotherWindow(QWidget):
8+
"""
9+
This "window" is a QWidget. If it has no parent, it
10+
will appear as a free-floating window.
11+
"""
12+
13+
def __init__(self):
14+
super().__init__()
15+
layout = QVBoxLayout()
16+
self.label = QLabel("Another Window % d" % randint(0, 100))
17+
layout.addWidget(self.label)
18+
self.setLayout(layout)
19+
20+
21+
class MainWindow(QMainWindow):
22+
def __init__(self):
23+
super().__init__()
24+
self.w = AnotherWindow()
25+
self.button = QPushButton("Push for Window")
26+
self.button.clicked.connect(self.toggle_window)
27+
self.setCentralWidget(self.button)
28+
29+
def toggle_window(self, checked):
30+
if self.w.isVisible():
31+
self.w.hide()
32+
33+
else:
34+
self.w.show()
35+
36+
37+
app = QApplication(sys.argv)
38+
w = MainWindow()
39+
w.show()
40+
app.exec_()

diffcast/app.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,11 @@
44

55
from PyQt6.QtCore import QSettings, QSize, Qt, QThreadPool
66
from PyQt6.QtGui import QColor, QIcon, QPalette
7-
from PyQt6.QtWidgets import (
8-
QAbstractItemView,
9-
QApplication,
10-
QCheckBox,
11-
QComboBox,
12-
QFileDialog,
13-
QHBoxLayout,
14-
QLineEdit,
15-
QListWidget,
16-
QListWidgetItem,
17-
QMainWindow,
18-
QMessageBox,
19-
QPushButton,
20-
QVBoxLayout,
21-
QWidget,
22-
)
7+
from PyQt6.QtWidgets import (QAbstractItemView, QApplication, QCheckBox,
8+
QComboBox, QFileDialog, QHBoxLayout, QLineEdit,
9+
QListWidget, QListWidgetItem, QMainWindow,
10+
QMessageBox, QProgressBar, QPushButton,
11+
QVBoxLayout, QWidget)
2312

2413
from diffrunner import DiffRunner
2514
from viewer import DISPLAY_MODES, CodeViewer
@@ -75,6 +64,12 @@ def __init__(self):
7564

7665
vl.addWidget(self.difflist)
7766

67+
self.progress = QProgressBar()
68+
self.progress.setMaximumHeight(5)
69+
self.progress.setRange(0, 100)
70+
self.progress.setTextVisible(False)
71+
vl.addWidget(self.progress)
72+
7873
controls = QHBoxLayout()
7974
self.start_btn = QPushButton("Start")
8075
self.start_btn.pressed.connect(self.start)
@@ -217,6 +212,7 @@ def diff(self, files):
217212
self.runner.signals.file_changed.connect(self.diff_file_changed)
218213
self.runner.signals.file_complete.connect(self.differ_file_complete)
219214
self.runner.signals.completed.connect(self.differ_complete)
215+
self.runner.signals.progress.connect(self.progress.setValue)
220216

221217
self.stop_btn.pressed.connect(self.runner.quit)
222218

@@ -230,7 +226,7 @@ def open_file_dialog(self):
230226
if not paths:
231227
return
232228

233-
# Sort alphanumerically before adding.
229+
#  Sort alphanumerically before adding.
234230
# FIXME: Split numeric suffix, something smarter?
235231
paths.sort()
236232

diffcast/diffrunner.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Signals(QObject):
3636
file_changed = pyqtSignal(str)
3737
file_complete = pyqtSignal(str, list)
3838
completed = pyqtSignal()
39+
progress = pyqtSignal(int)
3940

4041

4142
class DiffRunner(QRunnable):
@@ -169,6 +170,7 @@ def run(self):
169170
with open(initial_file, 'r') as f1:
170171
self.current = f1.readlines()
171172

173+
self.signals.progress.emit(0)
172174
self.signals.file_changed.emit(initial_file)
173175
self.signals.file_complete.emit(initial_file, self.current)
174176

@@ -179,12 +181,14 @@ def run(self):
179181
self.signals.updated.emit(last_line, last_char, self.current)
180182
time.sleep(INITIAL_SPEED)
181183

182-
for file in files:
184+
n_files = len(self.files) # So we don't hit 100% until last file is complete.
185+
for file_n, file in enumerate(files, 1):
183186

184187
if self._quit_requested:
185188
break
186189

187190
self.signals.file_changed.emit(file)
191+
self.signals.progress.emit(int(file_n / n_files * 100))
188192

189193
with open(file, 'r') as f2:
190194
target = f2.readlines()
@@ -293,4 +297,5 @@ def run(self):
293297
self.signals.file_complete.emit(file, self.current)
294298

295299
# We're finished.
300+
self.signals.progress.emit(100)
296301
self.signals.completed.emit()

diffcast/viewer.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,6 @@ def __init__(self, parent=None):
4949
lexer.setDefaultColor(QColor('#d4d4d4'))
5050
lexer.setHighlightSubidentifiers(False)
5151

52-
self.SendScintilla(
53-
QsciScintilla.SCI_SETKEYWORDS,
54-
0,
55-
b"False None True and as assert break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield",
56-
)
57-
self.SendScintilla(QsciScintilla.SCI_SETKEYWORDS, 1, b"self")
58-
5952
"""
6053
0 Default
6154
1 Comment
@@ -115,6 +108,14 @@ def __init__(self, parent=None):
115108
# Hide horizontal scrollbar.
116109
self.SendScintilla(QsciScintilla.SCI_SETHSCROLLBAR, 0)
117110

111+
# Tweak lexer.
112+
self.SendScintilla(
113+
QsciScintilla.SCI_SETKEYWORDS,
114+
0,
115+
b"False None True and as assert break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield",
116+
)
117+
self.SendScintilla(QsciScintilla.SCI_SETKEYWORDS, 1, b"self")
118+
118119

119120
class NoMouseListView(QListView):
120121
def mousePressEvent(self, e):

0 commit comments

Comments
 (0)