পিআইকিএটি / কিউটি অ্যাপ্লিকেশনগুলিতে যুক্তি থেকে সঠিকভাবে কীভাবে ইউআই ডিকুয়াল করবেন?


20

আমি এই বিষয় সম্পর্কে অতীতে বেশ কিছু পড়েছি এবং চাচা ববসের মতো এই মত কিছু আকর্ষণীয় আলোচনা দেখেছি । তবুও, আমি সর্বদা আমার ডেস্কটপ অ্যাপ্লিকেশনগুলিকে সঠিকভাবে স্থপতি করা এবং ইউআই পার্শ্বে কোনটি দায়বদ্ধ হওয়া উচিত এবং লজিকের দিকগুলির মধ্যে কোনটি আলাদা করা উচিত তা পৃথক করে তুলতে আমার পক্ষে খুব কঠিন ।

ভাল অনুশীলনের খুব সংক্ষিপ্তসার এইরকম কিছু। আপনি আপনার যুক্তিটি ইউআই থেকে ডিকপলড ডিজাইন করা উচিত, যাতে আপনি আপনার গ্রন্থাগারটি (তাত্ত্বিকভাবে) কোনও ধরণের ব্যাকএন্ড / ইউআই কাঠামোই বিবেচনা না করে ব্যবহার করতে পারেন। এর অর্থ যা মূলত ইউআইটি যতটা সম্ভব ডামি হওয়া উচিত এবং ভারী প্রসেসিংটি যুক্তির দিকে করা উচিত। অন্যথায় বলেছে, আমি আক্ষরিকভাবে আমার দুর্দান্ত লাইব্রেরিটি কনসোল অ্যাপ্লিকেশন, একটি ওয়েব অ্যাপ্লিকেশন বা একটি ডেস্কটপ ব্যবহার করতে পারি।

এছাড়াও, চাচা বব কোন প্রযুক্তি ব্যবহার করবেন তা আপনাকে বিভিন্ন উপকারিতা (ভাল ইন্টারফেস) দেবে তার বিবিধ আলোচনার পরামর্শ দেয়, মুলতুবি করার এই ধারণাটি আপনাকে অত্যন্ত ডিকম্পলড ভাল-পরীক্ষিত সত্তাগুলি রাখতে দেয়, এটি দুর্দান্ত শোনাচ্ছে তবে তবুও জটিল y

সুতরাং, আমি জানি এই প্রশ্নটি বেশ বিস্তৃত প্রশ্ন যা পুরো ইন্টারনেট এবং অনেক ভাল বইতেও বহুবার আলোচিত হয়েছে। সুতরাং এর থেকে ভাল কিছু পাওয়ার জন্য আমি পাইকিটিতে এমসিসি ব্যবহার করার চেষ্টা করার একটি খুব ছোট্ট ছদ্মবেশী উদাহরণ পোস্ট করব:

import sys
import os
import random

from PyQt5 import QtWidgets
from PyQt5 import QtGui
from PyQt5 import QtCore

random.seed(1)


class Model(QtCore.QObject):

    item_added = QtCore.pyqtSignal(int)
    item_removed = QtCore.pyqtSignal(int)

    def __init__(self):
        super().__init__()
        self.items = {}

    def add_item(self):
        guid = random.randint(0, 10000)
        new_item = {
            "pos": [random.randint(50, 100), random.randint(50, 100)]
        }
        self.items[guid] = new_item
        self.item_added.emit(guid)

    def remove_item(self):
        list_keys = list(self.items.keys())

        if len(list_keys) == 0:
            self.item_removed.emit(-1)
            return

        guid = random.choice(list_keys)
        self.item_removed.emit(guid)
        del self.items[guid]


class View1():

    def __init__(self, main_window):
        self.main_window = main_window

        view = QtWidgets.QGraphicsView()
        self.scene = QtWidgets.QGraphicsScene(None)
        self.scene.addText("Hello, world!")

        view.setScene(self.scene)
        view.setStyleSheet("background-color: red;")

        main_window.setCentralWidget(view)


class View2():

    add_item = QtCore.pyqtSignal(int)
    remove_item = QtCore.pyqtSignal(int)

    def __init__(self, main_window):
        self.main_window = main_window

        button_add = QtWidgets.QPushButton("Add")
        button_remove = QtWidgets.QPushButton("Remove")
        vbl = QtWidgets.QVBoxLayout()
        vbl.addWidget(button_add)
        vbl.addWidget(button_remove)
        view = QtWidgets.QWidget()
        view.setLayout(vbl)

        view_dock = QtWidgets.QDockWidget('View2', main_window)
        view_dock.setWidget(view)

        main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, view_dock)

        model = main_window.model
        button_add.clicked.connect(model.add_item)
        button_remove.clicked.connect(model.remove_item)


