2012-02-27 12 views
5

में drawPolyline के साथ एक एनिमेटेड तरंग बनाएं, मैं एक पॉलीलाइन को एनिमेट करने की कोशिश कर रहा हूं (इसे एक लहर की तरह कार्य करना है)। मैंने इस तरह से प्रयास किया है:PySide/PyQt

from PySide.QtCore import * 
from PySide.QtGui import * 
import sys, time 

class Test(QMainWindow): 
    def __init__(self, parent=None): 
     QMainWindow.__init__(self, parent) 

    def poly(self, pts): 
     return QPolygonF(map(lambda p: QPointF(*p), pts)) 

    def paintEvent(self, event): 
     painter = QPainter(self) 

     pts = [[80, 490], [180, 0], [280, 0], [430, 0], [580, 0], [680, 0], [780, 0]] 

     for i in pts: 
      while i[1] < 600: 

       painter.setPen(QPen(QColor(Qt.darkGreen), 3)) 

       painter.drawPolyline(self.poly(pts)) 

       painter.setBrush(QBrush(QColor(255, 0, 0))) 
       painter.setPen(QPen(QColor(Qt.black), 1)) 

       for x, y in pts: 
        painter.drawEllipse(QRectF(x - 4, y - 4, 8, 8)) 

       i[1] += 1 
       print pts 
       time.sleep(0.0025) 
       self.update() 

if __name__ == '__main__': 
    example = QApplication(sys.argv) 
    test2 = Test() 
    test2.resize(800, 600) 
    test2.show() 
    sys.exit(example.exec_()) 

लेकिन, यह काम नहीं कर रहा है! जब प्रोग्राम चलता है, तो स्क्रीन पर एक गड़बड़ होती है। ऐसा लगता है कि self.update() विंडो अपडेट नहीं करता है। कृपया, सहायता करें।

उत्तर

6

स्पष्ट रूप से इस कोड के साथ कुछ समस्याएं हैं। मैं सब कुछ मैं नोटिस सूची जाएगा, और फिर स्पष्टीकरण के माध्यम से जाना:

  1. एक paintEvent
  2. में अभी तक बहुत ज्यादा प्रसंस्करण कर रही है कि paintEvent (बुरा)
  3. बुला self.update के अंदर एक सोने कर() जबकि एक चित्र के अंदर

ठीक है। एक पेंट इवेंट वह जगह है जहां विजेट फिर से निकालना चाहता है और जितनी जल्दी हो सके होना चाहिए। आपको इस घटना में कुछ भी पुनरावर्ती नहीं करना चाहिए, या बहुत अधिक समय लेना चाहिए क्योंकि यह आपके ड्रॉ को धीमा कर देगा। साथ ही, आपके ईवेंट के अंदर अपडेट() को कॉल करना संभावित रूप से रिकर्सिव है। पेंट इवेंट का लक्ष्य विजेट की वर्तमान स्थिति, पेंट, और बाहर निकलने का जवाब देना चाहिए।

यहां आपके कोड का एक संशोधित संस्करण है जो काम करता है। इसके नहीं सबसे आदर्श दृष्टिकोण है, लेकिन मुझे लगता है कि नीचे और अधिक समझा जाएगा ...

from PySide.QtCore import * 
from PySide.QtGui import * 
import sys, time 

class Test(QMainWindow): 
    def __init__(self, parent=None): 
     QMainWindow.__init__(self, parent) 
     self.pts = [[80, 490], [180, 0], [280, 0], [430, 0], [580, 0], [680, 0], [780, 0]] 

    def poly(self, pts): 
     return QPolygonF(map(lambda p: QPointF(*p), pts)) 

    def paintEvent(self, event): 
     painter = QPainter(self) 

     pts = self.pts[:] 

     painter.setPen(QPen(QColor(Qt.darkGreen), 3)) 
     painter.drawPolyline(self.poly(pts)) 

     painter.setBrush(QBrush(QColor(255, 0, 0))) 
     painter.setPen(QPen(QColor(Qt.black), 1)) 

     for x, y in pts: 
      painter.drawEllipse(QRectF(x - 4, y - 4, 8, 8)) 

     # print pts 

    def wave(self): 

     for point in self.pts: 
      while point[1] < 600: 
       point[1] += 1 
       self.update()    
       QApplication.processEvents() 
       time.sleep(0.0025) 


