-
Notifications
You must be signed in to change notification settings - Fork 2
/
app_simple_pyqt5.py
305 lines (257 loc) · 11 KB
/
app_simple_pyqt5.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
"""Simple demo app for common Qt features.
This demo shows basic app init and startup, custom widgets, layouts,
commonly used standard widgets, signals and slots, and popup dialogs.
"""
import datetime
import os.path
import random
import sys
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QTextEdit, QPushButton,
QHBoxLayout, QSplitter, QLabel, QMessageBox, QFileDialog, QLineEdit,
QRadioButton, QGroupBox, QCheckBox)
class ChildWidget(QWidget):
"""A simple child widget of the main custom widget"""
def __init__(self):
super().__init__()
# Set up some basics
layout = QVBoxLayout()
self.setWindowTitle('Cool child window')
self.setLayout(layout)
# Add a basic label
layout.addWidget(QLabel('Status:'))
# Add a read-only text box
child_text = QTextEdit()
child_text.setPlainText('EMPTY')
child_text.setTextInteractionFlags(Qt.TextSelectableByMouse)
layout.addWidget(child_text)
self.child_text = child_text
# Size the widget after adding stuff to the layout
self.resize(400, 300) # Resize children (if needed) below this line
def handle_incoming_mood(self, mood):
"""This is an example slot (a function) for mood change signals"""
letters = [chr(codepoint) for codepoint in range(ord('A'), ord('A') + 26)]
some_rand_letters = random.choices(population=letters, k=4)
# Make a message with the mood and some random letters
message = 'This window is {}\n\nRandom letters: {}'.format(
mood,
''.join(some_rand_letters)
)
self.child_text.setPlainText(message)
class CustomWidget(QWidget):
"""A very simple custom widget"""
# This is a basic custom signal
mood_change = pyqtSignal(str)
def __init__(self):
super().__init__()
# Set some initial properties
layout = QVBoxLayout()
self.setWindowTitle('Basics sample app')
self.setLayout(layout)
# Add a main area with a draggable divider
primary_area = QSplitter(orientation=Qt.Horizontal)
layout.addWidget(primary_area)
# Add a text box
left_text_area = QTextEdit()
left_text_area.setAcceptRichText(False)
msg = 'Hello! Type here, or hit "Time to Text"'
left_text_area.setPlainText(msg)
primary_area.addWidget(left_text_area)
self.left_text_area = left_text_area
# Add a container widget with a horizontal layout
right_control_area = QWidget()
right_layout = QVBoxLayout()
right_layout.setContentsMargins(0, 0, 0, 0)
right_control_area.setLayout(right_layout)
primary_area.addWidget(right_control_area)
# "Shout" button sample
shout_lbl = QLabel('Some buttons')
shout_btn = QPushButton('Shout')
shout_btn.clicked.connect(self.handle_press_shout)
right_layout.addWidget(shout_lbl)
right_layout.addWidget(shout_btn)
# Add a stretchable space to consume space at
# the bottom of the layout (pushes other stuff up)
right_layout.addStretch(1)
self.shout_btn = shout_btn
# Food preference controls
right_layout.addWidget(QLabel('Food Preferences'), alignment=Qt.AlignRight)
food_layout = QHBoxLayout()
food_layout.addStretch()
right_layout.addLayout(food_layout)
# ..........................
breakfast_cb = QCheckBox('Breakfast')
breakfast_cb.stateChanged.connect(self.handle_food_check)
food_layout.addWidget(breakfast_cb)
self.breakfast_cb = breakfast_cb
# ...........................
lunch_cb = QCheckBox('Lunch')
lunch_cb.stateChanged.connect(self.handle_food_check)
food_layout.addWidget(lunch_cb)
self.lunch_cb = lunch_cb
# .............................
dinner_cb = QCheckBox('Dinner')
dinner_cb.stateChanged.connect(self.handle_food_check)
food_layout.addWidget(dinner_cb)
self.dinner_cb = dinner_cb
# File picker controls
# .....................
file_pick_lbl = QLabel('Basic file picker')
right_layout.addWidget(file_pick_lbl, alignment=Qt.AlignRight)
# ...........................
# Put the controls together in a group box
file_picker_box = QGroupBox()
file_picker_layout = QVBoxLayout()
file_picker_box.setLayout(file_picker_layout)
right_layout.addWidget(file_picker_box)
# ........................
# Use a row/stretchable space for better button sizing
picker_row = QHBoxLayout()
picker_row.addStretch()
file_picker_layout.addLayout(picker_row)
# ................................
file_picker_btn = QPushButton('Pick File')
file_picker_btn.clicked.connect(self.handle_pick_file)
picker_row.addWidget(file_picker_btn)
# ...................................
file_picker_result_field = QLineEdit()
file_picker_result_field.setPlaceholderText('Pick a file...')
file_picker_result_field.setAlignment(Qt.AlignRight)
file_picker_result_field.setReadOnly(True)
file_picker_layout.addWidget(file_picker_result_field)
self.file_picker_result_field = file_picker_result_field
# Fruit picker controls
right_layout.addWidget(QLabel('Fruit picker'), alignment=Qt.AlignRight)
fruit_choices = QGroupBox()
fruit_layout = QHBoxLayout()
fruit_choices.setLayout(fruit_layout)
right_layout.addWidget(fruit_choices)
# ...............................
apple_btn = QRadioButton('Apple')
apple_btn.setChecked(True)
fruit_layout.addWidget(apple_btn)
banana_btn = QRadioButton('Banana')
fruit_layout.addWidget(banana_btn)
kiwi_btn = QRadioButton('Kiwi')
fruit_layout.addWidget(kiwi_btn)
# Some controls at the bottom of the window
lower_row = QHBoxLayout()
lower_row.setContentsMargins(0, 0, 0, 0)
layout.addLayout(lower_row)
# Button for showing the time/some random data
time_to_text_btn = QPushButton('Time to Text')
# Add a tooltip (for on-hover)
time_to_text_btn.setToolTip('Show a sample message in the text box')
time_to_text_btn.clicked.connect(self.handle_press_time_to_text)
lower_row.addWidget(time_to_text_btn)
lower_row.addStretch(1) # Space the buttons apart
# Hold a hidden child widget (separate window)
child_widget = ChildWidget()
# Connect the mood_change signal to the child's
# handle_incoming_mood slot (basic signal/slot example)
self.mood_change.connect(child_widget.handle_incoming_mood)
self.child_widget = child_widget
# Controls for the child window
# .............................
show_child_btn = QPushButton('Show child')
show_child_btn.setToolTip('Show a child window')
show_child_btn.clicked.connect(self.handle_show_child)
lower_row.addWidget(show_child_btn)
self.show_child_btn = show_child_btn
# ..................................
child_happy_btn = QPushButton('Make child happy')
child_happy_btn.clicked.connect(self.handle_child_mood)
lower_row.addWidget(child_happy_btn)
self.child_happy_btn = child_happy_btn
# ..................................
child_confused_btn = QPushButton('Make child confused')
child_confused_btn.clicked.connect(self.handle_child_mood)
lower_row.addWidget(child_confused_btn)
self.child_confused_btn = child_confused_btn
# Size the widget after adding stuff to the layout
self.resize(900, 600)
primary_area.setSizes([2 * self.width() // 3, self.width() // 3])
# Make sure you show() the widget!
self.show()
def handle_press_time_to_text(self):
timestring = datetime.datetime.now().isoformat()
letters = [chr(codepoint) for codepoint in range(ord('A'), ord('A') + 26)]
some_rand_letters = random.choices(population=letters, k=4)
message = 'The time is: {}\nRandom letters: {}\n'.format(
timestring,
''.join(some_rand_letters)
)
self.left_text_area.setPlainText(message)
def handle_press_shout(self):
# Show a box with some shout options
box = QMessageBox()
box.setStandardButtons(
QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel
)
box.setWindowTitle('Shout Options')
box.setText('Pick a shout')
yes_btn = box.button(QMessageBox.Yes)
yes_btn.setText('Shout YAY')
no_btn = box.button(QMessageBox.No)
no_btn.setText('Shout NAW')
# Run the dialog/get the result
result = box.exec()
# Show another info box with the result
if result == QMessageBox.Yes:
QMessageBox.information(
self, 'Shouting', "I'm shouting YAY!!"
)
if result == QMessageBox.No:
QMessageBox.information(
self, 'Shouting', "I'm shouting NAW!!"
)
if result == QMessageBox.Cancel:
QMessageBox.information(
self, 'Canceled', 'You canceled the shout'
)
def handle_pick_file(self):
filepath, filefilter = QFileDialog.getOpenFileName(self)
if filepath:
self.file_picker_result_field.setText(os.path.basename(filepath))
else:
self.file_picker_result_field.clear()
def handle_food_check(self, state):
meal_type = ''
if self.sender() is self.breakfast_cb:
meal_type = 'breakfast'
if self.sender() is self.lunch_cb:
meal_type = 'lunch'
if self.sender() is self.dinner_cb:
meal_type = 'dinner'
if state:
QMessageBox.information(
self,
'Meal updated!',
'{} will be served.'.format(meal_type.title())
)
else:
QMessageBox.information(
self,
'Meal updated!',
'Canceling {}.'.format(meal_type)
)
def handle_show_child(self):
self.child_widget.show()
def handle_child_mood(self):
# Determine which button was clicked using self.sender(),
# then emit the mood_change signal with a string (signals
# are a main way of passing information around Qt)
if self.sender() is self.child_happy_btn:
self.mood_change.emit('HAPPY')
if self.sender() is self.child_confused_btn:
self.mood_change.emit('CONFUSED')
def run_gui():
"""Function scoped main app entrypoint"""
# Initialize the QApplication!
app = QApplication(sys.argv)
# This widget shows itself (the main GUI entrypoint)
my_widget = CustomWidget()
# Run the program/start the event loop with exec()
sys.exit(app.exec())
if __name__ == '__main__':
run_gui()