CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyscard

Smartcard library for Python providing PC/SC interface for smart card communication

Pending
Overview
Eval results
Files

gui-components.mddocs/

GUI Components

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.

Capabilities

Core GUI Utilities

Basic utilities and constants for smart card GUI applications.

def main_is_frozen():
    """
    Detect if the application is running from a frozen executable (py2exe, PyInstaller, etc.).
    
    Returns:
        bool: True if running from frozen executable, False otherwise
    """

# Icon constants
ICO_SMARTCARD: str  # Path to smartcard icon resource
ICO_READER: str     # Path to reader icon resource

Application Framework

Base classes for creating smart card applications with wxPython.

class SimpleSCardApp:
    """
    Base class for simple smart card applications.
    Provides basic application framework with card monitoring.
    """

class SimpleSCardAppFrame:
    """
    Main application frame for smart card applications.
    Provides standard menu bar and status bar.
    """

class SimpleSCardAppEventObserver:
    """
    Event observer for smart card application events.
    Handles card insertion/removal events in GUI context.
    """

Visual Components

Ready-to-use wxPython panels and controls for smart card applications.

class APDUTracerPanel:
    """
    Panel for displaying APDU command and response traces.
    Provides formatted display of smart card communication.
    
    Features:
    - Command/response logging
    - Hex and ASCII display modes
    - Export functionality
    - Search and filtering
    """

class CardAndReaderTreePanel:
    """
    Tree control panel showing readers and inserted cards.
    Provides hierarchical view of smart card system.
    
    Features:
    - Reader enumeration
    - Card detection display
    - ATR information
    - Context menus
    """

class ReaderToolbar:
    """
    Toolbar with common reader operations.
    Provides quick access to reader functions.
    
    Features:
    - Reader selection
    - Connect/disconnect buttons
    - Status indicators
    """

Input Validation

Specialized validators for smart card data input.

class APDUHexValidator:
    """
    wxPython validator for APDU hexadecimal input.
    Ensures only valid hex characters and proper APDU format.
    
    Features:
    - Hex character validation
    - Automatic spacing
    - Length checking
    - Invalid character filtering
    """

Usage Examples

Basic Smart Card Application

import wx
from smartcard.wx import SimpleSCardAppFrame, main_is_frozen
from smartcard.CardMonitoring import CardMonitor, CardObserver
from smartcard.util import toHexString

class MyCardObserver(CardObserver):
    def __init__(self, frame):
        self.frame = frame
    
    def update(self, observable, actions):
        """Handle card insertion/removal events in GUI."""
        (addedcards, removedcards) = actions
        
        # Update GUI in main thread
        wx.CallAfter(self.update_gui, addedcards, removedcards)
    
    def update_gui(self, addedcards, removedcards):
        for card in addedcards:
            self.frame.log_message(f"Card inserted: {toHexString(card.atr)}")
        for card in removedcards:
            self.frame.log_message(f"Card removed: {toHexString(card.atr)}")

class SmartCardApp(wx.App):
    def OnInit(self):
        # Create main frame
        frame = SimpleSCardAppFrame(None, "Smart Card Application")
        
        # Set up card monitoring
        self.monitor = CardMonitor()
        self.observer = MyCardObserver(frame)
        self.monitor.addObserver(self.observer)
        
        frame.Show()
        return True
    
    def OnExit(self):
        # Clean up monitoring
        if hasattr(self, 'monitor'):
            self.monitor.deleteObserver(self.observer)

# Application entry point
if __name__ == '__main__':
    app = SmartCardApp()
    app.MainLoop()

APDU Tracer Application

import wx
from smartcard.wx import APDUTracerPanel
from smartcard import Session
from smartcard.util import toHexString

class APDUTracerFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title="APDU Tracer", size=(800, 600))
        
        # Create main panel
        self.panel = wx.Panel(self)
        
        # Create APDU tracer panel
        self.tracer = APDUTracerPanel(self.panel)
        
        # Create input panel
        input_panel = wx.Panel(self.panel)
        input_sizer = wx.BoxSizer(wx.HORIZONTAL)
        
        wx.StaticText(input_panel, label="APDU:")
        self.apdu_input = wx.TextCtrl(input_panel, value="00 A4 00 00")
        send_btn = wx.Button(input_panel, label="Send")
        send_btn.Bind(wx.EVT_BUTTON, self.on_send_apdu)
        
        input_sizer.Add(wx.StaticText(input_panel, label="APDU:"), 0, wx.ALL|wx.CENTER, 5)
        input_sizer.Add(self.apdu_input, 1, wx.ALL|wx.EXPAND, 5)
        input_sizer.Add(send_btn, 0, wx.ALL, 5)
        input_panel.SetSizer(input_sizer)
        
        # Layout
        main_sizer = wx.BoxSizer(wx.VERTICAL)
        main_sizer.Add(self.tracer, 1, wx.ALL|wx.EXPAND, 5)
        main_sizer.Add(input_panel, 0, wx.ALL|wx.EXPAND, 5)
        self.panel.SetSizer(main_sizer)
        
        # Initialize session
        try:
            self.session = Session()
            self.tracer.add_trace("Session", "Connected to first reader")
        except Exception as e:
            self.tracer.add_trace("Error", f"Failed to connect: {e}")
            self.session = None
    
    def on_send_apdu(self, event):
        """Send APDU command and display in tracer."""
        if not self.session:
            self.tracer.add_trace("Error", "No session available")
            return
        
        try:
            # Parse hex input
            apdu_text = self.apdu_input.GetValue()
            apdu_bytes = [int(x, 16) for x in apdu_text.split()]
            
            # Send command
            self.tracer.add_trace("Command", toHexString(apdu_bytes))
            response, sw1, sw2 = self.session.sendCommandAPDU(apdu_bytes)
            
            # Display response
            if response:
                self.tracer.add_trace("Response", f"{toHexString(response)} {sw1:02X} {sw2:02X}")
            else:
                self.tracer.add_trace("Response", f"{sw1:02X} {sw2:02X}")
                
        except ValueError:
            self.tracer.add_trace("Error", "Invalid hex input")
        except Exception as e:
            self.tracer.add_trace("Error", f"Command failed: {e}")
    
    def __del__(self):
        if hasattr(self, 'session') and self.session:
            self.session.close()

class APDUTracerApp(wx.App):
    def OnInit(self):
        frame = APDUTracerFrame()
        frame.Show()
        return True

if __name__ == '__main__':
    app = APDUTracerApp()
    app.MainLoop()

Reader and Card Tree View

import wx
from smartcard.wx import CardAndReaderTreePanel
from smartcard.System import readers
from smartcard.CardMonitoring import CardMonitor, CardObserver

class ReaderCardFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title="Readers and Cards", size=(400, 500))
        
        # Create tree panel
        self.tree_panel = CardAndReaderTreePanel(self)
        
        # Create context menu
        self.create_context_menu()
        
        # Set up card monitoring
        self.monitor = CardMonitor()
        self.observer = TreeCardObserver(self.tree_panel)
        self.monitor.addObserver(self.observer)
        
        # Initial population
        self.refresh_tree()
        
        # Layout
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.tree_panel, 1, wx.EXPAND)
        self.SetSizer(sizer)
    
    def create_context_menu(self):
        """Create context menu for tree items."""
        menu = wx.Menu()
        menu.Append(wx.ID_REFRESH, "Refresh")
        menu.Append(wx.ID_PROPERTIES, "Properties")
        
        self.Bind(wx.EVT_MENU, self.on_refresh, id=wx.ID_REFRESH)
        self.Bind(wx.EVT_MENU, self.on_properties, id=wx.ID_PROPERTIES)
        
        self.tree_panel.Bind(wx.EVT_CONTEXT_MENU, 
                            lambda evt: self.PopupMenu(menu))
    
    def refresh_tree(self):
        """Refresh the reader/card tree."""
        self.tree_panel.clear()
        
        # Add readers
        for reader in readers():
            self.tree_panel.add_reader(reader)
            
            # Try to detect cards
            try:
                connection = reader.createConnection()
                connection.connect()
                atr = connection.getATR()
                self.tree_panel.add_card_to_reader(reader, atr)
                connection.disconnect()
            except:
                pass  # No card present
    
    def on_refresh(self, event):
        self.refresh_tree()
    
    def on_properties(self, event):
        selected_item = self.tree_panel.get_selected_item()
        if selected_item:
            # Show properties dialog
            dlg = wx.MessageDialog(self, f"Properties: {selected_item}", 
                                 "Item Properties", wx.OK)
            dlg.ShowModal()
            dlg.Destroy()
    
    def __del__(self):
        if hasattr(self, 'monitor'):
            self.monitor.deleteObserver(self.observer)

class TreeCardObserver(CardObserver):
    def __init__(self, tree_panel):
        self.tree_panel = tree_panel
    
    def update(self, observable, actions):
        (addedcards, removedcards) = actions
        
        # Update tree in main thread
        wx.CallAfter(self.update_tree, addedcards, removedcards)
    
    def update_tree(self, addedcards, removedcards):
        for card in addedcards:
            self.tree_panel.add_card_to_reader(card.reader, card.atr)
        for card in removedcards:
            self.tree_panel.remove_card_from_reader(card.reader)

class ReaderCardApp(wx.App):
    def OnInit(self):
        frame = ReaderCardFrame()
        frame.Show()
        return True