class Controller():

    def __init__(self, main_window):
        self.main_window = main_window

    def on_item_added(self, guid):
        view1 = self.main_window.view1
        model = self.main_window.model

        print("item guid={0} added".format(guid))
        item = model.items[guid]
        x, y = item["pos"]
        graphics_item = QtWidgets.QGraphicsEllipseItem(x, y, 60, 40)
        item["graphics_item"] = graphics_item
        view1.scene.addItem(graphics_item)

    def on_item_removed(self, guid):
        if guid < 0:
            print("global cache of items is empty")
        else:
            view1 = self.main_window.view1
            model = self.main_window.model

            item = model.items[guid]
            x, y = item["pos"]
            graphics_item = item["graphics_item"]
            view1.scene.removeItem(graphics_item)
            print("item guid={0} removed".format(guid))


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()

        # (M)odel ===> Model/Library containing should be UI agnostic, right now it's not
        self.model = Model()

        # (V)iew      ===> Coupled to UI
        self.view1 = View1(self)
        self.view2 = View2(self)

        # (C)ontroller ==> Coupled to UI
        self.controller = Controller(self)

        self.attach_views_to_model()

    def attach_views_to_model(self):
        self.model.item_added.connect(self.controller.on_item_added)
        self.model.item_removed.connect(self.controller.on_item_removed)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)

    form = MainWindow()
    form.setMinimumSize(800, 600)
    form.show()
    sys.exit(app.exec_())

উপরের স্নিপেটে প্রচুর ত্রুটি রয়েছে, ইউআই কাঠামোর (কিউবজেক্ট, পাইক্ট সংকেত) একত্র হয়ে মডেলটি আরও স্পষ্টভাবে প্রকাশিত। আমি জানি যে উদাহরণটি সত্যই ডামি এবং আপনি একটি একক কিউমেন উইন্ডো ব্যবহার করে কয়েকটি লাইনে কোড করতে পারেন তবে আমার উদ্দেশ্যটি কীভাবে একটি বৃহত পাইক্ট অ্যাপ্লিকেশনটি সঠিকভাবে স্থপতি করা যায় তা বোঝা।

প্রশ্ন

ভাল সাধারণ অভ্যাস অনুসরণ করে আপনি কীভাবে সঠিকভাবে একটি বড় পাইকিউটি অ্যাপ্লিকেশনটি স্থপতি করতে পারেন?

তথ্যসূত্র

আমি এখানে এটি একটি অনুরূপ প্রশ্ন করেছি

উত্তর:


1

আমি একটি (প্রাথমিকভাবে) ডাব্লুপিএফ / এএসপি.নেট ব্যাকগ্রাউন্ড থেকে এসেছি এবং এখনই একটি এমভিসি-ইশ পাইকিউটি অ্যাপ্লিকেশন করার চেষ্টা করছি এবং এই খুব প্রশ্নই আমাকে হতাশ করছে। আমি যা করছি তা শেয়ার করব এবং কোনও গঠনমূলক মন্তব্য বা সমালোচনা পেতে আগ্রহী হব।

এখানে কিছুটা ASCII চিত্রটি রয়েছে:

View                          Controller             Model
---------------
| QMainWindow |   ---------> controller.py <----   Dictionary containing:
---------------   Add, remove from View                |
       |                                               |
    QWidget       Restore elements from Model       UIElementId + data
       |                                               |
    QWidget                                         UIElementId + data
       |                                               |
    QWidget                                         UIElementId + data
      ...

আমার অ্যাপ্লিকেশনটিতে প্রচুর ইউআই উপাদান এবং উইজেট রয়েছে যা বেশ কয়েকটি প্রোগ্রামার দ্বারা সহজেই সংশোধন করা দরকার। "ভিউ" কোডটিতে একটি কিউমেন উইন্ডো রয়েছে একটি QTreeWidget আইটেম রয়েছে যা ডানদিকে QStackedWidget দ্বারা প্রদর্শিত হবে (মাস্টার-বিশদ বিবরণ দেখুন))

