Что нового

Ускорить добавление много строк в GUICtrlCreateListView

La2Angel

Новичок
Сообщения
156
Репутация
1
Доброго вечера, вопрос собственно в этом, как ускорить процесс добавления строк в GUICtrlCreateListView? У меня просто массив идет папок и подпапок ( в пределах до 20 000 файлов), соответственно и строк столько же. Вот, и если обычным способом добавлять, программа прям уходит в ожидание чуда на минуту или минуты 3. Помогите пожалуйста, есть ли другой способ?


Добавлено:
Сообщение автоматически объединено:

Оу, простите, уже нашел в справке у вас на сайте - Справка по AutoIt - _GUICtrlListView_AddArray
 

AZJIO

Меценат
Меценат
Сообщения
2,879
Репутация
1,194
Эти функции приостанавливают перерисовку ListView, что и ускоряет процесс.
_GUICtrlListView_BeginUpdate
_GUICtrlListView_EndUpdate
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Да, это значительно ускорит добавление элементов, но для 20000 записей это все равно может показаться недостаточно. Для достижения максимальной скорости в рамках AutoIt нужно работать на уровне API. В частности, избавиться от постоянного создания структур в функциях добавления элементом в список, что ускорит работу еще примерно в два раза.
 

YOgen

Знающий
Сообщения
58
Репутация
5
Yashied [?]
Да, это значительно ускорит добавление элементов, но для 20000 записей это все равно может показаться недостаточно. Для достижения максимальной скорости в рамках AutoIt нужно работать на уровне API. В частности, избавиться от постоянного создания структур в функциях добавления элементом в список, что ускорит работу еще примерно в два раза.
А можно было бы хоть какой-то приблизительный пример накидать? Хоть в какую сторону смотреть и изучать на конкретном примере (ибо сам не программист... я только учусь :-[)
 

AZJIO

Меценат
Меценат
Сообщения
2,879
Репутация
1,194
YOgen
Нужно оптимизировать функцию добавления пункта в ListView. Я уже пробовал в Search duplicates. Во первых добавление вызывается через функцию Insert, которая в свою очередь имеет ветвления взависимости от некоторых условий, и к этому ещё добавлена проверка поддержки юникода ListView, что увеличивает время ещё на 30%.

Вот мой вариант:
Код:
$fLV_Unicode = _GUICtrlListView_GetUnicodeFormat($hLV)
$aArr[$i] = _LV_InsertItem($hLV, $sText[$i]) 

Func _LV_InsertItem($hWnd, $sText, $iIndex = -1, $iImage = -1, $iParam = 0)
	Local $iBuffer, $tBuffer, $iRet
	If $iIndex = -1 Then $iIndex = 999999999

	Local $tItem = DllStructCreate($tagLVITEM)
	DllStructSetData($tItem, "Param", $iParam)

	$iBuffer = StringLen($sText) + 1
	If $fLV_Unicode Then
		$tBuffer = DllStructCreate("wchar Text[" & $iBuffer & "]")
		$iBuffer *= 2
	Else
		$tBuffer = DllStructCreate("char Text[" & $iBuffer & "]")
	EndIf
	DllStructSetData($tBuffer, "Text", $sText)
	DllStructSetData($tItem, "Text", DllStructGetPtr($tBuffer))
	DllStructSetData($tItem, "TextMax", $iBuffer)

	Local $iMask = BitOR($LVIF_TEXT, $LVIF_PARAM)
	If $iImage >= 0 Then $iMask = BitOR($iMask, $LVIF_IMAGE)
	DllStructSetData($tItem, "Mask", $iMask)
	DllStructSetData($tItem, "Item", $iIndex)
	DllStructSetData($tItem, "Image", $iImage)

	$iRet = _SendMessage($hWnd, $LVM_INSERTITEMW, 0, $tItem, 0, "wparam", "struct*")

	Return $iRet
EndFunc


Перед массовым добавлением обязательно вызвать проверку юникода (либо в начале программы если ListView постоянный). Обязательно только для внутреннего ListView, то есть нельзя применять для добавления во внешний (другой процесс). Оставлена возможность добавлять изображение и ассоциативный параметр. Но можно и это урезать, но проверка показывает что многие урезания не влияют заметно на скорость.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
AZJIO, я имел ввиду оптимизировать добавление большого количества элементов. DllStructCreate() сама по себе довольно медленная, поэтому имеет смысл сделать так:

- создать структуру $tagLVITEM и буфер для текста заведомо большего размера
- заполнить постоянные поля в структуре $tagLVITEM
- в цикле заполнить список посылая LVM_INSERTITEMW и заполняя буфер и переменные поля $tagLVITEM структуры
- удалить все используемые структуры

Это позволит существенно сократить время заполнения списка. Я это уже применял в своих программах, например, в SynFolders 3.0.

P.S.

Проверку Unicode делать не нужно, т.к. AutoIt больше не работает с ANSI. В _GUICtrl... функциях это реализовано только для сторонних окон. От этого, кстати, зависит какое сообщение посылать - LVM_INSERTITEMA или LVM_INSERTITEMW.
 
Автор
L

La2Angel

Новичок
Сообщения
156
Репутация
1
Ну вот то, что я смог накидать себе :smile:

Код:
; Функция выбора файлов и добавления в список отображения.
Func SelectFiles()
	$SelectFilder = FileSelectFolder($TitleOpenDialog,@DesktopDir,2)
	If @error Then
		MsgBox(0,$TitleInstallShell,'Вы не выбрали папку!')
	Else
		SplashTextOn($TitleInstallShell,'Идет обработка списка, пожалуйста ждите...',400,70,-1,-1,1,'',13)
		
		$FileList = _FO_FileSearch($SelectFilder) ; Запускаем сканер выбранной директории
		
		Dim $FileList2[UBound($FileList)][2] ; Создаем массив согласно размеру найденных файлов
		
		; Запускаем цикл по определению размера у каждого файла
		For $i = 1 To UBound($FileList)-1
			$FileList2[$i-1][0] = $FileList[$i]
			$FileList2[$i-1][1] = FileGetSize($FileList[$i])/1024
		Next
		
		$Sum = 0
		
		; Считаем общий размер всей выбранной директории
		For $i = 0 To UBound($FileList2)-1
			$Sum += $FileList2[$i][1]
		Next
		
		If Round($Sum) >1000 Then 
			GUICtrlSetData($SetSize,Round($Sum/1024/1024,1) & ' Гб.')
		Else
			GUICtrlSetData($SetSize,Round($Sum/1024,2) & ' Мб.')
		EndIf
		_GUICtrlListView_AddArray($ListFiles, $FileList2)
		SplashOff()
	EndIf

EndFunc



Теперь все работает шустро :smile: покрайней мере по сравнению с тем, что было, в раз 5-7 быстрее:smile:
 

AZJIO

Меценат
Меценат
Сообщения
2,879
Репутация
1,194
La2Angel
Оптимизация.
1. Массив уже содержит количество найденных файлов в элементе с индексом ноль
2. Два цикла объеденены в один, потому что можно за раз сделать операции.
3. Использовал функцию _FO_ShortFileSize, в принципе есть аналог WinAPI.
4. После вызова _FO_FileSearch обязательна проверка ошибок. Если папка пуста то возвращается не массив.
Код:
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <FileOperations.au3>
#include <GuiListView.au3>

$hGui = GUICreate('My Program', 495, 420, -1, -1, $WS_OVERLAPPEDWINDOW + $WS_POPUP)
$iListView = GUICtrlCreateListView("Файл|Размер", 5, 5, 485, 390)
GUISetState()

$SetSize = GUICtrlCreateLabel('StatusBar', 5, 420 - 20, 495 - 10, 17)
SelectFiles($iListView)
For $i = 0 To 1
	GUICtrlSendMsg($iListView, $LVM_SETCOLUMNWIDTH, $i, -1)
	GUICtrlSendMsg($iListView, $LVM_SETCOLUMNWIDTH, $i, -2)
Next

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

; Функция выбора файлов и добавления в список отображения.
Func SelectFiles($iListView)
	$TitleInstallShell = ''
	$SelectFilder = FileSelectFolder('', @DesktopDir, 2)
	If @error Then
		MsgBox(0, $TitleInstallShell, 'Вы не выбрали папку!')
	Else
		SplashTextOn($TitleInstallShell, 'Идет обработка списка, пожалуйста ждите...', 400, 70, -1, -1, 1, '', 13)

		$FileList = _FO_FileSearch($SelectFilder) ; Запускаем сканер выбранной директории
		If @error Then Return MsgBox(0, $TitleInstallShell, 'Папка пуста!')

		Local $FileList2[$FileList[0] + 1][2] ; Создаем массив согласно размеру найденных файлов

		; Запускаем цикл по определению размера у каждого файла
		$Sum = 0
		For $i = 1 To $FileList[0]
			$FileList2[$i - 1][0] = $FileList[$i]
			$tmp = FileGetSize($FileList[$i])
			$Sum += $tmp ; Считаем общий размер всей выбранной директории
			$FileList2[$i - 1][1] = _FO_ShortFileSize($tmp)
		Next

		GUICtrlSetData($SetSize, _FO_ShortFileSize($Sum))
		_GUICtrlListView_AddArray($iListView, $FileList2)
		SplashOff()
	EndIf

EndFunc   ;==>SelectFiles
 
Верх