check_readonly.cpp

本文介绍了一个简单的Windows应用程序实例,包括窗口类注册、消息处理及文件属性检查等功能。通过具体代码展示了如何针对不同版本的Windows系统进行适配,并实现基本的用户交互。

  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 "Check_ReadOnly.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_COMMAND :
              switch( LOWORD( wParam ) )
              {
                 case IDM_TEST :
                        // Check if the file is read-only.
                        //................................
                        if ( GetFileAttributes("FILE.DAT") |
                            FILE_ATTRIBUTE_READONLY )
                        {
                           // Clear the file attributes.
                           //...........................
                           SetFileAttributes("FILE.DAT",FILE_ATTRIBUTE_NORMAL);
                        }

                        DeleteFile("FILE.DAT");
                        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);
}

运行报以下错误,如何解决 Loaded checkpoint from ./trained_models/nyuv2/r34_NBt1D.pth I0000 00:00:1742470009.822851 1296506 gpu_device.cc:2019] Created device /job:localhost/replica:0/task:0/device:GPU:0 with 3088 MB memory: -> device: 0, name: NVIDIA GeForce RTX 4090 D, pci bus id: 0000:51:00.0, compute capability: 8.9 I0000 00:00:1742470009.823513 1296506 gpu_device.cc:2019] Created device /job:localhost/replica:0/task:0/device:GPU:1 with 464 MB memory: -> device: 1, name: NVIDIA GeForce RTX 4090 D, pci bus id: 0000:c3:00.0, compute capability: 8.9 [ WARN:0@3.446] global loadsave.cpp:268 findDecoder imread_('./datasets/nyuv2/test/rgb/labels/image_0726_label.png.png'): can't open/read file: check file path/integrity [ WARN:0@3.446] global loadsave.cpp:268 findDecoder imread_('./datasets/nyuv2/test/rgb/depth/image_0729_depth.png.png'): can't open/read file: check file path/integrity [ WARN:0@3.447] global loadsave.cpp:268 findDecoder imread_('./datasets/nyuv2/test/rgb/rgb/image_0724_color.png.png'): can't open/read file: check file path/integrity [ WARN:0@3.448] global loadsave.cpp:268 findDecoder imread_('./datasets/nyuv2/test/rgb/depth/image_0737_depth.png.png'): can't open/read file: check file path/integrity [ WARN:0@3.448] global loadsave.cpp:268 findDecoder imread_('./datasets/nyuv2/test/rgb/rgb/image_0748_color.png.png'): can't open/read file: check file path/integrity [ WARN:0@3.449] global loadsave.cpp:268 findDecoder imread_('./datasets/nyuv2/test/rgb/labels/image_0734_label.png.png'): can't open/read file: check file path/integrity [ WARN:0@3.449] global loadsave.cpp:268 findDecoder imread_('./datasets/nyuv2/test/rgb/rgb/image_0732_color.png.png'): can't open/read file: check file path/integrity [ WARN:0@3.450] global loadsave.cpp:268 findDecoder imread_('./datasets/nyuv2/test/rgb/rgb/image_0740_color.png.png'): can't open/read file: check file path/integrity 报错这些是什么问题,如何解决
03-21
帮我用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
//串口 void MainWindow::do_DataHandler(QByteArray rx_buff) //串口线程接收工作 { unsigned char crc = 0; ui->textBrowser->setText(rx_buff.toHex(' ')); qDebug()<<"接收到数据: "<<rx_buff.toHex(' '); while(((unsigned char)rx_buff.length() >= 6)) { if((unsigned char)rx_buff.at(1) == FRAME_COMMAND_WRITE || (unsigned char)rx_buff.at(1) == FRAME_COMMAND_READ || (unsigned char)rx_buff.at(1) == FRAME_COMMAND_UPDATE) { if((unsigned char)rx_buff.at(1) == FRAME_COMMAND_WRITE || (unsigned char)rx_buff.at(1) == FRAME_COMMAND_READ) { for(int k = 0; k < rx_buff.at(4)+4; k++) { crc ^= rx_buff.at(1+k); } if((unsigned char )rx_buff.at(rx_buff.at(4)+4+1) == crc)//校验通过 { if((unsigned char)rx_buff.at(1) == FRAME_COMMAND_WRITE) { qDebug()<<"111111111111111111111111111111111"; if(Update_start) update_Deal(rx_buff); }else if((unsigned char)rx_buff.at(1) == FRAME_COMMAND_READ) { if(Update_start == 0) { //qDebug() <<"tab是"<<tabChange; if(tabChange){ qDebug() <<"进串口了"; processAngleData(rx_buff); }else{ readState_Deal(rx_buff); } } } rx_buff.remove(0, (unsigned char)rx_buff.at(4)+4+1+1); qDebug()<<"接收正确: "<<rx_buff.toHex(' '); }else { rx_buff.clear();//整个包丢掉 } }else if((unsigned char)rx_buff.at(1) == FRAME_COMMAND_UPDATE)//更新指令 { QByteArray part = rx_buff.mid(1, 5); quint32 check = (unsigned char)rx_buff.at(6)<<0 | (unsigned char)rx_buff.at(7)<<8 | (unsigned char)rx_buff.at(8)<<16 | (unsigned char)rx_buff.at(9)<<24; if(crc32(part) == check)//校验通过 { qDebug()<<"222222222222222222222222222222222222"; if(Update_start) update_Deal(rx_buff); rx_buff.remove(0, (unsigned char)10); }else//校验失败 { rx_buff.clear();//整个包丢掉 } } }else { rx_buff.clear();//整个包丢掉 } } // 获取当前时间 // QDateTime currentTime = QDateTime::currentDateTime(); // 转换为字符串 // QString timeString = currentTime.toString("HH:mm:ss.zzz"); }这里的接收是怎么写的,具体解释一下
09-18
import os import re import sys import time import threading import tkinter as tk from tkinter import ttk, filedialog, messagebox, scrolledtext from tkinter.font import Font import fnmatch import subprocess import shutil import docx from openpyxl import load_workbook import PyPDF2 import zipfile import chardet import xlrd # 添加对旧版Excel的支持 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(side=tk.LEFT, fill=tk.Y, padx=(0, 10), pady=5) # 使用网格布局管理器 row = 0 ttk.Label(search_frame, text="搜索目录:").grid(row=row, column=0, sticky="w", pady=5) self.dir_entry = ttk.Entry(search_frame, width=40) self.dir_entry.grid(row=row, column=1, padx=5, pady=5, sticky="we") self.dir_entry.insert(0, os.getcwd()) ttk.Button(search_frame, text="浏览...", command=self.browse_directory).grid(row=row, column=2, padx=5, pady=5) row += 1 ttk.Label(search_frame, text="关键词:").grid(row=row, column=0, sticky="w", pady=5) self.keyword_entry = ttk.Entry(search_frame, width=40) self.keyword_entry.grid(row=row, column=1, padx=5, pady=5, sticky="we") row += 1 ttk.Label(search_frame, text="文件过滤:").grid(row=row, column=0, sticky="w", pady=5) self.filter_entry = ttk.Entry(search_frame, width=40) self.filter_entry.grid(row=row, column=1, padx=5, pady=5, sticky="we") self.filter_entry.insert(0, "*") row += 1 # 添加分隔线 ttk.Separator(search_frame, orient=tk.HORIZONTAL).grid(row=row, column=0, columnspan=3, sticky="ew", pady=10) row += 1 # 搜索选项 options_frame = ttk.Frame(search_frame) options_frame.grid(row=row, column=0, columnspan=3, sticky="we", padx=5, pady=5) # 使用网格布局替代pack布局,更紧凑 self.case_var = tk.BooleanVar(value=False) ttk.Checkbutton(options_frame, text="忽略大小写", variable=self.case_var).grid(row=0, column=0, sticky="w", padx=(0, 10)) self.regex_var = tk.BooleanVar(value=False) ttk.Checkbutton(options_frame, text="正则表达式", variable=self.regex_var).grid(row=0, column=1, sticky="w", padx=(0, 10)) self.binary_var = tk.BooleanVar(value=False) self.binary_check = ttk.Checkbutton(options_frame, text="包含二进制", variable=self.binary_var) self.binary_check.grid(row=0, column=2, sticky="w") # 添加文件大小限制选项 self.limit_var = tk.BooleanVar(value=True) ttk.Checkbutton(options_frame, text="限制大小(100MB)", variable=self.limit_var).grid(row=0, column=3, sticky="w", padx=(10, 0)) row += 1 # 添加分隔线 ttk.Separator(search_frame, orient=tk.HORIZONTAL).grid(row=row, column=0, columnspan=3, sticky="ew", pady=10) row += 1 # 搜索按钮 button_frame = ttk.Frame(search_frame) button_frame.grid(row=row, column=0, columnspan=3, 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) row += 1 # 添加分隔线 ttk.Separator(search_frame, orient=tk.HORIZONTAL).grid(row=row, column=0, columnspan=3, sticky="ew", pady=10) row += 1 # 状态栏 status_frame = ttk.Frame(search_frame) status_frame.grid(row=row, column=0, columnspan=3, sticky="we", pady=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) # 创建结果面板 results_frame = ttk.LabelFrame(main_frame, text="搜索结果", padding=10) results_frame.pack(fill=tk.BOTH, expand=True, padx=(5, 0), 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) # 使用Treeview替代Listbox columns = ("filename", "path") self.file_tree = ttk.Treeview(file_list_frame, columns=columns, 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=300, anchor="w") self.file_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.file_tree.bind('<<TreeviewSelect>>', self.show_file_content) self.file_tree.bind('<Double-1>', self.open_selected_file) file_scroll = ttk.Scrollbar(file_list_frame, command=self.file_tree.yview) file_scroll.pack(side=tk.RIGHT, fill=tk.Y) self.file_tree.config(yscrollcommand=file_scroll.set) # 右键菜单 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) # 文本区域右键菜单 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.content_text.tag_configure("match", background="yellow") self.content_text.tag_configure("linenum", foreground="blue") self.content_text.tag_configure("header", foreground="darkgreen", font=("Arial", 10, "bold")) self.content_text.tag_configure("warning", foreground="red", font=("Arial", 10, "italic")) # 初始化变量 self.results = {} self.all_files = [] self.file_paths = [] 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 start_search(self): # 重置状态 self.progress_var.set(0) self.stop_requested = False self.results = {} self.all_files = [] self.file_paths = [] self.file_tree.delete(*self.file_tree.get_children()) self.content_text.delete(1.0, tk.END) self.status_label.config(text="正在搜索...") self.search_button.config(state=tk.DISABLED) self.stop_button.config(state=tk.NORMAL) self.stats_label.config(text="") # 获取搜索参数 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.search_button.config(state=tk.NORMAL) self.stop_button.config(state=tk.DISABLED) return if not keyword: messagebox.showerror("错误", "请输入搜索关键词") self.search_button.config(state=tk.NORMAL) self.stop_button.config(state=tk.DISABLED) return # 解析文件过滤器 if file_filter == "": filter_patterns = ['*'] else: separators = [';', '|', ' ', ','] for sep in separators: if sep in file_filter: filter_patterns = [pat.strip() for pat in file_filter.split(sep)] break else: filter_patterns = [file_filter] # 编译搜索模式 flags = re.IGNORECASE if self.case_var.get() else 0 try: if self.regex_var.get(): pattern = re.compile(keyword, flags) else: escaped_keyword = re.escape(keyword) pattern = re.compile(escaped_keyword, flags) except re.error as e: messagebox.showerror("正则表达式错误", f"无效的正则表达式: {str(e)}") self.search_button.config(state=tk.NORMAL) self.stop_button.config(state=tk.DISABLED) return # 在后台线程中执行搜索 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: # 收集所有匹配的文件 all_files = [] for root, _, files in os.walk(directory): if self.stop_requested: self.master.after(0, lambda: self.status_label.config(text="搜索已取消")) return for file in files: file_path = os.path.join(root, file) # 检查文件大小限制(避免处理超大文件) try: file_size = os.path.getsize(file_path) if file_size > 100 * 1024 * 1024: # 100MB continue except: continue # 检查是否符合任一过滤模式 if any(fnmatch.fnmatch(file, pat) for pat in filter_patterns): all_files.append(file_path) self.all_files = all_files total_files = len(all_files) # 初始化进度条 self.master.after(0, lambda: self.progress_bar.config(maximum=total_files)) self.master.after(0, lambda: self.stats_label.config(text=f"扫描到 {total_files} 个文件")) # 搜索每个文件 self.results = {} processed = 0 matches_found = 0 for file_path in self.all_files: if self.stop_requested: break processed += 1 # 更新进度条(安全方式) self.master.after(0, lambda v=processed: self.progress_var.set(v)) if processed % 10 == 0: # 每处理10个文件更新一次进度 self.master.after(0, lambda p=processed, t=total_files: self.stats_label.config(text=f"处理中: {p}/{t} 文件 ({round(p/t*100,1)}%)")) # 忽略二进制文件(除非用户选择包含) if not self.binary_var.get() and self.is_binary(file_path): continue # 获取文件扩展名 _, ext = os.path.splitext(file_path) ext_lower = ext.lower() # 处理Office文档 if ext_lower in ['.docx', '.xlsx', '.xls', '.xlsm', '.pptx', '.pdf', '.doc']: matches = self.search_in_office_file(file_path, pattern) # 处理压缩文件 elif ext_lower in ['.zip', '.rar', '.7z', '.tar', '.gz']: matches = self.search_in_archive(file_path, pattern) # 处理文本文件 else: matches = self.search_in_text_file(file_path, pattern) if matches: self.results[file_path] = matches matches_found += len(matches) # 在UI线程中添加文件到列表 filename = os.path.basename(file_path) self.master.after(0, lambda fp=file_path, fn=filename: self.file_tree.insert("", "end", values=(fn, fp))) # 更新完成状态 if self.stop_requested: status_text = f"搜索已取消 - 找到 {len(self.results)} 个文件, {matches_found} 个匹配项" else: status_text = f"搜索完成 - 找到 {len(self.results)} 个文件, {matches_found} 个匹配项" self.master.after(0, lambda: self.status_label.config(text=status_text)) self.master.after(0, lambda: self.stats_label.config(text=f"已处理 {processed}/{total_files} 文件")) self.master.after(0, lambda: self.progress_var.set(total_files)) except Exception as e: # 记录详细错误日志 error_info = f"搜索错误: {type(e).__name__} - {str(e)}" print(error_info) with open("search_errors.log", "a") as log: log.write(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - {error_info}\n") import traceback traceback.print_exc(file=log) self.master.after(0, lambda: messagebox.showerror( "搜索错误", f"发生严重错误: {error_info}\n详细信息已记录到日志" )) finally: self.master.after(0, lambda: self.search_button.config(state=tk.NORMAL)) self.master.after(0, lambda: self.stop_button.config(state=tk.DISABLED)) self.search_thread = None def search_in_text_file(self, filepath, pattern): """在文本文件中搜索匹配项""" matches = [] try: encoding = self.detect_encoding(filepath) try: with open(filepath, '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)) except UnicodeDecodeError: # 特殊编码处理回退 with open(filepath, 'rb') as f: content = f.read() try: text = content.decode('utf-8', errors='replace') except: text = content.decode('latin-1', errors='replace') for line_num, line in enumerate(text.splitlines(), 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)) except Exception as e: print(f"读取文本文件失败 {filepath}: {str(e)}") return matches def search_in_office_file(self, filepath, pattern): """在Office文件中搜索文本内容""" matches = [] _, ext = os.path.splitext(filepath) ext_lower = ext.lower() try: # DOCX文件处理 if ext_lower == '.docx': doc = docx.Document(filepath) # 搜索段落 for i, para in enumerate(doc.paragraphs, 1): if para.text and pattern.search(para.text): matches.append((i, f"段落 {i}: {para.text[:100]}" + ("..." if len(para.text) > 100 else ""))) # 搜索表格 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/XLS文件处理 elif ext_lower in ('.xlsx', '.xls', '.xlsm'): # 处理新格式Excel文件 if ext_lower in ('.xlsx', '.xlsm'): wb = load_workbook(filepath, 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}" cell_value = str(cell).strip() if len(cell_value) > 100: cell_value = cell_value[:100] + "..." matches.append((row_idx, f"工作表 '{sheet_name}' 单元格 {cell_ref}: {cell_value}")) # 处理旧格式Excel文件 elif ext_lower == '.xls': wb = xlrd.open_workbook(filepath) 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}" cell_value = str(cell).strip() if len(cell_value) > 100: cell_value = cell_value[:100] + "..." matches.append((row_idx+1, f"工作表 '{sheet.name}' 单元格 {cell_ref}: {cell_value}")) # PPTX文件处理 elif ext_lower == '.pptx': from pptx import Presentation ppt = Presentation(filepath) # 搜索幻灯片文本 for slide_idx, slide in enumerate(ppt.slides, 1): for shape in slide.shapes: if hasattr(shape, "text"): if shape.text and pattern.search(shape.text): content = shape.text.strip() if len(content) > 100: content = content[:100] + "..." matches.append((slide_idx, f"幻灯片 {slide_idx}: {content}")) # PDF文件处理 elif ext_lower == '.pdf': with open(filepath, '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 and pattern.search(page_text): # 提取匹配内容 matches_found = [] for match in pattern.finditer(page_text): context = page_text[max(0, match.start()-20):match.end()+20] context = context.replace('\n', ' ').strip() matches_found.append(context) # 添加到结果 if matches_found: preview = "; ".join(matches_found[:3]) # 显示前3个匹配 if len(matches_found) > 3: preview += f" ... (+{len(matches_found)-3} 更多)" matches.append((page_num+1, f"页面 {page_num+1}: {preview}")) # 旧版DOC文件处理 elif ext_lower == '.doc': try: # 尝试使用antiword转换DOC为文本 result = subprocess.run(['antiword', filepath], capture_output=True, text=True, timeout=10) if result.returncode == 0: doc_text = result.stdout for line_num, line in enumerate(doc_text.split('\n'), 1): if line and pattern.search(line): cleaned_line = line.strip() if len(cleaned_line) > 150: cleaned_line = cleaned_line[:150] + "..." matches.append((line_num, cleaned_line)) except Exception: # 备用方法:使用python-doc处理 import win32com.client word = win32com.client.Dispatch("Word.Application") word.Visible = False doc = word.Documents.Open(filepath) doc_text = doc.Content.Text doc.Close() word.Quit() for line_num, line in enumerate(doc_text.split('\n'), 1): if line and pattern.search(line): cleaned_line = line.strip() if len(cleaned_line) > 150: cleaned_line = cleaned_line[:150] + "..." matches.append((line_num, cleaned_line)) except Exception as e: print(f"处理Office文件失败 {filepath}: {str(e)}") return matches def search_in_archive(self, filepath, pattern): """在压缩文件中搜索匹配项""" matches = [] _, ext = os.path.splitext(filepath) ext_lower = ext.lower() try: # ZIP文件处理 if ext_lower in ('.zip', '.jar', '.war'): with zipfile.ZipFile(filepath, 'r') as archive: for name in archive.namelist(): # 只处理文本文件和Office文档 if not name.endswith(('/')) and not self.is_binary(name): try: with archive.open(name) as file: content = file.read(4096) # 只读取前4KB # 尝试检测编码 result = chardet.detect(content) encoding = result['encoding'] if result['confidence'] > 0.7 else 'utf-8' # 解码内容并搜索 try: text_content = content.decode(encoding, errors='replace') if pattern.search(text_content): matches.append((name, f"压缩文件中的文件: {name}")) except: # 二进制内容搜索 if pattern.search(content): matches.append((name, f"压缩文件中的文件(二进制内容): {name}")) except Exception: continue # 其他压缩格式(需要外部工具) elif ext_lower in ('.rar', '.7z', '.tar', '.gz'): # 使用7zip命令行工具解压并搜索 temp_dir = tempfile.mkdtemp() try: subprocess.run(['7z', 'x', filepath, f'-o{temp_dir}'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, timeout=60) # 递归搜索解压的目录 for root, _, files in os.walk(temp_dir): for file in files: full_path = os.path.join(root, file) _, file_ext = os.path.splitext(file) file_ext = file_ext.lower() # 只在文本/Office文件中搜索 if file_ext in ['', '.txt', '.py', '.java', '.c', '.cpp', '.h', '.html', '.xml', '.json', '.csv', '.docx', '.xlsx', '.pptx', '.pdf']: if file_ext in ['.docx', '.xlsx', '.pptx', '.pdf']: file_matches = self.search_in_office_file(full_path, pattern) else: file_matches = self.search_in_text_file(full_path, pattern) if file_matches: matches.append((file, f"压缩文件中的文件: {file}")) finally: shutil.rmtree(temp_dir, ignore_errors=True) except Exception as e: print(f"处理压缩文件失败 {filepath}: {str(e)}") return matches def detect_encoding(self, filepath): """改进的文件编码检测方法""" try: # 尝试读取文件前4KB进行编码检测 with open(filepath, 'rb') as f: raw_data = f.read(4096) # 使用chardet进行编码检测 result = chardet.detect(raw_data) # 优先使用检测到的编码,否则尝试常见编码 if result['confidence'] > 0.7: return result['encoding'] # 中文环境常用编码回退策略 common_encodings = ['utf-8', 'gbk', 'gb2312', 'gb18030', 'latin1'] for encoding in common_encodings: try: # 尝试解码验证 raw_data.decode(encoding, errors='strict') return encoding except UnicodeDecodeError: continue # 默认使用UTF-8 return 'utf-8' except Exception: return 'utf-8' def is_binary(self, filepath): """检查文件是否为二进制文件""" try: with open(filepath, 'rb') as f: chunk = f.read(1024) if b'\0' in chunk: # 空字节是二进制文件的标志 return True # 检查高字节值 if any(byte >= 0x80 for byte in chunk): return True return False except: return False def stop_search(self): """停止当前搜索""" self.stop_requested = True self.status_label.config(text="正在停止搜索...") self.stop_button.config(state=tk.DISABLED) # 冻结进度条显示当前进度 self.progress_bar.config(mode='indeterminate' if self.progress_var.get() == 0 else 'determinate') def export_results(self): """导出搜索结果""" if not self.results: messagebox.showinfo("导出结果", "没有可导出的搜索结果") return file_path = filedialog.asksaveasfilename( title="保存搜索结果为", defaultextension=".csv", filetypes=[("CSV 文件", "*.csv"), ("文本文件", "*.txt")] ) if not file_path: return try: with open(file_path, 'w', encoding='utf-8') as f: # 写出CSV头部 f.write("文件路径,匹配行号,匹配内容\n") # 写出每项结果 for file, matches in self.results.items(): for line_num, match_content in matches: # 清理内容中的逗号 cleaned_content = match_content.replace('"', '""').replace(',', ';') f.write(f'"{file}",{line_num},"{cleaned_content}"\n') messagebox.showinfo("导出成功", f"搜索结果已保存到:\n{file_path}") except Exception as e: messagebox.showerror("导出错误", f"导出失败: {str(e)}") def show_file_content(self, event=None): """在预览区域显示文件内容""" # 获取选中的文件 selection = self.file_tree.selection() if not selection: return selected_item = selection[0] filepath = self.file_tree.item(selected_item, 'values')[1] # 清空预览区域 self.content_text.delete(1.0, tk.END) # 获取文件扩展名 _, ext = os.path.splitext(filepath) ext_lower = ext.lower() # 显示文件路径标题 self.content_text.insert(tk.END, f"文件路径: {filepath}\n", "header") # 处理不同文件类型 try: # 处理Office文档 if ext_lower in ['.docx', '.xlsx', '.xls', '.xlsm', '.pptx', '.pdf', '.doc']: matches = self.results.get(filepath, []) if not matches: self.content_text.insert(tk.END, "\n未找到匹配内容\n", "warning") return self.content_text.insert(tk.END, f"\n找到 {len(matches)} 个匹配项:\n\n", "header") # 显示每个匹配项 for i, (line_num, content) in enumerate(matches, 1): self.content_text.insert(tk.END, f"[匹配项 {i}] 位置: {line_num}\n") self.content_text.insert(tk.END, f"{content}\n\n") # 处理压缩文件 elif ext_lower in ['.zip', '.rar', '.7z', '.tar', '.gz']: matches = self.results.get(filepath, []) if not matches: self.content_text.insert(tk.END, "\n未找到匹配内容\n", "warning") return self.content_text.insert(tk.END, f"\n找到 {len(matches)} 个匹配项:\n\n", "header") for i, (file_in_zip, content) in enumerate(matches, 1): self.content_text.insert(tk.END, f"[匹配项 {i}] 文件: {file_in_zip}\n") self.content_text.insert(tk.END, f"{content}\n\n") # 处理文本文件 else: # 获取关键词高亮模式 keyword = self.keyword_entry.get().strip() flags = re.IGNORECASE if self.case_var.get() else 0 if self.regex_var.get(): try: pattern = re.compile(keyword, flags) except: pattern = None else: pattern = re.compile(re.escape(keyword), flags) # 显示文件内容并高亮匹配 self.content_text.insert(tk.END, "\n文件内容:\n\n", "header") # 限制预览内容大小(最多显示1000行) max_preview_lines = 1000 try: encoding = self.detect_encoding(filepath) with open(filepath, 'r', encoding=encoding, errors='replace') as f: line_count = 0 for line in f: line_count += 1 if line_count > max_preview_lines: self.content_text.insert(tk.END, f"\n... (文件过大,仅显示前{max_preview_lines}行)\n", "warning") break # 插入行号 self.content_text.insert(tk.END, f"{line_count:4d} | ", "linenum") # 插入行内容并高亮匹配 if pattern: start_idx = 0 for match in pattern.finditer(line): # 插入匹配前的文本 self.content_text.insert(tk.END, line[start_idx:match.start()]) # 插入高亮的匹配文本 self.content_text.insert(tk.END, match.group(), "match") start_idx = match.end() # 插入匹配后的文本 self.content_text.insert(tk.END, line[start_idx:]) else: self.content_text.insert(tk.END, line) except UnicodeDecodeError: self.content_text.insert(tk.END, "\n无法解码此文件内容(可能是二进制文件)\n", "warning") except Exception as e: self.content_text.insert(tk.END, f"\n读取文件时出错: {str(e)}\n", "warning") except Exception as e: self.content_text.insert(tk.END, f"\n加载文件内容出错: {str(e)}\n", "warning") def open_selected_file(self, event=None): """用系统默认程序打开选中的文件""" selection = self.file_tree.selection() if not selection: return selected_item = selection[0] filepath = self.file_tree.item(selected_item, 'values')[1] try: if sys.platform == "win32": os.startfile(filepath) elif sys.platform == "darwin": # macOS subprocess.run(["open", filepath]) else: # Linux subprocess.run(["xdg-open", filepath]) except Exception as e: messagebox.showerror("打开文件失败", f"无法打开文件: {str(e)}") def open_file_location(self): """在文件资源管理器中打开文件所在位置""" selection = self.file_tree.selection() if not selection: return selected_item = selection[0] filepath = self.file_tree.item(selected_item, 'values')[1] folder = os.path.dirname(filepath) try: if sys.platform == "win32": subprocess.run(["explorer", "/select,", filepath]) elif sys.platform == "darwin": # macOS subprocess.run(["open", "-R", filepath]) else: # Linux subprocess.run(["xdg-open", folder]) except Exception as e: messagebox.showerror("打开位置失败", f"无法打开位置: {str(e)}") 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 copy_selected_text(self): """复制预览区域中选中的文本""" selected_text = self.content_text.get(tk.SEL_FIRST, tk.SEL_LAST) if selected_text: self.master.clipboard_clear() self.master.clipboard_append(selected_text) # 程序入口 if __name__ == "__main__": root = tk.Tk() app = FileSearchApp(root) # 添加图标(如果有) try: if sys.platform == "win32": root.iconbitmap("search_icon.ico") else: img = tk.PhotoImage(file="search_icon.png") root.iconphoto(True, img) except: pass root.mainloop() 这是我的代码,所有的功能都已经实现,但我觉得它太过于冗余,帮我优化一下,另外我觉得目前这个布局不够美观,也不够实用,帮我修改布局,另外,在搜索到关键字之后,我希望能够在文件预览那个窗口中,将搜索到的关键字进行高亮,或者可以选择高亮。在新增一些比较实用的功能
09-13
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值