0
# GUI Components
1
2
wxPython-based graphical user interface components for building smart card applications with visual elements. These components provide ready-to-use GUI building blocks for smart card applications.
3
4
## Capabilities
5
6
### Core GUI Utilities
7
8
Basic utilities and constants for smart card GUI applications.
9
10
```python { .api }
11
def main_is_frozen():
12
"""
13
Detect if the application is running from a frozen executable (py2exe, PyInstaller, etc.).
14
15
Returns:
16
bool: True if running from frozen executable, False otherwise
17
"""
18
19
# Icon constants
20
ICO_SMARTCARD: str # Path to smartcard icon resource
21
ICO_READER: str # Path to reader icon resource
22
```
23
24
### Application Framework
25
26
Base classes for creating smart card applications with wxPython.
27
28
```python { .api }
29
class SimpleSCardApp:
30
"""
31
Base class for simple smart card applications.
32
Provides basic application framework with card monitoring.
33
"""
34
35
class SimpleSCardAppFrame:
36
"""
37
Main application frame for smart card applications.
38
Provides standard menu bar and status bar.
39
"""
40
41
class SimpleSCardAppEventObserver:
42
"""
43
Event observer for smart card application events.
44
Handles card insertion/removal events in GUI context.
45
"""
46
```
47
48
### Visual Components
49
50
Ready-to-use wxPython panels and controls for smart card applications.
51
52
```python { .api }
53
class APDUTracerPanel:
54
"""
55
Panel for displaying APDU command and response traces.
56
Provides formatted display of smart card communication.
57
58
Features:
59
- Command/response logging
60
- Hex and ASCII display modes
61
- Export functionality
62
- Search and filtering
63
"""
64
65
class CardAndReaderTreePanel:
66
"""
67
Tree control panel showing readers and inserted cards.
68
Provides hierarchical view of smart card system.
69
70
Features:
71
- Reader enumeration
72
- Card detection display
73
- ATR information
74
- Context menus
75
"""
76
77
class ReaderToolbar:
78
"""
79
Toolbar with common reader operations.
80
Provides quick access to reader functions.
81
82
Features:
83
- Reader selection
84
- Connect/disconnect buttons
85
- Status indicators
86
"""
87
```
88
89
### Input Validation
90
91
Specialized validators for smart card data input.
92
93
```python { .api }
94
class APDUHexValidator:
95
"""
96
wxPython validator for APDU hexadecimal input.
97
Ensures only valid hex characters and proper APDU format.
98
99
Features:
100
- Hex character validation
101
- Automatic spacing
102
- Length checking
103
- Invalid character filtering
104
"""
105
```
106
107
## Usage Examples
108
109
### Basic Smart Card Application
110
111
```python
112
import wx
113
from smartcard.wx import SimpleSCardAppFrame, main_is_frozen
114
from smartcard.CardMonitoring import CardMonitor, CardObserver
115
from smartcard.util import toHexString
116
117
class MyCardObserver(CardObserver):
118
def __init__(self, frame):
119
self.frame = frame
120
121
def update(self, observable, actions):
122
"""Handle card insertion/removal events in GUI."""
123
(addedcards, removedcards) = actions
124
125
# Update GUI in main thread
126
wx.CallAfter(self.update_gui, addedcards, removedcards)
127
128
def update_gui(self, addedcards, removedcards):
129
for card in addedcards:
130
self.frame.log_message(f"Card inserted: {toHexString(card.atr)}")
131
for card in removedcards:
132
self.frame.log_message(f"Card removed: {toHexString(card.atr)}")
133
134
class SmartCardApp(wx.App):
135
def OnInit(self):
136
# Create main frame
137
frame = SimpleSCardAppFrame(None, "Smart Card Application")
138
139
# Set up card monitoring
140
self.monitor = CardMonitor()
141
self.observer = MyCardObserver(frame)
142
self.monitor.addObserver(self.observer)
143
144
frame.Show()
145
return True
146
147
def OnExit(self):
148
# Clean up monitoring
149
if hasattr(self, 'monitor'):
150
self.monitor.deleteObserver(self.observer)
151
152
# Application entry point
153
if __name__ == '__main__':
154
app = SmartCardApp()
155
app.MainLoop()
156
```
157
158
### APDU Tracer Application
159
160
```python
161
import wx
162
from smartcard.wx import APDUTracerPanel
163
from smartcard import Session
164
from smartcard.util import toHexString
165
166
class APDUTracerFrame(wx.Frame):
167
def __init__(self):
168
super().__init__(None, title="APDU Tracer", size=(800, 600))
169
170
# Create main panel
171
self.panel = wx.Panel(self)
172
173
# Create APDU tracer panel
174
self.tracer = APDUTracerPanel(self.panel)
175
176
# Create input panel
177
input_panel = wx.Panel(self.panel)
178
input_sizer = wx.BoxSizer(wx.HORIZONTAL)
179
180
wx.StaticText(input_panel, label="APDU:")
181
self.apdu_input = wx.TextCtrl(input_panel, value="00 A4 00 00")
182
send_btn = wx.Button(input_panel, label="Send")
183
send_btn.Bind(wx.EVT_BUTTON, self.on_send_apdu)
184
185
input_sizer.Add(wx.StaticText(input_panel, label="APDU:"), 0, wx.ALL|wx.CENTER, 5)
186
input_sizer.Add(self.apdu_input, 1, wx.ALL|wx.EXPAND, 5)
187
input_sizer.Add(send_btn, 0, wx.ALL, 5)
188
input_panel.SetSizer(input_sizer)
189
190
# Layout
191
main_sizer = wx.BoxSizer(wx.VERTICAL)
192
main_sizer.Add(self.tracer, 1, wx.ALL|wx.EXPAND, 5)
193
main_sizer.Add(input_panel, 0, wx.ALL|wx.EXPAND, 5)
194
self.panel.SetSizer(main_sizer)
195
196
# Initialize session
197
try:
198
self.session = Session()
199
self.tracer.add_trace("Session", "Connected to first reader")
200
except Exception as e:
201
self.tracer.add_trace("Error", f"Failed to connect: {e}")
202
self.session = None
203
204
def on_send_apdu(self, event):
205
"""Send APDU command and display in tracer."""
206
if not self.session:
207
self.tracer.add_trace("Error", "No session available")
208
return
209
210
try:
211
# Parse hex input
212
apdu_text = self.apdu_input.GetValue()
213
apdu_bytes = [int(x, 16) for x in apdu_text.split()]
214
215
# Send command
216
self.tracer.add_trace("Command", toHexString(apdu_bytes))
217
response, sw1, sw2 = self.session.sendCommandAPDU(apdu_bytes)
218
219
# Display response
220
if response:
221
self.tracer.add_trace("Response", f"{toHexString(response)} {sw1:02X} {sw2:02X}")
222
else:
223
self.tracer.add_trace("Response", f"{sw1:02X} {sw2:02X}")
224
225
except ValueError:
226
self.tracer.add_trace("Error", "Invalid hex input")
227
except Exception as e:
228
self.tracer.add_trace("Error", f"Command failed: {e}")
229
230
def __del__(self):
231
if hasattr(self, 'session') and self.session:
232
self.session.close()
233
234
class APDUTracerApp(wx.App):
235
def OnInit(self):
236
frame = APDUTracerFrame()
237
frame.Show()
238
return True
239
240
if __name__ == '__main__':
241
app = APDUTracerApp()
242
app.MainLoop()
243
```
244
245
### Reader and Card Tree View
246
247
```python
248
import wx
249
from smartcard.wx import CardAndReaderTreePanel
250
from smartcard.System import readers
251
from smartcard.CardMonitoring import CardMonitor, CardObserver
252
253
class ReaderCardFrame(wx.Frame):
254
def __init__(self):
255
super().__init__(None, title="Readers and Cards", size=(400, 500))
256
257
# Create tree panel
258
self.tree_panel = CardAndReaderTreePanel(self)
259
260
# Create context menu
261
self.create_context_menu()
262
263
# Set up card monitoring
264
self.monitor = CardMonitor()
265
self.observer = TreeCardObserver(self.tree_panel)
266
self.monitor.addObserver(self.observer)
267
268
# Initial population
269
self.refresh_tree()
270
271
# Layout
272
sizer = wx.BoxSizer(wx.VERTICAL)
273
sizer.Add(self.tree_panel, 1, wx.EXPAND)
274
self.SetSizer(sizer)
275
276
def create_context_menu(self):
277
"""Create context menu for tree items."""
278
menu = wx.Menu()
279
menu.Append(wx.ID_REFRESH, "Refresh")
280
menu.Append(wx.ID_PROPERTIES, "Properties")
281
282
self.Bind(wx.EVT_MENU, self.on_refresh, id=wx.ID_REFRESH)
283
self.Bind(wx.EVT_MENU, self.on_properties, id=wx.ID_PROPERTIES)
284
285
self.tree_panel.Bind(wx.EVT_CONTEXT_MENU,
286
lambda evt: self.PopupMenu(menu))
287
288
def refresh_tree(self):
289
"""Refresh the reader/card tree."""
290
self.tree_panel.clear()
291
292
# Add readers
293
for reader in readers():
294
self.tree_panel.add_reader(reader)
295
296
# Try to detect cards
297
try:
298
connection = reader.createConnection()
299
connection.connect()
300
atr = connection.getATR()
301
self.tree_panel.add_card_to_reader(reader, atr)
302
connection.disconnect()
303
except:
304
pass # No card present
305
306
def on_refresh(self, event):
307
self.refresh_tree()
308
309
def on_properties(self, event):
310
selected_item = self.tree_panel.get_selected_item()
311
if selected_item:
312
# Show properties dialog
313
dlg = wx.MessageDialog(self, f"Properties: {selected_item}",
314
"Item Properties", wx.OK)
315
dlg.ShowModal()
316
dlg.Destroy()
317
318
def __del__(self):
319
if hasattr(self, 'monitor'):
320
self.monitor.deleteObserver(self.observer)
321
322
class TreeCardObserver(CardObserver):
323
def __init__(self, tree_panel):
324
self.tree_panel = tree_panel
325
326
def update(self, observable, actions):
327
(addedcards, removedcards) = actions
328
329
# Update tree in main thread
330
wx.CallAfter(self.update_tree, addedcards, removedcards)
331
332
def update_tree(self, addedcards, removedcards):
333
for card in addedcards:
334
self.tree_panel.add_card_to_reader(card.reader, card.atr)
335
for card in removedcards:
336
self.tree_panel.remove_card_from_reader(card.reader)
337
338
class ReaderCardApp(wx.App):
339
def OnInit(self):
340
frame = ReaderCardFrame()
341
frame.Show()
342
return True
343
344
if __name__ == '__main__':
345
app = ReaderCardApp()
346
app.MainLoop()
347
```
348
349
### APDU Input Validation
350
351
```python
352
import wx
353
from smartcard.wx import APDUHexValidator
354
355
class APDUInputFrame(wx.Frame):
356
def __init__(self):
357
super().__init__(None, title="APDU Input Validation", size=(500, 300))
358
359
panel = wx.Panel(self)
360
361
# Create validated text controls
362
wx.StaticText(panel, label="APDU Command (Hex):")
363
self.apdu_ctrl = wx.TextCtrl(panel, size=(300, -1))
364
self.apdu_ctrl.SetValidator(APDUHexValidator())
365
366
wx.StaticText(panel, label="Expected Response Length:")
367
self.len_ctrl = wx.TextCtrl(panel)
368
369
# Buttons
370
validate_btn = wx.Button(panel, label="Validate")
371
clear_btn = wx.Button(panel, label="Clear")
372
373
validate_btn.Bind(wx.EVT_BUTTON, self.on_validate)
374
clear_btn.Bind(wx.EVT_BUTTON, self.on_clear)
375
376
# Status
377
self.status = wx.StaticText(panel, label="Enter APDU command...")
378
379
# Layout
380
sizer = wx.BoxSizer(wx.VERTICAL)
381
sizer.Add(wx.StaticText(panel, label="APDU Command (Hex):"), 0, wx.ALL, 5)
382
sizer.Add(self.apdu_ctrl, 0, wx.ALL|wx.EXPAND, 5)
383
sizer.Add(wx.StaticText(panel, label="Expected Response Length:"), 0, wx.ALL, 5)
384
sizer.Add(self.len_ctrl, 0, wx.ALL, 5)
385
386
btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
387
btn_sizer.Add(validate_btn, 0, wx.ALL, 5)
388
btn_sizer.Add(clear_btn, 0, wx.ALL, 5)
389
sizer.Add(btn_sizer, 0, wx.ALL, 5)
390
391
sizer.Add(self.status, 0, wx.ALL, 5)
392
393
panel.SetSizer(sizer)
394
395
def on_validate(self, event):
396
"""Validate APDU input."""
397
try:
398
apdu_text = self.apdu_ctrl.GetValue()
399
if not apdu_text.strip():
400
self.status.SetLabel("❌ Empty APDU")
401
return
402
403
# Parse hex bytes
404
bytes_list = [int(x, 16) for x in apdu_text.split()]
405
406
# Basic APDU validation
407
if len(bytes_list) < 4:
408
self.status.SetLabel("❌ APDU too short (minimum 4 bytes)")
409
return
410
411
if len(bytes_list) > 255:
412
self.status.SetLabel("❌ APDU too long (maximum 255 bytes)")
413
return
414
415
cla, ins, p1, p2 = bytes_list[:4]
416
417
# Validate structure
418
if len(bytes_list) == 4:
419
apdu_type = "Case 1 (no data, no response)"
420
elif len(bytes_list) == 5:
421
le = bytes_list[4]
422
apdu_type = f"Case 2 (no data, Le={le})"
423
elif len(bytes_list) > 5:
424
lc = bytes_list[4]
425
if len(bytes_list) == 5 + lc:
426
apdu_type = f"Case 3 (Lc={lc}, no response)"
427
elif len(bytes_list) == 6 + lc:
428
le = bytes_list[-1]
429
apdu_type = f"Case 4 (Lc={lc}, Le={le})"
430
else:
431
self.status.SetLabel("❌ Invalid APDU structure")
432
return
433
434
self.status.SetLabel(f"✅ Valid APDU: {apdu_type}")
435
436
except ValueError:
437
self.status.SetLabel("❌ Invalid hex characters")
438
except Exception as e:
439
self.status.SetLabel(f"❌ Validation error: {e}")
440
441
def on_clear(self, event):
442
"""Clear all inputs."""
443
self.apdu_ctrl.Clear()
444
self.len_ctrl.Clear()
445
self.status.SetLabel("Enter APDU command...")
446
447
class APDUInputApp(wx.App):
448
def OnInit(self):
449
frame = APDUInputFrame()
450
frame.Show()
451
return True
452
453
if __name__ == '__main__':
454
app = APDUInputApp()
455
app.MainLoop()
456
```
457
458
## Installation Note
459
460
The GUI components require wxPython to be installed:
461
462
```bash
463
pip install pyscard[Gui]
464
# or
465
pip install wxPython
466
```
467
468
## Related Types
469
470
```python { .api }
471
# wxPython-based types (require wxPython installation)
472
wx.Panel # Base panel class
473
wx.Frame # Base frame class
474
wx.App # Application class
475
wx.Validator # Input validator base class
476
477
# GUI event types
478
wx.Event # Base event class
479
wx.CommandEvent # Command event class
480
481
# Icon resource types
482
IconResource = str # Path to icon file
483
```