Algunos controles no se dibujan, aparentemente al azar

2
Daniel Bauer 2019-05-07 14:34.

Estoy tratando de escribir una pequeña aplicación MFC solo para mí, para probar algunas IA que estoy entrenando.

Así que agregué un control de imagen y un control estático donde puedo pintar cosas libremente en el método OnPaint () de mi ventana principal.

Parece funcionar cuando solo dibujé mi aplicación una vez, pero ahora agregué un ciclo que realiza OnPaint () varias veces antes de detenerme.

Cuando en este bucle, algunos otros controles no aparecen, por ejemplo, todos mis botones se han ido, y algunos controles deslizantes incluso faltan algunas veces, pero otras veces, están ahí.

Mi código es así:

void CKiUebung1Dlg::OnBnClickedButtongo()
{
    m_bisGoing = true;
    OnPaint();
    if(m_fDiagramData.size() <= 0)
    {
        m_fDiagramData.push_back((float)rand() / RAND_MAX);
        InvalidateRect(NULL, TRUE);
    }
    OnPaint();
    for(int i(9); i >= 0; --i)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        m_fDiagramData.push_back((float)rand() / RAND_MAX);
        InvalidateRect(NULL, TRUE);
        OnPaint();
    }
    m_bisGoing = false;
    OnPaint();
}
void CKiUebung1Dlg::OnPaint()
{
    if(IsIconic())
    {
        CPaintDC dc(this); // Gerätekontext zum Zeichnen

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // Symbol in Clientrechteck zentrieren
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // Symbol zeichnen
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialogEx::OnPaint();
    }
    {
        constexpr const int border = 5;
        CPaintDC dc(&m_cDiagram);
        CRect l_cPos;
        m_cDiagram.GetClientRect(&l_cPos);
        const int width(l_cPos.Width() - border * 2 - 2), height(l_cPos.Height() - border * 2 - 12);
        const int numPoints(m_fDiagramData.size());
        POINT* points(new POINT[numPoints]);
        for(int i(numPoints - 1); i >= 0; --i)
        {
            const int
                x((float)i / (numPoints - 1) * width + border + 1),
                y(height - m_fDiagramData[i] * height + border + 9);
            points[i] = { x,y };
        }
        dc.Polyline(points, numPoints);

        static CString going(_T(" "));
        if(m_bisGoing) { going += _T("."); if(going.GetLength() > 300) going = _T(" ."); }
        else going = _T(" ");
        float fprog(0); if(m_fDiagramData.size() > 0) fprog = m_fDiagramData.back();
        CString prog; prog.Format(_T("Progress %03.2f%%"), fprog * 100); if(m_bisGoing) prog += going;
        m_cDiagram.SetWindowTextW(prog);

        m_cDiagram.RedrawWindow();

        delete[] points;
    }
}

Así es como se ve cuando el bucle no se está ejecutando:

Así es como se ve cuando el bucle se está ejecutando:

2 answers

3
Constantine Georgiou 2019-05-09 06:11.

Parece tener problemas para comprender cómo funciona la invalidación / pintura. La documentación que debe leer primero es: Pintura y dibujo

Si bien muchos desarrolladores recomiendan pintar solo durante el WM_PAINTprocesamiento ( OnPaint()en MFC), esta no siempre es la mejor solución, debido a que este mensaje es de baja prioridad, es posible que la pintura no sea inmediata (tenga una sensación "entrecortada") y puede aparecer "efecto.

En cambio, a veces recomiendo una combinación de dibujo y pintura:

  • Emplear pintura en el WM_PAINTprocesamiento. Esto debería pintar toda el área del cliente (o solo la parte invalidada, si quieres una implementación más "optimizada"). Tenga en cuenta que el WM_PAINTmensaje se puede recibir como resultado de invalidar una parte o la totalidad del área del cliente, debido a mover, cambiar el tamaño, mostrar, etc. la ventana, además de invalidarla programáticamente. Entonces, en respuesta a un WM_PAINTmensaje, debe realizar un repintado completo, es decir, todos los elementos que desea que se muestren.
  • Emplee el dibujo para los cambios que desea que se muestren de inmediato, mientras la aplicación está ocupada (sin esperar WM_PAINTa que se reciba el mensaje "asincrónico" ). Tenga en cuenta que estos deben estar en WM_PAINTel procesamiento, así, por lo que en lugar tiene que escribir algunas de dibujo / rutinas de pintura, teniendo una HDC(o CDC*) como un parámetro (a lo largo de cualquier otro parámetro necesario), y llamarlos tanto de la OnPaint()función (que pasa del ClientDCno ) y de sus acciones de dibujo adicionales necesarias (pasando un CDC*adquirido llamando GetDC()).