যেহেতু আইটেমগুলি কিউটি উইজেট থেকে গতিশীলভাবে যুক্ত এবং সরানো যেতে পারে এবং আমি পূর্বাবস্থায় ফিরিয়ে আনতে কার্যকারিতাটি সমর্থন করতে চাই, আমি এমন একটি মডেল তৈরি করতে বেছে নিয়েছি যা বর্তমান / পূর্ববর্তী অবস্থার উপর নজর রাখে। কন্ট্রোলার দ্বারা ইউআই কমান্ডগুলি মডেলটির মাধ্যমে তথ্য সরবরাহ করে (একটি উইজেট যুক্ত বা অপসারণ, একটি উইজেটে তথ্য আপডেট করে) commands কন্ট্রোলার কেবলমাত্র ইউআই-তে তথ্য পৌঁছে দেওয়ার সময় বৈধতা, ইভেন্ট হ্যান্ডলিং এবং কোনও ফাইল লোড / পূর্বাবস্থায় ফেরানো হয়।

মডেলটি নিজেই সর্বশেষটি ধারণকৃত মান (এবং কয়েকটি অতিরিক্ত তথ্যের টুকরো) সহ ইউআই উপাদান আইডির একটি অভিধান নিয়ে গঠিত। আমি পূর্ববর্তী অভিধানের একটি তালিকা রাখি এবং যদি কেউ পূর্বাবস্থায় ফিরেন তবে আগের একটিতে ফিরে যেতে পারি। শেষ পর্যন্ত মডেলটি একটি নির্দিষ্ট ফাইল ফর্ম্যাট হিসাবে ডিস্কে ফেলে যায়।

আমি সত্যবাদী হবো - আমি এটি নকশা করা বেশ কঠিন খুঁজে পেয়েছি। পাইকিউটি মনে হয় না যে এটি নিজেকে মডেল থেকে তালাকপ্রাপ্ত হওয়ার পক্ষে ভাল ধার দেয় এবং আমি সত্যিই এর সাথে বেশ কিছু অনুরূপ কিছু করার চেষ্টা করার মতো কোনও ওপেন সোর্স প্রোগ্রাম পাইনি। অন্যান্য ব্যক্তিরা কীভাবে এটি কাছে এসেছেন তা আগ্রহী।

পিএস: আমি বুঝতে পারি এমভিসি করার জন্য কিউএমএল হ'ল একটি বিকল্প, এবং জাভাস্ক্রিপ্টের কতটা জড়িত তা বুঝতে না পারলে এটি আকর্ষণীয় বলে মনে হয়েছিল - এবং এটি পিআইকিউটি (বা কেবলমাত্র পিরিয়ড) এ পোর্ট করার ক্ষেত্রে এটি এখনও যথেষ্ট অপরিপক্ক। কোনও দুর্দান্ত ডিবাগিং সরঞ্জামগুলির জটিল বিষয়গুলি (কেবল পাইকিউটি দিয়ে যথেষ্ট শক্ত) এবং অন্যান্য প্রোগ্রামারদের এই কোডটি সহজেই সংশোধন করার প্রয়োজন জেএস এটি জানেন না যা জানেন না।


0

আমি একটি অ্যাপ্লিকেশন তৈরি করতে চেয়েছিলাম। আমি স্বতন্ত্র ফাংশনগুলি লিখতে শুরু করেছি যা ক্ষুদ্র কাজগুলি করেছে (ডিবিতে কিছু সন্ধান করুন, কিছু গণনা করুন, স্বতঃসমম্পূর্ণ ব্যবহারকারী হিসাবে সন্ধান করুন)। টার্মিনালে প্রদর্শিত। তারপরে একটি পদ্ধতিতে এই পদ্ধতিগুলি রাখুন, main.py..

তারপরে আমি একটি ইউআই যুক্ত করতে চেয়েছিলাম। আমি বিভিন্ন সরঞ্জাম ঘুরে দেখেছি এবং কিউটিতে স্থির হয়েছি। আমি ইউআই তৈরি করতে, তারপরে pyuic4উত্পন্ন করার জন্য স্রষ্টাকে ব্যবহার করেছি UI.py

ইন main.py, আমি আমদানি করেছি UI। তারপরে মূল কার্যকারিতা শীর্ষে ইউআই ইভেন্টগুলি দ্বারা উদ্ভূত পদ্ধতিগুলি যুক্ত করা হয়েছে (আক্ষরিক অর্থে শীর্ষে: "মূল" কোডটি ফাইলের নীচে রয়েছে এবং ইউআইয়ের সাথে কোনও সম্পর্ক নেই, আপনি যদি চান তবে আপনি শেল থেকে এটি ব্যবহার করতে পারেন প্রতি).

এখানে এমন একটি পদ্ধতির উদাহরণ display_suppliersযা একটি সারণীতে সরবরাহকারীদের (ক্ষেত্রগুলি: নাম, অ্যাকাউন্ট) তালিকা প্রদর্শন করে। (আমি কেবল কাঠামোগত চিত্রিত করার জন্য এটি বাকী কোড থেকে কেটেছি)।

