Events während des Verschiebens eines Fensters

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Events während des Verschiebens eines Fensters

Beitrag von Kurzer »

Hallo,

Ich habe eine Eventschleife [WaitWindowEvent(25)], die u.a. das Event #PB_Event_MoveWindow eines Fensters auswertet und daraufhin den Fensterinhalt neuzeichnet.

Wie ich jetzt feststelle, kommen die Events während des Verschiebens des Fensters nicht bzw. wird die Eventschleife in der Zeit des Verschiebevorgangs nicht abgearbeitet.

Das hat zur Folge, daß der Fensterinhalt nicht refreshed wird, solange die Maustatste gedrückt ist und ich das Fenster verschiebe. Verschiebe ich es z.B. über den Rand des Desktops, dann wird der Inhalt gelöscht. Erst wenn ich die Maustatse wieder loslasse, dann kommen die Events und das Fenster wird refreshed.

Wie kann ich erreichen, daß ich auch während des Verschiebens des Fensters den Inhalt erneuern kann (möglchst eine saubere Methode - also nur dann refresh, wenn nötig)?
Benutzung eines ImageGadgets, in den der Inhalt gerendert wird, fällt leider aus.
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Benutzeravatar
edel
Beiträge: 3667
Registriert: 28.07.2005 12:39
Computerausstattung: GameBoy
Kontaktdaten:

Beitrag von edel »

Wenn ich dich richtig verstanden habe, kann man mit folgendem Code
dein Problem nachvollziehen.

Code: Alles auswählen

h = OpenWindow(0,0,0,300,300,"")

Repeat
	e = WaitWindowEvent()

	If e = #PB_Event_Repaint
		StartDrawing(WindowOutput(0))
		Box(0,0,300,300,$5F8EA0)
		StopDrawing()
	EndIf 

Until e = 16

Eine Loesung waere hier, einen Callback zu setzen :

Code: Alles auswählen

Procedure c(h,m,w,p)
	
	If m = #WM_PAINT
		StartDrawing(WindowOutput(0))
			Box(0,0,300,300,$5F8EA0)
		StopDrawing()		
	EndIf 
	
	ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure


h = OpenWindow(0,0,0,300,300,"")
SetWindowCallback(@c(),0)

Repeat
	e = WaitWindowEvent()

Until e = 16

Benutzeravatar
PMV
Beiträge: 2765
Registriert: 29.08.2004 13:59
Wohnort: Baden-Württemberg

Beitrag von PMV »

Wie ich jetzt feststelle, kommen die Events während des Verschiebens des Fensters nicht bzw. wird die Eventschleife in der Zeit des Verschiebevorgangs nicht abgearbeitet.
Während das Fenster festgehalten wird, wird das ganze Programm
"angehalten". (Wait)WindowEvent() kehrt erst wieder zurück, wenn die
Maus losgelassen wird. Das betrifft also nicht nur die Eventqueue.

Zum refreshen des Fensters benötigst du ein Callback wie edel dir es
vorzeigt.

MFG PMV
alte Projekte:
TSE, CWL, Chatsystem, GameMaker, AI-Game DLL, Fileparser, usw. -.-
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

kann aber auch extrem langsam werden dadurch, ruckeln und so.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Beitrag von Kurzer »

Jau, das ist es. Danke Edel. :allright:

@Kaeru: Jupp, ich sehe zu, daß die Refresh-Routine nicht zu aufwändig wird. In Edels code kann man ja gut sehen, daß sich das handling im Fall des Neuzeichnens einer simplen Box -gefühlt- überhaupt nicht verlangsamt.

In meinem Fall geht es zwar nur um das Refreshen von 2 Images (siehe irgend nen anderen Thread von mir ;-)), aber ich mag es halt, wenn ein Programm möglichst "in sich stimmig ist" und der Inhalt des Fensters nicht vom User "kaputtgeschoben" werden kann. Das provoziert nur Irritation oder mitleidiges Schmunzeln ("welcher Idiot hat das denn gecoded?") ;-)

*schmökert-in-C-Beispielcodes*
Sehe ich das richtig, daß die Messagebearbeitung in C/C++ generell über eine Callback-Procedure geregelt wird? Wenn ja, welchen Sinn macht dann das Event-Schleifen Konstrukt in PureBasic (und ggf. anderen Hochsprachen)? In meinem Beispiel hat es zumindest eine Schwäche, nämlich, daß die Events während des Verschiebens nicht verarbeitet werden können.
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