if __name__ == '__main__':
    app = ReaderCardApp()
    app.MainLoop()

APDU Input Validation

import wx
from smartcard.wx import APDUHexValidator

class APDUInputFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title="APDU Input Validation", size=(500, 300))
        
        panel = wx.Panel(self)
        
        # Create validated text controls
        wx.StaticText(panel, label="APDU Command (Hex):")
        self.apdu_ctrl = wx.TextCtrl(panel, size=(300, -1))
        self.apdu_ctrl.SetValidator(APDUHexValidator())
        
        wx.StaticText(panel, label="Expected Response Length:")
        self.len_ctrl = wx.TextCtrl(panel)
        
        # Buttons
        validate_btn = wx.Button(panel, label="Validate")
        clear_btn = wx.Button(panel, label="Clear")
        
        validate_btn.Bind(wx.EVT_BUTTON, self.on_validate)
        clear_btn.Bind(wx.EVT_BUTTON, self.on_clear)
        
        # Status
        self.status = wx.StaticText(panel, label="Enter APDU command...")
        
        # Layout
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(wx.StaticText(panel, label="APDU Command (Hex):"), 0, wx.ALL, 5)
        sizer.Add(self.apdu_ctrl, 0, wx.ALL|wx.EXPAND, 5)
        sizer.Add(wx.StaticText(panel, label="Expected Response Length:"), 0, wx.ALL, 5)
        sizer.Add(self.len_ctrl, 0, wx.ALL, 5)
        
        btn_sizer = wx.BoxSizer(wx.HORIZONTAL)
        btn_sizer.Add(validate_btn, 0, wx.ALL, 5)
        btn_sizer.Add(clear_btn, 0, wx.ALL, 5)
        sizer.Add(btn_sizer, 0, wx.ALL, 5)
        
        sizer.Add(self.status, 0, wx.ALL, 5)
        
        panel.SetSizer(sizer)
    
    def on_validate(self, event):
        """Validate APDU input."""
        try:
            apdu_text = self.apdu_ctrl.GetValue()
            if not apdu_text.strip():
                self.status.SetLabel("❌ Empty APDU")
                return
            
            # Parse hex bytes
            bytes_list = [int(x, 16) for x in apdu_text.split()]
            
            # Basic APDU validation
            if len(bytes_list) < 4:
                self.status.SetLabel("❌ APDU too short (minimum 4 bytes)")
                return
            
            if len(bytes_list) > 255:
                self.status.SetLabel("❌ APDU too long (maximum 255 bytes)")
                return
            
            cla, ins, p1, p2 = bytes_list[:4]
            
            # Validate structure
            if len(bytes_list) == 4:
                apdu_type = "Case 1 (no data, no response)"
            elif len(bytes_list) == 5:
                le = bytes_list[4]
                apdu_type = f"Case 2 (no data, Le={le})"
            elif len(bytes_list) > 5:
                lc = bytes_list[4]
                if len(bytes_list) == 5 + lc:
                    apdu_type = f"Case 3 (Lc={lc}, no response)"
                elif len(bytes_list) == 6 + lc:
                    le = bytes_list[-1]
                    apdu_type = f"Case 4 (Lc={lc}, Le={le})"
                else:
                    self.status.SetLabel("❌ Invalid APDU structure")
                    return
            
            self.status.SetLabel(f"✅ Valid APDU: {apdu_type}")
            
        except ValueError:
            self.status.SetLabel("❌ Invalid hex characters")
        except Exception as e:
            self.status.SetLabel(f"❌ Validation error: {e}")
    
    def on_clear(self, event):
        """Clear all inputs."""
        self.apdu_ctrl.Clear()
        self.len_ctrl.Clear()
        self.status.SetLabel("Enter APDU command...")

class APDUInputApp(wx.App):
    def OnInit(self):
        frame = APDUInputFrame()
        frame.Show()
        return True

if __name__ == '__main__':
    app = APDUInputApp()
    app.MainLoop()

Installation Note

The GUI components require wxPython to be installed:

pip install pyscard[Gui]
# or
pip install wxPython

Related Types

# wxPython-based types (require wxPython installation)
wx.Panel        # Base panel class
wx.Frame        # Base frame class  
wx.App          # Application class
wx.Validator    # Input validator base class

# GUI event types
wx.Event        # Base event class
wx.CommandEvent # Command event class

# Icon resource types
IconResource = str  # Path to icon file

Install with Tessl CLI

npx tessl i tessl/pypi-pyscard

docs

atr-card-types.md

card-connections.md

gui-components.md

index.md

monitoring.md

pcsc-interface.md

reader-management.md

session-api.md

status-word-handling.md

utilities.md

tile.json