Entonces, permítanme compartir mi experiencia con una aplicación que escribí hace (mucho) tiempo. Es una aplicación de visualización / manipulación de imágenes (entre otras), que procesa imágenes en un formato personalizado y utiliza una biblioteca especial, que era bastante "lenta", ya que solo proporcionaba una función para mostrar la imagen en el contexto del dispositivo (esto incluye posibles recortes, ajustes, cambio de tamaño, etc.que son operaciones costosas de la CPU). Aquí hay una imagen:

Puede ver al usuario realizando una selección. La aplicación tiene que mostrar la imagen y posiblemente el rectángulo de selección encima, y ​​por supuesto eso es lo que OnPaint()hace. Una implementación "fácil" (aunque técnicamente "correcta") sería llamar Invalidate()o InvalidateRect()en respuesta a cada mensaje de movimiento del mouse (mientras se selecciona). Esto causaría un repintado completo (que está "OK"), pero también sufriría problemas de rendimiento, debido a la lentitud de la biblioteca de imágenes: si también llama UpdateWindow()después de invalidar (solicitar una actualización inmediata), el rendimiento sería lento (tener que reprocesar / volver a mostrar la imagen), de lo contrario, la actualización se llevaría a cabo un tiempo (notable) más tarde. Esto se resolvió empleando dibujar (no pintar) en respuesta al WM_MOUSEMOVEmensaje: no invalidar allí, en su lugar dibujar solo el rectángulo de selección (después de restaurar la parte modificada por el mensaje de selección anterior: solo hago una copia de seguridad / restauro los cuatro lados del marco, no todo el rectángulo). Como resultado, la aplicación responde y el funcionamiento es fluido, a pesar de la lentitud de la biblioteca, y muestra la imagen y la selección correctamente, incluso si cambia a otra aplicación y luego vuelve a ella, mientras se realiza el seguimiento de la selección (línea discontinua) .

Algunas notas y sugerencias sobre su implementación (tiene bastantes problemas):

  • Como han señalado otros miembros, es posible que no se llame a OnPaint()sí mismo. Especialmente esas llamadas posteriores no Invalidate()tienen ningún sentido. En su lugar, llame UpdateWindow(), si desea una actualización inmediata.
  • Imo NO está bien realizar cálculos dentro OnPaint(), y me refiero a esos cálculos de puntos (aunque en su caso el cálculo es bastante trivial). OnPaint()debería mostrar los datos calculados en otra parte de su código.
  • Además, configurar el m_cDiagramtexto y volver a pintar desde adentro OnPaint()tampoco está bien (puede causar solicitudes de pintura adicionales). Mejor mueva estos a OnBnClickedButtongo().
  • No es necesario invalidar (y en particular borrar) toda el área del cliente para que se vuelvan a pintar algunos controles, en su lugar, invalide solo esos controles. Recuerde, la sleep_for()función se bloquea y el WM_PAINTmensaje no se enviará ni procesará mientras se esté ejecutando el bucle.
  • Por cierto, considere un enfoque sin bloqueo, por ejemplo, usando un temporizador, como sugirió @Barmak Shemirani. Alternativamente, es posible escribir un "no bloqueo sleep()" ejecutando el bucle de mensajes usted mismo (tome partes del código CWinApp::Run()y modifíquelo).
  • Dado que tiene un diálogo y creó controles separados para mostrar sus datos, usar OnPaint()no es una buena implementación, ya que afecta (pinta) toda el área del cliente. Es sobre todo útil para clases como CViewo CScrollView(o pintura personalizada CWnden general). Pinta el gráfico en la superficie del cuadro de diálogo y tiene que realizar cálculos para obtener las coordenadas m_cDiagram(por cierto, puede usar GetWindowRect()y luego en su ScreenToClient()lugar) pero sería mejor usar un control dibujado por el propietario (para pintar / dibujar el gráfico), y no es realmente difícil, solo tiene que responder a las solicitudes de pintura (como en OnPaint()), y el contexto del dispositivo que obtiene puede pintar solo en el control, no en el diálogo; las coordenadas son relativas al área del cliente del control, comenzando desde (0,0).

Espero que esto ayude

3
Barmak Shemirani 2019-05-08 16:32.

CWnd::OnPaintes una respuesta a un WM_PAINTmensaje y no se debe llamar directamente.

WM_PAINTllamadas CWnd::OnPaint, que llamadas CPaintDC dc(this), que a su vez llamadas BeginPaint/ EndPaintAPI. Esta secuencia de mensaje + respuesta debe dejarse como está.