PB ist procedural.
das bedeutet, du benötigst einen Befehl, der Events bearbeitet,
und ein Event zurückliefert, das bearbeitet werden kann.

in OOP bekommt jedes Objekt nur sein eigenes Event,
und hat selbstverständlich eine reaktionsbearbeitung.
also, wenn das Objekt die hand hebt, es wolle bei jedem Event benachrichtigt werden,
dann wird es das, und dann kann es auch während der verschiebung seine Events abschicken,
an das überobjekt das den DesktopRefresh regelt.

...also... so versteh ich soweit die OOOrganisation von windows,
möge edel oder andere mich korrigieren.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
edel
Beiträge: 3667
Registriert: 28.07.2005 12:39
Computerausstattung: GameBoy
Kontaktdaten:

Beitrag von edel »

Mit OOP hat das ueberhaupt nichts zu tun. AVAIK ist das eh alles in C
geschrieben.Langsam wird durch die Benutzung eines Callbacks auch
nichts.

Das Problem bei der PB Eventschleife ist, dass die Events die ankommen,
schon intern von Windows verarbeitet wurden. Man kann also erst immer
dann reagieren, wenn es schon vorbei ist. Mit einem Callback faengt man
die Events ab, und bestimmt selber was damit passiert.

Die Eventschleife von PB hat aber auch Vorteile, sie laeuft z.B. auf jedem
System (die Callbacks gibt es nur unter Windows) und man muss nicht
fuer jedes Fenster einen Callback setzen um an die Events zu kommen.
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Beitrag von Kurzer »

Callback ist installiert... allerdings stellt sich jetzt das nächste Problem.
Callback sieht so aus:

Code: Alles auswählen

Procedure Preview_WinCallback(hWnd, uMsg, wParam, lParam) 
	; +-----------------------------------------------------------------
	; |Description  : CallbackProzedure des Preview-Fensters
	; |Arguments    : hWnd    - Fensterhandle
	; |				: uMsg    - Messagekonstante
	; |				: wParam  - w-Parameter
	; |				: lParam  - l-Parameter
	; |Results      : -
	; +-----------------------------------------------------------------
  
  Select uMsg
   	Case #WM_PAINT
			Preview_RedrawImages()
	EndSelect

  ProcedureReturn #PB_ProcessPureBasicEvents 
  
EndProcedure
Das Programm zeichnet auch prima die Images neu, während ich das Fenster über die Desktopgrenzen hinaus bewege.
Bild

Allerdings habe ich auch eine Funktion, die das Fenster und die Images darin in der Größe anpasst.
Wenn ich also doppelt so große Images in einem entsprechend großen Fenster anzeige und danach das Fenster und die Images verkleinere, dann habe ich folgenden Effekt:
Bild

Der Hintergrund des Fensters wird nicht restauriert. Evtl. liegt das daran, daß ich eine eigene #WM_PAINT Routine im CallBack habe. Aber eigentlich kann es das auch nicht sein, weil die Rahmen des Fensters auch dann von Windows restauriert werden, wenn ich mit einem anderen Fenster drübergehe. Und zu diesem Zeitpunkt existiert der Callback noch.

Ich habe schon rumprobiert mit InvalidateRect_() oder RedrawWindow_(), aber das half alles nix, weil entweder das Fenster komplett grau bleib (die Images übermalt wurden) oder aber beim Moven alles wie blöd flackerte, weil "Images malen, Alles löschen, Images malen" ausgeführt wurde. Irgendwie bekomme ich Windows nicht dazu erst zu löschen und dann in meinen CallBack zu springen... Aber das scheint mir auch unlogisch, weils der Logik des CallBacks wiedersprechen würde.

