delete_items.cpp

本文介绍了一个简单的Windows应用程序示例,展示了如何在程序窗口的菜单中动态添加并删除菜单项。通过使用Windows API函数如`AppendMenu`和`DeleteMenu`,此示例程序实现了菜单项的创建和移除功能。

  name="google_ads_frame" marginwidth="0" marginheight="0" src="http://pagead2.googlesyndication.com/pagead/ads?client=ca-pub-5572165936844014&dt=1194442938015&lmt=1194190197&format=336x280_as&output=html&correlator=1194442937843&url=file%3A%2F%2F%2FC%3A%2FDocuments%2520and%2520Settings%2Flhh1%2F%E6%A1%8C%E9%9D%A2%2FCLanguage.htm&color_bg=FFFFFF&color_text=000000&color_link=000000&color_url=FFFFFF&color_border=FFFFFF&ad_type=text&ga_vid=583001034.1194442938&ga_sid=1194442938&ga_hid=1942779085&flash=9&u_h=768&u_w=1024&u_ah=740&u_aw=1024&u_cd=32&u_tz=480&u_java=true" frameborder="0" width="336" scrolling="no" height="280" allowtransparency="allowtransparency"> 
#include <windows.h> 
#include "Delete_Items.h"


#if defined (WIN32)
 #define IS_WIN32 TRUE
#else
 #define IS_WIN32 FALSE
#endif

#define IS_NT      IS_WIN32 && (BOOL)(GetVersion() < 0x80000000)
#define IS_WIN32S  IS_WIN32 && (BOOL)(!(IS_NT) && (LOBYTE(LOWORD(GetVersion()))<4))
#define IS_WIN95   (BOOL)(!(IS_NT) && !(IS_WIN32S)) && IS_WIN32

HINSTANCE hInst;   // current instance

LPCTSTR lpszAppName  = "MyApp";
LPCTSTR lpszTitle    = "My Application";

BOOL RegisterWin95( CONST WNDCLASS* lpwc );

int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                      LPTSTR lpCmdLine, int nCmdShow)
{
   MSG      msg;
   HWND     hWnd;
   WNDCLASS wc;

   // Register the main application window class.
   //............................................
   wc.style         = CS_HREDRAW | CS_VREDRAW;
   wc.lpfnWndProc   = (WNDPROC)WndProc;      
   wc.cbClsExtra    = 0;                     
   wc.cbWndExtra    = 0;                     
   wc.hInstance     = hInstance;             
   wc.hIcon         = LoadIcon( hInstance, lpszAppName );
   wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
   wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
   wc.lpszMenuName  = lpszAppName;             
   wc.lpszClassName = lpszAppName;             

   if ( IS_WIN95 )
   {
      if ( !RegisterWin95( &wc ) )
         return( FALSE );
   }
   else if ( !RegisterClass( &wc ) )
      return( FALSE );

   hInst = hInstance;

   // Create the main application window.
   //....................................
   hWnd = CreateWindow( lpszAppName,
                        lpszTitle,   
                        WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT, 0,
                        CW_USEDEFAULT, 0, 
                        NULL,             
                        NULL,             
                        hInstance,        
                        NULL              
                      );

   if ( !hWnd )
      return( FALSE );

   ShowWindow( hWnd, nCmdShow );
   UpdateWindow( hWnd );        

   while( GetMessage( &msg, NULL, 0, 0) )  
   {
      TranslateMessage( &msg );
      DispatchMessage( &msg ); 
   }

   return( msg.wParam );
}


BOOL RegisterWin95( CONST WNDCLASS* lpwc )
{
   WNDCLASSEX wcex;

   wcex.style         = lpwc->style;
   wcex.lpfnWndProc   = lpwc->lpfnWndProc;
   wcex.cbClsExtra    = lpwc->cbClsExtra;
   wcex.cbWndExtra    = lpwc->cbWndExtra;
   wcex.hInstance     = lpwc->hInstance;
   wcex.hIcon         = lpwc->hIcon;
   wcex.hCursor       = lpwc->hCursor;
   wcex.hbrBackground = lpwc->hbrBackground;
   wcex.lpszMenuName  = lpwc->lpszMenuName;
   wcex.lpszClassName = lpwc->lpszClassName;

   // Added elements for Windows 95.
   //...............................
   wcex.cbSize = sizeof(WNDCLASSEX);
   wcex.hIconSm = LoadImage(wcex.hInstance, lpwc->lpszClassName,
                            IMAGE_ICON, 16, 16,
                            LR_DEFAULTCOLOR );
   
   return RegisterClassEx( &wcex );
}

LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
   switch( uMsg )
   {
      case WM_CREATE :
              {
                 HMENU hMenu = GetMenu( hWnd );

                 AppendMenu( hMenu, MFT_STRING, IDM_ITEM1, "Item&1" );
                 AppendMenu( hMenu, MFT_STRING, IDM_ITEM2, "Item&2" );
                 AppendMenu( hMenu, MFT_STRING, IDM_ITEM3, "Item&3" );
              }
              break;

      case WM_COMMAND :
              switch( LOWORD( wParam ) )
              {
                 case IDM_ITEM1 :
                 case IDM_ITEM2 :
                 case IDM_ITEM3 :
                        {
                           HMENU hMenu = GetMenu( hWnd );

                           DeleteMenu( hMenu, LOWORD(wParam), MF_BYCOMMAND );
                           DrawMenuBar( hWnd );
                        }
                        break;

                 case IDM_ABOUT :
                        DialogBox( hInst, "AboutBox", hWnd, (DLGPROC)About );
                        break;

                 case IDM_EXIT :
                        DestroyWindow( hWnd );
                        break;
              }
              break;
     
      case WM_DESTROY :
              PostQuitMessage(0);
              break;

      default :
            return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
   }

   return( 0L );
}


LRESULT CALLBACK About( HWND hDlg,          
                        UINT message,       
                        WPARAM wParam,      
                        LPARAM lParam)
{
   switch (message)
   {
       case WM_INITDIALOG:
               return (TRUE);

       case WM_COMMAND:                             
               if (   LOWORD(wParam) == IDOK        
                   || LOWORD(wParam) == IDCANCEL)   
               {
                       EndDialog(hDlg, TRUE);       
                       return (TRUE);
               }
               break;
   }

   return (FALSE);
}