Por CPaintDC dc(this)lo tanto, debe aparecer una vez, y solo una vez, en el interior OnPainty no en ningún otro lugar. Anule de la OnPaintsiguiente manera:

void CMyDialog::OnPaint()
{
    CDialogEx::OnPaint(); //this will call CPaintDC dc(this);

    //optional: 
    CClientDC dc(this); //CClientDC can be used anywhere in a valid window
    //use dc for drawing
}

//or
void CMyDialog::OnPaint()
{
    CPaintDC dc(this); 
    //use dc for drawing
}

Tampoco necesita la if (IsIconic()) {...}condición desactualizada .

Para forzar que la ventana se vuelva a pintar, llame Invalidate()(lo mismo que InvalidateRect(NULL, TRUE))

InvalidateRect(NULL, TRUE)es una solicitud para volver a pintar la ventana. El sistema verá esta solicitud y enviará un WM_PAINTmensaje a esa ventana cuando haya una posibilidad. Por lo tanto, es posible que una llamada a InvalidateRectno se procese de la manera que espera que funcione en un programa secuencial. Por ejemplo, una segunda llamada consecutiva a InvalidateRectno tendrá ningún efecto. La ventana ya estaba marcada para actualizarse.

 for(int i(9); i >= 0; --i)
 {
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    m_fDiagramData.push_back((float)rand() / RAND_MAX);
    InvalidateRect(NULL, TRUE);
    OnPaint();
 }

OnPaint()debe eliminarse del código anterior. Aún así, la animación no es posible en un solo hilo (al menos no de esta manera). El programa está ocupado pasando por el bucle, no puede manejar WM_PAINTotros mensajes.

Por lo tanto, necesita un hilo adicional, o simplemente usar SetTimery responder a ON_WM_TIMER()/ OnTimerpara la animación. Ejemplo:

int counter = 0;

BEGIN_MESSAGE_MAP(CMyDialog, CDialogEx)
    ON_WM_PAINT()
    ON_WM_TIMER()
    ...
END_MESSAGE_MAP()

void CMyDialog::OnPaint()
{
    CPaintDC dc(this);
    CString s;
    s.Format(L"%02d", counter);
    dc.TextOut(0, 0, s);
}

void CMyDialog::animate()
{
    counter = 0;
    SetTimer(1, 1000, NULL);
}

void CMyDialog::OnTimer(UINT_PTR n)
{
    if(n == 1)
    {
        Invalidate(); //force repaint
        counter++;
        if(counter == 10)
            KillTimer(1);
    }
}

MORE COOL STUFF

La estrella de HGTV, Christina Hall, revela que tiene 'envenenamiento por mercurio y plomo' probablemente por voltear 'casas asquerosas'

La estrella de HGTV, Christina Hall, revela que tiene 'envenenamiento por mercurio y plomo' probablemente por voltear 'casas asquerosas'

La estrella de HGTV, Christina Hall, revela que le diagnosticaron envenenamiento por mercurio y plomo, probablemente debido a su trabajo como manipuladora de casas.

La estrella de 'Love Is Blind' Brennon Lemieux responde a los cargos de violencia doméstica

La estrella de 'Love Is Blind' Brennon Lemieux responde a los cargos de violencia doméstica

Recientemente salió a la luz un informe policial que acusa a la estrella de 'Love Is Blind', Brennon, de violencia doméstica. Ahora, Brennon ha respondido a los reclamos.

Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia Judd en un momento festivo de pánico

Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia Judd en un momento festivo de pánico

Conozca cómo Wynonna Judd se dio cuenta de que ahora es la matriarca de la familia mientras organizaba la primera celebración de Acción de Gracias desde que murió su madre, Naomi Judd.

Experto en lenguaje corporal explica los 'paralelos' entre Kate Middleton y la princesa Diana

Experto en lenguaje corporal explica los 'paralelos' entre Kate Middleton y la princesa Diana

Descubra por qué un destacado experto en lenguaje corporal cree que es fácil trazar "tales paralelismos" entre la princesa Kate Middleton y la princesa Diana.

Los láseres arrojan luz sobre por qué necesita cerrar la tapa antes de descargar

Los láseres arrojan luz sobre por qué necesita cerrar la tapa antes de descargar

Los inodoros arrojan columnas de aerosol invisibles con cada descarga. ¿Como sabemos? La prueba fue capturada por láseres de alta potencia.

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!

¿Caduca el repelente de insectos?

¿Caduca el repelente de insectos?

¿Sigue siendo efectivo ese lote de repelente de insectos que te quedó del verano pasado? Si es así, ¿por cuánto tiempo?