Vermutlich muß ich den Fensterhintergrund doch selbst löschen, bevor ich die Images neu draufzeichne.
Und da kommen wir zum nächsten Problem. Ich kann per PB nicht die Hintergrundfarbe eines Fensters auslesen, wenn ich sie nicht vorher explizit gesetzt habe.
Farbe = GetWindowColor(#Window)
Beschreibung
Gibt die Hintergrundfarbe des angegebenen Fensters '#Window' zurück, welche zuvor mittels SetWindowColor() festgelegt wurde.
Wenn noch keine Hintergrundfarbe festgelegt wurde, dann wird -1 zurückgegeben
Wo in etwa muß ich in der API graben, um an die Hintergrundfarbe meines Fensters zu kommen, damit ich es mit Box() löschen kann bevor ich die Images draufzeichne?

Edit:
Sodele, ich bin jetzt auf GetSysColor_() gestossen mit dem Parameter #COLOR_WINDOW, der angeblich die Hintergrundfarbe des im Device Context befindlichen Fensters zurückgibt. Stimmt zwar nicht, denn es kommt weiß zurück statt grau, was im Fenster dargestellt ist, aber egal. Nehme ich halt #COLOR_MENU, was angeblich die Hintergrundfarbe des Menüs des Fensters darstellen soll. Jedenfalls stimmt das mit der tatsächlichen Farbe überein, die das Fenster zuvor hatte. (Daß mich diese Methode nicht wirklich zufriedenstellt, ist nachvollziehbar, oder? /:-> ).

Jedenfalls bügle ich die restlichen Flächen zwischen den Images nun mit entsprechenden Box() Befehlen bei der Bearbeitung der #WM_PAINT Message mit über.
Ich habe das hier mal mit verschiedenen Farben deutlich gemacht:
Bild

Code: Alles auswählen

 	StartDrawing(WindowOutput(#Window_Preview))
 		Box(0, 0, 30 + (ImageSettings_Original\dsBitmap\bmWidth / ProgramSettings\lScaleFactor) * 2, 10, $aa00cc)
 		Box(0, 10 + ImageSettings_Original\dsBitmap\bmHeight / ProgramSettings\lScaleFactor, 30 + (ImageSettings_Original\dsBitmap\bmWidth / ProgramSettings\lScaleFactor) * 2, 10, $ff44ff)
 		Box(0, 10, 10, ImageSettings_Original\dsBitmap\bmHeight / ProgramSettings\lScaleFactor, $ff0000)
 		Box(10 + ImageSettings_Original\dsBitmap\bmWidth / ProgramSettings\lScaleFactor, 10, 10, ImageSettings_Original\dsBitmap\bmHeight / ProgramSettings\lScaleFactor, $00ff00)
 		Box(20 + (ImageSettings_Original\dsBitmap\bmWidth / ProgramSettings\lScaleFactor) * 2, 10, 10, ImageSettings_Original\dsBitmap\bmHeight / ProgramSettings\lScaleFactor, $000ff)

		DrawImage(ImageID(#ImageID_Original), 10, 10, ImageSettings_Original\dsBitmap\bmWidth / ProgramSettings\lScaleFactor, ImageSettings_Original\dsBitmap\bmHeight / ProgramSettings\lScaleFactor)
		DrawImage(ImageID(#ImageID_Encrypted), 20 + (ImageSettings_Original\dsBitmap\bmWidth / ProgramSettings\lScaleFactor), 10, ImageSettings_Encrypted\dsBitmap\bmWidth / ProgramSettings\lScaleFactor, ImageSettings_Encrypted\dsBitmap\bmHeight / ProgramSettings\lScaleFactor)
	StopDrawing()
Trotzdem glaube ich nicht, daß das der richtige Weg ist. Weiß jemand aus der C-Welt, wie es das Betriebssystem macht? Da wird doch mit Brushes hantiert, wenn der Fensterhintergrund neugezeichnet wird oder?
Noch besser wäre ja, wenn ich innerhalb der #WM_PAINT Bearbeitung (im Callback) die Windowseigene WM_PAINT Routine dieses Fensters aufrufen könnte, denn die kann den Fensterhintergrund "richtig" neuzeichnen.
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Benutzeravatar
Kurzer
Beiträge: 1617
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Beitrag von Kurzer »

*schubs* wegen des letzten Edits. ;)
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.12 x64, OS: Win 11 24H2 x64, Desktopscaling: 150%, CPU: I7 12700 H, RAM: 32 GB, GPU: Intel(R) Iris(R) Xe Graphics | NVIDIA GeForce RTX 3070
Useralter in 2025: 57 Jahre.
Benutzeravatar
Fluid Byte
Beiträge: 3110
Registriert: 27.09.2006 22:06
Wohnort: Berlin, Mitte

Beitrag von Fluid Byte »

Bin jetzt zu faul alles zu lesen, wo brauchste Hilfe?
Windows 10 Pro, 64-Bit / Outtakes | Derek
Antworten