WARNING [08/31 21:25:00 fvcore.common.checkpoint]: Some model parameters or buffers are not found in the checkpoint: backbone.fpn_lateral2.{bias, weight} backbone.fpn_output2.{bias, weight} proposal_generator.rpn_head.anchor_deltas.{bias, weight} proposal_generator.rpn_head.conv.{bias, weight} proposal_generator.rpn_head.objectness_logits.{bias, weight} roi_heads.box_head.fc1.{bias, weight} roi_heads.box_head.fc2.{bias, weight} roi_heads.box_predictor.bbox_pred.{bias, weight} roi_heads.box_predictor.cls_score.{bias, weight} roi_heads.mask_head.deconv.{bias, weight} roi_heads.mask_head.mask_fcn1.{bias, weight} roi_heads.mask_head.mask_fcn2.{bias, weight} roi_heads.mask_head.mask_fcn3.{bias, weight} roi_heads.mask_head.mask_fcn4.{bias, weight} roi_heads.mask_head.predictor.{bias, weight} sem_seg_head.p2.0.norm.{bias, weight} sem_seg_head.p2.0.weight sem_seg_head.p3.0.norm.{bias, weight} sem_seg_head.p3.0.weight sem_seg_head.p4.0.norm.{bias, weight} sem_seg_head.p4.0.weight sem_seg_head.p4.2.norm.{bias, weight} sem_seg_head.p4.2.weight sem_seg_head.p5.0.norm.{bias, weight} sem_seg_head.p5.0.weight sem_seg_head.p5.2.norm.{bias, weight} sem_seg_head.p5.2.weight sem_seg_head.p5.4.norm.{bias, weight} sem_seg_head.p5.4.weight sem_seg_head.predictor.{bias, weight} WARNING [08/31 21:25:00 fvcore.common.checkpoint]: The checkpoint state_dict contains keys that are not used by the model: pixel_mean pixel_std head.cls_subnet.0.{bias, weight} head.cls_subnet.2.{bias, weight} head.cls_subnet.4.{bias, weight} head.cls_subnet.6.{bias, weight} head.bbox_subnet.0.{bias, weight} head.bbox_subnet.2.{bias, weight} head.bbox_subnet.4.{bias, weight} head.bbox_subnet.6.{bias, weight} head.cls_score.{bias, weight} head.bbox_pred.{bias, weight} backbone.top_block.p6.{bias, weight} backbone.top_block.p7.{bias, weight} 0%| | 0/1 [00:00<?, ?it/s]/home/detectron/anaconda3/envs/sky/lib/python3.8/site-packages/torch/functional.py:478: UserWarning: torch.meshgrid: in an upcoming release, it will be required to pass the indexing argument. (Triggered internally at ../aten/src/ATen/native/TensorShape.cpp:2894.) return _VF.meshgrid(tensors, **kwargs) # type: ignore[attr-defined]
09-02
帮我用qtc++做一个跟这个效果差不多的import sys import serial import serial.tools.list_ports from PyQt5.QtWidgets import ( QApplication, QWidget, QLabel, QLineEdit, QPushButton, QTextEdit, QVBoxLayout, QHBoxLayout, QComboBox, QCheckBox, QGroupBox, QFormLayout, QGridLayout, QSpacerItem, QSizePolicy ) from PyQt5.QtCore import QTimer, QThread, pyqtSignal, Qt from PyQt5.QtGui import QFont def crc16_modbus(data: bytes) -> int: crc = 0xFFFF for byte in data: crc ^= byte for _ in range(8): if crc & 0x0001: crc >>= 1 crc ^= 0xA001 # 异或反向多项式 else: crc >>= 1 return crc class SerialReader(QThread): data_received = pyqtSignal(bytes) def __init__(self, port, baudrate): super().__init__() self.port = port self.baudrate = baudrate self.serial_port = None self.running = True def run(self): try: self.serial_port = serial.Serial(self.port, self.baudrate, timeout=1) while self.running: data = self.serial_port.read(256) if data: self.data_received.emit(data) except Exception as e: print("串口读取错误:", e) def stop(self): self.running = False if self.serial_port and self.serial_port.is_open: self.serial_port.close() self.wait() class SerialDataDisplay(QWidget): def __init__(self): super().__init__() self.setWindowTitle("串口数据显示界面") self.resize(1500, 1000) self.serial_reader = None self.buffer = bytearray() self.max_lines = 100 self.band_data_cache = { "band1": {}, "band2": {}, "band3": {}, "band4": {} } self.loop_timer = QTimer() self.loop_timer.timeout.connect(self.do_send_data) # 新增:保存上一次发送的预设帧 self.last_preset_data = None self.last_combined_frame = None # 保存最后一次发送的组合帧 # 新增:缓存原始数据用于显示 self.raw_data_lines = [] self.init_ui() self.refresh_ports() # 设置定时器刷新界面 self.update_timer = QTimer() self.update_timer.timeout.connect(self.flush_display) self.update_timer.start(100) def init_ui(self): main_layout = QVBoxLayout() # 版本查询按钮组 version_group = QGroupBox("版本查询") version_group.setFont(QFont("Arial", 12)) version_layout = QHBoxLayout() self.version_button = QPushButton("查询版本") self.version_button.setFont(QFont("Arial", 12)) self.version_button.clicked.connect(self.send_version_query) version_layout.addWidget(self.version_button) version_group.setLayout(version_layout) main_layout.addWidget(version_group) # 串口设置区域 port_config_layout = QHBoxLayout() self.port_combo = QComboBox() self.port_combo.setEditable(True) self.port_combo.setFont(QFont("Arial", 12)) self.port_combo.setFixedWidth(200) self.baud_combo = QComboBox() self.baud_combo.addItems(["9600", "19200", "38400", "57600", "115200"]) self.baud_combo.setCurrentText("115200") self.baud_combo.setFont(QFont("Arial", 12)) self.baud_combo.setFixedWidth(100) self.refresh_button = QPushButton("刷新串口") self.refresh_button.setFont(QFont("Arial", 12)) self.connect_button = QPushButton("连接串口") self.connect_button.setFont(QFont("Arial", 12)) self.status_label = QLabel("未连接") self.status_label.setFont(QFont("Arial", 12)) self.status_label.setStyleSheet("color: red;") port_config_layout.addWidget(QLabel("串口号:")) port_config_layout.addWidget(self.port_combo) port_config_layout.addWidget(QLabel("波特率:")) port_config_layout.addWidget(self.baud_combo) port_config_layout.addWidget(self.refresh_button) port_config_layout.addWidget(self.connect_button) port_config_layout.addWidget(self.status_label) port_config_layout.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) main_layout.addLayout(port_config_layout) # 频段信息显示区域 band_layout = QGridLayout() self.band_widgets = {} for i in range(4): group = QGroupBox(f"频段 {i + 1}") group.setFont(QFont("Arial", 12)) form = QFormLayout() form.setLabelAlignment(Qt.AlignLeft) enabled = QCheckBox() enabled.setEnabled(False) temp = QLineEdit(" ") temp.setReadOnly(True) temp.setFont(QFont("Arial", 11)) current = QLineEdit(" ") current.setReadOnly(True) current.setFont(QFont("Arial", 11)) voltage = QLineEdit(" ") voltage.setReadOnly(True) voltage.setFont(QFont("Arial", 11)) form.addRow("开启状态:", enabled) form.addRow("温度:", temp) form.addRow("电流:", current) form.addRow("电压:", voltage) self.band_widgets[f"band{i + 1}"] = { "enabled": enabled, "temp": temp, "current": current, "voltage": voltage } group.setLayout(form) band_layout.addWidget(group, i // 2, i % 2) main_layout.addLayout(band_layout) # 发送数据帧输入框 send_layout = QHBoxLayout() self.send_input = QLineEdit() self.send_input.setFont(QFont("Arial", 12)) self.send_input.setPlaceholderText("请输入Hex数据,如:5A 5A 5A 5A 01 02 03") self.send_button = QPushButton("发送") self.send_button.setFont(QFont("Arial", 12)) self.send_status = QLabel("") self.send_status.setFont(QFont("Arial", 11)) self.send_status.setStyleSheet("color: blue;") self.loop_checkbox = QCheckBox("循环发送") self.loop_checkbox.setFont(QFont("Arial", 12)) self.interval_input = QLineEdit("1000") # 默认间隔1000ms self.interval_input.setFixedWidth(80) self.interval_input.setFont(QFont("Arial", 12)) self.interval_input.setToolTip("发送间隔(毫秒)") send_layout.addWidget(QLabel("发送数据帧 (Hex):")) send_layout.addWidget(self.send_input) send_layout.addWidget(self.send_button) send_layout.addWidget(self.send_status) send_layout.addWidget(self.loop_checkbox) send_layout.addWidget(QLabel("间隔(ms):")) send_layout.addWidget(self.interval_input) main_layout.addLayout(send_layout) # 频段组合发送组 preset_group = QGroupBox("频段组合发送") preset_group.setFont(QFont("Arial", 12)) preset_layout = QGridLayout() self.band_checkboxes = [] # 每个频段的位掩码 self.band_masks = { "频段1": 0x01, "频段2": 0x02, "频段3": 0x04, "频段4": 0x08, } # 原始帧模板(去掉CRC部分) self.preset_frame_template = bytes.fromhex( "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 02 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", ) row = 0 for i, (name, mask) in enumerate(self.band_masks.items()): checkbox = QCheckBox(name) checkbox.setFont(QFont("Arial", 11)) checkbox.stateChanged.connect(self.update_combined_frame) self.band_checkboxes.append(checkbox) preset_layout.addWidget(checkbox, row // 2, row % 2) row += 1 self.send_combined_button = QPushButton("发送组合帧") self.send_combined_button.setFont(QFont("Arial", 12)) self.send_combined_button.clicked.connect(self.send_combined_frame) preset_layout.addWidget(self.send_combined_button, row // 2 + 1, 0, 1, 2) preset_group.setLayout(preset_layout) main_layout.addWidget(preset_group) # 自定义帧按钮组 custom_frame_group = QGroupBox("自定义帧") custom_frame_group.setFont(QFont("Arial", 12)) custom_frame_layout = QHBoxLayout() self.custom_frame_button = QPushButton("持续时间") self.custom_frame_button.setFont(QFont("Arial", 12)) self.custom_frame_button.clicked.connect(self.send_custom_frame) self.duration_input = QLineEdit("0") self.duration_input.setFixedWidth(80) self.duration_input.setToolTip("输入0~65535的持续时间") custom_frame_layout.addWidget(self.custom_frame_button) custom_frame_layout.addWidget(QLabel("持续时间 (0~65535):")) custom_frame_layout.addWidget(self.duration_input) custom_frame_layout.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)) custom_frame_group.setLayout(custom_frame_layout) main_layout.addWidget(custom_frame_group) # 干扰码切换模块 jammer_group = QGroupBox("干扰码切换") jammer_group.setFont(QFont("Arial", 12)) jammer_layout = QGridLayout() # 按钮列表 self.jammer_buttons = { "900M-1": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", "900M-2": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 0A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", "1600M-1": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", "1600M-2": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 0C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", "2400M-1": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", "2400M-2": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", "5800M-1": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 16 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", "5800M-2": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", } row = 0 col = 0 for name, hex_data in self.jammer_buttons.items(): btn = QPushButton(name) btn.setFont(QFont("Arial", 11)) btn.clicked.connect(lambda checked, data=hex_data: self.send_jammer_frame(data)) jammer_layout.addWidget(btn, row, col) col += 1 if col > 2: col = 0 row += 1 jammer_group.setLayout(jammer_layout) main_layout.addWidget(jammer_group) # 新增:功率控制模块 power_group = QGroupBox("功率控制") power_group.setFont(QFont("Arial", 12)) power_layout = QHBoxLayout() # 功率帧模板(A1 12 / A1 13 / A1 15) self.power_frames = { "低功率": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", "中功率": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00", "高功率": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 15 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" } for name, frame_hex in self.power_frames.items(): btn = QPushButton(name) btn.setFont(QFont("Arial", 11)) btn.clicked.connect(lambda checked, data=frame_hex: self.send_power_frame(data)) power_layout.addWidget(btn) power_group.setLayout(power_layout) main_layout.addWidget(power_group) # 原始数据帧显示框 raw_data_group = QGroupBox("原始数据帧显示") raw_data_group.setFont(QFont("Arial", 12)) self.raw_data_display = QTextEdit() self.raw_data_display.setReadOnly(True) self.raw_data_display.setFont(QFont("Courier", 10)) layout = QVBoxLayout() layout.addWidget(self.raw_data_display) raw_data_group.setLayout(layout) main_layout.addWidget(raw_data_group) # 信号连接 self.refresh_button.clicked.connect(self.refresh_ports) self.connect_button.clicked.connect(self.toggle_serial_connection) self.send_button.clicked.connect(self.handle_send_button_click) self.send_input.returnPressed.connect(self.handle_send_button_click) self.setLayout(main_layout) self.update_timer = QTimer() self.update_timer.timeout.connect(self.flush_display) self.update_timer.start(100) #新增方法:发送功率帧 def send_power_frame(self, hex_data): try: data_bytes = bytes.fromhex(hex_data.replace(' ', '')) except ValueError: self.send_status.setText("功率帧数据格式错误") self.send_status.setStyleSheet("color: red;") return try: # 取第12字节之后的数据计算 CRC crc_data = data_bytes[12:] crc_value = crc16_modbus(crc_data) # 转为可变列表插入 CRC data_list = list(data_bytes) data_list[8] = (crc_value >> 8) & 0xFF # CRC 高位 → 第8字节 data_list[9] = crc_value & 0xFF # CRC 低位 → 第9字节 final_bytes = bytes(data_list) # 格式化为带空格的 Hex 字符串 hex_with_crc = ''.join(f"{b:02X}" for b in final_bytes) formatted_hex = ' '.join(hex_with_crc[i:i+2] for i in range(0, len(hex_with_crc), 2)) self.send_input.setText(formatted_hex) # 发送 if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open: self.serial_reader.serial_port.write(final_bytes) cmd_code = hex_data.split()[13] self.send_status.setText(f"功率设置({cmd_code})发送成功") self.send_status.setStyleSheet("color: green;") else: self.send_status.setText("串口未连接") self.send_status.setStyleSheet("color: red;") except Exception as e: self.send_status.setText(f"功率帧发送失败: {str(e)}") self.send_status.setStyleSheet("color: red;") def send_jammer_frame(self, hex_data): try: data_bytes = bytes.fromhex(hex_data.replace(' ', '')) except ValueError: self.send_status.setText("干扰码数据格式错误") self.send_status.setStyleSheet("color: red;") return try: crc_data = data_bytes[12:] crc_value = crc16_modbus(crc_data) data_list = list(data_bytes) data_list[8] = (crc_value >> 8) & 0xFF # 高位 data_list[9] = crc_value & 0xFF # 低位 hex_with_crc = ''.join(f"{b:02X}" for b in data_list) formatted_hex = ' '.join(hex_with_crc[i:i+2] for i in range(0, len(hex_with_crc), 2)) self.send_input.setText(formatted_hex) except Exception as e: self.send_status.setText("CRC计算失败:" + str(e)) self.send_status.setStyleSheet("color: red;") return if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open: try: self.serial_reader.serial_port.write(data_bytes) #self.send_status.setText(f"干扰码 {hex_data} 发送成功") self.send_status.setStyleSheet("color: green;") except Exception as e: self.send_status.setText("发送失败:" + str(e)) self.send_status.setStyleSheet("color: red;") else: self.send_status.setText("串口未连接") self.send_status.setStyleSheet("color: red;") def handle_send_button_click(self): if self.loop_checkbox.isChecked(): try: interval = int(self.interval_input.text()) if interval < 100: self.send_status.setText("间隔太小(至少100ms)") self.send_status.setStyleSheet("color: red;") return self.loop_timer.start(interval) self.send_status.setText("循环发送中...") self.send_status.setStyleSheet("color: green;") except ValueError: self.send_status.setText("请输入有效的数字作为间隔") self.send_status.setStyleSheet("color: red;") else: self.loop_timer.stop() self.send_hex_data() def send_custom_frame(self): if not hasattr(self, 'last_combined_frame') or self.last_combined_frame is None: self.send_status.setText("请先发送一次组合帧") self.send_status.setStyleSheet("color: red;") return # 获取用户输入的持续时间 duration_str = self.duration_input.text().strip() try: duration = int(duration_str) if not (0 <= duration <= 65535): raise ValueError("范围必须在 0 ~ 65535 之间") except ValueError as e: self.send_status.setText(f"输入错误:{e}") self.send_status.setStyleSheet("color: red;") return # 将整数转换为两个字节(高位在前) byte16 = (duration >> 8) & 0xFF # 高位字节 byte17 = duration & 0xFF # 低位字节 # 拷贝上一次的组合帧 data_bytes = bytearray(self.last_combined_frame) # 插入到帧的第16和17个字节位置(索引16、17) data_bytes[16] = byte16 data_bytes[17] = byte17 try: # 计算 CRC(从第12字节开始) crc_data = bytes(data_bytes[12:]) crc_value = crc16_modbus(crc_data) # 插入 CRC 到第8、9字节 data_bytes[8] = (crc_value >> 8) & 0xFF data_bytes[9] = crc_value & 0xFF hex_with_crc = ''.join(f"{b:02X}" for b in data_bytes) self.send_input.setText(' '.join(hex_with_crc[i:i+2] for i in range(0, len(hex_with_crc), 2))) except Exception as e: self.send_status.setText("CRC计算失败:" + str(e)) self.send_status.setStyleSheet("color: red;") return if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open: self.send_hex_data() else: self.send_status.setText("请先连接串口") self.send_status.setStyleSheet("color: red;") def send_version_query(self): hex_data = "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" try: data_bytes = bytes.fromhex(hex_data.replace(' ', '')) except ValueError: self.send_status.setText("版本查询数据格式错误") self.send_status.setStyleSheet("color: red;") return try: crc_data = data_bytes[12:] crc_value = crc16_modbus(crc_data) data_list = list(data_bytes) data_list[8] = (crc_value >> 8) & 0xFF # 高位 data_list[9] = crc_value & 0xFF # 低位 hex_with_crc = ''.join(f"{b:02X}" for b in data_list) self.send_input.setText(' '.join(hex_with_crc[i:i+2] for i in range(0, len(hex_with_crc), 2))) except Exception as e: self.send_status.setText("CRC计算失败:" + str(e)) self.send_status.setStyleSheet("color: red;") return if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open: self.send_hex_data() else: self.send_status.setText("请先连接串口") self.send_status.setStyleSheet("color: red;") def do_send_data(self): hex_str = self.send_input.text().strip() if not hex_str: return try: hex_bytes = bytes.fromhex(hex_str.replace(' ', '')) if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open: self.serial_reader.serial_port.write(hex_bytes) else: self.loop_timer.stop() self.send_status.setText("串口已断开,停止循环发送") self.send_status.setStyleSheet("color: red;") except Exception as e: self.loop_timer.stop() self.send_status.setText("发送失败:" + str(e)) self.send_status.setStyleSheet("color: red;") def send_hex_data(self): hex_str = self.send_input.text().strip() if not hex_str: self.send_status.setText("请输入有效的Hex数据") self.send_status.setStyleSheet("color: red;") return try: hex_bytes = bytes.fromhex(hex_str.replace(' ', '')) if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open: self.serial_reader.serial_port.write(hex_bytes) self.send_status.setText("发送成功") self.send_status.setStyleSheet("color: green;") else: self.send_status.setText("串口未连接") self.send_status.setStyleSheet("color: red;") except Exception as e: self.send_status.setText("发送失败:" + str(e)) self.send_status.setStyleSheet("color: red;") def refresh_ports(self): current_text = self.port_combo.currentText() self.port_combo.clear() ports = serial.tools.list_ports.comports() for port in ports: self.port_combo.addItem(port.device) if current_text and current_text not in [self.port_combo.itemText(i) for i in range(self.port_combo.count())]: self.port_combo.addItem(current_text) self.port_combo.setCurrentText(current_text) else: self.port_combo.setCurrentText(current_text if current_text else "") def toggle_serial_connection(self): if self.serial_reader and self.serial_reader.isRunning(): self.serial_reader.stop() self.status_label.setText("已断开") self.status_label.setStyleSheet("color: red;") self.connect_button.setText("连接串口") self.loop_timer.stop() else: port = self.port_combo.currentText() baud = int(self.baud_combo.currentText()) self.serial_reader = SerialReader(port, baud) self.serial_reader.data_received.connect(self.handle_serial_data) self.serial_reader.start() self.status_label.setText("已连接") self.status_label.setStyleSheet("color: green;") self.connect_button.setText("断开串口") def handle_serial_data(self, data): self.buffer.extend(data) self.raw_data_lines.append("Received (Hex): " + ' '.join(f"{b:02X}" for b in data)) # 将原始数据转换为Hex格式并添加到缓冲区 self.process_buffer() def process_buffer(self): while len(self.buffer) >= 78: found = False for i in range(len(self.buffer) - 77): if self.buffer[i + 12] == 0xA2 and self.buffer[i + 13] == 0x02: frame = self.buffer[i:i+78] self.buffer = self.buffer[i+78:] self.raw_data_lines.append("Received Frame (Hex): " + ' '.join(f"{b:02X}" for b in frame)) self.parse_hex_data(frame) found = True break if not found: break def flush_display(self): if self.raw_data_lines: lines = self.raw_data_lines self.raw_data_lines = [] for line in lines: cursor = self.raw_data_display.textCursor() cursor.movePosition(cursor.End) cursor.select(cursor.LineUnderCursor) line_count = self.raw_data_display.document().lineCount() if line_count >= self.max_lines: cursor.movePosition(cursor.Start) cursor.select(cursor.LineUnderCursor) cursor.removeSelectedText() cursor.deleteChar() cursor.movePosition(cursor.End) cursor.insertText(line + '\n') self.raw_data_display.setTextCursor(cursor) self.raw_data_display.ensureCursorVisible() for i in range(4): band_name = f"band{i + 1}" self.update_band_display(band_name, self.band_data_cache[band_name]) def parse_hex_data(self, data): try: hex_data = list(data) # 检查长度是否足够 if len(hex_data) < 78: return switch_byte = hex_data[15] switches = [(switch_byte >> i) & 1 for i in range(4)] if len(hex_data) > 29: temp = hex_data[25] volt = (hex_data[26] << 8) | hex_data[27] curr = (hex_data[28] << 8) | hex_data[29] self.band_data_cache["band1"]["temp"] = f"{temp}°C" self.band_data_cache["band1"]["voltage"] = f"{volt / 1000:.3f}V" self.band_data_cache["band1"]["current"] = f"{curr / 1000:.3f}A" self.band_data_cache["band1"]["enabled"] = "on" if switches[0] else "off" if len(hex_data) > 38: temp = hex_data[33] volt = (hex_data[34] << 8) | hex_data[35] curr = (hex_data[36] << 8) | hex_data[37] self.band_data_cache["band2"]["temp"] = f"{temp}°C" self.band_data_cache["band2"]["voltage"] = f"{volt / 1000:.3f}V" self.band_data_cache["band2"]["current"] = f"{curr / 1000:.3f}A" self.band_data_cache["band2"]["enabled"] = "on" if switches[1] else "off" if len(hex_data) > 46: temp = hex_data[41] volt = (hex_data[42] << 8) | hex_data[43] curr = (hex_data[44] << 8) | hex_data[45] self.band_data_cache["band3"]["temp"] = f"{temp}°C" self.band_data_cache["band3"]["voltage"] = f"{volt / 1000:.3f}V" self.band_data_cache["band3"]["current"] = f"{curr / 1000:.3f}A" self.band_data_cache["band3"]["enabled"] = "on" if switches[2] else "off" if len(hex_data) > 54: temp = hex_data[49] volt = (hex_data[50] << 8) | hex_data[51] curr = (hex_data[52] << 8) | hex_data[53] self.band_data_cache["band4"]["temp"] = f"{temp}°C" self.band_data_cache["band4"]["voltage"] = f"{volt / 1000:.3f}V" self.band_data_cache["band4"]["current"] = f"{curr / 1000:.3f}A" self.band_data_cache["band4"]["enabled"] = "on" if switches[3] else "off" except Exception as e: print("解析Hex数据失败:", e) def update_band_display(self, band_name, data): widgets = self.band_widgets.get(band_name) if not widgets: return if "enabled" in data: widgets["enabled"].setChecked(data["enabled"] == "on") if "temp" in data: widgets["temp"].setText(data["temp"]) if "current" in data: widgets["current"].setText(data["current"]) if "voltage" in data: widgets["voltage"].setText(data["voltage"]) def update_combined_frame(self): # 构造帧副本 frame = bytearray(self.preset_frame_template) # 计算启用的频段掩码 combined_mask = 0x00 for checkbox in self.band_checkboxes: if checkbox.isChecked(): band_name = checkbox.text() combined_mask |= self.band_masks[band_name] # 设置第16个字节 frame[15] = combined_mask # 计算 CRC(从第12字节开始) crc_data = bytes(frame[12:]) crc_value = crc16_modbus(crc_data) # 插入 CRC frame[8] = (crc_value >> 8) & 0xFF # 高位 frame[9] = crc_value & 0xFF # 低位 # 保存组合帧用于后续自定义帧使用 self.last_combined_frame = frame # 显示在发送框中 hex_with_crc = ''.join(f"{b:02X}" for b in frame) formatted_hex = ' '.join(hex_with_crc[i:i+2] for i in range(0, len(hex_with_crc), 2)) self.send_input.setText(formatted_hex) def send_combined_frame(self): hex_str = self.send_input.text().strip() if not hex_str: self.send_status.setText("请输入有效的Hex数据") self.send_status.setStyleSheet("color: red;") return try: hex_bytes = bytes.fromhex(hex_str.replace(' ', '')) if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open: self.serial_reader.serial_port.write(hex_bytes) self.send_status.setText("组合帧发送成功") self.send_status.setStyleSheet("color: green;") else: self.send_status.setText("串口未连接") self.send_status.setStyleSheet("color: red;") except Exception as e: self.send_status.setText("发送失败:" + str(e)) self.send_status.setStyleSheet("color: red;") if __name__ == "__main__": app = QApplication(sys.argv) window = SerialDataDisplay() window.show() sys.exit(app.exec_())
最新发布
11-18
import os import re import sys import threading import tkinter as tk from tkinter import ttk, filedialog, messagebox, scrolledtext import fnmatch import chardet import docx from openpyxl import load_workbook import PyPDF2 import xlrd class FileSearchApp: def __init__(self, master): self.master = master master.title("高级文件搜索工具") master.geometry("1200x800") master.minsize(900, 650) # 设置现代主题 self.style = ttk.Style() self.style.theme_use("vista" if sys.platform == "win32" else "aqua") # 创建主框架 main_frame = ttk.Frame(master, padding=10) main_frame.pack(fill=tk.BOTH, expand=True) # 创建搜索面板 search_frame = ttk.LabelFrame(main_frame, text="搜索选项", padding=10) search_frame.pack(fill=tk.X, padx=5, pady=5) # 搜索目录 dir_frame = ttk.Frame(search_frame) dir_frame.pack(fill=tk.X, pady=5) ttk.Label(dir_frame, text="搜索目录:").pack(side=tk.LEFT, padx=(0, 5)) self.dir_entry = ttk.Entry(dir_frame, width=50) self.dir_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) self.dir_entry.insert(0, os.getcwd()) ttk.Button(dir_frame, text="浏览...", command=self.browse_directory).pack(side=tk.RIGHT) # 关键词和文件过滤 filter_frame = ttk.Frame(search_frame) filter_frame.pack(fill=tk.X, pady=5) ttk.Label(filter_frame, text="关键词:").pack(side=tk.LEFT, padx=(0, 5)) self.keyword_entry = ttk.Entry(filter_frame) self.keyword_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) ttk.Label(filter_frame, text="文件过滤:").pack(side=tk.LEFT, padx=(10, 5)) self.filter_entry = ttk.Entry(filter_frame, width=30) self.filter_entry.pack(side=tk.LEFT, padx=5) self.filter_entry.insert(0, "*.c;*.h;*.prm;*.xlsx;*.xls;*.doc;*.docx;*.pdf") # 搜索选项 options_frame = ttk.Frame(search_frame) options_frame.pack(fill=tk.X, pady=10) self.case_var = tk.BooleanVar(value=False) ttk.Checkbutton(options_frame, text="忽略大小写", variable=self.case_var).pack(side=tk.LEFT, padx=10) self.regex_var = tk.BooleanVar(value=False) ttk.Checkbutton(options_frame, text="正则表达式", variable=self.regex_var).pack(side=tk.LEFT, padx=10) self.binary_var = tk.BooleanVar(value=False) ttk.Checkbutton(options_frame, text="包含二进制", variable=self.binary_var).pack(side=tk.LEFT, padx=10) self.limit_var = tk.BooleanVar(value=True) ttk.Checkbutton(options_frame, text="限制大小(100MB)", variable=self.limit_var).pack(side=tk.LEFT, padx=10) self.highlight_var = tk.BooleanVar(value=True) ttk.Checkbutton(options_frame, text="关键字高亮", variable=self.highlight_var).pack(side=tk.LEFT, padx=10) # 按钮面板 button_frame = ttk.Frame(search_frame) button_frame.pack(fill=tk.X, pady=10) self.search_button = ttk.Button(button_frame, text="开始搜索", command=self.start_search) self.search_button.pack(side=tk.LEFT, padx=5) self.stop_button = ttk.Button(button_frame, text="停止搜索", command=self.stop_search, state=tk.DISABLED) self.stop_button.pack(side=tk.LEFT, padx=5) self.export_button = ttk.Button(button_frame, text="导出结果", command=self.export_results) self.export_button.pack(side=tk.LEFT, padx=5) # 状态栏 - 修复布局问题 status_frame = ttk.Frame(main_frame) status_frame.pack(fill=tk.X, padx=5, pady=(0, 5)) self.status_label = ttk.Label(status_frame, text="就绪", font=("Arial", 9)) self.status_label.pack(side=tk.LEFT, anchor='w') self.progress_var = tk.DoubleVar() self.progress_bar = ttk.Progressbar( status_frame, variable=self.progress_var, length=200, mode='determinate' ) self.progress_bar.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=10) self.stats_label = ttk.Label(status_frame, text="", font=("Arial", 9)) self.stats_label.pack(side=tk.RIGHT, padx=(0, 10)) # 结果面板 results_frame = ttk.LabelFrame(main_frame, text="搜索结果", padding=10) results_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 分割窗格 paned_window = ttk.PanedWindow(results_frame, orient=tk.HORIZONTAL) paned_window.pack(fill=tk.BOTH, expand=True) # 文件列表 file_list_frame = ttk.Frame(paned_window) paned_window.add(file_list_frame, weight=1) self.file_tree = ttk.Treeview( file_list_frame, columns=("filename", "path"), show="headings", selectmode="browse" ) self.file_tree.heading("filename", text="文件名") self.file_tree.heading("path", text="路径") self.file_tree.column("filename", width=200, anchor="w") self.file_tree.column("path", width=400, anchor="w") file_scroll = ttk.Scrollbar( file_list_frame, orient=tk.VERTICAL, command=self.file_tree.yview ) self.file_tree.configure(yscrollcommand=file_scroll.set) self.file_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) file_scroll.pack(side=tk.RIGHT, fill=tk.Y) self.file_tree.bind('<<TreeviewSelect>>', self.show_file_content) self.file_tree.bind('<Double-1>', self.open_selected_file) # 文件右键菜单 self.file_menu = tk.Menu(self.master, tearoff=0) self.file_menu.add_command(label="打开文件", command=self.open_selected_file) self.file_menu.add_command(label="打开文件位置", command=self.open_file_location) self.file_tree.bind("<Button-3>", self.show_file_context_menu) # 文件内容预览 content_frame = ttk.Frame(paned_window) paned_window.add(content_frame, weight=2) self.content_text = scrolledtext.ScrolledText( content_frame, wrap=tk.WORD, font=("Consolas", 10), padx=5, pady=5 ) self.content_text.pack(fill=tk.BOTH, expand=True) # 文本标签配置 self.content_text.tag_configure("match", background="yellow") self.content_text.tag_configure("linenum", foreground="blue", font=("Consolas", 9)) self.content_text.tag_configure("header", foreground="darkgreen", font=("Arial", 10, "bold")) self.content_text.tag_configure("warning", foreground="red", font=("Arial", 10, "italic")) # 文本右键菜单 text_menu = tk.Menu(self.master, tearoff=0) text_menu.add_command(label="复制", command=self.copy_selected_text) self.content_text.bind("<Button-3>", lambda e: text_menu.tk_popup(e.x_root, e.y_root)) # 初始化变量 self.results = {} self.all_files = [] self.stop_requested = False self.search_thread = None def browse_directory(self): directory = filedialog.askdirectory(title="选择搜索目录") if directory: self.dir_entry.delete(0, tk.END) self.dir_entry.insert(0, directory) def update_status(self, text): """更新状态标签""" self.status_label.config(text=text) def update_stats(self, text): """更新统计标签""" self.stats_label.config(text=text) def update_progress(self, value, total): """更新进度条和统计信息""" self.progress_var.set(value) percentage = round((value / total) * 100, 1) if total > 0 else 0 self.stats_label.config( text=f"处理中: {value}/{total} 文件 ({percentage}%)" ) def reset_search_state(self): """重置搜索按钮状态""" self.search_button.config(state=tk.NORMAL) self.stop_button.config(state=tk.DISABLED) def start_search(self): # 重置状态 self.progress_var.set(0) self.stop_requested = False self.results.clear() self.all_files.clear() self.file_tree.delete(*self.file_tree.get_children()) self.content_text.delete(1.0, tk.END) self.update_status("正在搜索...") self.search_button.config(state=tk.DISABLED) self.stop_button.config(state=tk.NORMAL) self.update_stats("扫描文件中...") # 获取搜索参数 directory = self.dir_entry.get().strip() keyword = self.keyword_entry.get().strip() file_filter = self.filter_entry.get().strip() # 验证输入 if not directory or not os.path.isdir(directory): messagebox.showerror("错误", "请选择有效的搜索目录") self.reset_search_state() return if not keyword: messagebox.showerror("错误", "请输入搜索关键词") self.reset_search_state() return # 编译搜索模式 flags = re.IGNORECASE if self.case_var.get() else 0 try: if self.regex_var.get(): pattern = re.compile(keyword, flags) else: pattern = re.compile(re.escape(keyword), flags) except re.error as e: messagebox.showerror("正则表达式错误", f"无效的正则表达式: {str(e)}") self.reset_search_state() return # 处理文件过滤器 - 修复过滤逻辑 if not file_filter: filter_patterns = ["*"] else: # 修复:处理带分号的过滤模式 filter_patterns = [pat.strip() for pat in file_filter.split(";") if pat.strip()] # 在后台线程中执行搜索 self.search_thread = threading.Thread( target=self.perform_search, args=(directory, filter_patterns, pattern), daemon=True ) self.search_thread.start() def perform_search(self, directory, filter_patterns, pattern): """在后台线程中执行文件搜索""" try: # 收集所有匹配的文件 - 修复文件过滤问题 self.all_files = [] for root, _, files in os.walk(directory): if self.stop_requested: self.update_status("搜索已取消") return for file in files: if self.stop_requested: break file_path = os.path.join(root, file) # 检查文件大小限制 if self.limit_var.get(): try: if os.path.getsize(file_path) > 100 * 1024 * 1024: # 100MB continue except: continue # 修复:正确匹配文件扩展名 if not any(fnmatch.fnmatch(file, pat) for pat in filter_patterns): continue self.all_files.append(file_path) total_files = len(self.all_files) self.update_progress(0, total_files) self.update_stats(f"扫描到 {total_files} 个文件") # 搜索每个文件 matches_found = 0 for i, file_path in enumerate(self.all_files): if self.stop_requested: break # 更新进度 self.update_progress(i + 1, total_files) # 检查二进制文件 if not self.binary_var.get() and self.is_binary(file_path): continue # 搜索文件内容 - 修复内容搜索问题 matches = self.search_file_content(file_path, pattern) if matches: self.results[file_path] = matches matches_found += len(matches) self.add_file_to_list(file_path) # 更新完成状态 status = f"搜索完成 - 找到 {len(self.results)} 个文件, {matches_found} 个匹配项" if self.stop_requested: status = f"搜索已取消 - 找到 {len(self.results)} 个文件, {matches_found} 个匹配项" self.update_status(status) self.update_progress(total_files, total_files) except Exception as e: self.update_status(f"搜索错误: {str(e)}") finally: self.reset_search_state() self.search_thread = None def search_file_content(self, file_path, pattern): """根据文件类型搜索内容""" _, ext = os.path.splitext(file_path) ext_lower = ext.lower() # 修复:确保所有支持的文件类型都能被搜索 if ext_lower in ['.c', '.h', '.prm', '.txt', '.py', '.java', '.cpp', '.hpp', '.log']: return self.search_in_text_file(file_path, pattern) elif ext_lower in ['.docx', '.doc', '.xlsx', '.xls', '.pdf']: return self.search_in_office_file(file_path, pattern) else: return [] # 其他文件类型不搜索 def search_in_text_file(self, file_path, pattern): """在文本文件中搜索匹配项""" try: encoding = self.detect_encoding(file_path) matches = [] with open(file_path, 'r', encoding=encoding, errors='replace') as f: for line_num, line in enumerate(f, 1): if pattern.search(line): # 截断过长的行 cleaned_line = line.strip() if len(cleaned_line) > 150: cleaned_line = cleaned_line[:150] + "..." matches.append((line_num, cleaned_line)) return matches except Exception as e: print(f"搜索文本文件出错: {file_path} | {str(e)}") return [] def search_in_office_file(self, file_path, pattern): """修复Office文件搜索逻辑""" _, ext = os.path.splitext(file_path) ext_lower = ext.lower() matches = [] try: # DOCX文件处理 if ext_lower == '.docx': doc = docx.Document(file_path) for i, para in enumerate(doc.paragraphs, 1): if para.text and pattern.search(para.text): content = para.text.strip() if len(content) > 100: content = content[:100] + "..." matches.append((i, f"段落 {i}: {content}")) for table in doc.tables: for row_idx, row in enumerate(table.rows, 1): for cell_idx, cell in enumerate(row.cells, 1): if cell.text and pattern.search(cell.text): content = cell.text.strip() if len(content) > 100: content = content[:100] + "..." matches.append((row_idx, f"表格 行{row_idx}列{cell_idx}: {content}")) # XLSX文件处理 elif ext_lower == '.xlsx': wb = load_workbook(file_path, read_only=True, data_only=True) for sheet_name in wb.sheetnames: sheet = wb[sheet_name] for row_idx, row in enumerate(sheet.iter_rows(values_only=True), 1): for col_idx, cell in enumerate(row, 1): if cell is not None and pattern.search(str(cell)): cell_ref = f"{chr(64+col_idx)}{row_idx}" content = str(cell).strip() if len(content) > 100: content = content[:100] + "..." matches.append((row_idx, f"工作表 '{sheet_name}' 单元格 {cell_ref}: {content}")) # XLS文件处理 elif ext_lower == '.xls': wb = xlrd.open_workbook(file_path) for sheet_idx in range(wb.nsheets): sheet = wb.sheet_by_index(sheet_idx) for row_idx in range(sheet.nrows): for col_idx in range(sheet.ncols): cell = sheet.cell_value(row_idx, col_idx) if cell and pattern.search(str(cell)): cell_ref = f"{chr(65+col_idx)}{row_idx+1}" content = str(cell).strip() if len(content) > 100: content = content[:100] + "..." matches.append((row_idx+1, f"工作表 '{sheet.name}' 单元格 {cell_ref}: {content}")) # PDF文件处理 - 修复内容提取 elif ext_lower == '.pdf': with open(file_path, 'rb') as f: pdf = PyPDF2.PdfReader(f) for page_num in range(len(pdf.pages)): page_text = pdf.pages[page_num].extract_text() if page_text: # 修复:查找所有匹配项 for match in pattern.finditer(page_text): # 提取匹配上下文 start = max(0, match.start() - 30) end = min(len(page_text), match.end() + 70) context = page_text[start:end].replace('\n', ' ').strip() matches.append((page_num+1, f"页面 {page_num+1}: {context}")) except Exception as e: print(f"搜索Office文件出错: {file_path} | {str(e)}") return [] return matches def is_binary(self, file_path): """检测文件是否为二进制""" try: with open(file_path, 'rb') as f: chunk = f.read(1024) if b'\x00' in chunk: # 二进制文件通常包含空字节 return True # 检测字符编码 result = chardet.detect(chunk) return result['encoding'] is None or 'ascii' not in result['encoding'].lower() except: return True def detect_encoding(self, file_path): """检测文件编码""" with open(file_path, 'rb') as f: raw_data = f.read(4096) result = chardet.detect(raw_data) return result['encoding'] or 'utf-8' def stop_search(self): """停止搜索""" self.stop_requested = True self.update_status("正在停止搜索...") def show_file_content(self, event=None): """在预览区域显示文件内容""" selected = self.file_tree.selection() if not selected: return file_path = self.file_tree.item(selected[0])['values'][1] matches = self.results.get(file_path, []) self.content_text.config(state=tk.NORMAL) self.content_text.delete(1.0, tk.END) # 显示文件头信息 self.content_text.insert(tk.END, f"文件: {file_path}\n", "header") self.content_text.insert(tk.END, f"共找到 {len(matches)} 个匹配项\n\n", "header") # 显示匹配内容 for line_num, content in matches: self.content_text.insert(tk.END, f"行 {line_num}: ", "linenum") # 高亮显示匹配的关键字 if self.highlight_var.get(): start_idx = "1.0" line_tag = f"line_{line_num}" self.content_text.insert(tk.END, content + "\n\n", line_tag) # 搜索并高亮匹配文本 pattern = re.escape(self.keyword_entry.get().strip()) if self.case_var.get(): pattern = f"(?i){pattern}" idx = "1.0" while True: idx = self.content_text.search( pattern, idx, stopindex=tk.END, regexp=True, nocase=self.case_var.get() ) if not idx: break end_idx = f"{idx}+{len(self.keyword_entry.get().strip())}c" self.content_text.tag_add("match", idx, end_idx) idx = end_idx else: self.content_text.insert(tk.END, content + "\n\n") self.content_text.config(state=tk.DISABLED) def open_selected_file(self, event=None): """打开选中的文件""" selected = self.file_tree.selection() if selected: file_path = self.file_tree.item(selected[0])['values'][1] if os.path.exists(file_path): os.startfile(file_path) if sys.platform == "win32" else os.system(f'open "{file_path}"') def open_file_location(self): """打开文件所在位置""" selected = self.file_tree.selection() if selected: file_path = self.file_tree.item(selected[0])['values'][1] if os.path.exists(file_path): if sys.platform == "win32": os.startfile(os.path.dirname(file_path)) elif sys.platform == "darwin": os.system(f'open "{os.path.dirname(file_path)}"') else: os.system(f'xdg-open "{os.path.dirname(file_path)}"') def copy_selected_text(self): """复制选中的文本""" if self.content_text.tag_ranges(tk.SEL): selected = self.content_text.get(tk.SEL_FIRST, tk.SEL_LAST) self.master.clipboard_clear() self.master.clipboard_append(selected) def show_file_context_menu(self, event): """显示文件右键菜单""" item = self.file_tree.identify_row(event.y) if item: self.file_tree.selection_set(item) self.file_menu.tk_popup(event.x_root, event.y_root) def export_results(self): """导出搜索结果到文本文件""" if not self.results: messagebox.showinfo("导出", "没有搜索结果可导出") return file_path = filedialog.asksaveasfilename( title="保存搜索结果", filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")], defaultextension=".txt" ) if not file_path: return try: with open(file_path, 'w', encoding='utf-8') as f: f.write(f"搜索目录: {self.dir_entry.get()}\n") f.write(f"关键词: {self.keyword_entry.get()}\n") f.write(f"文件过滤: {self.filter_entry.get()}\n") f.write(f"匹配选项: {'忽略大小写' if self.case_var.get() else '区分大小写'}, " f"{'正则表达式' if self.regex_var.get() else '普通文本'}\n\n") for file_path, matches in self.results.items(): f.write(f"文件: {file_path}\n") f.write(f"匹配数: {len(matches)}\n") for line_num, content in matches: f.write(f"行 {line_num}: {content}\n") f.write("\n") messagebox.showinfo("导出成功", f"搜索结果已保存到:\n{file_path}") except Exception as e: messagebox.showerror("导出错误", f"保存文件失败: {str(e)}") def main(): root = tk.Tk() app = FileSearchApp(root) root.mainloop() if __name__ == "__main__": main() 1、由于布局变化,进度条的显示出现问题。2、文件过滤使用时,检索不到文件,文件夹里确保有文件的前提下。3、将文件过滤里面的类型去除掉后,能够搜索到文件,但是搜索不到文件里面的内容,也就是找不到匹配项这几个问题仍然存在。我附上代码你排查问题
09-13
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值