পাঠ্য ক্ষেত্রে ব্যবহারকারী যেমন টাইপ করেন HSGsupplierNameEdit, পাঠ্যটি পরিবর্তিত হয় এবং প্রতিবার এটি হয়, এই পদ্ধতিটিকে তাই বলা হয় যাতে ব্যবহারকারীর টেবিলটি পরিবর্তিত হয়।

এটি সরবরাহকারীদের এমন একটি পদ্ধতি get_suppliers(opchoice)থেকে সরবরাহ করে যা ইউআই থেকে স্বতন্ত্র এবং কনসোল থেকেও কাজ করে।

from PyQt4 import QtCore, QtGui
import UI

class Treasury(QtGui.QMainWindow):

    def __init__(self, parent=None):
        self.ui = UI.Ui_MainWindow()
        self.ui.setupUi(self)
        self.ui.HSGsuppliersTable.resizeColumnsToContents()
        self.ui.HSGsupplierNameEdit.textChanged.connect(self.display_suppliers)

    @QtCore.pyqtSlot()
    def display_suppliers(self):

        """
            Display list of HSG suppliers in a Table.
        """
        # TODO: Refactor this code and make it generic
        #       to display a list on chosen Table.


        self.suppliers_virement = self.get_suppliers(self.OP_VIREMENT)
        name = unicode(self.ui.HSGsupplierNameEdit.text(), 'utf_8')
        # Small hack for auto-modifying list.
        filtered = [sup for sup in self.suppliers_virement if name.upper() in sup[0]]

        row_count = len(filtered)
        self.ui.HSGsuppliersTable.setRowCount(row_count)

        # supplier[0] is the supplier's name.
        # supplier[1] is the supplier's account number.

        for index, supplier in enumerate(filtered):
            self.ui.HSGsuppliersTable.setItem(
                index,
                0,
                QtGui.QTableWidgetItem(supplier[0])
            )

            self.ui.HSGsuppliersTable.setItem(
                index,
                1,
                QtGui.QTableWidgetItem(self.get_supplier_bank(supplier[1]))
            )

            self.ui.HSGsuppliersTable.setItem(
                index,
                2,
                QtGui.QTableWidgetItem(supplier[1])
            )

            self.ui.HSGsuppliersTable.resizeColumnsToContents()
            self.ui.HSGsuppliersTable.horizontalHeader().setStretchLastSection(True)


    def get_suppliers(self, opchoice):
        '''
            Return a list of suppliers who are 
            relevant to the chosen operation. 

        '''
        db, cur = self.init_db(SUPPLIERS_DB)
        cur.execute('SELECT * FROM suppliers WHERE operation = ?', (opchoice,))
        data = cur.fetchall()
        db.close()
        return data

আমি সর্বোত্তম অনুশীলন এবং সেগুলির মতো জিনিসগুলির সম্পর্কে খুব বেশি কিছু জানি না, তবে এটিই আমার বোধগম্য হয়েছিল এবং ঘটনাক্রমে এক বিরতি পরে আমার অ্যাপটিতে ফিরে আসা এবং ওয়েব 2 অ্যাপ্লিকেশন ব্যবহার করে একটি ওয়েব অ্যাপ্লিকেশন তৈরি করতে ইচ্ছুক হয়েছিল me বা ওয়েবঅ্যাপ 2। বস্তুটি আসলে স্টাফগুলি করে এমন কোডটি স্বাধীন এবং নীচে কেবল এটি দখল করতে সহজ করে তোলে এবং তারপরে ফলাফলগুলি কীভাবে প্রদর্শিত হবে তা পরিবর্তন করুন (এইচটিএমএল উপাদান বনাম ডেস্কটপ উপাদানসমূহ)।


0

... অনেক ত্রুটি, মডেলটি ইউআই কাঠামোর (কিউবজেক্ট, পাইক্ট সংকেত) সাথে মিলিত হওয়ার বিষয়টি আরও স্পষ্ট।

সুতরাং এটি করবেন না!

class Model(object):
    def __init__(self):
        self.items = {}
        self.add_callbacks = []
        self.del_callbacks = []

    # just use regular callbacks, caller can provide a lambda or whatever
    # to make the desired Qt call
    def emit_add(self, guid):
        for cb in self.add_callbacks:
            cb(guid)

এটি ছিল একটি তুচ্ছ পরিবর্তন, যা Qt থেকে আপনার মডেলটিকে পুরোপুরি ডিউপল করেছিল। আপনি এটিকে এখন অন্য কোনও মডিউলে স্থানান্তর করতে পারেন।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.