Ponle una tapa. En realidad, ponle una tapa a todo. Consigue 12 tapas de cocina elásticas de silicona por $14. [Exclusivo]

Ponle una tapa. En realidad, ponle una tapa a todo. Consigue 12 tapas de cocina elásticas de silicona por $14. [Exclusivo]

Tapas elásticas de silicona de Tomorrow's Kitchen, paquete de 12 | $14 | Amazonas | Código promocional 20OFFKINJALids son básicamente los calcetines de la cocina; siempre perdiéndose, dejando contenedores huérfanos que nunca podrán volver a cerrarse. Pero, ¿y si sus tapas pudieran estirarse y adaptarse a todos los recipientes, ollas, sartenes e incluso frutas en rodajas grandes que sobran? Nunca más tendrás que preocuparte por perder esa tapa tan específica.

Cuéntanos tus mejores trucos de Washington, DC

Cuéntanos tus mejores trucos de Washington, DC

Hemos pirateado algunas ciudades industriales en esta columna, como Los Ángeles y Las Vegas. Ahora es el momento de una ciudad militar-industrial-compleja.

Un minorista está eliminando su sección de tallas grandes y mezclando tallas más grandes con todo lo demás

Un minorista está eliminando su sección de tallas grandes y mezclando tallas más grandes con todo lo demás

Un minorista está enlatando su sección de tallas grandes. Pero no están tomando la categoría solo en línea o descontinuándola por completo.

La mejor forma de guardar animales de peluche es dentro de un puf

La mejor forma de guardar animales de peluche es dentro de un puf

Entiendo totalmente, completamente si tienes una relación difícil con los animales de peluche. Son lindos, tienen valor sentimental y es difícil separarse de ellos.

Patinaje artístico de EE. UU. 'frustrado' por falta de decisión final en evento por equipos, pide una decisión justa

Patinaje artístico de EE. UU. 'frustrado' por falta de decisión final en evento por equipos, pide una decisión justa

El equipo está a la espera de las medallas que ganó en los Juegos Olímpicos de Invierno de 2022 en Beijing, ya que se está resolviendo un caso de dopaje que involucra a la patinadora artística rusa Kamila Valieva.

Los compradores de Amazon dicen que duermen 'como un bebé mimado' gracias a estas fundas de almohada de seda que cuestan tan solo $ 10

Los compradores de Amazon dicen que duermen 'como un bebé mimado' gracias a estas fundas de almohada de seda que cuestan tan solo $ 10

Miles de compradores de Amazon recomiendan la funda de almohada de seda Mulberry, y está a la venta en este momento. La funda de almohada de seda viene en varios colores y ayuda a mantener el cabello suave y la piel clara. Compre las fundas de almohada de seda mientras tienen hasta un 46 por ciento de descuento en Amazon

Se busca al corredor de los Bengals Joe Mixon por orden de arresto emitida por presuntamente apuntar con un arma de fuego a una mujer

Se busca al corredor de los Bengals Joe Mixon por orden de arresto emitida por presuntamente apuntar con un arma de fuego a una mujer

El jueves se presentó una denuncia de delito menor amenazante agravado contra Joe Mixon.

Profesor de la Universidad de Purdue arrestado por presuntamente traficar metanfetamina y proponer favores sexuales a mujeres

Profesor de la Universidad de Purdue arrestado por presuntamente traficar metanfetamina y proponer favores sexuales a mujeres

El Departamento de Policía de Lafayette comenzó a investigar a un profesor de la Universidad de Purdue en diciembre después de recibir varias denuncias de un "hombre sospechoso que se acercaba a una mujer".

Concept Drift: el mundo está cambiando demasiado rápido para la IA

Concept Drift: el mundo está cambiando demasiado rápido para la IA

Al igual que el mundo que nos rodea, el lenguaje siempre está cambiando. Mientras que en eras anteriores los cambios en el idioma ocurrían durante años o incluso décadas, ahora pueden ocurrir en cuestión de días o incluso horas.

India me está pateando el culo

India me está pateando el culo

Estoy de vuelta por primera vez en seis años. No puedo decirte cuánto tiempo he estado esperando esto.

ℝ

“And a river went out of Eden to water the garden, and from thence it was parted and became into four heads” Genesis 2:10. ? The heart is located in the middle of the thoracic cavity, pointing eastward.

¿Merrick Garland le ha fallado a Estados Unidos?

Es más de la mitad de la presidencia de Biden. ¿Qué está esperando Merrick Garland?

¿Merrick Garland le ha fallado a Estados Unidos?

Creo, un poco tarde en la vida, en dar oportunidades a la gente. Generosamente.

Language