if __name__ == '__main__': 
    example = QApplication(sys.argv) 
    test2 = Test() 
    test2.resize(800, 600) 
    test2.show() 
    test2.raise_() 
    test2.wave() 
    sys.exit(example.exec_()) 

सूचना है कि अंक एक सदस्य विशेषता, self.pts ले जाया गया है, और paintEvent() अब केवल अंक की वर्तमान स्थिति पेंट । फिर, एनीमेशन तर्क wave() नामक दूसरी विधि में ले जाया गया है। इस विधि में, यह प्रत्येक बिंदु को लूप और संशोधित करता है और रेड्रो ट्रिगर करने के लिए update() पर कॉल करता है। ध्यान दें कि हम paintEvent के बाहर update() पर कॉल कर रहे हैं। यह महत्वपूर्ण है क्योंकि किसी भी अन्य घटनाओं को आपके आवेदन में होना चाहिए जिससे खिड़की को फिर से निकाला जा सकता है (आकार बदलना, आदि), आप पेंटवेन्ट हमेशा के लिए लुप्त हो सकते थे।

तो हम QApplication.processEvents() पर कॉल करने के लिए इस बिंदु सूची, नींद और एक महत्वपूर्ण जोड़ को संशोधित करते हैं। आम तौर पर, जब एप्लिकेशन निष्क्रिय हो जाता है तो ईवेंट संसाधित होते हैं (वर्तमान कॉल छोड़ देता है)। चूंकि आप लगातार एक पश्चाताप कर रहे हैं, और इन घटनाओं को ढेर कर रहे हैं, तो आपको घटना लूप को आगे बढ़ने और सबकुछ फ़्लश करने की आवश्यकता है। processEvents() कमांड पर टिप्पणी करने का प्रयास करें और देखें कि क्या होता है। लूप पूरा होने तक आपका ऐप बस कुछ भी नहीं कर सकता है, और परिणामी रेखा जगह में चली जाएगी।

अब जहां मैं सुझाव दे रहा था कि यह वास्तव में सबसे आदर्श दृष्टिकोण नहीं है, हालांकि यह एक उदाहरण के रूप में काम करता है। यह वर्तमान उदाहरण मुख्य धागे को अवरुद्ध करता है जबकि यह एक लहर कर रहा है। आपको हमेशा मुख्य धागे को अवरुद्ध करने से बचाना चाहिए क्योंकि इसका मतलब जीयूआई कार्यक्रमों का जवाब देना है। तो यहाँ कुछ संभव सुझाव हैं:

  1. आप समय समाप्त के रूप में 0.0025 एनीमेशन गति का उपयोग कर एक QTimer बना सकते हैं। wave() विधि के संस्करण को सिग्नल करें जो एक ही चरण और कॉल अपडेट करता है। अब और नींद की जरूरत नहीं है। एक बार आपकी लहर गणना समाप्त हो जाने के बाद, आप उस समय wave() में जांच लेंगे और टाइमर पर stop() पर कॉल करें।

  2. पूरे wave() लूप और ऊपर दिए गए उदाहरण से शुरुआती डेटासेट QThread पर ले जाएं। यह QThread emit a custom signalwaveUpdate(QPolygonF) जैसा होगा। जब आप इस धागे को शुरू करते हैं तो यह लूप करेगा, और QPolygonF बनाने और प्रत्येक चरण पर इसे सिग्नल और नींद छोड़ देगा। आप इस सिग्नल को अपनी मुख्य विंडो पर एक विधि से जोड़ सकते हैं जो नया पॉलीगॉन प्राप्त करेगा, इसे self._polygon पर असाइन करें, और अपडेट() को कॉल करें, जो तब स्वयं self._polygon को पकड़ लेगा और इसे पेंट करेगा। यहां विचार है कि जितना संभव हो उतना भारी भारोत्तोलन थ्रेड में स्थानांतरित करना है, और केवल अपने मुख्य जीयूआई थ्रेड को नए मानों से पश्चाताप करना है।

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^